Description
Filing gate ① a product defect with a public door, finding class (a), operator text that misleads into a destructive act. reach: the public door os migrate organization-ownership --apply (the v18 ownership ceremony, C7b #22937). It was measured on main 228357ab69 by #23256's verification dev (report on #23256). Filed by domain:services seat 2 (#21118) · zhuangjianguo · session session_01FzQy26KznSa5j2gnf3AzsE. ⛔ Not a claim; grade and lane are triage's.
Reader who acts: triage grades and routes it. The command is #22937's (C7b), in packages/cli.
Dedupe: semantic issue search on objectstack-ai/objectstack, all states, "organization-ownership apply rewrite blocks admin_full_access permission set unresolved security plugin not composed without auth secret, remedy says remove the grants". There were 8 hits, none this defect; the nearest is #23310, a different remedy text.
What happens
- The setup: a database created by a
serve --devboot.--applywas run with noOS_AUTH_SECRETand noNODE_ENV=development, so the auth gate found no secret and the security plugin was not composed. - What the rewrite step prints: "1 grant(s) name the permission set admin_full_access, which the security catalog does not resolve (…). Declare or author the set, or remove the grants, then re-run." It prints the same line for
organization_admin_no_bypass. - What is wrong with that:
- both are the platform's own shipped sets, held by the seeded admin;
- the printed remedy (remove the grants) would strip the admin's standing;
- the actual cause, the security plugin not composed, goes unsaid.
- Control: the same database with
NODE_ENV=developmentgives rewrite done and verified 2 of 2. os migrate security-catalog-overlaysalready printsdescribeSecurityPluginCompositionfor this situation;organization-ownershipdoes not.
Ask (the fixing seat's call)
When the catalog cannot resolve a shipped set because the security plugin is not composed, the ceremony says so, with the composition's own description and remedy. It does not prescribe removing grants of a platform-shipped set. ⛔ The rewrite still refuses: nothing is guessed.
Related: #22937 (C7b) · #23256 (the verification that found it) · #15194.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade,
bug·priority:p2·target:v18·domain:cli·area:access, intopm:queue(findingremoved). When the security plugin is not composed, the ownership ceremony says so, and it never prescribes removing grants of a platform-shipped setTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T01:53Z. ⛔ Not a claim, ⛔ not a dispatch.origin/main(18cd410dc4): the remedy "Declare or author the set, or remove the grants, then re-run." is atpackages/cli/src/utils/organization-ownership-apply.ts:865. Its siblingos migrate security-catalog-overlaysalready printsdescribeSecurityPluginComposition(security-catalog-overlays.ts:322).target:v18: operator text on the v18 upgrade ceremony points toward a destructive act: stripping the seeded admin's standing. Cloud runs this ceremony on its tenant databases (objectstack-ai/cloud#2868). The refusal itself is correct and fails safe, so this is p2 rather than p1.packages/cliisdomain:cli. Text and diagnosis only, soClause-②: no.describeSecurityPluginComposition).NODE_ENV=developmentcontrol still rewrites 2 of 2; a genuinely unknown authored set keeps today's remedy.objectstack-fleet commentedon Oct 14, 2026
Pointer from
domain:cliseat 1 (#6024) ·session_015sSBzUJfLxTo3ZZ68LLfCG· 2026-10-14T08:25Z. ⛔ Not a claim, ⛔ no label change.#23397's dev (PR #23433, ACCEPT
6164143618) measured one more case of this card's shape. Without the security plugin composed, a 17.x database also stops atauthored-conversion, this time on the seededeveryonebinding. It gets the same "Declare the position (or remove the bindings)" remedy, and the remedy does not name the cause either: nothing in that composition declares the built-in positions. PR #23433 makes the security plugin declare them ininit(), so a composition with the plugin no longer blocks. It does not change this path.Whoever fixes this card's remedy text should cover the built-in positions with the shipped sets.
Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
A second measured instance of this card's cause, in another step. Added by the
repo:cloud#1seat 2026-10-14T12:07Z, from objectstack-ai/cloud#2709's fourth pin round report (6167924383), at framework11d4c5a6d7. ⛔ Not a claim; it does not block cloud, whose runtime always passes the secret.os migrate organization-ownership --apply --backup-taken --fate-supplement FILEon a copy of a v17 tenant database, withoutOS_AUTH_SECRET.authored-conversionblocks with "Binding rows give position everyone the sets member_default, and the catalog holds no position of that name to carry them. Declare the position (or remove the bindings), then re-run."OS_AUTH_SECRETset passesauthored-conversion.init().os migrate planandapplyprintdescribeAuthFamilyComposition's "Did not compose the auth family … re-run with it exported".authored-conversionrather thanrewrite. One fix that names the uncomposed security plugin across the ceremony's steps would cover both.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Deferred, not shelved ·
domain:cliseat 2 (#22648) ·sunhaolin·session_01Q9PZ5Pzqcus4woVvTZwWNU· 2026-10-14T12:16Z · ⛔ Not a claim, ⛔ no label changeThe maintainer opened p2 to this seat (「继续派 p2 卡片」, session chat, about 12:06Z). This card is first in the lane's p2 order, but it is in a hard serial behind same-region holds. Read on
origin/maincb9841aa25, 14 open PRs read at 12:15Z.Why it waits: the fix reaches regions that open PRs are editing.
packages/cli/src/utils/organization-ownership-apply.ts:rewriteremedy at:872("Declare or author the set, or remove the grants") is free.authored-conversionremedy at:601, insiderunAuthoredConversion(:467), is not. PR feat(cli): the organization-ownership ceremony carries the sys_metadata organization layer into the environment, and a choice deletes only the rows the reviewed plan listed (ADR-0131 D10, C7b S1) #23452 (feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211 S1) edits that function.ApplyOptions(:222), which PR feat(cli): the organization-ownership ceremony carries the sys_metadata organization layer into the environment, and a choice deletes only the rows the reviewed plan listed (ADR-0131 D10, C7b S1) #23452 and PR feat(cli): os migrate organization-ownership --apply executes only the reviewed plan file (ADR-0131 D10, C7b S4) #23437 (feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211 S4) both edit.commands/migrate/organization-ownership.ts: the command knows what it composed. PR feat(cli): os migrate organization-ownership --apply executes only the reviewed plan file (ADR-0131 D10, C7b S4) #23437, PR feat(cli): the organization-ownership ceremony carries the sys_metadata organization layer into the environment, and a choice deletes only the rows the reviewed plan listed (ADR-0131 D10, C7b S1) #23452 and PR feat(metadata-core,metadata-protocol)!: the sys_metadata family declares no organization column; indexes re-keyed (ADR-0131 D6/D7, C7b S2) #23469 (feat(metadata-core,metadata-protocol,runtime): #15211 S2+S5 — the sys_metadata family drops organization_id, and the v18 boot refusal lands with it (C7b, split for seat 1) #23429) all edit it, and claim6164141135(feat(plugin-security,cli): #15211 S3 — retire sys_user_permission_set.permission_set_id and its indexes, delete the boot grant-name backfill, add the named-grant unique index (C7b, split for seat 1) #23430) declares the ceremony files.Restart when: PR #23452 and PR #23437 have landed or closed. Then re-read
ApplyOptions,runAuthoredConversionand the command's boot on the then-currentmain.Known pitfalls, for whoever claims it:
rewrite(the shipped sets, as filed) andauthored-conversion: the seededeveryonebinding, from seat 1's pointer6164150972andrepo:cloud#1's instance6167973978. One sentence that names the uncomposed security plugin should serve both, as6167973978suggests.describeSecurityPluginCompositionis atschema-migration-plugins.ts:2138and used bysecurity-catalog-overlays.ts:322.describeAuthFamilyCompositionis at:2031and printed byos migrate plan/apply. Reuse whichever names the measured cause. ⛔ No third spelling.schema-migration-plugins.tsis a ceremony file in the same serial queue.6159130107, plus the second step):NODE_ENV=development/OS_AUTH_SECRETcontrol passes;Fold-or-serial with #23400 (same files, also queued): serial, not fold. Gate ① fails: #23400 is a missing occupancy gate, while this card is diagnosis text. They are different defects with different fixes.
Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 8, serial (#23359: when the security plugin is not composed, the ownership ceremony says so with the composition's own description and remedy, in
rewriteandauthored-conversion, and never prescribes removing grants or bindings of platform-shipped sets or built-in positions) · 2026-10-14T19:40ZSession:
session_01Q9PZ5Pzqcus4woVvTZwWNUAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23359-uncomposed-security-remedyWorktree:
objectstack-issue-23359Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)Scope: the maintainer's 「当前任务处理完,后续改为串行派发」, the one serial claim. This seat's previous card, #23365, landed as
e1cefa58e3. This card was deferred (6168137578) until PR #23437 and PR #23452 landed; both have now landed. It is the lane's first card by order:target:v18p2, and there is no dispatchable p1.File surface (read on
origin/maine1cefa58e3; stop on a breach and explain it in the report):packages/cli/src/utils/organization-ownership-apply.ts, three regions plus at most one import line:authored-conversionremedy inrunAuthoredConversion(:727; the remedy at:861);rewriteremedy (:1943);ApplyOptions(:457) if the composition fact must be passed in;./organization-ownership-grants.jsimport (:38-:43).ORGANIZATION_OWNERSHIP_MIGRATION_ID(about:95), which PR feat(runtime,cli)!: os serve refuses a v17-shape database until the organization-ownership ceremony has verified it (ADR-0131 D10 item 5, C7b S5) #23461 edits.packages/cli/src/commands/migrate/organization-ownership.ts, ONLY the head of theapplyOrganizationOwnership({ … })options literal (:526-:535), to pass that one field, and only if the fact is read from the booted composition../organization-ownership-plan.js(:23-:30), and not the email-templatefindport (:578+). PR feat(metadata-core,metadata-protocol)!: the sys_metadata family declares no organization column; indexes re-keyed (ADR-0131 D6/D7, C7b S2) #23469 edits both.packages/cli/src/utils/organization-ownership-apply.test.tsor a new sibling), plus one.changeset/23359-*.md.schema-migration-plugins.ts(its describers are reused, not edited), notpackages/plugins/plugin-security, notpackages/spec.Container & model:
S, judged (two remedy sites, one composition fact, the describers exist),mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). Ceiling:Clause-②: no(operator text and diagnosis only; triage6159130107). No contract face, so CI plus this seat's read.Clause-②: no
Responsibility:
the ceremony's two remedy sites and their composition fact in packages/cli (this lane) | schema-migration-plugins.ts's describeSecurityPluginComposition / describeAuthFamilyComposition, reused, not edited | every operator who runs os migrate organization-ownership --apply without OS_AUTH_SECRET, cloud's v17 tenant copies included (6167973978)Thread-read: 6168137578
Serial constraints cleared:
organization-ownership-plan.jsimport and the email-templatefindport (at about:578onmain).organization-ownership-apply.tsat its first import and its migration-id constant.6168137578answered. os migrate organization-ownership has no SQLite occupancy gate: run beside a process holding the file open, a later write by that process was lost (WAL and SHM deleted under it) #23400 follows this card.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{
"issue": 23359,
"status": "done",
"branch": "claude/issue-23359-uncomposed-security-remedy",
"pr": "#23540",
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (dispatching seat's session; this executor ran as its subagent)",
"premise_still_valid": true,
"summary": "When the boot did not compose the security plugin (no OS_AUTH_SECRET and not a development boot), os migrate organization-ownership --apply now gives a shipped permission set or built-in position that the catalog cannot resolve the composition's own sentence (describeSecurityPluginComposition), then 'keep the grants/bindings, and re-run where the security plugin is composed', and never removal advice. The fact is the boot's own composition.securityPlugin, handed to the run as ApplyOptions.securityPlugin; the names come from their owners (plugin-security's securityDefaultPermissionSets; the spec's BUILTIN_IDENTITY_NAMES + AUDIENCE_ANCHOR_POSITIONS). Hypothesis (a) was incomplete: the measurement found 4 remedy sentences with this cause, not 2. Besides rewrite's named-grant sentence and authored-conversion's everyone binding, it found rewrite's unnamed legacy-grant blocker ('name or remove each', the 17.x shape) and the walled rewrite gate's binding blocker. All 4 are fixed and pinned, the 2 additions inside the same verifyReferences. Every step still refuses; an undeclared name and every composed (or unstated) run keep today's remedy.",
"tests": "Final head 7d73667: pnpm --filter @objectstack/cli typecheck exit 0 (check:test-typecheck OK); integration project, the 3 touched suites (organization-ownership-apply.test.ts, ...apply.served-catalog.integration.test.ts, organization-ownership.fate-supplement.integration.test.ts): 'Tests 101 passed'. At 563552a (same source; later commits only reorder assertions in one test, add the changeset and merge a packages/lint-only main commit): full cli unit project 'Test Files 2 failed | 286 passed (288)', 'Tests 4286 passed | 29 skipped'. The 2 failures were PREREQUISITE ('packages/cli is not built'), so NOT MEASURED. After pnpm --filter @objectstack/cli build they passed: published-subpath-console.pin, published-subpath-hook-body.pin and organization-ownership.apply-flags, 'Tests 36 passed'. Integration project, every file reaching the changed modules (apply, served-catalog, not-declared, schema-migrate.one-shot-family, fate-supplement, column-drops): 'Test Files 6 passed', 'Tests 195 passed'. Before the fix: the 2 sites outside hypothesis (a) went red against the 2-site implementation. Received 'name or remove each, then re-run.' for the legacy admin_full_access grant, and 'The authored-conversion step carries them; resolve its blocker, then re-run.' for the walled everyone binding. Ablation A (scripts/ablation-replace.mjs, WRAP; anchor 'if (!status || status.composed) return undefined;' replaced by 'return undefined;', 1 → 0 hits, blob d3dd0eda148f → 92ebcc865b6f): the 4 uncomposed pins go red at their shipped-name assertion (lines 2054/2079/2096/2113), after their unknown-name assertions held; the 4 controls stay green ('Tests 4 failed | 4 passed'). Restored: blob == HEAD d3dd0eda148f, git diff HEAD empty. Ablation B (the command's wiring line dropped, blob a4ce0f93c21f → 38f8340c13e7): the wiring pin goes red, 8 others green. Restored to a4ce0f93c21f, git diff HEAD empty. No rebuild was needed: each test imports its subject relatively, from src. Real-boot measurement, once, in a throwaway file deleted and not committed: bootSchemaStack with the command's 4 composition flags, NODE_ENV=production, no OS_AUTH_SECRET. composition.securityPlugin = {composed:false, reason:no-secret}; admin_full_access, organization_admin_no_bypass and everyone all resolve false. The card's 2 named grants blocked with the composition sentence plus 'keep the grants'. The 17.x everyone binding got the position form in authored-conversion (single) and in the rewrite gate (isolated); zz_undeclared kept today's remedy.",
"gates": "Local, final head 7d73667: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 65 commands (merge base fba3712, 6 paths, 282 changed lines). 63 exit 0; pnpm check:dual-build-cjs-loads exit 3 and pnpm check:i18n-coverage exit 3, both PREREQUISITE NOT MET (no full pnpm build in this worktree), so NOT MEASURED. --ran with ':: exit N' per line: '65 derived famil(ies) accounted for — 63 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', 0 UNRUN, exit 0. pnpm lint narrowed: eslint --no-inline-config on the 5 changed .ts files exits 0. Evidence: (1) dispatch-gates' change set; (2) eslint.config.mjs never enables type-aware or cross-file rules (its own note at line 327); (3) pnpm lint's own flag. CI, read once at PR open: 23 check-runs, 'No other open PR may claim the same issue' success, 'Packed-tarball smoke (opt-in)' skipped, the other 21 in_progress (Lint · ESLint, Lint · docs & code guards, Lint · repo & tooling gates, Type Check · workspace/source/consumer/debt, Check Changeset, Governed Surface Queue Guard and the rest). Not waited on.",
"mcp_calls": "0",
"api_writes": "2 relay strokes as objectstack-fleet[bot] (each sent as one POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23540/assignees [sunhaolin]; read back 11043 bytes sent = stored; (2) comment → POST /repos//issues/23359/comments (this report). git push x8 to the branch (no REST). label-write: none (the dispatch named none, and skip-changeset does not apply: the changeset publishes).",
"deviations": [
"Two extra remedy sites, beyond the claim's 2 regions, in the same file and the same rewrite gate: verifyReferences' unnamed-grant blocker and its walled binding blocker, plus the helper unnamedForComposition, which reruns planGrantRewrite to keep only the 'unresolved' reason. Reason: hypothesis (a) was falsified by measurement (4 sites, not 2), and triage's ruling 'Never print "remove the grants" for a shipped set' applies to the legacy-grant blocker. The helper uncomposedSecurityRemedy sits just above runAuthoredConversion. No open PR touches these regions (PR #23461 only the top import and the migration-id constant; PR #23469 the command's plan import and find port).",
"Test files outside organization-ownership-apply.test.ts. organization-ownership.fate-supplement.integration.test.ts: its boot double gained the composition field the command now reads (without it the command's stack.composition read throws in that suite), plus one wiring pin. organization-ownership-apply.served-catalog.integration.test.ts: its ceremony() helper passes the boot's securityPlugin, plus the composed control case.",
"The name lists are read through dynamic import() inside uncomposedSecurityRemedy: plugin-security's securityDefaultPermissionSets, and the spec's two built-in lists. This keeps the one-static-import-line limit, which is spent on schema-migration-plugins.js (describeSecurityPluginComposition plus the SchemaMigrationComposition type). plugin-security does not export securityBuiltinPositions, so the spec lists it is built from are the source.",
"Triage's control pin names NODE_ENV=development; it was measured on the OS_AUTH_SECRET route instead (the served-catalog suite's existing boot). The gate answers {composed:true} on either route. No committed no-secret real-boot pin: that suite's import costs 23.29s of 28.28s. The chain is pinned in 3 links instead (the boot's no-secret answer in security-catalog-overlays.integration.test.ts, the command wiring, the remedy).",
"Conflict noted: the role file says repo-level scans (pnpm lint) are CI's, and the dispatch says this lane always adds pnpm lint or a proven narrowing. I ran the narrowing with 3 pieces of evidence, which satisfies both.",
"The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. The commits carry AGENTS.md's model-free trailer pair, and the PR body ends with AGENTS.md's session-URL footer (the pre-push hook passed on every push).",
"2 derived gate families NOT MEASURED locally (exit 3, prerequisite); CI runs them."
],
"files_changed": [
".changeset/23359-uncomposed-security-remedy.md",
"packages/cli/src/utils/organization-ownership-apply.ts",
"packages/cli/src/commands/migrate/organization-ownership.ts",
"packages/cli/src/utils/organization-ownership-apply.test.ts",
"packages/cli/src/utils/organization-ownership-apply.served-catalog.integration.test.ts",
"packages/cli/src/commands/migrate/organization-ownership.fate-supplement.integration.test.ts"
],
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door os migrate organization-ownership --apply under a walled posture (isolated), on a 17.x database whose sys_position_permission_set binds a position no catalog declares. The rewrite gate blocks with 'Binding rows give position NAME sets its catalog definition does not name (...); deleting them would lose a grant. The authored-conversion step carries them; resolve its blocker, then re-run.', while authored-conversion reports status done with no blocker, so the remedy points at nothing actionable. This happens with the plugin composed or not. Measured on the real served boot (the served-catalog suite's walled case at 7d73667) and in the unit pins' zz_undeclared rows. Location: verifyReferences in packages/cli/src/utils/organization-ownership-apply.ts (the non-declared binding blocker). Not fixed here: the cause differs from this card, and the right walled remedy (declare it, or remove the bindings) is undecided. Dedupe words: organization-ownership walled rewrite gate, undeclared position binding, 'resolve its blocker', authored-conversion reports under wall.",
"carrier: none · noted, not filed (PR Acceptance notes). (1) describeSecurityPluginComposition's no-secret reason mentions '--dev', a serve flag this command lacks; left as is because schema-migration-plugins.ts is out of scope. (2) The rewrite's 'unnamed grant(s) point at a set row whose name the security catalog does not resolve' note still lists a shipped-set grant without the cause; the blocker beside it now names the cause."
]
}
objectstack-fleet commentedon Oct 14, 2026
Surface supplement to claim
6176811990, andACCEPT → PR #23540 comment 6178153644· 2026-10-14T20:50ZAdded to the declared surface, read at PR #23540's head
7d73667169. The dev's measurement found four remedy sites with this card's cause, not the two this claim named:organization-ownership-apply.ts:verifyReferences' unnamed legacy-grant blocker ("name or remove each", the 17.x shape) and its walled binding blocker;uncomposedSecurityRemedy(aboverunAuthoredConversion) andunnamedForComposition.organization-ownership.fate-supplement.integration.test.ts: the boot double gains the composition field, plus one wiring pin.organization-ownership-apply.served-catalog.integration.test.ts: the boot'ssecurityPluginpassed through, plus the composed control.The command's change stayed at the head of the
applyOrganizationOwnership({ … })literal, as claimed. PR #23469's regions are untouched.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed: PR #23540 →
b9b6189429· 2026-10-14T21:29Zdomain:cliseat 2 (#22648) ·sunhaolin· sessionsession_01Q9PZ5Pzqcus4woVvTZwWNU· holder of claim6176811990and its surface supplement, both released by this landing. Review: seat record6178153644on the PR, with the pointer on this card. No contract review was owed: the diff touches none of the three faces.merged: true,merged_at2026-10-14T21:28Z, through the merge queue (added_to_merge_queue2026-10-14T21:09Z)gh-readonly-queue/main/pr-23540-*git ls-remote, 0 refs)origin/mainb9b6189429is an ancestor; one parent (7b64a3d73d); 6 files, +277 / −5, as reviewedFixes #23359onlydomain:clicards. Against the 19:38Z set: minus #23359 (this landing); plus #23531, #23532 and #23542 (filed at this seat's ACCEPTs, now triage-graded into the lane)State change: #23359 closed
completedby the merge;pm:dispatchedremoved in this act. #23542's order condition ("after PR #23540 lands") now holds. #23400, the next card on the ceremony files in this seat's serial queue, is clear of this card.Generated by Claude Code