Skip to content

Retire tenancy.organizationField from the authorable surface — one platform table's fact, not customer configuration #19054

Description

@hotlong

Ruling (maintainer, 2026-09-18, live chat, verbatim): 「organizationField 撤出可授权面 同意你的建议」 — filed on that word. The recommendation it accepts: take the key off the authorable surface and keep its one real use as a platform-internal fact, but do it on its own card, AFTER #18420 lands.

What is wrong today

tenancy.organizationField is an authorable key — packages/spec/authorable-surface/data.json carries data/TenancyConfig:organizationField, so any app author can declare it on any object. That means every future piece of organization-related logic has to consider "what if somebody set this?".

It does not earn that reach. Measured on main, the whole repository declares it once:

packages/platform-objects/src/identity/sys-api-key.object.ts:69
  tenancy: { enabled: false, organizationField: 'active_organization_id' },

Every other hit is a test fixture, a spec docblock, a generated JSON-schema, or a comment. Zero business objects declare it, and the spec's own docblock says why: "For ordinary objects the two coincide and organizationField is never needed."

Why sys_api_key needs it at all (⛔ do not "just use organization_id")

Two facts make this table genuinely different, and both must survive the change:

  1. It is managedBy: 'better-auth' (sys-api-key.object.ts:22). resolveInjectedSystemColumns bails on managedBy before tenancy is consulted, so the platform never injects organization_id here. The column that exists is better-auth's active_organization_id (:231).
  2. The table must not be walled. In this platform "has an organization_id column" IS the wall — computeTenantField / resolveTenantFieldName fall back to it and the Layer-0 wall exempts objects that lack it. Naming the column organization_id would wall the credential table on an equality that excludes NULL, and every pre-[finding] API keys carry no organization — under the isolated posture a minted key reads no org data at all (no leak, but the key surface is inert) #8287 key would vanish from its own owner's "My Keys" list. That is the defect [finding] API keys carry no organization — under the isolated posture a minted key reads no org data at all (no leak, but the key surface is inert) #8287 exists to have removed, and the object's own comment forbids the move in as many words.

So the key's whole job is: one unwalled table whose audit/approval/run rows still stamp the organization the row is about. That is a fact about a platform table we ship — not a knob customers need.

Proposed change

  1. Remove organizationField from TenancyConfigSchema (packages/spec/src/data/object.zod.ts) and add its row to TENANCY_RETIRED_KEY_GUIDANCE beside the two existing precedents (tenancy.strategy, tenancy.crossTenantAccess, both removed after v15.0 with a prescription each).
  2. Replace limb 0 with a platform-internal constant in packages/metadata-core/src/record-organization.ts — one row, sys_api_key → active_organization_id, read by the stamp face only. The three sanctioned platform-row writers (audit stamping, the approval-row writer, the automation-run recorder) keep their behaviour byte for byte; their pins should stay green untouched.
  3. ADR-0087 conversion entry — retiring an authorable key is a protocol change, and an app that declared it needs a rewrite or a named refusal rather than a silent drop.
  4. Regenerate: json-schema/**, authorable-surface/data.json, liveness/object.json (its organizationField evidence row moves or retires), the generated reference page.
  5. The wall face added by [Decision] group posture is an on-premise shape — should package-authored scheduled flows run under group with the switch on, and which organization do their writes carry? (ruling G item 3, reopened by the maintainer) #18378 (resolveRecordWallOrganizationField) is unaffected — it never read the key.

The playbook for all of this already exists in-repo: .claude/skills/spec-property-retirement.

Sequencing and risk

Acceptance criteria

Dedup terms

organizationField · TenancyConfig · stamp-only · sys_api_key active_organization_id · authorable surface retirement


Filed from the #18378 / PR #18420 work, where the key's scope-pin was the finding that blocked the PR until the design was changed to stop reading it.

Activity

  1. os-steve commented on Sep 21, 2026

    @os-steve
    Collaborator

    Claim: PM loop round 17
    Session: session_01AmH9bKvGoLjiY86Q4Z3og2
    Branch: claude/issue-19054-retire-tenancy-organization-field
    Worktree: objectstack-issue-19054
    Domain: domain:spec
    Seat: domain:spec#4
    File surface: packages/spec/src/data/ · packages/spec/src/conversions/ · packages/spec/src/migrations/ · packages/spec/authorable-surface/ · packages/spec/authorable-defaults/ · packages/spec/json-schema/ · packages/spec/liveness/ · packages/metadata-core/src/ · packages/platform-objects/src/ · content/docs/ · .changeset/ (stop on breach; explain in the report)
    Container & model: L, mode:subagent, model: opus build — quoting this run's --tier output: 「no path-derived mandate … floor sonnet · default opus · ceiling opus」 and 「Clause ② SUSPECT surface — a hint, not a verdict … packages/spec/src/** — the contract surface」
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: none — probed across all 10 non-bot open PRs' file lists for tenancy / organizationField: exit 1, 0 hits; lit control packages/spec on the same list: 50 hits; dark control zzznotapath: 0. PR #17076 is the changesets bot's release PR and holds no seat's lock.

    ⚠️ Reading-time: 2026-09-21T16:58Z.

    ⭐ The Clause-②: no above DEPARTS from this card's own body, deliberately

    The body says 「This card is itself Clause-②: yes (an authorable key leaves the published surface)」. That reading is superseded by the maintainer's own criterion, which is LATER than this card.

    The card was filed 2026-09-18T14:36:19Z. The maintainer's criterion, recorded verbatim on the seat post at ≈2026-09-18T21:2xZ, draws the line this way:

    条款② … 本卡放宽接受集或扩大公开面吗 — 单向:只管放宽/扩大。收窄不触发

    ⇒ 一张卡可以是契约面卡而条款② = no(例:纯收窄、纯退役)

    ⇒ this is a pure retirement, which narrows. It is unambiguously a contract-surface card — the criterion names retirement of a published writable key explicitly — and it is nonetheless Clause-②: no, because ⛔ nothing here puts a new key on a published payload: the guidance row is a prescription string, the ADR-0087 entry is a registry row, and the regenerated json-schema/** loses a key.

    ⛔ Judged from the card CONTENT, ⛔ not from the path hint — the --tier tool says in its own words that 「Clause ② is NOT reachable from paths」, and this seat has been wrong once this session by reading the hint as a verdict.

    ⚠️ If the actual diff contradicts this, say so in the report rather than working around it: the declaration is checked against the diff at the enqueue gate, and the correction channel is a Clause-②-correction: comment on this card, which only this seat can write.

    Precondition verified, ⛔ not adopted from the card

    The card says ⛔ do not start before #18420 lands. Two-leg ancestry on current main 5c5b67fc41:
    git merge-base --is-ancestor 0a56d3b5111a0877cf1ef20d271b911a98caed57 origin/main exits 0; control
    --is-ancestor 5c5b67fc4140… 0a56d3b5111a… exits 1, so the instrument discriminates. PR #18420 merged
    2026-09-18T15:34:06Z; card #18378 closed completed. Precondition satisfied.

    The card's central measurement re-taken on 5c5b67fc41, ⛔ not adopted

    leg reading
    packages/spec/authorable-surface/data.json:986 "data/TenancyConfig:organizationField" — still on the authorable baseline
    declarations in *.object.ts 1 — packages/platform-objects/src/identity/sys-api-key.object.ts:69, tenancy: { enabled: false, organizationField: 'active_organization_id' }. The other two .object.ts hits (sys-api-key.object.ts:49, sys-approval-request.object.ts:125) are comments, read line by line ⛔ not counted
    repo-wide hits 191, spread over plugins / services / metadata-core / spec / cli / docs — consistent with the card's 「every other hit is a fixture, docblock, generated schema or comment」
    TENANCY_RETIRED_KEY_GUIDANCE real, packages/spec/src/data/object.zod.ts:527, with the two precedents the card names

    ⇒ the card's premise holds at head. ⭐ This re-measure is not ceremony: this seat dispatched a card earlier today whose central measurement had been staled by a commit landing four hours after it was written.


    Generated by Claude Code

  2. self-assigned this
    on Sep 21, 2026
  3. os-steve commented on Sep 21, 2026

    @os-steve
    Collaborator

    os-dev-report

    {
      "issue": 19054,
      "status": "done",
      "branch": "claude/issue-19054-retire-tenancy-organization-field",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/19618",
      "session": "session_01AmH9bKvGoLjiY86Q4Z3og2",
      "premise_still_valid": true,
      "summary": "Retired object.tenancy.organizationField from the authorable surface at protocol 18 via the strict-deletion route: the key leaves TenancyConfigSchema's strictObject shape, TENANCY_RETIRED_KEY_GUIDANCE gains its prescription beside the two v15.0 precedents, D2 conversion object-tenancy-organization-field-removed plus its D3 chain step and RETIRED_KEYS_BY_MAJOR[18] entry register the removal, the authorable-surface row and the liveness ledger row are deleted deliberately in the same commit, and json-schema/docs/state-counts regenerate. Limb 0 of the shared platform-row resolver now reads PLATFORM_STAMP_ORGANIZATION_COLUMNS in @objectstack/metadata-core (one row, sys_api_key to active_organization_id, keyed by the object NAME the engine-bound face was asked about) instead of a declaration, so the three sanctioned writers keep their behaviour with no authorable input and api-surface stays byte-identical. sys_api_key keeps enabled:false, keeps active_organization_id, and is NOT renamed to organization_id. assignee was set by the PM dispatch and I never wrote it. THREE files outside the dispatched surface had to change because this diff makes a statement in them FALSE, declared not silent: alias-integrity.test.ts (RED: pins the tenancy guidance key set), plugin-security tenant-layer.test.ts (RED: asserted the declaration off the shipped object), plugin-audit audit-writers.test.ts (RED, two cases) plus a fourth, spec/src/automation/schedule-organization.zod.ts, whose docblock asserted the key wins a limb and PUBLISHES into content/docs/references. Each is named with its failing assertion in the PR's Acceptance notes.",
      "tests": "All readings at final commit 7cc0ca1b3d, working tree clean. SUITES (pnpm test per package, through scripts/pm/os-verify-lock.sh): @objectstack/spec 509 files / 14898 passed / 1 todo; @objectstack/metadata-core 16 / 285; @objectstack/platform-objects 53 / 848; @objectstack/plugin-security 117 / 2249; @objectstack/plugin-audit 25 / 363. TYPECHECK green for all five, test layers included. GATES: dispatch-gates.mjs --ran reconciles 114 derived / 114 run / 0 NOT-MEASURED / 0 UNRUN; check:generated reports 15 of 15 artifacts current; pnpm lint (eslint . --no-inline-config, WHOLE repo, no narrowing claimed) exit 0. THE THREE SANCTIONED WRITERS' PINS STAYED GREEN UNTOUCHED: plugin-approvals approval-node + backfill-platform-row-organizations 33 passed, service-automation suspended-run-store 52 passed, service-storage backfill-sys-file-organizations 15 passed, zero edits; driver-sql sql-driver-tenant-scope 36 and trigger-schedule time-relative-trigger 61 likewise green untouched. ABLATION (two legs, both committed first, both via scripts/ablation-replace.mjs, both restored with blob == HEAD and 'git diff HEAD' empty): (1) renamed the platform stamp row sys_api_key -> sys_api_key_ABLATED, anchor 1 -> 0, blob 0be02fdcc6b7 -> 21856a4a20d0 on disk, 4 of 19 metadata-core tests went RED; (2) replaced the prescription's first clause with placeholder text, anchor 1 -> 0, blob 2e9e19825ef6 -> eea8b4c7f061, the refusal pin went RED with \"expected 'Unrecognized key(s) on `tenancy`: `or...' to contain '`tenancy.organizationField` was remov...'\" - i.e. the pin measures the PRESCRIPTION, not merely that parse throws. Neither ablation goes through dist (both suites resolve the module from src), so no dist preflight applies. NOT MEASURED, with reasons: check-issue-citations' PR-scoped mode and check-changeset-no-major's clause-2 axis both need a pull_request payload no local run has (I ran the citations gate with --base origin/main instead, which DID measure - see findings); the 51 artifact-roster families, 11 wide-population families and 6 path-scheduled CI jobs dispatch-gates names are outside the derived 114 and are CI's. Environmental note: an early spec run reported '1 skipped' that the final run does not - the spec local project carries describe.skipIf(!RUNNABLE) suites under scripts/, so that count is a property of the shared box, not of this diff.",
      "mcp_calls": "0 - no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl",
      "api_writes": "2 - POST /repos/objectstack-ai/objectstack/pulls (draft PR 19618); POST /repos/objectstack-ai/objectstack/issues/19054/comments (this report). ZERO label writes: the dispatch named no label and skip-changeset does not apply (this PR ships a changeset), so the sanctioned set is empty. needs:contract-review is the seat's.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none - noted, not filed. Six test files outside the dispatched surface still author tenancy: { ..., organizationField: ... } in raw object-definition fixtures and are GREEN, because those fixtures feed drivers and engine doubles rather than TenancyConfigSchema: packages/drivers/driver-sql/src/sql-driver-tenant-scope.test.ts, packages/triggers/trigger-schedule/src/time-relative-trigger.test.ts, packages/plugins/plugin-approvals/src/approval-node.test.ts and .../backfill-platform-row-organizations.test.ts, packages/services/service-automation/src/suspended-run-store.test.ts, packages/services/service-storage/src/backfill-sys-file-organizations.test.ts. Their assertions stay true; what goes vacuous is the CLAIM that a read path is neutral about a key nobody can write. Not a defect and not classes a/b/c, so Acceptance notes only. Dedupe words: organizationField fixture residue, read-neutrality pin, retired key in fixture.",
        "carrier: none - noted, not filed. packages/lint/src/validate-object-field-refs.ts names tenancy.organizationField in its list of scalar field-ref pointers it deliberately does NOT judge; the list now mentions a key that cannot be authored. Prose only, no code reads it. Dedupe words: validate-object-field-refs scalar pointers, deferred field-ref axes.",
        "MEASUREMENT, not a card: check-issue-citations --base origin/main refused 12 citations this change adds, all of them #8778 or #8707, both allocated-but-absent (minted, at or below frontier 19616, absent from the board; deleted vs transferred NOT MEASURED). Both are PRE-EXISTING text that the diff only re-adds by rewriting the docblocks around them. Fixed per the gate's own prescription - the added lines now name the rulings in prose and cite the cloud record that resolves - and NO number was guessed. The standing occurrences on unchanged lines elsewhere in the tree are untouched; the gate's own header says it declines a tree-wide verdict on ~2,785 such sites by design, so this is not a card to file."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Sep 23, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 19054,
    "status": "done",
    "branch": "claude/issue-19054-retire-tenancy-organization-field",
    "pr": "#19618",
    "session": "session_01AmH9bKvGoLjiY86Q4Z3og2 (the Claude-Session line this run was given; subagent of the dispatching seat, so the parent's id)",
    "premise_still_valid": true,
    "summary": "Base-merge patch round on PR #19618. Remote head was confirmed at 7cc0ca1 before any change. The merge of origin/main 67add13 went through scripts/pm/os-regen-merge.sh. The head moved in three commits, and the third is a SEPARATE CONTENT CHANGE, not merge resolution. (1) bde765b: the merge commit (parents 7cc0ca1 and 67add13), with both registry conflicts resolved by hand. (2) 9d5fb0b: a regeneration-only commit that discharges the os-regen deferral on the two driver-deferred reference pages. (3) 3fb1a49: the scope extension the coordinator authorized mid-round. It changes the tombstone prescription in packages/spec/src/data/object.zod.ts:540 and its refusal pin in object.test.ts:1947 from '@objectstack/spec 18' to '@objectstack/spec 17', because check:future-spec-major arrived with the merge (#19655) and reddened CI. That commit edits the string contract-review AC2 pinned, so the head move is NOT regeneration-only. No rebase, no force-push, no amend. The PR stays draft, with no label, body, ready or auto-merge write. The plugin-audit org-actor to org-key control was left untouched.",
    "new_head": "3fb1a4994cb5cfe66f4d67f042213940a9158f4c (remote-confirmed with ls-remote; PR reads draft=true, mergeable=true, mergeable_state=clean, auto_merge=null)",
    "merged_main_sha": "67add1301aa2bce363953b3f424f416d43bf29f0 (os-regen-merge record main_tip and MERGE_HEAD agree). origin/main has since moved to 43e17b8 with 2 commits (#19733, #19776). Neither touches the two registries or object.zod.ts, and it was not merged.",
    "conflicts": [
    "packages/spec/src/conversions/registry.ts: resolved by hand. This is a NOT_DRIVER_MANAGED file with no generated markers. In CONVERSIONS_BY_MAJOR[18], main's translationPerAppSettingsRemoved is kept and this branch's objectTenancyOrganizationFieldRemoved is appended after it. The file's own rule reads 'Newest majors last; ordering within a major is application order', and the major-18 array is append-in-landing-order: #19600 landed first. Both conversion definitions text-merged without conflict.",
    "packages/spec/src/migrations/registry.ts: resolved by hand in the two hand-written regions, byte-exactly by script. step18.rationale: git merged the shared closing line 'selected and no walker can move that intent into the dataset. ' once, with its trailing space, because both sides made that edit identically. Then comes #19600's 13-line paragraph verbatim, except that its last line changed from "manifest's own English literal.", to "manifest's own English literal. " (trailing space inside the string, comma removed). Then comes this branch's 15-line paragraph, whose last line ends with ',. step18.conversionIds: 'translation-per-app-settings-removed' is kept, then 'object-tenancy-organization-field-removed' is added. That gives 33 ids, 33 distinct: main had 32 and the branch had 32. The os-generated regions were never hand-touched. check:migration-registry was green right after the merge and at the final head. The RETIRED_KEYS_BY_MAJOR[18] row 'data/TenancyConfig:organizationField' is present (count 1), and its entry file entries/retired-keys/18.data__TenancyConfig__organizationField.ts exists.",
    "content/docs/references/data/object.mdx and content/docs/references/system/migration.mdx: these did NOT text-conflict. The os-regen driver deferred them and silently kept the branch side. The os-regen-merge.sh rerun took main's side into the worktree. Then pnpm --filter @objectstack/spec build (includes gen:schema) and gen:docs regenerated them. Result compared with origin/main: the changed lines are identical to the branch's own pre-merge change to these two pages, which is the organizationField removal (8 lines).",
    "Instrument note: the dispatch's reading of 'several generated mdx conflicts' did not reproduce. A driver-free probe (a bare --shared clone with git merge-tree --write-tree) of 7cc0ca1 against 67add13, and against 70a2e75, exited 1 both times with exactly the two registries conflicted. object.mdx and migration.mdx text-merged cleanly (Auto-merging)."
    ],
    "both_parents_diffs": {
    "main_parent_67add1301a_to_merge": "2 files changed, 128 insertions(+), 1 deletion(-). This is the same stat as the branch's own diff against its old base 5c5b67f. Hunks, all this branch's: conversions @@ -10056 = the 97-line objectTenancyOrganizationFieldRemoved definition; conversions @@ -10160 = the array entry after translationPerAppSettingsRemoved; migrations @@ -5231 = #19600's last line re-terminated plus this branch's 15-line paragraph; migrations @@ -5265 = the added 'object-tenancy-organization-field-removed'; migrations @@ -14289 = the generated RETIRED_KEYS_BY_MAJOR[18] row plus its comment. The changed-line multiset differs from the branch's own diff only at the re-termination joint (1 removed and 1 added line on each side).",
    "branch_parent_7cc0ca1b3d_to_merge": "2 files changed, 656 insertions(+), 28 deletions(-). Main's own diff against the same base is 657(+)/29(-). The changed-line multiset equals main's own diff except at the joint: the dataset closing-line pair is absent because the branch already carries it, and #19600's last line reads 'literal. "' instead of 'literal.",'. Hunks are main's only: the translationPerAppSettingsRemoved definition @@ -7113 plus its array entry; migrations header @@ -38; #19600's paragraph @@ -5212; conversionIds @@ -5259; and the generated-region hunks from main's other merged PRs (#19610, #19635, #19752, #19493 and others).",
    "never_cited": "The git merge-tree exit code was not used as evidence for registry.ts. Every claim above comes from a diff of the committed merge against each parent."
    },
    "grep_counts_registry_ts": {
    "dataset. '": 1,
    "Finally, it splits the translation bundle type in two": 1,
    "this branch's phrase: PLATFORM_STAMP_ORGANIZATION_COLUMNSin@objectstack/metadata-core, keyed by object": 1, "'translation-per-app-settings-removed' inside step18.conversionIds": 1, "'object-tenancy-organization-field-removed' inside step18.conversionIds": 1, "control, must be absent: into the dataset.',": 0, "extra: English literal. \" = 1; control English literal.\", = 0; conflict markers = 0; 'data/TenancyConfig:organizationField' = 1": "as stated" }, "pr_own_diff_change_facts": "Measured as the PR's diff against new main (67add1301a) versus its diff against the old base (5c5b67fc41). Both span the same 22 files, +688/-287. (a) At merge+regen head 9d5fb0ba5f: the changed-line multisets are identical in 21 of 22 files. In migrations/registry.ts, 1 removed and 1 added line differ, because the trailing-space re-termination moved from the old dataset closing line (already on main) onto #19600's last line. The conversions array entry has the same text in a new position, after translationPerAppSettingsRemoved. The regenerated object.mdx and migration.mdx changed lines are identical. (b) At final head 3fb1a4994c: in addition, object.zod.ts and object.test.ts each differ by exactly 1 line ('spec 18' becomes 'spec 17'). That is content, not resolution or regeneration.", "tests": "Counts at the final head 3fb1a4994c. Exit codes were captured before any pipe; heavy runs went through os-verify-lock, whose VERDICT lines read command-exit 0. Merge leg: os-regen-merge.sh run 1 exit 1 (the designed conflict exit, which named both registries as class 3 and warned of a 30-line outside-region difference); rerun exit 1 (the designed step-3 refusal until regeneration: TAKING main's side of object.mdx and migration.mdx, KEEPING branch bytes of api/metadata.mdx, automation/schedule-organization.mdx, authorable-surface/data.json and liveness/state-counts.md); spec build exit 0; check:generated exit 1 with 1 stale (check:docs); gen:docs exit 0; check:generated exit 0 with 15/15 current; the regen commit's pre-commit reported the deferral discharged and the marker cleared; check:nul-bytes exit 0; git push exit 0 (7cc0ca1b3d..9d5fb0ba5f). Scope-extension leg: node scripts/check-future-spec-major.mjs exit 1 before the edit (2 problems, object.test.ts:1947 and object.zod.ts:540), exit 0 after. Lit control through scripts/ablation-replace.mjs: re-planting 'spec 18' in object.zod.ts moved the anchor count 1 to 0 and the blob 94991396c203 to c9ca0b0cb282, and the gate exited 1 with exactly 1 problem at object.zod.ts:540. Restore proven: blob == HEAD 94991396c203 and git diff HEAD empty; gate exit 0 again. git push exit 0 (9d5fb0ba5f..3fb1a4994c). At 3fb1a4994c: spec rebuild exit 0; check:generated exit 0 with 15/15 current; typecheck @objectstack/spec exit 0 (tsc + scripts-typecheck + test-typecheck, 53 files, 257 errors, 142 pinned signatures held); typecheck @objectstack/metadata-core exit 0; pnpm --filter '@objectstack/plugin-audit^...' build exit 0. Tests: @objectstack/spec full suite exit 0, 516 files, 15065 passed, 1 todo; src/data/object.test.ts alone exit 0, 201 passed; @objectstack/metadata-core exit 0, 16 files, 285 passed (same as the PR body); @objectstack/plugin-audit exit 0, 25 files, 363 passed (same as the PR body). Spec differs from the PR body (509 files / 14898 tests): +7 files is exactly the 7 spec test files main added in 5c5b67fc41..67add1301a (7 added, 15 modified, 0 deleted). Those 7 files hold 114 tests (measured, exit 0), and the remaining +53 come from main's 15 modified spec test files (not measured per file). Type Check source gates job: all 32 run lines of lint.yml's typecheck-source-gates job exit 0 at 3fb1a4994c, including check:future-spec-major and spec tsc --noEmit. Derived gates: dispatch-gates --commands derived 115 (22 paths, merge base 67add1301). dispatch-gates --ran exit 0: 115 accounted, 112 run with exit 0, 3 NOT-MEASURED, 0 UNRUN. NOT MEASURED: check:dual-build-cjs-loads, reason: whole-repo build prerequisite (55 packages without dist). NOT MEASURED: check:i18n, reason: cli plus 9-package build closure prerequisite. NOT MEASURED: check:type-check-debt, reason: packages/* build closure prerequisite (16 deps unbuilt). All three are exit 3 prerequisite refusals and belong to CI jobs that build first. Two first-pass readings were re-measured and are recorded as re-measured: check:skill-examples exit 3 (client-react unbuilt) became exit 0 after building client and client-react (259 examples type-check); check:merge-driver exit 1 became exit 0 when run plainly. The first result was caused by this runner injecting NODE_USE_ENV_PROXY=1: the undici experimental warning on stderr breaks check-regen-pending's empty-output assertion for the fast-forward push case.", "mcp_calls": "0", "api_writes": "1 REST write: POST /repos/objectstack-ai/objectstack/issues/19054/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Plus 2 git pushes, which are not REST (7cc0ca1b3d..9d5fb0ba5f and 9d5fb0ba5f..3fb1a4994c). REST reads only otherwise: issues/comments/5780847968 and pulls/19618 (twice).", "open_questions": [ { "question": "The dispatch and the role file disagree on GitHub writes. The dispatch says 'Your only GitHub write is the git push of the merge commit'. The role file requires the os-dev-report issue comment and says the role file wins on conflict. The coordinator's scope extension also required a second push.", "options": [ "A: treat the report comment as the role file's unconditional write (done)", "B: withhold the comment and deliver the final message only" ], "recommendation": "A. Reason: the role file states that it prevails and that GitHub is the authoritative report channel, and the dispatch itself allows REST writes through the scripts/pm tools. Stated here rather than chosen silently." }, { "question": "Several parts of the PR body are now stale: the Verification section (every number was taken at 7cc0ca1b3d, with spec at 509/14898), the dispatch-gates line (114/114), and the serial-collision section, which says registry.ts is generated and that neither side hand-resolves. The body is the seat's to write.", "options": [ "A: the seat rewrites Verification with the final-head figures above and records the future-spec-major change as its own item", "B: leave the body and let the report comment carry the figures" ], "recommendation": "A. Reason: a reviewer reads the body. The tombstone wording change reaches the contract under review." } ], "out_of_scope_findings": [ "not filed, Acceptance notes only (prose nit, outside classes a/b/c): step18.rationale now has two consecutive 'Finally' paragraphs. #19600's 'Finally, it splits the translation bundle type in two' is followed by this branch's 'Finally it retires object tenancy.organizationField`', and there is a pre-existing mid-text 'Finally, it removes aria' too. Both were kept verbatim per 'drop no text'. carrier: #19657 (the third participant on this tail line) will touch the same region.",
    "not filed, observation: this worktree's pnpm install ran prepare (scripts/setup-git-hooks.mjs), which rewrote the SHARED .git/config core.hooksPath from '/home/user/objectstack/.githooks' to '.githooks'. The relative value is that script's designed value; the absolute one was set by someone else earlier. carrier: none."
    ]
    }


    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

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions