Repository navigation
security(forms): a second way of withdrawing a public form from anonymous intake is not honoured by the anonymous doors — sibling of #21331, detail withheld pending maintainer #21475
Description
Activity
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·security·priority:p1·domain:services·area:access·pm:queue. The withdrawal state is one state every anonymous door reads, whichever declared means set itTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T22:54Z. ⛔ Not a claim, ⛔ not a dispatch. ⛔ Classes and positions only. The withheld reproduction stays withheld (RUNNER rule 2).Why p1 and
security. It is an authorization defect that fails open, the class and grade of its siblings #21331 (p1, fixed) and #21468 (p1, in flight). An administrator's declared withdrawal is shown as effective but is not honoured by the anonymous doors.Routing. It is the public-form intake family,
domain:services, as the siblings are.Direction: already ruled, on #21331. Withdrawal is one state that every anonymous intake door reads.
- A second declared means of withdrawal is either folded into that one state, or retired as declared but not enforced.
- ⛔ No door reads one means and not the other.
- ⛔ No second check per door.
Pins: both directions on every tenancy posture.
- Withdrawn by either declared means: every anonymous door refuses, and nothing lands.
- Published: accepted. That is the control.
The withheld detail: the claiming seat obtains the reproduction and mechanism from the holding session (
session_018zT8d8NpiQ1ExhuNd5TxY6) on the maintainer's word. ⛔ They are never restated on this card or its PR.Serial: after PR #21473, which edits the same projection of which forms are open to anonymous intake.
Generated by Claude Code
- addedarea:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingSomething isn't workingpriority:p1High: required for production / M2High: required for production / M2and removed
on Oct 2, 2026 objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 (sibling of #21331 / #21468, dispatched on the maintainer's direct order)
Session:session_018zT8d8NpiQ1ExhuNd5TxY6
Account:hotlong(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-21475-public-form-withdrawal-second-means
Worktree:objectstack-issue-21475
Domain:domain:services(card label, as triage set it); expected surfacepackages/rest's anonymous form doors andpackages/metadata-protocol's anonymous-intake projection
File surface:packages/rest/src,packages/metadata-protocol/src,packages/qa/dogfood/test,.changeset/;packages/speconly if the key is retired rather than enforced (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus(default tier; no path-derived mandate)
Clause-②: no
Thread-read: 5962746069
Serial constraints cleared: #21473 merged (ce532184donmain), the serial predecessor triage namedProvenance: the maintainer, in Claude Code session
session_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-03, verbatim: 「21475 也派发吧,完成后进队列合并」. This session holds the withheld reproduction; the dispatch carries it privately. The withheld detail stays off this card and its PR.
Generated by Claude Code
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21475, "status": "done", "branch": "claude/issue-21475-public-form-withdrawal-second-means", "pr": "https://github.com/objectstack-ai/objectstack/pull/21566", "session": "session_018zT8d8NpiQ1ExhuNd5TxY6", "premise_still_valid": true, "summary": "Premise re-measured on origin/main ce532184d: the anonymous form doors and the metadata-protocol anonymous-intake projection both judged a form on one declared switch only. Route 1 (enforce, no spec change): the rule for which form candidates a view opens to anonymous intake is now defined once in @objectstack/metadata-core (anonymousFormIntakeCandidates / anonymousFormIntakeSlugs / anonymousFormIntakeSlug / publicFormSlug) and read by both rest doors (findPublicFormView) and the protocol's org-scoped view-write refusal; the metadata-protocol local copy is deleted. The rule follows SharingConfigSchema as declared, defaults included, so raw and parsed bodies agree. Pinned both sides (unit + single and walled dogfood boots), ablated once. Patch changeset for metadata-core, metadata-protocol, rest; forms guide and the sharing.zod.ts header (plus its generated reference page) corrected. PR assignee set to the card's (hotlong) via label-write, read back matching; no labels written.", "tests": "All at HEAD 0164be245. metadata-core vitest 17 files / 311 passed; rest vitest 262 files / 5044 passed, 327 skipped; metadata-protocol vitest 206 passed + 3 skipped files / 3177 passed, 19 skipped; dogfood 4 public-form files / 17 passed (showcase single posture + walled posture). typecheck metadata-core, metadata-protocol, rest, dogfood: Done. dispatch-gates --commands: 114 run, 113 exit 0, check:dual-build-cjs-loads PREREQUISITE NOT MET (needs a full build) = NOT MEASURED, left to CI; --ran reconciliation 114/114. spec check:generated 15/15 current after gen:docs. eslint narrowed to the 12 changed .ts files: 12 files, 0 errors, 0 warnings (no parserOptions.project in eslint.config.mjs, so no type-aware rule can move an untouched file). Ablation: ablation-replace deleted the second-means check in metadata-core (anchor 1 -> 0, blob bf5a099a -> ad331202), rebuilt metadata-core, ablation-dist-preflight --absent: guard absent from all 12 dist files; rest 4 failed/12 passed (exactly the second-means cases), metadata-protocol 1 failed/22 passed, dogfood 2 failed/9 passed, metadata-core 5 failed/8 passed; all allowAnonymous pins and controls green. Restore: blob == HEAD, git diff HEAD empty, rebuilt, preflight found the guard back in 2 dist files, git status --porcelain empty.", "mcp_calls": "0", "api_writes": "3 — git push (not REST); fleet-write relay pr_create POST /repos/objectstack-ai/objectstack/pulls (PR 21566, draft); label-write relay POST /repos/objectstack-ai/objectstack/issues/21566/assignees; this os-dev-report comment POST /repos/objectstack-ai/objectstack/issues/21475/comments", "open_questions": [ { "question": "Upgrade note: a form whose sharing does not declare public sharing as the schema defines it stops being served anonymously. The changeset states this class-level only (confidentiality). Should the CHANGELOG carry the explicit key-level migration once the detail may be disclosed?", "options": ["A: amend the changeset text before release with the explicit key and the one-line fix", "B: keep it class-level and rely on the public forms guide"], "recommendation": "A, because the CHANGELOG is the text an upgrading author greps; the seat that holds the withheld detail decides when it can be said." }, { "question": "A published skill's description of the public-form opt-in now under-states it (Tier H surface, left out to keep this PR ungoverned). Who carries the follow-up?", "options": ["A: separate Tier H PR after this lands", "B: fold into this PR (makes it Tier H, no queue landing on green)"], "recommendation": "A, so the p1 fix can land through the queue; the follow-up is a small edit." } ], "out_of_scope_findings": [ "class: c · reach: named producer — the published objectstack-api skill describes the public-form opt-in with fewer keys than the doors now require, so an AI following it authors a form both anonymous doors answer 404 FORM_NOT_FOUND for · evidence: skills/objectstack-api/SKILL.md public form endpoints section vs anonymousFormIntakeSlug in packages/metadata-core/src/anonymous-form-intake.ts · dedupe words: public form skill opt-in, objectstack-api SKILL public forms, anonymous form sharing keys", "carrier: none · noted, not filed — spec migration/conversion registry prose (packages/spec/src/migrations/registry.ts, entries/semantic/17.ui-notification-action-embed-config-retired.ts) describes the public-form gate with fewer keys; already-shipped release text, not rewritten here", "carrier: none · noted, not filed — the sibling console's public-forms developer page lists published forms with its own reading of sharing (static read only, not measured at a public door)" ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsCorrection to the claim above:
Clause-②: yes (widening), notno.The fix moved the anonymous-intake rule into
@objectstack/metadata-coreand exports it from that package's index (publicFormSlug,anonymousFormIntakeSlug,anonymousFormIntakeCandidates,anonymousFormIntakeSlugs, typeAnonymousFormIntakeCandidate). A new export on a published package's index is a public-surface widening, soClause-②isyesand metadata-core is gradedminor(contract review on PR #21566, comment 5967245381). PR #21566's body and changeset now say so (head769594d9c). No other part of the claim changes.
Generated by Claude Code
- added a commit that references this issue
on Oct 7, 2026
QA-source: #21330 · access-security.public-form-intake · acceptance[6]
Sibling of #21331 (fixed by #21420) and #21468. Found while building #21468 and measured by the dispatching session on the 17.6.0 build
617f25f8: a second, separately declared way of withdrawing a public form from anonymous intake — saved successfully and shown as effective in the administrator's read — is not read by the anonymous intake doors, on every tenancy posture. Both doors keep serving and accepting the form (reproduced twice, env-wide save; still present onmainafter #21420).session_018zT8d8NpiQ1ExhuNd5TxY6) holds them.objectstack.Generated by Claude Code