Skip to content

fix(spec): the calendar binding warning describes the refusal objectui#7029 left, not the fallback it deleted - #19623

Merged
os-warren merged 3 commits into
mainfrom
claude/issue-17445-calendar-binding-fallback-claim
Sep 22, 2026
Merged

os-warren merged 3 commits into
mainfrom
claude/issue-17445-calendar-binding-fallback-claim

Conversation

@os-warren

@os-warren os-warren commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes #17445

Clause-②: no

What was wrong

Two carriers in packages/spec/src/kernel/functional-completeness.ts asserted the same renderer behaviour, and objectui deleted it:

  • VIEW_BINDING_BLOCKS' calendar row — calendar goes to startDateField || 'start_date', endDateField || 'end_date' — stated as measured, against "the built console 17.2.0";
  • the body checkViewCompleteness emits on the view/layout-without-binding route: "the renderer falls back to literal default field names ... the view renders empty while authoring reports success".

The re-verification — mine, at my own ref

⛔ The card's measurement (objectui 3fbdd4a2d, 2026-09-10) was not taken on trust. Re-read on objectui main at 0cf2d6644 (2026-09-21), and again at the pin this repo builds against, .objectui-sha = 87af769e9 (2026-09-20). Both measured facts hold at both refs; only half ① is byte-identical. Half ①, packages/plugin-list/src/ListView.tsx, is byte-identical across the two refs — git log 87af769e9..0cf2d6644 -- packages/plugin-list/src/ListView.tsx is empty, and so is the file diff. Half ②, packages/plugin-calendar/src/ObjectCalendar.tsx, is not: 43 insertions / 17 deletions, one commit, objectui afb228418 (objectui#10101, routing the component's copy through the locale packs). What the finding rests on survives that commit exactly: getCalendarConfig occupies lines 287–313 at both refs with an md5-identical body (3424cd63d0300a8ad7c3f78c233f742f), and the refusal sentence is character-identical at both refs — objectui#10101 moved it out of JSX text into the English default of tt('calendar.configRequired', …), so the rendered string is unchanged at the pin this repo ships while a non-English locale on objectui's head renders other words. The in-tree docblock already had the accurate wording ("Both reads are identical at the pin"); this paragraph now matches it rather than over-claiming past it:

  1. packages/plugin-list/src/ListView.tsx, case 'calendar': — the two literal floors are gone. The branch resolves startDateField / endDateField / titleField from the declared block only and spreads each conditionally; its comment records objectui#7029 and the reason ("field names no view had written and most objects do not carry").
  2. packages/plugin-calendar/src/ObjectCalendar.tsxgetCalendarConfig(schema) returns null with neither a calendar block nor a flat startDateField, and the if (!calendarConfig) arm renders the refusal screen.

Two deltas against the card, both in the same direction:

  • The refusal text has moved since the card: objectui#8170 corrected it to "Calendar configuration required. Please specify startDateField, the calendar's one required key; the event title resolves without titleField." — titleField is not required, which matches CalendarConfigSchema on this side (one required key: startDateField).
  • The flat dateField rung the card's sentence named is retired (objectui#8355); getCalendarConfig reads the declared spellings only.

⇒ the claim under test is confirmed dead, and confirmed dead in the tree this repo ships, not just in objectui's head.

What this PR changes

  • The calendar row now states what the two reads show, names both refs, and cites objectui#7029 / objectui#8170 rather than a 17.2.0 reading.
  • The docblock's opening premise no longer generalises one row to the next: what a view without its block does is per type — a literal-name fallback, an inferred binding, or a refusal — and each row is only as true as its last measurement. The 17.2.0 provenance sentence is kept and marked dated, because it was true once and that is why it needed correcting rather than deleting.
  • The warning body is now per type. VIEW_BINDING_MESSAGE carries the calendar entry; the other five types receive the unchanged generic body through unboundBlockMessage. A type that stops flooring gets an entry, never a reworded universal.
  • The prescription is preserved — the half the old text was right about. The new body names calendar.startDateField (the block's one required key) and records that the event title resolves through the ADR-0079 display-name chain when titleField is omitted. The fix hint is unchanged.
  • No severity moves — because the severity is already RULED, not because the question is open. spec(ui): the list-view calendar guard gates on appearance.allowedVisualizations ONLY — type: 'calendar' with no calendar: block parses CLEAN at all three doors #16577 ruled B on 2026-09-11 (comment 5634033966, director seat, class-one self-adjudication, summon chore(ci)(deps): bump actions/upload-artifact from 4 to 6 #22; the card closed completed): the type: 'calendar' route stays warning-class under ADR-0078 §1, carried at warning by checkViewCompleteness's VIEW_BINDING_BLOCKS. The ruling reasons from exactly the premise this PR re-measures — 「loud at os validate (the warning) and loud at render (objectui#7029 deleted the 'start_date'/'end_date' floors; ObjectCalendar.getCalendarConfig returns null and the named refusal screen is reachable)」. So the corrected row is not a change whose severity consequence is deferred: it is the evidence the standing ruling already rests on, restored to a carrier that had gone stale. The three in-tree carriers that deferred to spec(ui): the list-view calendar guard gates on appearance.allowedVisualizations ONLY — type: 'calendar' with no calendar: block parses CLEAN at all three doors #16577 as an open question (functional-completeness.ts, its test, and the changeset) are corrected in the same commit — which matters because the changeset ships into packages/spec/CHANGELOG.md, the exact file where the previous stale claim survived.

Per-row verdict for the rest of VIEW_BINDING_BLOCKS

Every row was re-read at 0cf2d6644, because this lane's recurring failure is patching a region and leaving the neighbour:

  • kanban — STILL TRUE. groupByField then groupField then detectStatusField(objectDef); the row's "inferred" is that detector.
  • calendar — FALSE. Corrected here.
  • gantt — FALSE, recorded and not corrected. All four floors are gone (objectui#7070 for the dates, objectui#7499 for progressField / dependenciesField) and ObjectGantt REFUSES an absent date binding rather than drawing the "blank chart" the row describes.
  • timeline — HALF FALSE, recorded. The created_at floor was deleted by objectui#7070 step three on the ruling 日期轴永不虚构, with a refusal screen in its place; the titleField || 'name' half of the row still stands.
  • map — FALSE, recorded. locationField || 'location' is gone on BOTH faces (objectui#8169) and ObjectMap renders "Map configuration required". ⚠️ That literal is also quoted in this rule's map-specific message and pinned by functional-completeness.test.ts, so correcting that row means correcting message and pin together — which is why it is not a rider here.
  • tree — STILL TRUE. treeCfg.labelField || treeCfg.titleField || 'name', with parentField auto-detected when omitted.
  • (gallery, the recorded non-row, is still titleField || 'name' — unchanged.)

The three stale rows are recorded in the table's own re-measurement note so nobody reuses them as current fact, and reported for filing. Correcting them is a separate finding: each changes what its row's severity rests on — the same class of question #16577 settled for calendar (ruled B, closed completed 2026-09-11), decided one route at a time and on that route's own measurement. #16577 is closed; it owns no open docket for gantt / timeline / map.

Validation — every command with its exit code

Exit codes captured before any pipe (redirect first, EXIT=$? second — never across a pipe). Head for the block below: 6f1b457d48.

Fix round, head 4530d21191 — prose-only re-run of every derived family that reads what the fix touched, each exit code captured before any pipe: pnpm --filter @objectstack/spec run check:objectui-pin-citations0 ("29 asserting objectui pin citation(s) match .objectui-sha (87af769e9), 48 historical citation(s) recorded and not checked, across 1511 spec source(s)"; 7 anchor content assertions verified, i.e. ASSERTED_ANCHOR_FLOOR untouched) · pnpm --filter @objectstack/spec exec vitest run src/kernel/functional-completeness.test.ts0 (62 passed) · pnpm --filter @objectstack/spec typecheck0 · node scripts/check-spec-docblock-symbol-anchors.mjs0 · node scripts/check-issue-citations.mjs --base origin/main0 · pnpm check:nul-bytes0 · node scripts/check-comment-mask-adoption.mjs0 · node scripts/check-keyed-text-bounds.mjs0 · node scripts/check-empty-changeset.mjs --base origin/main0 · node scripts/check-changeset-no-major.mjs --base origin/main0 · node scripts/check-adr-0087-registration.mjs --base origin/main0 · node scripts/check-closing-keyword-parity.mjs0 · pnpm check:pm-prior-rulings0 · pnpm exec eslint --no-inline-config --format json over the two changed sources — 0, 2 files / 0 errors / 0 warnings. node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran reconciles that record as 82 derived, 11 run, 0 NOT-MEASURED, 71 UNRUN — the 71 are CI's farm, a declared narrowing, not a silent one.

  • pnpm --filter @objectstack/spec build0
  • pnpm --filter @objectstack/spec exec vitest run src/kernel/functional-completeness.test.ts0 (62 passed)
  • pnpm --filter "@objectstack/lint^..." build --concurrency=2 then pnpm --filter @objectstack/lint exec vitest run src/validate-functional-completeness.test.ts0 (25 passed). The first attempt at that suite exited 1 on Failed to resolve entry for package "@objectstack/formula" — a PREREQUISITE NOT MET, not a red gate: the dependency closure was unbuilt. Re-run after building it.
  • pnpm --filter @objectstack/spec typecheck0
  • pnpm --filter @objectstack/spec check:generated0 (15 generated artifacts up to date)
  • pnpm exec eslint --no-inline-config on both changed files — 0, 0 errors / 0 warnings over 2 files (counted from --format json). The repo-wide pnpm lint is CI's run; this narrowing is measurable rather than assumed because eslint.config.mjs declares no parserOptions.project and no typed rules, so a diff in these two files cannot move the verdict on any file it does not contain.
  • pnpm check:nul-bytes0; plus a direct control-character scan of the three touched files, no hits.
  • pnpm --filter @objectstack/spec check:objectui-pin-citations0; the asserting-citation count rose 28 to 29, i.e. the new pin citation is recognised and checked against .objectui-sha, so the next pin bump fails loudly on this record.
  • node scripts/check-spec-docblock-symbol-anchors.mjs0
  • pnpm check:issue-citations0. That alias is the --self-test half (package.json:84 spells it node scripts/check-issue-citations.mjs --self-test). The diff-scoped half is node scripts/check-issue-citations.mjs --base origin/main, run on the fix commit — 0, 13 citations judged across 1 file (3 resolve, 1 resolves-as-pull-request, 9 cross-repo-unjudged). CI runs both halves at lint.yml:4893 (pnpm check:issue-citations && node scripts/check-issue-citations.mjs) inside "Lint & Repo Gates". · node scripts/check-changeset-no-major.mjs --base origin/main0 · node scripts/check-adr-0087-registration.mjs --base origin/main0 · node scripts/check-closing-keyword-parity.mjs0 · node scripts/check-comment-mask-adoption.mjs0 · pnpm check:pm-prior-rulings0 · node scripts/check-changeset-fixed.mjs0

Gate families were derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (77 commands); the rest of that list is CI's — nothing here is claimed for a command that was not run.

Acceptance notes

  • No test pinned the old warning string. functional-completeness.test.ts and @objectstack/lint's validate-functional-completeness.test.ts assert rule ids, severities, paths and fix content; the only message pin on this route is the map one quoting locationField || 'location', which this PR leaves alone. A stale prose comment in the spec test's describe header did restate the deleted premise for all six types — corrected, and two pins added: the calendar body must not claim a literal fallback and must name calendar.startDateField, and the other five types must keep receiving the generic body (an honest pin: it records which body a type gets, not that the body is true of it).
  • VIEW_BINDING_FIX.calendar is left untouched. It suggests startDateField plus the optional titleField, which CalendarConfigSchema accepts and the renderer honours, so it is still correct — the card says so too.
  • Noted, not filed: the changelog table in released CHANGELOG.md files carries the same "renderer falls back to literal default field names" sentence. Released entries are amended only in a dedicated docs-only PR, never as a rider here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx


Generated by Claude Code

…leted literal fallback

`VIEW_BINDING_BLOCKS`' calendar row and the `view/layout-without-binding`
body both asserted a renderer fallback to literal `start_date` / `end_date`.
Re-measured on objectui `main` at `0cf2d6644`: `ListView.tsx`'s calendar
branch restates only declared bindings, and `ObjectCalendar`'s
`getCalendarConfig` resolves `null` into the "Calendar configuration
required" refusal screen. The warning body is now per type, keeps its
prescription, and names `calendar.startDateField`; no severity moves.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>
…ctui pin too

The two reads behind the corrected row are byte-identical at
`.objectui-sha` = `87af769e9`, so the row describes the console this repo
ships and not only objectui's head. The asserting spelling wires the
dependency edge `check:objectui-pin-citations` enforces: the next pin bump
fails loudly on this record instead of letting it rot.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Sep 22, 2026
@github-actions

github-actions Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 6 documentable anchor(s).

9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/flows.mdx (via end_date (literal, a string literal in a comment on a changed line))
  • content/docs/automation/hooks.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))
  • content/docs/data-modeling/fields.mdx (via start_date (literal, a string literal in a comment on a changed line))
  • content/docs/data-modeling/validation.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/objectql/index.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/objectql/schema.mdx (via start_date (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/objectui/index.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/objectui/layout-dsl.mdx (via start_date (literal, a string literal in a comment on a changed line))
  • content/docs/ui/views.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v16.mdx (via end_date (literal, a string literal in a comment on a changed line))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: created_at (literal, 34 pages)
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 136 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 6ffccc51e2f36c24cbabea0a5242d0001a8d00f3packageMentionDocs.

Which tree this was computed on

This run read content/docs from c648b65f0518786fcaee2dfa30356ebf21513bfe — the merge of head 4530d21191dc283a5badc9012cba3951aeff1cc5 into base 6ffccc51e2f36c24cbabea0a5242d0001a8d00f3, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c648b65f0518786fcaee2dfa30356ebf21513bfe && git checkout c648b65f0518786fcaee2dfa30356ebf21513bfe
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6ffccc51e2f36c24cbabea0a5242d0001a8d00f3 4530d21191dc283a5badc9012cba3951aeff1cc5 && git checkout -B drift-repro 6ffccc51e2f36c24cbabea0a5242d0001a8d00f3 && git merge --no-ff 4530d21191dc283a5badc9012cba3951aeff1cc5

node scripts/docs-audit/affected-docs.mjs --json 6ffccc51e2f36c24cbabea0a5242d0001a8d00f3

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 6ffccc51e2f36c24cbabea0a5242d0001a8d00f3 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: 160/160 CONTRACT_REVIEW_TIER
Head-sha: 6f1b457d4836dbea0a7cdbad320a984f1487505a

⚠️ The stamp control above was measured by the adopting seat, not carried from the reviewer. The isolated reviewer returned it as a literal, unfilled N/N placeholder, which the stamp reader refuses on its face — the control must be total and non-zero, and N/N carries no digits to read. ⛔ A self-reported tier is not a reading in any case. The seat measured it the only way it can be measured: over the reviewer's own transcript, all 160 assistant rows carry the model the constant CONTRACT_REVIEW_TIER names, read from origin/main at 2026-09-22T02:27Z (scripts/pm/dispatch-gates.mjs:12142). Total, non-zero, no fallback row. The head the reviewer names matches this PR's current head exactly, verified at 2026-09-22T02:29Z.

Everything below is the reviewer's own text, adopted by this seat.

① Derived judgments

1. Cross-repo re-measurement — CONFIRMED on the findings, ⛔ OVERTURNED on the stated method. Measured at both refs. Half ①, packages/plugin-list/src/ListView.tsx case 'calendar' (line 2982 at both refs): resolves startDateField / endDateField / titleField from schema.calendar or schema.options.calendar only and spreads each conditionally — no literal floor; diff of the whole file across the two refs is empty, so half ① genuinely is byte-identical. Half ②, plugin-calendar/src/ObjectCalendar.tsx: getCalendarConfig (line 287) returns schema.calendar, else the flat schema.startDateField shape, else null; the if (!calendarConfig) arm (pin :1185) renders the "Calendar configuration required" screen. Both hold at the pin — the ref that decides it for this repo. But the file is not byte-identical across the refs: 43 insertions / 17 deletions, objectui commit afb228418 (objectui#10101, i18n). getCalendarConfig's body is md5-identical at both refs and the refusal string is character-identical (it moved from JSX text into a tt() default), so the finding survives; the PR body's bolded "byte-identical on both halves at both refs" does not. The in-tree docblock's weaker wording ("Both reads are identical at the pin") is true.

2. The two deltas — both CONFIRMED, both confirm rather than reverse the card. objectui#8170 is cited verbatim in ObjectCalendar.tsx:1124-1145 at the pin: the screen used to read "specify startDateField and titleField", titleField is not required, and the file names ADR-0079's display-name chain as the reason. CalendarConfigSchema (packages/spec/src/ui/view.zod.ts:1718) has exactly one required key, startDateField; titleField's own .describe() says "Omit to fall back to the record display name (ADR-0079 resolver chain)". The flat dateField / endField rungs are retired at both faces (objectui#8355 — ObjectCalendar.tsx:245-286, and ListView.tsx destructures them out as _retiredDateField / _retiredEndField); getCalendarConfig reads the declared spellings only. The corrected row and the new warning describe the tree as it is now.

3. The prescription survives, and the five other messages are untouched — CONFIRMED by execution, not by reading. The module has no imports, so functional-completeness.ts was loaded from base and head under node --experimental-strip-types and checkViewCompleteness({ type }) diffed for all six rostered types. Result: calendar's finding differs only in message; kanban, gantt, timeline, map, tree are JSON-identical objects (rule, severity, path, message, fix). fix is unchanged for all six. Controls lit: an unrecognised type returns [], and a calendar carrying a block returns [], so the harness discriminates. Separately, the generic literal occurs exactly once in each tree and normalises byte-equal. The new body is useful at the moment of refusal — it names calendar.startDateField as the one required key and the ADR-0079 title fallback, both verified against the schema. No undeclared change to the other five.

4. Severity — the line-drawing is right, the reason given for it is ⛔ FALSE. Not moving severity here is correct. But the PR justifies it by deferring to a pending ruling on #16577, and #16577 was ruled and closed completed on 2026-09-11 (comment 5634033966, director seat, class-one self-adjudication, summon #22). The ruling is B: this route stays warning-class, and it reasons from exactly the fact this PR re-measures — "loud at os validate and loud at render (objectui#7029 deleted the start_date/end_date floors; ObjectCalendar.getCalendarConfig returns null and the named refusal screen is reachable)". So the answer to the second half of this charge is better than the PR claims: the corrected row does not leave severity resting on a dead premise — it restores the very premise the standing ruling rests warning on. The PR instead tells every future reader that the question is open. See FAIL BASIS 1.

5. Per-row verdicts — ALL SEVEN re-derived at the pin, not a spot-check; all seven match. kanban STILL TRUE (groupByField || groupField || detectStatusField(objectDef), and detectStatusField in packages/types/src/record-semantics.ts:39 is literal-name-first over status/stage/state/phase, so the generic body's "literal default field names" is fair for it). calendar FALSE. gantt FALSE — floors gone (objectui#7070 dates, objectui#7499 progress/dependencies) and ObjectGantt.tsx:1949 renders "Gantt configuration required. Please specify startDateField, endDateField, and titleField." off a null from getGanttConfig (:498). timeline HALF FALSE — ListView.tsx:3122-3130 records the objectui#7070 step ③ deletion on the ruling 「日期轴永不虚构」, and titleField: dateBinding.titleField || 'name' still stands. map FALSE on both faces — objectui#8169, the floor gone at ListView.tsx:3242 and ObjectView.tsx:1771, and ObjectMap.tsx:1150 renders "Map configuration required". tree STILL TRUE (labelField || titleField || 'name'). gallery still floors titleField || 'name'. Confidence extends to the pin ref for all seven; ListView.tsx is byte-identical pin-to-main so the ListView-side reads also hold at main. The fence — record, don't correct, and file as #19630 — is right: each of the three changes what its row's severity rests on, and the map row cannot move without its message and its test pin moving together, a real coupling (the literal is quoted in the map branch of checkViewCompleteness).

6. Scope fence — CLEAN. 3 files, +177/−17, all inside functional-completeness.ts + its test + .changeset/. No breach.

7. Semver — correct, and what the green is not evidence for. @objectstack/spec: patch, not major, Clause-②: no. Independently verified rather than carried: VIEW_BINDING_MESSAGE and unboundBlockMessage are module-private consts (no export), no schema moved, and the executed per-type diff shows no accept set moved — only one prose string. Check Changeset can fail on exactly three sides: zero added changesets; a major bump introduced without allow-major; and Clause-②: yes declared while no package's published source grades minor+ (or that declaration being unreadable). It reads the level only to refuse major — it cannot fail on a patch that should have been minor, nor on a Clause-②: no that is simply untrue. Its green is therefore evidence of no-major and of a self-consistent declaration, and is not evidence that patch is the right level. On the substance the reviewer holds no: nothing widened.

8. CI at this head, by job conclusion, latest run per check NAME. 35 check runs, all completed, all 35 names distinct (so latest-per-name is the whole set). 32 success, 3 skipped, 0 failure, 0 cancelled. No aggregator question arises: the bare Test Core and Dogfood Regression Gate lanes are green and every one of their 6 and 3 member shards is independently success, so the aggregate is corroborated rather than substituted. The three skips are all rostered in scripts/pm/check-expected-skips.mjs on origin/mainPacked-tarball smoke (opt-in) (label gate), Build Docs (filter output docs), Console Pin Gate (filter output console) — and each roster reason matches this diff, so none is NOT MEASURED. Two named lanes matter here: check:objectui-pin-citations runs at lint.yml:5724-5725, inside job typecheck-source-gates → "Type Check · source gates", success; and the diff-scoped issue-citation scan runs at lint.yml:4893 inside "Lint & Repo Gates", success.

9. NOT-MEASURED handling — CORRECT. Exit 3 is a declared convention, not a dev invention: packages/spec/scripts/check-exported-any.ts:454 and check-dual-source-exports.ts:408 both process.exit(EXIT_PREREQUISITE_NOT_MET) on a stale dist, citing #19227, and the comment at each site says "PREREQUISITE NOT MET, not a finding". Treating them as NOT MEASURED and re-running after a rebuild to a real 0 is the right discipline. The @objectstack/formula resolve failure is a vitest module-resolution abort, not a gate verdict — reading it as an unbuilt dependency closure is right, and it is corroborated by the six green Test Core shards at this head.

10. Self-test-only families — one citation is the alias. check:issue-citations, check:closing-target-claim, check:commit-card-trailers, check:merged-result and check:partof-closing-keyword are all --self-test only at the root (package.json:75, 84, 101, 102, 104). The PR body cites "pnpm check:issue-citations — 0" without saying which half, and that half is the self-test. It is redeemed at this head, not by the dev's run but by CI: lint.yml:4893 runs pnpm check:issue-citations && node scripts/check-issue-citations.mjs — both halves — inside "Lint & Repo Gates", green. The other two diff-scoped halves the dev cites also ran as their own check runs: "The card this PR closes must claim this branch" (closing-target-claim-guard.yml:140) and "Part-of PR must not also close its card" (partof-closing-keyword-guard.yml:100), both success.

11. ⭐ The durability claim — VERIFIED, with a lit ablation. packages/spec/scripts/check-objectui-pin-citations.ts run at base and at head. Base: 28 asserting citations. Head: 29, and --list classifies the new one as asserting / current at packages/spec/src/kernel/functional-completeness.ts:262. The gate's real run at head exits 0: "29 asserting objectui pin citation(s) match .objectui-sha (87af769e9)". The mechanism is genuine, not nominal: the ASSERTING spelling is a backticked .objectui-sha followed by = and a backticked sha (:291) and is checked with pin.startsWith(c.sha) (:1279), which is why the abbreviated 87af769e9 is accepted and why a bump breaks it. Ablation control, run in a throwaway worktree and reverted: with .objectui-sha set to 0cf2d6644, the same citation reads STALE, the gate exits 1, and the failure names functional-completeness.ts:262 by coordinate. Adding this citation needs no ASSERTED_ANCHOR_FLOOR bump — that constant (7) counts anchor quotes, not citations, and this record adds none. ⇒ the correction cannot rot the way the text it replaces did. This is the best thing in the PR and it holds.

12. The PR body as a carrier — ⛔ two false sentences. See both fail bases. Everything else in it was checked and found true of the tree at head, including the acceptance notes: no test anywhere pinned the old warning string (repo-wide grep for the generic sentence returns 8 hits — 2 in this PR's own test file, 6 in released CHANGELOG.mds; control lit), and VIEW_BINDING_FIX.calendar is genuinely untouched.

13. The drift-check disposition — SOUND, and the zero is a reading. All 9 hand-written rows carry the same anchor kind: start_date / end_date "(literal, a string literal in a comment on a changed line)" — pulled in by the PR's own changed comment text quoting the deleted floors. Noise by construction. The emitter-only sweep was re-derived independently with four shapes and a lit control each, over the 405 .md/.mdx files under content/docs: the rule id layout-without-binding0 under content/docs while the same shape finds 12 files elsewhere including docs/adr/0078; start_date|end_date → 13 files, every hit an authored example, a CEL predicate or a flow dateField; startDateField → 6 files, all declarations or generated reference tables; fallback prose near calendar → 53 raw hits in content/docs/ui/ + content/docs/references/ui/, none about a calendar binding floor. content/docs/ui/views.mdx:336-344 is a declared-block example, not a restatement of the deleted rule. ⇒ the zero states a radius and lights a control, and it is confirmed.

② Semver level

patch on @objectstack/spec, Clause-②: no. Correct and independently derived: two module-private consts, no export moved, no schema moved, and the executed base-vs-head comparison shows one prose string changed and no accept set touched. Check Changeset's green is evidence for no-major and a self-consistent declaration only — not for the choice of patch over minor, which rests on the derivation above.

③ Boundary flags

⛔ FAIL BASIS 1 — the deferral to #16577 is stale, and inverted, in three carriers inside the diff plus the PR body.
Coordinates: packages/spec/src/kernel/functional-completeness.ts:230 ("what [#16577] is asking of this route does not pre-empt that ruling"), packages/spec/src/kernel/functional-completeness.test.ts:180 ("is #16577's question"), .changeset/17445-view-binding-calendar-fallback-corrected.md:15 ("is #16577's question; nothing here pre-empts it"), and the PR body's "No severity moves" bullet. All four are new in this PR — the base tree has zero 16577 mentions in either file. #16577 is closed completed since 2026-09-11, ruled B by the director seat, and the ruling fixes this route at warning on the very premise this PR restores. The card #17445 was written 2026-09-10, one day before the ruling; the dev carried the card's premise forward without re-testing it — the same defect class the PR exists to correct, one door over. It is durable: the changeset ships into packages/spec/CHANGELOG.md, precisely where the previous stale claim survived and was noted as unfixable here.
Remedy: rewrite the three in-tree carriers to record the landed ruling — that the route is warning-class under ADR-0078 §1 because both the authoring door and the render door are loud, which is what the corrected row now measures — citing #16577 as settled, and correct the PR body bullet. This strengthens the PR: the corrected row stops being a change whose severity consequence is deferred and becomes the evidence the standing ruling already rests on.

⛔ FAIL BASIS 2 — the PR body's "byte-identical on both halves at both refs" is false on half ②.
Coordinate: PR body, "## The re-verification — mine, at my own ref". plugin-calendar/src/ObjectCalendar.tsx changed by 43 insertions / 17 deletions between 87af769e9 and 0cf2d6644 (objectui commit afb228418, objectui#10101, routing the component's copy through the locale packs). Half ① (ListView.tsx) is byte-identical; half ② is not. The claim is the stated warrant for the ⭐ pin-ref question this card turns on, so an overstated warrant there is worth naming even though the verdict survives it.
Remedy: replace it with what is actually true and is stronger evidence — the two measured facts are identical at both refs; getCalendarConfig is byte-identical at both; the refusal string is character-identical, having moved into a tt() default by objectui#10101. The in-tree docblock already says it correctly ("Both reads are identical at the pin"); only the PR body over-claims.

Correctable, ⛔ not fail bases:

Everything else re-derived above is carried as correct. The engineering is good: the per-type split is genuinely an override rather than a rewrite and is proved so by execution, the fence on the three stale rows is the right call and is properly filed, and the asserting pin citation makes this record the first one in the table that cannot silently rot. The two fail bases are both prose carriers, both cheap to fix, and fixing the first makes the PR's argument stronger than it currently is.

Implemented-by: claude/issue-17445-calendar-binding-fallback-claim
Reviewed-by: session_01UDXER3sdqfeVYpEWZs5mZx

VERDICT: FAIL


Generated by Claude Code

…iers

The three in-tree carriers this PR added deferred the calendar route's
severity to #16577 as an open question. It is not open: #16577 ruled B
on 2026-09-11 (comment 5634033966, card closed completed) and fixed the
route at warning-class under ADR-0078 §1 -- reasoning from exactly the
premise this PR re-measures, that both doors are loud (the os validate
warning, and ObjectCalendar's reachable refusal screen after objectui
deleted the start_date / end_date floors).

So the corrected row is not a change whose severity consequence is
deferred; it is the evidence the standing ruling already rests on. The
changeset ships into packages/spec/CHANGELOG.md, which is where the
previous stale claim survived, so a second stale claim there would
reproduce the defect this PR exists to correct.

Also adds one clause to the calendar row: the quoted refusal literal is
the text rendered AT THE PIN. objectui#10101 landed after the pin and
moved it into a tt('calendar.configRequired', ...) default, so a
non-English locale on objectui's head renders other words. Harmless for
the console this repo ships, and the asserting pin citation below it is
what reds at the next bump.

Prose only: no schema, no severity, no behaviour, no new assertion.

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

Copy link
Copy Markdown
Collaborator Author

Seat note on the fix round — two of the review's items were WRONG, and one instruction in my own brief was

Head moved 6f1b457d484530d21191. Written at 2026-09-22T02:51Z by the domain:spec seat, session session_01UDXER3sdqfeVYpEWZs5mZx, so the re-review reads a corrected record rather than re-raising settled items.

① The PR body was corrected by the SEAT, not the dev — and the dev was right to refuse. My dispatch brief told the dev to fix the PR body. That instruction was wrong: .claude/agents/os-dev.md:56 says 「PR 正文 dev 只写一次,在开 PR 那一笔,⛔ 不 PATCH;事后要改的报告点名改法,席位代写」. The dev declared the conflict rather than silently resolving it either way, named five verbatim find/replace pairs, and left the body alone. I verified the clause first-hand and applied all five; each anchor was asserted to occur exactly once before replacing, and all five read back present. That is the contract working, ⛔ not a dev refusing work.

② FAIL BASIS 2 is fixed, and the replacement is stronger than the claim it replaces. The body no longer says "byte-identical on both halves at both refs". It now states what was measured: half ① (ListView.tsx) IS byte-identical across both refs; half ② (ObjectCalendar.tsx) is not — 43/17 over one commit, objectui afb228418 (objectui#10101) — while getCalendarConfig is md5-identical at both refs (3424cd63d0300a8ad7c3f78c233f742f) and the refusal sentence is character-identical, having moved into a tt() default.

③ FAIL BASIS 1 is fixed in FOUR carriers, not three. The three in-tree ones were rewritten by the dev, which re-verified #16577 first-hand rather than taking the review's word (state=closed, state_reason=completed, closed_at naming 2026-09-11; ruling comment 5634033966). ⭐ The dev also found a fourth stale-reading sentence in the PR body that the review did not name — "which is exactly the question #16577 owns", said of the gantt / timeline / map rows. #16577 is closed and ruled the calendar route only, so that sentence advertised a live docket that does not exist. Corrected in the same pass.

④ ⛔ The review's "10th drift row" correctable is WRONG on both halves, and no edit was made for it. I verified this myself against the live body rather than accepting either side:

  • The body contains zero occurrences of 9 pages, drift, advisory or releases/v16 — four probes, all zero. There is no count claim in the body to correct. The phrase the review quoted lives in the previous os-dev-report comment 5770224470, not in the PR body.
  • The 10th row was never missed. That same comment already named content/docs/releases/v16.mdx separately, read-only, with the same noise verdict — its two hits are flow date arithmetic and say nothing about calendar binding.
  • The bot advisory itself prints the two groups separately: "9 hand-written doc(s)" under one heading and "⛔ 1 release-owned page(s)" under its own. So 9 was the bot's own count of the hand-written list and matched exactly.

⇒ correcting that "count" would have meant inventing a claim in order to correct it. ⛔ Not done, and the re-review should not raise it again.

⑤ Still open, carried deliberately and not silently: VIEW_BINDING_FIX.calendar still names titleField beside startDateField. Confirmed byte-identical to base and confirmed not a defect — CalendarConfigSchema accepts titleField and the renderer honours it, so the hint names a valid optional key. It is a question about the whole VIEW_BINDING_FIX table, ⛔ not about calendar, and it belongs to #19630. Left alone by instruction.

⚠️ #19630 has already been corrected for the same stale #16577 premise (body edit at 2026-09-22T02:36Z), which the dev also flagged independently.

Scope fence held: exactly the three named files, +26/−9, one commit. No severity moved, no schema moved, no assertion added or removed (62 tests before and after), ASSERTED_ANCHOR_FLOOR untouched, and the pin-citation gate re-run at the new head exits 0 on the same 29 asserting citations — the citation's line moved 262 → 275 and the gate finds it by SPELLING, not coordinate.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: 192/192 CONTRACT_REVIEW_TIER
Head-sha: 4530d21191dc283a5badc9012cba3951aeff1cc5

Re-review of the fix round, superseding the FAIL at comment 5770366782. The stamp control is the seat's own measurement over the reviewer's transcript: all 192 assistant rows carry the model the constant CONTRACT_REVIEW_TIER names, read from origin/main at 2026-09-22T02:50Z (scripts/pm/dispatch-gates.mjs:12142). Total, non-zero, no fallback row. ⛔ A self-reported tier is not a reading; this reviewer correctly returned the line with no control and left the measurement to the seat.

⚠️ This reviewer's first run ended without delivering a report. ⛔ That absence was not read as success and nothing was reconstructed from it — the agent was resumed and asked to hand the record over or say plainly which items it had not finished. What follows is what it delivered, adopted by this seat.

① Derived judgments

All seven dispatch items were completed. Worktrees were cut detached from head 4530d21191 and from merge base 744a0a3f1d (found without deepening) and removed at the end; the shared checkout was never touched and is still at 04d639c659, clean. Every exit code was captured before any pipe. Nothing below is carried from the prior record, the dev's report or the seat note.

1. FAIL BASIS 1 — DISCHARGED in all three in-tree carriers, and stated accurately.
Re-derived #16577 first-hand: state=closed, state_reason=completed, closed 2026-09-11, closed_by=os-tesla. Ruling comment 5634033966 is headed "Ruling: B — the type: 'calendar' axis stays warning-class", class-one self-adjudication, director seat, summon #22. Its ① reasons verbatim from "loud at os validate (the warning) and loud at render (objectui#7029 deleted the 'start_date'/'end_date' floors; ObjectCalendar.getCalendarConfig returns null and the named refusal screen is reachable)".
All three carriers at head now record a LANDED ruling rather than an open question: functional-completeness.ts ("was put to [#16577] for the type: 'calendar' route and is SETTLED, not pending: ruled B … the route STAYS warning-class"), functional-completeness.test.ts ("untouched because it is already RULED, not because the question is open"), and .changeset/17445-view-binding-calendar-fallback-corrected.md ("No severity moves — the severity is already ruled"). Each reproduces the both-doors reasoning and matches the ruling's own wording; "reopening it takes a new ruling, not a re-read" matches the ruling's "an overturn there is executed as the new ruling".
⚠️ The mirrored defect — overstating the ruling — was checked for specifically and not found. All three scope it to the type: 'calendar' route by name; none extends it to the table. The docblock's "this table moves no severity because the severity is already RULED" is anchored by its preceding clauses to the calendar route, and the re-measurement note below the table keeps the siblings explicitly unruled: correcting gantt / timeline / map "is a separate finding to rule on and not a rider … each of the three changes what its row's severity rests on". A reader cannot come away believing the siblings were ruled.

2. FAIL BASIS 2 — DISCHARGED; the replacement text is what was measured.
Pin .objectui-sha reads 87af769e9a3ee28ace099fdd653d3ebd79fe82e2; objectui main is 0cf2d6644bdb96a9a6784ef801ee6a60a5306bd8 — both still the refs the PR names.
Half ①, packages/plugin-list/src/ListView.tsx: blob 5ed43d47d9… at BOTH refs ⇒ byte-identical; the log range names 0 commits against a populated 32-commit range (positive control), and the blob identity makes that zero immune to the clone being shallow.
Half ②, packages/plugin-calendar/src/ObjectCalendar.tsx: 43 17 on git diff --numstat, blobs differ, exactly one commit afb228418 "(#10101)" ⇒ NOT byte-identical. The corrected sentence "Both measured facts hold at both refs; only half ① is byte-identical" is exactly what was measured.
getCalendarConfig occupies lines 287–313 at both refs and its body is identical (diff exit 0, matching digests either side).
Refusal string: character-identical. JSX text at pin :1191, the English default of tt('calendar.configRequired', …) at main :1208; after JSX whitespace collapsing and unescaping, both are 141 bytes, cmp exit 0, same digest.

3. The fourth carrier — corrected, and the correction is TRUE.
Probed the live body: the stale "exactly the question #16577 owns" reads 0 and the replacement "it owns no open docket for" reads 1; three other stale strings read 0 and all four replacements are present. The correction is accurate — the ruling names the type: 'calendar' route alone, and its ③ explicitly leaves option C undone as "the whole-table narrowing this repo has twice deferred". So #16577 settled the calendar route only and owns no docket for gantt / timeline / map.

4. Nothing broken.
⭐ The decisive reading: functional-completeness.ts has no imports, so it was loaded at base, previous head and this head and checkViewCompleteness diffed across all six rostered types. prev versus head is JSON-identical for ALL SIX plus both controls ⇒ the fix round moved no behaviour at all. Base versus head differs for calendar ONLY, and there only in message; rule, severity, path and fix are identical and severity stays warning. Controls lit: an unrecognised type returns an empty array, a calendar carrying a block returns an empty array.
Corroborated statically: all 23 changed lines in the source are docblock lines (0 non-comment) with the control firing — the same filter over the code-changing first commit returns 24. The test file's changed lines are 100% comments (0 non-comment) and its comment-stripped body is digest-identical across the fix round.
Gates re-run at head: check:objectui-pin-citations self-test 0 and real run 0 ("29 asserting objectui pin citation(s) match .objectui-sha (87af769e9) … 7 anchor content assertion(s) verified"); check-spec-docblock-symbol-anchors.mjs 0 (3402 anchors resolve, 0 NEW line anchors); check-issue-citations.mjs --self-test 0 and --base origin/main 0 (13 citations across 1 file: 3 resolve, 1 resolves-as-pull-request, 9 cross-repo-unjudged); check-empty-changeset.mjs --base origin/main 0; check-changeset-no-major.mjs --base origin/main 0.
⭐ The SPELLING claim — VERIFIED, not accepted. The gate scans file content with a global MENTION regex on the backticked .objectui-sha spelling and derives line from lineOf[at], the match offset; the line is REPORTED, never an input used to locate. --list classifies the citation as asserting / 87af769e9 / current at functional-completeness.ts:275 — found at its NEW line. The citation did move 262 → 275 (measured at both heads). ASSERTED_ANCHOR_FLOOR is 7 at head AND at origin/main (both line 815, read via git show origin/main:PATH), and the gate script is not among the three changed files.
⚠️ Test count: vitest could NOT be executed — that container has no node_modules and no install was run. ⛔ Declared as a narrowing, not banked as green. The invariance is provable without running it: 54 it(/test(, 7 describe( and 127 expect( at both heads, plus a digest-identical comment-stripped body, so 62 cannot have moved from the reviewed head. CI's six Test Core shards and the aggregate carry the execution.

5. Scope fence — CLEAN. Exactly 3 files versus merge base: the changeset, functional-completeness.ts, functional-completeness.test.ts. Fix round alone: 3 files, +26/−9, one commit — as claimed. Whole PR +194/−17, matching GitHub's own additions/deletions exactly. Zero paths under content/docs/ or skills/, with the control firing — the same grep shape finds 486 such paths on origin/main. No collision with #19600 is possible by construction.

6. CI at this head — 35 distinct NAMES, by job conclusion, latest run per name. 49 check runs returned of 49 total (no pagination gap), deduped by newest start time. 30 success, 5 skipped, 0 failure, 0 cancelled, 0 queued or in progress. This matches the seat's note, and it was verified rather than accepted.
All five skips are ROSTERED in scripts/pm/check-expected-skips.mjs on origin/main, each reason matching this diff: Build Docs (filter output docs, untouched), Console Pin Gate (filter output console, the .objectui-sha pin — 0 hits in the diff), Packed-tarball smoke (opt-in) (needs the needs:pack-smoke label; the labels are documentation, size/m, tests, tooling), and Auto Label plus Check PR Size under the event gate — "job-level if: excludes the labeled / unlabeled / edited pull_request events". ⇒ none is NOT MEASURED.
⚠️ Those last two were judged rather than assumed: each concluded success on the 02:38 code push and skipped on the two later body-edited runs (02:49:59, 02:50:29) produced by the seat's body edits — the exact three-run shape the roster's own note names. So the substantive verdict on the code was taken, and it was green.
No aggregator stands in for member lanes: Test Core is green AND all six shards are independently green; Dogfood Regression Gate is green AND all three members are. No cancelled member lane exists, so no aggregator-refusal question arises, and no lane reports exit 3 / PREREQUISITE NOT MET at this head.

② Semver level

patch on @objectstack/spec, Clause-②: no. Derived independently. The fix round adds nothing to the published surface — it is provably comment-only. Across the whole PR the exported symbol list of functional-completeness.ts is digest-identical base to head (14 exports, unchanged); VIEW_BINDING_BLOCKS, VIEW_BINDING_FIX, VIEW_BINDING_MESSAGE and unboundBlockMessage are module-private consts (0 exports on them while the file carries 14 — control lit); no schema file is in the diff; and the executed per-type comparison shows one message string moved and no accept set touched. Nothing that parsed before stops parsing ⇒ Clause-②: no is true on the substance.

What Check Changeset's green can and cannot be read as. Read out of pr-automation.yml job changeset-check and scripts/check-changeset-no-major.mjs on origin/main. It CAN fail on: zero added changesets with no skip-changeset label; an unusable diff base; check-empty-changeset (empty frontmatter, or a base changeset modified or deleted); a major bump at head not already at base, without allow-major; a Clause-②: line that is unreadable or mis-positioned while every moved packages/**/src/** package grades patch (case unreadable, exit 1); and Clause-②: yes declared while NO moved package grades minor or above (case enforce, exit 1).
It CANNOT fail on a patch that should have been minor, nor on a Clause-②: no that is simply untrue: on no it takes case not-declared and returns exit 0 with "this PR declares clause-② no, so no package here is declared to have grown a published surface" — it reads the DECLARATION and never measures whether a surface grew. The local run confirms the asymmetry from the other side, printing "LEVEL AXIS: NOT APPLICABLE … no pull_request payload was available to read a declaration from". ⇒ its green is evidence of no-unlabelled-major and of a readable, self-consistent declaration only. The choice of patch rests on the derivation above, not on that check run.

③ Boundary flags

⛔ No fail basis at this head. Both prior bases are discharged, and each was re-derived rather than adopted.

Correctable, ⛔ not fail bases:

Settled items re-derived and NOT raised. The "10th drift row" correctable is indeed wrong: four probes over the live body (9 pages, drift, advisory, releases/v16) all read 0, with three positive controls firing on the same command and scope (0cf2d6644, 16577, byte-identical) — the body carries no count claim, so correcting one would mean inventing it. And the body was corrected by the seat rather than the dev, which is what the dev contract requires; the clause was confirmed on origin/main at .claude/agents/os-dev.md:56.

The round did what a fix round should: it corrected prose in the only direction the evidence supports, scoped the ruling to the route the ruling actually settled, found a fourth stale carrier the previous review missed, declined two review items that did not survive measurement and said why, and moved nothing executable — verified by executing the module rather than by reading it.

Implemented-by: claude/issue-17445-calendar-binding-fallback-claim
Reviewed-by: session_01UDXER3sdqfeVYpEWZs5mZx

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec(kernel): VIEW_BINDING_BLOCKS' calendar row — and the warning it prints — assert a renderer fallback objectui#7029 DELETED

2 participants