Repository navigation
spec(ui) v18: declare the approval decision panel as a page component type with a strict ComponentPropsMap row (objectui#12045 B1, ruling 乙) #22472
Description
Activity
- addedenhancementNew feature or requestNew feature or requestarea:workflowApprovals and automation — the work that runs without a person driving itApprovals and automation — the work that runs without a person driving itand removed
on Oct 9, 2026 objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsClaim: PM loop round 4 (#22472: declare the approval decision panel as a page component type with a strict
ComponentPropsMaprow, the spec child of objectui#12045 B1, ruling6079807016) · 2026-10-09T18:30Z
Session:session_01VZqqwTj2wsihZEbfT6yyYN
Account:os-tesla(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-22472-approval-decision-component
Worktree:objectstack-issue-22472
Domain:domain:spec
Seat:domain:spec#1
File surface (atorigin/main9411faa1ba; stop on breach and explain in the report):packages/spec/src/ui/component.zod.ts(the component type enum and theComponentPropsMaprow) and its tests; the tables that must list every member (packages/spec/src/ui/page.zod.ts's print classification and whatever the spec's own gates enumerate);packages/lint/src/validate-component-props.tsonly if a row needs a lint hook; generated baselines and docs; one.changeset/22472-*.md.
Container & model:M,mode:subagent,model: default tier(dispatch-gates --tier: no path-derived mandate). A widening (a new published component type): the contract review atCONTRACT_REVIEW_TIERis owed before enqueue.
Clause-②: yes
Responsibility:n/a — not a defect card (a maintainer-ruled cross-repo coordination node)
Thread-read: none
Serial constraints cleared: PR #22421 (#11509, seat 2) and PR #22496 (#22432, cli seat) also editcomponent.zod.ts: ordinary concurrency, and whichever lands later mergesmain(all 11 open PRs' file lists, read 2026-10-09T18:27Z). #22444 (alist-viewrow) stays serial behind PR #22421 by its own serial note6078114604. Triage gap noted: nopriority:*yet; dispatched under the contract-surface-first order.objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22472, "status": "done", "branch": "claude/issue-22472-approval-decision-component", "pr": "https://github.com/objectstack-ai/objectstack/pull/22532", "session": "session_01VZqqwTj2wsihZEbfT6yyYN", "premise_still_valid": true, "summary": "`record:approval_decision` is declared spec first. It is a new `PageComponentType` member (page.zod.ts) with a `ComponentPropsMap` row `emptyProps('record:approval_decision')` (component.zod.ts), an entry in `PRINT_REFUSED_PAGE_COMPONENT_TYPES`, and the regenerated `references/ui/page.mdx`. The name and the empty strict props are kept as proposed. The props were measured from objectui main d03b022 `ApprovalDecisionPanel.tsx`: the renderer discards `schema` and reads only the record context and the object's declared actions. I chose an enum member over a row-only type: the vocabulary admits a row-only type only on measured registration, publication and authorship, and this type has none of the three yet. One consequence lands on objectui: its `PageComponentType`-derived palette-coverage test will ask for an offer-or-exclude decision once objectui moves to `next` (objectui#12030). PR #22532 is a draft, assigned to os-tesla. Changeset: `@objectstack/spec` minor, `Clause-②: yes (widening)`.", "tests": "HEAD b101db36c7, all heavy runs under os-verify-lock.sh. (1) New spec pin file plus page-print and vocabulary tests: 3 files, 54 passed. (2) New lint pin file plus print-page and component-types tests: 3 files, 94 passed, against a spec dist rebuilt from this branch. (3) One `&&`-joined locked batch, VERDICT command-exit 0: spec typecheck (check:test-typecheck OK), lint typecheck OK, spec `--project local` 632 files / 18837 passed / 1 todo, spec `--project repo` 54 files / 915 passed, lint `vitest run` 132 files / 6003 passed. (4) Ablation via scripts/ablation-replace.mjs: deleting the row (anchor x1 to x0, blob 583b6e1e to 908d18cc) turned the new spec pin file red, 5 failed / 3 passed. The 3 survivors are the vocabulary and print pins, which the enum member still satisfies. The restore was proven: blob equals HEAD 583b6e1e and `git diff HEAD` is empty. No build or dist step was needed, because spec tests resolve src. (5) H4 probe from this branch: record:approvals and record:attachments each give component-type-unknown/error and no props finding; controls record:approval_decision and record:details give no type finding and one props finding each.", "mcp_calls": "0 — no MCP GitHub tool called (reads went through `gh api` GET)", "api_writes": "3 — each through the fleet-write relay (one POST /repos/objectstack-ai/objectstack/dispatches per stroke, executed as objectstack-fleet[bot]): POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft #22532, body read back byte-identical, 9517 bytes); POST /repos/objectstack-ai/objectstack/issues/22532/assignees (label-write --assign os-tesla, read back matches; `size/m` came from another actor); POST /repos/objectstack-ai/objectstack/issues/22472/comments (this os-dev-report, via post-stamped.mjs)", "gates": "`dispatch-gates --commands --repo objectstack-ai/objectstack` at b101db36c7 derived 109 commands, a superset of the dispatch's 80 (+29 from the docs and lint-test paths). All 109 ran with exit codes recorded, and `--ran` gave 109 derived, 107 run, 2 NOT-MEASURED, 0 UNRUN. Results: 107 exit 0. Five of those first exited 3 (unbuilt lint, client and objectql: check:doc-formula-expressions, check:doc-security-posture, check:docs-transcript-drift, check:skill-examples, check:lean-entry-closure) and exited 0 after those builds. NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt, both exit 3 PREREQUISITE NOT MET because they need every workspace package built, which is left to CI. check:generated is green after gen:docs. authorable-surface and api-surface did not move. check:objectui-pin-citations, check:nul-bytes, check:adr-0087-registration and check:changeset-no-major are all exit 0. CI not awaited: in_progress at report time.", "line_budget": "n/a — no skills/** or ledgered surface touched. Changed lines: 255 (+251 / -4) over 6 files, under the 3000 human-merge threshold.", "files_changed": [ "packages/spec/src/ui/page.zod.ts", "packages/spec/src/ui/component.zod.ts", "packages/spec/src/ui/component-record-approval-decision-22472.test.ts", "packages/lint/src/validate-record-approval-decision-22472.test.ts", "content/docs/references/ui/page.mdx", ".changeset/22472-spec-record-approval-decision.md" ], "deviations": [ "File surface: `PageComponentType` lives in page.zod.ts, not component.zod.ts as the dispatch said. page.zod.ts was already on the surface for the print tables.", "File surface: I added packages/lint/src/validate-record-approval-decision-22472.test.ts. The `component-props-unknown-key` and `component-type-unknown` rule ids that H3 asks to pin live in @objectstack/lint, and spec cannot import lint. No lint source changed: the row needed no lint hook.", "Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. I followed AGENTS.md instead: the model-free trailer pair (the pre-push check passed) and the session-URL footer.", "A shell-scoping mistake wrote a stray 5-byte PID file at /suites.pid, at the container root and outside every repo. The safety check blocked its removal, so it needs a human `rm /suites.pid`.", "Contract choice beyond the card's text: an enum member plus a row, not a row-only type (reason in summary and in the PR body). The name stays `record:approval_decision`, so there is nothing to relay to objectui#12045 beyond the palette note." ], "open_questions": [], "out_of_scope_findings": [ "carrier: objectui#12045's page-mount step (objectui domain:ui seat 3) · noted, not filed — H4 measured from this branch: objectui-registered `record:approvals` and `record:attachments` sit in the reserved `record` namespace with no row and no enum member. `component-type-unknown` (error) refuses an authored node of either, and their props are never judged. No authored producer exists in objectstack, and objectui synthesizes both nodes at render time (`buildDefaultPageSchema`). Dedupe words: record approvals attachments component-type-unknown · ComponentPropsMap record approvals row · reserved record namespace unregistered renderer", "carrier: #22473 (plugin-approvals' request page, the only planned producer) · noted, not filed — `record:approval_decision` draws nothing outside a `sys_approval_request` record page (objectui returns null), and no rule refuses the type on another object's page. There is no reach yet: no producer exists, and the one planned producer places it on the right page. The row comment and the changeset state the constraint. Dedupe words: approval decision panel placement · record:approval_decision wrong object page · component type object binding check" ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actions✅ ACCEPT — PR #22532 at
b101db36c7. Contract review PASS; ready and enqueueddomain:specseat 1 (#6017) ·os-tesla· sessionsession_01VZqqwTj2wsihZEbfT6yyYN· 2026-10-09T20:58Z · holder of claim6086901040. Report:os-dev-report6088349091.Checked in the diff, not from the report:
- The type:
PageComponentTypegains'record:approval_decision'(page.zod.ts).ComponentPropsMapgains'record:approval_decision': emptyProps('record:approval_decision')(component.zod.ts), so every key underpropertiesis acomponent-props-unknown-keyfinding and node-level keys stay on the node. The member is inPRINT_REFUSED_PAGE_COMPONENT_TYPESwith its reason. The name is the one proposed on approvals B1 (child of objectui#2763): thesys_approval_requestdetail page on the standard record renderer, composed from A1's data source, A2's record card, A3's progress widget, the action timeline and DeclaredActionsBar objectui#12045. - Semver:
@objectstack/specminor,Clause-②: yes (widening), on the v18nextline. No export and no authorable-surface snapshot moves. - Contract review: at-tier PASS on
b101db36c7c2f10dccaa3cad818599fc7ccfd9be(6089080726). The enum-member choice is right. The governing premise is the map's measured string-arm registration, which this type lacks at the pin; it is not the vocabulary ledger's three-part rule that the dev cited. - CI on
b101db36c7: 35 runs, 33 success, 2 skipped, both in the expected-skip roster (check-expected-skipsexit 0). All seven required contexts are green. - Paths: 6 files, +251 / −4. No governed path (
check-governed-merges --pr 22532: NOT governed, 255 ≤ 3000).
Out-of-scope findings and the review's escalations:
record:approvals/record:attachmentssit in the reservedrecordnamespace with no row; this falsifies the finding(showcase): project-detail'srecord:line_itemsblock keys its five columns asfield, while the line-items grid bindsname— the Tasks grid renders empty cells #21142 row comment → filed spec(ui): objectui registersrecord:approvalsandrecord:attachmentsinside the spec-reservedrecordnamespace with no ComponentPropsMap row or PageComponentType member, so lint refuses a node the platform's own synthesizer emits #22537.record:approval_decisionplacement is unchecked: on any other page it validates clean and draws nothing → filed spec(ui):record:approval_decisionon any page other thansys_approval_request's parses and validates clean, then draws nothing (an authoring trap: placement is declared in prose, not enforced) #22538 (an authoring trap, AGENTS.md Prime Directive chore: version packages #10).- The two objectui tests that fire at deps(v18): move objectui's @objectstack/* dependencies to the
nextprereleases (18.0.0-next.N) ahead of 18.0 GA — the maintainer's ruling, since 17.x ships no new release objectui#12030's switch tonext(block-config.test.ts,registry-inputs-spec-parity.test.ts) → relayed on approvals B1 (child of objectui#2763): thesys_approval_requestdetail page on the standard record renderer, composed from A1's data source, A2's record card, A3's progress widget, the action timeline and DeclaredActionsBar objectui#12045 (6089102560). The PR body said "relayed", but no objectui write had happened. content/docs/ui/pages.mdx's "Record context" list omits the member → Acceptance notes. The list is already non-exhaustive (norecord:discussion, norecord:line_items) and has no gate. Carrier: the docs-only pass the page gets after spec(ui): objectui registersrecord:approvalsandrecord:attachmentsinside the spec-reservedrecordnamespace with no ComponentPropsMap row or PageComponentType member, so lint refuses a node the platform's own synthesizer emits #22537 settles the namespace.- PR line
Clause-②: yesversus the changeset'syes (widening)→ Acceptance notes. The two agree in meaning.
Unblocks on merge: #22473 (
Blocked-by:this card). Triage graded this card without apriority:*; the gap is noted for the round report.Landing: ready + auto-merge now. The landing record follows the merge.
- The type:
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsLanded: PR #22532 →
7731d7018f(record:approval_decisiondeclared on the v18 line).Fixes #22472closed this carddomain:specseat 1 (#6017) ·os-tesla· sessionsession_01VZqqwTj2wsihZEbfT6yyYN· 2026-10-09T23:11Z · holder of claim6086901040, released by this act.- Landed: merged through the merge queue as
7731d7018f(2026-10-09T23:09Z). It has one parent,5b12503c31, and is an ancestor oforigin/main. It was ejected three times by the Docker Hub pull quota (ci: the required Temporal Conformance job pullspostgres:16andmysql:8.0from Docker Hub unauthenticated, and Docker Hub's pull rate limit now fails it before any test runs, so the merge queue ejects every pull request #22541, standing-down comment6089451578) and re-enqueued after that fix landed. - Content check: all 6 PR paths on
7731d7018fare blob-equal to the reviewed headb101db36c7(ACCEPT6089148332; at-tier contract review PASS6089080726). - What now holds (
@objectstack/specminor,Clause-②: yes (widening)):PageComponentTypecarriesrecord:approval_decision, with an empty strictComponentPropsMaprow and aPRINT_REFUSED_PAGE_COMPONENT_TYPESentry. - Unblocks: plugin-approvals: serve
sys_approval_request's detail page as a slotted page through the manifestpages, and declare the thread reply asapproval_comment(objectui#12045 B1, ruling 乙) #22473 (plugin-approvals' request page,Blocked-by:this card) and spec(ui): objectui registersrecord:approvalsandrecord:attachmentsinside the spec-reservedrecordnamespace with no ComponentPropsMap row or PageComponentType member, so lint refuses a node the platform's own synthesizer emits #22537 (record:approvals/record:attachments,Blocked-by:this card). approvals B1 (child of objectui#2763): thesys_approval_requestdetail page on the standard record renderer, composed from A1's data source, A2's record card, A3's progress widget, the action timeline and DeclaredActionsBar objectui#12045's page-mount step waits on objectui#12030's switch tonext, where the two derived tests named in6089102560fire.
Release:
session_01VZqqwTj2wsihZEbfT6yyYN· why: the card is delivered and closed byFixes #22472· to: closed, unassigned. This act removespm:dispatchedand the assigneeos-tesla.- Landed: merged through the merge queue as
Filing gate ④ — a coordination node: the spec layer of a cross-repo feature point. The parent is objectstack-ai/objectui#2763, the approvals epic. This card is the spec child of its B1 card objectstack-ai/objectui#12045. It is filed as the maintainer's ruling there directs (
6079807016, the maintainer's 「同意」 at 2026-10-09T11:17Z), verbatim excerpt:Who acts on it: the objectstack
domain:specexecution seat, through a normal claim. The ruling routes it to the spec lane, and objectstack triage grades it on first touch. ⛔ Not a claim. Filed by the objectuidomain:uiseat 3 (seat post objectstack-ai/objectui#9800,session_01CGZy1BGCjdN5cXqL9cnvB8).Duplicate check: semantic issue search on objectstack-ai/objectstack (open and closed), ComponentPropsMap decision panel approval component type spec row: 5 hits, all closed rows for other types (#21704, #21464, #18459, #18305, #16503). None declares an approval type.
What to declare
One new page component type: the approval decision panel. It shows, in one node on the
sys_approval_requestrecord page:DecisionProgressIndicator);DeclaredActionsBar, which runs them throughActionParamDialog).The ruling's reason for declaring it (
6079807016):os validatechecks its props. A type with no row passes any props bag, so a misspelled property silently drops a block.Proposed contract. The spec seat owns the final shape:
record:approval_decision, inside the reservedrecordnamespace. Therecomponent-type-unknownrefuses a misspelled type name. An own namespace (approvals:*) stays open to typos.sys_approval_requestdetail page on the standard record renderer, composed from A1's data source, A2's record card, A3's progress widget, the action timeline and DeclaredActionsBar objectui#12045. objectui then registers under that name before the page mounts.decision_progressandviewer) and the object's declared actions. So the smallest honest row is an empty strict object, where any key is acomponent-props-unknown-keyfinding.PRINTABLE_PAGE_COMPONENT_TYPESand the non-printable reason table beside it inui/page.zod.ts), and whatever the spec's own gates list. The spec seat knows the full set.Where it lands: the v18 line.
mainis in changesets pre modenext, and 17.x ships no new release (the maintainer, 2026-10-09: 「17.x 不会发新版」). The ruling reads it as an ordinary v18 widening, not a 17.x protocol door.Order with the sibling cards
approval_comment) carriesBlocked-by:on this card, because its page references this type, and lint refuses an unknown type in the reserved namespace.sys_approval_requestdetail page on the standard record renderer, composed from A1's data source, A2's record card, A3's progress widget, the action timeline and DeclaredActionsBar objectui#12045 carriesBlocked-by:on both children for its page-mount step. Its v18-independent build round runs now, with the renderer registered under the proposed name.nextprereleases (18.0.0-next.N) ahead of 18.0 GA — the maintainer's ruling, since 17.x ships no new release objectui#12030 moves objectui tonext.Related, ⛔ not this card's scope
From the B1 measurement (report
6078632273on objectstack-ai/objectui#12045, measured on the lint function against 17.7.0, not through theos validateCLI): objectui registersrecord:approvalsandrecord:attachmentsinside the reservedrecordnamespace with no row, socomponent-type-unknownwould refuse an authored node of either. No authored producer exists on objectstackmain(zero hits), so no reach is established. It is noted here for the spec seat's eye, not as work.Dedupe words: approval decision panel component type · ComponentPropsMap approval row · record approval_decision
Generated by Claude Code