Conversation
Strict removal from TenancyConfigSchema + guidance row, the D2/D3 registration, the liveness ledger row, and the platform-internal stamp table that replaces limb 0 in metadata-core. Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude <noreply@anthropic.com>
…lsifies Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude <noreply@anthropic.com>
check-issue-citations judged 12 citations this change adds; #8778 and #8707 are allocated-but-absent on the board. The rulings they named are cited in prose and by the cloud record that does resolve. Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 4 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 — 136 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 7575cbdfca7c5cfb63f2076effc62001dd9e9827 && git checkout 7575cbdfca7c5cfb63f2076effc62001dd9e9827
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9cc5010c708c55d9a0d0c58d955e4b0293cef904 7cc0ca1b3dc996a7e667cc90185ffdec2fd8559d && git checkout -B drift-repro 9cc5010c708c55d9a0d0c58d955e4b0293cef904 && git merge --no-ff 7cc0ca1b3dc996a7e667cc90185ffdec2fd8559d
node scripts/docs-audit/affected-docs.mjs --json 9cc5010c708c55d9a0d0c58d955e4b0293cef904
|
Contract reviewServed-tier: Reviewed from the diff and the tree, not from the PR narration. Every zero below is paired with the radius it was taken over plus a known target outside it. ① Derived judgmentsAC1 — met. AC2 — met, and it measures the prescription. I traced the channel rather than taking the ablation's word: AC3 — met. The end-to-end half I verified by reading the other two writers' fixtures rather than trusting "untouched": AC4 — met. D2 AC5 — ⭐ NOT met as the card words it. This is the finding. Sanctioned writer #1's pin was edited:
I hold this as a declared, forced finding rather than a block, on three measured grounds: the shape is unreachable for the shipped table ( ② Semver level
③ Boundary flags1. The serial collision is accurately declared, and the file is genuinely generated. #19610's file list does contain 2. The four out-of-surface files — each forced, none bent.
3. The ablations prove what they claim — with one radius named. The second is independently corroborated from source: the refusal pin carries five 4. The unresolvable citations — re-measured, and the PR's claim about them is imprecise. 5. The residue list checks out; one item it does not name. The three sanctioned writers' fixtures still declare the retired key but stay green for the reason given in ① (all keyed 6. CI state at review time (2026-09-21T18:43Z), measured at this head rather than taken from the PR. 39 check runs: 34 Implemented-by: VERDICT: PASS Generated by Claude Code |
只剩一个确认,答「同意」即可落地 — 2026-09-22T16:59Z维护者指示「你的两个 PR 应该跟进到合并」。本 PR 的其余前提已全部清掉,逐条量过:
⇒ 只差 draft 这一步,而翻 draft 是本席位的动作 —— 我不做,只因为下面这一条。 要你确认的那一条(复审自己划出的边界)复审 PASS,但它在 AC5 上明写:
具体是:sanctioned writer #1 的一条对照断言被倒置了 —— ⭐ 但这个形状对已发运的表不可达,我自己在本 PR head 上复核过,⛔ 不是转述复审:
两者都在 ⇒ 那个 所以问题只有一个卡面写的验收判据是「逐字节 / pins 原封不动保持绿」。这条倒置的对照不满足它的字面。你同意按「已记录、已论证、在已发运元数据上不可达」接受它吗?
Generated by Claude Code |
|
| PR | card | hunk | shape |
|---|---|---|---|
| #19657 | #19580 | @@ -5211,7 +5211,35 @@ |
deletes the tail line, re-adds it with a trailing space, appends its own paragraph |
| #19618 | #19054 | @@ -5211,7 +5211,22 @@ |
identical shape, different paragraph |
| #19600 | #15178 | @@ -5211,7 +5218,20 @@ |
identical shape, different paragraph |
All three delete exactly this line:
- + 'selected and no walker can move that intent into the dataset.',
⛔ #19637 is NOT on this region — it edits the same file, but all four of its hunks sit at @@ -9686 and below. Stated so the population is exact rather than 「everything touching the file」.
Second contended point, same class: the conversionIds array immediately below — #19657 at @@ -5232, #19618 and #19600 at @@ -5244.
The rule for whoever lands second and third
- Keep the shared closing line exactly ONCE, and keep the trailing space the first lander added to it.
- Keep every paragraph already on
main, ⛔ not just your own. - Append yours after them.
- Same for
conversionIds: it is consumed as a set, so a dropped id is a retirement that silently stops being declared.
⛔ Why you cannot lean on the usual instruments here
git merge-treeexit 0 is a FALSE GREEN on this file. Measured on this board today: a real merge on PR feat(spec)!: split the translation bundle type —settingsis a platform group, not a per-app one (#15178) #19600 revertedmain'senableOnInstallcorrection in a generated doc whilemerge-treewas happy. Diff the merge commit against BOTH parents, ⛔ not against one.gen:migration-registrywill not save you.registry.ts:18-38says it itself: the generator covers the three<os-generated …>tables, and 「Everything OUTSIDE the markers — this header, each step'srationaleandconversionIds… is still hand-written and still merges as text.」- No gate reds on a dropped paragraph. The result is a syntactically valid string and a green build;
gen:upgrade-guidereprojects from whatever survives, so the only symptom is an upgrade notice that stops mentioning one retirement. ⇒ ⛔ green is not evidence here.
Likely order, so nobody plans against the wrong one
#19600 is closest to landing — contract review PASS on record for its head, CI green on all seven required contexts, mergeable_state: clean. It is held only by its GOVERNED tier H step (skills/objectstack-i18n/SKILL.md), which needs the maintainer's hand or an authorized approval. ⇒ plan on #19600's paragraph being on main first, ⛔ but verify against origin/main at your merge rather than against this sentence.
The structural half is filed, ⛔ not carried here
This tail line reproduces exactly the class #7297 retired for the three tables — the header records it cost 613 hand-resolved lines of conflict markers in four days before that fix. The generator deliberately left rationale outside the markers, and the pattern moved there. not_planned after running the duplicate pass it owed BEFORE filing — the class has been through triage four times (#6957→**#7297** fixed the tables by design; #7464, #8360 and #18062 were each closed, the last folded into #18047, which fixed os-regen-merge.sh's bucketing and ⛔ not this residue). ⛔ An execution seat does not re-litigate a judgement triage has made three times. ⇒ this comment is the record, and ⛔ it changes nothing for the three PRs above, which follow the four rules and land.
Generated by Claude Code
收到
|
Fixes #19054
Clause-②: no
Executes the maintainer ruling recorded verbatim on the card: 「organizationField 撤出可授权面 同意你的建议」.
object.tenancy.organizationFieldleaves the authorable surface at protocol 18 (ADR-0049 enforce-or-remove). The divergence the key existed for is not retired — only its authorability.What the key was, and why it could never be more than one table's fact
It answered "which column says who this platform row is ABOUT", where
tenancy.tenantFieldanswers "what is this object WALLED by". The spec's own docblock stated the consequence: "For ordinary objects the two coincide andorganizationFieldis never needed." Re-measured at head before this branch: the entire repository declared it once, onpackages/platform-objects/src/identity/sys-api-key.object.ts— the better-auth credential table — and zero business objects declared it. Its readers were three platform-row writers, scope-pinned by name, so an application declaration was inert by construction while still being authorable on every object.The shape of the change
TenancyConfigSchemais astrictObject, so this is the strict-deletion route:TENANCY_RETIRED_KEY_GUIDANCEgains its prescription beside the two v15.0 precedents (tenancy.strategy,tenancy.crossTenantAccess). Authoring it is now refused with the prescription, not strippedobject-tenancy-organization-field-removed(toMajor: 18,retiredFromLoadPath: true) strips it from authored sources and storedsys_metadatarows; D3 wires it into the protocol-18 chain step;RETIRED_KEYS_BY_MAJOR[18]declaresdata/TenancyConfig:organizationFieldauthorable-surface/data.jsonrow is deleted in this same commit — the strict route's tripwire, with the build computing the guidance-route proof for itselfliveness/README.md'sobjectrow records why, andstate-counts.mdmovesobject51 → 50 liveLimb 0 of the shared resolver now reads a platform-internal table instead of a declaration:
keyed by the object's registered NAME, read by the STAMP face alone.
resolveRecordOrganizationFieldandcreateRecordOrganizationResolverkeep their signatures —check:api-surfaceis byte-identical — and the engine-bound face passes the name it was asked about rather than readingobjectDef.name, because several engine doubles in this monorepo return a bare{ tenancy, fields }map with noname.The two facts the card said must survive
sys_api_keyismanagedBy: 'better-auth', soresolveInjectedSystemColumnsbails before tenancy is consulted and noorganization_idis injected. Pinned, and the pin is now stated as the better-auth bail rather than as key-blindness (packages/spec/src/data/injected-system-columns.test.ts).organization_id. In this platform "has anorganization_idcolumn" IS the wall, so the rename would wall the credential table on an equality that excludes NULL.plugin-security's Layer-0 suite pins both halves against the real shipped object.The stamp/wall divergence pin is green:
resolveRecordWallOrganizationFieldnever read the key and is untouched.packages/spec/src/migrations/registry.tsis held by PR #19610 (claude/issue-15932-retire-scan-result-surface) as well as by this branch;comm -12over the two file lists returns exactly that one path, and the same comparison against PR #19609 returns 0.That file is generated. ⛔ Neither side hand-resolves a conflict in it. Whichever PR lands second regenerates from its own entries under
packages/spec/src/migrations/entries/viascripts/pm/os-regen-merge.sh's four-step merge order — it does not text-merge. This side regenerates from one new file,entries/retired-keys/18.data__TenancyConfig__organizationField.ts, plus two hand-written edits OUTSIDE the generated markers that do merge as text:step18.conversionIdsgains'object-tenancy-organization-field-removed', andstep18.rationalegains its closing paragraph. If this branch needs the base, it mergesmain(⛔ never rebase, ⛔ never force-push) and regenerates rather than resolving the hunk.Verification
Every number below was taken at
7cc0ca1b3d, the final commit.Reverse verification (both legs committed first, both restored byte-identically, both via
scripts/ablation-replace.mjs):sys_api_key→sys_api_key_ABLATED)0be02fdcc6b7→21856a4a20d0blob == HEAD,git diff HEADempty2e9e19825ef6→eea8b4c7f061expected 'Unrecognized key(s) on 'tenancy': 'or…' to contain ''tenancy.organizationField' was remov…'— the pin measures the PRESCRIPTION, not merely that parse throws; restore verified the same waySuites (
pnpm testper package, through the shared verify lock):@objectstack/spec@objectstack/metadata-core@objectstack/platform-objects@objectstack/plugin-security@objectstack/plugin-auditTypecheck:
@objectstack/spec,@objectstack/metadata-core,@objectstack/platform-objects,@objectstack/plugin-audit,@objectstack/plugin-security— all green, test layers included.Gates:
node scripts/pm/dispatch-gates.mjs --ranreconciles 114 derived / 114 run / 0 NOT-MEASURED / 0 UNRUN against this diff.pnpm --filter @objectstack/spec check:generatedreports 15 of 15 artifacts current.pnpm lint(eslint . --no-inline-config, the whole repo, no narrowing) exits 0.The three sanctioned platform-row writers' pins stayed green UNTOUCHED, as the card required —
plugin-approvals(approval-node,backfill-platform-row-organizations),service-automation(suspended-run-store),service-storage(backfill-sys-file-organizations): 33 + 52 + 15 tests, zero edits. Thedriver-sqlandtrigger-scheduleread-neutrality suites are green untouched too (36 + 61).Acceptance notes
Declared widening of the dispatched file surface — three files, each because this diff makes a statement in it FALSE. None was edited for tidiness; each is named with the measurement that forced it.
packages/spec/src/shared/alias-integrity.test.ts— RED. It pins the exact key set of the foldedtenancyguidance table:expected [ 'crossTenantAccess', …(2) ] to deeply equal [ 'crossTenantAccess', 'strategy' ]. The retirement adds the third row, which is the only channel the refusal travels on.packages/plugins/plugin-security/src/tenant-layer.test.ts— RED. It asserted the declaration off the shipped object:expected undefined to be 'active_organization_id'. Rewritten to pin what this suite actually owns: the stamp column exists as a field,organization_iddoes not, andtenancyis exactly{ enabled: false }.packages/plugins/plugin-audit/src/audit-writers.test.ts— RED, two cases, and one of them is a finding the card asked for. See the next section.A fourth file,
packages/spec/src/automation/schedule-organization.zod.ts, carried a docblock asserting "tenancy.organizationFieldwins there" — a statement this diff falsifies, and one that publishes, intocontent/docs/references/automation/schedule-organization.mdx. Corrected in prose; the generated page follows.⭐ Finding — one sanctioned writer's pin DID have to be edited, and the reason is not cosmetic. Two
plugin-auditcases went red:organizationFieldoutrankstenantField" pinned the precedence oncrm_lead, an object declaring BOTH keys, with the comment "No shipped object declares both; this pins the precedence so the day one does is not a coin flip." After the retirement no application can declare a stamp column at all, so the question is closed rather than answered. The case is rewritten to pin the closed set — an application object carrying a lookalike column stamps from its own wall.sys_api_keyschema with notenancyblock and pinned the actor's org, proving the stamp came from the declaration rather than from a column-name heuristic. Keying limb 0 by object name makes that shape stampactive_organization_idinstead. This is a real, deliberate behaviour change on a shape that is not reachable for the shipped table —sys_api_keyismanagedBy: 'better-auth'andprotection: { lock: 'full' }, so its block cannot be dropped. Recorded in the rewritten case rather than smoothed over, and the#5315guard that did not move (column absent ⇒ fall through to the actor's org) is pinned beside it.⭐ Finding — two issue citations this repo carries in these files do not resolve.
check-issue-citations --base origin/mainjudged 12 citations this change adds and refused all 12:#8778and#8707areallocated-but-absent(minted, ≤ frontier 19616, not on the board; deleted vs transferred NOT MEASURED). Both are pre-existing text — the diff only re-adds them by rewriting the docblocks around them. Following the gate's own prescription, the added lines now name the rulings in prose and cite the cloud record that does resolve. ⛔ No number was guessed. The standing occurrences on unchanged lines elsewhere in the tree are untouched and are not this PR's to repair.Stale-but-green fixture residue, deliberately NOT touched (green today, outside the dispatched surface, and not a defect — the fixtures feed drivers and engine doubles, never
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,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.tsstill authortenancy: { …, organizationField: … }in raw object-definition fixtures. Their assertions remain true; what has gone vacuous is the claim that the driver / wall face is neutral about a key nobody can write.packages/lint/src/validate-object-field-refs.tscarries the key in a list of scalars it deliberately does not judge.No tree-scoped absence pin is added, and that is a decision rather than an omission: the playbook's tree-scoped form would have to declare its radius in
scripts/cross-package-test-inputs.mjsandturbo.json, both far outside this card's surface, and it would go red against exactly the six inert fixtures above. The absence is instead enforced where it is cheap and exact —authorable-surface/data.jsonhas no row, andcheck:authorable-surfaceis the gate over that baseline.Clause-② re-judged from the diff
no, and the diff agrees. No hunk puts a new key on a published payload: the guidance row is a prescription string, theRETIRED_KEYS_BY_MAJOR/CONVERSIONS_BY_MAJORentries are registry rows,json-schema/**loses a key, andapi-surface/is byte-identical —resolveRecordOrganizationField's signature is unchanged. This is a pure retirement, which narrows.🤖 Generated with Claude Code
https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Generated by Claude Code