* fix(release): finalize job calls sync-next-version.cjs to bump next after final release (#2423) The release pipeline's 'finalize' job shipped X.Y.0 to npm 'latest' and merged to main, but never bumped 'next' to match. 'scripts/sync-next-version.cjs' exists exactly for this — its docstring promises to run 'for every release type (rc / hotfix / final)' — but it was wired only into the 'rc' job (release.yml:479), not 'finalize'. As a result, after 1.7.0 shipped on 2026-07-15, 'next' stayed at 1.7.0-rc.6 and every npm script banner on 'next' (and feature branches cut from it) reported the stale rc version. Regression of #1104 — closed incomplete (covered rc only, not final). This patch: - adds a 'Sync next branch to the published release' step to the 'finalize' job, mirroring the rc job's pattern at line 479 (uses VERSION from inputs.version rather than PRE_VERSION from steps.prerelease, since finalize does not run the prerelease step); - gates it on !inputs.dry_run + continue-on-error:true (matches rc); - adds tests/release-finalize-syncs-next-version.test.cjs — a structural YAML assertion that fails against the pre-fix workflow and passes after. Failing-first demonstrated during development; the test parses job blocks by indentation rather than grep so it stays valid as the file grows. Root-cause diagnosis: scripts/sync-next-version.cjs:14 docstring admits 'used by release.yml's rc job, which has no next-targeting PR of its own'. release.yml:506-685 (finalize job) had no sync-next-version step before this patch. Every prior rc.N release has a matching 'chore: sync next package version to 1.7.0-rc.N' commit; there is no such commit for 1.7.0. * chore(release): sync next package version to 1.7.0 (#2423) Replays the canonical 'chore: sync next package version to <v>' commit that the release pipeline's rc job auto-produces via scripts/sync-next-version.cjs, for the 1.7.0 final release that shipped on 2026-07-15 (commitdd4c90f82'chore: finalize v1.7.0' on main). Without this, 'next' (and every feature branch cut from it) carried 1.7.0-rc.6 indefinitely and reported it in every npm script banner (e.g. 'lint:ci'). Bumps 43 synchronized manifests via the npm 'version' lifecycle hook (scripts/sync-manifest-versions.cjs --stage + scripts/gen-capability-registry.cjs --write), matching the file set of commit27f69cc48('chore: sync next package version to 1.7.0-rc.6') and commitdd4c90f82('chore: finalize v1.7.0'). This is the immediate Layer-1 repair for #2423. Layer-2 (prevent recurrence) is the workflow patch in the previous commit; Layer-3 (regression test) ships with it. Future X.Y.0 final releases will produce this commit automatically once the workflow fix lands. * test(release): tighten #2423 dry-run gate assertion to the sync step Code review of fix/2423 found that test #3 ('gates sync-next-version on !inputs.dry_run') asserted too loosely: it scanned the entire finalize block for any '!inputs.dry_run' line, so it would still pass if the gate were stripped from the sync-next-version step specifically — the exact regression the test name promises to catch. The finalize job has multiple steps with their own !inputs.dry_run gates (e.g. Verify publish), so the loose version masked the very bug it claimed to detect. Tighten by extracting the specific YAML step block containing 'scripts/sync-next-version.cjs' and asserting the gate appears within THAT step's lines, not anywhere in the job. Verified the tightened test: - PASSES against the post-fix workflow (sync step has its own gate) - FAILS when the sync step's gate is stripped (even when other steps in finalize retain their own !inputs.dry_run gates) — the exact regression that previously slipped through Adds extractStepBlockContaining(jobBlock, marker) helper alongside the existing extractJobBlock(text, jobName). Reuses the same indentation- based parsing, so it stays valid as the file grows. * chore(changeset): backfill pr:2437 in .changeset/sturdy-ibex-jump.md CLAUDE.md changeset convention: 'Use placeholder pr:0 during initial commit. Backfill immediately after gh api POST /pulls returns the real number.' PR #2437 created from branch fix/2423-release-finalize-sync-next-version. * fix(#2423): add see #2423 to allow-test-rule exemption per ADR-456 CI lint-allow-test-rule-refs failed on PR #2437: ADR-456 requires new allow-test-rule exemptions added after the ADR's acceptance to include a tracking issue number in the comment, in the form // allow-test-rule: <reason> (see #NNN) The exemption added in commit 976c8b0a2 lacked this ref. Fixed. Verified locally: node scripts/lint-allow-test-rule-refs.cjs → ok lint-allow-test-rule-refs: 173 grandfathered exemption(s) tracked, no novel untracked offenders
82 lines
3.5 KiB
JSON
82 lines
3.5 KiB
JSON
{
|
|
"id": "external-job",
|
|
"role": "feature",
|
|
"version": "1.7.0",
|
|
"title": "Async external-job scheduler adapter",
|
|
"description": "Default-off producer of the async external-job manifest (#1164). At execute:wave:post an executor can externalize long-running compute (SLURM first, scheduler-pluggable), commit a .planning/async-jobs/<job>.json manifest, defer SUMMARY.md, and return external_job_waiting. The core loop (#1165) consumes the manifest; this capability is the only thing that writes it. NOTE on contribution point: #1164 specifies execute:wave:pre, but execute-phase.md only dispatches execute:wave:post today (wave:pre is declared in the loop host contract but not rendered); wiring wave:pre dispatch is a core-loop change #1164 explicitly puts out of scope, so this capability registers at wave:post and the executor honors the runtime_budget classification guidance before running any tagged task. The adapter (scripts/slurm-adapter.cjs) reads external_job.submit_timeout_ms / poll_timeout_ms / artifact_dir through the canonical capability-config seam (env override > config > registry default).",
|
|
"tier": "full",
|
|
"requires": [],
|
|
"engines": {
|
|
"gsd": ">=1.7.0"
|
|
},
|
|
"runtimeCompat": {
|
|
"supported": [
|
|
"*"
|
|
],
|
|
"unsupported": []
|
|
},
|
|
"skills": [],
|
|
"agents": [],
|
|
"hooks": [],
|
|
"config": {
|
|
"external_job.enabled": {
|
|
"type": "boolean",
|
|
"default": false,
|
|
"description": "Master toggle for the async external-job producer capability. Default-off: the core loop consumes manifests whether or not this is on, but no manifest is ever written unless an executor opts in here."
|
|
},
|
|
"external_job.backend": {
|
|
"type": "enum",
|
|
"values": [
|
|
"slurm"
|
|
],
|
|
"default": "slurm",
|
|
"description": "Scheduler backend. SLURM is the first adapter; the field is the pluggability seam for future backends (LSF, PBS, Kubernetes batch). Core never interprets this value."
|
|
},
|
|
"external_job.artifact_dir": {
|
|
"type": "string",
|
|
"default": "Artifacts/jobs",
|
|
"description": "Root for per-job artifact directories (e.g. Artifacts/jobs/<jobid>/). Avoids fixed log paths and hardcoding a cluster/project layout. Surfaced by the adapter (`slurm-adapter.cjs submit` prints the resolved value); override via GSD_EXTERNAL_JOB_ARTIFACT_DIR."
|
|
},
|
|
"external_job.submit_timeout_ms": {
|
|
"type": "number",
|
|
"default": 30000,
|
|
"description": "Hard timeout (ms) for the scheduler submit subprocess (e.g. sbatch). Bounded per CLAUDE.md unbounded-subprocess policy. Read by the adapter (env GSD_SLURM_SUBMIT_TIMEOUT_MS overrides)."
|
|
},
|
|
"external_job.poll_timeout_ms": {
|
|
"type": "number",
|
|
"default": 15000,
|
|
"description": "Hard timeout (ms) for the scheduler poll subprocess (squeue, with sacct fallback). Read by the adapter (env GSD_SLURM_POLL_TIMEOUT_MS overrides)."
|
|
}
|
|
},
|
|
"steps": [],
|
|
"contributions": [
|
|
{
|
|
"point": "execute:wave:post",
|
|
"into": "executor",
|
|
"fragment": {
|
|
"path": "fragments/execute-wave-post.md"
|
|
},
|
|
"produces": [
|
|
".planning/async-jobs/<job>.json"
|
|
],
|
|
"consumes": [
|
|
"PLAN.md"
|
|
],
|
|
"when": "external_job.enabled",
|
|
"onError": "skip"
|
|
},
|
|
{
|
|
"point": "plan:post",
|
|
"into": "planner",
|
|
"fragment": {
|
|
"path": "fragments/plan-post.md"
|
|
},
|
|
"produces": [],
|
|
"consumes": [],
|
|
"when": "external_job.enabled",
|
|
"onError": "skip"
|
|
}
|
|
],
|
|
"gates": []
|
|
}
|