docs(cli,examples): name the live owner-key helper in three stale comments, and converge the one bare 'global' key beside them - #14876
Conversation
…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
📓 Docs Drift CheckThis PR changes 1 package(s): 20 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 22 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # 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
|
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:
packages/cli/src/commands/lint.tspackages/cli/test/lint-namespace-prefix.test.tsexamples/app-showcase/test/actions.test.ts.changeset/cli-action-dedup-global-key-constant.mdpackages/cliis 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-treefirst: a stale cwd exits 128 and a||fallback would turn that into a convincing false negative).At
origin/mainf3ae441, overpackages/objectql/src/plugin.ts, the file that used to define it: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:
standaloneActionObjectNamestill exists inpackages/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 isreturn standaloneActionOwnerKey(action);. Lockstep-by-docblock was replaced by one implementation. All three comments now say that.packages/cli/src/commands/lint.ts— the rationale forPREFIXED_TYPES'actionsentry. Now: the object half is resolved bystandaloneActionOwnerKey, the single implementation the ObjectQL plugin calls directly and the runtime re-exports, falling back toGLOBAL_ACTION_OBJECT_KEY.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.examples/app-showcase/test/actions.test.ts— the object-less specimen's mirror of the engine key. TheAppPluginhalf of that sentence is left alone:app-plugin.tsstill resolvesaction.objectelse the global key, andaction-governance.ts:53still characterisesAppPluginin 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:registryKey: (item, name) =>built${...objectName... : 'global'}:${name}.'global'spellings inpackages/runtime/src/action-execution.tsafter #14422 converged the plugin's — the runtime side kept the literal the constant exists to replace #14678 established, not the spelling. That value becomes the object half of theobjectName:actionNameengine registration keyexecuteActionlooks up. [finding] Three residual bare'global'spellings inpackages/runtime/src/action-execution.tsafter #14422 converged the plugin's — the runtime side kept the literal the constant exists to replace #14678 measured 626 occurrences of the token across 160 files sharing at least five unrelated vocabularies; this is not one of theownerLabelcases (hook-body-lowering.ts,lower-callables.ts) where'global'becomes a handler-name prefix or a diagnostic string.GLOBAL_ACTION_OBJECT_KEYis exported from@objectstack/objectql's entry (src/index.ts:8, present in the builtdist/index.d.ts) and@objectstack/clialready declares that dependency.check:undeclared-dep-importsis green with the new import (specifier count 1869 to 1870, still declared).No behaviour moves:
GLOBAL_ACTION_OBJECT_KEYis'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-changesetwas the right fork for the comments-only diff:packages/cli'sfileswhitelist 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 publisheddist/and adds a runtime import to it — so ⛔ that fork no longer applies. Apatchchangeset for@objectstack/cliis 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 offf3ae441.Gate family, derived not recalled.
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsfrom the real change set (not a hand-builtgit difflist) 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: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_limitanswers 200 with 15000 left while every repo-scoped path answers 403).Tests and typecheck.
validate-build-gate-parity.test.tsis in that list on purpose: it is the one test that readslint.tsas source text. It matchesnormalizeStackInput(...)/onConversionNoticeand thelintFoo(/validateFoo(call-site regex, none of which these edits touch — and it passes.@objectstack/connector-*, all four withdistabsent. Building them made the same command green. Recording it because an unbuilt dependency reads exactly like a broken import.packages/cli'stsconfig.jsonisinclude: ["src"], sopackages/cli typecheck: Donesays nothing about the edited test file — it coverssrc/commands/lint.tsonly. The showcase program does reach its test file:tsc --noEmit --listFilesfindsapp-showcase/test/actions.test.tsin 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#isPathIgnoredovergit ls-filesgives 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 oneeslint.config.mjsthat 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.cli×2,client×2,runtime×2), PR refactor(objectql,runtime): give the standalone-action owner-key ladder one spelling #14667's own changeset,packages/objectql/src/action-governance.ts:78andpackages/runtime/src/action-owner-key-single-source.test.ts:10— every one of these names it in the past, as a writer that was converged away — pluspackages/objectql/src/action-owner-key-single-source.test.ts:6/:69/:72, the weld that asserts its absence (expect(plugin!.text).not.toContain('actionObjectKey')). ⛔ Editing that one deletes the check.packages/spec/src/stack.zod.ts:381and:1572name it in the present tense as the registration-key reader. Filed as [finding] Twopackages/spec/src/stack.zod.tscomments name the deletedactionObjectKeyas the LIVE registration-key reader — the two sites #14669's face did not reach #14873 rather than fixed:stack.zod.tsis the spec's schema source, so editing it pulls the spec gate farm into this PR's affected set — a new verification surface, not an adjacent one. Note for whoever takes it: only theactionObjectKeyhalf rots there;collectBundleActions, named in the same clause, is live atapp-plugin.ts:1878.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-changesetandcheck-changeset-no-majoralready establish the mechanics — both diffHEADagainst 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.mdand.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.tspins 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
packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsintermittent hang (os devin an unbuilt workspace sometimes HANGS instead of exiting 2 when its reader goes away — measured at 180103 ms against a 7046 ms calibration on the same runner #14832) was neither hit nor touched — that file is outside this face and was never run.mainwith no overlap:git merge-treereports 0 conflicts and none of the 4 files is touched by those commits.🤖 Generated with Claude Code
Generated by Claude Code
Generated by Claude Code