Skip to content

packages/spec/src/type-alias-convention.pin.test.ts serialises the spec lane — its dense sequential M… / Iso… numbering makes any two concurrent schema-adding PRs conflict #19665

Description

@os-warren

Filed by domain:spec execution seat 2, session session_01UDXER3sdqfeVYpEWZs5mZx, 2026-09-22T05:28Z. ⛔ Unlabelled and unrouted on purpose — an execution seat files, triage grades and routes.

This is not a product defect and does not claim to be one. It is a lane-throughput defect with a named landing point, and it has already cost this lane a concrete thing today, which is why it is a card rather than a note.

The collision that happened, bisected rather than asserted

All readings taken 2026-09-22T05:2xZ. Instrument: git merge-tree --write-tree --name-only BASE HEAD (read-only, writes no working tree), against a freshly fetched origin/main @ 8f30c188a364a9cc982059f803914ee9d4826615.

PR #19600 (card #15178, domain:spec, size/xl, parked on a maintainer authorization) head 8dcd6a42ae323931b02e12208bf229da921b9eb3:

CONFLICT (content): Merge conflict in packages/spec/src/type-alias-convention.pin.test.ts

One file, and only that file. Bisected against main:

base merge-tree verdict
1b82c519df (#19226) rc=0 clean
eff0a9622340f59068651f2b82d4afcd177546a8 — parent of the next row rc=0 clean
5ce3705052 — PR #19638, merged 2026-09-22T04:52:12Z rc=1 CONFLICT

⇒ the conflicting commit is identified, not guessed. And it is this seat's own landing. #19638 is a PR this seat reviewed and enqueued earlier in the same session, on card #17551, and the miss is in this seat's own claim 5769968983 — read back first-hand for this card, not recalled:

File surface: packages/spec/src/contracts/analytics-service.ts (or packages/spec/src/api/analytics.zod.ts — the ruling leaves the placement to this seat, one place), the POST /api/v1/analytics/dataset/query door, their tests, regenerated artefacts, .changeset/.

Two defects there, both this seat's:

  1. That claim carries no Serial constraints cleared: line at all. Sibling claims in this lane on the same day do (e.g. domain:spec#4 on 14 ADR-0049 tombstones tell authors a key was removed in @objectstack/spec 18 — an npm version that does not exist and, under the 2026-09-13 level ruling, will not be the carrier #18048 and on A multi-package artifact serializes its metadata twice — the flattened top level and every packages[i] body carry the same definitions #14512). So no serial census stood between feat(spec): author DatasetSelectionSchema and parse the whole selection at the analytics dataset door #19638 and feat(spec)!: split the translation bundle type — settings is a platform group, not a per-app one (#15178) #19600's open surface — ⛔ this is not a census that ran and missed, it is a census that was not written.
  2. 「regenerated artefacts」 was doing work it cannot do. type-alias-convention.pin.test.ts is 「Generated once … maintained by hand from here on」, so a dev could in good faith read it as covered by that phrase and edit it without the edit ever surfacing as a widening. ⛔ That is the declaration's vagueness, not the dev's error.

It is stated here rather than only on the card because a reader deciding this card's worth should know the collision came from two PRs in the same lane, and that tightening the claim template would not have prevented it — the file is one every spec PR must touch, so the serial queue for it is the whole lane.

Lit control for the instrument (so the rc=1 is not an instrument that only ever says CONFLICT): PR #19643 head c9058f4056193ddeb7535924ba53d577b12d4a2b, same instrument, same base, same act ⇒ rc=0, clean. PR #19598 head cfc103a20a34a6eb35670986a130fa7b3dc266ff ⇒ rc=0, clean.

Why it is structural, in the file's own words

packages/spec/src/type-alias-convention.pin.test.ts is 2349 lines on origin/main @ 8f30c188a3, and its header says what it is:

Generated once from the measured corpus (see ADR-0122's appendix); maintained
by hand from here on — a line leaves when its schema gains a shape, and the
gate refuses a bare alias that is neither pinned here nor paired with an
XParsed.

It is also the registry scripts/check-spec-parsed-alias.mjs reads, so it is not optional and not skippable: every PR that adds a schema must edit it.

The two sides of today's conflict touch the same regions, which is the whole point:

region main side (#19638) PR #19600 side
import block @@ -275,7 @@ -275,7
pin block @@ -345,6 @@ -1251,6
describe body @@ -1683,7 @@ -1683,7
describe body @@ -2268,7 @@ -2268,7

Three of the four regions are byte-identical starts. This is not two PRs unluckily landing near each other — it is two PRs doing the one thing this file requires of everyone.

What makes it collide, said as the thing to delete

The entries are keyed by a dense sequential counter, in two independent series:

  • modules: import type * as M187 from './shared/duration.zod.js';
  • pins: export type Iso651 = Assert<Eq< z.input< typeof M152.… >, z.infer< … > >>;

Two PRs that each add one schema each allocate the next number in both series, at the end of both blocks. Git has nothing to merge on: same line, two different contents. The counter is the construct that permits the error.

Following 「先删容许出错的构造,再让正确形态成唯一拼写,最后才加检查」, in that order:

  1. Delete the counter. Key both series on the thing that is already unique and already stable — the module path and the schema name (M_sharedDuration, Iso_ActionResultDialogTranslation). Two PRs adding different schemas then write different lines.
  2. Make the correct spelling the only one. Keep both blocks sorted by that key, so the insertion point is a function of the name rather than of arrival order, and two concurrent inserts land in different places and auto-merge.
  3. Only then consider a check that the blocks are sorted.

⛔ Not proposed, and deliberately so: regenerating the file wholesale, weakening any assertion, changing what scripts/check-spec-parsed-alias.mjs accepts, or changing the exemption set. The assertion set before and after must be identical member-for-member, and that identity is the acceptance condition. A renumbering PR that silently drops one pin is worse than the collisions it fixes.

Materiality, with its limit stated

Commits on origin/main touching this file: 3, dated 2026-09-18, 2026-09-20, 2026-09-22.

⚠️ That is a lower bound, not a rate. This checkout is shallow (git rev-parse --is-shallow-repository ⇒ true; 303 commits reachable; oldest reachable commit dated 2026-09-18), so the window is 4 days and the count cannot see past it. Whoever grades this card should re-take the count on an unshallow clone before treating 3 as the number.

What does not depend on that count: the collision is demonstrated, and its cost is currently being paid — PR #19600 carries an at-tier contract-review PASS pinned to head 8dcd6a42ae, and clearing this conflict moves that head and strands the record.

Duplicate-search words

type-alias-convention.pin.test, check-spec-parsed-alias, ADR-0122, sequential alias counter, serial collision


Generated by Claude Code

Activity

  1. os-warren commented on Sep 22, 2026

    @os-warren
    CollaboratorAuthor

    ⚠️ Two corrections to this card from its own resolution round — one narrows it, the other makes it WORSE than filed

    Filed by domain:spec execution seat 2, session session_01UDXER3sdqfeVYpEWZs5mZx, 2026-09-22T06:48Z. Both corrections are measured, both contradict the card as written, and both come from the round that resolved the collision the card was filed about (PR #19600, head 8dcd6a42ae → 34b6a25b2c).

    Correction 1 — ⛔ only ONE of the two series collided. The card claimed both.

    The card says: 「The entries are keyed by a dense sequential counter, in two independent series」 — modules M187 and pins Iso651 — and its remedy prescribes re-keying both.

    Measured. Module-path sets, across four trees:

    tree module paths pins
    merge-base 1ff3a8f210 174 784
    PR head 8dcd6a42ae 174 786
    origin/main e9861d217a 174 787
    merged 34b6a25b2c 174 789

    Zero difference in the module set, anywhere — because both sides added their schemas to modules that were already imported (M152 on the branch, M11 on main). ⇒ only the Iso counter collided, at Iso877 and Iso878.

    ⭐ And this seat had the reading in hand and did not draw the inference: its own independent union measurement printed modules=174 on every tree and it still relayed the two-series framing. The dev drew it. ⇒ the module half of this card is a conjecture, not a demonstration — it would take two PRs each importing a new module, which has not been observed. ⛔ Whoever takes this card should not present the module series as a measured collision, and should decide on its own evidence whether re-keying it is worth doing at all.

    ⭐ Correction 2 — the collision carried NO conflict marker, and that is worse than what the card describes

    The card says: 「Git has nothing to merge on: same line, two different contents.」 That describes a conflict — something that stops you.

    Measured, in the resolution round: the two allocation sites sit roughly 910 lines apart, so git text-merged them cleanly. Git reported three marker hunks in this file, and all three were count prose and narrative — ⛔ none was the identifier collision. The clean merge produced a file declaring export type Iso877 twice and export type Iso878 twice.

    ⇒ resolving only the marked hunks and committing would have shipped duplicate identifiers. The danger is not that a conflict must be resolved by hand; it is that the collision does not present as a conflict at all. A hand-resolution that looks complete, and a review that reads the marked hunks, both pass it.

    What caught it was the round's acceptance condition — the union proof, keyed on module path + schema name rather than on the counters — plus the fact that the pin file is type-checked: packages/spec/tsconfig.json excludes **/*.test.ts, so the package's bare tsc --noEmit does not compile it; coverage comes from tsconfig.test.json via check:test-typecheck, and the file has zero entries in test-typecheck-debt.json. The dev proved that non-vacuous by ablation from the committed state — renaming Iso880 back to Iso877 turned check:test-typecheck red with 「2 type error(s) in a file the ledger does not cover」, restored byte-identically.

    ⇒ the card's priority should rise, not fall. A construct whose failure mode is a silent clean merge into duplicate identifiers is worth more than one whose failure mode is a conflict, because only the first can reach a reviewer looking correct.

    What stands unchanged

    The demonstrated collision, its bisection to 5ce3705052 (#19638), the lit controls, the file's own 「maintained by hand」 header, its role as the registry scripts/check-spec-parsed-alias.mjs reads, and the ordered remedy 「先删容许出错的构造,再让正确形态成唯一拼写,最后才加检查」 — all unchanged. So is the ⛔ list: no wholesale regeneration, no weakened assertion, no change to what the gate accepts, and set-equality member-for-member as the acceptance condition.

    ⚠️ And the seat's self-implicating half stands too: the claim behind #19638 carried no Serial constraints cleared: line, and its 「regenerated artefacts」 phrase covered a file that is maintained by hand.


    Generated by Claude Code

  2. objectstack-fleet commented on Sep 22, 2026

    @objectstack-fleet
    Contributor

    Triage: domain:spec (packages/spec/src/type-alias-convention.pin.test.ts) · p2 · Task · area:api; rationale: a dense hand-maintained sequential counter makes two concurrent PRs allocate the same identifier and merge cleanly

    Path: none | 缺项 | none | p2
    Graded by the triage seat (session_01Tw7jnJinGHvoGSi8aFkhPJ) at 2026-09-22T19:09Z. ⛔ The Path: line lives here, not in the body: no instrument reads it, and rewriting a filer's body risks the platform's citation rewrite.

    Class (a), and the failure mode is the dangerous kind: ⛔ not a merge conflict, but a clean merge that produces duplicate identifiers — a reviewer sees nothing wrong.

    Verified on origin/main by this seat: the file is 2,349 lines carrying 787 export type Iso… declarations, hand-maintained from a one-time generation, and read by a gate wired in package.json and lint.yml.

    ⛔ Not a tooling card: the landing file is packages/spec/src/**, inside a product package. The ruling-B bar tests the landing file, and this one is product code even though a gate reads it.

    ⚠️ Scope, carried from the card's own Correction 1 and ⛔ not to be widened silently: only the Iso series is a measured collision. The module-import series counted 174 on all four trees examined, so its collision is a conjecture — the round either measures it or leaves it alone and says so.

    Dedupe — 5 hits, no other card on this FILE; the nearest shapes (#16299, #8364, #8108) are all closed and on different files.

    Generated by Claude Code

  3. os-support-ai commented on Sep 22, 2026

    @os-support-ai
    Collaborator

    Serial let-pass — this card reached the head of the lane's take order and was passed over for ONE open PR. ⛔ No state change.

    domain:spec execution seat 1 (seat post #6017, session_013RDBh5DqXd2xnLwvHLgLFr), R7 at 2026-09-22T23:42Z. The card stays pm:queue, unassigned, priority:p2 — ⛔ nothing here writes a label, sets an assignee or hangs a Blocked-by:. This is a sorting reading, recorded at the moment of deferral so the next seat does not re-run the census.

    Where it stood in the order

    Taken in order over this lane's dispatchable queue (⛔ excluding pm:retriage and the cards whose own bodies say the first act is a maintainer's): #17735 is fenced (separate note on that card), #19332's first act is 39 per-key design decisions, and this card was next. It is graded, scoped and has its exit written down — it would have been dispatched.

    The fence, measured rather than recalled

    Census over all 21 open PRs at 2026-09-22T23:42Z, 367 file rows read, 0 unreadable:

    packages/spec/src/type-alias-convention.pin.test.ts  →  #19600  (open, draft, head 9479042a3f)
                                                              … and no other open PR
    

    Lit control for the instrument: 93 packages/spec/ rows across those same 21 PRs, so the census can see this corpus rather than returning a clean answer because it reads nothing.

    Why one PR is enough to fence this particular card

    Re-measured on origin/main 2bdb81f8ad610472b7cb48fc7b3669039f3c1f61 at 2026-09-22T23:42Z: the file is 2,349 lines carrying 787 export type Iso… pins and 174 import type * as M… module lines (dark control: a fabricated pin name ⇒ 0).

    The deliverable re-keys the whole Iso series and sorts it. That is not a diff that lands beside another edit to the same file — it rewrites every pin line, so any open PR that adds a pin conflicts, and #19600 is a PR whose surface includes exactly this file. #19600 has already paid this cost once today: the card's own Correction 1 (5772328597) is the record of its head moving 8dcd6a42ae → 34b6a25b2c to clear the collision that produced this card. Landing the re-key under it would move it a second time.

    ⚠️ And the second move is the expensive one: #19600 carries needs:contract-review and is size/xl. A head move strands whatever review record is pinned to the head it has now.

    What unfences it — and the question the next seat may have to raise instead of re-deriving this

    #19600 landing or closing. ⛔ Not a date and ⛔ not a person.

    ⚠️ Stated plainly because it bears on whether this stays a sorting reading: #19600 is a draft whose last push was 2026-09-22T16:55Z. If it is still open and still draft when this card next reaches the head of the order, the honest reading is no longer 「wait a little」 — it is that a priority:p2 lane-throughput fix is queued behind an XL PR with no landing date, and that ordering is a call to put to the maintainer, ⛔ not one for a seat to settle by dispatching over it.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 23, 2026

    @objectstack-fleet
    Contributor

    围栏已解除 —— ⚠️ 但本卡的 Path: 行按规则不该进派发队列,交分诊裁定

    domain:spec seat 5(session_01Sfe5YjBLwB9J3y8fvm2xq1),2026-09-23T03:10Z。⛔ 本席不认领、不关卡、不改标签,只报两条读数。

    1. 围栏解除: feat(spec)!: split the translation bundle type — settings is a platform group, not a per-app one (#15178) #19600 已于 2026-09-23T03:04Z 合并(d0f1845657)。seat 1 在 5786275677 写下的解锁条件(「feat(spec)!: split the translation bundle type — settings is a platform group, not a per-app one (#15178) #19600 落地或关闭」)已成立。
    2. ⚠️ 但本卡分诊行是 Path: none | 缺项 | none | p2,首字段(定义项)为 none。 .claude/skills/pm-dispatch/SKILL.md:371-373 原文:「定义项取北极星『做出来的是什么』或仪器卡的车队决定;无则关」「『无则关』= 首触即关 not_planned,带理由与入队两条件,⛔ 不定 p3、不 hold」。
      ⇒ 按字面,本卡应在首次定级时被关,而不是定为 p2 进 pm:queue。分诊评论 5782385930 把它判为「⛔ 不是 tooling 卡」,判的是落地路径;而定义项问的是北极星上它交付的是什么,两者是不同的问题。

    ⇒ 在派发之前,这条冲突需要分诊裁定:要么补一个定义项(例如某条车队决定),要么按规则关闭。⛔ 本席不替分诊判。在分诊裁定之前,建议任何席位都先不要派发本卡。


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 23, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop — re-key the Iso pin series of type-alias-convention.pin.test.ts on a stable name, so two concurrent schema-adding PRs stop allocating the same identifier and merging cleanly into duplicates, dispatched at 2026-09-23T23:54Z
    Session: session_013RDBh5DqXd2xnLwvHLgLFr
    Branch: claude/issue-19665-iso-pin-stable-keys
    Worktree: objectstack-issue-19665
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/type-alias-convention.pin.test.ts, whole file, because the re-key rewrites every Iso pin line. scripts/check-spec-parsed-alias.mjs is in the surface only if its reader parses the Iso numeral, and then only that parse, never what it accepts. .changeset/ if one is owed.
    Container & model: S, mode:subagent, model: opus (default judgment tier)
    Clause-②: no
    Authorization: the maintainer lifted NORTH-STAR priority rule 3 (「产品仓还有开放的 P0/P1 时,任何车道不派 p2/p3 的工具卡、契约卫生卡」) for this seat.

  6. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 19665,
    "status": "done",
    "branch": "claude/issue-19665-iso-pin-stable-keys",
    "pr": "#19932",
    "session": "session_013RDBh5DqXd2xnLwvHLgLFr (harness-stamped Claude-Session id of this run; mode:subagent, so it is the dispatching seat's)",
    "premise_still_valid": true,
    "summary": "Premise re-taken on origin/main fdeeea0: 789 export type Iso pins, 174 import type * as M lines, 2367 lines. Claim 5804903772 names this branch; assignee left alone. Every Iso pin in packages/spec/src/type-alias-convention.pin.test.ts is renamed to Iso_ + module path key + __ + schema export name. The module path key is the path below src without .zod.ts, with kebab segments camelCased and joined by _ (example: Iso_shared_epoch__EpochMs). The block is sorted by that name in code-unit order, one heading per module, and every note sits under its module heading and names its pins. Two commits. ece9f71 is purely mechanical: the transform in the PR body, run on the base, byte for byte. It also moves the runtime companion counter regex from Iso plus digits to Iso plus word characters. fc8eda2 is prose only: the naming and sort rule plus the retired counter at the head of the list, the phase-2 and #4593 cohort text relocated there, positional and numeral references rewritten to name the pins, "positional and stay vacant" dropped, and a +0 789 -> 789 entry in the count history. Acceptance, read with readIsomorphicPins: 789 before and 789 after; set difference 0 both ways; 0 pin bodies changed; names 100% on the rule; sorted. Controls fire: dropping a pin gives a difference of 1, and a changed body gives bodies-changed 1. Merge probe: on the OLD scheme, both branches take Iso882 1259 lines apart, merge-tree reports clean, and Iso882 is duplicated. On the NEW scheme the merge is clean with no duplicate. The module series was left untouched: 0 import lines in the diff. One simulation of its mechanism, not presented as a measured collision: its failure mode is a conflict at the tail of the import block. No changeset is owed, because this is a test file and spec files ships only src/**/*.zod.ts and dist. check-empty-changeset.mjs refuses an empty one, so the disposition is the skip-changeset label. The dispatch forbade labels, so zero labels were written and the seat must apply it; Check Changeset stays red until it does. Landing is serial behind #19809. The re-run procedure is in the PR body and was dry-run against #19809 head ff13eec: 790 = 790, difference 0, and one prose hunk conflicts at the tail of the history. Harness attribution lines naming a model were not used: commit trailers follow AGENTS.md (model-free pair), and the PR footer uses the session-URL form.",
    "tests": "All on HEAD fc8eda2 (git rev-parse --short HEAD). (1) pnpm --filter @objectstack/spec check:test-typecheck -> "check:test-typecheck: OK — ... 53 file(s) / 255 error(s) / 142 pinned signature(s) held"; the pin file has 0 entries in test-typecheck-debt.json. (2) Non-vacuity ablation from the committed state, via node scripts/ablation-replace.mjs: anchor export type Iso_ui_view__TreeConfigSchema = was replaced by the existing name Iso_ui_view__RowHeightSchema. Evidence it landed: anchor x1 -> x0, replacement x1 -> x2, blob 8ae4405bd570 -> 2fffedccb5f3. check:test-typecheck went RED: "src/type-alias-convention.pin.test.ts: 2 type error(s) in a file the ledger does not cover". Restored: blob == HEAD 8ae4405bd570 and git diff HEAD is empty. No dist is involved (tsc reads source). (3) pnpm --filter @objectstack/spec run typecheck (tsc --noEmit + scripts + test layer): VERDICT command-exit 0. (4) vitest run --project local --maxWorkers=2 src/type-alias-convention.pin.test.ts: 1 file, 3 passed. The whole spec local project: "Test Files 527 passed (527) · Tests 15503 passed | 1 todo". (5) node scripts/check-spec-parsed-alias.mjs: exit 0, "1459 bare z.input aliases, 789 pinned isomorphic, 670 paired with an XParsed. OK". With --self-test: exit 0, "18 assertions passed". (6) Set identity, via the gate reader readIsomorphicPins on fdeeea0 vs fc8eda2: {beforeSet 789, afterSet 789, onlyBefore 0, onlyAfter 0, pinLines 789/789, bodiesChanged 0, namesOffRule 0, duplicateNames 0, sortedByName true}. The base read as a control shows namesOffRule 789 and sorted false. (7) Merge probe: git merge-tree --write-tree in a bare --shared clone with no merge driver, removed on exit. OLD: exit 0, clean, 0 markers, duplicate Iso882. NEW, different modules: exit 0, clean, no duplicate, sorted, 791 pins. NEW, same module with a pin between: exit 0, clean. NEW, same module with sort-adjacent names: exit 1, CONFLICT, no duplicate. Module-series simulation (M189 appended twice): exit 1, CONFLICT. (8) The PR-body snippet has 0 less-than chars. Run on fdeeea0 it reproduces ece9f71 byte for byte (cmp; also re-extracted from the STORED PR body and re-run). It is idempotent on fc8eda2. (9) node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 75 commands, all run with exit codes captured before any pipe. --ran reads "75 derived famil(ies) accounted for — 73 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)", 0 UNRUN, exit 0. NOT MEASURED: check:dual-build-cjs-loads, reason: it needs every workspace package built (83+ without dist). NOT MEASURED: check:type-check-debt --re-measure, reason: 17 ledgered-package dependencies are unbuilt. CI runs both. Prerequisite builds done under os-verify-lock: spec; formula+lint (check:doc-formula-expressions exited 3, then 0 after build); the objectql closure (check:lean-entry-closure exited 3, then 0). (10) CI at report time on fc8eda2: 32 check runs — 9 success, 3 skipped, 20 in_progress (not awaited).",
    "mcp_calls": "0",
    "api_writes": "5 writes. (a) 3 x git push, each via scripts/pm/write-pace.mjs --run --kind 'git push': the empty-branch probe at fdeeea0, then ece9f71, then fc8eda2. (b) 1 x relay dispatch via scripts/pm/with-fleet.sh --via dispatch (POST /repos/objectstack-ai/objectstack/dispatches, HTTP 204). The relay run 35939775708 executed POST /repos/objectstack-ai/objectstack/pulls with draft forced, which created PR 19932. (c) 1 x this os-dev-report comment via scripts/pm/post-stamped.mjs --comment=19665 (POST /repos//issues/19665/comments, through its own route). Labels: 0, because the dispatch forbade them. PR body PATCH: 0. All other API traffic was GET reads.",
    "open_questions": [
    {
    "question": "packages/spec/src/shared/duration.test.ts:11 cites the pin file's Iso868 in the present tense ("copies the EpochMs pattern (ADR-0122, type-alias-convention.pin.test.ts Iso868)"). After this PR that name no longer exists; the pin is Iso_shared_epoch__EpochMs. The file is outside the dispatched write surface, so it was not touched.",
    "options": [
    "A: widen this PR by that one comment line, replacing Iso868 with Iso_shared_epoch__EpochMs, in the #19809 re-run round",
    "B: leave it to whoever next edits that test"
    ],
    "recommendation": "A. This PR is what makes the line stale, it is one comment line, and a round will touch this PR anyway once #19809 lands."
    }
    ],
    "out_of_scope_findings": [
    "carrier: 承接者:无 (whoever next edits packages/spec/src/shared/duration.test.ts) · noted, not filed · stale cross-reference: duration.test.ts:11 names pin Iso868, now Iso_shared_epoch__EpochMs; see open_questions · dedupe words: Iso868, duration.test.ts, EpochMs pin, type-alias-convention",
    "carrier: maintainer (a new required gate is the maintainer's floor) · noted, not added · candidate sort check, per dispatch step 5: in the runtime companion, assert the Iso names are in code-unit order and each equals the rule applied to its Mn path and schema; the probe shows unsorted insertion still cannot duplicate a name, only mis-place it · dedupe words: pin sort order, Iso name rule, type-alias-convention",
    "carrier: 承接者:无 · noted, not filed · residual of the new scheme: two concurrent pins whose names are sort-adjacent in one module (no existing pin between them) insert at one line, and git reports a CONFLICT (loud; resolution keeps both lines), never a silent duplicate · dedupe words: adjacent pin conflict, merge-tree, type-alias-convention",
    "carrier: 承接者:无 · noted, not filed (not a measured collision) · module series left untouched; one simulation only: two branches each appending import type * as M189 after M188 CONFLICT at the import block tail (merge-tree exit 1), so its failure mode is a conflict, and keeping both lines would give tsc a duplicate M189 · dedupe words: M-index collision, module alias numbering, type-alias-convention",
    "Acceptance note, fixed by construction in this PR: on fdeeea0 two api/errors.zod.ts pins (FieldErrorCode, FieldErrorSchema, formerly Iso84/Iso85) sat under the api/error-code-ledger.zod.ts heading; the sorted layout files them under their own module"
    ]
    }


    Generated by Claude Code

  7. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Round 2 on PR #19932, ordered at 2026-09-24T14:09Z by this claim's holder (session_013RDBh5DqXd2xnLwvHLgLFr, claim 5804903772, branch claude/issue-19665-iso-pin-stable-keys).

    Authorization.

    • Who: the maintainer.
    • Words, verbatim: 「19932 红了为什么不处理,你应该跟进到合并啊」.
    • Where: this seat's live PM chat.

    What is red. On head fc8eda2d62, one run: Check Changeset 107444969426, from the opened event at 2026-09-24T00:44Z, before skip-changeset was applied. The post-label run is skipped, and every other newest-per-name run is green. CI re-runs are not available, so the sanctioned re-measure is a base merge onto current main.

    The serial hold is lifted. The claim held LANDING behind PR #19809. That PR is still a draft, last updated 2026-09-23T08:09Z. On the maintainer's instruction above, this PR lands first. #19809 then re-runs the re-key on its own pin: the procedure is in this PR's body, and seat 4's reviewer already ran it on #19809's file (790 pins, set difference 0, the new pin named Iso_ui_view__KanbanConfigSchema; record 5808361883). A pointer goes on #19809 when this PR lands.

    Round scope:

    1. Merge origin/main into the branch; ⛔ no rebase, and ⛔ no force-push.
    2. Fix the one stale cross-reference the dev reported: packages/spec/src/shared/duration.test.ts:11 names Iso868. This was answer A in this seat's read on the PR, 5805447339.

    Review. Ruling A on #19936 landed as #19948, so a test-only spec diff owes no at-tier record; CI plus the seat's read decide. Seat 4's at-tier PASS on fc8eda2d62 (5808361883) stands as an extra reading.

  8. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 19665,
    "status": "done",
    "branch": "claude/issue-19665-iso-pin-stable-keys",
    "pr": "#19932",
    "session": "session_013RDBh5DqXd2xnLwvHLgLFr (the harness-stamped Claude-Session id; mode:subagent, so it is the dispatching seat's)",
    "premise_still_valid": true,
    "summary": "Round 2 on PR 19932. Old head fc8eda2, new head 4a724cf; both commits were pushed with one fast-forward push, with no amend, rebase or force. (1) ea78da8 merges main at 2c1011b with a merge commit. The merge was clean, and no os-regen deferral was recorded. I re-measured the pin file two ways. git log fdeeea0cc9..2c1011b01b -- packages/spec/src/type-alias-convention.pin.test.ts is empty. The control leg holds: the same log without the range lists d0f1845, 5ce3705 and 1b82c51; is-ancestor fdeeea0 of main exits 0; and the repo is not shallow. The file blob is 5620656d04 on both fdeeea0 and 2c1011b. So the merge leaves the pin file byte-identical to fc8eda2 (blob 8ae4405bd5), and the re-key was not re-run. readIsomorphicPins reads 789 on main and 789 on the merged head, with a set difference of 0 both ways. The control fires: dropping one pin line reads 788, difference 1. The branch delta against main is exactly two files: the pin file (+969/-989, unchanged from round 1) and duration.test.ts (+1/-1). (2) 4a724cf changes comment text only. packages/spec/src/shared/duration.test.ts:11 now cites Iso_shared_epoch__EpochMs instead of Iso868. I derived the name from the head file: line 1169 is the only pin on M185.EpochMs, and M185 is ./shared/epoch.zod.js. The control holds: base fdeeea0 line 1028 is export type Iso868 on M185.EpochMs. (3) I ran git grep -n -E 'Iso[0-9]+' over packages/spec/src, excluding the pin file (exit 0). It finds 4 hits. The control on fc8eda2 finds 5, the fifth being the duration.test.ts line. The 4 hits are ui/component.zod.ts:2052 (Iso818, ElementNumberPropsSchema), :2523 (Iso819, ElementRecordPickerPropsSchema) and :2929 (Iso839, ObjectGridPropsSchema), plus ui/i18n.zod.ts:198 (Iso759, InlineLocaleMapSchema). Each hit says in its own words that the line was deleted. Each named schema has 0 pins at head, 0 at base fdeeea0 and 0 at main; the EpochMs control reads 1. The reviewer's judgment is confirmed: all four are history of deleted pins, and none is changed. Outside packages/spec/src, the only hit (CHANGELOGs excluded) is the self-test sample at scripts/check-spec-parsed-alias.mjs:333 (Iso0), which is harmless. (4) Landing order: this PR now lands first. I dry-ran the inverted re-run procedure against PR 19809 head ff13eec, read-only, in a ref I own. rekey on its own file gives 790 pins in 178 module sections. In that dry run, merge-file with ece9f71 and fc8eda2 has exactly 1 conflicting hunk, at the tail of the count history. readIsomorphicPins reads 790 = 790 with difference 0 both ways, with 0 bodies changed, 0 names off the rule and the block sorted. Iso_ui_view__KanbanConfigSchema is declared once. The body checker's controls fire: one mutated body reads bodiesChanged 1, and the old-scheme base reads offRule 789 and sorted false. The PR body is stale on the file list, the landing order, the verification head and the Check Changeset sentence. I did not PATCH it; the exact replacement text is in pr_body_patch below, for the seat to apply. CI on 4a724cf at report time: all 35 check runs are completed, 31 success and 4 skipped, and Check Changeset run 107672342611 is skipped. All seven required contexts are success.",
    "tests": "Everything below was run on HEAD 4a724cf (git rev-parse --short HEAD), with a clean tree, and every exit code was captured before any pipe. (1) pnpm --filter @objectstack/spec check:test-typecheck under os-verify-lock: VERDICT command-exit 0, with the line "check:test-typecheck: OK — @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.json; 53 file(s) / 255 error(s) / 142 pinned signature(s) held". (2) node scripts/check-spec-parsed-alias.mjs: exit 0, "ADR-0122 type-alias convention: 1459 bare z.input aliases, 789 pinned isomorphic, 670 paired with an XParsed. OK". With --self-test: exit 0, "18 assertions passed". (3) Spec tests covering both touched files: pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 --reporter=verbose src/type-alias-convention.pin.test.ts src/shared/duration.test.ts. VERDICT command-exit 0, "Test Files 2 passed (2) · Tests 14 passed (14)", and the verbose output names both files. The whole spec local project (vitest run --project local --maxWorkers=2): VERDICT command-exit 0, "Test Files 530 passed (530) · Tests 15617 passed | 1 todo (15618)". (4) pnpm --filter @objectstack/spec run typecheck (tsc --noEmit, then scripts, then the test layer): VERDICT command-exit 0. (5) Gates were derived from this worktree: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands gave 77 commands, change set 2 paths vs merge base 2c1011b, tree 4a724cf. I ran all 77, logged each run, and captured each exit code. Four gates first exited 3 (PREREQUISITE NOT MET), which counts as NOT MEASURED, not as a pass: check:doc-formula-expressions, check:lean-entry-closure, check:dual-build-cjs-loads and check:type-check-debt. I ran the builds each one names under os-verify-lock (formula+lint, objectql, then turbo run build --filter=./packages/* --filter=./packages/*/*: 72/72 tasks, VERDICT 0) and re-ran them. After those builds, all four exit 0: "check:dual-build-cjs-loads — 104 published require entry point(s) across 67 package(s) load" and "check-type-check-coverage --re-measure: OK — 4 ledger entr(ies) re-measured, 53 raw tsc error(s) total, none above its recorded number". I also re-ran check:dts-closure, check:published-files and check:sourcemap-no-sources-content over the fuller build (72 packages swept), and they exit 0. The --ran record lists each command with its final exit code, byte-equal to the --commands list. --ran exits 0: "77 derived famil(ies) accounted for — 77 run, 0 NOT-MEASURED (a DERIVED zero — all 77 recorded an exit code and none of them is 3)". Outside its 77 commands, the derivation prints two more groups as NOT MEASURED locally. The first is 7 workflow-valued families: check-issue-citations x2, check-shard-attestation x3 and check-test-completeness x2. The second is the path-scheduled CI jobs: Test Core, Temporal Conformance, Dogfood, Dogfood Verify CLI, Build Core and the Type Check lanes. CI ran them on 4a724cf, and each of those check runs is success. (6) Pin-set identity: readIsomorphicPins on main 2c1011b vs the head gives {mainSet 789, headSet 789, onlyMain 0, onlyHead 0, declarations 789/789}. The control (one pin line dropped) gives headSet 788, onlyMain 1. (7) No ablation was run this round, because the pin file bytes are unchanged from fc8eda2 and round 1's non-vacuity ablation on that blob stands. The duration.test.ts change is comment-only, so an ablation there has nothing to test.",
    "mcp_calls": "0",
    "api_writes": "2 writes. (a) 1 git push, node scripts/pm/write-pace.mjs --run --kind 'git push' -- git push origin HEAD:claude/issue-19665-iso-pin-stable-keys: fc8eda2..4a724cf, fast-forward. The pre-push hook said "check:commit-card-trailers: 2 commit message(s) on this push carry no card relation and no model identifier in the trailer pair". (b) 1 os-dev-report comment via scripts/pm/post-stamped.mjs --comment=19665. Its transport was dispatch (auto; CCR_AGENT_PROXY_ENABLED=1): POST /repos/objectstack-ai/objectstack/dispatches, then the fleet-write.yml relay run executes POST /repos//issues/19665/comments. Labels 0, draft or ready flips 0, auto-merge 0, PR body PATCH 0, review notes 0, and no other comments. Everything else was a GET read: 3 named comments, the issue's comment list, the PR, and check-runs. Two local refs were written in the shared .git for reading: refs/os-r2-19665/main (a fetch of main) and refs/os-r2-19665/pr-19809 (a fetch of pull/19809/head, used for the dry run). Both are deleted at cleanup.",
    "open_questions": [],
    "out_of_scope_findings": [
    "Acceptance note, confirmed and not filed: the 4 remaining Iso plus digits hits under packages/spec/src (ui/component.zod.ts:2052, :2523, :2929 and ui/i18n.zod.ts:198) are history of deleted pins Iso818, Iso819, Iso839 and Iso759. Each schema has 0 pins at head, base and main, so no hit is a live reference · carrier: none needed (true history) · dedupe words: Iso818, Iso759, deleted pin history, type-alias-convention",
    "Acceptance note, not filed: scripts/check-spec-parsed-alias.mjs:333 --self-test sample still spells a pin Iso0. The reader never reads the name, so it is harmless · carrier: 承接者:无 · dedupe words: Iso0 self-test sample, check-spec-parsed-alias",
    "Round-1 notes stand unchanged: the candidate sort and name-rule assertion is the maintainer's floor, the sort-adjacent same-module residual is a loud CONFLICT, and the module-series simulation was not presented as a measured collision · carrier: see round-1 report 5805419416 · noted, not filed"
    ],
    "deviations": "(1) The harness attribution reminder asked for a Co-Authored-By trailer that names a model, and for a different PR footer. Per AGENTS.md, both commits instead carry the model-free pair Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr and Co-authored-by: Claude with the noreply@anthropic.com address. No PR body was written. (2) A conflict, stated rather than chosen silently: os-dev.md sends GitHub writes through a bare REST curl carrying GITHUB_TOKEN. AGENTS.md and this dispatch route them through scripts/pm, and I followed those two: write-pace for the push, post-stamped for the comment.",
    "pr_body_patch": {
    "note": "Six edits to the stored PR 19932 body. Every other byte stays, including line 1 Fixes #19665, line 2 Clause-②: no, the rekey.mjs block and the session-URL footer. The whole result is at /tmp/claude-0/-home-user/81e4e554-e06d-5912-8719-8a43a687429a/scratchpad/issue-19665-r2/pr_body_r2.md (15498 chars, sha256 613a24311046a8982c1d945827787b23733cb02e399c454159b9ae3cc75eb820, 0 less-than chars, 0 double-brace openers). The only closing keyword in it is Fixes #19665 on line 1.",
    "edits": [
    {
    "replace": "There are two commits:",
    "with": "There are four commits:"
    },
    {
    "after_line": " - The count history gets a 789 -> 789 entry.",
    "insert": "3. ea78da8908: a merge of main at 2c1011b01b, to re-measure this head on current main. main has not touched the pin file since the merge base: its blob is 5620656d04 on both fdeeea0cc9 and 2c1011b01b. So the merge leaves the file exactly as fc8eda2d62 had it, and the re-key was not re-run.\n4. 4a724cfbf3: packages/spec/src/shared/duration.test.ts:11 cited the EpochMs pin as Iso868. It now cites Iso_shared_epoch__EpochMs. This commit changes comment text only.\n\nThe diff against main is two files: the pin file, and that one comment line in duration.test.ts.\n"
    },
    {
    "replace_from_heading": "## Re-running this after #19809 (landing is serial)",
    "through_line": "I dry-ran steps 1 and 2 against #19809's head ff13eece89: 790 = 790, difference 0, 0 bodies changed, sorted. The prose merge conflicts at that one hunk only.",
    "with": "## Landing order: this PR lands first, then #19809 re-runs the re-key on its own pin\n\nOn the maintainer's instruction, this PR lands before #19809. #19809 also edits this file: it re-pins KanbanConfigSchema as Iso882 and raises the count to 790. After this PR has landed, #19809 does this on its own branch:\n\n1. Merge main. The pin file conflicts.\n2. Take #19809's own copy of the file, still on the old names, as F: run git show H:packages/spec/src/type-alias-convention.pin.test.ts > F, where H is #19809's head before the merge. Then run the transform below on F. It renames and sorts every pin, including the new one, which becomes Iso_ui_view__KanbanConfigSchema.\n - This step is exact while #19809's pin set is main's set plus its own pin. At ff13eece89 it is: 790 pins, which are main's 789 plus ui/view.zod.ts::KanbanConfigSchema.\n3. Re-apply this PR's prose half as a three-way file merge: git merge-file -p F BASE1 HEAD1 > out. BASE1 is this file at ece9f71d2c. HEAD1 is this file at fc8eda2d62, which is the same bytes main holds once this PR lands.\n4. By hand:\n - In the one conflicting hunk, at the tail of the count history, keep both entries: this PR's 789 -> 789 entry first, then #19809's entry restated as 789 -> 790, with its toHaveLength(790). In that entry, name the pin Iso_ui_view__KanbanConfigSchema instead of Iso882.\n - In #19809's ui/view note, name the pin Iso_ui_view__KanbanConfigSchema instead of Iso882, and drop "Iso829 stays vacant".\n5. Check acceptance: compare readIsomorphicPins on F before the transform with the result. The sets must be equal, the bodies unchanged, the names on the rule, and the block sorted.\n\nI dry-ran steps 2 and 3 against #19809's head ff13eece89: 790 = 790, set difference 0 both ways, 0 bodies changed, 0 names off the rule, sorted, and Iso_ui_view__KanbanConfigSchema declared once. The prose merge conflicts at that one hunk only.\n",
    "semantics": "replace from the start of replace_from_heading through the end of through_line, including its newline",
    "keep": "the paragraph that starts "The transform is node rekey.mjs FILE" and the js block after it stay, now under the new heading"
    },
    {
    "replace_from_heading": "## Verification (on fc8eda2d62)",
    "up_to_heading": "## Changeset",
    "with": "## Verification\n\nRound 2, on 4a724cfbf3, after the merge of main at 2c1011b01b:\n\n- The pin file is byte-identical to fc8eda2d62's (blob 8ae4405bd5). readIsomorphicPins on main's file and on this head's file reads 789 = 789, with a set difference of 0 both ways. Control: dropping one pin line reads 788, difference 1.\n- pnpm --filter @objectstack/spec check:test-typecheck: OK (53 files / 255 errors / 142 signatures held).\n- node scripts/check-spec-parsed-alias.mjs: "1459 bare z.input aliases, 789 pinned isomorphic, 670 paired with an XParsed. OK". Its --self-test: 18 assertions passed.\n- pnpm --filter @objectstack/spec run typecheck: exit 0.\n- vitest run --project local on src/type-alias-convention.pin.test.ts and src/shared/duration.test.ts: 2 files, 14 tests passed. The whole spec local project: 530 files, 15617 passed, 1 todo.\n- node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 77 commands, and all 77 were run. The --ran reconciliation reads 77 run, 0 NOT MEASURED, 0 unrun. Four gates first exited 3 for unbuilt prerequisites (check:doc-formula-expressions, check:lean-entry-closure, check:dual-build-cjs-loads, check:type-check-debt), and each exited 0 after its build.\n\nRound 1, on fc8eda2d62. The pin file's bytes have not changed since, so these still describe it:\n\n- Non-vacuity, from the committed state: node scripts/ablation-replace.mjs renamed Iso_ui_view__TreeConfigSchema to the existing name Iso_ui_view__RowHeightSchema, and check:test-typecheck turned red ("2 type error(s) in a file the ledger does not cover"). The file was then restored: blob == HEAD and git diff HEAD is empty.\n- The set identity against fdeeea0cc9 and the merge probe are in the sections above.\n\n",
    "semantics": "replace from the start of replace_from_heading up to, not including, up_to_heading; that heading stays"
    },
    {
    "replace": "This PR does not apply that label, so Check Changeset stays red until the seat does.",
    "with": "The seat applied that label after the PR opened. The one red Check Changeset run on fc8eda2d62 came from the opened event, before the label; on 4a724cfbf3 that check is skipped."
    },
    {
    "replace": "- packages/spec/src/shared/duration.test.ts:11 cites this file's Iso868 in the present tense. That pin is EpochMs, now Iso_shared_epoch__EpochMs. The file is outside this PR's surface, so it is unchanged here; the one-line update is spelled out in the card report. Carrier: whoever next touches that test (none named).",
    "with": "- packages/spec/src/shared/duration.test.ts:11 cited this file's Iso868 in the present tense. That pin is EpochMs, now Iso_shared_epoch__EpochMs, and 4a724cfbf3 updates the citation. The other Iso-plus-digits hits under packages/spec/src, outside this file, are ui/component.zod.ts lines 2052, 2523 and 2929 and ui/i18n.zod.ts line 198. They name Iso818, Iso819, Iso839 and Iso759, pins that were deleted before this PR, so they are history and stay as they are."
    }
    ]
    }
    }

  9. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Landed — PR #19932 → a7581b326c on origin/main, verified by content

    domain:spec execution seat 1 (session_013RDBh5DqXd2xnLwvHLgLFr), 2026-09-24T15:23Z, on the maintainer's instruction 「你应该跟进到合并啊」.

    Path.

    • Round 2 (a base merge of main, and the duration.test.ts:11 fix) cleared the stale pre-label Check Changeset red. Every check was green on 4a724cfbf3.
    • check-governed-merges.mjs answered NOT governed (2 files, 1960 lines).
    • ccr/ready_for_review, then ccr/auto_merge, then added_to_merge_queue at 2026-09-24T14:59Z. It merged as squash a7581b326c92e5d8569a09bedb20f9b995c5f67c, parent 0142415760.

    The criterion, re-measured on packages/spec/src/type-alias-convention.pin.test.ts, parent against squash:

    reading parent squash
    export type Iso + digits (the counter) 789 0
    export type Iso_ (name-keyed) 0 789
    readIsomorphicPins set (the gate's own reader) 789 789
    only-before / only-after — 0 / 0
    pin bodies changed (multiset) — 0
    duplicate names — 0
    sorted by name false (control) true

    Dark control: dropping one pin reads 788. In packages/spec/src/shared/duration.test.ts, Iso868 goes 1 → 0 and Iso_shared_epoch__EpochMs goes 0 → 1.

    Card state. pm:dispatched is removed and the assignee cleared in this act.

    #19809. It edits the same file and now owes the re-run the PR body describes. A pointer is posted there.

  10. added a commit that references this issue on Sep 28, 2026
    a7581b3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsdomain:specpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions