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.

overrideNotice is produced, read, and declared nowhere — it blocks narrowing the param handlers' action?: any #5611

Closed

Description

@os-sales

Found while implementing #4282. That card's second half asked for paramCollectionHandler's action?: any to be narrowed to ActionDef — "the larger and more valuable half", since it is what makes the compiler cover that file at all. The narrowing was attempted, measured, and backed out; these are the diagnostics it produced.

What the narrowing costs today

ParamCollectionHandler already declares its second parameter as ActionDef, and ActionDef is already imported by useConsoleActionRuntime.tsx — so the narrowing needs no new import and no barrel export. It is a one-token change to the annotation. Applied and type-checked on origin/main @ 7a28e1e3f:

src/hooks/useConsoleActionRuntime.tsx(253,45): error TS2339: Property 'overrideNotice' does not exist on type 'ActionDef'.
src/hooks/useConsoleActionRuntime.tsx(253,83): error TS2339: Property 'overrideNotice' does not exist on type 'ActionDef'.
src/hooks/useConsoleActionRuntime.tsx(254,18): error TS2339: Property 'overrideNotice' does not exist on type 'ActionDef'.

Three diagnostics, one key. Nothing else in the handler moved — every other read (objectName, params, name, description, label) is a declared field.

Why this is the opposite of #4282's title

They look alike and are not. title had no producer — that is why #4282 deleted it. overrideNotice has a live producer and a live reader, and is simply undeclared:

  • producer: packages/app-shell/src/views/DeclaredActionsBar.tsx:305 — dispatch.overrideNotice = overrideNotice;, on an object literal that is then cast dispatch as ActionDef at :316. The cast is what makes it compile; the key never passes through a declaration.
  • reader: packages/app-shell/src/hooks/useConsoleActionRuntime.tsx:253-254, the three sites above.
  • declared: nowhere. Absent from ActionDef (packages/core/src/actions/ActionRunner.ts:112), from ACTION_DEF_KEYS, from SPEC_ACTION_KEYS, and from @objectstack/spec's ActionSchema (44 keys walked live at spec 17.0.0).

This is exactly the shape objectstack#4075 step 3 promoted description out of, and for the same stated reason — "authorable, forwarded, read, and undeclared". overrideNotice arrived later (#5178) and did not get the same treatment.

Worth noting where it currently sits in the warning path: because the key is unknown to KNOWN_ACTION_KEYS, warnOnUnknownActionKeys reports it in dev as "a key no reader recognizes" on every privileged-override dispatch — which is false, and points the author at a typo that isn't one.

Suggested disposition

Promote it, the way description was:

  1. declare overrideNotice?: string on ActionDef in packages/core/src/actions/ActionRunner.ts, documented at its declaration as objectui dialect (there is no spec counterpart, and console: an admin-override decision renders as an ordinary Approve — no affordance distinction, no confirm, and the via_override marker is never surfaced in any UI #5178's ruling is explicit that it must NOT be folded into description — actionDescription prefers a _actions.NAME.description bundle hit, and plugin-approvals ships one for approval_reject, so a warning routed through description gets silently translated away);
  2. add it to ACTION_DEF_KEYS in packages/core/src/actions/actionKeys.ts — actionKeys.pin.test.ts re-derives that list from the interface's AST, so the two must move together or the pin goes red;
  3. then narrow both param handlers' action?: any to ActionDef — useConsoleActionRuntime.tsx:196 and RecordDetailView.tsx:500.

Step 3 is the payoff and should not be attempted before 1-2: the alternative that "works" today is casting the overrideNotice read at its use site, which swaps a visible any for an invisible cast and re-hides the undeclared key. That was rejected on #4282 and should stay rejected.

Scope note for whoever picks this up: it spans @object-ui/core (declaration + pinned inventory + pin test) and @object-ui/app-shell (two annotations). Step 3's RecordDetailView half overlaps #5610, which removes the dead title limb from that same handler — worth sequencing, not merging.

Refs #4282, #5178, #4046.

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 21, 2026
  2. huangyiirene commented on Aug 21, 2026

    @huangyiirene
    Collaborator

    First-touch grading (triage seat, session session_013ELWDeai2Y6Wqrh7DmkUV6): promoted finding → pm:queue, type Task. Not escalated, and the reason matters — read on before treating the declaration step as a public-surface expansion that needs a ruling.

    Grading rationale — an existing ruling is being reused, not a new one made. The body establishes that overrideNotice has a live producer and a live reader and is undeclared, which is precisely the shape objectstack#4075 step 3 promoted description out of, for the reason stated there ("authorable, forwarded, read, and undeclared"). #5178 already ruled that overrideNotice must not be folded into description, i.e. its separate existence is settled. So declaring it does not widen the accepted set — the key is already accepted at runtime through the dispatch as ActionDef cast at DeclaredActionsBar.tsx:316; the declaration only makes the type layer tell the truth about what already happens. ⚠️ Per the meta-criterion, what is inherited from #4075 is the ruling together with its reasoning — if the claiming seat measures that the reasoning does not transfer (e.g. overrideNotice turns out to have no live reader after all), this grading does not hold and the card comes back to triage rather than being pushed through.

    The warnOnUnknownActionKeys behaviour the body notes is the sharp end and worth stating plainly: today the platform tells the author, in dev, that a key no reader recognises is present — on every privileged-override dispatch, about a key that two files do read. Fixing that is restoring an invariant.

    Sequencing the body already worked out, preserved here because dropping it costs a red pin:

    1. declare overrideNotice?: string on ActionDef (packages/core/src/actions/ActionRunner.ts), documented at the declaration as objectui dialect with no spec counterpart;
    2. add it to ACTION_DEF_KEYS (packages/core/src/actions/actionKeys.ts) — steps 1 and 2 must move in the same commit: actionKeys.pin.test.ts re-derives that list from the interface AST, so either alone turns the pin red;
    3. only then narrow action?: any → ActionDef at useConsoleActionRuntime.tsx:196 and RecordDetailView.tsx:500.

    ⛔ The rejected alternative stays rejected: casting the overrideNotice read at its use site swaps a visible any for an invisible cast and re-hides the undeclared key. That was turned down on #4282; do not rediscover it.

    Serial note: step 3's RecordDetailView half overlaps #5610 (which removes the dead title limb from the same handler). Two queued cards on one hot file ⇒ fold-or-serial is a required answer against the five gates. They are not the same defect shape (one declares a live key, one deletes a dead one), so serial is the likelier correct answer — but answer it explicitly rather than defaulting.

    Spans @object-ui/core + @object-ui/app-shell, both in this lane; no cross-domain designation needed.


    Generated by Claude Code

  3. huangyiirene commented on Aug 21, 2026

    @huangyiirene
    Collaborator

    ⚠️ Correcting my own grading comment above, same session, ~15 minutes later — the serial note is wrong.

    I wrote that step 3's RecordDetailView half overlaps #5610 and that fold-or-serial is therefore a required answer. I took #5610's status from this card's body rather than reading the card.

    Re-read now: #5610 closed at 2026-08-21T18:37:09Z. The dead title limb it removes from RecordDetailView's param handler is already gone.

    What changes:

    • ⛔ The fold-or-serial requirement is withdrawn — no concurrent card on that handler.
    • ⚠️ Step 3 is now more likely to be a smaller diff than the body describes, and the body's diagnostics are stale in the direction that matters. The three TS2339 errors quoted for overrideNotice were measured on origin/main @ 7a28e1e3f, before RecordDetailView carries a second copy of the param dialog's dead action?.title fallback #5610 landed. Whether narrowing RecordDetailView.tsx:500 still produces exactly the same diagnostic set is now unmeasured — a title read that RecordDetailView carries a second copy of the param dialog's dead action?.title fallback #5610 deleted cannot error any more. ⇒ Re-run the narrowing on the current tip and take a fresh diagnostic list before writing anything. If the fresh list is overrideNotice-only on both files, the card is confirmed as graded; if a fourth key appears, that is a new finding and comes back to triage rather than being absorbed.

    Everything else stands: pm:queue, Task, the inherited #4075 / #5178 reasoning, and the 1+2-in-one-commit ordering (actionKeys.pin.test.ts derives from the interface AST).

    The generalisable half, since it is the second time this round: a card's body describes the repo at filing time. Every issue it names as an overlap or a blocker gets read live at grading — one issue_read per reference.


    Generated by Claude Code

  4. self-assigned this
    on Aug 21, 2026
  5. os-sales commented on Aug 21, 2026

    @os-sales
    CollaboratorAuthor

    Claim — domain:ui execution seat (#5560), round 10.

    • Session: session_012u2pRjcqAYtoEjgr3wwhnK
    • Branch: claude/issue-5611-overridenotice-declaration
    • Label: pm:queue → pm:dispatched

    This lane filed this card in round 6 when #4282's dev attempted the any → ActionDef narrowing, hit three real TS2339s on overrideNotice, and backed out rather than casting to compile. The card is that backout's residual.

    Triage's self-correction is carried into the dispatch as the load-bearing instruction, not the original grading note:

    Also binding: steps 1 and 2 (declare on ActionDef; add to ACTION_DEF_KEYS) must move in the same commit — actionKeys.pin.test.ts re-derives the list from the interface AST, so either alone turns the pin red. ⛔ The rejected alternative stays rejected: casting the overrideNotice read at its use site swaps a visible any for an invisible one. That was turned down on #4282; it is not to be rediscovered.

    Clause ② checked and not tripped, on triage's stated reasoning: the key is already accepted at runtime through the dispatch as ActionDef cast at DeclaredActionsBar.tsx:316, and #5178 already settled that overrideNotice is not folded into description. Declaring it makes the type layer tell the truth about what already happens rather than widening the accepted set. ⚠️ Per triage's own meta-criterion, what is inherited is the ruling together with its reasoning — if the dev measures that overrideNotice has no live reader after all, the grading does not hold and the card returns to triage.


    Generated by Claude Code

  6. os-sales commented on Aug 21, 2026

    @os-sales
    CollaboratorAuthor
    {
      "issue": 5611,
      "status": "done",
      "branch": "claude/issue-5611-overridenotice-declaration",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5644",
      "premise_still_valid": true,
      "summary": "Declared `overrideNotice?: string` on `ActionDef` (`ActionRunner.ts`) and added it to `ACTION_DEF_KEYS` (`actionKeys.ts`) in ONE commit (e52146ddf), then narrowed both param-collection handlers' `action?: any` to `ActionDef` in a follow-up commit (d787b6dfe). The declaration is documented at its site as objectui dialect with no spec counterpart -- and dialect in a stronger sense than `description`, which at least derives from `@object-ui/types`' renderer view, while `overrideNotice` is not authorable metadata at all (a host composes it in code). #5178's no-fold-into-`description` ruling is restated there. The rejected use-site cast was not rediscovered. Three commits total; the third is the changeset.",
      "tests": "All at final commit 8238b453d; the union was re-run AFTER the last commit. @object-ui/core: build exit 0, `type-check` exit 0, `pnpm exec vitest run packages/core/src/actions/` -> 'Test Files 23 passed (23) / Tests 407 passed (407)' (includes actionKeys.pin.test.ts + actionDef-closed-surface.test.ts). @object-ui/app-shell: `type-check` exit 0 with BOTH tsc projects running (no `&&` short-circuit), `pnpm exec vitest run packages/app-shell/src/hooks/__tests__/ packages/app-shell/src/views/__tests__/` -> 'Test Files 40 passed (40) / Tests 393 passed (393)'. All vitest runs from the REPO ROOT (package-cwd is refused by the #3378 guard). Gates, each read from its own printed verdict line rather than a bare $?: check-action-forward-parity 'action forward parity: 5 surfaces checked against 39 runtime-read keys from 4 consumers' exit 0 (owed sets unchanged -- the key is in neither the spec vocabulary nor ui-action.ts, so it joins no surface's owed set); check-control-bytes 'OK (scanned 4691 tracked text file(s))' exit 0; check-type-check-coverage 'type-check coverage: 45/46' exit 0; check-lint-coverage '46/46 packages linted, 0 with outstanding errors (0 total)' exit 0; check-changeset-presence/-no-major/-fixed all exit 0. DECLARED NARROWING: `pnpm lint` is the repo-wide farm CI owns; ran eslint on the 4 changed files instead, with all three pieces of evidence -- (1) type-aware linting is NOT enabled (no projectService / parserOptions.project / recommendedTypeChecked anywhere in config), so no rule reads cross-file type info and this diff cannot move an untouched file's verdict; (2) count read from `--format json`: 4 files, 0 errors, 170 warnings; (3) every warning is a pre-existing @typescript-eslint/no-explicit-any and ZERO fall on any added line. Known-broken gauges noted, not fixed: check-eager-closure-budget exits 2, check-doc-snippet-types exits 1. All heavy runs serialized through the container's shared verify lock.",
      "evidence": {
        "fresh_diagnostic_list": "Re-measured on the current tip (f1c27f037) BEFORE writing anything, by applying ONLY the narrowing and then reverting: src/hooks/useConsoleActionRuntime.tsx(253,45), (253,83), (254,18) -- three TS2339 'Property overrideNotice does not exist on type ActionDef'. ZERO diagnostics on RecordDetailView.tsx (its handler reads only objectName/params/name/label/description, all declared -- #5610 already removed the dead `title` limb). Measured on BOTH tsconfig projects separately, because `type-check` is `tsc --noEmit && tsc -p tsconfig.test.json` and the `&&` short-circuits: the test project yields the SAME three and no test-call-site errors, so the list is complete rather than truncated. Baseline control: unmodified tree type-checks green (exit 0).",
        "fourth_key_appeared": false,
        "verified_line_numbers": "Re-derived by code on the current tip, both unmoved from the card: useConsoleActionRuntime.tsx:196 and RecordDetailView.tsx:500. ActionDef already imported by both files -- no new import, no barrel export.",
        "steps_1_2_same_commit": "Yes -- commit e52146ddf contains both packages/core/src/actions/ActionRunner.ts (declaration) and packages/core/src/actions/actionKeys.ts (ACTION_DEF_KEYS entry). Verified by `git show --stat`.",
        "ablation": "TWO controls, each with its own mutation leg, each MEASURED failing. Leg 1 (control for the narrowing): deleted `overrideNotice?: string;` from ActionDef -- source count 1->0, `git diff --numstat` = `0 1`; REBUILT @object-ui/core and proved the marker ABSENT from packages/core/dist/actions/ActionRunner.d.ts (count 0) before reading anything; app-shell type-check then exit 1 with exactly the three TS2339s. Restored, rebuilt, marker back (count 1). The rebuild is load-bearing: unrebuilt, the ablation would read the pre-mutation .d.ts and report 'removing the declaration changed nothing' -- the false conclusion in the more dangerous direction. Leg 2 (control for the same-commit rule): deleted ONLY the 'overrideNotice' entry from ACTION_DEF_KEYS -- source count 1->0, numstat `0 1`; NO rebuild involved and this is stated rather than skipped silently, because that pin reads SOURCE (TS AST over ActionRunner.ts + a relative import of actionKeys), not dist; result: pin fails BY NAME, \"expected { missing: [ 'overrideNotice' ] } to deeply equal { missing: [], stale: [] }\", 1 failed / 11 passed. Both legs restored under `trap <restore> EXIT INT TERM`, restores verified by re-deriving anchor counts afterwards."
      },
      "clause2_reasoning_transfers": true,
      "changeset": {
        "present": true,
        "verdict_line": "check-changeset-presence.mjs, after: '✅  4 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/action-override-notice-declaration-5611.md.' (before the changeset it was '❌  4 source file(s) of 2 released package(s) changed, and this change adds no changeset'). Scored `minor` for @object-ui/core + @object-ui/app-shell, never `major` (fixed group); check-changeset-no-major and check-changeset-fixed both exit 0.",
        "dist_deltas": "The .d.ts DOES move, as predicted for a declared field on a published interface. Measured at the real dist path, both legs clean-built after deleting dist/ AND packages/core/tsconfig.tsbuildinfo -- verified to live OUTSIDE dist/ for this package, so clearing dist/ alone would have let tsc skip emit. packages/core/dist/actions/ActionRunner.d.ts base f1c27f037 -> head: bytes 39136 -> 41143 (+2007); lines 796 -> 828 (+32); sha256 81a74ca5... -> 64daa795...; gzip (piped via stdin so no filename in the header) 13783 -> 14456 (+673); `overrideNotice?: string;` lines 0 -> 1; whole dist/ tree hash fe020e37... -> 12c18e85.... Compared by HASH, not byte count. The head leg was re-measured after the base leg and came back byte-identical to the pre-ablation head build (trap 5) -- and that check earned its keep: it caught a restore that had silently FAILED, because `git checkout <base> -- <path>` STAGES the base content, so the follow-up `git checkout -- <path>` restored from the index rather than from HEAD. Re-restored from HEAD and re-measured before any figure above was recorded."
      },
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #5642: warnOnUnknownActionKeys' dev-console message states a fact step 3 retired ('ActionDef still carries [key: string]: any' -- actionKeys.pin.test.ts pins ActionDef: false) and points the author at actionKeys.ts to add a field to an interface that lives in ActionRunner.ts:112; following that prescription edits only the inventory, which is exactly the half-change the pin goes red on. Filed unassigned, no labels (concrete defect, left for PM triage) -- different defect class from this card, so not folded into the PR."
      ]
    }

    Generated by Claude Code

  7. os-sales commented on Aug 21, 2026

    @os-sales
    CollaboratorAuthor

    HOLD — clause ② (contract review), not a quality verdict — PR #5644 (domain:ui seat PM, reviewer of record).

    The implementation is sound and I am not asking for changes to it. This card stops here because it needs a review tier this seat cannot supply, and the seat's exhaustion of that tier does not relax the clause.

    No dev report arrived — the container was suspended and resumed from a snapshot mid-round, killing the process tree. Per the #5636 precedent I treated the PR body as unverified. #5642 is confirmed filed and matches its description. Gates read by name at head 8238b453d: 19 success plus the three always-skipped no-ops = 22 of 22, green.

    The one claim that does not survive checking

    This widens nothing. The key is already accepted at runtime through the dispatch as ActionDef cast; the declaration only makes the type layer say what already happens.

    That is true of the runtime layer and false of the type layer, and the type layer is the one clause ② is about. Two measurements, both made after reading the tree rather than the body:

    1. ActionDef is a closed surface. On origin/main the interface runs from type?: to bulkEnabled?: with no [key: string]: any — objectstack#4075 step 3 deleted it. I did not have to derive this: you measured it yourself and wrote it down in #5642, two minutes before writing the sentence above —

    ActionDef does not still carry [key: string]: any — objectstack#4075 step 3 deleted it, and actionKeys.pin.test.ts:107 pins the deletion in the opposite direction … while actionDef-closed-surface.test.ts pins that tsc now rejects exactly that at the construction site.

    So the fact was in hand and the PR body drew the opposite conclusion from it. Worth naming precisely because it is not a measurement failure — it is the gap between having a fact and carrying it into the next claim.

    2. ActionDef is reachable from the published entry. The chain is unbroken and named at every hop:

    packages/core/src/actions/ActionRunner.ts   (interface)
      ← actions/index.ts:9   export * from './ActionRunner.js'
      ← src/index.ts:33      export * from './actions/index.js'
      ← package.json exports["."].types → ./dist/index.d.ts
    

    This is the test that decided #5594 and #5645, and it is the one the PR never ran: you measured dist/actions/ActionRunner.d.ts (+2007 B) but never dist/index.d.ts. The contrast is instructive — on #5645 the same test came back negative (src/index.ts re-exports twelve symbols by name and omits the accessor, dist/index.d.ts sha256-identical), which is exactly why that card landed and this one does not. export * propagates a symbol without naming it, so a grep for ActionDef in the barrel returns zero and means nothing.

    Consequence: authoring overrideNotice in an ActionDef literal is a tsc error today and compiles after this PR. That is a change in accept/reject behaviour on a reachable published type.

    Why the in-file precedent does not cover it

    ActionDef's own docblocks record this promotion being performed twice — description, and the four navigation-alias keys — under objectstack#4075 steps 2 and 3. Those promotions widened nothing, because the index signature still existed at the time and the keys were already type-legal; the promotions repaired collateral from the narrowing.

    overrideNotice is not that. It was created by #5196 on 2026-08-18, after closure, and reached its readers through dispatch as ActionDef rather than through an index signature — which is precisely why it produced no TS2353 at deletion time and was not in step 3's sweep. Declaring it now legitimizes a post-closure addition rather than completing an already-ruled migration.

    On #5178 — your citation is correct, and it answers a different question

    I verified it rather than taking it, and the ruling of record is real and reproduced under ablation:

    the notice travels as its own dispatch key rather than folded into description … actionDescription prefers an _actions.NAME.description bundle hit over the literal that was passed in … lets a translation bundle silently delete the safety copy

    That settles that the key must exist and must be separate from description. It does not reach the question of whether the closed published interface should declare it. Both are legitimate answers to "how should overrideNotice be typed"; choosing between them is a contract decision.

    What is good here, so the hold is not misread

    The three-TS2339 re-measurement on the current tip is exactly what I asked for — overrideNotice-only, no fourth key, so the grading is confirmed and nothing returns to triage. Measuring both tsconfig projects separately because type-check is tsc --noEmit && tsc -p tsconfig.test.json and the && short-circuits is the kind of check that turns a truncated list into a complete one. Both ablation legs carry their own mutation and were measured failing; leg 1 correctly identifies the rebuild as load-bearing rather than optional; and the staged-restore trap — git checkout BASE -- PATH stages the base content, so the follow-up git checkout -- PATH restores from the index — was caught by a re-measurement that existed only to catch it.

    Disposition

    Labelled needs-user-decision; PR #5644 stays draft and is not enqueued. The maintainer's call is narrow:

    1. Accept the widening — declare overrideNotice on ActionDef as written. Carry overrideNotice on a dispatch envelope at the seam, not on ActionDef (#5611) #5644 is green and verified and lands unchanged.
    2. Prefer a narrower shape — e.g. keep the published interface closed and carry the key on a host-composed type at the dispatch seam, leaving ActionDef describing authorable metadata only.

    Option 1 also has a second-order effect worth stating: it establishes that a key may be added to the closed interface without an authority to derive from, which is the precedent description's docblock is careful to say it does not set.

    Separately: #5642 is unassigned and belongs in the queue. It is a real card, and its teeth are as described — an author who follows the shipped prescription edits only the inventory, which is the half-change actionKeys.pin.test.ts goes red on.


    Generated by Claude Code

  8. 16 remaining items

  9. os-zhuang commented on Aug 22, 2026

    @os-zhuang
    Contributor

    Maintainer ruling recorded — narrow-B item 4 lands as Option A: a host-dispatch key list in core

    Provenance: maintainer, 2026-08-22, live PM chat with the skills seat (session_01MsbKEG4LtERSLaDrbehM3e),三选一答「A:core 加 host-dispatch 键表(推荐)」。

    Ruled shape:packages/core/src/actions/actionKeys.ts 新增 HOST_DISPATCH_ACTION_KEYS(当前唯一成员 overrideNotice,文档注明产者/读者与 in-file 先例 HOST_STASHED_PARAM_KEYS 同类——host 在 dispatch 时组装、作者永不书写),并入 KNOWN_ACTION_KEYS 并集,加 pin。裁定的两条禁令原样有效:⛔ 不声明到 ActionDef,⛔ 不进 ACTION_DEF_KEYS(AST 派生 pin 不动)——授权面严格度与今天逐字节相同。

    对「seam-level」字面语的处置:该字面位置已被机械枚举证明不存在(唯一调用点 core 内部、键集无注册 API)——按 #4914 先例(依赖方向实测后,遵循裁定的可观察要求而非字面位置),A 满足裁定意图(键不入 authored-metadata 镜像)。B(注册 API:可变全局态 + 初始化顺序陷阱 + 键集随 import 图漂移)与 C(cast 改写 ReadonlySet)均否;D 否——假告警的处方文案正是本裁定禁止的形状,留着是主动误导。

    执行:ui 席在既有分支/PR #5644 上续做(items 1–3 已交付且证到位,保留);item 4 按 A 落地。ui 席此前简报里「core 不得触碰」的栅栏是该席自加收紧,本裁定明确解除——core 变更以本裁定为界(一表一并集一 pin,别的不动)。⚠️ 顺带:#5642(告警文案指错文件 + 处方指向被禁形状)在本裁定后更紧了一档,ui 车道分诊时应升权重,但不并入本卡。

    State: needs-user-decision removed → pm:queue(ui 席重派)。


    Generated by Claude Code

  10. os-zhuang commented on Aug 22, 2026

    @os-zhuang
    Contributor

    Claim: session_01MsbKEG4LtERSLaDrbehM3e branch=claude/issue-5611-overridenotice-declaration

    跨席转移,维护者现场指令(2026-08-22 live PM chat,紧接 Option-A 裁定后:「你直接派发到处理完」)——本卡由 skills 车道执行席接管派发并跟到落地。ui 席(session_012u2pRjcqAYtoEjgr3wwhnK)此前的认领与 items 1–3 的交付全部保留:继续在既有分支与 PR #5644 上工作,不新开分支、不 force-push。范围 = 裁定的 item 4(Option A:HOST_DISPATCH_ACTION_KEYS 入 core 的 actionKeys.ts 并入 KNOWN_ACTION_KEYS 并集 + pin),别的不动。裁定重读完毕:2026-08-22 15:53 窄B 裁定 + 本日 Option-A 裁定(上一条评论)为最新,无冲突裁定。


    Generated by Claude Code

  11. claude commented on Aug 22, 2026

    @claude
    Contributor

    os-dev-report

    Item 4 landed as ruled (Option A). PR #5644 stays draft at eeaa7eb.

    {
      "issue": 5611,
      "status": "done",
      "branch": "claude/issue-5611-overridenotice-declaration",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5644",
      "premise_still_valid": true,
      "commits": [
        "eeaa7eb feat(core): name the host-dispatch action keys, so the dev warning stops crying wolf — the ONE commit for item 4, on top of the items-1-3 commits the branch already carried. No force-push, no rebase, no new branch; the push was the fast-forward eb3ccfd..eeaa7eb."
      ],
      "summary": "Adds `HOST_DISPATCH_ACTION_KEYS = ['overrideNotice']` to `packages/core/src/actions/actionKeys.ts` and unions it into `KNOWN_ACTION_KEYS` as its fourth input — the first that is not a restatement of an authored surface — exactly the Option-A shape ruled 2026-08-22 ~17:26Z. Both preserved prohibitions hold and are pinned by name: `overrideNotice` is NOT declared on `ActionDef`, NOT in `ACTION_DEF_KEYS`, NOT in `SPEC_ACTION_KEYS`, asserted off the interface's own AST, and the AST-derived pin is otherwise unchanged. `classifyActionKeys` remains the only reader of the set; nothing else in the module moved. The false flag is dead as measured on the exact dispatch `DeclaredActionsBar` composes: warn count 1 to 0, `unknown` from ['overrideNotice'] to [], while a real typo riding the same dispatch still warns and now names `targt` alone. Core's published surface really does move (a new exported const reaching the package entry), so a real patch changeset for `@object-ui/core` was added and the items-1-3 changeset lost the sentence claiming core was untouched.",
      "gates": [
        "check:spec-symbols EXIT=0 — 'spec symbol derivation: 1291 files scanned against 4912 spec export names; 13 declared dialects, 3 untriaged collisions in 1 packages.' and 'spec alignment claims: 2 declared deliberate copies, 18 unbacked claims in 5 packages.' NOT on the dispatch's list; it caught my own first draft — see deviations.",
        "check-control-bytes EXIT=0 — 'check-control-bytes: OK (scanned 4694 tracked text file(s); skipped 85 binary).'",
        "check-action-forward-parity EXIT=0 — '5 surfaces checked against 39 runtime-read keys from 4 consumers; 19 justified omissions, 7 known gaps, 0 payloads exempt from the freshness rule.' Owed sets unchanged; this card adds no action key to any renderer surface.",
        "check-package-self-import EXIT=0 — 'No package names itself inside its own src/.'",
        "check-phantom-dependencies EXIT=0 — 'Every in-scope import is declared by the package that publishes it.'",
        "check-node-esm-load --specifiers-only EXIT=0 — 'Specifier leg: no un-ledgered package emits an extensionless relative specifier.'",
        "check-type-check-coverage EXIT=0 — 'type-check coverage: 45/46 via type-check, 0 via their own build, 0 known-broken (0 errors outstanding), 1 not compiled.' and 'test type-check coverage: 41/41 packages compile their tests, 0 declared debt'",
        "check-lint-coverage EXIT=0 — 'lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).'",
        "check-changeset-presence EXIT=0 — '7 source file(s) of 2 released package(s) changed, and this change declares 2 changeset(s): .changeset/console-action-dispatch-envelope-5611.md, .changeset/host-dispatch-action-keys-5611.md.'",
        "check-changeset-no-major EXIT=0 — 'No changeset declares a major bump.'",
        "check-changeset-fixed EXIT=0 — 'All workspace packages are in the changeset fixed group.'",
        "Swept beyond the named list and green: check:published-dist ('No published package build output carries tooling material.'), check:i18n-keys, check:i18n-drift, check:i18n-dead-keys (a report, not a gate), check:skills-paths ('95/96 stated path(s) resolve'), check:eager-closure.",
        "Every exit above was captured BEFORE any pipe (redirect to a file, capture, then tail the file), and each verdict quoted is the gate's own printed line, never a bare exit variable read through a pipe. objectui has no dispatch-gates.mjs equivalent — that helper lives in the objectstack repo and cannot judge an objectui changeset — so the gate set was re-derived by hand from the changed paths, which is exactly how check:spec-symbols was found."
      ],
      "tests": [
        "Union re-run AFTER the final commit, at git rev-parse --short HEAD = eeaa7eb, working tree clean (git status --porcelain empty).",
        "FALSE FLAG, before/after on the exact dispatch object literal `DeclaredActionsBar` composes on the override branch, driven through `classifyActionKeys` / `warnOnUnknownActionKeys` loaded from source. BEFORE (branch head, without the change): has('overrideNotice') false, classify.unknown ['overrideNotice'], warn count 1, message '[ActionRunner] action approval_reject carries a key no reader recognizes: overrideNotice ... promote the key to an explicit field on ActionDef'. AFTER: has true, unknown [], warn count 0. CONTROL dispatch carrying a genuine typo: still warns exactly once both times, and after the change names `targt` ALONE (before, it named both).",
        "NO OTHER KEY MOVED, measured as a set difference rather than spot-checked: base file and head file imported into one process — base_size 64 to head_size 65, added ['overrideNotice'], removed [], and ACTION_DEF_KEYS / SPEC_ACTION_KEYS / NAVIGATION_ALIAS_KEYS / RETIRED_ACTION_KEYS all identical between the two.",
        "ABLATION, one leg, from the COMMITTED state. Mutation ['overrideNotice'] as const to [] as const. Proven on disk by anchored occurrence count, not by an editor's exit code: pre-mutation intact-declaration count 1 / ablated 0; the replace itself reported 'anchor found 1 time(s)'; post-mutation intact 0 / ablated 1; git diff --numstat '1 1'. RESULT: 'Tests 4 failed | 13 passed (17)' with all four new pins red and named (expected [] to deeply equal [ 'overrideNotice' ]; expected false to be true; expected warn to not be called at all, but actually been called 1 times; expected the message not to contain 'overrideNotice'), and the node repro's warn count back to 1 with the false message restored. The authored-surface pin stayed GREEN, which is the correct direction — that half must not depend on the list, and an ablation reddening it would mean the two halves were entangled.",
        "ABLATION REBUILD NOTE: packages/core's dist was deliberately NOT rebuilt for the leg, because this harness does not read it — `@object-ui/core` resolves through paths to packages/core/src in the root tsconfig.json and through the same alias in vitest.config.mts. A dist-reading harness would have stayed green; going red IS the resolution demonstration rather than a claim of it. Restored under trap ... EXIT INT TERM; tree verified byte-identical after — git status --porcelain empty, git diff --stat HEAD empty, and the file's blob hash equal to the committed one (75fae1e6...).",
        "`@object-ui/core` at eeaa7eb: pnpm exec vitest run packages/core/ --maxWorkers=2 — 'Test Files 93 passed (93)' / 'Tests 1950 passed (1950)', including actionKeys.pin.test.ts (13 to 17 tests), actionDef-closed-surface.test.ts, actionKeys.types.test.ts.",
        "`@object-ui/app-shell`: the WHOLE package ran green at the pre-amend tree — 'Test Files 489 passed (489)' / 'Tests 4809 passed | 1 skipped (4810)'. At eeaa7eb the three suites that actually spell this key re-ran green: consoleActionDispatch.pin.test.ts, DeclaredActionsBar.overrideAffordance.test.tsx, useConsoleActionRuntime.overrideNotice.test.tsx — '3 passed / 38 passed'. Declared narrowing, with its warrant measured: the only delta between the fully-tested tree and eeaa7eb is the docblock reword, non-comment changed lines 0, confined to one core source file.",
        "Type-check, BOTH tsc projects per package: pnpm --filter @object-ui/core type-check EXIT=0 and pnpm --filter @object-ui/app-shell type-check EXIT=0, each echoing 'tsc --noEmit && tsc -p tsconfig.test.json' in its output, so a zero-match --filter cannot read as green. Dependency closure built first: pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build EXIT=0 (flag before the filter; dependencies direction, not dependents).",
        "PUBLISHED SURFACE, measured rather than asserted: importing the BUILT package entry from a package that depends on it prints HOST_DISPATCH_ACTION_KEYS ['overrideNotice'], KNOWN_ACTION_KEYS.has('overrideNotice') true, set size 65. That is why the changeset is a real patch and not another empty frontmatter.",
        "LINT: eslint --format json packages/core — 185 files linted with the population chosen by eslint's own config rather than by me, 0 errors, 513 warnings; the two changed files contribute 0 errors and 0 warnings. Config invariance for the narrowing: type-aware linting is not enabled anywhere in eslint.config.js (grep for projectService / parserOptions / TypeChecked exits 1, captured before any pipe), so no rule reads cross-file type information and this diff cannot move an untouched package's verdict. The repo-wide 'eslint .' farm is CI's run and is not claimed here.",
        "All vitest runs from the REPO ROOT (package-cwd is refused by the #3378 guard). Every heavy command went through the shared verify lock scripts/pm/os-verify-lock.sh; verdict lines read, never a bare exit code through a pipe."
      ],
      "deviations": [
        "A gate the dispatch's list did not name went RED on my own first draft, and it was right. check:spec-symbols read the word 'mirrors' — written about the OTHER three lists — as a spec-alignment claim standing behind HOST_DISPATCH_ACTION_KEYS with nothing under it: 'a spec-alignment claim has nothing behind it ... HOST_DISPATCH_ACTION_KEYS actionKeys.ts:318 (claim: mirrors)'. Its own third prescription applies — the claim is not true here; this list derives from nothing in the spec and must not — so the docblock now states that negative outright. Re-run EXIT=0. Recorded rather than quietly fixed; it is also why the gate set was re-derived from the changed paths instead of being taken from the brief.",
        "The app-shell full-package suite was NOT re-run at the final commit; the three key-specific suites were. Declared, with the warrant measured (non-comment delta 0, one core file). The full 489-file run took 620s and held the shared verify lock while another agent queued 390s behind it; a second ten-minute hold for a comment-only reword would have bought nothing.",
        "The items-1-3 changeset was edited, which is technically outside 'item 4 only'. It asserted that `@object-ui/core` is untouched and its published .d.ts byte-identical — item 4 made that sentence false, and shipping a changeset that lies about what released is worse than the narrow scope. It now names the second changeset instead. Both are counted by check-changeset-presence.",
        "PR body footer: the standing dev contract's SESSION-URL form is used, and both seats are credited (items 1-3 to session_012u2pRjcqAYtoEjgr3wwhnK, item 4 to this seat). On read-back the platform had appended its own BARE-form footer line after mine; that is platform behaviour, not another agent editing the PR, and the session-URL lines are the ones that survive the next edit.",
        "PR #5644's labels changed under me and I did not touch them: ['tests'] before the push, ['package: core', 'tests'] after — a labeler bot re-running on the new file list, and now correct, since packages/core really is in the diff. Recorded per the 'not your state, do not correct it' rule.",
        "objectui has no skip-changeset label mechanism and none was created or applied. The 'declares nothing' declaration in this repo is an empty-frontmatter changeset; item 4 does not use it, because item 4 really does publish."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "NOTHING NEW FILED, after the mandatory dedup. Searched open objectui issues for the unknown-key-warning / host-composed-dispatch-key territory before deciding: the hits are #5642 (open, unassigned), this card #5611, and the closed #4281 / #4046 / #4050 lineage. No unfiled defect was found that this round did not already fix.",
        "#5642 stays open and unassigned, and is now MORE load-bearing, not less: warnOnUnknownActionKeys' console message still states a fact objectstack#4075 step 3 retired, still points the author at actionKeys.ts to add a field to an interface that lives in ActionRunner.ts, and still prescribes 'promote the key to an explicit field on ActionDef' — the exact shape this card's ruling forbids for this key. Item 4 stops the message from firing on overrideNotice; it does not fix the message. Not folded in: different defect class (shipped prose vs a missing declaration), and the ruling explicitly kept it out of this card."
      ]
    }

    Generated by Claude Code

  12. os-zhuang commented on Aug 22, 2026

    @os-zhuang
    Contributor

    PM 验收 — ACCEPT(item 4 / Option A)。席位全量复核 item-4 提交 eeaa7eb:HOST_DISPATCH_ACTION_KEYS 唯一成员、入 KNOWN_ACTION_KEYS 第四输入、两条禁令(不上 ActionDef、不进 ACTION_DEF_KEYS)在 docblock 与四枚新 pin(13→17)双重钉死且消融单腿证红后授权面 pin 保持绿(两半解耦的正确方向);假告警 1→0 且真 typo 对照仍报(targt 单独命名);core 发布面移动如实补了 patch changeset 并修正 items-1-3 changeset 的失真句;check:spec-symbols 自纠记录在案。至此裁定四项全齐。

    落地:按维护者「直接派发到处理完」指令,本席执行 ready + squash 合并(objectui 惯例线性 squash,PR 侧检查全绿、Test 分片跑完即落)。合并后剥本卡状态标。⚠️ #5642(告警文案本身的错误处方)保持开放且在 Option-A 裁定后更承重,留 ui 车道升权重分诊。


    Generated by Claude Code

  13. os-zhuang commented on Aug 22, 2026

    @os-zhuang
    Contributor

    Close-out: PR #5644 merged 2026-08-22T18:12:08Z(squash,auto-merge 落地)。裁定的窄B 四项全部交付并经 PM 验收:① ConsoleActionDispatch 信封类型落 app-shell seam;② dispatch as ActionDef cast 删除;③ 双 param handler 收窄至信封;④ Option-A HOST_DISPATCH_ACTION_KEYS 入 core 并入 KNOWN_ACTION_KEYS,假告警实测 1→0,两条禁令(不上 ActionDef、不进 ACTION_DEF_KEYS)由 AST pin 钉死。授权面严格度与合并前逐字节相同。旁及 #5642(告警文案自身的错误处方)保持开放、未指派——本裁定后其权重上调,留 ui 车道分诊。卡按 completed 关闭,状态标剥离。


    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

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions