Skip to content

feat(web): per-account collection cap and admin moderation for collections - #115

Merged
mrgoonie merged 1 commit into
mainfrom
claude/modest-wozniak-36vvlk
Oct 20, 2026
Merged

mrgoonie merged 1 commit into
mainfrom
claude/modest-wozniak-36vvlk

Conversation

@mrgoonie

Copy link
Copy Markdown
Contributor

Closes #114. No duplicate or related issues were found.

What changes

  • Per-account cap. An account keeps at most 100 collections. The SKILLX_COLLECTIONS_MAX_PER_USER var overrides this with a positive integer.
    • A create or clone over the cap answers 409 collection.limit_reached and writes nothing.
    • The cap is checked inside the INSERT … SELECT … WHERE count < cap. A refused parent row makes the revision insert fail its foreign key, so the whole D1 batch rolls back.
  • Admin moderation. POST /api/v1/collections/:id/moderation takes {from, to, reason?} and moves a collection between active and hidden.
    • It accepts X-Admin-Secret only. Accounts get 403 collection.admin_only.
    • It reuses the audited compareAndSet from releases, with the new subject kind collection recorded in release_state_audit.
    • A stale from answers 409 collection.moderation_conflict.
    • GET on the same path returns the state, the latest revision and the audit history.
  • Effect of hiding. A hidden collection answers 404 to everyone except its owner, whatever its visibility.
    • Changing the visibility does not lift the hide.
    • The owner still sees collection.moderation and can keep editing.
    • The owner cannot clone it (403 collection.hidden) or delete it (409 collection.hidden). Blocking delete stops the owner from re-posting the content unmoderated, and the hidden collection keeps its slot under the cap.
  • Migration 0020_collection_moderation. It is additive: moderation_state NOT NULL DEFAULT 'active', plus the nullable moderation_reason and moderated_at.
    • Either deploy order works. Code treats a missing column as active, and only the admin route needs the columns.
    • The rollback steps and the "rollback un-hides content" warning are in the SQL header and in docs/deployment-guide.md.
  • Refactor. Row types and views moved to collection-views.ts to keep files under 200 LOC. The test helper was split into collections-harness.ts.
  • Docs. Updated docs/api-reference.md#collections, docs/deployment-guide.md, the CLAUDE.md routes table, and the Issue Workflow section, which gains the blocked label as requested for issue claiming.

Verification

  • pnpm typecheck: clean.
  • pnpm test: 116 files, 2315 passed and 2 skipped. This includes 6 new moderation and cap tests.
  • pnpm build: OK.
  • An isolated code review found nothing blocking. Its medium findings are fixed in this PR: the owner could delete a hidden collection and recreate it, and the docs did not warn about rollback. Its low findings on operator visibility of content and deterministic history order are also fixed.

Deploy note

Run pnpm run db:migrate:remote (after a backup) before or after deploying. Until it runs, only the moderation route answers 500.


Generated by Claude Code

…tions

An account may keep at most 100 collections (SKILLX_COLLECTIONS_MAX_PER_USER
overrides it); a create or clone over the cap answers 409
collection.limit_reached. The cap is checked inside the insert, so
concurrent creates cannot pass it.

Operators hide or unhide a collection with POST /api/v1/collections/:id/moderation
(admin secret only), an audited compare-and-set on the new
collections.moderation_state column (migration 0020). A hidden collection
answers 404 to everyone but its owner, whatever its visibility; the owner
can neither clone nor delete it. GET on the same path returns the state,
the latest revision and the audit history.

Closes #114

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017CWq2UVSFw5KgH464tpw3b
@mrgoonie
mrgoonie merged commit dfcb3f1 into main Oct 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): per-user collection cap and moderation plan for public collections

2 participants