From 57137550ed038728725ba6065cc425025e0d4d6f Mon Sep 17 00:00:00 2001 From: Eivind Meyer Date: Mon, 13 Jul 2026 17:31:30 -0700 Subject: [PATCH 1/2] ci: guard setup worktree cleanliness Opcore ship runs depend on setup being idempotent in a fresh worktree. Add a CI guard that runs setup in a throwaway worktree and fails if it leaves dirty files. --- package.json | 1 + scripts/check-workspace.mjs | 2 ++ scripts/ci/check-setup-clean-worktree.sh | 28 ++++++++++++++++++++++++ scripts/ci/run-local-ci-equivalent.sh | 1 + 4 files changed, 32 insertions(+) create mode 100644 scripts/ci/check-setup-clean-worktree.sh diff --git a/package.json b/package.json index 3608dad..3ce7140 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "ace:sync": "bash ./scripts/run-ace.sh sync", "ace:validate": "bash ./scripts/run-ace.sh validate", "setup": "npm ci && npm run setup:tools", + "setup:check-clean": "bash ./scripts/ci/check-setup-clean-worktree.sh", "setup:tools": "bash ./scripts/setup-current-tools.sh", "test": "node --test tests/*.test.mjs", "test:ci": "node scripts/run-test-ci.mjs", diff --git a/scripts/check-workspace.mjs b/scripts/check-workspace.mjs index 64a4af2..d96fa4e 100644 --- a/scripts/check-workspace.mjs +++ b/scripts/check-workspace.mjs @@ -66,6 +66,7 @@ for (const scriptName of [ "ci", "ci:local", "setup", + "setup:check-clean", "setup:tools", "verify", "test:ci", @@ -454,6 +455,7 @@ function validateLocalCiEquivalent() { const localCi = readFileSync("scripts/ci/run-local-ci-equivalent.sh", "utf8"); for (const token of [ "npm run setup:tools", + "npm run setup:check-clean", "npm run ci", "npm run current-tools:validate-all", "npm run current-tools:validate-rust-graph" diff --git a/scripts/ci/check-setup-clean-worktree.sh b/scripts/ci/check-setup-clean-worktree.sh new file mode 100644 index 0000000..428c274 --- /dev/null +++ b/scripts/ci/check-setup-clean-worktree.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" +temp_root="$(mktemp -d "${TMPDIR:-/tmp}/opcore-setup-clean.XXXXXX")" +worktree_path="${temp_root}/repo" + +cleanup() { + git -C "${repo_root}" worktree remove --force "${worktree_path}" >/dev/null 2>&1 || true + rm -rf "${temp_root}" +} +trap cleanup EXIT + +git -C "${repo_root}" worktree add --detach "${worktree_path}" HEAD + +( + cd "${worktree_path}" + npm run setup +) + +status="$(git -C "${worktree_path}" status --short --untracked-files=normal)" +if [ -n "${status}" ]; then + printf 'error: npm run setup must leave a fresh worktree clean.\n' >&2 + printf 'Dirty paths after setup:\n%s\n' "${status}" >&2 + exit 1 +fi + +printf 'setup clean-worktree check passed\n' diff --git a/scripts/ci/run-local-ci-equivalent.sh b/scripts/ci/run-local-ci-equivalent.sh index 34e6769..3da54bb 100755 --- a/scripts/ci/run-local-ci-equivalent.sh +++ b/scripts/ci/run-local-ci-equivalent.sh @@ -99,6 +99,7 @@ if docs_or_agent_only_changes "${changed_file_list}"; then fi run_step npm run setup:tools +run_step npm run setup:check-clean snapshot_generated_artifacts run_step npm run ci restore_generated_artifacts From 43eabea430ef1a4ab5b12dd265c9ec1412072ef3 Mon Sep 17 00:00:00 2001 From: Eivind Meyer Date: Mon, 13 Jul 2026 18:32:50 -0700 Subject: [PATCH 2/2] ci: target zeroshot feature runs at dev Align Opcore Zeroshot defaults with the enforced branch workflow: feature runs target dev and dev promotes to main. --- .zeroshot/settings.json | 4 ++-- scripts/check-workspace.mjs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.zeroshot/settings.json b/.zeroshot/settings.json index d206dc9..5eee803 100644 --- a/.zeroshot/settings.json +++ b/.zeroshot/settings.json @@ -1,9 +1,9 @@ { "github": { - "prBase": "main" + "prBase": "dev" }, "worktree": { - "baseRef": "origin/main", + "baseRef": "origin/dev", "setup": [ "npm run setup" ] diff --git a/scripts/check-workspace.mjs b/scripts/check-workspace.mjs index d96fa4e..6f98350 100644 --- a/scripts/check-workspace.mjs +++ b/scripts/check-workspace.mjs @@ -304,8 +304,8 @@ for (const rustCheck of ["cargo fmt --check", "cargo clippy --all-targets --all- } const zeroshot = readJson(".zeroshot/settings.json"); -if (zeroshot.github?.prBase !== "main" || zeroshot.worktree?.baseRef !== "origin/main") { - fail("Zeroshot must target the lattice main branch"); +if (zeroshot.github?.prBase !== "dev" || zeroshot.worktree?.baseRef !== "origin/dev") { + fail("Zeroshot feature runs must target the lattice dev branch"); } if (!zeroshot.worktree?.setup?.includes("npm run setup")) fail("Zeroshot setup must generate current-tool wrappers");