From eb97136b60626f5d09e2de7cb86aaef648667947 Mon Sep 17 00:00:00 2001 From: Lior Date: Wed, 27 May 2026 13:32:08 -0400 Subject: [PATCH 1/3] claim: codex-loop-bash-retirement-drift-guard-20260527 Co-Authored-By: Codex --- ...loop-bash-retirement-drift-guard-20260527.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 docs/claims/codex-loop-bash-retirement-drift-guard-20260527.md diff --git a/docs/claims/codex-loop-bash-retirement-drift-guard-20260527.md b/docs/claims/codex-loop-bash-retirement-drift-guard-20260527.md new file mode 100644 index 0000000000..a0da1020c8 --- /dev/null +++ b/docs/claims/codex-loop-bash-retirement-drift-guard-20260527.md @@ -0,0 +1,17 @@ +# Claim - codex-loop-bash-retirement-drift-guard-20260527 + +- **Session ID:** codex/launchd-loop +- **Harness:** codex +- **Claimed at:** 2026-05-27T17:31:00Z +- **ETA:** 2026-05-27T18:15:00Z +- **Scope:** Maintain the TypeScript/Bun bash-retirement inventory guard for newly tracked non-Lean shell drift. +- **Durable target:** `tools/hygiene/check-bash-retirement-inventory.ts`, `tools/hygiene/check-bash-retirement-inventory.test.ts`, and `docs/trajectories/typescript-bun-migration/RESUME.md` +- **Platform mirror:** PR to be opened from this branch. + +## Notes + +- surface: codex-background-service +- origin: codex-launchd-loop +- run_id: 20260527T172714Z +- pickup source: `bun .codex/bin/codex-backlog-runner.ts --json` selected `docs/trajectories/typescript-bun-migration/RESUME.md`. +- assumption: stale local bash-retirement heartbeats from earlier headless runs are superseded because their worktrees and remote claim refs are absent; this claim takes a fresh bounded guard-maintenance slice. From 28835797a00b8b49645270ca2048f4d785ef65d8 Mon Sep 17 00:00:00 2001 From: Lior Date: Wed, 27 May 2026 13:38:54 -0400 Subject: [PATCH 2/3] hygiene: guard shell-family bash-retirement drift Extend the bash-retirement inventory guard beyond .sh files so newly tracked non-Lean shell-family entrypoints cannot bypass the retained-shell allowlist. Co-Authored-By: Codex --- .../typescript-bun-migration/RESUME.md | 7 ++-- .../check-bash-retirement-inventory.test.ts | 39 +++++++++++++++++++ .../check-bash-retirement-inventory.ts | 11 +++--- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/docs/trajectories/typescript-bun-migration/RESUME.md b/docs/trajectories/typescript-bun-migration/RESUME.md index bcad73f199..b9ae599a14 100644 --- a/docs/trajectories/typescript-bun-migration/RESUME.md +++ b/docs/trajectories/typescript-bun-migration/RESUME.md @@ -4,8 +4,8 @@ **Milestone**: 43 ported. All clusters complete: budget (14/18/19), peer-call (15/16/17), git (13/20), pr-preservation (21), cluster-inventory capture. Bucket B is empty as of 2026-04-30T08:07:32Z. The remaining non-Lean `.sh` inventory is guarded repo-wide by `tools/hygiene/check-bash-retirement-inventory.ts` and wired through package script `hygiene:check-bash-retirement-inventory` plus the `gate.yml` bash-retirement inventory lint job. **Current blocker**: None. **Next concrete action**: Maintain the bash-retirement inventory guard and treat -any newly tracked non-Lean `.sh` outside the allowlist as drift. Do not revive -the old Cluster G/H/I or budget-cluster port queues. +any newly tracked non-Lean shell-family file outside the allowlist as drift. Do +not revive the old Cluster G/H/I or budget-cluster port queues. **Last updated**: 2026-05-26 ## Why this trajectory exists @@ -52,7 +52,8 @@ bun run hygiene:check-bash-retirement-inventory The expected retained surface is the explicit repo-wide allowlist: setup and bootstrap scripts, host-service wrappers, NixOS installer scripts, dev-cluster wrappers, launchd-bootstrap, and the Kiro loop wrapper. Any new -non-Lean `.sh` outside the allowlist is bash-retirement drift. +non-Lean shell-family file (`.sh`, `.bash`, `.zsh`, `.ksh`, or `.command`) +outside the allowlist is bash-retirement drift. ### Bucket A — Should stay Shell (21 files) diff --git a/tools/hygiene/check-bash-retirement-inventory.test.ts b/tools/hygiene/check-bash-retirement-inventory.test.ts index 1a9ff99ad0..a62c503396 100644 --- a/tools/hygiene/check-bash-retirement-inventory.test.ts +++ b/tools/hygiene/check-bash-retirement-inventory.test.ts @@ -1,4 +1,8 @@ import { describe, expect, test } from "bun:test"; +import { spawnSync } from "node:child_process"; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { buildInventoryReport, @@ -9,6 +13,13 @@ import { trackedNonLeanShellFilesFromGit, } from "./check-bash-retirement-inventory"; +function runGit(args: readonly string[], cwd: string): void { + const result = spawnSync("git", args, { cwd, encoding: "utf8" }); + if (result.status !== 0) { + throw new Error(result.stderr.trim() || `git ${args.join(" ")} failed`); + } +} + function splitExpectedRetained(): readonly [string, readonly string[]] { const [missing, ...rest] = EXPECTED_RETAINED_SHELL; if (missing === undefined) throw new Error("expected retained shell allowlist must be non-empty"); @@ -145,6 +156,34 @@ describe("buildInventoryReport", () => { expect(report.drift.unexpected).toEqual([]); expect(report.drift.missingRetained).toEqual([]); }); + + test("enumerates tracked shell-family files while excluding Lean vendor scripts", () => { + const repo = mkdtempSync(join(tmpdir(), "zeta-bash-retirement-")); + try { + runGit(["init"], repo); + + mkdirSync(join(repo, "scripts"), { recursive: true }); + mkdirSync(join(repo, "tools", "lean4"), { recursive: true }); + writeFileSync(join(repo, "scripts", "a.sh"), "#!/usr/bin/env bash\n"); + writeFileSync(join(repo, "scripts", "b.bash"), "#!/usr/bin/env bash\n"); + writeFileSync(join(repo, "scripts", "c.zsh"), "#!/usr/bin/env zsh\n"); + writeFileSync(join(repo, "scripts", "d.ksh"), "#!/usr/bin/env ksh\n"); + writeFileSync(join(repo, "scripts", "e.command"), "#!/usr/bin/env bash\n"); + writeFileSync(join(repo, "tools", "lean4", "vendor.sh"), "#!/usr/bin/env bash\n"); + writeFileSync(join(repo, "README.md"), "not shell\n"); + runGit(["add", "."], repo); + + expect(trackedNonLeanShellFilesFromGit(repo)).toEqual([ + "scripts/a.sh", + "scripts/b.bash", + "scripts/c.zsh", + "scripts/d.ksh", + "scripts/e.command", + ]); + } finally { + rmSync(repo, { recursive: true, force: true }); + } + }); }); describe("renderReport", () => { diff --git a/tools/hygiene/check-bash-retirement-inventory.ts b/tools/hygiene/check-bash-retirement-inventory.ts index 06d78882fc..ee16cb0ccb 100644 --- a/tools/hygiene/check-bash-retirement-inventory.ts +++ b/tools/hygiene/check-bash-retirement-inventory.ts @@ -2,7 +2,7 @@ // check-bash-retirement-inventory.ts — verify the retained shell surface. // // The TypeScript/Bun migration is in bash-retirement mode: repo-owned scripts -// should not grow new `.sh` entrypoints outside the explicit repo-wide +// should not grow new shell-family entrypoints outside the explicit repo-wide // retained-shell allowlist. Retained shell exists only where the script runs // before Bun is available, bootstraps a host service environment, or belongs // to a low-level installer/dev-cluster surface that is still shell-native. @@ -66,6 +66,7 @@ export interface InventoryReport { const SPAWN_MAX_BUFFER = 64 * 1024 * 1024; export const RETAINED_SHELL_SCOPE = "repo-wide setup/bootstrap/service-wrapper/installer/dev-cluster allowlist"; +export const TRACKED_SHELL_FILE_GLOBS: readonly string[] = ["*.sh", "*.bash", "*.zsh", "*.ksh", "*.command"]; export const EXPECTED_RETAINED_SHELL: readonly string[] = [ ".gemini/service/install-lior-service.sh", @@ -166,9 +167,9 @@ function runGit(args: readonly string[], cwd?: string): string { return result.stdout; } -export function trackedNonLeanShellFilesFromGit(): readonly string[] { - const repoRoot = runGit(["rev-parse", "--show-toplevel"]).trim(); - const raw = runGit(["ls-files", "-z", "*.sh"], repoRoot); +export function trackedNonLeanShellFilesFromGit(cwd?: string): readonly string[] { + const repoRoot = runGit(["rev-parse", "--show-toplevel"], cwd).trim(); + const raw = runGit(["ls-files", "-z", ...TRACKED_SHELL_FILE_GLOBS], repoRoot); return raw .split("\0") .filter((file): file is string => file.length > 0) @@ -361,7 +362,7 @@ function usage(): string { " bun tools/hygiene/check-bash-retirement-inventory.ts --enforce", " bun tools/hygiene/check-bash-retirement-inventory.ts --json", "", - `Checks that non-Lean tracked .sh files match ${RETAINED_SHELL_SCOPE}.`, + `Checks that non-Lean tracked shell-family files match ${RETAINED_SHELL_SCOPE}.`, ].join("\n"); } From 15a5db215a56e672b2fe669fac18bbc92e3c9465 Mon Sep 17 00:00:00 2001 From: Lior Date: Wed, 27 May 2026 13:48:59 -0400 Subject: [PATCH 3/3] fix: address bash-retirement guard review Handle git spawn failures in the temp-repo test helper, align the migration resume wording and timestamp with the shell-family guard surface, and release the live claim file before merge. Co-Authored-By: Codex --- ...loop-bash-retirement-drift-guard-20260527.md | 17 ----------------- .../typescript-bun-migration/RESUME.md | 4 ++-- .../check-bash-retirement-inventory.test.ts | 5 +++++ 3 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 docs/claims/codex-loop-bash-retirement-drift-guard-20260527.md diff --git a/docs/claims/codex-loop-bash-retirement-drift-guard-20260527.md b/docs/claims/codex-loop-bash-retirement-drift-guard-20260527.md deleted file mode 100644 index a0da1020c8..0000000000 --- a/docs/claims/codex-loop-bash-retirement-drift-guard-20260527.md +++ /dev/null @@ -1,17 +0,0 @@ -# Claim - codex-loop-bash-retirement-drift-guard-20260527 - -- **Session ID:** codex/launchd-loop -- **Harness:** codex -- **Claimed at:** 2026-05-27T17:31:00Z -- **ETA:** 2026-05-27T18:15:00Z -- **Scope:** Maintain the TypeScript/Bun bash-retirement inventory guard for newly tracked non-Lean shell drift. -- **Durable target:** `tools/hygiene/check-bash-retirement-inventory.ts`, `tools/hygiene/check-bash-retirement-inventory.test.ts`, and `docs/trajectories/typescript-bun-migration/RESUME.md` -- **Platform mirror:** PR to be opened from this branch. - -## Notes - -- surface: codex-background-service -- origin: codex-launchd-loop -- run_id: 20260527T172714Z -- pickup source: `bun .codex/bin/codex-backlog-runner.ts --json` selected `docs/trajectories/typescript-bun-migration/RESUME.md`. -- assumption: stale local bash-retirement heartbeats from earlier headless runs are superseded because their worktrees and remote claim refs are absent; this claim takes a fresh bounded guard-maintenance slice. diff --git a/docs/trajectories/typescript-bun-migration/RESUME.md b/docs/trajectories/typescript-bun-migration/RESUME.md index b9ae599a14..095b998d84 100644 --- a/docs/trajectories/typescript-bun-migration/RESUME.md +++ b/docs/trajectories/typescript-bun-migration/RESUME.md @@ -1,12 +1,12 @@ # Trajectory — TypeScript / Bun migration **Status**: Closed-maintained bash-retirement phase (Lane B slice 21 merged — [#908](https://github.com/Lucent-Financial-Group/Zeta/pull/908); bash-retirement inventory guard landed — [#2764](https://github.com/Lucent-Financial-Group/Zeta/pull/2764); **Bucket B is empty**; retained non-Lean shell surface is the repo-wide setup/bootstrap/service-wrapper/installer/dev-cluster allowlist) -**Milestone**: 43 ported. All clusters complete: budget (14/18/19), peer-call (15/16/17), git (13/20), pr-preservation (21), cluster-inventory capture. Bucket B is empty as of 2026-04-30T08:07:32Z. The remaining non-Lean `.sh` inventory is guarded repo-wide by `tools/hygiene/check-bash-retirement-inventory.ts` and wired through package script `hygiene:check-bash-retirement-inventory` plus the `gate.yml` bash-retirement inventory lint job. +**Milestone**: 43 ported. All clusters complete: budget (14/18/19), peer-call (15/16/17), git (13/20), pr-preservation (21), cluster-inventory capture. Bucket B is empty as of 2026-04-30T08:07:32Z. The remaining non-Lean shell-family inventory is guarded repo-wide by `tools/hygiene/check-bash-retirement-inventory.ts` and wired through package script `hygiene:check-bash-retirement-inventory` plus the `gate.yml` bash-retirement inventory lint job. **Current blocker**: None. **Next concrete action**: Maintain the bash-retirement inventory guard and treat any newly tracked non-Lean shell-family file outside the allowlist as drift. Do not revive the old Cluster G/H/I or budget-cluster port queues. -**Last updated**: 2026-05-26 +**Last updated**: 2026-05-27T17:48Z ## Why this trajectory exists diff --git a/tools/hygiene/check-bash-retirement-inventory.test.ts b/tools/hygiene/check-bash-retirement-inventory.test.ts index a62c503396..412d8be506 100644 --- a/tools/hygiene/check-bash-retirement-inventory.test.ts +++ b/tools/hygiene/check-bash-retirement-inventory.test.ts @@ -14,7 +14,12 @@ import { } from "./check-bash-retirement-inventory"; function runGit(args: readonly string[], cwd: string): void { + // Test helper uses repo-pinned git with explicit argv; no shell expansion. + // eslint-disable-next-line sonarjs/no-os-command-from-path const result = spawnSync("git", args, { cwd, encoding: "utf8" }); + if (result.error) { + throw new Error(`failed to start git ${args.join(" ")}: ${result.error.message}`); + } if (result.status !== 0) { throw new Error(result.stderr.trim() || `git ${args.join(" ")} failed`); }