Repository navigation
spec: interface pages accept appearance.allowedVisualizations: ['calendar'] but the page surface has NO calendar binding key at all — the #13748 shape on a different door #14073
Description
Activity
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsClaim: PM loop,
domain:specseat (seat post #6017), R3 of this shift — group member; the anchor claim with the full file surface, tier reading and serial-constraint check is on #14074 (comment posted in the same stroke, per triage 5488531595 / 5488535566: one measurement serves both cards, ⛔ not merged)
Session:session_0174WZTU6XcFcS7g2kykC53i(GitHubzhuangjianguo)
Branch:claude/issue-14074-switcher-visualization-binding(the group's shared branch; this card produces no code)
Worktree:objectstack-issue-14074(shared with the anchor)
Domain:domain:spec
File surface: MEASUREMENT ONLY — the page door: an interfacelistpage whoseappearance.allowedVisualizationswhitelistscalendarwith no binding key available on the page surface (packages/spec/src/ui/page.zod.tsdeclares zerocalendar:keys on0fb944b4; showcaseexamples/app-showcase/src/ui/pages/task-visualizations.pages.ts:48whitelists['calendar']), read on objectui at two points — BEFORE objectui#7029 landed (the commit preceding its merge) and AFTER (objectuiorigin/mainf0f774b0) — throughpackages/app-shell/src/views/InterfaceListPage.tsx:401and the calendar renderer path. ⛔ No spec edit for this card: the A / B / C directions are a contract decision the seat routes after the measurement (A widens the public surface, C narrows an accept set — either is Clause ②). Deliverable: anos-dev-reporton THIS card with the measured behaviour on both sides of objectui#7029 and an evidenced recommendation among A / B / C.
Container & model: as the anchor — M,mode:subagent,model: fable.
Clause-②: no — this card ships no diff; the eventual A / B / C implementation is a separate dispatch after a decision.
Serial constraints cleared: see the anchor claim (scan 19:08Z; no open PR touchespage.zod.tsor the showcase pages; churn none; H17 index names no row forpage.zod.ts).Decision re-read (1 comment): triage 5488531595 — measure first, ⛔ do not change spec first; the measurement must be taken before and after objectui#7029 (now closed 2026-08-31, so "after" is current objectui
mainand "before" is a git reading); the seat needs both repos readable (it does); group with #14074; H14 removal frozen. Premise re-read onorigin/main0fb944b4(19:10Z):page.zod.ts:324-327,:359-361,:412-413route the display mode toappearance.allowedVisualizationsand nocalendar:key exists on the page config; showcase whitelists['calendar']at:48. Premise holds.
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsDispatch (R3 of this shift, 2026-09-03T19:13Z) —
domain:specseat,session_0174WZTU6XcFcS7g2kykC53i, seat post #6017. Group member; the dispatch text is on the anchor #14074 (comment posted in the same stroke). Same dev, same worktree,model: fable, size M. This card: measurement only, no code,Clause-②: nofor the dispatch (the A / B / C implementation is a separate decision-routed dispatch).What the dev measures for this card (triage 5488531595's "⛔ 先测,再选 A/B/C" and its before/after objectui#7029 requirement quoted verbatim): an interface
listpage whitelistingcalendarwith no binding key on the page surface — whatInterfaceListPage.tsx:401and the calendar renderer path do at the commit preceding objectui#7029's landing and at objectuiorigin/mainf0f774b0; whether the showcase pages (task-visualizations.pages.ts:48,:129) render, hide the toggle, refuse, or invent a date. Deliverable: anos-dev-reporton this card with both readings and an evidenced recommendation among A / B / C; the seat routes the decision from there.
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsos-dev-report
{ "issue": 14073, "status": "done", "branch": "claude/issue-14074-switcher-visualization-binding", "pr": null, "premise_still_valid": true, "summary": "Page-door reading (measurement only, no code; Clause-②: no). Schema premise confirmed on objectstack `82291fba` (origin/main when the worktree was cut; page.zod.ts unchanged since 0fb944b4): `InterfacePageConfigSchema` (packages/spec/src/ui/page.zod.ts:421-467) carries `appearance` at :438 and no per-visualization key — the strict-key guidance map :412-415 routes `visualization`/`visualizations`/`kanban`/`groupBy` to `appearance`; the showcase page task-visualizations.pages.ts:48 whitelists `['calendar']` and :129 the full set. AFTER objectui#7029 (closed 2026-08-31 by PR #7062 = commit 2a7ac324; measured at f0f774b0, tip 98d4108a identical on these files): InterfaceListPage.tsx:409-414 derives `calendar = view.calendar ?? (allowed has calendar ? defaultCalendarFromObject(objectDef) : undefined)` — defaultDateField :164-169 takes the first non-hidden, non-system field typed date/datetime/time, else the first field whose NAME matches date|due|start|end|deadline|schedule — a real field or undefined, never a literal; timeline :414 borrows the same helper. Showcase :48 over showcase_task (task.object.ts:70 declares `due_date` before `start_date`/`end_date`): derives `startDateField: 'due_date'` (probe: showcase calendar = {startDateField:'due_date'}), the ListView gate resolves calendar (ListView.tsx:2101), `viewType = allowed[0] = 'calendar'` (:460), and the page RENDERS a truthful calendar bound to due_date — it never relied on the invented default: the derivation exists on both sides of #7029 (InterfaceListPage diff 14884620..f0f774b0 touches only defaultColumnsFromObject). Source object with NO date field and `['calendar']` leading: derivation is undefined (probe on a dateless object), but `viewType: 'calendar'` is force-pushed into `resolvable` (ListView.tsx:2137-2140, 'always allow switching back to the viewType') so the calendar branch (:2423-2450) mounts and forwards no startDateField (probe: forced calendar props = {}); ObjectCalendar getCalendarConfig (plugin-calendar/src/ObjectCalendar.tsx:142-165) returns null and the refusal screen :795 shows 'Calendar configuration required. Please specify startDateField and titleField.' = (b) honest refusal — whose remedy is unauthorable on this door: the page config has no calendar key; the only channel is `sourceView` naming an object view that carries a `calendar:` block (exactly how the showcase map page :100-121 binds map). Dateless object with calendar whitelisted but NOT first (e.g. ['grid','calendar']): filtered out silently = (a) hidden, no signal at build or run time (the switcher chrome still appears because showViewSwitcher reads the whitelist length, :482). BEFORE #7029 (14884620, parent of 2a7ac324): identical derivation; the difference is the forced dateless case — the ListView calendar branch floored `startDateField || 'start_date'` and `endDateField || 'end_date'` (14884620 ListView.tsx:2240-2241; plugin-view ObjectView.tsx:1319-1320), getCalendarConfig then read a complete-looking config and every record without start_date landed on today = (c). The 'due_date' fabrication itself was the VIEW door (14884620 app-shell ObjectView.tsx:2220), never the page door. So the #13748 shape on the page door existed only pre-#7029 and only for a source object with no derivable date; today it is (b) when calendar leads the whitelist and (a) otherwise. Both showcase pages render today. Recommendation: B — the runtime already derives honestly; what is missing is the LOUD half at authoring time for the (a) case and a remedy the door can take. Concretely an authoring-time lint in packages/lint (the shape of #14106's `view/layout-without-binding` and of validate-list-view-field-refs.ts, which already reads object field maps): for every interface `list` page, each entry of `appearance.allowedVisualizations` must be derivable from the source object's fields with the predicates InterfaceListPage uses (kanban: select-like type or status-like name; calendar/timeline: date-typed or date-like name; gallery: image-typed; gantt: two distinct date fields; map: location-typed or geo-like name) OR bound by the referenced `sourceView` block, with `sourceView` named as the remedy. No page.zod.ts change — the page surface stays closed, which is what ADR-0047 §7 open question 3 asks for ('authoring-time diagnostics'). Not A: it widens the published page surface and restates on the page the bindings the ADR's 2026-06-19 revision left on the object view, while `sourceView` already provides the channel. Not C: it would refuse the six showcase pages that derive correctly today. Optional objectui rider for the PM to route: the refusal screen could name `sourceView` when reached from an interface page.", "tests": "Same run as #14074's report (objectui throwaway worktree at f0f774b0, `pnpm exec vitest run --maxWorkers=2`, VERDICT command-exit 0, `Tests 32 passed (32)` for the two probe files; `103 passed` across the 8 existing pins in run 1). Page-door readings: PROBE showcase calendar = {startDateField:'due_date'}; showcase gantt = {startDateField:'start_date', endDateField:'due_date'}; showcase kanban = {groupByField:'status'}; showcase gallery = {coverField:'cover'}; showcase map = {locationField:'location'}; a dateless object derives undefined for calendar, gantt, map, kanban and gallery; forced calendar props = {} (no startDateField) — and plugin-calendar's ObjectCalendar.unconfiguredRefusal-7029 pin (green) is what turns that into the refusal screen. The BEFORE reading is a git reading at 14884620 (the parent of 2a7ac324): `git show 2a7ac324 -- packages/plugin-list/src/ListView.tsx` shows the deleted `|| 'start_date'` / `|| 'end_date'` floors; `git diff 2a7ac324^ f0f774b0 -- InterfaceListPage.tsx` touches only defaultColumnsFromObject; `git log --grep 7029` and the objectui#7029 issue payload (closedByPullRequestsReferences = #7062, MERGED) agree on the landing commit. No objectstack code changed; no gate owed.", "mcp_calls": "5 in total for the group run (counted once on #14074's report): search_issues x2, issue_write x1 (objectui#7544), add_issue_comment x2.", "open_questions": [ { "question": "If B is adopted as a lint, should it mirror BOTH of InterfaceListPage's derivation predicates (field TYPE, then the NAME regex fallback) or only the type-based half?", "options": [ "A — type-based only: simpler and drift-free, but stricter than the runtime (a page whose only date is a text field named due_date would be refused while objectui derives it).", "B — mirror both predicates: exact parity with what renders, at the cost of a second copy of objectui's heuristic that can drift (pin it with a fixture test that lists the predicates verbatim)." ], "recommendation": "B, because the #13748 ruling targets silent WRONG screens, not working derivations — a lint stricter than the runtime refuses pages that render correctly today; note the drift risk in the rule header." }, { "question": "Severity for the not-first case (calendar whitelisted, not leading, no derivable date — the type is silently hidden).", "options": [ "A — warn, matching `view/layout-without-binding`'s posture (the page is visibly smaller, nothing wrong renders).", "B — error, matching #13817's parse-time refusal on the view door." ], "recommendation": "A for the hidden case and B for the leading-entry case (where the runtime reaches the refusal screen), so the lint's severity tracks what the user actually sees." } ], "out_of_scope_findings": [ "Observation, not filed (objectui heuristic, PM to route): defaultGanttFromObject (InterfaceListPage.tsx:185-197) on showcase_task derives endDateField 'due_date' rather than 'end_date' because due_date precedes end_date in declaration order and the end predicate matches /end|due|finish|deadline|close/ — the showcase Gantt page (:74) renders start_date to due_date bars; a real field, not the #13748 shape.", "Cross-reference: the group's other findings (objectui#7544 filed; the forced-path literals; the gallery 'image' literal; the showViewSwitcher length quirk; the timeline-via-calendar axis note) are listed once on #14074's report." ] }Generated by Claude Code
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsMeasurement review — ACCEPT · direction B chosen · card returns to
pm:queuefor the implementation dispatch (report 5531217232, no PR by design)Reviewer of record: PM seat
domain:spec(session_0174WZTU6XcFcS7g2kykC53i). Triage 5488531595 deferred the A / B / C choice to the measurement; the measurement is in, so the seat picks on the measured facts (recorded here for the maintainer's veto in the round report).What the page door does today (objectui
f0f774b0, spot-checked 19:49Z).InterfaceListPage.tsx:412derivescalendarfrom the source object's fields (defaultCalendarFromObject:170→defaultDateField:163: first date-typed field, else the first date-like name) — a real field orundefined, never a literal; the showcase pagetask-visualizations.pages.ts:48therefore renders a truthful calendar bound todue_dateand never depended on the invented default objectui#7029 removed. With NO derivable date:calendarleading the whitelist is force-pushed as theviewTypeand reaches the refusal screen (ObjectCalendar.tsx:795, "Calendar configuration required") = (b), whose remedy is unauthorable on this door except throughsourceView;calendarnot leading is filtered out silently = (a), with switcher chrome still shown (showViewSwitcherreads the whitelist length,:482). The #13748 shape (c) on this door existed only before objectui#7029, only for a dateless source object. Both showcase pages render today.Direction: B. Not A — it widens the published page surface and restates on the page the bindings ADR-0047's 2026-06-19 revision left on the object view, while
sourceViewalready is the channel. Not C — it would refuse the six showcase pages that derive correctly today. B is what is missing: the LOUD half at authoring time for the (a) case and a named remedy for the (b) case, with nopage.zod.tschange (ADR-0047 §7 open question 3 asks exactly for authoring-time diagnostics).Implementation scope (the next dispatch on this card,
packages/lint, spec lane per the #14106 precedent — the rule guards the page contract). A rule in the shape ofview/layout-without-bindingandvalidate-list-view-field-refs.ts(which already reads object field maps): for every interfacelistpage, each entry ofappearance.allowedVisualizationsmust be derivable from the source object's fields with the SAME predicatesInterfaceListPage.tsxuses (kanban: select-like type or status-like name; calendar / timeline: date-typed or date-like name; gallery: image-typed; gantt: two distinct date fields; map: location-typed or geo-like name) OR bound by the referencedsourceView's block, namingsourceViewas the remedy. The dev's two open questions, answered: (1) mirror BOTH predicates (type, then the name regex) — a lint stricter than the runtime would refuse pages that render, against the #13748 ruling's target of silent WRONG screens; pin the predicate list with a fixture test and note the drift risk in the rule header; (2) severitywarnfor the hidden case (not leading),errorfor the leading-entry case (the runtime reaches the refusal screen) — the diagnostic tracks what the user sees. Clause ② no (a lint rule; no spec accept/reject or surface change). The optional objectui rider (the refusal screen namingsourceViewwhen reached from an interface page) goes into the objectui finding filed for the #14074 group's observations.State.
pm:dispatched→pm:queue, assignee released; the H14 freeze stays (the card names objectui#7029). Dev session for the measurement: Agent task ad5c6cb868bd3b038 (subagent, finished).
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsClaim: PM loop,
domain:specseat (seat post #6017), R3 of this shift — round 2 on this card: the direction-B implementation ruled by the measurement (5531261969)
Session:session_0174WZTU6XcFcS7g2kykC53i(GitHubzhuangjianguo)
Branch:claude/issue-14073-interface-page-visualization-lint
Worktree:objectstack-issue-14073
Domain:domain:spec(the rule guards the page contract; #14106'sview/layout-without-bindingprecedent)
File surface: a new authoring-time rule underpackages/lint/src/(name the implementer's; shape ofvalidate-list-view-field-refs.ts/validate-page-field-bindings.ts, which already read pages and object field maps) + its registration (packages/lint/src/index.tsexports; the suite table inreference-integrity-suite.tsor the authoring-rule table, whichever the shape belongs to — measured, not guessed) + its test file with fixtures (the six showcase interface pages inexamples/app-showcase/src/ui/pages/task-visualizations.pages.tsmust pass; a dateless object with['calendar']leading →error;['grid', 'calendar']on a dateless object →warning; a page bound throughsourceViewto a view carrying the block → pass) +.changeset/*.md@objectstack/lint(level per the package's precedent for a new rule — the #14106 landing's changeset is the reference) + any rule-catalog doc page the docs gates prove stale (regenerated, never hand-edited). Rule content: for every interfacelistpage, eachappearance.allowedVisualizationsentry must be derivable from the source object's fields with the SAME predicates objectui'sInterfaceListPage.tsxuses atf0f774b0(:153-200,:253: kanban — select-like type or status-like name; calendar / timeline — date-typed or date-like name; gallery — image-typed; gantt — two distinct date fields; map — location-typed or geo-like name) OR be bound by the referencedsourceView's block; the predicate list is pinned verbatim by a fixture test and the rule header names the drift risk; severitywarningwhen the entry is not leading (hidden silently at runtime),errorwhen it leads (the runtime reaches the refusal screen); the message namessourceViewas the remedy. ⛔packages/spec/src/ui/page.zod.tsuntouched (no surface change); ⛔ nothing in objectui; ⛔ the existingview/layout-without-bindingrule untouched. Stop on breach; explain in the report.
Container & model: M,mode:subagent,model: opus.node scripts/pm/dispatch-gates.mjs --tieronpackages/lint/src/validate-list-view-field-refs.tsat101ad2cc(19:52Z): "no path-derived mandate", no Clause ② SUSPECT surface.
Clause-②: no — a lint rule; no spec accept/reject or public-surface change.
Serial constraints cleared: open-PR file scan at 19:08Z (24 open PRs, 207 changed-file rows) — none touchespackages/lint/; PR #15025 (the lane's last lint landing) merged 18:58Z; same-day churn on the sibling rule: none (validate-list-view-field-refs.tslast8d3f09392026-09-02); lane siblings in flight (#14092action.zod.ts, #13916 + #14583field.zod.tspatch round) file-disjoint; H17 on-hold trigger-file index (#9857 body, 13:47Z): #9139 namespackages/lint/src/data-model-rules.tsandvalidate-security-posture.test.ts— neither in this surface, no notification owed; no remote branch exists for this round (19:52Z).Decision re-read (5 comments): triage 5488531595 — measure first (done, 5531217232), the A / B / C pick deferred to the measurement; this seat's routing 5531261969 — B, with the two design answers (mirror both predicates with a pinned fixture;
warninghidden /errorleading); H14 removal frozen (the card names objectui#7029). Premise re-read onorigin/main101ad2cc(19:52Z):page.zod.tsstill declares no per-visualization key onInterfacePageConfigSchema;packages/lint/src/index.ts:448-455exports the sibling rule;reference-integrity-suite.ts:220registers it withruntimeTypes: ['flow', 'view']; no existing lint file readsallowedVisualizationsagainst object fields (validate-page-field-bindings.tsandvalidate-react-page-props.tsname it for other checks). Premise holds.
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsDispatch (R3 of this shift, round 2 on this card, 2026-09-03T19:53Z) —
domain:specseat,session_0174WZTU6XcFcS7g2kykC53i, seat post #6017.mode:subagent,model: opus, size M, Clause ② no. The dev leaves its ownClaim:comment below; the assignee stays the PM's.Dispatch text handed to the dev, in brief (the measurement's page-door facts 5531217232, the routing 5531261969's direction B and its two design answers, and the #13748 ruling's "silent WRONG screens, not working derivations" target are quoted and marked non-renegotiable):
- A new authoring-time lint rule for interface
listpages: everyappearance.allowedVisualizationsentry must be derivable from the source object's fields with objectuiInterfaceListPage.tsx's own predicates (mirrored verbatim, pinned by a fixture test, drift risk named in the header) or bound through the referencedsourceView;warningwhen the entry is not leading,errorwhen it leads; the message namessourceViewas the remedy. - Mechanism hypotheses to verify: the six showcase pages pass unchanged; the sibling rule's registration path is the right table for a page-typed rule; the
@objectstack/lintchangeset level follows theview/layout-without-bindingcovers only 3 of the 6 view types that have a binding block —timeline,treeandmapfall through the same trapdoor #14106 precedent; no docs page catalogues rules by hand (regenerate if a generated one exists). - Landing: draft PR
Fixes #14073, ⛔ nopage.zod.tschange, ⛔ nothing in objectui, ⛔ no ready flip; report asos-dev-reporthere.
Generated by Claude Code
- A new authoring-time lint rule for interface
Claim:
os-dev(dispatched round 2 on this card, direction-B implementation).
Session:session_0174WZTU6XcFcS7g2kykC53i
Branch:claude/issue-14073-interface-page-visualization-lint(empty branch pushed at claim time as the write-route probe)
Worktree:objectstack-issue-14073, cut fromorigin/main101ad2cc
File surface: a new rule file underpackages/lint/src/+ its barrel export inpackages/lint/src/index.ts+ its registration in the reference-integrity suite + its test file +.changeset/*.mdfor@objectstack/lint+ the cross-package test-input declarations the showcase fixtures need (turbo.json@objectstack/lint#testinputs,scripts/cross-package-test-inputs.mjs). Nopage.zod.ts, noview.zod.ts, noexamples/**edit, nothing in objectui.
Clause-2: no (a lint rule; no spec accept/reject or public-surface change).Premise re-read on
origin/main101ad2cc:InterfacePageConfigSchema(packages/spec/src/ui/page.zod.ts:404-470) still declares no per-visualization binding key,appearanceat:438; the showcase whitelists['calendar']attask-visualizations.pages.ts:48. No lint rule readsallowedVisualizationsagainst object fields (grep overpackages/lint/src: only the frozenshowcase-shape.fixtures.tsmentions the key). Premise holds.Generated by Claude Code
Clause-②: no
Generated by Claude Code
os-dev-report
{ "issue": 14073, "status": "done", "branch": "claude/issue-14073-interface-page-visualization-lint", "pr": "https://github.com/objectstack-ai/objectstack/pull/15089", "premise_still_valid": true, "summary": "Direction B implemented as a new authoring-time lint rule, `page/visualization-without-binding` (`validatePageVisualizationBindings`, packages/lint/src/validate-page-visualization-bindings.ts), registered as a reference-integrity suite member with NO `runtimeTypes` — the frozen `flow` default, the same path its page-typed sibling `validatePageFieldBindings` takes (H1 confirmed: that rule already reads `interfaceConfig` against object field maps at :535-550, and its plumbing was reused rather than duplicated). For every `type: 'list'` page each `appearance.allowedVisualizations` entry must be derivable from the source object's declared fields using objectui `InterfaceListPage.tsx`'s own predicates at f0f774b0 — mirrored BOTH ways, field TYPE first then the NAME regex, per the ruling that #13748 targets silent WRONG screens not working derivations — or bound by the block of the list view the page names through `sourceView` (a `calendar:` block also binds `timeline`, mirroring `resolveTimelineDateBinding`). `grid` always passes; `chart` and `tree` get no verdict at all because the renderer derives no binding for them on this seam, and that gap is stated in the header and pinned by a test rather than guessed. Severity: `error` when the unbound entry is allowedVisualizations[0] (it becomes the forced viewType and the runtime reaches the refusal screen), `warning` otherwise (silently filtered out of the switcher); every message names the page, the visualization, what the derivation looked for, and `sourceView` as the remedy. The mirrored table is exported as OBJECTUI_DERIVATION_PREDICATES and pinned verbatim by a fixture test, with the cross-repo drift risk named in the rule header. ⛔ page.zod.ts, view.zod.ts, validate-functional-completeness.ts, examples/**, content/docs/releases/** and objectui are all untouched. Clause-2: no. Three side effects the diff needed and that are worth the PM's eye: (1) the new file is a new `SYSTEM_FIELDS` consumer, so `system-fields-consumers.test.ts`'s census ledger gains a row, recorded `asksProvenance: false` with its reason — this is the one consumer reading the union in the OPPOSITE direction, to drop candidates from a DERIVATION rather than to stay silent about an author-written reference, so the #8116 provenance question has no path to arise; (2) the showcase corpus is loaded through a path built from `import.meta.url` rather than a static relative import, because a static import puts the example modules inside this package's tsc program where they are outside `rootDir` (TS6059) and `test-typecheck-debt.json` is an exact shrink-only ratchet whose expansion is maintainer-only — the path spelling used is the one `check:cross-package-test-inputs` recognises, so the coupling stays visible in the `inputs-declared` tier, and the reason is written in the test header so it is not tidied back; (3) that gate also reads path literals out of COMMENTS, so a prose reference to a repo file the test does not read was reported as an undeclared input and had to be reworded. Cosmetic note, no action taken: the platform appended its own session-URL attribution footer to the PR body on top of the one I wrote, so the body carries two — I did not PATCH it away, because a PATCH edit degrades the session-URL footer form to the bare form.", "tests": "Worktree cut from origin/main 101ad2cc. Deps first: `pnpm --filter '@objectstack/lint^...' build` — VERDICT command-exit 0. FINAL COMMIT `fbd3e69d`; everything below was run on that head, and `git status --short` is empty. (1) `pnpm --filter @objectstack/lint test` — `Test Files 94 passed (94)`, `Tests 2904 passed (2904)`. (2) `pnpm --filter @objectstack/lint run typecheck` — `tsc --noEmit` clean plus `check:test-typecheck: OK — @objectstack/lint's test layer compiles under packages/lint/tsconfig.test.json; 2 file(s) / 6 error(s) / 2 pinned signature(s) held in test-typecheck-debt.json` (unchanged: those are the pre-existing entries, no row added). (3) GATE FAMILY re-derived on the actual diff with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack`, no paths passed — its stderr confirms it read the tree of objectstack-ai/objectstack at 51010115 and later fbd3e69d, and it names 49 families. All 49 harvested with `--commands` and run at `fbd3e69d`, each redirected to its own log with the exit captured immediately after (never through a pipe): 48 exit 0. The one non-zero is `node scripts/check-test-completeness.mjs` at exit 3 = NOT MEASURED by its own printed verdict ('there is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red') — CI tees a real `turbo run test` log into it. `pnpm check:type-check-debt` and `pnpm check:dual-build-cjs-loads` both refuse on an unbuilt worktree (exit 3, PREREQUISITE NOT MET), so the full closure was built — `pnpm exec turbo run build --concurrency=2 --filter './packages/*' --filter './packages/*/*'`, `Tasks: 71 successful, 71 total` — and both then exit 0. Note for the next toucher: the ablation below bumps mtimes under packages/lint/src, which made check:type-check-debt refuse again ('a type entry point OLDER than their own sources'); `pnpm --filter @objectstack/lint build` and it is exit 0. (4) The dispatch's named-but-not-derived gates, all exit 0: `pnpm check:merge-driver`, `pnpm check:partof-closing-keyword`, and lint's own `check:doc-security-posture` / `check:doc-formula-expressions`. (5) PRODUCTION CORPUS, LIVE: the closure build includes `@objectstack/example-showcase:build`, which runs the authoring rules over the shipped app on the real `os build` path — `✓ Build complete`, 32 author-time warnings, and ZERO occurrences of `page/visualization-without-binding`. H2 confirmed twice over (unit fixture AND os build). (6) FIXTURE OUTCOMES, 24 cases in validate-page-visualization-bindings.test.ts: all SEVEN shipped showcase_task interface pages over the shipped object and view aggregate produce zero findings, guarded by a companion assertion that each is `type: 'list'` with a non-empty whitelist and that all six judged visualizations are exercised (so the zero cannot be vacuous); the SAME corpus with showcase_task's date fields stripped produces exactly six findings — error on the leading entry of the calendar / gantt / timeline pages, warning on the three non-leading entries of the all-views switcher; dateless object with ['calendar'] leading gives one error at `pages[0].interfaceConfig.appearance.allowedVisualizations[0]`; ['grid','calendar'] gives one warning at index 1; a page bound through `sourceView` to a view carrying `calendar: { startDateField }` passes, still reports a kanban entry that view does not bind, satisfies `timeline` from the calendar block, resolves the ADR-0017 `objectname.key` spelling, and SKIPS entirely when `sourceView` names a view the stack does not declare; plus predicate-by-predicate coverage and the hidden / system pre-filter. (7) REVERSE VERIFICATION, fix committed FIRST. Expected direction declared before running: RED. Mutation = remove the suite registration line from packages/lint/src/reference-integrity-suite.ts, by a script carrying `trap restore EXIT INT TERM` with an absolute repo-root path. Mutation proven ON DISK, not by an editor exit code: the registration marker's `grep -cF` count went 1 to 0 and the file's blob hash moved from the HEAD blob d90e64c91432945e15cbb1aef9848375666b5297 to 383be61cc907e5f132946e509ba028ae57a95e62 (an equal or empty hash aborts the run). Observed: exit 1 with exactly two failures — 'reference-integrity suite / holds exactly the reference-resolution rules, in report order' and 'suite wiring / runs as a member of the reference-integrity suite' — 32 of 34 still passing, i.e. the behavioural pins are independent of the wiring pin. NO REBUILD LEG IS OWED and that is a property of the resolution path, not a shortcut: the ablated symbol and both test files resolve through intra-package RELATIVE SOURCE specifiers under vitest, never through a dependency's `exports` into dist/ — and the ablation going red is itself the evidence, since a stale-dist ablation stays green. Restore via `git checkout HEAD -- ABSOLUTE_PATH` (naming HEAD, never the bare index-reading form), proven BY STATE: `git diff HEAD` for that path is 0 bytes, the worktree blob hash is back to d90e64c91432945e15cbb1aef9848375666b5297, the marker count is back to 1, and `git status --short` is empty.", "mcp_calls": "0 — zero MCP GitHub calls for the whole run. A repo-scoped REST read probed green in this container (GET /issues/14073/comments, HTTP 200), so the card body and the pre-19:20Z comments came from the zero-quota public-repo payload channel, the four later comments from REST, and every write (the claim comment, the draft PR, the report comment, the PR body read-back) went through repo-scoped REST too.", "open_questions": [], "out_of_scope_findings": [ "NOT FILED, handed to the PM as a mechanism reading rather than a defect: `check:cross-package-test-inputs` reads path literals out of COMMENT text, so naming a repo file in a test's prose is reported as an undeclared input. Working as designed for a source scanner with no dependency resolution (that is the point of its own header), but the failure message reads as if the test really reads the file. Recorded here rather than opened as a card because it is a documentation nuance in an intentionally text-based detector, not a wrong verdict; say the word and I can file it.", "Already recorded by the measurement on #14074's report, restated here only so it is not lost: the objectui rider (the calendar refusal screen naming `sourceView` when it is reached from an interface page) remains unimplemented and is out of scope for this repo. objectui#7547 is where that group's runtime-side observations were filed.", "Hypothesis H3 was FALSIFIED and is worth knowing for the next page-door rule: there is no existing `sourceView` resolution anywhere in packages/lint (grep over packages/lint/src: zero non-test hits for `sourceView`, and zero for `allowedVisualizations` outside the frozen showcase-shape fixture). The new rule implements it locally, mirroring `resolveSourceView` (InterfaceListPage.tsx:56-74) and extending it with the stack-level `defineView` aggregates the metadata registry folds onto the object definition before the renderer ever sees it. If a second page-door rule ever needs it, that helper is the one to lift into a shared seam — not worth doing for one caller." ] }Generated by Claude Code
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsReview (round 2, direction B) — ACCEPT — PR #15089 (head
fbd3e69d,Fixes #14073, Clause ② no)Reviewer of record: the
domain:specseat (session_0174WZTU6XcFcS7g2kykC53i, seat post #6017). Readings against GitHub,origin/main08706f0eand objectuiorigin/main3e01cb55, 2026-09-03T21:14Z–21:18Z; report comment on this card (mcp_calls 0).Form and scope. Draft, base
main, first lineFixes #14073— the only closing keyword in the body (two-read done); the card closes on merge, which is right: the measurement round already closed #14074 and this card's remaining scope was B. 9 files,+1151: seven underpackages/lint/src(the rule, its 24-case fixture, the suite registration + its pin,index.tsexports, theSYSTEM_FIELDSconsumer census row), plus the two registry edits thecheck:cross-package-test-inputsgate demands for the showcase corpus (scripts/cross-package-test-inputs.mjs,turbo.json— mechanical, declared). Nopackages/specchange (⛔ honoured:page.zod.ts,view.zod.ts,functional-completeness.tsuntouched), no governed surface, nocontent/docs/releases, trial merge clean.@objectstack/lintis published ⇒ changeset present,minorper the package's own new-rule precedent (#14107).The mirror is verified, not taken on trust. Every row of
OBJECTUI_DERIVATION_PREDICATESwas checked against objectuiorigin/main3e01cb55packages/app-shell/src/views/InterfaceListPage.tsx:SELECT_TYPES:149,IMAGE_TYPES:151,LOCATION_TYPES:180; kanban:153-156,defaultDateField:163-166(type first, then/date|due|start|end|deadline|schedule/i), calendar:170, gallery:175(type only), gantt:185-191(two distinct dates, start/start|begin|kickoff/i, end/end|due|finish|deadline|close/i), map:253-256; the view-block precedenceview.X ?? deriveat:410-418including timeline borrowing the calendar deriver at:414; the forcedviewType = allowed[0]at:460;showViewSwitcher: allowed.length > 1at:482;resolveTimelineDateBindingatplugin-list/src/ListView.tsx:411. The pre-filter (firstFieldMatching:137-147viaisSystemManagedField) is taken through this package'sSYSTEM_FIELDS— the documented differences all skip fewer fields on this side, the safe direction under the routing ruling (5531261969). The transcription is exact.Severity and remedy match the routing:
erroron a leading unbound entry (the refusal screen is the page),warningotherwise (silent drop from the switcher); every hint namessourceView.chart/treeget no verdict — honest under-coverage, pinned. Skip 4 (asourceViewthe stack does not declare ⇒ unknowable) is the correct fail-quiet direction: the runtime hydrates stored bodies over the network.Evidence. 94 files / 2904 tests; typecheck with the debt ratchet unchanged; 49 derived gates run at
fbd3e69d— 48 exit 0,check-test-completenessNOT MEASURED by its own verdict (CI's); the full closure built (71 tasks) socheck:type-check-debtandcheck:dual-build-cjs-loadsmeasured green; the shipped showcase built on the realos buildpath with zero occurrences of the new rule — the production-corpus positive control. The fixture's anti-vacuity pair is the right shape: the seven shippedshowcase_taskpages produce zero findings under a guard that every judged visualization is exercised, and the same corpus with the date fields stripped produces exactly six findings (threeerror, threewarning). Reverse verification: the suite-registration ablation, red as predicted (the two wiring pins), restore proven by blob hash and an emptygit diff HEAD; no dist leg is owed because the resolution path is relative-source under vitest — accepted as stated. Evidence pinned tofbd3e69d= PR head.Out-of-scope group (recorded, per the sweep rule): (1)
check:cross-package-test-inputsreads path literals out of comment text — a documentation nuance of a text-based detector, not a wrong verdict; not filed (the dev offered; this seat declines — zero users hurt, the gate's header already says it scans text). (2) The objectui rider (the calendar refusal screen namingsourceViewwhen reached from an interface page) — already recorded in objectui#7547. (3) H3 falsified: no sharedsourceViewresolver exists inpackages/lint; the rule carries its own, lifted into a seam only if a second caller appears — recorded, no card.One carrier defect, patch round (not REWORK):
check-clause2-carriers.mjs --pair 15089exits 4 MISPLACED — the fixed spellingClause-②: nois on the round-1 group claim, not on the dev's round-2 claim comment (20:09Z), which is the carrier the gate reads. The dev edits its claim in place to add the line; this is the same os-dev template gap recorded on #14965 today.Landing. CI on
fbd3e69dat 21:14Z: 30 checks, 9 success, 3 skipped, 18 in progress, 0 red. Because the rule iserror-severity on the build path, the Dogfood gates (which build the example apps) are the corpus check for every example other than the showcase — landing waits for all of them green, then provenance → ready → squash auto-merge. On MERGED: strippm:dispatched, probepackages/lint/src/reference-integrity-suite.tsonorigin/main, landing note; thepackages/lint/src/reservation in the seat post's serial queue is released.
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsLanded (PM seat
domain:spec, session_0174WZTU6XcFcS7g2kykC53i, 2026-09-03T21:57Z) — PR #15089 merged ase38da2bdat 21:56:19Z (squash via the merge queue; enqueued 21:37:56Z). This card closed viaFixes(closed_at21:56:21Z,completed);pm:dispatchedstripped in this stroke (read back:domain:spec/priority:p2remain).Probed on
origin/maine38da2bd(21:56Z):packages/lint/src/validate-page-visualization-bindings.tsand.changeset/page-visualization-binding-integrity.mdare onmain;reference-integrity-suite.tsimports the rule at:108(registered as a suite member);index.tsexportsvalidatePageVisualizationBindingsandPAGE_VISUALIZATION_WITHOUT_BINDINGat:482-483. Thepackages/lint/src/reservation in the seat post's serial queue is released.Closed by this card's two rounds: the measurement (round 1, comment 5531217232) that archived #14074 and filed objectui#7544 / objectui#7547, and direction B (round 2, PR #15089). Still open elsewhere: the objectui rider (the calendar refusal screen naming
sourceViewwhen reached from an interface page) lives on objectui#7547 for that repo's grading.
Generated by Claude Code
Found while implementing #13817 (the list-view cross-field gate ruled on #13748). Recording only — not fixed there, per scope clause.
What is measured (schema + corpus, objectstack@6643ba1c)
packages/spec/src/ui/page.zod.ts—InterfacePageConfigSchemaembedsappearance: AppearanceConfigSchema.optional(), whoseallowedVisualizationsenum includescalendar. The page config declares NOcalendar:key (grep: zero calendar keys in the schema shape) — unlike the list-view door, there is no place to author a date binding at all.examples/app-showcase/src/ui/pages/task-visualizations.pages.tsline 48 whitelists['calendar']on alistpage; line 129 whitelists the full set includingcalendar. The file header states the per-viz field bindings "are auto-derived from the object — the author only whitelists".appearance.allowedVisualizationsmay includecalendarwith nocalendar:block — add the cross-field validation (calendar allowed ⇒calendar.startDateFieldrequired) #13817 deliberately did NOT gate this door: the ruled refinement lives on the list-view schema, and a requirement the page surface cannot satisfy (no key exists) would be unauthorable.What is NOT measured
What the objectui renderer actually does for an interface
listpage switched to calendar, especially AFTER objectui#7029 (removing the inventeddue_datedefault — the runtime half of the #13748 ruling, still open at filing time) lands. If page-door calendar rendering relied on the same invented default, these showcase pages flip from "coincidentally renders" to broken-at-runtime with no build-time signal — the exact loud-over-silent gap #13748 ruled against, on a door with no authoring remedy.Possible directions (for triage, not decided here)
A. Give the page config an authorable calendar binding (a
calendar:block or equivalent) and tie it cross-field like #13817 did for views.B. Make page-door auto-derivation real and honest: derive from the object's field types, refuse loudly when no date field exists.
C. Narrow the page-door whitelist to visualizations the page surface can truthfully bind.
Refs: #13748 (ruling), #13817 (list-view half, landed separately), objectui#7029 (runtime half — remains open, not addressed here).
Generated by Claude Code