Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,64 @@ jobs:
printf ' %s\n' "${files[@]}"
shellcheck --format=gcc --severity=style "${files[@]}"

lint-bash-retirement-inventory:
# Bash-retirement guard for the TypeScript/Bun migration trajectory:
# Zeta-owned post-install repo scripts should not grow new `.sh`
# entrypoints after Bucket B reached zero. The retained shell surface is
# setup/bootstrap, launchd-bootstrap, and the Kiro loop wrapper only.
name: lint (bash retirement inventory)
timeout-minutes: 5
runs-on: ubuntu-24.04

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Cache install.sh outputs (mise runtimes + dotnet tools + verifier jars)
# Comprehensive cache — see lint-shell job above for the
# rationale (maintainer dev-CI parity input +
# transient 502 prevention). Same cache key shape so all
# lint jobs share the cache when toolchain unchanged.
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.local/bin/mise
~/.local/share/mise
~/.cache/mise
~/.dotnet/tools
~/.elan
~/.config/zeta
key: install-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.mise.toml', 'tools/setup/**', 'global.json') }}

- name: Install toolchain via three-way-parity script (GOVERNANCE §24)
# Provides bun via mise's pin in .mise.toml. The inventory guard is
# intentionally separate from shellcheck: shellcheck validates the
# retained setup scripts, while this job fails any new non-allowlisted
# tracked `.sh` surface before it reaches main.
# See lint-shell job above for retry rationale.
run: |
set -euo pipefail
for attempt in 1 2 3 4 5; do
if ./tools/setup/install.sh; then exit 0; fi
[ "$attempt" = "5" ] && { echo "install.sh failed after 5 attempts"; exit 1; }
# Backoff: 10s, 30s, 60s, 120s — covers transient CDN
# blips from short-burst-and-recover up to multi-minute
# outages. The retry budget is five attempts because prior
# bun CDN 502s exhausted mise-internal retries plus a
# three-attempt wrapper.
case "$attempt" in
1) backoff=10 ;;
2) backoff=30 ;;
3) backoff=60 ;;
4) backoff=120 ;;
esac
echo "install.sh attempt $attempt failed; retrying in ${backoff}s..." >&2
sleep "$backoff"
done

- name: Run bash-retirement inventory guard
run: bun run hygiene:check-bash-retirement-inventory

lint-workflows:
# actionlint catches .github/workflows/*.yml bugs: unknown
# context refs, invalid runner labels, shellcheck-style warnings
Expand Down
38 changes: 25 additions & 13 deletions docs/trajectories/typescript-bun-migration/RESUME.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# Trajectory — TypeScript / Bun migration

**Status**: Soak + bash-retirement phase (Lane B slice 21 merged — [#908](https://github.com/Lucent-Financial-Group/Zeta/pull/908); **Bucket B is empty**; retained non-Lean bash surface is setup/bootstrap only)
**Milestone**: 42 ported. All clusters complete: budget (14/18/19), peer-call (15/16/17), git (13/20), pr-preservation (21). Bucket B is empty as of 2026-04-30T08:07:32Z. The remaining non-Lean `.sh` inventory is guarded by `tools/hygiene/check-bash-retirement-inventory.ts`.
**Status**: Soak + 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 bash surface is setup/bootstrap, launchd-bootstrap, and the Kiro loop wrapper only)
**Milestone**: 42 ported. All clusters complete: budget (14/18/19), peer-call (15/16/17), git (13/20), pr-preservation (21). Bucket B is empty as of 2026-04-30T08:07:32Z. The remaining non-Lean `.sh` inventory is guarded 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**: Land the bash-retirement inventory check, then wire it
into the appropriate hygiene/CI surface after one clean soak pass. Do not revive
the old Cluster G/H/I or budget-cluster port queues.
**Last updated**: 2026-05-12
**Next concrete action**: Shepherd the bash-retirement inventory wire-in PR
through review and CI; after merge, decide whether the bash-retirement phase can
move from soak to closed-maintained. Do not revive the old Cluster G/H/I or
budget-cluster port queues.
**Last updated**: 2026-05-26

## Why this trajectory exists

Expand All @@ -32,29 +33,38 @@ Per the maintainer-channel correction via the multi-AI review surface (2026-04-2
| [#882](https://github.com/Lucent-Financial-Group/Zeta/pull/882) | 2026-04-30 (commit `02266a7`) | `tools/hygiene/validate-agencysignature-pr-body.{sh→ts}`, `tools/hygiene/audit-agencysignature-main-tip.{sh→ts}`, `tools/hygiene/capture-tick-snapshot.{sh→ts}` | Merged |
| [#883](https://github.com/Lucent-Financial-Group/Zeta/pull/883) | 2026-04-30 (commit `271bc38`) | `tools/hygiene/counterweight-audit.{sh→ts}`, `tools/hygiene/append-tick-history-row.{sh→ts}` | Merged |
| [#884](https://github.com/Lucent-Financial-Group/Zeta/pull/884) | 2026-04-30 (commit `9237756`) | `tools/skill-catalog/backfill_dv2_frontmatter.{sh→ts}`, `tools/audit-packages.{sh→ts}` | Merged |
| [#2764](https://github.com/Lucent-Financial-Group/Zeta/pull/2764) | 2026-05-12 (commit `b563ba0`) | `tools/hygiene/check-bash-retirement-inventory.ts`, `tools/hygiene/check-bash-retirement-inventory.test.ts` | Merged |

## Inventory — Python (tools/, Zeta-authored)

**Status: 0 remaining.**

After PR #849, Zeta has zero Python files in `tools/` (Zeta-authored — the 22 `.py` files under `tools/lean4/.lake/packages/mathlib/scripts/` are mathlib upstream, not in scope). Python→TS in `tools/` is **100% complete**.

## Inventory — Bash (tools/, Zeta-authored, 13 retained files)
## Inventory — Bash (tools/, Zeta-authored, 15 retained files)

Current count is repo-derived and guarded by:

```bash
bun tools/hygiene/check-bash-retirement-inventory.ts --enforce
bun run hygiene:check-bash-retirement-inventory
```

The expected retained surface is setup/bootstrap only. Any new non-Lean `.sh`
outside the allowlist is bash-retirement drift.
The expected retained surface is setup/bootstrap, launchd-bootstrap, and the
Kiro loop wrapper only. Any
new non-Lean `.sh` outside the allowlist is bash-retirement drift.

### Bucket A — Should stay Bash (13 files)
### Bucket A — Should stay Bash (15 files)

These run **before** Bun is installed (post-install scripts can use Bun; pre-install scripts cannot). Per Otto-235 4-shell portability target (macOS bash 3.2 / Ubuntu / git-bash / WSL), these are the bootstrap layer.
These either run **before** Bun is installed (post-install scripts can use Bun;
pre-install scripts cannot) or bootstrap macOS launchd into the pinned Bun
environment before handing off to TypeScript. Per Otto-235 4-shell portability
target (macOS bash 3.2 / Ubuntu / git-bash / WSL), these are the bootstrap
layer.

```text
tools/kiro/kiro-loop-wrapper.sh
tools/kiro/launchd/install.sh
tools/setup/install.sh
tools/setup/doctor.sh
tools/setup/linux.sh
Expand All @@ -70,7 +80,9 @@ tools/setup/common/sync-upstreams.sh
tools/setup/common/verifiers.sh
```

Rationale: TS/Bun is itself one of the things `install.sh` installs. These scripts cannot depend on Bun.
Rationale: TS/Bun is itself one of the things `install.sh` installs, and
launchd needs a small shell bootstrap to establish PATH/state before `exec`
hands control to Bun.

### Bucket B — Should become TypeScript (0 files remaining — empty as of 2026-04-30T08:07:32Z)

Expand Down Expand Up @@ -100,7 +112,7 @@ tools/hygiene/snapshot-github-settings.ts # was .sh

### Bucket D — Ported, bash retained (0 tracked files; historical list)

The TS ports landed in #866 + #868 + #870 + #872 + #874 + #876 + #878 + #880 + #882 + #883 + #884 + #885 + #892 + #894 + #896 + #898 + #900 + #901 + #902. The bash originals listed below are now historical references, not tracked live files; the bash-retirement inventory check fails if any equivalent post-install `.sh` surface reappears outside setup/bootstrap.
The TS ports landed in #866 + #868 + #870 + #872 + #874 + #876 + #878 + #880 + #882 + #883 + #884 + #885 + #892 + #894 + #896 + #898 + #900 + #901 + #902. The bash originals listed below are now historical references, not tracked live files; the bash-retirement inventory check fails if any equivalent post-install `.sh` surface reappears outside setup/bootstrap, launchd-bootstrap, and the Kiro loop wrapper.

**Removed 2026-05-03 (CI-workflow .sh→.ts conversion completed):** the 5 files
listed in #1376's risk-stratification (audit-memory-index-duplicates,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test:typescript": "bun test",
"hygiene:sort-tick-history": "bun ./tools/hygiene/sort-tick-history-canonical.ts",
"hygiene:fix-markdown": "bun ./tools/hygiene/fix-markdown-md032-md026.ts",
"hygiene:check-bash-retirement-inventory": "bun ./tools/hygiene/check-bash-retirement-inventory.ts --enforce",
"shadow": "bun tools/shadow/zeta-shadow.ts"
},
"bin": {
Expand Down
11 changes: 7 additions & 4 deletions tools/hygiene/check-bash-retirement-inventory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
EXPECTED_RETAINED_BASH,
hasDrift,
renderReport,
RETAINED_BASH_SCOPE,
} from "./check-bash-retirement-inventory";

function splitExpectedRetained(): readonly [string, readonly string[]] {
Expand All @@ -14,7 +15,7 @@ function splitExpectedRetained(): readonly [string, readonly string[]] {
}

describe("buildInventoryReport", () => {
test("accepts the retained setup/bootstrap allowlist", () => {
test("accepts the retained bash allowlist", () => {
const report = buildInventoryReport(EXPECTED_RETAINED_BASH);

expect(hasDrift(report)).toBe(false);
Expand All @@ -31,7 +32,7 @@ describe("buildInventoryReport", () => {
expect(report.drift.missingRetained).toEqual([]);
});

test("flags missing retained setup scripts", () => {
test("flags missing retained bash scripts", () => {
const [missing, rest] = splitExpectedRetained();
const report = buildInventoryReport(rest);

Expand All @@ -45,7 +46,9 @@ describe("renderReport", () => {
test("renders an OK summary for a matching inventory", () => {
const report = buildInventoryReport(EXPECTED_RETAINED_BASH);

expect(renderReport(report)).toContain("OK: retained non-Lean bash surface matches setup/bootstrap allowlist.");
expect(renderReport(report)).toContain(
`OK: retained non-Lean bash surface matches ${RETAINED_BASH_SCOPE} allowlist.`,
);
});

test("renders drift sections", () => {
Expand All @@ -55,7 +58,7 @@ describe("renderReport", () => {

expect(rendered).toContain("## Unexpected non-Lean bash files");
expect(rendered).toContain("tools/hygiene/new-post-install-wrapper.sh");
expect(rendered).toContain("## Missing retained setup/bootstrap files");
expect(rendered).toContain(`## Missing retained ${RETAINED_BASH_SCOPE} files`);
expect(rendered).toContain(missing);
});
});
13 changes: 9 additions & 4 deletions tools/hygiene/check-bash-retirement-inventory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
//
// The TypeScript/Bun migration is in bash-retirement mode: repo tools should
// not grow new post-install `.sh` entrypoints. The only non-Lean shell scripts
// still allowed are setup/bootstrap scripts that run before Bun is available.
// still allowed are setup/bootstrap scripts that run before Bun is available,
// launchd bootstrap scripts that establish the pinned Bun environment, and
// the Kiro loop wrapper that is itself launched by launchd.
//
// Usage:
// bun tools/hygiene/check-bash-retirement-inventory.ts
Expand Down Expand Up @@ -33,8 +35,11 @@ export interface InventoryReport {
}

const SPAWN_MAX_BUFFER = 64 * 1024 * 1024;
export const RETAINED_BASH_SCOPE = "setup/bootstrap/launchd-bootstrap/Kiro-wrapper";

export const EXPECTED_RETAINED_BASH: readonly string[] = [
"tools/kiro/kiro-loop-wrapper.sh",
"tools/kiro/launchd/install.sh",
"tools/setup/common/curl-fetch.sh",
"tools/setup/common/dotnet-tools.sh",
Comment thread
AceHack marked this conversation as resolved.
"tools/setup/common/elan.sh",
Expand Down Expand Up @@ -129,7 +134,7 @@ export function renderReport(report: InventoryReport): string {
lines.push(`missing_retained: ${String(report.drift.missingRetained.length)}`);
lines.push("");
if (!hasDrift(report)) {
lines.push("OK: retained non-Lean bash surface matches setup/bootstrap allowlist.");
lines.push(`OK: retained non-Lean bash surface matches ${RETAINED_BASH_SCOPE} allowlist.`);
return `${lines.join("\n")}\n`;
Comment thread
AceHack marked this conversation as resolved.
}
if (report.drift.unexpected.length > 0) {
Expand All @@ -139,7 +144,7 @@ export function renderReport(report: InventoryReport): string {
lines.push("");
}
if (report.drift.missingRetained.length > 0) {
lines.push("## Missing retained setup/bootstrap files");
lines.push(`## Missing retained ${RETAINED_BASH_SCOPE} files`);
lines.push("");
for (const file of report.drift.missingRetained) lines.push(`- ${file}`);
lines.push("");
Expand All @@ -154,7 +159,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 are limited to setup/bootstrap scripts.",
`Checks that non-Lean tracked .sh files are limited to ${RETAINED_BASH_SCOPE} scripts.`,
].join("\n");
}

Expand Down
Loading