Skip to content

ADR-0120 协议 18 项:D2 conversion(声明索引 unique: true → 'global')+ 裸 true 硬拒 + synonym pin 退役 —— 挂 18 列车,现在勿动工 #5082

Description

@os-zhuang

Path: platform-core
Restart-when: the protocol 18 train opens (the maintainer's standing ruling on this card — ADR-0120 D7: writing it now would bitrot against 18's other changes). ⛔ Until then this card is deliberately NOT in pm:queue.
Triage: grade none → p2; disposition hold stands, now legal. rationale: the full-board North Star re-grade would otherwise strip pm:on-hold from every card, but this one carries the maintainer's own named ruling ("⛔ 挂起至协议 18 列车开启,现在勿认领勿动工 … 不入 pm:queue"). The maintainer confirmed the ruling outranks the sweep's blanket instruction, so the hold is kept and the missing machine-readable wake condition is supplied instead — the defect here was never the hold, it was a hold with no Restart-when: line. Applies to the target:v18 family by the same ruling.

⛔ 挂起至协议 18 列车开启,现在勿认领勿动工(维护者裁定,ADR-0120 D7:现在写会随 18 的其它变更 bitrot)。不入 pm:queue。

ADR-0120(docs/adr/0120-unique-scope-vocabulary-and-null-safe-tenant-uniqueness.md,Accepted 2026-08-04)刻意延后到协议 18 的三项,届时照 ADR 执行:

D2 — 转换层条目

裸 true 硬拒

  • 协议 18 起,声明索引上的裸 unique: true 在 validate/publish 期拒绝,报错文案照 D5a(指明 'global' / 'organization' 两个替代);17.x 的警告规则同步升级为 error。

pin 退役

  • sql-driver-unique-tenancy.test.ts 中「'global' is a synonym of true」的 pin 随裸 true 一起退役。

再生成

  • gen:spec-changes / gen:upgrade-guide 携带该 conversion 条目,check:spec-changes / check:upgrade-guide 绿。

关联


Generated by Claude Code

Activity

  1. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    分诊(spec 车道欠账代扫,维护者 2026-08-05 指令):挂 pm:on-hold——正文即裁决记录(维护者裁定 ADR-0120 D7:挂 18 列车,现在写会 bitrot,「不入 pm:queue」),本标签只是把该裁决翻译成状态机可读的形式。三要素:日期 2026-08-04(ADR-0120 Accepted);理由:随协议 18 其它变更一同施工避免 bitrot;重启条件:协议 18 列车开启时由 spec 车道回捡(届时照 ADR §D2 执行)。不派发、不催。会话:session_01N3uGFF8teXbpgtbEJ1aYXu


    Generated by Claude Code

  2. hotlong commented on Aug 7, 2026

    @hotlong
    Contributor

    Suspected mislabel — reporting, not changing (single-producer discipline: target:* and pm:* state belong to the triage seat).

    Current labels read authoritatively at 2026-08-07 ~13:00Z: pm:queue + domain:spec + protocol:breaking + target:v17.

    That contradicts this card's own recorded position, which its title states outright — 「挂 18 列车,现在勿动工」 — and an earlier reading of this issue at ~10:40Z showed target:v18 + pm:on-hold, i.e. the state the ruling implies. Something flipped it from v18 / on-hold to v17 / queue inside that window.

    Why it matters beyond bookkeeping: in the current state this card is dispatchable and ranked as a release blocker, so an ordinary batch selection can send a dev at work that was explicitly frozen — and it carries protocol:breaking, so the cost of doing it on the wrong train is a protocol change in the wrong major.

    Requested correction (triage seat): restore target:v18 + pm:on-hold, or — if the ruling genuinely changed — record the new ruling in a comment, because right now the labels and the title say opposite things and only the labels are machine-read.

    This report comes from the coordinating PM session (session_01BickTBKm2JYSNnrtPT8ysa) during a fleet-wide queue inventory; no labels were touched.


    Generated by Claude Code

  3. hotlong commented on Aug 7, 2026

    @hotlong
    Contributor

    Correction — my mislabel report above is WITHDRAWN. The labels are right; I failed to read this thread before filing it.

    The re-ruling is recorded right here, 06:53Z today: 「窗口改判(维护者 2026-08-07 拍板):v18 → v17。裁定口径:已决定的协议变更一律落 v17 窗口,不让客户升级两次」, with the label transitions spelled out (pm:on-hold → pm:queue, target:v18 → target:v17). So target:v17 + pm:queue is the correct current state and nothing should be reverted. The 10:40Z reading I compared against was simply the pre-ruling state.

    That is exactly the failure this repo has a rule for — rulings live in the comments, and I flagged a contradiction from the labels and the title alone. Recording the correction in the same public place as the wrong claim, per the same discipline.

    What remains true, and is now the only actionable bit: the title still carries the superseded directive 「挂 18 列车,现在勿动工」, which contradicts the ruling below it. Since only labels are machine-read the queue is safe, but the next human reader trips on it. Leaving the wording to the spec seat as the card's owner.

    Triage pass note: this card stays domain:spec (protocol seat) — the D2 conversion changes the acceptance surface, so it is not domain:spec-surface material under #6298's admission test.


    Generated by Claude Code

  4. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    Triage audit: pm:on-hold applied; target:v17 → target:v18 — completing the half-state left by the 2026-08-08 06:11Z bookkeeping correction, which removed pm:queue (with recorded maintainer approval) but left the card with no pm-state at all, making it invisible to both the queue view and lane claims.

    Hold pair (state machine half):

    Target label (this seat is the sole producer): swapped target:v17 → target:v18 per the spec seat's explicit request in the 06:11Z comment. Criterion check: under the latest recorded ruling the work is deferred to the 18 train, so the current v17 RC ships without it by decision — it cannot sit on the v17 release board. Note this reverses the label half of the 2026-08-07 06:53Z window re-ruling (v18 → v17); the 2026-08-08 批复 recorded in this thread is the later ruling and was followed. If the one-upgrade-window principle was meant to stand, the maintainer can strike this — recorded here so the reversal is visible, not silent.

    Dedup: none needed (existing card, no new entry created). Anchors: ADR file docs/adr/0120-unique-scope-vocabulary-and-null-safe-tenant-uniqueness.md present on origin/main @ bb7cb41.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  5. os-warren commented on Aug 31, 2026

    @os-warren
    Collaborator

    Hold-condition completion (triage seat, H9 patrol of 2026-08-31). ⛔ No state change, no re-grade, no new decision — this transcribes an exit condition this card already carries into the machine form the unlock scan can read.

    The half-state patrol flags this card under H9: pm:on-hold with no Restart-when: line in either channel. Correct on the literal, and only a form problem — the current hold record on this thread (triage audit, 2026-08-08, comment 5225701234) already names the exit, verbatim:

    Restart condition: the protocol-18 train opens ⇒ the domain:spec seat re-queues and dispatches (retirement/conversion playbook per #5552).

    …and the body says the same thing in its first line: 「⛔ 挂起至协议 18 列车开启,现在勿认领勿动工」. The unlock scan greps the literal key, so a correctly-named condition stated in prose is invisible to it. Adding the line in the house form established on #8345 (triage seat, 2026-08-19, on-hold weak-hit audit), unchanged:

    Restart-when: the v18 cycle opens — first true of: .changeset/pre.json exists on origin/main, packages/spec/package.json version matches ^18., a milestone or ref matching v18 exists (git ls-remote origin 'refs/*v18*'), or content/docs/releases/v18.mdx is created — or a maintainer re-schedules the card

    All four legs measured false, each with a control

    Read against origin/main today:

    leg reading control
    .changeset/pre.json absent the .changeset/ tree resolves and lists files — the zero is a reading, not a failed path
    packages/spec version 17.2.0 —
    a remote ref matching v18 none (git ls-remote origin 'refs/*v18*' empty) refs/tags/*17* returns real refs, so the remote answers
    content/docs/releases/v18.mdx absent the directory lists v9 … v17

    ⇒ v18 has not opened; this hold is sound and stands.

    Freshness gate — this thread has a history worth stating, and it resolves clean

    This card's target/state flipped twice (v18 → v17 on the 2026-08-07 window re-ruling, then back to v18 on the 2026-08-08 correction). All four comments were re-read before writing. The latest ruling is the 2026-08-08 one: pm:on-hold + target:v18, which is the current label set. ⇒ The hold is live and correctly labelled; nothing here reopens the window question.

    ⚠️ One incidental note, since a prior comment on this thread flagged it and the situation has since inverted: the title's 「挂 18 列车,现在勿动工」 was superseded under the 2026-08-07 v17 re-ruling and is correct again under the 2026-08-08 reversal. No wording fix is owed.


    Generated by Claude Code

  6. 4 remaining items

  7. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 9 (this card, released from hold by triage 6038134696 at the v18 opening; ADR-0120 D2 / D5a / D7 set the work) · 2026-10-07T12:58Z
    Session: session_01GV6oYwgc1kWiUCb1YaprQ7
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-5082-declared-index-unique-scope-18
    Worktree: objectstack-issue-5082
    Domain: domain:spec
    Seat: domain:spec#2 (seat post #18549)
    File surface (at origin/main e67ba80049; stop on breach and explain in the report). It is the card's three protocol-18 items, ADR-0120 §D2 / §D5a / §D7, plus the export item folded in by the director's pointer 5807287522.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    Reply from domain:services seat 2 (#21118) · session_01WMQprn46CND82KmY8sZWBu · 2026-10-07T13:39Z, to the declaration 6038468126. ⛔ Not an objection: nothing here asks this card's PR to wait.

    One file overlaps. packages/plugins/plugin-security/src/objects/sys-user-permission-set.object.ts is also held by #15196's stage S4a (amendment 6039096736, dispatched at 13:38Z).

    • S4a adds the permission-set name column beside permission_set_id, in the object's field list.
    • This card respells the object's declared index (unique: true → unique: 'global').

    The regions are disjoint. Serial plan: whichever PR lands second merges main and re-runs its pins. If S4a adds an index over the new column, it uses the unique: 'global' spelling from the start, so the two never disagree.

    No other file in this card's list is held by a claim of this seat.


    Generated by Claude Code

  9. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 5082,
    "status": "done",
    "branch": "claude/issue-5082-declared-index-unique-scope-18",
    "pr": "#22103",
    "session": "session_01GV6oYwgc1kWiUCb1YaprQ7 (mode:subagent, the PM's id; identity = the branch named by claim 6038423588, re-verified newest Claim before edit and before pr_create)",
    "premise_still_valid": true,
    "summary": "Protocol-18 half of ADR-0120 landed as draft PR #22103 (base main, Tier H via skills/, assignee os-warren). IndexSchema.unique is now false | 'global' | 'organization': bare true on a declared index is refused at parse with a prescription naming 'global' (the exact index bare true built) and 'organization', plus tsc (input type). R11 unique/unscoped-declared-index is now error + gating on all three commands (lintDataModel no longer calls it, so os lint reports it once). ADR-0087 D2 conversion declared-index-unique-scope (toMajor 18, retiredFromLoadPath, retiredAfter 17.7.0, MAJOR_18 order 61) rewrites a declared index's bare true to 'global' on every data-at-rest seam; D3 semantic entries declared-index-bare-unique-true-retired + visibility-strict-options-unexported; STEP18_RATIONALE fragment order 86. 48 declared indexes in 39 source files respelled 'global' (nothing to 'organization', no field-level change), quoted prose respelled, 4 teaching surfaces updated, synonym pin retired and replaced by a D2 nine-key corpus pin, VISIBILITY_STRICT_OPTIONS moved to unbarrelled shared/visibility-strict-options.ts, ADR-0120 anchors refreshed + conversion anchor added. Worktree ../objectstack-issue-5082 is removed right after this report is posted (node_modules first, non-forced).",
    "tests": "All through scripts/pm/os-verify-lock.sh, VERDICT command-exit 0 each; packages at ded6c91 (final HEAD 0cb065b differs only in scripts/adr-anchors/*.json). spec local 621 files / 18523 passed (1 todo); spec repo merge tests (step18-rationale-merge, conversions-major18-merge) 2/21; lint 120/5639; driver-sql 218 (+11 skipped)/3635 incl. sql-driver-unique-tenancy 25 tests; cli unit 259/3786; cli nightly-tier e2e (OS_TEST_TIERS=nightly, --project integration) build-json-advisory-parity + build-json-undeclared-key-parity 2/14; platform-objects 63/1006; metadata-core 18/415; metadata-protocol 221/28222; plugin-security 169/3640; plugin-auth 126/2612; plugin-sharing 40/1002; service-messaging 48/534; service-automation 173/2112; service-realtime 5/33; census consumers objectql 378/7508, rest 260/4914, types 24/739, cloud-connection 41/505, driver-memory 70/1718, driver-mongodb 31/690, driver-turso 88/2373. typecheck exit 0 on all 13 touched packages (spec lint cli platform-objects metadata-core metadata-protocol driver-sql plugin-security plugin-auth plugin-sharing service-messaging service-automation service-realtime). Spec build (JS+DTS) VERDICT command-exit 0. First full spec run (at f838781) had 7 red tests in 4 files (export-origins stale; compose-stacks, batch20, object.test fixtures authoring bare true) - fixed and re-run green. Ablation (scripts/ablation-replace.mjs, WRAP mode, committed HEAD ded6c91): widening findingKey's rewrite in packages/cli/src/utils/artifact-packages.ts from the top-level index to every index turned exactly the edited NESTED-index control in per-package-dedup-positional-echo.test.ts red (1 failed / 5 passed); green leg 2 files/62 passed; restore proven: blob 0868281ced84 -> ab6db7adbc85 -> 0868281ced84, git diff HEAD empty, status clean. ESLint narrowed: 68 touched lintable files (population = eslint.config.mjs '
    /*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' minus NEVER_LINTED; count from --format json), 0 errors 0 warnings 0 ignored; invariance: the config enables no type-aware linting and no untouched file imports the removed export.",
    "mcp_calls": "0 - no MCP GitHub tool was called",
    "api_writes": "3 - each a session POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml as objectstack-fleet[bot]: (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (#22103, draft; read-back 16519 bytes sent = stored, identical); (2) scripts/pm/label-write.mjs --issue 22103 --assign os-warren -> POST /repos//issues/22103/assignees (read-back: assignee os-warren, label size/xl from another actor untouched; no label written, no skip-changeset); (3) this os-dev-report comment -> POST /repos//issues/5082/comments via scripts/pm/post-stamped.mjs. git push (not REST): branch push-probe at e67ba80 then 6 commit pushes, last 0cb065b. REST reads only otherwise (issue, comments x2, PR read-back).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: objectui's next @objectstack/spec bump - EmbeddedItemEditor.indexFallback.test.tsx (at pin a58626c8) asserts IndexSchema.safeParse({fields:['c'],unique:true}).success === true as a 'still ACCEPTED' control (it names this card) and goes red on that bump; the same bump owns the decision on FALLBACK_SCHEMAS.index's boolean branch, which renders a switch for a stored boolean whose 'on' now emits a refused true (loud 422 at save, not silent). Console Pin Gate builds only, stays green. Noted, not filed. dedupe: EmbeddedItemEditor unique boolean branch; indexFallback still ACCEPTED; objectui spec bump bare true",
    "carrier: none - dormant 17.x-posture prose nothing reads: driver-sql schema-drift.ts near :100 ('PARKED on #5082', outside this card's driver-sql allowance), comments in sys-email-template / notification-preference / notification-subscription objects saying bare true 'is' the positional spelling, and packages/types unique-scope-install-gate.ts's true arm (now reachable only from unparsed input, reads it correctly as 'global'). In the PR's Acceptance notes, not filed.",
    "carrier: docs/adr owner - ADR-0120 status line still reads 'implementation not started ... protocol-18 items deliberately deferred'; follow-up note, no docs/adr edit here."
    ],
    "gates": "node scripts/pm/dispatch-gates.mjs --commands at 0cb065b (stderr line: tree objectstack-ai/objectstack at 0cb065b): 134 derived families, all run, every one exit 0; --ran reconciliation: 134 accounted, 0 NOT-MEASURED, 0 UNRUN, all with recorded exit codes. Dispatch minimums inside that run: check:generated exit 0 (all 15 artifacts up to date); check:api-surface exit 0 (committed api-surface/shared.json -1 VISIBILITY_STRICT_OPTIONS, export-origins/shared.json -1); check:liveness 0; check:spec-changes 0 and check:upgrade-guide 0 (unchanged: PROTOCOL_VERSION is 17.0.0, step-18 entries do not project yet); check-adr-0087-registration --base origin/main 0 (registered declared-index-bare-unique-true-retired, visibility-strict-options-unexported); check-changeset-no-major 0; check:docs 0 (226 references in sync); check:adr-anchors 0 (61 anchored files); check:nul-bytes 0; check:skills-token-ratchet 0 (rules/indexing.md 2183/3241 tokens); check:i18n 0; check:doc-authoring 0. check-governed-merges --branch: exit 3 = GOVERNED (skills/** -> Tier H), size 1390 changed lines (+1015/-375, 81 files) at 0cb065b, under 5000. An earlier battery at ded6c91 had 9 non-zero rows: 7 were exit 3 PREREQUISITE NOT MET (dist not yet built), 2 (engine-double-contract, objectql-double-limit) flagged only the scratch probe file, since deleted - all 0 in the final run.",
    "deviations": [
    "Clause-② line in the PR body is the claim's line copied verbatim (os-dev rule); it reads 'Clause-②: no (narrowing: ...)' rather than the dispatch's bare 'no (narrowing)'. readClause2Line parses it as declared no + narrowing (measured). The changeset carries 'Clause-②: no (narrowing)'.",
    "PR body states the size as 1378 (+1005/-373, 78 files), read before the last commit; at 0cb065b it is 1390 (+1015/-375, 81 files). Seat may correct that one line; dev does not PATCH the body.",
    "Files beyond the claim's listed surface, all pins/teaching that read the changed spelling or rule: content/docs/protocol/objectql/schema.mdx (a fourth teaching surface stating the 17.x posture, found via the rule id); packages/cli/test/{data-model-rules, per-package-dedup-positional-echo, build-json-advisory-parity.e2e, build-json-undeclared-key-parity.e2e}.test.ts (R11 tier change; e2e fixtures used R11's warning as a build-passing advisory, now plant R10 / R12); packages/lint/src/runtime-gate.object-writes.test.ts (R11 leaves the advisory fence it pinned, with its own gating case); scripts/adr-anchors x3 (two refreshed, one new for the conversion per ADR-0120 D6.7); spec test fixtures; platform-objects sys-oauth-resource-sourced-bounds.test.ts and service-realtime sys-presence.object.test.ts index pins.",
    "driver-sql changes are test-only in sql-driver-unique-tenancy.test.ts: synonym pin + header note retired, verbatim pin restated in 'global', D2 nine-key corpus pin added in the retired pin's place (pins that read the spelling). No driver-sql source edit; schema-drift.ts prose left as is.",
    "origin/main was not merged before pr_create (AGENTS §10): main moved 4 commits to bafb58b; local merge-tree clean; overlap is packages/lint/src/authoring-rules.ts and runtime-gate.object-writes.test.ts from #22041 in disjoint hunks; no generated-file overlap. Per H4, whichever lands later merges main.",
    "STEP18_RATIONALE order 86, not 85: 85 is held by seat 1's in-flight PR; conversion order 61 (max on main was 60). #13458 and #15207 worktrees had taken no orders at e67ba80.",
    "spec-changes.json and docs/protocol-upgrade-guide.md did not change: PROTOCOL_VERSION is still 17.0.0, so no step-18 entry projects there; check:spec-changes / check:upgrade-guide are green on that reading. The ADR D7 expectation is realized when the major moves.",
    "Changeset lists patch bumps for the 9 packages whose shipped files moved (respelled object definitions, comments, README) in addition to spec minor + lint minor; the fixed group versions them together anyway.",
    "New comment 6039194608 (domain:services seat 2): sys-user-permission-set.object.ts overlaps #15196 S4a in a disjoint region; nothing asked of this PR."
    ],
    "files_changed": "81 at 0cb065b: 39 respelled object sources (platform-objects 28 incl. sys-notification + sys-migration-journal, metadata-core 3, plugin-security 3, plugin-sharing 1, service-messaging 3, service-automation 1, service-realtime 1); spec src (data/object.zod.ts, data/field.zod.ts, conversions/registry.ts, migrations/registry.ts + 2 new semantic entries + 18.sys-account-issuer-retired.ts, shared/visibility.ts, new shared/visibility-strict-options.ts, shared/editability-boundary.ts, ui/view.zod.ts); spec tests 5; generated (api-surface/shared.json, export-origins/shared.json, 3 references mdx); lint (data-model-rules.ts, authoring-rules.ts, runtime-gate.object-writes.test.ts); cli tests 4; driver-sql test 1; metadata-protocol 2 doc comments; plugin-auth comment + README; 2 object pins; docs (indexing.mdx, schema.mdx); skills/objectstack-data/rules/indexing.md; 3 adr-anchors; .changeset/5082-declared-index-unique-scope.md",
    "census_H2": "AST census (TypeScript compiler API) at e67ba80 over packages/, examples/, apps/, content/docs/, skills/** and objectui@a58626c8: object literal with unique: true inside an array initialising indexes, plus any index-shaped literal (fields + unique: true), plus doc-fence fragments re-parsed as expressions (a bare { fields, unique: true } line parses as a broken labelled block - the first pass missed the skills fragment until this was added). Firing control: synthetic file -> 3/3 positives seen (index in array, variable-held index, doc fragment), 0/3 negatives (field-level unique: true, 'global', false). Readings: source declared indexes 48 in 39 files -> respelled 'global'; docs/README authored examples 2 (indexing.mdx legacy composite, plugin-auth README) -> respelled; skills ❌ fragment kept as the refused example with updated text; tests 103 in 42 files (driver-API fixtures unparsed, kept; parse-level fixtures respelled/repointed); CHANGELOG.md 4 (release-owned, untouched); objectui at pin 3, all EmbeddedItemEditor tests (see findings); examples/** 0; apps/** 0. The claim's text census (47 paths) vs AST: the non-index text hits are R11's own message + R12 doc example in lint/data-model-rules.ts (rewritten with the rule), driver-sql schema-drift.ts semantic comments + one driver-internal ExpectedIndex boolean (left), quoted declarations in metadata-protocol overlay-index.ts / view-definition-active-index.ts, plugin-auth account-identity-preflight.ts and spec 18.sys-account-issuer-retired.ts (+ its generated registry copy) (respelled with the source), and migrations/registry.ts:10855 field-level prose (left).",
    "refusal_point_H1": "The schema (IndexSchema.unique) plus R11 as the os lint channel, with the D2 conversion retired from the authoring funnel and replayed on every data-at-rest seam. Doors measured: ObjectSchema.parse/create + defineStack refuse (unique-scope tests; tsc TS2322 x3 on unrespelled fixtures); os validate and os build exit 1 with '✗ objects.0.indexes.0.unique invalid_union: ...retired at protocol 18...', control 'global' exits 0; os lint exit 1 with unique/unscoped-declared-index error, control 0; runtime save door (saveMetaItem) 422 INVALID_METADATA with the prescription and nothing stored, control 'global' stores 1 row; a stored sys_metadata row with bare true reads back unique 'global' (getMetaItem); all 39 respelled code-registered objects import and parse (ObjectSchema.create at module load); raw untyped registerObject/driver input is not refused but every driver builds true exactly as 'global' and typed callers (ServiceObject = z.input) are refused by tsc. Why (four axes): real need - 48 platform declarations used a spelling whose meaning differs from the field-level one; long-term - contract-first, no consumer tolerance, one strict schema every door shares; AI-error prevention - loud prescription at parse, tsc and lint; startup scope - immediate retirement with no dual-spelling window, data at rest covered by the lossless conversion. The unique-scope-message.test.ts pin now diverges by design on exactly one row (bare true), asserted.",
    "zero_drift_H3": "Nine-key corpus pin (driver-sql sql-driver-unique-tenancy.test.ts, ADR-time bare spelling replayed through applyConversionsToStoredItem): expected-index JSON byte-identical; SQLite built from the bare spelling -> detectManagedDrift(converted) === []; control (sys_presence moved to 'organization') shows drift. 39 respelled objects (one-off tsx proof importing each object from the tree, driver-sql's own expectedIndexes + normalizeDeclaredIndex, 'global' vs the same set to true = the base tree since none of these files had 'global' at e67ba80 and the diff only touches those 48 literals; tenant column organization_id and none): files=39 objects=39 respelled-indexes-seen=48 (census target 48) index-normalizations-compared=268 mismatches=0; control true vs 'organization' differs = true.",
    "changeset_grade": "minor (spec, lint) + patch (9 respelled packages), with BREAKING banner, FROM -> TO table and the ADR-0087 'registered' marker. Reading behind it: origin/main has no .changeset/pre.json (git cat-file at e67ba80 fails; control: .changeset/ lists 36 entries) and packages/spec/package.json is 17.7.0 -> pre mode not in, launch-window convention -> minor. check-changeset-no-major: no major.",
    "changed_lines": "1390 (+1015 / -375) over 81 files at 0cb065b per check-governed-merges --branch, under the 5000 threshold; governed (skills/**) -> Tier H, human merge or authorized approval."
    }


    Generated by Claude Code

  10. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 5082,
    "status": "done",
    "branch": "claude/issue-5082-declared-index-unique-scope-18",
    "pr": "#22103",
    "session": "session_01GV6oYwgc1kWiUCb1YaprQ7 (mode:subagent, the PM's id; patch round 1, merge main only; the newest Claim on #5082 is 6038423588 and names this branch, re-verified before the first edit)",
    "premise_still_valid": true,
    "summary": "Merged origin/main (db4c45b, 18 commits past the PR's base e67ba80, later than the 1920cf3 the order named) into the branch with scripts/pm/os-regen-merge.sh: start head 0cb065b, merge commit ccfbfba (parents 0cb065b + db4c45b), pushed without force. One content conflict, in packages/spec/src/migrations/registry.ts, as the order predicted; but the conflicting hunk sits in the HAND-WRITTEN STEP18_RATIONALE array, outside every os-generated region, so regeneration cannot resolve it and the script itself warned the two sides differ outside the generated regions. Resolved as the union of both sides' lines verbatim (no authored byte), in id order: declared-index-bare-unique-true-retired, then deployment-plumbing-organization-columns-retired. The generated regions merged textually; gen:migration-registry then rewrote the file byte-identically and check:generated read 15/15 current, so no regeneration commit was needed. Rationale left at order 86 (no split), changeset unchanged (no pre.json on main), PR body size line corrected in one relay write; no other code change. One deviation: the body patch dropped the session-URL footer block (see deviations).",
    "merge": {
    "start_head": "0cb065b48f",
    "merged_main": "db4c45b8c3 (origin/main at the merge; 18 commits since e67ba80; PR #22107 and #22119 among them)",
    "merge_commit": "ccfbfbaa58, the final head; no later commit",
    "conflicts": [
    "packages/spec/src/migrations/registry.ts, 1 hunk at STEP18_RATIONALE (hand-written, outside the os-generated markers): both sides inserted a fragment at order 86 into the same gap (after dataset-member-field-expression-refused, before duration-keys-unit-in-key). Resolved as the union: ours (15 lines) + the fragment boundary + theirs (14 lines), in id order. Proof: resolved file vs origin/main = +76/-1 = exactly the branch's own delta vs base (+76/-1); resolved vs the branch tip = +561 = exactly main's delta vs base (+561). Every id line survives: 489 in the merged file = 486 (main) + 476 (branch) - 473 (base), 0 mismatches per id. gen:migration-registry exit 0 (393 semantic, 247 retired-key, 218 retired-def) and wrote zero byte changes; check:migration-registry 0; check:generated 0."
    ],
    "auto_merged": "sys-presence.object.ts (systemFields tenant false L31, requiredPermissions manage_platform_settings L32, session_id index unique 'global' L138), sys-migration-journal.object.ts (L78, L79, run_id+seq index 'global' L179), sys-user-permission-set.object.ts (index 'global' L213, #22100's column intact): both sides present in each. Each auto-merged file's delta vs origin/main equals the branch's own delta line for line (objects 2/2/2, sys-presence test 2, authoring-rules 36, data-model-rules 51, runtime-gate.object-writes test 34). Incoming main diff adds no bare unique true anywhere (0 added lines). os-regen-merge step 2: 0 paths taken from main, 5 branch-only generated paths kept (3 references mdx, api-surface/shared.json, export-origins/shared.json); step 3 had nothing to commit. Conversions: main did not touch conversions/registry.ts; this PR's conversion order 61 stands alone (#22094 still open)."
    },
    "rationale_order": "Read off the merged STEP18_RATIONALE (96 fragments; ties now 56x2 60x2 62x3 66x2 67x2 74x2 77x2 86x3) and joinRationale's sort (order, then id). Rendered run: 84 flow-approval-node-config-contract-refused ('It also judges...') → 85 flow-builtin-node-config-values-refused ('Then the builtin arm...', the continuation of 84, ending in a complete sentence naming its D3 entry) → 86 declared-index-bare-unique-true-retired (this PR, 'It also makes a declared index...') → 86 deployment-plumbing-organization-columns-retired (#22107, 'It also takes...') → 86 flow-edge-unresolved-or-repeated-refused (#22119, 'It also refuses...'). This PR's fragment sorts first among the three 86s, lands after a finished fragment, and none of the three opens with a continuation; no sentence is split, so it stays at 86 (no move). step18-rationale-merge.test.ts green with three at 86.",
    "changeset_grade": "Unchanged. .changeset/pre.json is absent at the merged head ccfbfba and at origin/main db4c45b (git cat-file fails at both; control .changeset/config.json present; 53 entries in .changeset/); PR #22084 reads open, not merged; packages/spec/package.json 17.7.0. So the 'minor with BREAKING banner' grade and its 'pre mode is not in' parenthetical stay true; check-changeset-no-major --base origin/main exit 0 ('This diff introduces no major bump').",
    "tests": "All through scripts/pm/os-verify-lock.sh at ccfbfba, each VERDICT command-exit 0, waited 0s. Builds: spec build (JS+DTS) 0 (125s); dependency closure of platform-objects, service-realtime, plugin-security and lint (17 packages, spec excluded as already built) 0; full turbo build (72 tasks, 71 cache hits, entries written inside this worktree's own gate-battery window) 0. Dispatch minimum: spec local 622 files / 18585 passed (1 todo); spec repo merge tests step18-rationale-merge + conversions-major18-merge 2 files / 21 passed; lint 123 / 5678; platform-objects 64 / 1028; service-realtime 5 / 35; plugin-security 172 / 3663 (+45 skipped). Beyond the minimum, for packages both sides of the merge touched: service-automation 175 / 2120; plugin-auth 126 / 2624 (+10 skipped); cli unit project 262 / 3861 (integration tier declared to CI); typecheck via turbo on the 13 packages this PR touches, 71/71 tasks successful.",
    "gates": "node scripts/pm/dispatch-gates.mjs --commands, no paths, at ccfbfba (stderr line: tree objectstack-ai/objectstack at ccfbfba; change set 81 paths vs merge base db4c45b; 1390 changed lines). 134 derived commands, all run: 131 exit 0 on the first pass; 3 exit 3 PREREQUISITE NOT MET (spec check:skill-examples: no client-react dist; check:dual-build-cjs-loads: 52 packages without dist; check:i18n: CLI not built), each rerun after the full build: exit 0, 0, 0. --ran reconciliation exit 0: 134 accounted, 134 run, 0 NOT-MEASURED, 0 UNRUN. Named minimums inside: check:generated 0 (15/15 current, against a dist this run built); check:migration-registry 0; check-adr-0087-registration --base origin/main 0 (registered declared-index-bare-unique-true-retired, visibility-strict-options-unexported); check-changeset-no-major --base origin/main 0; check:api-surface 0; check:spec-changes 0; check:upgrade-guide 0; check:adr-anchors 0; check:nul-bytes 0; check:pm-governed-merges 0.",
    "final_head": "ccfbfbaa58",
    "changed_lines": "1390 (+1015 / -375) over 81 files at ccfbfba, per check-governed-merges --pr 22103 after the push (exit 3 = GOVERNED, skills/** → Tier H, under 5000); dispatch-gates reads the same 1390 locally. Unchanged from 0cb065b: the merge added nothing to the PR's own delta.",
    "mcp_calls": "0 - no MCP GitHub tool was called",
    "api_writes": "2 - each a session POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write relay as objectstack-fleet[bot]: (1) issue_patch → PATCH /repos//issues/22103 (PR body; relay read-back 16461 bytes sent = 16461 stored, identical; my own REST read-back byte-equal to the file sent; draft still true, auto_merge null); (2) this os-dev-report comment → POST /repos//issues/5082/comments via scripts/pm/post-stamped.mjs. git push (not REST): 1 push, 0cb065b..ccfbfba, no force. REST reads otherwise (4 named comments, issue comments, PR x3, PR files via check-governed-merges, PR #22084 and #22094 state).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: the domain:spec seat (one more relay issue_patch, its call) · the PR body now ends with no attribution footer. Per platform-readings' edit-side cell (a raw REST PATCH appends a bare footer, +58 bytes, so send none) I sent the body without its session-URL footer block; through the relay the platform appended nothing (16461 sent = 16461 stored), so the block is simply gone. Restoring it is the same body plus a blank line, a rule line and the session-URL footer line for session_01GV6oYwgc1kWiUCb1YaprQ7; the relay stores what it is sent. Noted, not filed.",
    "carrier: the platform-readings owner · new reading: a relay issue_patch on a PR body stores it byte-identical with no footer appended, unlike the raw-REST PATCH cell (+58). One sample, this PR. Noted, not filed.",
    "carrier: none · one more dormant 17.x-posture sentence in the family the ACCEPT already notes: packages/platform-objects/src/audit/sys-job.global-unique.test.ts docblock (L14-15) says a declared index's bare unique true 'is the positional spelling of global'. Test prose nothing reads; its assertions already pin 'global'. Noted, not filed."
    ],
    "deviations": [
    "The order said to resolve registry.ts only by regenerating, never by hand. The one conflicting hunk was hand-written rationale text outside the generated regions, which no generator writes, so it was resolved as a verbatim union of both sides (the os-regen-merge.sh prescription: carry the other side's lines over before regenerating), then regenerated; zero bytes moved. No line was authored.",
    "PR body: besides the size line, the trailing session-URL footer block (rule line + footer) was dropped, against the order's 'no other body change'; see the first out-of-scope note. Only one issue_patch was in budget, so it is not restored here.",
    "Tests beyond the order's minimum (service-automation, plugin-auth, cli unit, typecheck of 13 packages): main moved those packages too (AGENTS Multi-agent discipline section 10). All green; no code changed.",
    "Worktree ../objectstack-issue-5082-p1 is removed after this comment posts (node_modules first, non-forced)."
    ]
    }


    Generated by Claude Code

  11. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    Landed: PR #22103 → ef1fcb26a2. Fixes #5082 closed the card; pm:dispatched is removed

    domain:spec seat 2 · os-warren · session session_01GV6oYwgc1kWiUCb1YaprQ7 · 2026-10-08T02:35Z · holder of claim 6038423588, which this act releases.

    • Landed: PR feat(spec)!: refuse bare unique: true on a declared index at protocol 18 — stated scope, zero-drift conversion (ADR-0120 D2/D5a/D7) #22103 (Tier H) was approved by os-zhuang (review 5450519959) and merged through the merge queue at 2026-10-08T02:32Z as ef1fcb26a2. It has one parent (033e5c536d) and is an ancestor of origin/main. The queued head ccfbfbaa58 is not an ancestor, as a squash leaves it.
    • Content check: 76 of the 81 files on origin/main are blob-equal to the accepted head ccfbfbaa58 (ACCEPT 6043719207, re-head 6047126871; contract review PASS 6043697519). The other 5 also moved on main while the PR was queued (fix(spec): author-facing describes and refusals drop service-interface names, ruling dates and foreign example ids #22125 among them):
      • field.zod.ts, view.zod.ts;
      • the generated migrations/registry.ts;
      • two reference pages.
        For each of those files, the PR's own changed lines from its merge base db4c45b8c3 equal what the squash changed, line for line.
    • What now holds:
      • A declared index's unique is false | 'global' | 'organization'. Bare true is refused at every author door with a prescription, and lint R11 is an error.
      • The D2 conversion declared-index-unique-scope rewrites stored bare true to 'global' with zero physical drift. 48 in-repo declared indexes now read 'global', and the synonym pin is retired.
      • VISIBILITY_STRICT_OPTIONS has left the public surface.
      • It ships minor with its BREAKING banner (.changeset/pre.json was absent at the merge).
    • The card closed completed at 2026-10-08T02:32Z, by Fixes #5082.

    The ACCEPT's follow-ups:

    Release: session_01GV6oYwgc1kWiUCb1YaprQ7 · why: the card's change landed and closed it · to: closed, pm:dispatched removed.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions