Repository navigation
docs(skills): the platform skill counts the eight generator barrels, picklists included - #21337
Conversation
…picklists included The blank starter wires eight barrels since the picklist generator landed (`src/picklists` under the `picklists` stack key). The template sentence in skills/objectstack-platform/SKILL.md still said seven and listed seven; it now says eight and names `picklists`, net zero lines. Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
…ill-generator-barrels
Contract reviewServed-tier: Inputs read: card #21018 (body and all 10 comments: claim Checks on Shape: the branch is one authored commit ① Derived judgments(a) The sentence is true at (b) Published rule text: nothing false, nothing newly ambiguous. Each of the eight names is both a (c) The sweep, re-done independently over all 65 files under
② Semver levelThe diff publishes nothing from any released package: ③ Boundary flags
Implemented-by: VERDICT: PASS |
维护者速读
改了什么平台技能包
为什么改上一个 PR(#21167,已合并为 风险与代价(含回滚)
席位意见建议批准。理由:
你要做的在 PR #21337 上点一次 Approve(你或 os-zhuang 任一账户)。批准记录到位后,由席位转 ready、入队落地,你无需再操作。 |
… Node, re-arming the reroute probe CONTROL leg (objectstack-ai#21356) Fixes objectstack-ai#21308 Clause-②: no ## What this changes `tsx` 4.23.15 moved its ESM-API registration out of a `dist/` chunk and into `dist/esm/api/index.cjs`, two directories deeper. It did not rebase three file-relative specifiers. On a Node without tsx's `module.registerHooks` path, the CommonJS build of `tsx/esm/api` then calls `module.register()` with a hooks URL that names `dist/esm/api/esm/index.mjs`, a file that does not exist. `@oclif/core`'s `ts-path` is the caller in this repo. It swallows the error, logs `Could not find tsx`, and stays on `dist/`. This PR adds a `pnpm patch` of `tsx@4.23.15` that points the three specifiers back at the files 4.23.14 resolved. No version moves. - `patches/tsx@4.23.15.patch`: new, written by `pnpm patch` / `pnpm patch-commit`. - `pnpm-workspace.yaml`: the `patchedDependencies` entry and its note, next to the existing `tsup@8.5.1` entry. - `pnpm-lock.yaml`: regenerated by `pnpm install`, not edited by hand. - `packages/cli/test/published-entry-node-env-source-reroute.test.ts`: a docblock pointer only. No leg, assertion or bound changes. ## Measured ### H0: CI's reading of the CONTROL leg is NOT MEASURED (log hosts unreachable) Both log hosts refused this session at CONNECT (`gateway answered 403`): - `productionresultssa14.blob.core.windows.net`, the job logs of all six `Test Core` shards of run 36964705223 on 6091136; - `results-receiver.actions.githubusercontent.com`, the run log zip. `node scripts/pm/ci-failure.mjs --run 36964705223` exited 2 with `job log NOT RETRIEVED`. What was measured instead is the mechanism that decides it. tsx 4.23.12, 4.23.14 and 4.23.15 all take the synchronous `registerHooks` path only on Node 22.22.3+, 24.11.1+, 25.1.0+ or 26+ (version table `[[22,22,3],[24,11,1],[25,1,0],[26,0,0]]`). That path never reaches the broken specifier. Node 22.22.3 was released on 2026-05-13. CI's `setup-node` asks for `node-version: '22'`, and the dev containers run v22.22.0. Same tree (6091136), unpatched tsx: | Node | `published-entry-node-env-source-reroute.test.ts` | |---|---| | v22.22.0 | 1 failed (CONTROL), 4 passed | | v22.22.3 (official tarball, sha256 checked against `SHASUMS256.txt`) | 5 passed | So CI arms the leg if its runner resolved `'22'` to 22.22.3 or later. The runner's actual version is in the log this session could not read. ### H1: confirmed, with a version bound `DEBUG=oclif:config:ts-path` on v22.22.0, CONTROL preload, the test's own env (no `NODE_PATH`): ```text Could not find tsx. Skipping tsx registration for .../packages/cli. Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../tsx/dist/esm/api/esm/index.mjs' imported from .../tsx/dist/esm/api/index.cjs ``` Next it logs `Cannot find module 'ts-node'`, and `tsPath` returns the `dist/` path. In the tarballs, 4.23.12, 4.23.13 and 4.23.14 keep the `register()` call in `dist/register-*.cjs`, where it resolves. 4.23.15 inlines it into `dist/esm/api/index.cjs`, which grows from 1,298 to 16,770 bytes. The `.mjs` build of `tsx/esm/api` is unaffected. oclif reaches the `.cjs` build because it locates the module with `require.resolve`. ### H2: holds `npm view tsx dist-tags` gives `latest` as 4.23.15 (2026-09-20). There is no newer release, so no UP move (remedy d) exists. ### A second casualty of the same defect On v22.22.0 with unpatched tsx, `bin/run-dev.js` also lands on `dist/commands`. That file is the CLI's SOURCE entry, the one `runServe` and the other spawning tests launch (104 test files under `packages/cli` mention it). `tsx bin/run-dev.js --version` under `DEBUG=oclif:config:ts-path` logs `Could not find tsx` and never `Found source directory`. With the patch it logs `Found source directory for .../dist/commands at .../src/commands`. So on such a Node, source-entry tests have measured the build output rather than `src/` since objectstack-ai#21162. The same patch fixes this with nothing added. ## Why (b), and not (a) or (c) **(a)** pins tsx down to 4.23.14. That is a DOWN move and needs a ruling, and measurement does not make it the only sound remedy. Not taken. **(c)** reworks the CONTROL leg. To arm without oclif's own tsx registration, the child would need a test-only change to how it resolves `tsx/esm/api`: a `Module._resolveFilename` shim in the preload, or `--conditions=import` on the child. That fails the second acceptance criterion as measured. The absence legs do not load the preload, so on v22.22.0 with unpatched tsx, deleting the declaration leaves them green (row 4 below). Arming them too would put the shim in every leg, and then every leg measures a resolution no real install has. **(b)** patches the component that fails. The test's behaviour is unchanged, and both acceptance criteria hold on v22.22.0 and on v22.22.3. Four axes: - **Real business need:** measured. Two consumers break on Node below 22.22.3. One is this probe's CONTROL leg. The other is the source entry `bin/run-dev.js`, which is the larger one, because the CLI's spawning tests silently ran `dist/`. The published CLI is not reached: `bin/run.js` sets `settings.enableAutoTranspile = false`, so oclif never registers tsx there, and nothing in `packages/**` source imports `tsx/esm/api`. - **Long-term soundness:** the patch is a workaround for an upstream packaging defect, and it has a cost. It replaces one 16 KB minified line, which nobody can review by eye. The token comparison below is how to review it. The key names the exact version, so a tsx bump fails `pnpm install` (`ERR_PNPM_UNUSED_PATCH`, the same rule the `tsup@8.5.1` entry records), and the patch cannot outlive its reason silently. Retire it when a tsx release resolves the three paths itself, or when the toolchain's Node floor reaches 22.22.3. (c) would carry a test shim with no such tripwire. After an upstream fix it would become dead code that still makes every leg diverge from a real install. - **Making AI mistakes harder:** (b) gives the probe one meaning in every environment. Today the same file is green in CI and red in the dev containers, and three devs reported that red in passing, without a card, because it read as "environment". (c) adds a second resolution dialect inside a test fixture, which is the consumer-side tolerance that contract-first rules out. - **Startup focus:** no new gate, no new dependency, no version movement. Three tracked files, plus the lockfile the tooling wrote. ## CONTROL leg and reverse verification (Node v22.22.0 unless stated) | tree | tsx | declaration in `bin/run.js` | `development` / `test` legs | CONTROL (development, neutralised) | |---|---|---|---|---| | 6091136 (main) | unpatched | present | pass | FAIL: output is only `@objectstack/cli/17.6.0 linux-x64 node-v22.22.0` | | cc0c05b | patched | present | pass | pass: card signature present, exit non-zero | | cc0c05b | patched | deleted | FAIL x2: `expected '(node:...) [MODULE_NOT_FOUND] Warni...' not to contain 'Cannot find module './registry''` | pass | | cc0c05b, `packages/cli/node_modules/tsx` linked to the pristine 4.23.15 | unpatched | deleted | pass (vacuous: 1 failed, 4 passed) | FAIL | | 8569d5f (final) | patched | present | pass | pass (5 passed) | | 8569d5f, Node v22.22.3 | patched | present | pass | pass (5 passed) | `scripts/ablation-replace.mjs --delete` removed the declaration (anchor 1 to 0, blob `569e6b6f` to `40a368ce`). The same tool restored it and showed the blob equal to HEAD's, with `git diff HEAD` empty. The tsx link swap ran under an EXIT/INT/TERM trap, and `readlink` confirmed the restore. `bin/run.js` runs unbuilt, so no `dist/` step is involved. ## Reviewing the patch The patch replaces one minified line. Review it with this comparison, which splits the pristine and the patched file on commas and prints every token that differs: ```sh npm pack tsx@4.23.15 && tar xzf tsx-4.23.15.tgz node -e 'const fs=require("fs");const a=fs.readFileSync(process.argv[1],"utf8").split(","),b=fs.readFileSync(process.argv[2],"utf8").split(",");let n=0;for(let i=0;i!==a.length;i++)if(a[i]!==b[i]){n++;console.log("-",a[i].slice(-60));console.log("+",b[i].slice(-60))}console.log(n,"of",a.length,"differ")' package/dist/esm/api/index.cjs node_modules/.pnpm/tsx@4.23.15_patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6/node_modules/tsx/dist/esm/api/index.cjs ``` Output: ```text - se=[new URL("loader.mjs" + se=[new URL("../../loader.mjs" - new URL("esm/index.mjs" + new URL("../index.mjs" - essageChannel;R.register(`./esm/index.mjs?${_.randomUUID()}` + e.MessageChannel;R.register(`../index.mjs?${_.randomUUID()}` 3 of 514 comma-separated tokens differ ``` The `register()` specifier is the one that breaks things. The two `new URL(...)` entries feed tsx's `isTsxImport` set and carry the same wrong base. They are fixed in the same patch because they are the same defect, in the same file, with the same mechanical change. Both rebased targets exist on disk (`dist/esm/index.mjs`, `dist/loader.mjs`). No test here reaches those two entries, because no child in this suite passes a TypeScript preload ahead of an absolute tsx loader path. ## Lockfile `pnpm install` regenerated it. With the tsx patch hash normalised away, every removed line equals an added line, except for the three new `patchedDependencies` lines. 0 versions moved, 0 DOWN, 0 packages added or removed. Ten snapshot keys change only by the hash suffix: `tsx` itself, and the peer suffix of `tsup`, `postcss-load-config`, `fumadocs-mdx`, `vite` x2, `vitest` x2 and `@vitest/mocker` x2. `pnpm install --frozen-lockfile` passes on the final head. ## Verification - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` at 8569d5f derives 58 commands from 4 paths. All 58 were run with exit codes captured before any pipe, and all exited 0. `--ran` reports 58 derived, 58 run, 0 NOT-MEASURED. (In an earlier pass at 6a22902, `check:dual-build-cjs-loads` answered PREREQUISITE NOT MET while sibling packages had no `dist/`. Once those were built it passed: 105 entry points across 66 packages.) - `@objectstack/cli` unit tier at 6a22902: 244 files, 3461 tests passed. - `@objectstack/cli` integration tier at 6a22902, two runs under the verify lock: 71 files, 607 tests passed, 1 skipped. The skip is the existing `it.skipIf` in `test/migrate-meta-default-range.test.ts`, a file this branch does not touch. - `pnpm --filter @objectstack/cli typecheck` at 6a22902: exit 0, `check:test-typecheck` included. - 6a22902 to 8569d5f is two merges of `main`: objectstack-ai#21317 (plugin-security), objectstack-ai#21335 (objectql, rest, plugin-auth), and two docs-only commits (objectstack-ai#21337, objectstack-ai#21343). None touches a CLI file, the lockfile, `pnpm-workspace.yaml` or tsx, so the CLI tiers were not re-run for them. The packages the merge touched (objectql, plugin-auth, rest) were rebuilt. Then the reroute file (5 passed on v22.22.0, 5 passed on v22.22.3) and all 58 gates were re-run on 8569d5f. - Not run here: `pnpm lint` (repo-wide, CI's run). **skip-changeset:** nothing published moves. `@objectstack/cli` ships only `dist`, `README.md` and `CHANGELOG.md`, so the test file is not published. `patches/`, `pnpm-workspace.yaml` and `pnpm-lock.yaml` are root files no package ships. The CLI's dependency range on tsx is unchanged. ## Acceptance notes - The dev containers run Node v22.22.0, while CI's `setup-node` floats on `'22'`. That gap is why the same file read red in three dev containers and green in `Test Core`. Observation only, no card. Carrier: none. - Upstream: whether tsx already has an issue or fix in flight was not read, because this session cannot reach `privatenumber/tsx` through the API. The retirement condition is in the `pnpm-workspace.yaml` note. - Published reach: `@objectstack/cli` depends on `tsx ^4.23.15` at runtime, so a customer on Node 22.0 to 22.22.2 installs the defective build. No published code path calls `require('tsx/esm/api')`, so no public entry point reaches it. Measured by a grep of `packages/**` source and by the published entry's `enableAutoTranspile = false`. - `packages/cli/README.md` and `packages/cli/package.json` belong to objectstack-ai#21310 and are not touched. --- _Generated by [Claude Code](https://claude.ai/code/session_018gA1pE6eJtwHhqx72G8U9X)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #21018
Clause-②: no
This PR is item 4 of #21018, the last item on the card: the Tier H line in
skills/objectstack-platform/SKILL.mdthat counts and lists the blank starter's generator barrels. The code half landed onmainasbcd68a29f3(PR #21167), and since that landing the sentence has been false. The claim for this PR is5945859013on the card; the dev session issession_01VvcEokUG1tvVxkceYfR5XB.Premise, checked on
origin/main(1caa603730, then merged222ecc27f9)packages/create-objectstack/src/templates/blank/objectstack.config.tsimports eight barrels and hands each to its stack key, in this order:objects,views,actions,flows,dashboards,apps,skills,picklists.src/of the template holds the same eight directories.os initderives its wiring from the roster:SCAFFOLD_WIRED_BARRELS(packages/cli/src/commands/init.ts:602) mapsGENERATOR_SCAFFOLD_TARGETS, which isObject.entries(GENERATORS)ingenerate.ts— eight generators:object,view,action,flow,dashboard,app,skill,picklist.packages/cli/test/create-objectstack-wiring-parity.test.tsholds the blank template's import lines and stack-key lines equal to theos initrendering, so the template list and the roster cannot drift apart.picklistslast — the order the template file uses. The SKILL.md line said seven and listed seven.What changed
One file, two lines, net zero lines:
skills/objectstack-platform/SKILL.md:194-195.Before:
After:
This is the text PR #21167's "Not in this PR" section proposed, byte for byte.
The
skills/**readings (both halves)1caa603730)82e06107cf)skills/objectstack-platform/SKILL.md, linesskills/objectstack-platform/SKILL.md, tokens (ceil(utf8 bytes / 4), the ratchet's convention)skills/**/SKILL.md, linesskills/**/SKILL.md, tokensThe 13 added bytes are the word
eightforseven(same length) plus, \picklists`. No re-wrap, no content removed, no ceiling moved.node scripts/check-skills-token-ratchet.mjsreadsskills/objectstack-platform/SKILL.md is 5830 tokens (ceiling 5833; headroom 3)on82e0610`.The sweep of
skills/**Every file under
skills/was grepped for a count word (six to nine) near barrel / generator / template / scaffold, for the wordbarrel, forpicklist, and forsrc/directory listings. OnlySKILL.md:194-195states a count or list that PR #21167 made false. The other hits, each left alone:skills/objectstack-platform/SKILL.md:210-235, the "Project Structure Conventions" tree: a generic convention listing (objects,views,apps,flows,actions,dashboards,reports,datasets,i18n,handlers, each marked optional). It never enumerated the template's barrels (it omittedskillsalready), so it states no count or list that is now wrong.skills/objectstack-platform/references/bootstrap.md:55-72and:163-180: illustrative config examples with four barrels and one barrel. Examples, not a roster.skills/objectstack-platform/evals/config-plugins-ops.json:7: an eval's expected output for a CRM config ("barrel imports … same for views / flows"). Not a roster.skills/objectstack-platform/SKILL.md:44-45: the stack-key list already namespicklistsandpicklistExtensions. Correct.Changeset
None, declared with the
skip-changesetlabel. The diff publishes nothing from any released package: nopackage.jsonunderpackages/orapps/names a skills path in itsfiles[](positive control:@objectstack/spec'sfiles[]listsdist,json-schema, …), andcreate-objectstackinstalls the catalog at scaffold time withnpx skills add objectstack-ai/objectstack/skills(packages/create-objectstack/src/skills-install.ts), reading this repository directly rather than a bundled copy.Gates
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, run with no paths, derived 25 families from the change set (1 path). The list is identical before and after theorigin/mainmerge.82e06107cf, each exit code captured before any pipe: 25 exit 0.--ranreconciliation:✓ dispatch-gates --ran: 25 derived famil(ies) accounted for — 25 run, 0 NOT-MEASURED.84fc471b7fthe same 25 were run once before: 24 exit 0 and onePREREQUISITE NOT MET(exit 3,@objectstack/lintcheck:doc-formula-expressions, the package was not built). Afterpnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lintunder the verify lock it exited 0; the merged-head run above includes it green.pnpm --filter @objectstack/spec run check:skill-examples. It refused withPREREQUISITE NOT MET(exit 3:packages/client-react/distholds no declarations). NOT MEASURED locally; it is declared to CI. The diff changes nots/tsxfence, which is the only surface that gate reads.scripts/pm/check-skill-line-ratchet.mjsis not applicable: its header excludes the publishedskills/catalog by design; the token ratchet above is the catalog's gate.pnpm lintis CI's run; this diff touches one Markdown file, which is outside eslint's population (eslint.config.mjslintsts/tsx/js/mjs), so nothing here moves a lint verdict.Acceptance notes
skills/objectstack-platform/references/operations.md:26reads "os generate KIND| Scaffold an object / view / flow / agent from a template" (KIND spelled there as a placeholder in angle brackets), whileskills/objectstack-ai/SKILL.md:344saysos g agentis retired. Pre-existing, not a count or list PR feat(cli,create-objectstack): os generate picklist, the src/picklists starter barrel, and a Picklists count in the metadata summary #21167 touched, and outside this claim's purpose; noted, not filed.skills/norpicklists/. It is a generic convention list with no count, so it is not false; noted for a future prose pass, not changed here.origin/main(222ecc27f9, two commits touchingscripts/pm/fleet-write/*andscripts/pm/issue-*.mjs, none touchingskills/or a gate this diff derives). The PR's net diff againstorigin/mainis the one file, +2 / −2.维护者速读(草稿)
改了什么
平台技能包
skills/objectstack-platform/SKILL.md里描述 blank 模板的那一句:把「七个生成器目录」改成「八个」,并在列表末尾补上picklists。改两行、删两行,净零行;整个技能包行数不变(4397 行),token 读数 5827 → 5830(上限 5833,余量 3)。为什么改
上一个 PR(代码半,#21167)落地后,
npm create objectstack新建的项目实际接了 8 个目录,多出的是src/picklists(共享选项列表,os generate picklist的产物)。技能文本还写 7 个:AI 读了会少认一个目录,不知道新生成的选项列表落在哪里、挂在哪个 stack 键下。风险与代价(含回滚)
只改一句说明文字,不改任何代码,不发任何 npm 包。技能目录由
npx skills add从仓库直接拉取,所以合并后新建的项目立刻读到新句子;已建项目不受影响。回滚就是 revert 这一个 commit。本地派生的 25 个门禁全绿;skills/**的 token 棘轮没动上限。席位意见
(留空,席位定稿时填写)
你要做的
在本 PR 上给一个 Approve(
skills/**是受管面 Tier H,需要维护者的批准记录);之后由席位负责落地,不用你再操作。Generated by Claude Code