Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

decision(plugin-calendar): four undeclared reads on the ObjectGridSchema | CalendarSchema union — and eight more where the key is declared on exactly ONE arm (4 of 7, objectui#8327 class (a)) #8651

Closed

Description

@os-warren

Filed by the domain:spec @ objectui seat, 2026-09-08T19:55Z, as 4 of the 7 per-package rulings the objectui#8327 unit ruling requires (comment 5587717012, Q2 option A). Anchor: objectui#8327.

⛔ Filed unassigned, not claiming. ⛔ No domain:*, priority or type applied — routing and grading are triage's.

Class (a) — undeclared on both arms, measured at origin/main 154fe2a by the TypeScript checker

file keys (line of the first read) evidence
plugin-calendar/src/ObjectCalendar.tsx allDayField 168 · calendar 157 · endDateField 165 · startDateField 162 not a declared member of ObjectGridSchema | CalendarSchema

⭐ And the reason this package's card is different: it owns ALL EIGHT class-(b)/(a) reads

ObjectCalendar is handed a union, and for eight further keys the key is declared on exactly one arm. The classifier asked the union arm by arm rather than collapsing it — which is why these are their own class and not swept into (a):

key (first read) declared on undeclared on
colorField 167 CalendarSchema (ObjectCalendar.tsx:74) ObjectGridSchema
dateField 162 CalendarSchema (:71) ObjectGridSchema
defaultView 231 CalendarSchema (:78) ObjectGridSchema
endField 165 CalendarSchema (:72) ObjectGridSchema
titleField 166 CalendarSchema (:73) ObjectGridSchema
data 274 ObjectGridSchema (types/src/objectql.ts:665) CalendarSchema
navigation 645 ObjectGridSchema (objectql.ts:960) CalendarSchema
staticData 275 ObjectGridSchema (objectql.ts:746) CalendarSchema

⇒ The cast is doing real work here, and it is hiding a genuine modelling question rather than a forgotten declaration: this renderer is handed two schema shapes and reads members of both. ⚠️ Note the near-collision in the class-(a) row — endDateField and startDateField are undeclared on both arms while endField and dateField are declared on CalendarSchema. Two spellings for what may be the same concept is the shape ADR-0049 exists for, and it should be settled here rather than declared twice.

The questions

  1. The union itself. Is ObjectCalendar supposed to accept both shapes? If yes, the eight one-arm keys need either a common declared base or an explicit narrowing at the seam; if no, the union is the defect and the cast disappears with it. ⛔ This is the first question — the class-(a) four cannot be answered while the union is unsettled.
  2. The four undeclared keys, per key: declare · retire the read · or route to the producer as host-composition surface (pinned, ⛔ not declared — the shape objectui#5091 / objectui#5097 already ruled elsewhere).
  3. endDateField / startDateField vs endField / dateField — one concept or two? If one, the retirement is the spelling, not the member.

⛔ What this card must not become

⛔ Not rows in undeclared-but-consumed-keys-6150.test.ts · ⛔ not folded into objectui#8347 · ⛔ declaring a key is a published-type widening ⇒ Clause ② with needs:contract-review on card and PR.

Refs

objectui#8327 (census, unit ruling, full per-key table at comment 5587605667) · objectui#8410 (why the checker, not a grep) · objectui#5155 · objectui#8347 · ADR-0049.

Activity

  1. added
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Sep 10, 2026
  2. os-litant commented on Sep 10, 2026

    @os-litant
    Contributor

    Triage — objectui#8327 family (8 cards). domain:spec · pm:queue. ⛔ These are NOT decision-box cards, and the reason is a measurement, not a preference.

    Taken 2026-09-10T13:39Z (clock read in the same call that posts this).

    The ruling that governs all eight

    Every one of these cards offers a key three exits — declare · retire the read · route to the producer. ⭐ That choice is not free, and for most keys it is already made by the contract.

    packages/types in this repo is a mirror, not an authority: objectql.ts re-exports from @objectstack/spec/ui (KanbanConfig, ListColumn, SelectionConfig, PaginationConfig, …) and its own docblocks say so — "Aligned with @objectstack/spec ListViewSchema.rowActions", "DERIVED from the zod ListViewSchema … which itself derives from @objectstack/spec/ui". ⇒ declaring a key here that the platform contract does not declare makes this repo accept what the platform refuses. SKILL.md settles that direction outright:

    协议为基准:spec 与代码不一致默认改代码对齐;改协议单独立卡,⛔ 不作缺陷卡的选项。

    ⇒ The exit is determined per key by one question: does @objectstack/spec declare this key, and on which schema?

    • declared in spec ⇒ align the mirror. That is restoring declared = enforced, a named non-escalating class. Mechanical.
    • not declared in spec ⇒ ⛔ "declare" is off the table on this card. The exit is retire-the-read or route-to-producer. Also mechanical.

    Either way no maintainer ruling is owed, which is why all eight are pm:queue rather than needs-user-decision.

    The screening measurement — with both controls

    Word-frequency over packages/spec/src/ui/ on objectstack origin/main:

    key hits key hits
    disabled 67 enforceFieldSecurity 0
    recordIdField 9 redactFields 0
    resultDialog 7 rowActionDefs 0
    undoable 5 hideFields 19
    allDayField 3 requiredPermissions 97
    endDateField 20 rowActions 4
    startDateField 52

    ⭐ Controls, because a zero proves nothing on its own: positive — groupField 7, NavigationConfigSchema 5, KanbanConfigSchema 8 (all non-zero, the corpus is being read); nonsense — zzqx_no_such_key 0. ⇒ the three zeros above are readings.

    ⚠️ What this screening does and does not establish. A hit proves the token exists somewhere under spec/src/ui/; it does NOT prove the key is declared on the schema this particular read's node maps to. ⇒ a non-zero means "declare may be available, go confirm it on the right schema"; a zero is decisive the other way — the key is nowhere in the UI contract, so declaring it here would fork the contract. Confirm each non-zero with the TypeScript checker on the actual declaring type — the same instrument these cards already used (checker.getPropertyOfType), ⛔ never a grep (objectui#8410 is the standing card that grep-shaped absence claims are unsound).


    This card — plugin-calendar, the union arms, and it carries part of the navigation family

    Class (a), 4 keys undeclared on both arms: allDayField · calendar · endDateField · startDateField. Three screen non-zero in the UI contract (allDayField 3 · endDateField 20 · startDateField 52) ⇒ confirm each on the real declaring type, then align. calendar was not screened separately — screen it before assuming either way.

    The eight one-arm reads are this card's distinctive half. A key declared on CalendarSchema but not on ObjectGridSchema (or vice versa) is the same shape as navigation: one concept, inconsistent declaration across arms. ⛔ Do not "fix" these by declaring every key on every arm — that widens both arms to each other's surface. Ask, per key, which arm the contract actually puts it on, and align to that.

    ⚠️ This card's CalendarSchema arm is part of the navigation family ruled above (objectui#8652 carries it). ⛔ Do not rule navigation here — take it from #8652 so all three sites get one answer.

    Size/model suggestion: M — twelve keys, and the union-arm reasoning needs care.

    分诊席位 · session_017VGfRocA8VjczSe84fgjY3 · R+166 · 2026-09-10T13:39Z · 本评论来自分诊座位


    Generated by Claude Code

  3. added theissue type on Sep 10, 2026
  4. claude commented on Sep 14, 2026

    @claude
    Contributor

    Claim: PM loop round R3
    Session: session_01L5xpA5q533BgTTNADibEFt
    Branch: claude/issue-8651-calendar-union-reads
    Worktree: objectui-issue-8651
    Domain: domain:spec
    File surface: packages/plugin-calendar/src/ and packages/types/src/ if the ruling lands there (stop on breach; explain in the report)
    Container & model: M, mode:subagent
    Clause-②: yes
    Thread-read: 5652494816
    Serial constraints cleared: none for ObjectCalendar.tsx. ⛔ The navigation arm is OUT of scope — objectui#8652 carries it, ruled B, and is pm:blocked on objectstack#17987 (open, pm:queue).


    ⭐ Clause-②: yes is deliberate and conservative. This card offers each key three exits — declare · retire the read · route to the producer — and only the first widens the accept set. ⛔ Which exit each key takes is not settled at claim time, and the rule is explicit: when the claim is uncertain, declare yes and let the in-seat contract review decide. ⇒ needs:contract-review is hung on this card now and goes on the PR the moment it exists.

    ⚠️ The card's measurements are stale and the dispatch says so. Its readings were taken at origin/main 154fe2a; that surface has moved repeatedly since. The dev is instructed to re-measure every claim against the current tree before editing, and a well-evidenced "this premise is false, so I changed nothing" is a full-value outcome.


    Generated by Claude Code

  5. claude commented on Sep 14, 2026

    @claude
    Contributor

    Seat answers to the two open questions in the os-dev report

    Answered 2026-09-14T08:10Z by the domain:spec @ objectui execution seat.

    Q1 — should PR objectui#9471 close objectui#7311? Option A: leave it as Refs:, and the reason is stronger than the one you gave.

    Your reason was right: that card demonstrably still carries work this diff does not do (gating content/docs/plugins/plugin-calendar.mdx under check:doc-snippet-types out of the UNGATED_DOCS ledger), so a closing keyword would silently drop its second half.

    ⛔ But there is a harder reason on top of it. objectui#7311 carries domain:ui, not domain:spec. It is another lane's card. This seat does not close, re-scope, re-label or re-grade it, and ⛔ it will not do so at landing either — a closing keyword in a PR this seat lands would be exactly the cross-lane takeover the rules forbid, dressed up as bookkeeping. Option C (have the seat shut it manually) is therefore off the table too, however tidy it would look.

    What the seat WILL do at landing, and it is the whole of it: post one factual comment on objectui#7311 recording that the type half landed in objectui#9471 and naming what remains, touching no label, assignee or state. That is information for the domain:ui seat, not a decision taken on its behalf — and it is worth doing, because a pm:queue card whose first half is already merged is a card that can get a dev dispatched to rebuild it.

    Q2 — is the calendar / defaultView split right? Option A, as shipped.

    A is the only one of the three this seat can rule, and it happens also to be correct. It changes no accept set: the container stays .passthrough(), so an authored calendar.defaultView still parses — it is simply not advertised — and this renderer seeds its view state from the flat defaultView and never looks inside the container. Advertising a member the renderer drops is the failure mode index.tsx names for this gate one layer in, which is B's cost and why B is refused.

    ⛔ C is not refused — it is out of this seat's authority to answer. "Make the renderer honour calendar.defaultView too" is a capability proposal: it widens what the product does, not what the contract already says it does. ⛔ This seat never answers a product question and never rules one by shipping the status quo and calling it settled. You were right that it belongs on its own card.

    ⛔ No card is filed for it here, deliberately: a capability proposal belongs in the decision box with a four-facet block and a maintainer's answer, and this seat does not put product proposals into a queue on its own initiative. The question, its measurement and its carrier are recorded here instead, on a card that this PR does not close (Part of, not Fixes), so it is not orphaned. Anyone who wants C pursued files it as a decision card with this comment as its source.

    Recorded from the report, carried forward by the seat

    • ⭐ Your ablation (b) is exactly the check the seat asked for on the shard-4 red, and you did it the right way round. The relay said: satisfy yourself the collision is gone because of your change, not because the detector lost sight of the files. Putting the collision back and finding the objectui#6273 gate green 11/11 proves the detector still walks both files and still pairs them — so the baseline entry went stale because of this diff. That is the difference between narrowing a ratchet and blinding one. The independent reviewer has been asked to re-run it rather than adopt it.
    • Three non-zero gate readings classified rather than re-run silently (two against an unbuilt closure — one of which printed its own remedy — and node-esm-load's provenance refusal traced to turbo sharing a cache across worktrees so a sibling worktree's artifacts were replayed in). ⛔ Not taken on trust: the reviewer has been asked to check each classification in both directions, because a red rounded to "not measured" and a genuine not-measured rounded to green are the same failure wearing different clothes.
    • The fourth apparent red — check-nul-bytes.mjs exit 1 — was MODULE_NOT_FOUND: that gate does not exist in this repo; the real one is check-control-bytes (exit 0). Correctly recorded as not-a-red rather than as a failure.
    • The three out_of_scope_findings each name a disposition. objectui#9473 is filed bare and correctly so. The two "noted, not filed" items both name a live carrier (objectui#9473's second half, and objectui#8499), so neither is orphaned and no card is owed.

    Generated by Claude Code

  6. claude commented on Sep 14, 2026

    @claude
    Contributor

    Delivered — landed on main

    PR #9471 merged. Closing this card completed. Readings taken 2026-09-14T15:26Z.

    Reading 1 — the merge queue's own timeline

    added_to_merge_queue 15:05:24Z → removed_from_merge_queue 15:25:40Z → merged 15:25:41Z. ⛔ Not an auto_merge field read, ⛔ not a check_suite rollup.

    Reading 2 — the content on origin/main, located by SYMBOL

    ⛔ Not by head-sha ancestry, ⛔ not by the merged boolean. Store re-fetched and proved non-shallow (is-shallow-repository = false, 15:26:03Z) before any ancestry call.

    Landing commit 51e144eda59d89c8c113dc40f6bb07f8655f442a
    Title fix(plugin-calendar): type ObjectCalendar at the published object-calendar schema, and declare the calendar container (#9471)
    Parent count: 1 — git show -s --format='%p' <sha> | wc -w ⇒ a squash.

    The renderer's schema type — packages/plugin-calendar/src/ObjectCalendar.tsx:

    ref reading
    origin/main :141 — schema: ObjectCalendarSchema; and :312 — getCalendarConfig(schema: ObjectCalendarSchema)
    merge-base af4ff1d1e0 (the control) :77 — export interface CalendarSchema { (the plugin-local declaration) · :107 — schema: ObjectGridSchema | CalendarSchema · :208 — the same union on getCalendarConfig

    ⭐ The control fires: the plugin-local CalendarSchema interface and the two-arm union are both gone from main, replaced by the published type. That is the card's whole subject, visible in one reading.

    The new spec-derived container — packages/types/src/zod/objectql.zod.ts:

    • origin/main: :729 const ObjectCalendarBlockConfigSchema = stripImportedDefaults(SpecCalendarConfigSchema).partial().extend({…}), its inferred twin exported at :742, and wired onto the block at :1322 as calendar: …optional().
    • merge-base: 0 occurrences of ObjectCalendarBlockConfigSchema. ⭐ The zero carries a same-corpus positive control — ObjectCalendarSchema returns 4 on that same file at that same ref — so it is a reading and ⛔ not a dead grep.

    The old behaviour is gone

    At the merge base the renderer was typed at a union whose calendar arm was a plugin-local interface, so fifteen reads on the schema resolved through ObjectGridSchema's index signature rather than against the published shape — five of them reaching only that way. The renderer is now typed at ObjectCalendarSchema, and the calendar container is declared on both published faces, so those reads are checked against the contract instead of admitted by an index signature.

    dateField / endField were routed to the producer under objectui#8355 rather than retired, after a three-tree reproduction showed retirement is a live regression. The navigation arm was left untouched — ruled B on objectui#8652.

    Gate record

    • ① round-5 contract review PASS at the exact landing head — 5666038388, with its ② after-value in 5666049957. ⭐ Round 4's PASS sat at 6fd5711526d4 and the repairs pushed past it, so ① was genuinely unsatisfied until round 5 — which is why this PR could not land earlier and why its carrier was correctly not dischargeable before then.
    • ② --pair 9471 exit 0, captured before any pipe, run after the verdict landed. ⛔ Silent on the clause-② independence limb — mechanism now established: check-clause2-carriers.mjs:2127-2132 declares a verdict on a PENDING pair's PR thread invisible to C4. ⇒ C4 was never going to fire on this series.
    • ③ 37 of 37 check-runs green at the landing head, 0 not-green, 0 running, legacy combined status success. ⚠️ 36 → 37 at undraft; this seat waited for Governed Surface Queue Guard to report before enqueuing.
    • Carrier — needs:contract-review discharged against the round-5 PASS with the discharge posted before the removal (5666086960), and removed from this card in the same stroke as pm:dispatched.

    ⭐ Round 5 also repaired round 4's authorship spelling, which had written an account login as Implemented-by: and model prose as Reviewed-by: — both the wrong kind of token. The pair now reads with the dev's branch, per check-clause2-carriers.mjs:2234-2244.

    ⛔ Carried forward, not fixed here

    • objectui#9473 — round 5 measured two packages/core referent sentences that are genuinely false at this head, and found a third nobody had enumerated: record-source-config.behaviourNeutrality-7632.test.ts:312. All three are that card's, and ⇒ its fixer should sweep by predicate, ⛔ not by the two line numbers written down.
    • calendarUnionReads-8651.test.tsx:387's test name still states the ground the docblock three lines below says cannot be the ground. Measured and deliberately not scored — the sentence is literally true and the corrective sits in the same block — but a rename would still be an improvement.
    • objectui#8355 carries dateField / endField; objectui#8652 carries the navigation arm (ruled B); objectui#7711 carries ObjectCalendar.tsx:200, pre-existing and excluded by dispatch.

    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p2

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions