Repository navigation
feat(scenarios): gallery-refresh, the gallery refresh in one deterministic step - #31313
Conversation
…istic step Version check against npm's latest stable @objectstack/cli, run on that release, export into the gallery checkout, commit on export/<version>; never pushes. Driver header names ruling D2; README runbook for the scheduled task. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5pZeHgu9hYkeNdBNEA
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 0 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 6ee3ea26cc35eca99d84b78423c81dae4304df4c && git checkout 6ee3ea26cc35eca99d84b78423c81dae4304df4c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0f254f474588916ccd3d61450a35adb531d0416f de7c89c59fdbf78cd6421ea0e33309997baa5a40 && git checkout -B drift-repro 0f254f474588916ccd3d61450a35adb531d0416f && git merge --no-ff de7c89c59fdbf78cd6421ea0e33309997baa5a40
node scripts/docs-audit/affected-docs.mjs --json 0f254f474588916ccd3d61450a35adb531d0416f
|
|
domain:devx#3 · os-project-manager · session_01FEkQ5njkh3xbcDN6EtSfw7 Contract reviewServed-tier: Face: declaration. ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe declaration face owes no ④; this is the lane seat's judgment offered on the points the brief asked for.
Implemented-by: VERDICT: PASS |
|
domain:devx#3 · os-project-manager · session_01FEkQ5njkh3xbcDN6EtSfw7 席位核对Verdict: ACCEPT · head
|
Requested by Leehom · project thread
domain:devx#3 · os-project-manager · session_01FEkQ5pZeHgu9hYkeNdBNEA
Fixes #31121
Clause-②: yes — a new
gallery-refreshentry point on the published@objectstack/scenarioswidens its surface; the PR body carries the readingMetadata-protocol: none
维护者速读
@objectstack/scenarios新增gallery-refresh --gallery DIR [--cards DIR] [--model M] [--debut]。它读 npm 上@objectstack/cli的最新稳定版和画廊index.json里每个应用的framework.version;一致就只打印一行skip: …并以 0 退出,什么都不跑。不一致就在这个版本上跑所有可上画廊的卡片(generic + starter/delivery,--debut只跑第一张),导出到画廊检出目录,在本地分支export/VERSION上提交,打印分支名和现成的 PR 正文。⛔ 永不推送、不开 PR。driver.ts头部写明裁决 D2,README 加了定时任务的运行手册(含任务提示词原文)。run-scenarios、export-gallery行为不变(export-gallery的截图步骤只是抽成了一个函数)。回滚即还原本 PR。定时任务本身和画廊仓库不在本 PR 内。packages/qa/scenarios/src/export/refresh.ts(跳过规则、拒绝条件、提交步骤)、README.md新节「Refreshing the gallery」里的任务提示词、src/runner/driver.ts头部。pnpm --filter @objectstack/scenarios exec vitest run --maxWorkers=2 test/export/refresh.test.ts,预期 17 个用例全过(含「版本一致只打一行 skip」「永不推送:远端引用不变」「版本不符拒绝提交」)。What changes
gallery-refresh(src/export/refresh.ts, CLIsrc/export/refresh-cli.ts, package scriptgallery-refresh), in order, each step a refusal (exit 1, the reason on stderr) or a quiet skip (oneskip: …line, exit 0):--gallerymust be the top of a git checkout with a clean working tree (untracked files count), and itsindex.json(absent means an empty gallery) must validate as one the exporter wrote.latestdist-tag of@objectstack/cli(npm view, so the caller's npm config applies). A value that is not a plainMAJOR.MINOR.PATCHis refused.framework.versionequals it.export/VERSIONbranch exists (that refresh was pushed and awaits review). A localexport/VERSIONbranch is refused: an earlier run's commit is never overwritten.GALLERY_GUARDadmits (origingeneric, tierstarterordelivery). With--debut, only the first of them by id, and only into a gallery whose index lists no app.run-scenarios --only CARDS --modes scratch --packages published --create-version VERSION --out WORK/runs --work WORK/work --record WORK/recording.json [--model M] [--cards DIR](runnerArgv), thenexportGalleryinto the checkout with real screenshots.framework.versionis not the release, or when anything outsideapps/andindex.jsonchanged in the checkout.git switch -c export/VERSION,git add --all -- apps index.json,git commitunder the checkout's own git identity. It prints the export summary,branch:,commit:and the PR body (refreshPrBody: version, previous version, cards run, model, exported / refused / removed apps).--modelpasses through torun-scenarios --model(PR feat(scenarios): run-scenarios --model / --author-model / --ask-model, with the served model recorded per turn #31204's flag), with the same refusal of an empty or flag-shaped value.driver.tsheader names ruling D2: a local session on the developer's own login, or a scheduled cloud Claude Code task on the maintainer's subscription login. An API-billed model is still never allowed.ClaudeCodeDriver's API-key refusal is unchanged.gallery-refresh", and "The scheduled task's runbook": what it runs, what it needs, what it never does, and the task's prompt verbatim (clone the gallery, rungallery-refreshdetached and wait in the foreground, push onlyexport/VERSION, open one PR with the printed body, and on any failure comment on or open oneGallery refresh failedissue).src/export/cli.ts: the live screenshot wiring moves intoliveScreenshots(chromium?), which both CLIs call. No behaviour change.Public surface (Clause-②: yes)
@objectstack/scenarios/exportre-exportsrefresh.ts. Read with the TypeScript checker (getExportsOfModule) over the builtdist/export.d.tson this head: 84 exports, 49 values. The 15 new names (none of them onorigin/main,git grep): valuesgalleryRefresh,runnerArgv,readGalleryVersions,npmLatestVersion,refreshPrBody,galleryBranch,RefreshRefusal,FRAMEWORK_PACKAGE,STABLE_VERSION; typesRefreshOptions,RefreshDeps,RefreshOutcome,RunPlan,GalleryVersions,PrBodyInput. No name is removed or retyped, so the subpath only widens (69 → 84).refresh-cli.tsis not anexportskey and not abincommand: the card asks for neither, and the scheduled task runs from a checkout. Nothing inpackages/specmoves.Tests
test/export/refresh.test.ts(new, 17 cases). The gallery is a real git checkout (main, a README, optionally anindex.jsonand app directories) with a bareorigin. The npm reader is a stub. The run step runs the real crm card throughrunCardwith the fixture's scripted model inside the runner's ownRecordingDriver(test/export/fixture.ts), and writes the record and the recording whererun-scenarioswould. The crm card is acontrolcard, so these cases pass the exporter tests' all-eligible guard double. One case uses the real guard on the repo's cards.skip:line, calls no run, and leaves HEAD, the working tree and the remote as they were. A mixed index, an empty index and no index do not skip. A remote-trackingorigin/export/18.3.0skips with its own line.export/18.3.0withmainas its parent, touches onlyindex.jsonandapps/crm_account_with_contacts/, and is authored by the checkout's configured identity. The tree is clean, the index states 18.3.0, and the PR body names the branch, the card and the model. ⛔ Never pushes: the bare remote's refs are byte-identical before and after. A second refresh then skips.runnerArgvoutput parsed by the runner's ownparseArgsgives exactly thatcreateVersion,packages: published,modes: ['scratch'], the cards, out/work/record and the model, with iterate Run on. No model means no--model. With the real guard on the repo's nine cards, the plan is the sixstarterids, and--debutgives the first of them (asset_lifecycle).18.4, empty orv-prefixedlatest; an npm read that throws; a plain directory; a subdirectory of a checkout; an untracked file; an index that does not validate; a localexport/18.3.0;--debutover a listed app; npm at 18.4.0 while the project pins 18.3.0 (… states framework 18.3.0, not 18.4.0); a run with no iterate Run (no app passes); a.envwritten into the checkout.--gallerymissing, bad--model, unknown--push).mainprints exactly one line on a skip. On the repo's cards it commits and printsbranch: export/18.3.0and the body. A prerelease exits 1 withgallery-refresh: refused — ….Ablations (each restored: blob == HEAD, git diff HEAD empty)
All through
scripts/ablation-replace.mjson commitde7c89c5(anchor x1 → x0, replacement x0 → x1). The suite importssrc/directly, so there is no dist leg. Each rantest/export/refresh.test.ts -t NAME.filter((a) => a.framework.version !== latest)gets&& String("ABL1").length < 0):1 failed | 16 skipped, the "another framework version" case.--debutrefusal disarmed:1 failed | 16 skipped, the "--debut into a gallery that already lists an app" case.before.versions[0] === latest):1 failed | 16 skipped, the "every listed app states npm's latest" case.1 failed | 16 skipped, the ".env" case.1 failed | 16 skipped, the "top of a clean git checkout" case.Acceptance notes
index.jsoncarriesframework.versionper app, not once. The index schema (manifest.ts, strict) is{ schemaVersion, apps }, and each app'sframework.versionis the@objectstack/specits project pins (create-objectstack pins its own release exactly, and the fixed group versions@objectstack/cliwith it). So "the gallery's version" is read as: the index lists an app, and every listed app states that version. No index field was added: that would change the format objectstack-ai/gallery: a read-only check workflow on every PR (index schema, complete app directories, no stray files) and a PR-only main, before the scheduled export writes there #31122's check reads.--forceflag: the ruling names none.export/VERSIONon the remote) is an addition. Without it, every daily run while the PR awaits review would rerun every card for hours of subscription quota, and then fail to push an existing branch. It reads only the clone's remote-tracking refs, so no network.pass.ts) owes scratch mode's six halves. Template mode would roughly double each card's time and quota without changing what is exported.--cards DIRkeeps the meaning it has inrun-scenariosandexport-gallery(the card directory, where any directory but the repo's is the private overlay). A flag of the same name taking card ids in the same package would be a trap.--debuttherefore picks its card itself: the first exportable id (asset_lifecycletoday).nohupplus repeatedtimeout 590 tail --pid). A full refresh runs onestarterbuild after another, longer than the 2-hour cap on a background command in a cloud session. This is written into the runbook; no live run measured it (live runs are paused, cloud#3600).export-gallery --checkand the gallery workflow. The maintainer still owes adding the gallery to the environment's resources.Verification
All on head
de7c89c5(base7c523aa1, currentorigin/mainwhen cut), worktreeobjectstack-issue-31121.pnpm --filter @objectstack/scenarios build:pnpm --filter @objectstack/scenarios testgivesTest Files 31 passed (31),Tests 608 passed (608),VERDICT command-exit 0.pnpm --filter @objectstack/scenarios typecheck(tsc --noEmit;--listFilesincludestest/export/refresh.test.ts) exits 0.eslint packages/qa/scenarios --no-inline-configexits 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackgives 74 commands, all run on this head, each exit code captured before any pipe. 72 exited 0 on the first pass. Two exited 3, both prerequisites and not reds:check-plugin-teardown-shape --self-test: the clone is shallow. It exits 0 (48 cases) aftergit fetch --depth=1of its pinned sha.pnpm check:dual-build-cjs-loads: PREREQUISITE NOT MET. It exits 0 (122 entry points across 72 packages) after the fullturbo run buildunder the lock.--ran:74 derived famil(ies) accounted for — 74 run, 0 NOT-MEASURED.pnpm --filter @objectstack/spec run check:error-code-provenanceexits 0.check-changeset-no-major --base origin/mainagainst this body exits 0 (LEVEL AXIS … clause-② yes … patch).!in the title and no(narrowing)arm.The 74 derived gates, as run
patchchangeset.changeset/31121-scenarios-gallery-refresh.md(published@objectstack/scenarios;minoris refused by the launch-window guard), with both declaration lines.Generated by Claude Code