Skip to content

security(forms): withdrawing a public form from anonymous intake on a walled (and degraded walled) tenancy posture — follow-up to #21331, detail withheld pending maintainer #21468

Description

@objectstack-fleet

QA-source: #21330 · access-security.public-form-intake · acceptance[6]

Follow-up to #21331 (fix PR #21420). That fix makes withdrawing a public form from anonymous intake take effect on a single-posture deployment, and deliberately leaves two deployment shapes unchanged:

  • the walled tenancy posture;
  • the degraded walled posture (OS_ALLOW_DEGRADED_TENANCY=1).

On those shapes the same both-sides contract (a withdrawn form refuses anonymous intake on every door; a published one still accepts it) is not yet guaranteed.

  • Class: authorization (fails open). Under RUNNER rule 2 the mechanism stays withheld pending maintainer; the dispatching session (session_018zT8d8NpiQ1ExhuNd5TxY6) holds it.
  • Direction, chosen by the maintainer in that session (verbatim: 「按照你的建议继续,开发完整就进队列合并」, accepting the recommendation): where the deployment cannot honour a withdrawal on the anonymous doors, the withdrawal is refused loudly at authoring time rather than saved as a state the doors do not read. No working intake on a walled deployment closes by surprise.
  • Pins: both sides on a walled boot — the refused authoring write names the reason; whatever withdrawal the posture does accept closes every anonymous door.
  • Owning repo: objectstack.

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (follow-up of #21331, dispatched on the maintainer's direct order)
    Session: session_018zT8d8NpiQ1ExhuNd5TxY6
    Account: hotlong (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-21468-walled-form-withdrawal
    Worktree: objectstack-issue-21468
    Domain: domain:services (inherited from the in-flight parent #21331, whose fix PR #21420 is queued); expected surface packages/rest and the metadata write door, the cross-lane surface triage named on #21331
    File surface: packages/rest/src, packages/metadata-protocol/src (only the view save door, if the refusal belongs there), packages/qa/dogfood/test, .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (default tier; no path-derived mandate)
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: #21420 (same file packages/rest/src/rest-server.ts; this branch starts after it lands, or on its head if still queued)

    Provenance: the maintainer, in Claude Code session session_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-02, verbatim: 「按照你的建议继续,开发完整就进队列合并」 and 「同意, 合并你可以做。」. Labels set by this seat at filing because the card is a derived follow-up of #21331 (same class, same lane); triage may re-route. The withheld detail stays off this card and its PR.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21468,
    "status": "done",
    "branch": "claude/issue-21468-walled-form-withdrawal",
    "pr": "#21473",
    "session": "session_018zT8d8NpiQ1ExhuNd5TxY6",
    "premise_still_valid": true,
    "summary": "Implemented the accepted direction: on a posture where the anonymous form doors cannot honour an organization-scoped change (walled, degraded or not), an organization-scoped view save, draft save or draft promotion that changes which slugs a form opens to anonymous intake is refused 403 NOT_OVERRIDABLE with the reason and the remedy (save it env-wide). The seat is the metadata-protocol save door (saveMetaItem plus the promotion path), reading the tenancy service; no spec change and no new error code (the sibling org-scope refusal's code and status are reused), so Clause-2 stays no. Branched off the head of claude/issue-21331-public-form-withdrawal (4d4d342) because #21420 had not merged at dispatch; this PR's own diff does not touch packages/rest/src/rest-server.ts. AGENTS.md section 2 says stacked series are unsupported; the dispatch ordered this base, so the stacking is stated in the PR body and the branch needs one merge of origin/main after #21420 lands.",
    "tests": "At b5e4b51: metadata-protocol full suite 201 files passed / 3 skipped, 2990 tests passed / 19 skipped; new unit block in protocol.org-scoped-write-refused.test.ts 22/22 green (4 refusal cases, 3 controls); new dogfood pin public-form-withdrawal-walled.dogfood.test.ts 5/5 green on a multiTenant posture-only boot (org-scoped withdrawal 403 with nothing saved, org-scoped non-sharing edit 200, env-wide withdrawal makes both doors answer 404 FORM_NOT_FOUND with no row landing, republish restores 200/201); single-posture pins from #21420 plus showcase-public-form and public-form-read-back-masking all green (9 + 6 tests). typecheck green for metadata-protocol and dogfood. Ablation on the committed head: the guard was made to always return null via scripts/ablation-replace.mjs with a restore trap, the package was rebuilt, and ablation-dist-preflight found the marker in 2 dist files. Unit 4 red / 18 green; dogfood 1 red / 4 green. Restore: blob equals HEAD and git diff HEAD is empty; after the rebuild the marker is absent from all 24 dist files. dispatch-gates derived 70 families, all run: 68 exit 0; check-plugin-teardown-shape --self-test exited 3 on the shallow clone and 0 after fetching its pinned commit; check:dual-build-cjs-loads is NOT MEASURED (exit 3, PREREQUISITE NOT MET, unrelated packages have no dist). pnpm lint is NOT MEASURED locally and is declared to CI. CI: in_progress at report time.",
    "mcp_calls": "0",
    "api_writes": "3 relay writes (fleet-write dispatch): pr_create POST /repos/objectstack-ai/objectstack/pulls (draft #21473); assign POST /repos//issues/21473/assignees (hotlong, read back MATCHES); comment POST /repos//issues/21468/comments (this report). Plus git pushes of the branch (not REST).",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: POST /forms/SLUG/submit on a walled posture (multiTenant posture-only boot) answers 500 ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED for a published public form bound to a tenant-scoped object (the showcase contact form). GET of the same form serves 200, so the form looks published while every submit fails · evidence: the first run of public-form-withdrawal-walled.dogfood.test.ts on the showcase form, engine.ts resolveSystemInsertOrganization · dedupe words: public form submit walled 500, SystemWriteOrganizationRequired anonymous submit, form intake tenant-scoped object",
    "class: b · reach: withheld, authorization class; specifics delivered privately to the dispatching session · dedupe words: withheld",
    "carrier: dispatching seat (already noted in the dispatch) · the GET fail-closed answer is 500 FORM_RESOLVE_FAILED while submit answers 503 SERVICE_UNAVAILABLE for the same outcome · noted, not filed"
    ]
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — PR #21473 (landing when CI is green on the merged head)

    Reviewing seat: the dispatching session session_018zT8d8NpiQ1ExhuNd5TxY6 (class-level only).

    Landing on the maintainer's instruction (verbatim): 「按照你的建议继续,开发完整就进队列合并」 / 「同意, 合并你可以做。」 — ready + auto-merge once CI on 296d7342 is green.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:servicespriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions