Repository navigation
fix(rest): one anonymous-intake rule honours every declared public-form withdrawal - #21566
Conversation
…e metadata protocol (WIP) Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
…(WIP) Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
…ous-intake rule Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 4 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 141 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f7f6721ffa96467cc63a36e498e9317ba359f313 && git checkout f7f6721ffa96467cc63a36e498e9317ba359f313
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1ca1eb09727d7769bc605428ad1e6fb62d743f49 769594d9c68e109be0535ac9ee3b2e5fbd168ef2 && git checkout -B drift-repro 1ca1eb09727d7769bc605428ad1e6fb62d743f49 && git merge --no-ff 769594d9c68e109be0535ac9ee3b2e5fbd168ef2
node scripts/docs-audit/affected-docs.mjs --json 1ca1eb09727d7769bc605428ad1e6fb62d743f49
|
…blic-form-withdrawal-second-means
Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #21475 (body and its three comments — triage 5962746069, claim 5966742063, os-dev-report 5967097888), PR #21566 (body, 16-path file list, net diff ① Derived judgments
② Semver levelChangeset
What must change, exactly — and nothing else: ③ Boundary flags(A) Dev Q1 — CHANGELOG key-level migration. Decided A by the seat; the changeset at this head carries it (" (B) Dev Q2 — the published skill's under-statement. Decided A: a separate Tier H card, #21567 (open, created 2026-10-03T08:20Z, (C) Raised here, not in the dev's questions — the claim's file surface named (D) Residual prose naming one key for the public-form opt-in, outside this PR: the (E) Check-runs on this head, read from the commit's check-runs API at 2026-10-03T08:38Z (32 runs). Completed Implemented-by: VERDICT: FAIL |
The shared rule widens @objectstack/metadata-core's public index by five exported symbols, which takes at least minor (Clause-② yes, widening). Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #21475 (body and its four comments — triage 5962746069, claim 5966742063, os-dev-report 5967097888, claim correction 5967269801), PR #21566 (body as read at this head, 16-path file list, net diff This is the fresh record the prior one said was owed on the head carrying its two fixes. The head-to-head delta ① Derived judgments
② Semver levelChangeset
The ② finding of the prior record is FIXED on this head, with exactly the three prescribed consequences and nothing else. ③ Boundary flags(A) Dev Q1 — CHANGELOG key-level migration. Decided A by the seat; the changeset at this head carries it unchanged from (B) Dev Q2 — the published skill's under-statement. Decided A: a separate Tier H card, #21567 (re-read: open, created 2026-10-03T08:20Z, names (C) Claim file-surface deviation — the claim named (D) Residual prose naming one key for the public-form opt-in, outside this PR: (E) Check-runs on this head, read from the commit's check-runs API at 2026-10-03T08:45Z (39 runs, including two Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #21475
Clause-②: yes (widening) — five new exports on
@objectstack/metadata-core's index (the shared anonymous-intake rule); gradedminor.What
A second declared means of withdrawing a public form from anonymous intake is now honoured by every anonymous door; pinned both sides.
Which forms a
viewopens to anonymous intake is now one rule, defined once in@objectstack/metadata-core(anonymousFormIntakeCandidates, withanonymousFormIntakeSlugs/anonymousFormIntakeSlug/publicFormSlug), and read by:@objectstack/rest(registerFormEndpoints→findPublicFormView), andviewwrite check in@objectstack/metadata-protocol(anonymousFormIntakeOrgScopeRefusal), whose local projection (src/anonymous-form-intake.ts) is deleted in favour of the shared one.So no door reads one declared means and not the other, there is no second per-door check, and the write-time judgement cannot drift from what the doors serve. The rule follows
SharingConfigSchemaas declared, including its defaults, so a raw stored body and its parse get the same answer.Pins (both sides)
packages/metadata-core/src/anonymous-form-intake.test.ts— the rule itself, every closed shape, raw-vs-parse parity, the three candidate shapes.packages/rest/src/public-form-withdrawal.test.ts— new block: withdrawn by either declared means ⇒ both doors404 FORM_NOT_FOUND,createDatanever called; published ⇒200/201(control); with tenancy resolving an organization and with tenancy unregistered. Existing fixtures that never declared public sharing per the schema now declare it.packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts— an org-scoped walled write that withdraws through the second means now counts as a change and is refused403 NOT_OVERRIDABLE, nothing saved.showcase-public-form-withdrawal.dogfood.test.ts, real showcase boot): withdrawn env-wide by the second means ⇒ both doors 404FORM_NOT_FOUNDand no row lands; republished ⇒ 200/201 and the row lands. The existingallowAnonymouspins stay green.public-form-withdrawal-walled.dogfood.test.ts): the same withdrawal org-scoped is refused 403; env-wide closes both doors; republish restores.Ablation (one-shot, not kept)
Deleted the second-means check from the shared rule via
node scripts/ablation-replace.mjs(anchor 1 → 0, blobbf5a099a→ad331202), rebuilt@objectstack/metadata-core,ablation-dist-preflight --absentconfirmed the guard gone from all 12 built files. Result: rest 4 failed / 12 passed (exactly the four second-means cases), metadata-protocol 1 failed / 22 passed, dogfood 2 failed / 9 passed, metadata-core 5 failed / 8 passed; everyallowAnonymouspin and every control stayed green. Restore: blob == HEAD,git diff HEADempty; rebuilt and the preflight found the guard back in dist;git status --porcelainempty.Tests (at
0164be245)@objectstack/metadata-corevitest: 17 files / 311 passed.@objectstack/restvitest: 262 files / 5044 passed, 327 skipped.@objectstack/metadata-protocolvitest: 206 passed, 3 skipped files / 3177 passed, 19 skipped.@objectstack/dogfood(the four public-form files): 4 files / 17 passed.typecheckfor metadata-core, metadata-protocol, rest, dogfood: allDone.node scripts/pm/dispatch-gates.mjs --commands: 114 derived commands run; 113 exit 0;check:dual-build-cjs-loadsanswered PREREQUISITE NOT MET (needs a full workspace build): NOT MEASURED, left to CI.--ranreconciliation: 114 of 114 accounted for.@objectstack/speccheck:generated: all 15 artifacts current aftergen:docs(the regeneratedcontent/docs/references/ui/sharing.mdxcarries the corrected module header)..tsfiles (--no-inline-config --format json): 12 files, 0 errors, 0 warnings. Narrowing is sound:eslint.config.mjsenables no type-aware linting (noparserOptions.project), so this diff cannot move any untouched file's verdict. The fullpnpm lintis CI's.Acceptance notes
content/docs/ui/forms.mdxand thesharing.zod.tsmodule header are corrected to state the rule as enforced.skills/**is a Tier H surface, so it is left out of this PR to keep this one ungoverned; it needs its own follow-up (carrier: none yet).Generated by Claude Code