You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
finding(types): AppComponentSchema inherits the renderer's hidden predicate from BaseSchema while its Zod mirror takes the spec's app-catalogue hidden boolean — one name, two meanings #7542
Found while widening BaseSchema.hidden under objectui#7455 (maintainer ruling 2026-09-03, option A). Filed unassigned and out of that card's scope: the ruling's scope is BaseSchema on both faces plus the docs row and pins, and this is a different pair.
Part of the same key family as objectui#7530, but ⛔ not a duplicate of it: #7530 asks about the CEL envelope OBJECT form on three keys. This is about the app node taking two different hidden keys, one per face.
The measurement
Measured on @objectstack/spec@17.2.0 (the version the lockfile pins), by resolving AppSchema.shape at runtime:
key
declared by the spec's AppSchema?
accepts a boolean
accepts a string
visible
NO
—
—
disabled
NO
—
—
hidden
YES
yes
no
packages/types/src/zod/app.zod.ts builds the mirror as BaseSchema.extend(SpecAppFields.shape).extend({ ... }), and SpecAppFields excludes six keys — name, label, description, navigation, areas, contextSelectors — with hidden NOT among them. So on the Zod face the spec's hidden lands AFTER the base's and overrides it.
packages/types/src/app.ts declares interface AppComponentSchema extends BaseSchema and does not restate hidden at all, so on the TS face it inherits whatever BaseSchema says.
Why this surfaced now
Before #7455 both faces read boolean and the collision was invisible. #7455 widened BaseSchema.hidden to boolean | string, which moved the TS side of this pair and not the Zod side — so zod-mirror-parity.test.ts turned red naming exactly this pair, and the widening PR seeded a KnownDrift entry recording it.
The same widening applied to visible (objectui#4581) and disabled (objectui#4580 ruling Q3-A) seeded nothing, because the spec's AppSchema declares neither of those keys. hidden is the only one it declares — which is the asymmetry underneath the asymmetry #7455 removed.
The actual question
The two keys collide in NAME and differ in MEANING:
the spec's AppSchema.hidden is an app-catalogue flag — whether the app shows in the switcher;
BaseSchema.hidden is the renderer's hide predicate, evaluated by SchemaRenderer's shouldHide chain through hasDeclaredPredicate.
Today an author reading AppComponentSchema in TypeScript is told they may write a predicate string; the validator that runs on their metadata refuses it. Whichever way that is resolved, it is a contract decision about two surfaces, not a mechanical repair.
Some directions, ⛔ none of them chosen here:
Keep the spec's key on both faces for the app node and restate hidden?: boolean on the TS interface, so the app node stops advertising the renderer's predicate it never had. Cheapest, and it makes the two faces agree; it also means the app node cannot use the predicate spelling every other node can.
Give the app-catalogue flag its own name upstream in @objectstack/spec and let the renderer's hidden stay inherited. Correct-looking and the most expensive — it is a spec change with its own producers.
Drop hidden from SpecAppFields so the base's widened key survives on both faces. ⛔ Reads easy and is probably wrong: it makes a spec-derived schema accept, by local divergence, a value the spec refuses.
⛔ Do not close this by editing the ledger
The KnownDrift entry seeded by #7455 records the drift with its reason; it is a ratchet, not a waiver. Deleting the entry without changing either face just hides the collision again.
Before #7455 both faces read boolean and the collision was invisible. #7455 widened BaseSchema.hidden to boolean | string, which moved the TS side of this pair and not the Zod side.
以及那条不对称之下的不对称:
The same widening applied to visible (#4581) and disabled (#4580 ruling Q3-A) seeded nothing, because the spec's AppSchema declares neither of those keys. hidden is the only one it declares.
有一个活的 declared ≠ enforced:卡片说得准 —— "an author reading AppComponentSchema in TypeScript is told they may write a predicate string; the validator that runs on their metadata refuses it."
⛔ Do not close this by editing the ledger. The KnownDrift entry seeded by #7455 records the drift with its reason; it is a ratchet, not a waiver. Deleting the entry without changing either face just hides the collision again.
⚠️ 这条要特别当心,因为删掉台账项是让测试变绿的最快路径,而它恰好是错的。
⛔ 三条方向的定型 —— 差别很大,⛔ 不要按"哪条最快"选
方向
落点
型
manual floor
1. TS 面重述 hidden?: boolean
packages/types/src/app.ts
收窄 TS 声明使其与验证器一致 ⇒ Bug/tidy
不踩
2. 上游给 app-catalogue flag 改名
@objectstack/spec,跨仓
协议变更
踩,且最贵
⛔ 3. 把 hidden 从 SpecAppFields 排除掉
packages/types
让本地 schema 接受一个 spec 拒绝的值
踩,且方向错误
⭐ 卡片对方向 3 的评价我完全同意并复述:"Reads easy and is probably wrong: it makes a spec-derived schema accept, by local divergence, a value the spec refuses." ⛔ 这是最诱人的一条(一行删除、测试变绿),也是唯一会制造新缺陷的一条。
Claim: domain:spec @ objectui execution seat, session session_01BAZFhALsQsGqxui8sNqM8s, claimed 2026-09-05T17:18Z (PM loop round R1, seventh wave). Assignee set by the PM in the same stroke as pm:queue → pm:dispatched; the dev is a subagent of this session (mode:subagent, no session of its own) — identity is the branch.
Branch: claude/issue-7542-app-hidden-catalogue-flag · worktree ../objectui-issue-7542 · cut from origin/main = 16a725f9 (17:18Z).
Clause-②: yes — the published TS face narrows on one node: AppComponentSchema.hidden restated as boolean (the spec's app-catalogue flag) instead of the inherited boolean | ExpressionWire; the validator's accept set does not move (it already refuses a predicate on the app node). The PM seat reviews at tier before it lands.
Container & model: fable (CONTRACT_REVIEW_TIER), size S, mode:subagent.
Direction: 1, under a declared veto window. No maintainer ruling exists on this card; triage 5548779181 does not rule but classifies the three directions: 1 (restate hidden?: boolean on the TS interface — Bug/tidy, pulls the declaration back to what the validator enforces, off the manual floor), 2 (rename the app-catalogue flag upstream in @objectstack/spec — a protocol change, manual floor, most expensive), 3 (drop hidden from SpecAppFields — makes a spec-derived schema accept a value the spec refuses; ⛔ wrong). Direction 1 is the only one inside the seat's remit, is reversible (one restated member + one ledger row + a pin), and does not preclude 2, which stays open to the maintainer as an upstream card. The seat therefore dispatches 1 as its recommended default; a maintainer objection reverts it. Recorded in the decision batch.
Scope: packages/types/src/app.ts — restate hidden?: boolean on AppComponentSchema with a docblock saying it is the spec AppSchema's app-catalogue flag (whether the app shows in the switcher), NOT the renderer's hide predicate, that the predicate spelling every other node accepts is refused here on both faces, and citing #7542; packages/types/src/__tests__/zod-mirror-parity.test.ts — delete the KnownDrift entry 'app.zod.ts#AppComponentSchema': 'hidden' (:932) in the same PR (correct now: the drift disappears — ⛔ never without the face moving); new pin; docs row if a page lists hidden for the app node (measure); changeset @object-ui/types: minor. ⛔ app.zod.ts untouched (direction 3 forbidden), ⛔ BaseSchema.hidden untouched (#7455 / #7530), ⛔ not folded with #7530.
Premise verified on origin/main16a725f9 at 17:18Z: zod/app.zod.ts:279SpecAppFields = specFieldsExcept(SpecAppSchema.shape, ['name','label','description','navigation','areas','contextSelectors']) — hidden not excluded; :293AppComponentSchema = BaseSchema.extend(SpecAppFields.shape).extend({…}) — the spec's hidden lands after the base's; app.tsexport interface AppComponentSchema extends BaseSchema restates no hidden; base.ts:376hidden?: boolean | ExpressionWire and base.zod.ts:200z.union([z.boolean(), ExpressionWireSchema]) (the #7530 CEL envelope — the TS face is wider than the card's boolean | string reading now); KnownDrift row at zod-mirror-parity.test.ts:932, seeded by #7455. The spec-side reading (AppSchema.shape.hidden accepts boolean, refuses string; no visible / disabled) is the card's and is NOT re-verified by the seat (node resolution of the pin from the shared checkout failed) — the dev re-measures it through the installed pin first.
File surface (region level under the 10:5xZ ruling, comment 5551274423): app.ts — the AppComponentSchema block; zod-mirror-parity.test.ts — the one KnownDrift row; new pin; one docs row if any; changeset. Hot files: zod-mirror-parity.test.ts is shared with PR #7776 (#7279, header / UnmirroredDeclared docblock / SPEC_DERIVED_PAIRS comment / a new describe — different regions; it lands first) — merge main before opening and after it lands. Half-state patrol anchor: none exists in objectui (#5986).
Claim (dev): branch claude/issue-7542-app-hidden-catalogue-flag · worktree ../objectui-issue-7542 · cut from origin/main = 669d71bf (17:25Z), origin/main1b69ca58 (PR #7776) merged before opening · dev of session session_01BAZFhALsQsGqxui8sNqM8s's dispatch (mode:subagent, no session of its own — identity is the branch). Assignee is the PM's and is not touched.
Premise re-verified on the tree, all five points of the claim hold; the spec-side reading that the seat could not take is now measured through the installed pin (@objectstack/spec@17.2.0, ui entry): AppSchema.shape.hidden is optional(boolean), accepts true / false, refuses a string and the CEL envelope object with invalid_type at hidden; visible and disabled are absent from the shape (30 keys; controls label and active present). Both entry paths (AppComponentSchema.safeParse, safeValidateSchema) refused the predicate string and the envelope at hidden before editing and accepted the booleans — recorded in the PR body. Direction 1 implemented; app.zod.ts and BaseSchema.hidden untouched; the KnownDrift row goes only because the face moved (both directions ablated).
{
"issue": 7542,
"status": "done",
"branch": "claude/issue-7542-app-hidden-catalogue-flag",
"pr": "https://github.com/objectstack-ai/objectui/pull/7782",
"head": "1d3074f8 (= 6bd44196 implementation + merge of origin/main 1b69ca58 / PR #7776; base 669d71bf, whose packages/types/src/app.ts blob 14d12b7c equals 16a725f9's)",
"premise_still_valid": true,
"clause_2": "yes — the published TS face narrows on one node: AppComponentSchema.hidden restated boolean (was the inherited boolean | ExpressionWire); the validator's accept set does not move",
"line_budget": "n/a",
"tier": "CONTRACT_REVIEW_TIER; label needs:contract-review attached by MCP label union (REST additive endpoint 403 in this session), read back as [package: types, tests, needs:contract-review] = the union, nothing stripped; PR left draft; not governed (0 of 4 paths, objectstack d701e65)",
"summary": "Direction 1 implemented under the seat's declared veto window: packages/types/src/app.ts restates hidden?: boolean on the AppComponentSchema block with a docblock naming the collision (spec AppSchema app-catalogue flag taken by reference through SpecAppFields vs BaseSchema's renderer predicate), the refusal of the predicate spelling on both faces, the in-repo reader (filterActiveApps reads hidden !== true, never evaluates), direction 1 as the ruling and direction 2 as the open alternative. The KnownDrift row 'app.zod.ts#AppComponentSchema': 'hidden' is deleted in the same PR because the drift is gone (both faces boolean); header counts 42/63 to 41/62 with a history sentence added in the cumulative style and the dangling 'see that entry' pointer repointed. New pin app-hidden-catalogue-flag-7542.test.ts (8 tests). Changeset @object-ui/types minor. app.zod.ts and BaseSchema.hidden untouched; docs measured, no app-node hidden row exists (the three app-schema.mdx hits are AppMenuItem.hidden), so none moved. Assignee not touched; the PM's assignee field was empty on the payload read, as expected for the shared identity.",
"spec_reading": "@objectstack/spec@17.2.0 ui entry resolved from packages/types/node_modules (dist/ui/index.js): AppSchema.shape has 30 keys; hidden present, optional(boolean), described 'Hide from the App Switcher; the shell surfaces hidden apps via the avatar menu instead (navigation only — never an access gate)'; visible ABSENT, disabled ABSENT (strict schema refuses them as unrecognized_keys); controls label and active present. safeParse hidden true/false OK; predicate string REFUSED invalid_type at hidden 'expected boolean, received string'; CEL envelope object REFUSED invalid_type at hidden 'expected boolean, received object'.",
"entry_path_verdicts": {
"before_669d71bf": "AppComponentSchema.safeParse: hidden true/false OK; predicate string REFUSED invalid_type at path hidden; envelope REFUSED invalid_type at path hidden (received object). safeValidateSchema: true/false OK; string REFUSED root invalid_union with the app arm carrying the hidden issue; envelope REFUSED the same way. Controls: BaseSchema and ButtonSchema accept string and envelope on hidden; safeValidateSchema accepts a button with the string.",
"after_1d3074f8": "runtime identical (validator unchanged); declaration moved: AppComponentSchema['hidden'] is boolean | undefined (invariant Equal pinned), a predicate string on a typed app document is a compile error (ts-expect-error pinned)."
},
"files_changed": [
"packages/types/src/app.ts (+44: hidden?: boolean with docblock, AppComponentSchema block only)",
"packages/types/src/__tests__/zod-mirror-parity.test.ts (-28 row+docblock, header count line 42/63 to 41/62 + history sentence, one pointer repointed)",
"packages/types/src/__tests__/app-hidden-catalogue-flag-7542.test.ts (new, 265 lines, 8 tests)",
".changeset/app-hidden-catalogue-flag-7542.md (@object-ui/types: minor)"
],
"gates": [
{"command": "pnpm --filter '@object-ui/types^...' build", "exit": 1, "head": "669d71bf", "verdict": "ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT — closure is @object-ui/test-support with no build script; nothing to build (n/a, not red)"},
{"command": "pnpm --filter @object-ui/types build", "exit": 0, "head": "1d3074f8", "verdict": "dist completeness: 1 package(s) complete (124 emitted files verified)"},
{"command": "pnpm --filter @object-ui/types type-check (tsc, tsconfig.examples.json, tsconfig.test.json)", "exit": 0, "head": "1d3074f8", "verdict": "clean"},
{"command": "pnpm exec vitest run --maxWorkers=2 packages/types/", "exit": 0, "head": "1d3074f8", "verdict": "Test Files 126 passed (126) · Tests 2233 passed (2233)"},
{"command": "pnpm --filter @object-ui/types lint", "exit": 0, "head": "1d3074f8", "verdict": "270 problems (0 errors, 270 warnings) — pre-existing no-explicit-any, none in the four touched files"},
{"command": "node scripts/check-changeset-presence.mjs", "exit": 0, "head": "1d3074f8", "verdict": "3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/app-hidden-catalogue-flag-7542.md"},
{"command": "node scripts/check-changeset-fixed.mjs · check-changeset-no-major.mjs · check-changeset-overwrite.mjs", "exit": "0 / 0 / 0", "head": "1d3074f8", "verdict": "green (No changeset declares a major bump; No pre-existing changeset was modified or deleted)"},
{"command": "node scripts/check-control-bytes.mjs", "exit": 0, "head": "1d3074f8", "verdict": "check-control-bytes: OK (scanned 6342 tracked text file(s); skipped 85 binary)"},
{"command": "pnpm check:spec-symbols · check:doc-types · check:doc-fences", "exit": "0 / 0 / 0", "head": "1d3074f8", "verdict": "green"},
{"command": "node /home/user/objectstack/scripts/pm/check-governed-merges.mjs --test (final four paths)", "exit": 0, "head": "objectstack d701e65", "verdict": "0 of 4 path(s) hit the register — NOT governed"},
{"command": "pnpm --filter @object-ui/app-shell type-check (read-only consumer sweep)", "exit": 2, "head": "6bd44196", "verdict": "NOT MEASURED — 934 TS2307 'Cannot find module @object-ui/components…' errors: app-shell resolves 29 sibling packages via dist declarations absent in a fresh worktree; zero errors mention hidden or AppComponentSchema; declared narrowing, CI measures it"},
{"command": "node scripts/check-doc-links.mjs", "exit": "not run", "head": "-", "verdict": "no docs row moved"},
{"command": "CI on PR #7782", "exit": "in_progress", "head": "1d3074f8", "verdict": "not awaited (report at PR-open time)"}
],
"tests": "Targeted (pre-merge, 6bd44196): vitest app-hidden-catalogue-flag-7542 + zod-mirror-parity → 2 files, 26 tests passed. Final union at 1d3074f8 under the shared verify lock (shared-box seconds, 33s held): build 0, type-check 0, vitest packages/types/ 0 (126 files, 2233 tests). Ablation at 6bd44196 under the lock, trap-restored, absolute paths, blob-hash proof — Leg A: app.ts checked out from 669d71bf (git hash-object 14d12b7c = base blob, 'hidden?: boolean;' count 0, docblock marker count 0) → tsc -p tsconfig.test.json exit 2 with exactly 3 errors: app-hidden-catalogue-flag-7542.test.ts(99,3) TS2344 'false' does not satisfy 'true' (the Equal leg), (186,5) TS2578 unused ts-expect-error, zod-mirror-parity.test.ts(1849,14) TS2322 '\"app.zod.ts#AppComponentSchema\"' is not assignable to 'never' (the parity file naming the pair); vitest on the two files stayed green 26/26 (mutation is on the TS face; runtime legs measure the mirror — expected direction, reported as observed); restored via git checkout HEAD -- PATH: hash b4010c1e = HEAD blob, count 1, git diff HEAD empty. Leg B: the KnownDrift row put back from 669d71bf with the face narrowed (hash 6a35c73e = base blob, row count 1) → tsc exit 2 with 1 error: zod-mirror-parity.test.ts(1871,14) TS2322 naming the pair; vitest exit 1, 1 failed | 25 passed (the pin's ledger leg); restored: hash 9147eee6 = HEAD blob, row count 0, git diff HEAD empty, git status clean. Resolution path is source (../app, ../zod/app.zod, tsconfig.test.json) — no dist in the loop, dist preflight n/a.",
"mcp_calls": "9 — create_pull_request, add_issue_comment (claim), issue_read get_labels on the PR number (failed: not resolvable as an Issue), pull_request_read get (body read-back), issue_read get_comments (claim read-back), issue_write update (label union), pull_request_read get (label read-back), add_issue_comment (this report), issue_read get_comments (report read-back). Issue body + triage + PM claim read through the zero-quota public-page payload (frontTimelineItems hasNextPage false, 15 items); REST read probe returned 403 (GitHub access not enabled for this session).",
"deviations": [
"REST channel 403 for this session: reads went git → public payload; label write went MCP union with compare read-back instead of the additive REST endpoint; declared here as the channel switch.",
"app-shell read-only type-check NOT MEASURED locally (dist closure of 29 packages absent in the fresh worktree; build does not fit the foreground cap); substitute evidence: grep sweep — no source in app-shell/layout/console authors hidden on an app-typed document, the only AppComponentSchema consumer (useNavigationSync.ts) writes no hidden, and filterActiveApps reads hidden !== true.",
"Ablation revert target was 669d71bf, not 16a725f9 as the dispatch spelled it: app.ts blob is identical at both (14d12b7c), so the leg is equivalent; the parity file was reverted to 669d71bf for leg B.",
"PR body ends with the system-reminder attribution form (🤖 Generated with … + session URL line); the platform appended a second session-URL footer on create (the known create-time behaviour) — recorded, not repaired.",
"The vitest half of leg A stayed green — the dispatch's 'the parity suite goes red naming the pair' was observed under tsc -p tsconfig.test.json (type-level LedgerMismatch), not under vitest; both instruments reported."
],
"open_questions": [
{"question": "Direction 2 (rename the app-catalogue flag upstream in @objectstack/spec so the app node can inherit the renderer's predicate) remains open to the maintainer; nothing in this PR precludes it.", "options": ["A — leave direction 1 landed and file/keep an upstream spec card for the rename", "B — veto direction 1 and take direction 2 first (cross-repo, with producers)"], "recommendation": "A, because the in-repo readers and the spec producer already treat the key as a boolean, direction 1 is one member + one row + one pin and reversible by one revert, and direction 2 is a protocol change with its own producers"}
],
"out_of_scope_findings": []
}
Contract review — PASS at CONTRACT_REVIEW_TIER (in-seat, 2026-09-05T17:50Z) — PR #7782, head 1d3074f8
Implemented-by: dev subagent of this session's dispatch, branch claude/issue-7542-app-hidden-catalogue-flag (mode:subagent, no session of its own); dev report 5553657368
Reviewed-by: session_01BAZFhALsQsGqxui8sNqM8s (domain:spec @ objectui seat; served tier read via get_session at 17:49Z: session_context.model and last_served_model both claude-fable-5-1)
Diff read by ref against origin/main1b69ca58 (four files, +324 / −30; the app.ts hunk, the parity-file hunk, the changeset and the pin's describe/it list re-read off the branch tip with git diff origin/main... at 17:49Z; app.zod.ts untouched — verified, 0 lines).
① Derived judgments — each accept-set and public-surface change named and judged
zod-mirror-parity.test.ts: the KnownDrift row 'app.zod.ts#AppComponentSchema': 'hidden' and its docblock deleted in the same PR — correct, the drift is gone (both faces boolean), and leg B of the ablation proves the ledger refuses the deletion alone (row restored with the face narrowed → TS2322 naming the pair + the pin's ledger leg red). Header counts 42 / 63 → 41 / 62 with a history sentence ADDED in the cumulative style; the dangling "see that entry" pointer re-pointed. No conflict with PR test(types): re-derive ObjectViewSchema's side of the #6058 split as LOCAL and pin the header counts (objectui#7279) #7776's regions (merged before opening, clean). Triage's hard prohibition honoured.
New pin app-hidden-catalogue-flag-7542.test.ts (8 tests): invariant Equal of AppComponentSchema['hidden'] with boolean | undefined; BaseSchema['hidden'] still boolean | ExpressionWire | undefined and ButtonSchema['hidden'] equal to it (controls); a @ts-expect-error on a predicate string authored on a typed app document; the mirror accepting true / false and refusing the string and the envelope AT PATH hidden on both entry paths; controls on BaseSchema, ButtonSchema and the union path with a button; the spec reading live through the pin with control key active; the KnownDrift row absent off disk beside a control that the reader sees the ledger and the pair stays in MIRRORS. Ablation: leg A (app.ts reverted to the base blob by hash) → tsc -p tsconfig.test.json exit 2 with exactly three errors (the Equal leg, the unused @ts-expect-error, the parity file's TS2322 naming the pair) while the runtime legs stay green — the right asymmetry for a TS-face-only change, reported as observed; leg B as above; restores proven by blob hash, git diff HEAD empty. Adequate and red-first on both instruments.
Docs: measured, no row exists for hidden on the app node (the three app-schema.mdx hits are AppMenuItem.hidden) — none moved; check-doc-links not run for that reason. Accepted.
Parity suite green on the merged head (126 files / 2233 tests); three tsc projects green; lint 0 errors on the four touched files.
② semver — .changeset/app-hidden-catalogue-flag-7542.md: @object-ui/types: minor, naming the collision, the ruling direction, the TS-only breaking edge (a predicate on an app node no longer type-checks; the app node cannot use the predicate spelling), that authored JSON metadata is unaffected (the validator never admitted it), the reader's agreement, and the removed KnownDrift row. A TS-face narrowing is the minor class this seat has graded all shift; consistent. The card's own requirement (triage boundary 3: write down the new fact that the app node does not support the predicate spelling) is met in the changeset, the docblock and the PR body.
③ Boundary flags — open_questions: one — direction 2 (upstream rename) stays open; the dev recommends A (leave direction 1 landed, keep an upstream card if the maintainer wants it). That is the veto-window arrangement the claim declared (5553481421), recorded in the decision batch as item 8; no new maintainer item. Deviations reviewed and accepted (REST 403 channel fallbacks with read-backs; the app-shell read-only type-check NOT MEASURED — a fresh worktree lacks the 29 sibling dists, 934 unrelated TS2307s, zero mentioning hidden / AppComponentSchema, with the grep sweep standing in and CI's farm measuring it; ablation revert target 669d71bf whose app.ts blob equals 16a725f9's; the pnpm --filter '@object-ui/types^...' build non-red — the closure has no build script). No out-of-scope findings. Nothing touches content/docs/releases/.
Verdict: PASS for PR #7782. Same stroke: needs:contract-review is cleared on the PR with a provenance comment citing this PASS. Landing follows once every check run on 1d3074f8 is completed: success (farm started 17:45Z; Type Check, four shards, Lint, Doc Snippet Type Check, Bundle Analysis, Skill Example Check, README Export Check in progress at 17:48Z) — flip re-check armed. Head moves ⇒ the label is re-hung and the delta re-reviewed.
Landing — PR #7782 (2026-09-05T18:01Z). Pre-landing three: ① flipped ready at 18:01Z after the PASS above (comment 5553667716) and the carrier cleared on the PR with provenance (PR comment 5553670229); every check run on 1d3074f8 was completed: success or skipped at 18:00Z (32 runs), branch tip re-read 1d3074f8 at 18:00:52Z; ② auto-merge enabled, method SQUASH (the API echoes an empty method string — known platform reading); ③ queue probe running against refs/heads/gh-readonly-queue/main/* (the queue was empty at 18:00Z after PR #7777 landed; origin/main = 554f2b6b). This PR carries Fixes #7542: the card closes on its squash; pm:dispatched is stripped once MERGED is read twice (PR merged: true + origin/main containing the squash). Direction 1 lands under the veto window declared on the claim; direction 2 (upstream rename) stays open to the maintainer — recorded in the decision batch as item 8.
MERGED (2026-09-05T18:17Z) — PR #7782 landed through the merge queue as squash 2fcefb95 on main. Three readings: PR merged: true (merged_at 18:16:31Z); this card's closed_by_pull_requests reference to PR #7782 reads MERGED and the card closed at 18:16:32Z; git log origin/main at 18:17:10Z shows 2fcefb95 feat(types): restate AppComponentSchema.hidden as the spec's app-catalogue boolean (objectui#7542) (#7782) at the tip. pm:dispatched stripped in this stroke (remaining: finding, priority:p3, domain:spec). Direction 1 is on main under the veto window declared on the claim (5553481421): a maintainer objection reverts it by one revert (the restated member, the removed KnownDrift row, the pin, the changeset); direction 2 (renaming the app-catalogue flag upstream in @objectstack/spec) stays open and is recorded in the seat's decision batch as item 8. Session session_01BAZFhALsQsGqxui8sNqM8s.
Maintainer ruling recorded — direction 2 = A: direction 1 stays landed; ⛔ no upstream rename card. The veto window declared on the claim is now closed.
Recorded by the domain:spec @ objectui seat, session session_01BAZFhALsQsGqxui8sNqM8s, 2026-09-06T02:30Z. Provenance (who / verbatim / where): the maintainer (os-justin), live PM chat with this session, replying to decision batch 2 of 2026-09-05T23:1xZ, in which this card was item 8 — tabled with the seat's recommendation A and the four-facet analysis in Chinese. Verbatim reply: 「第二批(4 项,契约形状族) 帮我处理,全部同意」.
What this settles. PR #7782 (2fcefb95, merged 2026-09-05T18:16Z) landed direction 1 under a declared veto window: AppComponentSchema.hidden is restated on the TypeScript face as the spec AppSchema's app-catalogue boolean, matching what the validator has always enforced; the KnownDrift row is gone because the drift is gone. The window closes with this ruling — direction 1 is now a decision, not a default awaiting objection.
Direction 2 is declined, on the record: no card is filed upstream in @objectstack/spec to rename the app-catalogue flag so the app node could inherit the renderer's hidden predicate. The four facets as tabled: no measured pull (the only in-repo reader, filterActiveApps, tests hidden !== true; the spec producer writes a boolean; nothing asks to hide an app by expression), one name one meaning is already achieved, the TypeScript face and the validator now agree so an authored predicate is a compile error at the site, and a protocol change with its own producers for a hypothetical need is capability expansion the startup-focus rule refuses.
Consequence for authors, stated so it is not re-reported as a bug: on an app node, hidden is the app-switcher catalogue flag and takes true / false only; the predicate spelling every other node accepts (hidden: "user.role == 'admin'", or the CEL envelope) is refused on both faces there. That is recorded in the member's docblock, in PR #7782's changeset and now here. The card stays closed; no label change (finding, priority:p3, domain:spec retained).
Activity
os-zhuang commentedon Sep 5, 2026
分诊 —
domain:spec/priority:p3/pm:queue/finding锚定 (anchoring):方向 1(在 TS 接口上重述
hidden?: boolean)与方向 3 都落在packages/types⇒domain:spec。方向 2 是上游 spec 改名,跨仓。在
origin/maina472b07上复核 —— 两个面的构造方式成立⭐
zod/app.zod.ts:285的BaseSchema.extend(SpecAppFields.shape)就是机制本身:spec 的键在 base 之后落,所以它覆盖 base 的。而 TS 面app.ts:364只写extends BaseSchema、不重述hidden⇒ 继承 base 的。两个面因此对同一个键取了不同定义。⛔ spec 侧的 runtime 解析(
AppSchema.shape里有hidden、没有visible/disabled)本席验不了(未装 spec),采信未复核。但那正是卡片论证的支点,接手者请重测。⭐ 卡片对"为什么现在才浮现"的解释,是它最有价值的部分
以及那条不对称之下的不对称:
⭐ 三个同族键、同一次加宽、只有一个炸出台账项 —— 这个对照本身就是诊断:碰撞不在加宽里,在"spec 恰好也声明了这一个键"里。
定级理由
priority:p3:AppComponentSchemain TypeScript is told they may write a predicate string; the validator that runs on their metadata refuses it."ObjectKanbanSchemarequiresgroupField(zero read sites) and declares neithergroupBynorlimit— no workingobject-kanbannode is assignable to any declared type #7322(整个object-kanban声明面误导,p2),这里作者会在校验时被响亮拒绝,不是静默不分组。BaseSchema.hiddenis declaredboolean, but the renderer evaluates it as a predicate — the third key onvisible/disabled's evaluated path, never widened #7455 播的KnownDrift项),不是静默漂移。不上 p2:拒绝是响亮的;影响面是单键单节点。
⛔ 硬禁止(卡片明写,本席背书并加重)
⛔ 三条方向的定型 —— 差别很大,⛔ 不要按"哪条最快"选
hidden?: booleanpackages/types/src/app.ts@objectstack/spec,跨仓hidden从SpecAppFields排除掉packages/types⭐ 卡片对方向 3 的评价我完全同意并复述:"Reads easy and is probably wrong: it makes a spec-derived schema accept, by local divergence, a value the spec refuses." ⛔ 这是最诱人的一条(一行删除、测试变绿),也是唯一会制造新缺陷的一条。
方向 1 是唯一不踩人工底线的,代价卡片也如实说了:app 节点从此用不了其他节点都能用的谓词拼写。⛔ 分诊席不裁决,但把这个代价摆明,因为它是选 1 还是 2 的唯一实质分歧。
⛔ 给执行席的其他边界
visible/hidden/disabledby the shared evaluator but declared on none of them — declare it on all three, or refuse it on all three #7530 合并。卡片已正确划界:那张问的是三个键上的 CEL 信封对象形式,本卡问的是app 节点在两个面上取了两个不同的hidden。相邻、不同。KnownDrift项(此时删是正确的:漂移真的消失了),⛔ 并在 PR 说明里写清楚"app 节点不支持谓词拼写"这个新事实,否则下一个作者会当它是 bug 报回来。⛔ 分诊席不认领、不派单、不写码、不裁决方向。
Generated by Claude Code
os-justin commentedon Sep 5, 2026
Claim:
domain:spec@ objectui execution seat, sessionsession_01BAZFhALsQsGqxui8sNqM8s, claimed 2026-09-05T17:18Z (PM loop round R1, seventh wave). Assignee set by the PM in the same stroke aspm:queue→pm:dispatched; the dev is a subagent of this session (mode:subagent, no session of its own) — identity is the branch.Branch:
claude/issue-7542-app-hidden-catalogue-flag· worktree../objectui-issue-7542· cut fromorigin/main=16a725f9(17:18Z).Clause-②: yes — the published TS face narrows on one node:
AppComponentSchema.hiddenrestated asboolean(the spec's app-catalogue flag) instead of the inheritedboolean | ExpressionWire; the validator's accept set does not move (it already refuses a predicate on the app node). The PM seat reviews at tier before it lands.Container & model:
fable(CONTRACT_REVIEW_TIER), size S,mode:subagent.Direction: 1, under a declared veto window. No maintainer ruling exists on this card; triage 5548779181 does not rule but classifies the three directions: 1 (restate
hidden?: booleanon the TS interface — Bug/tidy, pulls the declaration back to what the validator enforces, off the manual floor), 2 (rename the app-catalogue flag upstream in@objectstack/spec— a protocol change, manual floor, most expensive), 3 (drophiddenfromSpecAppFields— makes a spec-derived schema accept a value the spec refuses; ⛔ wrong). Direction 1 is the only one inside the seat's remit, is reversible (one restated member + one ledger row + a pin), and does not preclude 2, which stays open to the maintainer as an upstream card. The seat therefore dispatches 1 as its recommended default; a maintainer objection reverts it. Recorded in the decision batch.Scope:
packages/types/src/app.ts— restatehidden?: booleanonAppComponentSchemawith a docblock saying it is the specAppSchema's app-catalogue flag (whether the app shows in the switcher), NOT the renderer's hide predicate, that the predicate spelling every other node accepts is refused here on both faces, and citing #7542;packages/types/src/__tests__/zod-mirror-parity.test.ts— delete theKnownDriftentry'app.zod.ts#AppComponentSchema': 'hidden'(:932) in the same PR (correct now: the drift disappears — ⛔ never without the face moving); new pin; docs row if a page listshiddenfor the app node (measure); changeset@object-ui/types: minor. ⛔app.zod.tsuntouched (direction 3 forbidden), ⛔BaseSchema.hiddenuntouched (#7455 / #7530), ⛔ not folded with #7530.Premise verified on
origin/main16a725f9at 17:18Z:zod/app.zod.ts:279SpecAppFields = specFieldsExcept(SpecAppSchema.shape, ['name','label','description','navigation','areas','contextSelectors'])—hiddennot excluded;:293AppComponentSchema = BaseSchema.extend(SpecAppFields.shape).extend({…})— the spec'shiddenlands after the base's;app.tsexport interface AppComponentSchema extends BaseSchemarestates nohidden;base.ts:376hidden?: boolean | ExpressionWireandbase.zod.ts:200z.union([z.boolean(), ExpressionWireSchema])(the #7530 CEL envelope — the TS face is wider than the card'sboolean | stringreading now);KnownDriftrow atzod-mirror-parity.test.ts:932, seeded by #7455. The spec-side reading (AppSchema.shape.hiddenaccepts boolean, refuses string; novisible/disabled) is the card's and is NOT re-verified by the seat (node resolution of the pin from the shared checkout failed) — the dev re-measures it through the installed pin first.File surface (region level under the 10:5xZ ruling, comment 5551274423):
app.ts— theAppComponentSchemablock;zod-mirror-parity.test.ts— the oneKnownDriftrow; new pin; one docs row if any; changeset. Hot files:zod-mirror-parity.test.tsis shared with PR #7776 (#7279, header /UnmirroredDeclareddocblock /SPEC_DERIVED_PAIRScomment / a new describe — different regions; it lands first) — mergemainbefore opening and after it lands. Half-state patrol anchor: none exists in objectui (#5986).Generated by Claude Code
os-justin commentedon Sep 5, 2026
Claim (dev): branch
claude/issue-7542-app-hidden-catalogue-flag· worktree../objectui-issue-7542· cut fromorigin/main=669d71bf(17:25Z),origin/main1b69ca58(PR #7776) merged before opening · dev of sessionsession_01BAZFhALsQsGqxui8sNqM8s's dispatch (mode:subagent, no session of its own — identity is the branch). Assignee is the PM's and is not touched.Premise re-verified on the tree, all five points of the claim hold; the spec-side reading that the seat could not take is now measured through the installed pin (
@objectstack/spec@17.2.0,uientry):AppSchema.shape.hiddenisoptional(boolean), acceptstrue/false, refuses a string and the CEL envelope object withinvalid_typeathidden;visibleanddisabledare absent from the shape (30 keys; controlslabelandactivepresent). Both entry paths (AppComponentSchema.safeParse,safeValidateSchema) refused the predicate string and the envelope athiddenbefore editing and accepted the booleans — recorded in the PR body. Direction 1 implemented;app.zod.tsandBaseSchema.hiddenuntouched; theKnownDriftrow goes only because the face moved (both directions ablated).Generated by Claude Code
os-justin commentedon Sep 5, 2026
os-dev-report
{ "issue": 7542, "status": "done", "branch": "claude/issue-7542-app-hidden-catalogue-flag", "pr": "https://github.com/objectstack-ai/objectui/pull/7782", "head": "1d3074f8 (= 6bd44196 implementation + merge of origin/main 1b69ca58 / PR #7776; base 669d71bf, whose packages/types/src/app.ts blob 14d12b7c equals 16a725f9's)", "premise_still_valid": true, "clause_2": "yes — the published TS face narrows on one node: AppComponentSchema.hidden restated boolean (was the inherited boolean | ExpressionWire); the validator's accept set does not move", "line_budget": "n/a", "tier": "CONTRACT_REVIEW_TIER; label needs:contract-review attached by MCP label union (REST additive endpoint 403 in this session), read back as [package: types, tests, needs:contract-review] = the union, nothing stripped; PR left draft; not governed (0 of 4 paths, objectstack d701e65)", "summary": "Direction 1 implemented under the seat's declared veto window: packages/types/src/app.ts restates hidden?: boolean on the AppComponentSchema block with a docblock naming the collision (spec AppSchema app-catalogue flag taken by reference through SpecAppFields vs BaseSchema's renderer predicate), the refusal of the predicate spelling on both faces, the in-repo reader (filterActiveApps reads hidden !== true, never evaluates), direction 1 as the ruling and direction 2 as the open alternative. The KnownDrift row 'app.zod.ts#AppComponentSchema': 'hidden' is deleted in the same PR because the drift is gone (both faces boolean); header counts 42/63 to 41/62 with a history sentence added in the cumulative style and the dangling 'see that entry' pointer repointed. New pin app-hidden-catalogue-flag-7542.test.ts (8 tests). Changeset @object-ui/types minor. app.zod.ts and BaseSchema.hidden untouched; docs measured, no app-node hidden row exists (the three app-schema.mdx hits are AppMenuItem.hidden), so none moved. Assignee not touched; the PM's assignee field was empty on the payload read, as expected for the shared identity.", "spec_reading": "@objectstack/spec@17.2.0 ui entry resolved from packages/types/node_modules (dist/ui/index.js): AppSchema.shape has 30 keys; hidden present, optional(boolean), described 'Hide from the App Switcher; the shell surfaces hidden apps via the avatar menu instead (navigation only — never an access gate)'; visible ABSENT, disabled ABSENT (strict schema refuses them as unrecognized_keys); controls label and active present. safeParse hidden true/false OK; predicate string REFUSED invalid_type at hidden 'expected boolean, received string'; CEL envelope object REFUSED invalid_type at hidden 'expected boolean, received object'.", "entry_path_verdicts": { "before_669d71bf": "AppComponentSchema.safeParse: hidden true/false OK; predicate string REFUSED invalid_type at path hidden; envelope REFUSED invalid_type at path hidden (received object). safeValidateSchema: true/false OK; string REFUSED root invalid_union with the app arm carrying the hidden issue; envelope REFUSED the same way. Controls: BaseSchema and ButtonSchema accept string and envelope on hidden; safeValidateSchema accepts a button with the string.", "after_1d3074f8": "runtime identical (validator unchanged); declaration moved: AppComponentSchema['hidden'] is boolean | undefined (invariant Equal pinned), a predicate string on a typed app document is a compile error (ts-expect-error pinned)." }, "files_changed": [ "packages/types/src/app.ts (+44: hidden?: boolean with docblock, AppComponentSchema block only)", "packages/types/src/__tests__/zod-mirror-parity.test.ts (-28 row+docblock, header count line 42/63 to 41/62 + history sentence, one pointer repointed)", "packages/types/src/__tests__/app-hidden-catalogue-flag-7542.test.ts (new, 265 lines, 8 tests)", ".changeset/app-hidden-catalogue-flag-7542.md (@object-ui/types: minor)" ], "gates": [ {"command": "pnpm --filter '@object-ui/types^...' build", "exit": 1, "head": "669d71bf", "verdict": "ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT — closure is @object-ui/test-support with no build script; nothing to build (n/a, not red)"}, {"command": "pnpm --filter @object-ui/types build", "exit": 0, "head": "1d3074f8", "verdict": "dist completeness: 1 package(s) complete (124 emitted files verified)"}, {"command": "pnpm --filter @object-ui/types type-check (tsc, tsconfig.examples.json, tsconfig.test.json)", "exit": 0, "head": "1d3074f8", "verdict": "clean"}, {"command": "pnpm exec vitest run --maxWorkers=2 packages/types/", "exit": 0, "head": "1d3074f8", "verdict": "Test Files 126 passed (126) · Tests 2233 passed (2233)"}, {"command": "pnpm --filter @object-ui/types lint", "exit": 0, "head": "1d3074f8", "verdict": "270 problems (0 errors, 270 warnings) — pre-existing no-explicit-any, none in the four touched files"}, {"command": "node scripts/check-changeset-presence.mjs", "exit": 0, "head": "1d3074f8", "verdict": "3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/app-hidden-catalogue-flag-7542.md"}, {"command": "node scripts/check-changeset-fixed.mjs · check-changeset-no-major.mjs · check-changeset-overwrite.mjs", "exit": "0 / 0 / 0", "head": "1d3074f8", "verdict": "green (No changeset declares a major bump; No pre-existing changeset was modified or deleted)"}, {"command": "node scripts/check-control-bytes.mjs", "exit": 0, "head": "1d3074f8", "verdict": "check-control-bytes: OK (scanned 6342 tracked text file(s); skipped 85 binary)"}, {"command": "pnpm check:spec-symbols · check:doc-types · check:doc-fences", "exit": "0 / 0 / 0", "head": "1d3074f8", "verdict": "green"}, {"command": "node /home/user/objectstack/scripts/pm/check-governed-merges.mjs --test (final four paths)", "exit": 0, "head": "objectstack d701e65", "verdict": "0 of 4 path(s) hit the register — NOT governed"}, {"command": "pnpm --filter @object-ui/app-shell type-check (read-only consumer sweep)", "exit": 2, "head": "6bd44196", "verdict": "NOT MEASURED — 934 TS2307 'Cannot find module @object-ui/components…' errors: app-shell resolves 29 sibling packages via dist declarations absent in a fresh worktree; zero errors mention hidden or AppComponentSchema; declared narrowing, CI measures it"}, {"command": "node scripts/check-doc-links.mjs", "exit": "not run", "head": "-", "verdict": "no docs row moved"}, {"command": "CI on PR #7782", "exit": "in_progress", "head": "1d3074f8", "verdict": "not awaited (report at PR-open time)"} ], "tests": "Targeted (pre-merge, 6bd44196): vitest app-hidden-catalogue-flag-7542 + zod-mirror-parity → 2 files, 26 tests passed. Final union at 1d3074f8 under the shared verify lock (shared-box seconds, 33s held): build 0, type-check 0, vitest packages/types/ 0 (126 files, 2233 tests). Ablation at 6bd44196 under the lock, trap-restored, absolute paths, blob-hash proof — Leg A: app.ts checked out from 669d71bf (git hash-object 14d12b7c = base blob, 'hidden?: boolean;' count 0, docblock marker count 0) → tsc -p tsconfig.test.json exit 2 with exactly 3 errors: app-hidden-catalogue-flag-7542.test.ts(99,3) TS2344 'false' does not satisfy 'true' (the Equal leg), (186,5) TS2578 unused ts-expect-error, zod-mirror-parity.test.ts(1849,14) TS2322 '\"app.zod.ts#AppComponentSchema\"' is not assignable to 'never' (the parity file naming the pair); vitest on the two files stayed green 26/26 (mutation is on the TS face; runtime legs measure the mirror — expected direction, reported as observed); restored via git checkout HEAD -- PATH: hash b4010c1e = HEAD blob, count 1, git diff HEAD empty. Leg B: the KnownDrift row put back from 669d71bf with the face narrowed (hash 6a35c73e = base blob, row count 1) → tsc exit 2 with 1 error: zod-mirror-parity.test.ts(1871,14) TS2322 naming the pair; vitest exit 1, 1 failed | 25 passed (the pin's ledger leg); restored: hash 9147eee6 = HEAD blob, row count 0, git diff HEAD empty, git status clean. Resolution path is source (../app, ../zod/app.zod, tsconfig.test.json) — no dist in the loop, dist preflight n/a.", "mcp_calls": "9 — create_pull_request, add_issue_comment (claim), issue_read get_labels on the PR number (failed: not resolvable as an Issue), pull_request_read get (body read-back), issue_read get_comments (claim read-back), issue_write update (label union), pull_request_read get (label read-back), add_issue_comment (this report), issue_read get_comments (report read-back). Issue body + triage + PM claim read through the zero-quota public-page payload (frontTimelineItems hasNextPage false, 15 items); REST read probe returned 403 (GitHub access not enabled for this session).", "deviations": [ "REST channel 403 for this session: reads went git → public payload; label write went MCP union with compare read-back instead of the additive REST endpoint; declared here as the channel switch.", "app-shell read-only type-check NOT MEASURED locally (dist closure of 29 packages absent in the fresh worktree; build does not fit the foreground cap); substitute evidence: grep sweep — no source in app-shell/layout/console authors hidden on an app-typed document, the only AppComponentSchema consumer (useNavigationSync.ts) writes no hidden, and filterActiveApps reads hidden !== true.", "Ablation revert target was 669d71bf, not 16a725f9 as the dispatch spelled it: app.ts blob is identical at both (14d12b7c), so the leg is equivalent; the parity file was reverted to 669d71bf for leg B.", "PR body ends with the system-reminder attribution form (🤖 Generated with … + session URL line); the platform appended a second session-URL footer on create (the known create-time behaviour) — recorded, not repaired.", "The vitest half of leg A stayed green — the dispatch's 'the parity suite goes red naming the pair' was observed under tsc -p tsconfig.test.json (type-level LedgerMismatch), not under vitest; both instruments reported." ], "open_questions": [ {"question": "Direction 2 (rename the app-catalogue flag upstream in @objectstack/spec so the app node can inherit the renderer's predicate) remains open to the maintainer; nothing in this PR precludes it.", "options": ["A — leave direction 1 landed and file/keep an upstream spec card for the rename", "B — veto direction 1 and take direction 2 first (cross-repo, with producers)"], "recommendation": "A, because the in-repo readers and the spec producer already treat the key as a boolean, direction 1 is one member + one row + one pin and reversible by one revert, and direction 2 is a protocol change with its own producers"} ], "out_of_scope_findings": [] }Generated by Claude Code
os-justin commentedon Sep 5, 2026
Contract review — PASS at
CONTRACT_REVIEW_TIER(in-seat, 2026-09-05T17:50Z) — PR #7782, head1d3074f8Implemented-by: dev subagent of this session's dispatch, branch
claude/issue-7542-app-hidden-catalogue-flag(mode:subagent, no session of its own); dev report 5553657368Reviewed-by:
session_01BAZFhALsQsGqxui8sNqM8s(domain:spec@ objectui seat; served tier read viaget_sessionat 17:49Z:session_context.modelandlast_served_modelbothclaude-fable-5-1)Diff read by ref against
origin/main1b69ca58(four files, +324 / −30; theapp.tshunk, the parity-file hunk, the changeset and the pin's describe/it list re-read off the branch tip withgit diff origin/main...at 17:49Z;app.zod.tsuntouched — verified, 0 lines).① Derived judgments — each accept-set and public-surface change named and judged
packages/types/src/app.ts,AppComponentSchemablock only:hidden?: boolean;restated with a docblock naming the collision (specAppSchema.hiddenapp-catalogue flag taken by reference throughSpecAppFieldsvsBaseSchema's renderer predicate), the spec reading through the installed pin (AppSchema.shape.hidden=z.boolean().optional(), the spec's description quoted;visible/disabledabsent — which explains why finding(types): BaseSchema under-declares visible (predicate string) and ariaLabel (I18nLabel) — the renderer supports both, five test fixtures now cast past the gap #4581 / finding(types): two competing SchemaNode declarations — core's interface vs types' union #4580's widenings reached this node and finding(types,docs):BaseSchema.hiddenis declaredboolean, but the renderer evaluates it as a predicate — the third key onvisible/disabled's evaluated path, never widened #7455's did not), both refusals measured before the edit (safeParseandsafeValidateSchema), the in-repo reader (filterActiveAppskeeps an app out of the launcher onhidden !== true, never evaluates), direction 1 as the ruling and direction 2 as the open alternative. Every other member kept. Accept set: the TS face narrows on one node (a predicate string / CEL envelope on a typedappdocument is now a compile error); the validator's accept set does not move — it refused those spellings at pathhiddenbefore and after, on both entry paths, measured withBaseSchema/ButtonSchemacontrols that still accept them. This is direction 1 exactly as claimed: the declaration pulled back to what the validator has enforced all along; ⛔ direction 3 not taken (app.zod.tsuntouched, verified),BaseSchema.hiddenuntouched on both faces.zod-mirror-parity.test.ts: theKnownDriftrow'app.zod.ts#AppComponentSchema': 'hidden'and its docblock deleted in the same PR — correct, the drift is gone (both facesboolean), and leg B of the ablation proves the ledger refuses the deletion alone (row restored with the face narrowed → TS2322 naming the pair + the pin's ledger leg red). Header counts42 / 63→41 / 62with a history sentence ADDED in the cumulative style; the dangling "see that entry" pointer re-pointed. No conflict with PR test(types): re-derive ObjectViewSchema's side of the #6058 split as LOCAL and pin the header counts (objectui#7279) #7776's regions (merged before opening, clean). Triage's hard prohibition honoured.app-hidden-catalogue-flag-7542.test.ts(8 tests): invariantEqualofAppComponentSchema['hidden']withboolean | undefined;BaseSchema['hidden']stillboolean | ExpressionWire | undefinedandButtonSchema['hidden']equal to it (controls); a@ts-expect-erroron a predicate string authored on a typed app document; the mirror acceptingtrue/falseand refusing the string and the envelope AT PATHhiddenon both entry paths; controls onBaseSchema,ButtonSchemaand the union path with abutton; the spec reading live through the pin with control keyactive; theKnownDriftrow absent off disk beside a control that the reader sees the ledger and the pair stays inMIRRORS. Ablation: leg A (app.tsreverted to the base blob by hash) →tsc -p tsconfig.test.jsonexit 2 with exactly three errors (theEqualleg, the unused@ts-expect-error, the parity file's TS2322 naming the pair) while the runtime legs stay green — the right asymmetry for a TS-face-only change, reported as observed; leg B as above; restores proven by blob hash,git diff HEADempty. Adequate and red-first on both instruments.hiddenon the app node (the threeapp-schema.mdxhits areAppMenuItem.hidden) — none moved;check-doc-linksnot run for that reason. Accepted.② semver —
.changeset/app-hidden-catalogue-flag-7542.md:@object-ui/types: minor, naming the collision, the ruling direction, the TS-only breaking edge (a predicate on anappnode no longer type-checks; the app node cannot use the predicate spelling), that authored JSON metadata is unaffected (the validator never admitted it), the reader's agreement, and the removedKnownDriftrow. A TS-face narrowing is theminorclass this seat has graded all shift; consistent. The card's own requirement (triage boundary 3: write down the new fact that the app node does not support the predicate spelling) is met in the changeset, the docblock and the PR body.③ Boundary flags —
open_questions: one — direction 2 (upstream rename) stays open; the dev recommends A (leave direction 1 landed, keep an upstream card if the maintainer wants it). That is the veto-window arrangement the claim declared (5553481421), recorded in the decision batch as item 8; no new maintainer item. Deviations reviewed and accepted (REST 403 channel fallbacks with read-backs; the app-shell read-only type-check NOT MEASURED — a fresh worktree lacks the 29 sibling dists, 934 unrelated TS2307s, zero mentioninghidden/AppComponentSchema, with the grep sweep standing in and CI's farm measuring it; ablation revert target669d71bfwhoseapp.tsblob equals16a725f9's; thepnpm --filter '@object-ui/types^...' buildnon-red — the closure has no build script). No out-of-scope findings. Nothing touchescontent/docs/releases/.Verdict: PASS for PR #7782. Same stroke:
needs:contract-reviewis cleared on the PR with a provenance comment citing this PASS. Landing follows once every check run on1d3074f8iscompleted: success(farm started 17:45Z; Type Check, four shards, Lint, Doc Snippet Type Check, Bundle Analysis, Skill Example Check, README Export Check in progress at 17:48Z) — flip re-check armed. Head moves ⇒ the label is re-hung and the delta re-reviewed.Generated by Claude Code
os-justin commentedon Sep 5, 2026
Landing — PR #7782 (2026-09-05T18:01Z). Pre-landing three: ① flipped ready at 18:01Z after the PASS above (comment 5553667716) and the carrier cleared on the PR with provenance (PR comment 5553670229); every check run on
1d3074f8wascompleted: successorskippedat 18:00Z (32 runs), branch tip re-read1d3074f8at 18:00:52Z; ② auto-merge enabled, method SQUASH (the API echoes an empty method string — known platform reading); ③ queue probe running againstrefs/heads/gh-readonly-queue/main/*(the queue was empty at 18:00Z after PR #7777 landed;origin/main=554f2b6b). This PR carriesFixes #7542: the card closes on its squash;pm:dispatchedis stripped once MERGED is read twice (PRmerged: true+origin/maincontaining the squash). Direction 1 lands under the veto window declared on the claim; direction 2 (upstream rename) stays open to the maintainer — recorded in the decision batch as item 8.Generated by Claude Code
os-justin commentedon Sep 5, 2026
MERGED (2026-09-05T18:17Z) — PR #7782 landed through the merge queue as squash
2fcefb95onmain. Three readings: PRmerged: true(merged_at18:16:31Z); this card'sclosed_by_pull_requestsreference to PR #7782 readsMERGEDand the card closed at 18:16:32Z;git log origin/mainat 18:17:10Z shows2fcefb95 feat(types): restate AppComponentSchema.hidden as the spec's app-catalogue boolean (objectui#7542) (#7782)at the tip.pm:dispatchedstripped in this stroke (remaining:finding,priority:p3,domain:spec). Direction 1 is onmainunder the veto window declared on the claim (5553481421): a maintainer objection reverts it by one revert (the restated member, the removedKnownDriftrow, the pin, the changeset); direction 2 (renaming the app-catalogue flag upstream in@objectstack/spec) stays open and is recorded in the seat's decision batch as item 8. Sessionsession_01BAZFhALsQsGqxui8sNqM8s.Generated by Claude Code
os-justin commentedon Sep 6, 2026
Maintainer ruling recorded — direction 2 = A: direction 1 stays landed; ⛔ no upstream rename card. The veto window declared on the claim is now closed.
Recorded by the
domain:spec@ objectui seat, sessionsession_01BAZFhALsQsGqxui8sNqM8s, 2026-09-06T02:30Z. Provenance (who / verbatim / where): the maintainer (os-justin), live PM chat with this session, replying to decision batch 2 of 2026-09-05T23:1xZ, in which this card was item 8 — tabled with the seat's recommendation A and the four-facet analysis in Chinese. Verbatim reply: 「第二批(4 项,契约形状族) 帮我处理,全部同意」.What this settles. PR #7782 (
2fcefb95, merged 2026-09-05T18:16Z) landed direction 1 under a declared veto window:AppComponentSchema.hiddenis restated on the TypeScript face as the specAppSchema's app-catalogue boolean, matching what the validator has always enforced; theKnownDriftrow is gone because the drift is gone. The window closes with this ruling — direction 1 is now a decision, not a default awaiting objection.Direction 2 is declined, on the record: no card is filed upstream in
@objectstack/specto rename the app-catalogue flag so the app node could inherit the renderer'shiddenpredicate. The four facets as tabled: no measured pull (the only in-repo reader,filterActiveApps, testshidden !== true; the spec producer writes a boolean; nothing asks to hide an app by expression), one name one meaning is already achieved, the TypeScript face and the validator now agree so an authored predicate is a compile error at the site, and a protocol change with its own producers for a hypothetical need is capability expansion the startup-focus rule refuses.Consequence for authors, stated so it is not re-reported as a bug: on an
appnode,hiddenis the app-switcher catalogue flag and takestrue/falseonly; the predicate spelling every other node accepts (hidden: "user.role == 'admin'", or the CEL envelope) is refused on both faces there. That is recorded in the member's docblock, in PR #7782's changeset and now here. The card stays closed; no label change (finding,priority:p3,domain:specretained).Generated by Claude Code