feat(write-api): POST/PATCH/DELETE endpoints + indexing + reconcile + authz#29
Merged
Conversation
themightychris
added a commit
that referenced
this pull request
May 16, 2026
…indices, state-apply, commit-meta, notifier
…wanted, people, tags
The gitsheets path template renders against the validated record. With Zod's
default object behavior, denormalized path fields (projectSlug, personSlug,
etc.) supplied by write services would be stripped before the renderer ran,
causing PathTemplateError. Use .passthrough() on ProjectMembership,
ProjectUpdate, ProjectBuzz, HelpWantedRole, and HelpWantedInterestExpression
so those extras survive validation.
Regenerated the corresponding JSON Schema exports (additionalProperties:{}
instead of false).
This was referenced May 16, 2026
1f0e374 to
fa41a45
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements plan: write-api (plans/write-api.md).
Every documented POST/PATCH/DELETE across projects, members, updates, buzz, help-wanted, people, and tags is now wired through
store.transact, producing one gitsheets commit per mutation with the documented author + trailer policy. Newsletter PATCH is the lone private-only mutation (no public commit). Authorization is enforced via a newrequireAuth(marker, ctx?)helper at the route and service boundaries per behaviors/authorization.md.Absorbed deferrals from upstream plans:
Sheet.defineIndexcalls wired for all secondary indices indata-model.md(apps/api/src/store/sheet-indices.ts).apps/api/scripts/reconcile-private-store.tswalks public Person records and flags /--fix-es missing private profiles.invalidateFacets()+ FTS upsert/remove are dispatched via a smallStateApplyhelper that runs after the transaction successfully commits so in-memory state and gitsheets stay in lockstep on commit and on rollback.Tag handling: user-supplied unknown slugs return
422 tag_not_found; staff-supplied unknown slugs auto-create the tag in the same commit per behaviors/tags.md.Schemas for sheets carrying denormalized path-template fields (memberships, updates, buzz, help-wanted roles + interest) gain
.passthrough()soprojectSlug/personSlugsurvive validation and reach the gitsheets path renderer. JSON Schema exports regenerated (additionalProperties: {}).Authenticated
permissionscross-check from the read-api absorbed deferral is exercised bycross-cutting > GET project permissions flip across anonymous → maintainer → staff.Test plan
POST /api/projects(anon → 401, user → 201 + founder membership, slug collision → 409, reserved slug → 422, unknown tag for non-staff → 422, auto-create tag for staff)PATCH /api/projects/:slug(maintainer can edit, staff slug rename writes new + deletes old + writes SlugHistory; non-staff slug change → 422)DELETE /api/projects/:slug(staff soft-delete → subsequent GET 404; non-staff → 403)POST /api/projects/:slug/members(duplicate add → 409 already_member)DELETE /api/projects/:slug/members/:slug(current maintainer → 409 cannot_remove_maintainer)?q=GET /api/projects/:slugnpm run lint,npm run -w apps/api type-check,npm run build— all clean🤖 Generated with Claude Code