Repository navigation
feat(types): NamedListView declares the 17 protocol members, each with a read point (objectui#8980) - #9534
Conversation
…ith a read point `NamedListView` declared none of the 17 live members the protocol declares on the same surface (`ObjectListViewSchema`, the value type of `ViewSchema.listViews` and `ObjectSchema.listViews`) — objectui narrower than the protocol, the direction the maintainer's standing principle forbids. All 17 are declared with their types taken from the protocol, and each gets a read point in the same delivery per the director-seat ruling's item 2: - the 8 view-kind blocks reach the renderer ObjectView dispatches to for that `type`, at the protocol's own top level; the legacy `options.<kind>` nesting keeps working and a canonical block wins key-by-key over it - `grouping` / `rowColor` reach ObjectGrid on the authored grid path - `fieldOrder` / `appearance` / `userActions` / `grouping` / `rowColor` / `data` reach ListView through the host delegation; `data` loses its `as any` cast, which answers objectui#7928's open half - `name` is read on the named-view tab strip `tabs` and `pageName` are declared and reported with their measurement — no renderer behaviour to attach on this surface — as the ruling requires. The objectui#7924 / objectui#7779 census ledger is re-taken with the new figures: 64 declared = 21 read + 43 unread. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
…stView` type The first cut routed it through a new `z.input<typeof ObjectListViewSchema>` alias in the zod mirror, which is a VALUE crossing of the objectui#8317 import boundary — `imported-defaults-8317.test.ts` red by name, correctly: a symbol a mirror imports must be in that file's differential. `@objectstack/spec/ui` already publishes the authored type (`ListView` = `z.input<typeof ListViewSchema>`), so the member indexes that instead, as a type-only import in `objectql.ts` beside the other spec types it already takes. No value crosses the boundary and the mirror is untouched. `name` is the same declaration on both spec shapes — `ObjectListViewSchema` omits and re-extends only `userFilters`. Also: the `gantt` and `chart` fixtures in the new pin carry the protocol's own required bindings, measured by tsc while writing them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
Contract reviewIndependent contract-review seat, round 1, at head Verdict: PASS. 50 claims discharged, where one claim = one assertion carrying its own Population bound (PROVED before any counter was read)Whole tracked tree at this head, 7791 files, by two independent enumerations that agree
Corpus, with resolved paths printed. From Ruled item 1 — the 17, and their types — SATISFIEDMeasured at runtime against the resolved spec, not read off a
⭐ The tombstones do refuse — the load-bearing step that turns 22 into 17. All five The member census is a brace-walk of ownership, not proximity: the TypeScript AST's own Types are taken from the protocol. 16 members index this package's spec-derived Ruled item 2 — a read point each, exceptions REPORTED — SATISFIED15 of the 17 have a read point;
Ruled item 3 — which
|
Contract-review discharge — PR #9534 / card #8980Posted before the Verdict being discharged against
Read at Clause ② and why the carrier was hung Card #8980 declares The reviewer measured both limbs independently and reached
One limb firing is sufficient. Other gates at this same head
What is being removed, and from where
Label sets before removal — PR #9534: The two non-blocking wording observations the reviewer recorded (the Generated by Claude Code |
EnqueuedContinuation of the discharge record above. Every step below is a reading, with its instrument and time named.
The ordering the discharge record promised is visible in the PR's own timeline and can be checked without trusting this comment:
Generated by Claude Code |
…s entry's retired figures The census changeset still carried `47 declared / 6 read / 41 unread`. Those figures were re-taken at objectui#8980 (PR #9534), which declared the seventeen members the protocol declares on this surface: the partition is 21 read and 43 unread of 64 today, and the other three sites that carry the pair together were updated with it. This was the fourth site and the only one left — an unreleased changeset, so the retired figures would have shipped into the release notes. Frontmatter untouched (empty before, empty after): no package's release is affected by the edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
…t the protocol (objectui#7924) (objectstack-ai#9720) Part of objectstack-ai#7924 — the card is NOT settled by this PR; see "What this does not do". Clause-②: no — no declaration face moves. `packages/types/src/objectql.ts`, every `*.zod.ts` and every renderer read set are untouched. ## Why The liveness census (PR objectstack-ai#8933) answers one question per member: *is it declared, and is it read off a named view?* The disposition this card is waiting on turns on a **second** question the census never pinned: **does `@objectstack/spec` declare the member?** That is what separates "the renderer sources it from the wrong place" from "objectui invented a spelling", and the 2026-09-16 director-seat ruling is written entirely on that axis — as **prose, with no assertion that can fail**. That is the exact failure mode this card has already produced twice: `about 52 / seven / ~45` → `47 / six / 41` → **stale again** at `64 / 21 / 43` since objectui#8980. The protocol-side partition is today in the same state the member count was in then: measured once, on a **branch** revision (`d4733f27e4`) in a shallow checkout with a stale `origin/main`, and carried forward in prose. The sweep said *"Re-sample before dispatching bucket ②"*; the ruling repeated it. This PR re-samples it **and makes it an assertion**. ## Figures, each with its instrument — all re-derived on `78a9c6744`, none inherited | figure | value | instrument | | --- | --- | --- | | `NamedListView` declared members | **64** | TypeScript parser (`ts.createSourceFile`, the interface's own `PropertySignature` members). No heritage clause, no index signature, no computed member — so the property signatures ARE the population. One required member: `label`. | | read off a named view | **21** | the pin's AST derivation, every route from `schema.listViews` to a named view classified | | declared and unread | **43** | set difference of the two above | | protocol keys for a named list view | **50** | `ObjectListViewSchema.shape` off `@objectstack/spec@17.4.0` as installed — read off the schema object, not a hand list | | protocol retirement tombstones | **5** | derived by the slot description's own `[REMOVED]` marker, then each refused BY NAME through a real `safeParse` | | live protocol keys | **45** | 50 minus the 5 tombstones | | live protocol keys objectui does NOT declare | **0** | set difference over those two corpora | | objectui-only members | **19** | the same set difference, other direction — the control that the 0 above is a reading | | unread partition | **23 + 8 + 10 + 2 = 43** | derived per member from the protocol key set and the runtime fold | ### Disagreement with the figures the card body carries The card's corrected block says **47 declared / six read / 41 unread**. On today's head those read **64 / 21 / 43**. This is not a new correction by me: objectui#8980 (PR objectstack-ai#9534) declared the seventeen protocol members objectui was missing, and the census pin, `objectql.zod.ts`, `zod-mirror-parity.test.ts` and `.changeset/object-view-unmirrored-keys-7779.md` were all updated with it. **The card body and its title were not.** Not repaired here — a card body is the PM/filing seat's to edit. ## What lands A new `describe` block in the existing census pin (extended, not duplicated — same corpus, same helpers), with every figure **derived at test time** and the literals as the pinned reading: - **the protocol declares 50 keys, 5 of them tombstones** — each refused by name with the protocol's own `[REMOVED]` prescription, with the accept leg (`{ label, columns }` parses) as the control. So objectui not declaring those five is **agreement, not narrowness**; a raw key-count diff reads it the other way and is wrong to. - **objectui declares all 45 live protocol keys** — the narrower-than-protocol direction is **empty today**. It was **seventeen** when objectui#8979 was filed. The control for that zero is the same set difference over the same two key sets in the other direction: **19** objectui-only members. - **exactly one objectui-only member is read** (`options`) — the whole cost of a by-reference mirror in one name, with the strict protocol value's refusal of it measured. - **`.omit().extend().superRefine()` still refuses unknown keys under zod 4** — executed with `safeParse` plus an accept control. This was carried as NOT MEASURED by the 2026-09-10 sweep and by the triage that answered it; if it ever flips, bucket ③'s "the protocol refuses these" weakens to "does not declare these". - **the 43 unread members partition four ways, disjoint and exhaustive**, each member pinned by name: ① 23 protocol-declared, ② 8 legacy `show*` spellings, ③ 10 objectui-only, plus the 2 that objectui#8980 declared inert by ruling. A member changing bucket now fails **by name** instead of staling a prose list. - **the fold is read off the fold**: `SHOW_FLAG_TO_USER_ACTION` is parsed out of `packages/core/src/utils/normalize-list-view.ts` and each of the seven is asserted against its canonical protocol twin inside `userActions`; the eighth (`showDescription` to `appearance.showDescription`) by its own branch. Control: a flag the fold does not know is absent from the map on the same query. ## Ablation — both legs, mutation proven on disk BEFORE any result was read The subject reaches the assertions by `readFileSync` of the source (plus the installed schema object), so there is no `dist` hop to prove; the proof is the on-disk text count plus the blob hash. **Leg A — the fold derivation** (`packages/core/src/utils/normalize-list-view.ts`, HEAD blob `59dc77b6`): removed-text count 1 to 0, injected 0 to 1, blob `59dc77b6` to `1567eae0`. Result: `Tests 1 failed | 213 passed (214)`, and the **only** red row is *"the runtime fold maps seven of the eight legacy spellings…"*. Restored with `git checkout HEAD -- ABSOLUTE_PATH` from a `trap … EXIT INT TERM`; restoration proven by blob equality back to `59dc77b6` **and** an empty `git diff HEAD` for the path. **Leg B — the declaration and the protocol difference** (`packages/types/src/objectql.ts`, HEAD blob `033c24f9`): one bucket ① member renamed (`navigation` to `navigationZZZ`), counts 1 to 0 and 0 to 1, blob `033c24f9` to `147d6bbd`. Result: `Tests 5 failed | 209 passed (214)`, red rows exactly: the declared-set pin, the 21+43=64 partition, `UNREAD — navigation`, **`objectui declares every LIVE protocol key … is EMPTY today`**, and **`BUCKET ① — navigation`**. So the zero in this PR's headline row is a live query, not a dead one. Restored and proven the same way; blob back to `033c24f9`, `git diff HEAD` empty. Neither ablation file is edited by this PR. ## Gates — exit code captured to a file before any pipe | gate | exit | printed verdict | | --- | --- | --- | | `pnpm exec vitest run packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts` | 0 | `Test Files 1 passed (1)` · `Tests 214 passed (214)` (164 before) | | `pnpm --filter @object-ui/types test` | 0 | `Test Files 202 passed (202)` · `Tests 4748 passed (4748)` | | `pnpm --filter @object-ui/types run type-check` | 0 | all three `tsc` invocations echoed, `tsconfig.test.json` among them, so the test file IS typechecked | | `eslint --no-inline-config` on the changed test file | 0 | `--format json`: 1 file linted, 0 errors, 0 warnings | | `check:control-bytes` | 0 | | | `check-changeset-presence` | 0 | *"Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption"* | | `check-changeset-no-major` | 0 | `No changeset declares a major bump.` | | `check-changeset-fixed` | 0 | `All workspace packages are in the changeset fixed group.` | | `check-changeset-overwrite` | 0 | **report-only finding, named on purpose — see below** | | `check:changeset-claims` · `check:pending-changeset-literals` | 0 | | | `check:new-line-citations` | 0 | this PR adds no cross-file line address | | `check:test-path-roots` · `check:installed-pin-claims` | 0 | | | `check-governed-queue-guard --test` on the changed test path | 0 | `NOT GOVERNED — 1 path(s) checked against 5 governed surface(s); none matched.` | Every heavy run went through the shared verify lock (`OS_VERIFY_LOCK_SLOT=dev-7924`), each printing its own `VERDICT command-exit` line. **Declared narrowing of the lint run.** Repo-wide `pnpm lint` is `turbo run lint` and belongs to CI; locally only the changed file was linted. The narrowing is measurable: the file count is read off eslint's own `--format json` output (1), and `eslint.config.js` configures **no** type-aware linting (no `project` / `projectService`), so this diff cannot move the verdict for any untouched file. Figures taken at `4794f4786`, the final commit. ## The in-place repair, named with its evidence `.changeset/7924-named-list-view-liveness-census.md` — the census's own, still unreleased — carried the retired `47 declared / 6 read / 41 unread`. The other three sites that carry the pair were re-taken at objectui#8980; this was the fourth and last, and it would have shipped those figures into the release notes. Repaired in the same defect class as this card. **Frontmatter untouched** (empty before, empty after), so no package's release changes; `check-changeset-overwrite` reports the edit (`declared at base: nothing` / `declares now: nothing`) and it is that gate's own documented case 2, *"CORRECTING a declaration on purpose — prose that no longer matches the change"*. ## What this does not do - **No member is retired and no member is declared.** Buckets ② and ③ of the 2026-09-16 ruling retire 18 members from a published TS face — that narrows a published accept set (`Clause-②: yes`) and is outside this dispatch's remit. This PR only makes the bucket each of those 18 sits in a **derived, failing** assertion, which is the input that PR needs and the thing that was still prose. - **Bucket ① is not implemented.** Making the delegation read the 23 protocol-declared members off the named view is capability growth, on `packages/plugin-view/src/ObjectView.tsx`. - **The card is not closed** and its body's figures are not edited. ## 维护者速读(草稿) - **改了什么**:只加测试。把「这 43 个成员分别属于哪一类处置」从评论里的文字,变成每次跑测试都会重新推导的断言 —— 推导来源是安装着的 `@objectstack/spec`、声明文件自己的 AST、以及运行时的折叠表。 - **为什么改**:这张卡的数字已经错过两轮(52 → 47 → 今天的 64)。裁决所依据的「协议有没有声明这个键」这一面,至今只在一条分支上量过一次,从没有任何断言守着。现在有了:某个成员换了桶,测试按名字红。 - **风险与代价(含回滚)**:不动任何已发布面,风险仅限于测试变严;回滚就是 revert 这个 PR。 - **席位意见**:(留空) - **你要做的**:不需要做什么。若要推进退休(八个 `show*` 拼法 + 十个本地键),那是另一张 `Clause-②: yes` 的 PR。 --- _Generated by [Claude Code](https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #8980
NamedListView(@object-ui/types) declared none of the 17 live members theprotocol declares on the same surface. objectui was NARROWER than the protocol on
all 17 — the direction the maintainer's standing principle forbids. This declares
all 17 with their types taken from the protocol, and gives every one of them a
read point in the same delivery.
Governing ruling: director-seat class-one adjudication, objectui#8980 comment
5651818304, 2026-09-13. Its four items are executed as written; none isre-litigated here.
Premise, re-derived on this branch — the card still holds
Re-measured at
origin/mainbbc9dc34e3, non-shallow, against the resolved@objectstack/spec@17.4.0, with the instrument self-tested on a firing positivecontrol (two real interfaces, 9 and 2 members, both verified against source) and
a non-firing negative control (a nonexistent interface name, exit 3):
ObjectListViewSchemashape keysNamedListViewmembers, beforeborderedperformanceresponsivestripedvirtualScrollThe counting rule, published with the number — the seat deliberately
certified no total, so this is mine and is stated so it can be re-derived:
top-level property signatures at brace-depth 1 of the
export interface NamedListViewbody, line and block comments stripped,readonlyand?allowed; members of nested object-literal types are NOT counted. Under that rule
the interface reads 47 before and 64 after. The interface's own census pin
(
object-view-unmirrored-keys-7779.test.ts) walks the same population with theTypeScript parser and agrees.
The tombstones are measured, not assumed. Each refuses a plausible value with⚠️ Six other keys also refused my first
a message naming its 17.0.0 removal.
probe values (
namepageNameon a 2-character minimum,datakanbangantttimelinechartgroupingon missing required inner fields) — that is thePROBE's fault and not six more tombstones, the same self-correction the earlier
seat recorded. The discriminator is the refusal MESSAGE, not the refusal.
NamedListViewopensat
objectql.ts:2088, not:2073. Everything here binds to the SYMBOL.Ruled item 3 —
tabs, measured before it was declaredThe protocol spends the word
tabson two different keys, and this establisheswhich one the list shape carries:
ObjectListViewSchemaisListViewShapeSchema.omit({ userFilters }).extend({ userFilters: ObjectUserFiltersSchema }), so the top-leveltabs: z.array(ViewTabSchema)passes through untouched. That is the key declared here.userFilters.tabsis the oneObjectUserFiltersSchemaOMITS as page-only, in its own words: "an object view's tab bar is its saved-view switcher (ViewTabBar), and a second one would collide."objectui#9517's landing on this same file does not change that reading: it moved
the user-filter FIELD shape and declared the container a dialect, not the
container's
tabs. objectui'suserFiltersdialect keeps carrying its ownlegacy
tabsseparately, so the two keys now sit on one interface and thedeclaration says so.
What each of the 17 reaches
ObjectViewhas three exits and the members do not all leave by the same one.kanbancalendargallerytimelineganttmapgenerateViewSchema— the AUTHORED path — now resolves the protocol's top-level block and merges the legacyoptions.KINDnesting under it, so each reachesObjectKanban/ObjectCalendar/ObjectGallery/ObjectTimeline/ObjectGantt/ObjectMapcharttreegroupingrowColorObjectGridon the authored grid path (it already reads both), andListViewthrough the host delegationfieldOrderappearanceuserActionsListViewthrough the host delegation.fieldOrderis the live third key of the protocol'scolumnstimeshiddenFieldstimesfieldOrdercomposition — objectstack#15184 ruling B, read on the card rather than assumed: that card is OPEN, and its ruling B KEPT the keydataListView— and theas anycast the renderer reached this key through is gone, which is objectui#7928's open half answerednamelabeland the record keytabspageNameThe merge is per-KIND and per-KEY, not wholesale, and identity is preserved
when no canonical block is declared — which is the whole existing population,
since these keys could not be authored before this change. So the renderer
produces byte-identical nodes for every stored document, and there is an
absence-control case beside every fix arm asserting exactly that.
Ruled item 2's report — three members measured, none invented, none dropped
"A member for which the renderer has no behaviour to attach is REPORTED on this
card with the measurement." Posted in full on objectui#8980.
tabs— no reader on this surface. objectui's tab bar for an object is the HOST-owned saved-view switcher (ADR-0053); the onlytabsread underpackages/plugin-listisUserFilters'config.tabs, which is the page-only key, not this one.pageName—pageis not a member ofNamedListView['type'](seven values against the protocol's ten), so no authored named view can select the branch this key configures.chart/tree— READ, and the pin proves it, but objectui#5321 ruled both view KINDS host-composition-only. A named view reaches them only by declaring notypeof its own while a hostviewsentry selects the kind. The read is real; the authored route to it is not.All three stay declared. ⛔ No read point was invented to make the count come out
even, and ⛔ no member was dropped to avoid the report.
Types are taken from the protocol
Sixteen index this package's own spec-derived
list-viewnode type(
ListViewSchema), whose members arrive fromSpecListViewSchema.shapebyreference — the derivation
NamedListView.userFiltersalready used one memberup, and the one that keeps a named view and the
list-viewnode it is relayedINTO the same type for every key that crosses.
nameindexes the protocol's ownpublished authored type (
ListView,@objectstack/spec/ui) instead: on the nodethat spelling resolves through
BaseSchema.name, the component-name slot — adifferent contract wearing the same word.
by
imported-defaults-8317.test.ts: importing a spec schema into a mirror is acrossing of the objectui#8317 import boundary and must appear in that file's
differential. The type-only import in
objectql.tscrosses no boundary and themirror is untouched.
Two narrowings the protocol owns, found by tsc while writing the pins
Reported rather than worked around, because the declared face is now the
protocol's:
ganttrequirestitleFieldalongsidestartDateField/endDateField. Unlike the four blocks this package keeps a local.partial()dialect for,ganttflows into the mirror straight fromSpecListViewSchema.shape, so the protocol's required bindings are required here.chartis the ADR-0021 dataset-bound shape only (dataset+valuesrequired). The renderer still carries a legacy inline-aggregate branch below it (xAxisField/valueField/aggregation) that the declared face cannot express. ⛔ Not widened locally — that legacy shape still reaches the branch through the untypedoptions.chartbag, and narrowing an authoring surface toward the protocol is the direction the principle asks for.The census ledger is re-taken, not adjusted
object-view-unmirrored-keys-7779.test.tspinsNamedListView's population byname and by count, and its own failure message names the four files that carry
the figures together. All four are updated in this change:
64 declared = 21 read + 43 unread (was 47 = 6 + 41, plus one undeclared cast
read).
tabsandpageNameare two of the 43, and the ledger's docblock saysthat is the ruled outcome rather than an oversight. The instrument agreement is
re-pinned too: parser 64 = strict regex 64, loose regex 76 — still a gap of
exactly 12, because every new member is a single-line type reference that adds no
nested object literal.
⭐ One genuine ledger change beyond the numbers: the AST derivation and the
currentNamedViewConfig?.KEYregex it replaced now disagree by one NAME, notjust by sites —
nameis read only at the tab strip. The difference is assertedby name rather than smoothed over.
Verification
Run from the repo root in a dedicated worktree, every heavy run through the
shared verify lock.
pnpm exec vitest run packages/types/ packages/plugin-view/— 229 files / 4707 tests passed, lock VERDICT command-exit 0.pnpm --filter @object-ui/types --filter @object-ui/plugin-view type-check— exit 0. It covers the new test file: its first run reported the two fixture errors that produced thegantt/chartfindings above, so the coverage is a reading and not an assumption.pnpm --filter '@object-ui/plugin-view^...' build— exit 0, 16 projects. The@object-ui/typesdependency closure is EMPTY (it depends only on@objectstack/specandzod), so that package's own closure build is a no-op and is reported as such rather than as a pass.package.jsonand.github/workflows/, ⛔ not from the sibling repo'sdispatch-gates.mjs, which answers only about the tree it lives in:check:control-bytescheck:new-line-citationscheck:spec-symbolscheck:vi-mock-specifierscheck:vi-mock-inheritcheck:vi-mock-override-shapecheck:test-path-rootscheck:changeset-claimscheck:comment-mask-corpuscheck:unreferenced-sourcescheck:lint-rule-coveragecheck:handler-key-readscheck:changeset-presence— all exit 0.check:eager-closure,check:sdui-registration-pinsandcheck:readme-exportsare NOT MEASURED here, ⛔ not red: each refuses for a missing console/workspace build in its own words ("this is a broken gauge, not a passing budget"; "the population COLLAPSED — this run proves nothing", citing 21 unbuilt packages). CI builds and owns them.eslinton the six changed files: 0 errors, 115 pre-existing warnings, exit 0. Package-scopedlintfor both affected packages: 0 errors (286 + 348 warnings, none new-rule). The narrowing is a measurement, with its three pieces: the covered population iseslint .minus this config's ownignores; the file count is 6, read from--format json; and the invariance holds because the config configures NO type-aware linting (zeroproject/projectServiceentries), so this diff cannot move the verdict on any file it does not touch. Measured on final head0e69156.grep -naPover every changed file finds none, with a firing positive control on a synthetic file.Reverse verification — direction predicted before the run
The renderer alone was restored to the merge-base on disk (blob
b6c0cc566bto008aa55149, mutation proven by hash comparison AND by fivemarker counts going 4/1/1/1/1 to 0/0/0/0/0), the new pin re-run, then restored
under a
trap ... EXIT INT TERMand proven back by blob hash and an emptygit diff HEADon that path.Predicted: every FIX arm RED, every CONTROL arm GREEN. Measured: 13 failed, 7
passed — the 13 are exactly the fix arms (six view kinds, the chart/tree route,
the canonical-wins and per-key-merge arms, the grid pair, the delegation six, the
userActionsmerge, thenamefallback) and the 7 that stayed green are thecontrols (the legacy-nesting control, both absence controls, the
labelprecedence and record-key controls, the
tabs/pageNameabsence pin, and thedatabehavioural arm — which passes on both trees because the old cast relayedthe same value, exactly as that case's own comment says, its source half being
pinned in the types census instead).
distpreflight: this repo's root vitest config aliases every@object-ui/*specifier to that package'ssrc, so the suite runs from sourceand an on-disk proof is the right evidence here.
Scope
Clause-②: no, measured: the runtime accept set does not move.ObjectViewSchema.listViewsis UNMIRRORED on the zod side and ridesBaseSchema's passthrough, so no parse changes; the TS face gains optional members, which widens rather than narrows. The--pairexit code is reported to the PM seat.needs:contract-reviewincluded — labels are the PM seat's alone.NamedListViewdeclares beyond the protocol are objectui#7924's remedy and are untouched (ruled item 4). That card shares this file and is serial behind this one.packages/app-shellis NOT touched, though it is wherenameis written today (mergeViewsIntoObjectsstampsname: keyon every composed entry and primary-view promotion matches on it — recorded at the declaration as the evidence that this key has been travelling on this surface undeclared). It is outside the claim's declared file surface.Generated by Claude Code