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.

warnOnUnknownActionKeys's dev-console message states a fact step 3 retired, and points the author at the wrong file #5642

Closed

Description

@os-sales

Found while implementing #5611 (declaring overrideNotice on ActionDef). Filed rather than fixed there: different defect class — that card is a missing declaration, this is shipped prose that outlived the change it describes. Not folded into #5611's PR.

The dev-mode warning at packages/core/src/actions/actionKeys.ts:367-373 ends with:

'check for a typo, or promote the key to an explicit field on `ActionDef` ' +
'(packages/core/src/actions/actionKeys.ts). Warned rather than rejected because `ActionDef` ' +
'still carries `[key: string]: any`, so the compiler cannot see this (objectstack#4075).',

Two separate problems, both in text that reaches an author's console.

1. The stated reason is false

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:

}).toEqual({ ActionDef: false, ActionContext: true });

So the message tells the author the compiler is structurally unable to see an unknown key, while actionDef-closed-surface.test.ts pins that tsc now rejects exactly that at the construction site. The warning's own module header already explains the real reason it survived step 3 — the two mechanisms cover disjoint populations (tsc sees action literals authored in code, the warning sees actions arriving as data from unparsed sys_metadata rows, objectstack#3903). That is the sentence the message should be carrying; it is a genuinely good reason, and the message currently substitutes a retired one for it.

2. The prescription names the wrong file

It says "promote the key to an explicit field on ActionDef (packages/core/src/actions/actionKeys.ts)". The ActionDef interface is at packages/core/src/actions/ActionRunner.ts:112. actionKeys.ts holds the inventory (ACTION_DEF_KEYS), not the interface.

This one has teeth rather than being a typo: an author who follows the path as given edits only the inventory, which is precisely the half-change actionKeys.pin.test.ts goes red on (it re-derives ACTION_DEF_KEYS from the interface's AST, so the entry becomes stale). #5611 had to carry an explicit "steps 1 and 2 must be in the same commit" instruction to keep a dev off that exact rock; the shipped message steers onto it.

Same-root prose elsewhere

Comments that also describe the pre-step-3 world, worth sweeping in the same pass so the file stops contradicting its own pin:

  • actionKeys.pin.test.ts:9 — "ActionDef cannot be enumerated at runtime — its [key: string]: any widens keyof to string | number". The conclusion (the list stays data) is still right; the stated cause is retired.
  • actionKeys.pin.test.ts:163 — "targt type-checks today: ActionDef's index signature accepts it." It does not type-check today; the sibling closed-surface test pins the rejection.

Suggested disposition

Rewrite the message's tail to give the surviving reason (data-borne actions no compiler ever sees) and point at ActionRunner.ts for the interface, naming the inventory as the second, same-commit edit. Refresh the two comments. No behaviour change — the classification logic is correct and is not in question here.

Refs #5611, objectstack#4075, objectstack#3903, #4046.

Activity

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

    @os-zhuang
    Contributor

    Triage: pm:queue, domain:ui, Task. Shipped dev-console prose states a retired fact (the [key: string]: any reason died with objectstack#4075 step 3) and prescribes the wrong file (interface lives in ActionRunner.ts:112, not actionKeys.ts) — and the wrong pointer steers authors onto exactly the half-edit the pin test reddens on. Scope per the card's disposition: rewrite the message tail with the surviving reason (data-borne actions no compiler sees, objectstack#3903), point at ActionRunner.ts + name the inventory as the same-commit second edit, refresh the two stale comments in actionKeys.pin.test.ts. No behavior change.


    Generated by Claude Code

  3. claude commented on Aug 23, 2026

    @claude
    Contributor

    Claiming this card.

    • session id: 01EuPCi56cnGyykygi3z9w4m
    • branch: claude/issue-5642-action-keys-warning-text

    No prior claim comment with a different session id on this issue (only the triage comment above), so proceeding.


    Generated by Claude Code


    Generated by Claude Code

  4. claude commented on Aug 23, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 5642,
      "status": "done",
      "branch": "claude/issue-5642-action-keys-warning-text",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5889",
      "premise_still_valid": true,
      "summary": "Both halves of the card verified true on today's origin/main (merge base eddc1dd97) and both fixed in packages/core/src/actions/actionKeys.ts. Half 1: the message claimed ActionDef 'still carries `[key: string]: any`' — the interface (ActionRunner.ts:112-440) carries no index signature (the only ones in that file are ActionContext at :63 and ActionParamOption at :584), and actionKeys.pin.test.ts:108 pins it in the opposite direction, green today: '✓ ActionDef has NO index signature, and ActionContext still does'. The tail now carries the module header's own surviving reason (disjoint populations: tsc sees code-authored literals, the warning sees actions arriving as data from rows rehydrated unparsed, objectstack#3903) — the header's phrasing, not a third one. Half 2: the prescription pointed at actionKeys.ts (the inventory) for a field that lives on ActionDef in ActionRunner.ts; it now names ActionRunner.ts and ACTION_DEF_KEYS as the same-commit second edit, with the reason. Also swept, and named in the PR body: two more comments in actionKeys.ts (warnOnUnknownActionKeys' JSDoc, ACTION_DEF_KEYS' JSDoc) carried the same retired claim in the present tense beyond the two the card listed in the pin test; the file's other three console messages were checked and carry no index-signature claim, so they are untouched. No behaviour change — classification, KNOWN_ACTION_KEYS and the inventory derivation are unmodified.",
      "tests": "All legs on ef08b2453 (the final commit; working tree clean, `git status --porcelain` empty). Exit codes captured before any pipe (`cmd > log 2>&1; EXIT=$?`); heavy legs through scripts/pm/os-verify-lock.sh. (1) `pnpm --filter '@object-ui/core^...' build` -> 'os-verify-lock: VERDICT command-exit 0 · held the lock 8s'. (2) `pnpm vitest run --maxWorkers=2` over actionKeys.pin.test.ts + actionDef-closed-surface.test.ts + actionKeys.types.test.ts -> 'Test Files  3 passed (3)' / 'Tests  59 passed (59)', captured EXIT=0; verbose reporter shows the two new tests green ('✓ sends the author to the interface, and drops the reason step 3 retired', '✓ the file path it prints really declares the interface it prescribes'). (3) `pnpm --filter @object-ui/core type-check` -> script name echoed '> @object-ui/core@17.6.0 type-check' running 'tsc --noEmit && tsc -p tsconfig.test.json', 'VERDICT command-exit 0'. (4) `pnpm --filter @object-ui/core lint` -> 'VERDICT command-exit 0'; --format json: 191 files, 0 errors, and 0 errors/0 warnings on both changed files. (5) Gates, each quoting its own verdict line: check-changeset-presence '✅  2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major '✅  No changeset declares a `major` bump.'; check-changeset-fixed '✅  All workspace packages are in the changeset fixed group.'; check-control-bytes '✅  check-control-bytes: OK (scanned 4901 tracked text file(s); skipped 85 binary).'; check-action-forward-parity '✅  action forward parity: 5 surfaces checked against 39 runtime-read keys from 4 consumers'; check-lint-coverage '✅  lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).'; check-type-check-coverage '✅  type-check coverage: 45/46 via `type-check` ... 0 errors outstanding'. ABLATION (reverse verification, run on the committed fix with a `trap ... EXIT INT TERM` restore): re-injected the old message tail, confirmed the mutation ON DISK before measuring (grep -c of the injected text = 1, of the removed text = 0 — not an editor exit code), then re-ran the pin test: 'Tests  2 failed | 17 passed (19)', failing exactly the two new tests and naming each half ('namesTheSecondEdit' true->false, 'pointsAtTheInterfaceFile' true->false, 'repeatsTheRetiredIndexSignature' false->true; 'declaringPaths' [ActionRunner.ts]->[]). NO REBUILD was needed or run for either leg, and this is not a skipped rebuild: the pin test imports the subject as `from '../actionKeys'`, a relative SOURCE specifier that never resolves through the package exports to dist/, so no dist artifact participates. Restore leg confirmed on disk afterwards (new text count 1, retired-claim count 0, `git diff` empty against the commit). DECLARED LINT NARROWING: lint ran over packages/core (the package's own `eslint .`, exactly what `turbo run lint` runs for it) rather than the whole monorepo — population from ESLint's own config resolution, count 191 read from --format json, and the config is not type-aware (eslint.config.js extends tseslint.configs.recommended, languageOptions declares no parserOptions.project/projectService), so this diff cannot move any untouched file's verdict. `scripts/pm/dispatch-gates.mjs` does not exist in objectui and the framework copy is bound to its own checkout (from this worktree it reported 'this branch changes nothing against origin/main (merge base 95437e7d2)'), so the gate family was derived by hand from the three changed paths. CI convergence not awaited, per the dispatch contract.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    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 seatpm:queue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions