Skip to content

test(spec): name each isomorphic pin for its module and schema, sorted - #19932

Merged
os-support-ai merged 4 commits into
mainfrom
claude/issue-19665-iso-pin-stable-keys
Sep 24, 2026
Merged

os-support-ai merged 4 commits into
mainfrom
claude/issue-19665-iso-pin-stable-keys

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #19665
Clause-②: no

What this changes

packages/spec/src/type-alias-convention.pin.test.ts keyed its isomorphic pins on a dense, hand-kept counter (Iso0 … Iso881). Two branches off one base each took "the next free number" for a different schema, at insertion points far apart, and git merged them cleanly into two declarations of one name: Iso871 once already, then Iso877 / Iso878 on #19600, where only check:test-typecheck caught it.

Each pin is now named for the pair that is already unique to it, its module path and its schema export name:

  • The name is Iso_, then the module path below packages/spec/src without .zod.ts, with each kebab segment camelCased and the segments joined by _ (so ai/knowledge-document.zod.ts becomes ai_knowledgeDocument), then __, then the schema export name. For example: Iso_shared_epoch__EpochMs.
  • The block is sorted by that name in code-unit order (the order LC_ALL=C sort gives), with one heading per module. Each note about a module's pins sits under that module's heading and names the pins it covers. The schema alone now decides both a new pin's name and where it goes.

There are four commits:

  1. ece9f71d2c: the mechanical half. It is the transform below, run on fdeeea0cc9, byte for byte. That includes the runtime companion's pin counter, whose regex now matches the name family (Iso followed by word characters) instead of Iso followed by digits.
  2. fc8eda2d62: prose only.
  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.
  4. 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.

The diff against main is two files: the pin file, and that one comment line in duration.test.ts.

No assertion is added and none is weakened. The exemption set is unchanged, and nothing is regenerated from a corpus measurement. scripts/check-spec-parsed-alias.mjs is untouched, because its reader never reads the numeral.

Acceptance: the assertion set is identical, member for member

Both sides are read with the gate's own readIsomorphicPins: before is fdeeea0cc9 and after is fc8eda2d62.

reading before after
readIsomorphicPins entries 789 789
pin declarations 789 789
set difference, either direction 0
pin bodies changed (the text right of =, keyed on path.ts::Schema) 0
names off the rule / duplicate names 0 / 0
pin names in sorted order no yes

Controls, to show the instrument can fire:

  • Deleting one pin line gives a set difference of 1.
  • Swapping one pin's z.infer operand to another schema keeps the set at 789 and reads 1 body changed.

The module series is untouched: there are 174 import type * as M… lines before and after, and no import line is in the diff.

The collision is gone: merge probe with a firing control

The probe ran git merge-tree --write-tree in a throwaway git clone --bare --shared with no merge driver registered. The clone was removed afterwards. In each case, two branches off one base each add one pin for a different schema:

scheme and base the two insertions merge-tree duplicate pin names
OLD, fdeeea0cc9: both take Iso882, one in api/analytics, one in ui/view 1259 lines apart exit 0, clean, 0 markers Iso882 (the #19600 shape)
NEW, fc8eda2d62: Iso_api_analytics__ProbeAlphaSchema and Iso_ui_view__ProbeBetaSchema, each at its sorted place 1174 lines apart exit 0, clean, 0 markers none, and the merged names are still sorted
NEW, same module, with an existing pin between the two names 6 lines apart exit 0, clean none
NEW, same module, with the two names sort-adjacent same line exit 1, CONFLICT none

The last row is the case that remains. Two new names with no existing pin between them insert at the same place, and git stops with a conflict; the resolution is to keep both lines. That failure is loud, and it cannot merge into a duplicate.

The module series (M…) is left untouched, and it is not a measured collision: no one has yet seen two PRs that each import a new module. I ran one simulation of its mechanism only. Two branches that each append import type * as M189 after M188 conflict at the tail of the import block (merge-tree exit 1). Keeping both lines when resolving would give tsc a duplicate M189. So in this simulation its failure mode is a conflict, not a silent clean merge.

Landing order: this PR lands first, then #19809 re-runs the re-key on its own pin

On 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:

  1. Merge main. The pin file conflicts.
  2. Take fix(spec): one row bound per view — retire the unpublished per-kind view limit #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 fix(spec): one row bound per view — retire the unpublished per-kind view limit #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.
  3. 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.
  4. By hand:
  5. 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.

I 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.

The transform is node rekey.mjs FILE. It rewrites the file in place and is idempotent: running it on fc8eda2d62 changes nothing. It is written without a less-than character so this body keeps it intact. Running exactly this text on fdeeea0cc9 reproduces ece9f71d2c byte for byte.

// rekey.mjs FILE: re-key and sort the isomorphic pin block of
// packages/spec/src/type-alias-convention.pin.test.ts, in place. Idempotent.
// Refuses (exit 1) on any line it cannot place. Spelled without a less-than
// character so it survives a GitHub body intact: LT stands for one.
import { readFileSync, writeFileSync } from 'node:fs';

const LT = String.fromCharCode(60);
const file = process.argv[2];
const L = readFileSync(file, 'utf8').split('\n');
const die = (m) => { console.error(`rekey: ${m}`); process.exit(1); };

// Module alias -> module path, read the way the gate reads it.
const mods = new Map();
for (const l of L) {
  const m = l.match(/^import type \* as (M\d+) from '\.\/(.+?)\.js';$/);
  if (m) mods.set(m[1], m[2]);
}

// Stable name: path below packages/spec/src minus `.zod`, kebab segments
// camelCased, joined by `_`; then `__`; then the schema export name.
const keyOf = (path) => {
  if (!path.endsWith('.zod')) die(`module ${path} is not a .zod module`);
  return path.slice(0, -'.zod'.length).split('/').map((s) => {
    if (!/^[a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*$/.test(s)) die(`segment "${s}" of ${path} is not lowercase kebab`);
    return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
  }).join('_');
};

// Region: after the rule closing the "N isomorphic aliases" banner, up to the
// rule opening "Representative spot-checks".
const bannerAt = L.findIndex((l) => /^\/\/ \d+ isomorphic aliases:/.test(l));
if (bannerAt === -1) die('count banner not found');
const start = L.findIndex((l, i) => i > bannerAt && l.startsWith('// ----')) + 1;
const spotAt = L.findIndex((l) => l === '// Representative spot-checks on the phase-2 FLIP.');
if (start === 0 || spotAt === -1 || !L[spotAt - 1].startsWith('// ----')) die('region bounds not found');
const end = spotAt - 1;

const HEADING = /^\/\/ (?:\[#\d+\] )?([a-z0-9-]+(?:\/[a-z0-9-]+)*)\.zod\.ts(?:$|[ ,;:(—-])/;
const PIN = new RegExp(`^export type (Iso\\w+) = (Assert${LT}Eq${LT} z\\.input${LT} typeof (M\\d+)\\.(\\w+) >, z\\.infer${LT} typeof \\3\\.\\4 > >>;)$`);
const preamble = [];
const sections = new Map(); // key -> { heading, notes[], pins: Map(name -> line) }
const names = new Set();
let cur = null;
for (let i = start; i !== end; i++) {
  const l = L[i];
  if (l === '') continue;
  if (l.startsWith('// ----')) { // an inner banner moves, verbatim, to the preamble
    const endRule = L.findIndex((x, j) => j > i && x.startsWith('// ----'));
    if (endRule === -1 || endRule >= end) die(`unclosed banner at line ${i + 1}`);
    if (preamble.length) preamble.push('//');
    preamble.push(...L.slice(i + 1, endRule));
    i = endRule;
    continue;
  }
  const h = l.match(HEADING);
  if (h) {
    const key = keyOf(`${h[1]}.zod`);
    if (!sections.has(key)) sections.set(key, { heading: l, notes: [], pins: new Map() });
    else if (l !== `// ${h[1]}.zod.ts`) die(`second heading for ${h[1]} carries text (line ${i + 1})`);
    cur = sections.get(key);
    continue;
  }
  const p = l.match(PIN);
  if (p) {
    const path = mods.get(p[3]);
    if (!path) die(`line ${i + 1}: ${p[3]} imports no module`);
    const key = keyOf(path);
    if (!cur || cur !== sections.get(key)) {
      console.error(`rekey: line ${i + 1} pins ${path}.ts outside its section; moved there`);
      if (!sections.has(key)) sections.set(key, { heading: `// ${path}.ts`, notes: [], pins: new Map() });
    }
    const name = `Iso_${key}__${p[4]}`;
    if (names.has(name)) die(`duplicate pin ${name} (line ${i + 1})`);
    names.add(name);
    sections.get(key).pins.set(name, `export type ${name} = ${p[2]}`);
    continue;
  }
  if (l.startsWith('//')) {
    if (!cur) preamble.push(l);
    else cur.notes.push(l);
    continue;
  }
  die(`line ${i + 1} is neither a heading, a pin, a comment nor blank: ${l.slice(0, 80)}`);
}

// Code-unit order; every name is ASCII, so byte order is the same order.
const byCodeUnit = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b));
const out = [''];
if (preamble.length) out.push(...preamble, '');
for (const key of [...sections.keys()].sort((a, b) => byCodeUnit(`Iso_${a}__`, `Iso_${b}__`))) {
  const s = sections.get(key);
  out.push(s.heading, ...s.notes, ...[...s.pins.keys()].sort(byCodeUnit).map((n) => s.pins.get(n)), '');
}

const next = [...L.slice(0, start), ...out, ...L.slice(end)];
// The runtime companion counts pins by declaration: the name family, not a numeral.
const text = next.join('\n').replace(
  `self.match(/^export type Iso\\d+ = Assert${LT}/gm)`,
  `self.match(/^export type Iso\\w+ = Assert${LT}/gm)`,
);
writeFileSync(file, text);
console.log(`rekey: ${names.size} pins in ${sections.size} module sections`);

Verification

Round 2, on 4a724cfbf3, after the merge of main at 2c1011b01b:

  • 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.
  • pnpm --filter @objectstack/spec check:test-typecheck: OK (53 files / 255 errors / 142 signatures held).
  • 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.
  • pnpm --filter @objectstack/spec run typecheck: exit 0.
  • 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.
  • 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.

Round 1, on fc8eda2d62. The pin file's bytes have not changed since, so these still describe it:

  • 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.
  • The set identity against fdeeea0cc9 and the merge probe are in the sections above.

Changeset

None. The file is a test. @objectstack/spec's files ships src/**/*.zod.ts and dist but never *.test.ts, so nothing published changes. check-empty-changeset.mjs refuses a new empty-frontmatter changeset, so the repo's disposition for this diff is the skip-changeset label. 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.

Acceptance notes

  • On the base, two api/errors.zod.ts pins (FieldErrorCode and FieldErrorSchema) sat under the api/error-code-ledger.zod.ts heading. The sorted layout files them under their own module.
  • 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.
  • A check that the block stays sorted, and that each name matches its derivation, would be a new assertion, so none is added. The candidate, for the maintainer: in the runtime companion, assert that the Iso names are in code-unit order and that each one equals the rule applied to its Mn path and schema.

Generated by Claude Code


Generated by Claude Code

Mechanical half of the re-key: every `IsoNNN` pin in
type-alias-convention.pin.test.ts is renamed to
`Iso_<module path key>__<schema export>` and the block is sorted by that
name, one section per module. Bodies are byte-identical; the gate's
`readIsomorphicPins` set is unchanged (789 = 789, difference 0). The
runtime companion's pin counter now matches the name family instead of
a numeral. Prose follows in the next commit.

Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
Co-authored-by: Claude <noreply@anthropic.com>
The naming and sort rule is written at the head of the pin list, with
the two cohorts that used to head their own blocks (phase 2, the #4593
backfill) filed there. Notes that pointed at a pin by position or by a
numeral now name it; "positional and stay vacant" goes, since no pin
name is positional any more; the count history gains a +0 receipt
saying the numerals it cites are the pins' former names.

Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2c1011b01bc071c545f72f2761647b8d9ab56375 → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat read — PR #19932 at head fc8eda2d62227853362672f5a2daf7825968e602

domain:spec execution seat 1 (seat post #6017, session_013RDBh5DqXd2xnLwvHLgLFr), 2026-09-24T00:49Z. The diff is one test file, packages/spec/src/type-alias-convention.pin.test.ts. It touches none of the five review surfaces in references/contract-review.md ⇒ no at-tier contract review is owed; CI plus this seat's own read decide.

Measured by the seat, with the gate's own reader readIsomorphicPins from origin/main fdeeea0cc9, on the base file and this head's file:

reading base head
path::Schema pin set 789 789
only-before / only-after — 0 / 0
export type Iso… lines 789 789
duplicate names — 0
pin bodies changed, multiset compared — 0
names in code-unit order false (control) true

Dark control: dropping one pin from the head file reads 788.

Changeset. Test-only, so the disposition is skip-changeset. This seat applies the label now (references/review-checklist.md: 「Tests/docs-only … 走 skip-changeset 标签,⛔ 不走空 changeset」). No .changeset/*.md is touched.

The dev's open question, packages/spec/src/shared/duration.test.ts:11 still naming Iso868, is answered A. This PR is what makes that line stale, so the one-line fix rides the round that re-runs the re-key after #19809.

Landing is held, per the claim (5804903772). PR #19809 also edits this file. This PR is not armed until #19809 has landed or closed and the re-key has been re-run on that base; that head then owes this read again.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Correction to this seat's read above, at 2026-09-24T01:15Z. It says 「no at-tier contract review is owed」 for this test-only diff. Under the enqueue gate, that is wrong.

references/landing-operations.md 入队与落地 reads 「路径肢 = diff 触及契约面 packages/spec/src/**」 and 「双肢命中任一 ⇒ 无达档条款②复核 PASS 在案 ⛔ 禁止入队」, with no test exemption. This file sits under packages/spec/src/. contract-review.md excludes tests from its review surface, so the two rules disagree for exactly this diff shape. That conflict is filed as #19936. Until it is decided, this seat acts on the stricter one: this PR owes an at-tier ## Contract review PASS on its landing head before it enqueues, in addition to the #19809 hold. The seat's own readings above stand as readings. ⛔ They are not a review record.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: 82/82 CONTRACT_REVIEW_TIER
Head-sha: fc8eda2d62227853362672f5a2daf7825968e602

Isolated at-tier reviewer subagent, run by the domain:spec seat-4 session on the maintainer's instruction in that session (「帮我处理」, with the landing route chosen there); every one of its 82 transcript turns served at the tier the constant names. Adopted by the seat 2026-09-24T05:37Z. The record below is the reviewer's, unedited except the two header lines.

① Derived judgments

Inputs read: card #19665 body plus all 6 comments (incl. dev report 5805419416); PR #19932 body, pulls/19932/files, the vnd.github.diff, 3 issue comments, 0 review comments, 0 reviews; commits/fc8eda2d62…/check-runs (39 runs); issue #19936 body plus 5 comments; AGENTS.md, the pm-dispatch references, pr-automation.yml, check-spec-parsed-alias.mjs and packages/spec/package.json at origin/main (c1641868a3da71537c9c6c572d2bd2044103236b); the pin file at base fdeeea0cc9183f9c80343a552ef6523b1a53f99b, at ece9f71d2cc1aac1b20d93c9b3b0ae5870a68aa2 and at head, saved to scratch as base.ts / mech.ts / head.ts. Merge-base of head and origin/main is fdeeea0cc9…; git log fdeeea0cc9..origin/main -- packages/spec/src/type-alias-convention.pin.test.ts is empty, so the base file is still main's file.

Surface. git diff --name-only fdeeea0cc9 fc8eda2d62 prints exactly one path: packages/spec/src/type-alias-convention.pin.test.ts (+969 / -989). grep -c '^[-+]import ' base-head.diff is 0: no import line moves. Filtering the diff to changed lines that are neither // comment, blank, nor export type Iso… leaves exactly 2 lines, the counter regex pair (below). So the whole change is: pin renames, comment prose, one regex. RIGHT against the card's ⛔ list (no wholesale regeneration, no exemption change, no gate change).

Pin set, independently re-derived (not the PR's instrument). compare.py in scratch parses every export type Iso… line with a strict pattern (name, M-alias, schema, and the infer operand must equal the input operand), resolves M-alias to module path via the import block, and compares keyed on (path, schema):

base.ts: imports=174 pins=789 unparsed-Iso-lines=0  input/infer operand mismatch: 0  duplicate (path,schema): []  duplicate names: []
mech.ts: imports=174 pins=789 unparsed-Iso-lines=0  (same zeros)
head.ts: imports=174 pins=789 unparsed-Iso-lines=0  (same zeros)
import maps identical base/head: True
base.ts vs head.ts: only-in-base=0 only-in-head=0; bodies changed (raw text right of " = "): 0; bodies changed (alias-normalised): 0
mech.ts vs head.ts: only-in-mech=0 only-in-head=0; bodies changed: 0

Same pin set, same 789 bodies, byte for byte right of =. No pin dropped, none weakened, none duplicated. RIGHT; this is the card's stated acceptance condition ("identical member-for-member") and it holds under a reader that does key on the declaration, unlike the gate's readIsomorphicPins (scripts/check-spec-parsed-alias.mjs:136-152 at origin/main), which matches z.input of typeof Mn.Schema occurrences and never reads the Iso name, so a rename is invisible to the gate by construction (the PR's "reader never reads the numeral" claim is confirmed at that file).

Name rule and order. Same script, head.ts: names off rule=0, sorted by byte order: True, all-ascii: True, pins under a heading of a different module: 0, headings in pin region=178 sorted=True distinct=True. The 178 headings are 174 pinned modules plus 4 pin-less retirement notes that already exist at base (head.ts:848 data/external-lookup, :986 kernel/metadata-customization, :1318 system/message-queue, :1570 ui/theme; base.ts:740, :865, :1166, :1351), so no heading was invented and none lost a pin. The keyOf mapping is injective on lowercase-kebab segments (camelCase marks each hyphen, _ only ever comes from /), so two distinct (path, schema) pairs cannot collide on a name; duplicate-name count at head is 0. The example in the new preamble (head.ts:290-292, ai/knowledge-document to ai_knowledgeDocument) is real: head.ts:361 Iso_ai_knowledgeDocument__KnowledgeChunkSchema. RIGHT.

The transform is what the PR says it is. Extracted the fenced rekey.mjs from the stored PR body (4676 bytes, 0 less-than characters), ran node rekey.mjs on a copy of base.ts: exit 0, stderr rekey: line 425 pins api/errors.zod.ts outside its section; moved there (x2, the disclosed FieldErrorCode / FieldErrorSchema relocation), 789 pins in 178 module sections; cmp against mech.ts: BYTE-IDENTICAL to ece9f71d2c. Run on a copy of head.ts: cmp identical, so idempotent on the head. git diff ece9f71d2c fc8eda2d62 (mech-head.diff, 262 lines) contains only // lines: the second commit is prose-only as claimed. RIGHT.

Counter regex. base.ts:2151 self.match of ^export type Iso then \d+ then = Assert then a less-than, /gm; head.ts:2118 the same with \w+. The match is the runtime companion's pin COUNT, compared at head.ts:2308-2314 against the prose phrases (\d+) isomorphic \w+; those phrases are unchanged (head.ts:278 789 isomorphic aliases, :1666 789 isomorphic pins; base.ts:278, :1699 identical). With \d+ the new names would count 0 and the test would go red, so the widening is required, and every one of the 789 head names is Iso followed by word characters (grep -cE '^export type Iso_' head.ts is 789; Iso[0-9]+ is 0). Does \w+ let a malformed name through? Only in the sense the base regex also did nothing for the rule: the counter never validated a name against anything; at base an off-family name simply went uncounted (and tripped the count). At head an off-rule name such as Iso_wrongKey__X or a stale Iso882 is counted and neither sorted-ness nor derivation is asserted anywhere. That matches the card's ordered remedy (delete the counter, make the correct spelling the only one, "only then consider a check") and the PR's explicit choice to add no new assertion; tsc still refuses a duplicate name (the ablation in dev report item (2) turned check:test-typecheck red on a duplicate). RIGHT for the card; the missing rule-check is recorded under ③ as non-blocking.

Prose. Every pin the rewritten notes cite by its new name exists exactly once at head (22 names grepped, e.g. Iso_data_analytics__AnalyticsQuerySchema, Iso_system_settingsManifest__SpecifierValueDomainSchema, Iso_shared_epoch__EpochMs, Iso_api_errors__FieldErrorCode, all 1). "Positional and stay vacant" is gone (mech-head.diff hunks at head.ts:987-990, :1562-1564, :1570). Remaining positional wording in the pin region is two true statements (head.ts:307 about the Mn aliases above; :1566 "see the Iso696/Iso697 note above", three lines up in the same section). The 87 IsoNNN numerals still inside the file are in the count history and retirement notes, which the file itself declares unrewritable history (head.ts:2303-2307) and which the new preamble labels as former names (head.ts:306-308); the 789 to 789 entry sits at head.ts:2272-2284. RIGHT.

Verification claims in the PR body are consistent with the head's check-runs, which I did not re-run: all 39 runs completed; every name's latest run is success or skipped; Lint and Repo Gates (which wires check:spec-parsed-alias), all six Test Core shards, all four Type Check lanes and TypeScript Type Check are success on fc8eda2d62….

② Semver level

None; Clause-②: no is correct and no changeset is owed. The only file is a test: packages/spec/package.json files at origin/main is dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json, which ships no *.test.ts; the pin file is compiled only by tsconfig.test.json via check:test-typecheck (card comment 5772328597), not into dist. No export, schema, key or gate acceptance moves (pin set and bodies identical, check-spec-parsed-alias.mjs untouched). AGENTS.md:1083-1084 at origin/main: skip-changeset "is for a diff that publishes nothing from any released package", which this is; review-checklist.md:20: "Tests/docs-only … 走 skip-changeset 标签,⛔ 不走空 changeset". The PR adds no .changeset/*.md (file list is one path). Disposition RIGHT.

Check Changeset on the head, two runs. Run id 107444969426 (workflow run 35939802837, the opened event): started 2026-09-24T00:44:52Z, completed 00:46:51Z, conclusion failure. Label events on the PR: skip-changeset labeled by objectstack-fleet[bot] at 00:49:06Z. So the failure PREDATES the label by 2 min 15 s and was the correct verdict for an unlabeled test-only PR at that moment. Run id 107446035571 (workflow run 35940134036, the labeled event): started 00:49:19Z, conclusion skipped, which is the job-level if: at .github/workflows/pr-automation.yml:289-290 (!contains(labels, 'skip-changeset')) short-circuiting on the payload that now carries the label. The post-label verdict is skipped; the earlier failure is the stale opened run the workflow's own comment block (pr-automation.yml:257-268) describes as "permanently red by construction" and not an unmet requirement.

③ Boundary flags

Implemented-by: claude/issue-19665-iso-pin-stable-keys
Reviewed-by: session_019c3Hi6ZMU1p6m6aA6Bz45d

VERDICT: PASS


Generated by Claude Code

Base merge onto main 2c1011b to re-measure the head. The pin file is
byte-identical on the merge base and on main, so it merges untouched.

Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
Co-authored-by: Claude <noreply@anthropic.com>
The pin file's isomorphic pins are now named for their module and
schema; the EpochMs pin this test's header points at is
`Iso_shared_epoch__EpochMs`, no longer `Iso868`. Comment text only.

Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants