Skip to content

docs(cli,examples): name the live owner-key helper in three stale comments, and converge the one bare 'global' key beside them - #14876

Merged
os-trump merged 3 commits into
mainfrom
claude/issue-14669-actionobjectkey-stale-comments
Sep 3, 2026
Merged

docs(cli,examples): name the live owner-key helper in three stale comments, and converge the one bare 'global' key beside them#14876
os-trump merged 3 commits into
mainfrom
claude/issue-14669-actionobjectkey-stale-comments

Conversation

@os-trump

@os-trump os-trump commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes #14669

Three comments named ObjectQLPlugin.actionObjectKey, a private method PR #14667 deleted. Each is repaired to name what replaced it rather than by dropping the sentence. A fourth change rides along on a PM hand-off from #14678; it is declared in full below.

File face

Declared, and nothing outside it:

file change
packages/cli/src/commands/lint.ts comment repair, plus the #14678 hand-off (below)
packages/cli/test/lint-namespace-prefix.test.ts comment repair
examples/app-showcase/test/actions.test.ts comment repair
.changeset/cli-action-dedup-global-key-constant.md new — see the changeset fork

packages/cli is crowded right now. Checked against every armed or dispatched holder named at dispatch — #14833 (tsconfig.test.json, package.json, test-typecheck-debt.json), #14834 (src/commands/generate.ts), #14812 (src/commands/serve.ts), #14809 (src/commands/explain.ts, test/commands.test.ts), #14832 (bin/run-dev.js), #14716 (test/run-dev-unbuilt-workspace.e2e.test.ts) — and this face intersects none of them.

The premise, re-measured, with a positive control

The card's claim (a) is a negative — that the symbol is gone — so it is paired with a control on the same object, run with the identical command shape from inside an asserted work tree (git rev-parse --is-inside-work-tree first: a stale cwd exits 128 and a || fallback would turn that into a convincing false negative).

At origin/main f3ae441, over packages/objectql/src/plugin.ts, the file that used to define it:

git grep -c "actionObjectKey"           -> exit 1, no match   (the negative)
git grep -c "standaloneActionOwnerKey"  -> 5                  (control)
git grep -c "registerAction"            -> 4                  (control)
git grep -c "isArtifactShippedAction"   -> 2                  (control)
git grep -n "class ObjectQLPlugin"      -> :208               (control: the object itself is not renamed or moved)
git grep -n "private actionObjectKey" -- .  -> exit 1, no match repo-wide

Claim (b), that the three comments still name it, held verbatim at all three sites.

What each comment now says, and why that case

The live spelling is standaloneActionOwnerKey (packages/objectql/src/action-governance.ts:91), exported from @objectstack/objectql. In every case the thing the sentence explained still exists, so ⛔ none of the three was deleted — each names the replacement.

The card's warning is load-bearing and was honoured: standaloneActionObjectName still exists in packages/runtime/src/action-execution.ts, so rewriting the whole clause would make it wrong in the other direction. But its relationship changed too, and the old wording is now false in a second way nobody flagged: it was described as "kept in lockstep with" a sibling copy, and it is now a delegating alias whose body is return standaloneActionOwnerKey(action);. Lockstep-by-docblock was replaced by one implementation. All three comments now say that.

  1. packages/cli/src/commands/lint.ts — the rationale for PREFIXED_TYPES' actions entry. Now: the object half is resolved by standaloneActionOwnerKey, the single implementation the ObjectQL plugin calls directly and the runtime re-exports, falling back to GLOBAL_ACTION_OBJECT_KEY.
  2. packages/cli/test/lint-namespace-prefix.test.ts — the same rationale for the [17.0-rc2验收] lint naming/namespace-prefix 把按对象区分的同名 action 误判为包内重复 —— 规则去重键漏掉了 objectName,HotCRM 每次 lint 固定 12 条误报 #5510 dedup suite, same repair.
  3. examples/app-showcase/test/actions.test.ts — the object-less specimen's mirror of the engine key. The AppPlugin half of that sentence is left alone: app-plugin.ts still resolves action.object else the global key, and action-governance.ts:53 still characterises AppPlugin in exactly those words.

The #14678 hand-off, declared as an addition to the face

The PM seat handed over one site the #14678 seat deliberately left: lint.ts:321 (pre-edit numbering) built the same key with a bare 'global' literal, inside the docblock this PR was already editing, five lines below it — a two-line change here, a merge conflict for anyone else. That adjacency is the whole justification, so it was verified here rather than taken on report:

No behaviour moves: GLOBAL_ACTION_OBJECT_KEY is 'global' (action-governance.ts:61), so every key the table builds is byte-identical. The point is the one #14667 made about the plugin's own copy — a copy that agrees by value is the one that parts in silence the day the constant moves, with nothing able to see it.

The changeset fork, re-decided because of that

skip-changeset was the right fork for the comments-only diff: packages/cli's files whitelist is ["dist","README.md","CHANGELOG.md"], and the closed list covers comments. The hand-off changed the shape — it edits an expression that compiles into the published dist/ and adds a runtime import to it — so ⛔ that fork no longer applies. A patch changeset for @objectstack/cli is written instead, and no label is applied. That also sidesteps the size-labeler's whole-set PUT, which has been measured stripping a correct additive label about a second later.

Verification

Everything below was run at c550b205e2, the final commit, in a dedicated worktree off f3ae441.

Gate family, derived not recalled. node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands from the real change set (not a hand-built git diff list) named 38 families once the changeset existed — 30 before it, plus exactly the 8 it predicted. All 38 were run at the final commit, exit code captured before any pipe:

  • 36 green.
  • 2 NOT MEASURED, by their own verdict line, exit 3 rather than a finding's 1:
    • check-test-completeness: PREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named.
    • check-half-states: PREREQUISITE NOT MET — the transport authenticates but repo-scoped reads are refused (this seat's session gate; /rate_limit answers 200 with 15000 left while every repo-scoped path answers 403).

Tests and typecheck.

pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 \
  test/lint-namespace-prefix.test.ts test/validate-build-gate-parity.test.ts
  -> Test Files 2 passed (2) · Tests 24 passed (24)
pnpm --filter @objectstack/example-showcase exec vitest run --maxWorkers=2 test/actions.test.ts
  -> Test Files 1 passed (1) · Tests 10 passed (10)
pnpm --filter @objectstack/cli run typecheck                  -> Done
pnpm --filter @objectstack/example-showcase run typecheck     -> exit 0

validate-build-gate-parity.test.ts is in that list on purpose: it is the one test that reads lint.ts as source text. It matches normalizeStackInput(...)/onConversionNotice and the lintFoo(/validateFoo( call-site regex, none of which these edits touch — and it passes.

⚠️ The showcase typecheck was red on first run and that red was not this change: four TS2307s on @objectstack/connector-*, all four with dist absent. Building them made the same command green. Recording it because an unbuilt dependency reads exactly like a broken import.

⚠️ packages/cli's tsconfig.json is include: ["src"], so packages/cli typecheck: Done says nothing about the edited test file — it covers src/commands/lint.ts only. The showcase program does reach its test file: tsc --noEmit --listFiles finds app-showcase/test/actions.test.ts in it.

ESLint — a declared narrowing, not a skip. Run over the three edited source files with the repo's flag (--no-inline-config): 3 files, 0 errors, 0 warnings. Three readings make that a measurement rather than an omission: (1) the population comes from ESLint's own config — ESLint#isPathIgnored over git ls-files gives 5775 lintable of 5775 tracked JS/TS files, 0 ignored; (2) the file count comes from --format json, which reports 3; (3) the config cannot carry a verdict from an edited file to an untouched one — this repo runs one eslint.config.mjs that never enables type-aware linting (grep -cE "^\s*project\s*:|projectService" is 0, and the config's own docblock states it, measured there with a positive control). A comment and a local-constant substitution inside 3 files therefore cannot move the other 5772.

The sweep, and what is deliberately left

Required by the card, and it is why the count is worth stating: the card said three, and repo-wide the token is 17 at f3ae441 (git grep -n "actionObjectKey" origin/main -- .). The card's number was right for its own scope — it measured -- packages/cli examples — but the class is larger.

On making the next one loud

Asked, and the answer is yes but not in the shape this card suggests, and not in this PR — nothing was added here.

⛔ The tempting shape, a tree-wide scanner for symbol-looking tokens in comments that resolve nowhere, is disproportionate and would rot. For this one symbol, 12 of the 17 mentions are accurate history and would each need an allowlist entry on day one; separating "names a deleted thing as live" from "names it as past" is tense-reading in prose, which is not mechanizable. A gate whose green depends on a hand-maintained exception list becomes the permission slip validate-build-gate-parity.test.ts's own docblock warns about.

✅ The proportionate shape inverts it: check at removal time, from the diff, where the population is tiny and the one person who can classify a remaining mention is present. check-empty-changeset and check-changeset-no-major already establish the mechanics — both diff HEAD against the merge base. A gate in that family would take the identifiers a diff removes from an exported or class-member declaration, grep the post-image tree for each, and report hits outside **/CHANGELOG.md and .changeset/, requiring the deleting PR to repair them or record them as historical. Typical PRs remove zero exports, so it costs one grep on the rare PR and nothing on the rest.

Worth noting that PR #14667 did build a guard for this — action-owner-key-single-source.test.ts pins that the plugin no longer contains the name. It is scoped to one file, so it could not see the five comments in three other packages. That is the gap the removal-time shape closes, and it is a proper card rather than a rider here.

Residue

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…ments

PR #14667 (#14422) collapsed the standalone-action owner-key ladder onto one
implementation: `standaloneActionOwnerKey` in
`packages/objectql/src/action-governance.ts`, exported from
`@objectstack/objectql`. The private `ObjectQLPlugin.actionObjectKey` copy is
gone (0 occurrences in `packages/objectql/src/plugin.ts` at `origin/main`
f3ae441; positive controls on the same class hit: `standaloneActionOwnerKey` 5,
`registerAction` 4, `isArtifactShippedAction` 2, `class ObjectQLPlugin` 1).

Three comments outside that PR's face still named the deleted method. Each
explained something that still exists, so each now names its replacement rather
than dropping the sentence:

- `packages/cli/src/commands/lint.ts` — the composite-key rationale behind
  `PREFIXED_TYPES`' actions entry.
- `packages/cli/test/lint-namespace-prefix.test.ts` — the same rationale for
  the #5510 dedup suite.
- `examples/app-showcase/test/actions.test.ts` — the object-less specimen's
  mirror of the engine key.

Also corrected in the same stroke: `standaloneActionObjectName` is no longer
"kept in lockstep with" a sibling copy — it is a delegating alias that returns
`standaloneActionOwnerKey(action)`. Comments-only; no behaviour changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
…ACTION_OBJECT_KEY

Folded in on a PM hand-off from #14678, whose seat deliberately left this one
site: it sits inside the same docblock this PR was already editing, five lines
below it, so for any other seat it is a merge conflict rather than a two-line
change.

Verified here rather than taken on report:
- the ladder is real — `PREFIXED_TYPES`' `actions` entry built its key half with
  `typeof item?.objectName === 'string' && item.objectName ? item.objectName : 'global'`;
- the role is the right one — that value becomes the object half of the
  `<objectName>:<name>` engine registration key `executeAction` looks up, which
  is the discriminator #14678 established (626 occurrences across 160 files
  share the token across at least five unrelated vocabularies; an `ownerLabel`
  that becomes a handler-name prefix is NOT this class);
- the constant is reachable with no new dependency — `GLOBAL_ACTION_OBJECT_KEY`
  is exported from `@objectstack/objectql`'s entry (`src/index.ts`) and
  `@objectstack/cli` already declares that dependency.

No behaviour moves: the constant is `'global'`, so every key this table builds
is byte-identical. The #5510 dedup suite passes unchanged (24 tests over
lint-namespace-prefix + the validate-build-gate-parity source pin).

A changeset rides with it. The diff is no longer comments-only: it changes an
expression that compiles into the published `dist/`, so the `skip-changeset`
closed list no longer covers this shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
@github-actions github-actions Bot added the size/s label Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 2 documentable anchor(s).

20 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json f116b8f8d1b43ec2bfd64fbf0ebb0cf3c301b1c8.

4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 22 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f116b8f8d1b43ec2bfd64fbf0ebb0cf3c301b1c8packageMentionDocs.

Which tree this was computed on

This run read content/docs from 64b79ee31f3f944d8f23266df233053ae7bc267c — the merge of head a9ebe9b6105e02da469fb59605eaf2a789038100 into base f116b8f8d1b43ec2bfd64fbf0ebb0cf3c301b1c8, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 64b79ee31f3f944d8f23266df233053ae7bc267c && git checkout 64b79ee31f3f944d8f23266df233053ae7bc267c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f116b8f8d1b43ec2bfd64fbf0ebb0cf3c301b1c8 a9ebe9b6105e02da469fb59605eaf2a789038100 && git checkout -B drift-repro f116b8f8d1b43ec2bfd64fbf0ebb0cf3c301b1c8 && git merge --no-ff a9ebe9b6105e02da469fb59605eaf2a789038100

node scripts/docs-audit/affected-docs.mjs --json f116b8f8d1b43ec2bfd64fbf0ebb0cf3c301b1c8

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs f116b8f8d1b43ec2bfd64fbf0ebb0cf3c301b1c8 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-trump
os-trump added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit 79af704 Sep 3, 2026
34 checks passed
@os-trump
os-trump deleted the claude/issue-14669-actionobjectkey-stale-comments branch September 3, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Three comments in packages/cli and examples/app-showcase still name ObjectQLPlugin.actionObjectKey, deleted by PR #14667

2 participants