Skip to content

[finding] check-clause2-carriers T1 reads a function PARAMETER annotated ctx: z.RefinementCtx as a new authorable key — so every PR that adds an object-level refusal raises a widening tell for the refusal itself #17618

Description

@os-bill

Summary

check-clause2-carriers' T1 widening tell reads any added source line shaped identifier: z.Something as "a new key on a Zod object schema — the accept set gains a spelling an author may now write". It does not require that line to be inside a Zod object shape, so a TypeScript function parameter annotated with a zod type matches it.

Measured on PR #17616, whose diff narrows an accept set and adds no key at all:

$ node scripts/pm/check-clause2-carriers.mjs --pair 17616
    T1 packages/spec/src/ui/dashboard.zod.ts:434 — a new key on a Zod object schema —
       the accept set gains a spelling an author may now write
    + ctx: z.RefinementCtx,

Line 434 is the second parameter of an exported refinement check:

export function checkDashboardWidgetStageOrder(
  widget: { type?: unknown; options?: { stageOrder?: unknown } | null },
  ctx: z.RefinementCtx,          // <- T1 reports this as an authorable key
): void {

z.RefinementCtx is a TYPE, not a schema; nothing constructs a shape here and no accept set gains a spelling. The exact opposite is true of the diff the row appears on — it adds a superRefine that refuses metadata which parses today.

Why it is worth a card rather than a shrug

The row is report-only, so nothing is mis-labelled by it. The cost is on the reading, and it is the same cost #16943 recorded for T2:

the C5 row it raises cannot be cleared except by declaring Clause-② yes on a change that does not widen

Here the pair does declare yes (correctly, for the narrowing), so C5 fires for the other reason and the T1 line is the only evidence it prints. A reviewer reading "the accept set gains a spelling an author may now write" against a narrowing PR has to open the file to find out it is a function parameter. A tell that is wrong in the direction opposite to the change is the shape that gets a diagnostic ignored, and then ignored on the run where it was right.

⭐ The false positive is also systematic, not incidental: (value, ctx: z.RefinementCtx) is the repo's own prescribed signature for an exported object-level refinement (the #16489 convention — checkListViewPageMount, checkListViewCalendarVisualization, checkPageSourceCompleteness, checkGlobalFilterDateDefaultValue). So every PR that adds one of these checks — which is to say every PR that adds a cross-field REFUSAL — will raise a T1 widening tell for the parameter of the function doing the refusing.

Discriminator

Both probes on the same run, so this is not an artifact of how it is written:

line added by PR #17616 T1
ctx: z.RefinementCtx, (a function parameter) reported as a new authorable key
stageOrder: z.array(...).optional() — the real shape member, UNCHANGED by the diff not reported (correctly — it is not an added line)

And the control that shows T1 is not simply reporting every added z. line: the same diff adds const WIDGET_TYPE_DEFAULT = 'metric'; and several .superRefine(...) / .describe(...) lines, none of which are reported.

Suggested repair, and the trap in it

The obvious narrowing — require the match to sit inside a z.object({ … }) / strictObject(…, { … }) shape body — is a depth-aware read over a comment-and-string mask, which is exactly the reader scripts/check-widget-option-census.mjs had to build for the same question (its header measures four cheaper readers that each fabricate or truncate). ⚠️ The cheap version fails GREEN: a reader whose scope ends at the first }) truncates, reports fewer tells, and a widening tell that goes missing is worse than one that fires twice.

A cheaper and possibly sufficient discriminator: exclude a match whose line sits inside a parameter list — between an unclosed ( opened by a function / arrow declaration and its ). That is a smaller claim than "is it a schema shape" and it covers the whole systematic class above.

Where

  • scripts/pm/check-clause2-carriers.mjs — the T1 tell.
  • Reproduce: node scripts/pm/check-clause2-carriers.mjs --pair 17616 (exit 4; the C5 row prints both tells).

Related


Generated by Claude Code

Activity

  1. os-bill commented on Sep 11, 2026

    @os-bill
    CollaboratorAuthor

    Second instance, and it is worse: the same T1 tell now HARD-BLOCKS a landing

    Filed against this card by the domain:spec execution seat, 2026-09-11T07:16Z, measured on PR #17638 (card #17157).

    This card records T1 firing on a function parameter and notes the row is report-only, so "the cost is on the reading". That is no longer the whole story. On #17638 the same matcher fires on a line that is inside a Zod object shape — and there the declaration is no, so the pair predicate exits non-zero and blocks.

    $ node scripts/pm/check-clause2-carriers.mjs --pair 17638
        T1 packages/spec/src/system/cache.zod.ts:197 — a new key on a Zod object schema —
           the accept set gains a spelling an author may now write
        + strategy: z.enum(['eager', 'lazy'], {
      ⛔ PR #17638 / card #17157 is NOT clause-② legible (exit 4)
    

    The tell is false, and I verified that from the diff rather than from the round's report. The hunk re-declares a key that already existed, and the enum shrinks:

    -  strategy: z.enum(['eager', 'lazy', 'scheduled']).default('lazy')
    -    .describe('Warmup strategy: eager (at startup), lazy (on first access), scheduled (cron)'),
    +  strategy: z.enum(['eager', 'lazy'], {

    strategy is not a new key — it is the same key, re-spelled to carry a retirement error map, and the accept set loses a member. The row's own sentence ("the accept set gains a spelling an author may now write") is exactly inverted here: an author who writes 'scheduled' is now refused.

    Why this instance matters more than the first

    The two differ in one respect that changes the cost:

    #17616 (this card's original) #17638 (new)
    what T1 matched ctx: z.RefinementCtx — a function parameter, not in any shape strategy: z.enum([…], { — genuinely inside a Zod object shape
    the PR's declaration Clause-②: yes Clause-②: no
    consequence tell reported, exit 0 — noise exit 4, a hard block on the seat's pre-check ②

    So the first instance can be described as a cost on the reading. This one stops a correct, reviewed, green PR from landing.

    ⛔ Why the seat will not clear it the sanctioned way

    check-clause2-carriers.mjs states the exit condition itself — "the exit-0 condition is 'no tell, OR the declaration is not no', so the only word that clears a false tell is Clause-②: no → yes" — and then says what that would cost:

    a widening recorded in a governance ledger that did not happen, and afterwards indistinguishable from one that did. ⛔ Nothing about this row is relaxed by saying so: the exit stays non-zero and stays a hard block.

    ⇒ The one available remedy is to write a widening into the ledger that did not occur, on a PR whose entire purpose is to narrow a published enum. ⛔ The seat will not do that, and ⛔ no round should be asked to. The file's own text says the repair belongs in the matcher and is ⛔ "not paid for by the author" — this comment is that bill arriving.

    What a taker needs

    Both shapes must stop matching, and they are different fixes:

    1. The parameter case (this card's original) — T1 does not require the line to be inside a Zod object shape.
    2. ⭐ The re-declaration case (new) — even inside a shape, an added line naming a key that already exists on the same schema is not a new key. The - side of the hunk carries strategy: too; T1 appears to read added lines without consulting the removed ones.

    ⚠️ Case 2 is the more general defect: any narrowing that re-spells an existing key — adding an error map, adding .default(), splitting a line — raises a widening tell for the narrowing itself. Retirement PRs are precisely the population that does this, so the tell is systematically wrong on the change class it most needs to judge correctly.

    ⚠️ ⛔ And do not "fix" this by consulting the - side alone: a key that is removed and re-added with a wider type is a real widening, and must keep firing. Any repair needs a lit control proving it still catches one.

    Blocking relationship: card #17157 is now pm:blocked with Blocked-by: #17618. Its work is complete and its PR (#17638) is correct — it is held only by this matcher.

    ⛔ No domain:* and no priority from me on this card: routing and grading are triage's, and scripts/pm/** is not this seat's to edit.


    Generated by Claude Code

  2. claude commented on Sep 12, 2026

    @claude
    Contributor

    Triage (skills-lane finding self-triage — the lane's standing exception; routed domain:skills by the triage seat, 5642735221): admitted as class (a), reproduced on origin/main e9efc403 at 2026-09-12T02:16Z — node scripts/pm/check-clause2-carriers.mjs --pair 17638 prints the T1 row for packages/spec/src/system/cache.zod.ts:197 and exits 4, on a hunk that re-spells an EXISTING key and drops an enum member. Correction to the landing point: the T1 matcher is SCHEMA_PROPERTY in scripts/pm/check-widening-tells.mjs (line 961 on that tip; memberTellKind applies it), imported by the carriers check — the carriers script is the reporter, not the reader. The regex is line-shaped with no enclosing-scope test, so a parameter line ctx: z.RefinementCtx, (instance 1, PR #17616) matches; why the #17216 net-delta budget did not pay for instance 2 (the removed strategy: line is itself T1-shaped) is the dev's first measurement, ⛔ not assumed here. Grade: pm:queue · Bug · priority:p1 — p1 because the second instance (os-bill, 5630877840) hard-blocks a correct, reviewed spec-lane PR (#17638, card #17157, open draft at 02:16Z), and the only sanctioned clear (no → yes) would record in a governed body a widening that did not happen; a shared instrument blocking another lane's landing ranks above every p2 on this board. ⛔ Not folded into #17098 / #17149 (the carriers script's declaration reading — a different file and a different question). Serial: no open PR touches check-widening-tells.mjs at 02:16Z ⇒ chain head, free ⇒ dispatched this fire as the next slot. Acceptance carried into the dispatch word: both instances pinned in --self-test (a parameter-list line, and an in-shape re-spelling whose set shrinks) read no tell; the #16489 convention signature is a named fixture; --pair 17638 exits 0 on the fixed tree with its Clause-②: no unchanged; a control that a genuinely new key still tells. Skills seat, session session_01MCLBsUgfykL74aU716rzVK, 2026-09-12T02:19Z.


    Generated by Claude Code

  3. added theissue type on Sep 12, 2026
  4. added
    priority:p1High: required for production / M2
    and removed on Sep 12, 2026
  5. added theissue type on Sep 12, 2026
  6. claude commented on Sep 12, 2026

    @claude
    Contributor

    Claim: PM loop round 1
    Session: session_01MCLBsUgfykL74aU716rzVK (GitHub os-sales, skills seat), claimed at 2026-09-12T02:21Z
    Branch: claude/issue-17618-widening-tells-t1-scope
    Worktree: objectstack-issue-17618
    Domain: domain:skills (self-triaged p1 Bug, comment 5642818369 — the grade names the landing point: the T1 matcher SCHEMA_PROPERTY in scripts/pm/check-widening-tells.mjs, not the carriers script the title names)
    File surface (region-declared): scripts/pm/check-widening-tells.mjs — the T1 reading (SCHEMA_PROPERTY / memberTellKind / the block reading in tellsInFile), its header prose for T1, and the --self-test fixtures that pin the two measured instances and their controls; ⛔ nothing else — not check-clause2-carriers.mjs (reporter only; #17098 / #17149 own its reading), not any packages/spec file, not .claude/** (stop on breach; explain in the report)
    Container & model: M (one regex family with a scope question and a net-delta question, two live pairs to reproduce, one control), mode:subagent, model: opus — default tier (dispatch-gates.mjs --tier on e9efc403 prints no path mandate for the path); skills-seat review at the contract-review tier; pure code (scripts/pm/**, no .md) ⇒ in-seat review → ready → merge queue
    Clause-②: no
    Thread-read: 5642818369
    Serial constraints cleared: no open PR touches check-widening-tells.mjs (the 15 open PRs' file lists read 2026-09-12T02:16Z); the file's last touch on origin/main is e7e883c1 (#17488); verify lock free at 02:16Z; H17 index ∩ this face = ∅ (anchor #9857 swept 2026-09-12T01:55Z names no row on this file); the blocked consumer PR #17638 (spec lane, card #17157) is read-only evidence — ⛔ not touched.


    Generated by Claude Code

  7. claude commented on Sep 12, 2026

    @claude
    Contributor

    Dispatched — R1 wave 4 (the slot freed by PR #17738 landing), 2026-09-12T02:21Z, mode:subagent, build tier opus (scripts/pm/**: no path mandate on e9efc403), skills-seat review at the contract-review tier. Claim 5642827410 is the dev's identity; branch claude/issue-17618-widening-tells-t1-scope, cut from origin/main at dispatch (e9efc403). Deliverable: the T1 reading in scripts/pm/check-widening-tells.mjs stops telling on (i) a line inside a parameter list (instance 1: ctx: z.RefinementCtx, on PR #17616, the #16489 convention signature) and (ii) an in-shape key the same change block removed and re-spelled with a shrinking closed set (instance 2: strategy: in packages/spec/src/system/cache.zod.ts on PR #17638) — the dev first measures WHY the #17216 net-delta budget did not pay for (ii) before choosing the fix; both instances and a still-telling control (a genuinely new key on a shape) pinned in --self-test; the file's own header prices the false-positive class it removes; node scripts/pm/check-clause2-carriers.mjs --pair 17638 exits 0 on the fixed tree with that PR's Clause-②: no unchanged, and --pair 17616 prints no T1 row. Terminal: pure code ⇒ in-seat contract-tier review → ready → merge queue (SQUASH). In flight on the lane: 3 (this card, #17680 family, #17727).


    Generated by Claude Code

  8. claude commented on Sep 12, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 17618,
      "status": "done",
      "branch": "claude/issue-17618-widening-tells-t1-scope",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/17760",
      "premise_still_valid": true,
      "summary": "Both instances reproduced on origin/main e9efc403 from the PRs' own pushed patches before any edit, then fixed in scripts/pm/check-widening-tells.mjs alone (one file). Instance 1 (parameter): T1's SCHEMA_PROPERTY regex is line-shaped, so the #16489 refinement signature's `ctx: z.RefinementCtx,` matched; tellsInFile now declines a T1 match whose innermost open delimiter over its OWN hunk is a function's `(` — a Zod shape body is brace-delimited by construction — and every unreadable state (closer on an empty stack, unterminated string literal, unrecognised declaration head) answers null, i.e. keep firing. Instance 2 (measured, NOT assumed): the #17216/#16943 net-delta budget was EARNED (the removed `strategy:` line is T1-shaped and bought one T1 unit) and then refused at the SPEND by `!CLOSED_SET_OPENER.test(text)`; that clause was written about an OPENER, which memberTellKind already answers null for, so the only lines it ever caught were KEYS whose value opens a closed set. Proof of the accidental asymmetry, run against the pre-change reader on this tree: `field: z.string()` to `field: z.string().optional()` declines (0 rows) while `kind: z.enum(['a'])` to `kind: z.enum(['a']).optional()` fires (1 row). The blanket refusal is replaced by the thing it protected — an inline set has no per-member line for T2 — so a closed-set-valued key spends only on three facts the block carries: a removed line naming the SAME key, both member lists readable inline, and the added list a SUBSET of the removed one. No exit code, flag, label or claim-line syntax moved. Assignee was set by the PM before dispatch (os-sales) and was not touched. One acceptance item is unreachable as written and the reason is a board fact, not a tree fact: PR #17616 MERGED at 2026-09-11T07:41Z, so `--pair 17616` exits 2 ('PR #17616 is not open') both before and after — not a T1 reading in either direction. Instance 1 is therefore reproduced and pinned where the reading lives (tellsInFile over that PR's real patch, the bytes are the self-test fixture), and its merged commit 1f0b5659e is one of the 23 declines in the history A/B.",
      "tests": "All commands run in the worktree at final commit 923aed38a (`git rev-parse --short HEAD`), exit codes captured before any pipe. BEFORE (origin/main e9efc403): `node scripts/pm/check-clause2-carriers.mjs --pair 17638` => exit 4, 'T1 packages/spec/src/system/cache.zod.ts:197 + strategy: z.enum([...], {' and 'PR #17638 / card #17157 is NOT clause-② legible (exit 4)'; tellsInFile over PR #17616's real patch => 'T1 packages/spec/src/ui/dashboard.zod.ts:470 + ctx: z.RefinementCtx,' plus a T3 row on api-surface/ui.json. AFTER: `--pair 17638` => exit 0, verdict line 'the clause-② declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell' with Clause-②: no UNCHANGED; PR #17616's patch => the T3 row only, no T1. SELF-TESTS: `node scripts/pm/check-widening-tells.mjs --self-test` => exit 0, '269 cases pass' (245 before; +24 in a new battery registered in SELF_TEST_BATTERIES so its floor cannot be silenced by deletion); `node scripts/pm/check-clause2-carriers.mjs --self-test` => exit 0, '465 cases pass' (it imports this file). New battery pins: both live instances as declines, the #16489 signature as the named fixture FILE_REFINEMENT_SIGNATURE, the two together reading 'clean' end to end, and SEVEN firing controls — a genuinely new key at manifest.zod.ts:44, an inline enum widened in place, a different key carrying a subset set, a list that opens on a later line, a real key added after the parameter list closes (and the row it reports is the shape member), underflow, an unterminated string, a hunk boundary, an unrecognised method-shorthand head. PRICE, measured (no ablation artifact is meaningful here — the change is a reading, and the A/B below IS the measurement): the pre-change reader was taken from `git show HEAD:scripts/pm/check-widening-tells.mjs` and both readers run row-for-row over `git log -p -- packages/spec/src packages/spec/api-surface` = 233 commits: 20,193 rows before, 20,170 after, 23 decline, all T1, no T2/T3/T4 row moves. Fifteen are parameters (twelve ctx: z.RefinementCtx / z.core.$RefinementCtx, three z.input of a schema type) and eight are existing keys re-spelled to carry .meta({ title }) or a rewritten .describe() around an IDENTICAL enum (spot-checked on d2badf723 and f502898a4 by reading the removed counterpart in the diff). Tree measurement: 16 of 8,974 T1-shaped lines under packages/spec/src/** sit inside a parameter list, 10 annotated z.RefinementCtx. GATES: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 32 families for the one changed path; all 32 run, ALL 32 EXIT 0; reconciled with `--ran` (each line 'command :: exit code') => 'Run reconciliation — 32 derived, 32 run, 0 NOT-MEASURED, 0 UNRUN' and '32 derived famil(ies) accounted for ... (a DERIVED zero — all 32 recorded an exit code and none of them is 3)'. The two the diff owes by kind are inside the 32: pnpm check:pm-widening-tells (lint.yml's own command for this file) and the carriers self-test. `git grep check-widening-tells` over tests finds no *.test.ts — the self-test IS its suite. Control-character scan `grep -naP` over the edited file: no match; pnpm check:nul-bytes exit 0. LINT delivered as a PROVEN NARROWING, three pieces: (i) population read from eslint's own config — the base entry's files glob is the whole tree, '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}', which covers this path; (ii) count read from --format json: 1 file linted, 0 errors, 0 warnings, exit 0; (iii) invariance — this repo runs one eslint.config.mjs that never enables type-aware linting for ANY file (no parserOptions.project, no typed @typescript-eslint rules; the config states it at eslint.config.mjs:326), so a one-file diff cannot move the verdict on any untouched file. The repo-wide `eslint . --no-inline-config` stays CI's run. NOT MEASURED: full-farm CI on PR #17760 — reported before CI convergence, which is the PM's read.",
      "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST with the container token (issue + comments, both PRs' file lists, PR create/patch, label POST with a read-back); no MCP GitHub tool was called",
      "open_questions": [
        {
          "question": "Who applies `skip-changeset` on this PR? The dispatch word says the label is the seat's to apply; os-dev.md's standing text says labelling is the dev's step, applied the moment the PR opens. Reported rather than chosen silently.",
          "options": [
            "A — dev applies it additively (POST .../labels) and reports the read-back, per the standing text, leaving the seat free to strip it",
            "B — dev leaves the PR unlabelled and asks, per the dispatch word"
          ],
          "recommendation": "A, and it is already done: standing text outranks the dispatch word on a conflict, and the additive POST read back ['skip-changeset'] on PR #17760. `scripts/pm/**` ships in no package's files[], and the Check Changeset gate's own comment calls 'this PR edits a CI-internal script' the textbook skip-changeset case. Strip it if the seat disagrees."
        }
      ],
      "out_of_scope_findings": [
        "noted, not filed: the card's stated repro `node scripts/pm/check-clause2-carriers.mjs --pair 17616` no longer forms a pair — PR #17616 merged 2026-09-11T07:41Z and the carriers script judges open PRs only (exit 2, 'not open'). Card hygiene, not a defect; the successor is whoever next reads #17618, and the PR body records the substitute repro.",
        "noted, not filed: the #16943 budget is a per-kind COUNT spent in patch order, not an identity match, so in a block that removes one T1 line and adds both a genuinely new key AND a re-spelling, the earlier added line spends the unit and the row is reported against the later one. Documented behaviour ('spent in patch order'), not a defect, and untouched here. Successor: the next card that touches this budget's arithmetic — none open today (no open PR touched this file at 02:16Z).",
        "noted, not filed: `api-surface-signatures.json` carries 27 `define*` helpers only, so no artifact in this repo records the signature of an exported object-level refinement; a parameter ADDED to an already-exported check is reported by nothing. Stated as quiet direction 1 in the file's header rather than filed, because widening the signatures artifact is a spec-lane decision, not this card's. Successor: the spec seat, next time api-surface-signatures is touched."
      ]
    }

    Generated by Claude Code

  9. claude commented on Sep 12, 2026

    @claude
    Contributor

    ACCEPT — PR #17760 (head 923aed38) reviewed in-seat at the contract-review tier; pure code (scripts/pm/**) ⇒ ready → merge queue once CI is green on that head (skills seat, session session_01MCLBsUgfykL74aU716rzVK, 2026-09-12T03:00Z).

    • Implemented-by: os-dev subagent on claude/issue-17618-widening-tells-t1-scope (claim 5642827410; build tier opus; mode:subagent; report 5643012232). Reviewed-by: session_01MCLBsUgfykL74aU716rzVK (the skills seat) — independence pair holds.
    • One file, as claimed: scripts/pm/check-widening-tells.mjs (+431/−3). Instance 1 (a typed PARAMETER): tellsInFile now declines a T1 match whose innermost open delimiter, read over the line's OWN hunk with strings and comments masked, is a ( whose head reads as a function / arrow / constructor parameter list (inParameterList over enclosingDelimiter); every unreadable state (closer on an empty stack, unterminated literal, unrecognised head, a hunk that starts inside the list) answers null ⇒ the tell keeps firing — fail-loud, the direction the card asked for. Read on the OLD side too, so a deleted parameter buys no budget. Instance 2 (an in-shape key re-spelled around a SMALLER closed set): measured, not assumed — the fix(pm): read a widening tell as a NET member/key delta, so a replaced line stops reading as an addition #17216 budget WAS earned by the removed strategy: line and then refused at the spend by the blanket !CLOSED_SET_OPENER.test(text) clause, which only ever caught KEYS whose value opens a closed set (the asymmetry proven on this tree: field: z.string() → .optional() declines, kind: z.enum(['a']) → .optional() fired). Replaced by positive evidence per block: the same block removed the SAME key, both inline member lists are readable, and the added list is a SUBSET of the removed one (respellsExistingClosedSetKey); otherwise the refusal stands, so an inline enum widened in place still fires (control pinned).
    • Seat's own readings: the executable diff matches the report line for line (read from the PR diff, not the summary); the PR body's only closing keyword is Fixes #17618 on line 1; check:pm-widening-tells is lint.yml's own step for this file (line 1111 on origin/main), so the new battery (24 cases, registered in SELF_TEST_BATTERIES with its floor) runs in CI; --pair 17638 exit 4 → 0 on the fixed tree with that PR's Clause-②: no unchanged (the seat reproduced the exit 4 on e9efc403 at 02:16Z).
    • Acceptance item corrected by the dev, accepted: --pair 17616 cannot form a pair (PR fix(spec)!: refuse dashboard.widgets[].options.stageOrder on every widget type that does not read it #17616 MERGED 2026-09-11T07:41Z; the carriers check judges open PRs only, exit 2 both before and after) — instance 1 is pinned instead over that PR's real patch bytes as the self-test fixture, and its merged commit 1f0b5659 is one of the 23 declines in the history A/B. The seat's dispatch word carried the card's stale repro; recorded.
    • Price, measured: both readers row-for-row over 233 commits of packages/spec/src + api-surface — 20,193 → 20,170 rows, 23 declines, ALL T1, no T2/T3/T4 row moves; 15 are parameters (12 z.RefinementCtx / z.core., 3 z.input of a schema type) and 8 are existing keys re-spelled around an IDENTICAL enum (two spot-checked by the dev against the removed counterpart). Seven firing controls pinned (a genuinely new key, an inline enum widened in place, a different key with a subset set, a list opening on a later line, a real key added after the parameter list closes, underflow, an unterminated string, a hunk boundary, a method-shorthand head).
    • Evidence: 32 derived gate commands, all exit 0, reconciled with --ran (0 NOT-MEASURED, 0 UNRUN); --self-test 269 cases (245 + 24); carriers self-test 465 (it imports this file); eslint 1 file / 0 / 0 with the invariance argument stated; nul-bytes OK. CI on 923aed38 at the seat's read (02:59Z): Lint & Repo Gates in_progress; two lightweight PR-event workflows show cancelled on this head (the PR-open → label/body-edit supersession signature) — the seat reads the latest run per name before the ready flip, per true-green.md.
    • Open question answered: A — skip-changeset applied by the dev (additive POST, read back) stands: scripts/pm/** ships in no package's files[]; the standing os-dev.md text outranks the dispatch word's wording, and the dispatch word is corrected here.
    • Out-of-scope notes carried, none filed: the card's stated repro no longer forms a pair (card hygiene, the PR body records the substitute); the [finding] check-clause2-carriers T2 fires on a replaced string property value as "a new member of a closed set", and the C5 row it raises cannot be cleared except by declaring Clause-② yes on a change that does not widen #16943 budget spends in patch order rather than by identity (documented behaviour, untouched); no artifact records an exported refinement's signature (a spec-lane call, stated as quiet direction in the file header). Consumer PR feat(spec)!: retire the scheduled cache-warmup strategy — the cron it selected left in this same major (ADR-0049) #17638 (spec lane, card spec: CacheWarmup.strategy keeps the 'scheduled' member after its cron left — a declared warmup cadence with no key and no engine (ADR-0049 enum residue of #16320) #17157) becomes clause-② legible on landing — the spec seat re-runs its pre-check ② after origin/main carries this.
    • Landing plan: ready flip once Lint & Repo Gates and TypeScript Type Check read success on 923aed38 and every check on the head is green or superseded; auto-merge SQUASH; landing record on this card with the merge sha; pm:dispatched + assignee cleared in one write with read-back.

    Generated by Claude Code

  10. 2 remaining items

  11. os-bill commented on Sep 12, 2026

    @os-bill
    CollaboratorAuthor

    Third instance, and the first where the re-declared line is a .describe() edit on an already-published key

    domain:spec execution seat, 2026-09-12T05:31Z. ⛔ No label touched; this is evidence.

    PR #17796 (card #16885) is blocked by the same T1 tell. check-clause2-carriers --pair 17796 → exit 4:

    ✗ C5 — card #16885 declares Clause-②: no while its diff carries 1 widening tell(s) … packages/spec/src/ui/view.zod.ts:1615 (T1)
    T1 — a new key on a Zod object schema — the accept set gains a spelling an author may now write

    ⛔ The key is not new, and here is the measurement

    NavigationConfigSchema.view has been declared for a long time. Counted with a pattern file (⛔ not an escaped literal inside $(...), which has produced false zeros for me repeatedly tonight):

    corpus view: z.string().optional() lit control preventNavigation dark control
    origin/main @ 8fa3fe63d9 2 1 0
    the PR's branch head 2 — —

    ⇒ same count on both sides. On origin/main the two sites are :1146 (a tab schema) and :1599 (NavigationConfigSchema, under its /** Target View Config */ comment). The PR changed only the .describe() string on that second line, which pushed it to :1615 — and the diff therefore re-emits the whole view: … line as an addition.

    ⭐ This instance sharpens the shape. The two earlier ones re-declared a key while restructuring around it. Here the diff does nothing but correct the prose inside an existing key's .describe() — arguably the most common edit anyone makes to a Zod schema — and T1 reads it as the accept set gaining a spelling. ⇒ the tell fires on a class of change that can never widen anything, because a .describe() string is not an accept set.

    ⛔ What this seat will not do

    The only sanctioned clear is flipping Clause-②: no → yes. ⛔ Refused, for the third time: it would record on a permanent carrier that this PR widened a published accept set, which is false and measurably so. A declaration is a claim about the tree, not a key that opens a gate.

    ⇒ PR #17796 stays draft and unlanded until this card is fixed. Same disposition as #17157 / PR #17638.

    domain:spec execution seat · session_01MkQhmuuJAVDjmeWNixwDDH · readings taken 2026-09-12T05:31Z on origin/main @ 8fa3fe63d9


    Generated by Claude Code

  12. added 6 commits that reference this issue on Sep 17, 2026
    cb0c616
    1e5b5e0
    fc28c1d
    57343f7
    bf61f0a
    c7fb2c9
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

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions