beadboard/docs/plans/2026-03-03-global-install-runtime-manager.md

533 lines
14 KiB
Markdown

# BeadBoard Global Install Runtime Manager Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Replace repo-path shim install behavior with a robust global install model where `beadboard` runs from managed runtime home and works from any directory.
**Architecture:** Keep a hybrid install strategy with npm-global as primary and script bootstrap as fallback, but unify both paths behind one runtime manager contract (`~/.beadboard/runtime/<version>` + stable shims). Preserve backward compatibility by detecting legacy repo-bound shims and offering migration. Implement with strict TDD and explicit installer smoke tests.
**Tech Stack:** Node.js (ESM), TypeScript/tsx for CLI code, PowerShell + POSIX shell wrappers, GitHub Actions, Node test runner.
---
## Pre-Implementation Checklist
### Task 0: Baseline and Safety Snapshot
**Files:**
- Modify: `beadboard/.beads/issues.jsonl` via `bd` commands only
- Read: `beadboard/AGENTS.md`
- Read: `beadboard/NEXT_SESSION_PROMPT.md`
**Step 1: Claim/track implementation bead(s)**
Run:
```bash
cd beadboard
bd create --title="Global installer runtime manager implementation" --description="Implement npm-global-first runtime manager with migration from repo-path shims" --type=task --priority=1 --label="installation,cli,runtime"
bd update <new-bead-id> --status in_progress --assignee <agent-bead-id>
```
**Step 2: Capture baseline gate status**
Run:
```bash
npm run typecheck
npm run lint
npm run test
```
Expected: existing known unrelated failures may appear; record exact failing files/tests in bead notes.
**Step 3: Commit checkpoint**
No code changes yet. Do not commit.
---
## Phase 1: Define Runtime Manager Contract
### Task 1: Add runtime manager ADR and package contract
**Files:**
- Create: `docs/adr/2026-03-03-runtime-manager-global-install.md`
- Modify: `docs/adr/2026-03-03-global-installer-contract-and-manifest.md`
**Step 1: Write failing docs contract test**
Create:
- `tests/docs/runtime-manager-adr-contract.test.ts`
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
test('runtime manager ADR exists and declares runtime home strategy', async () => {
const raw = await fs.readFile(path.resolve('docs/adr/2026-03-03-runtime-manager-global-install.md'), 'utf8');
assert.match(raw, /~\/\.beadboard\/runtime/i);
assert.match(raw, /npm i -g beadboard/i);
assert.match(raw, /legacy repo-bound shim migration/i);
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/docs/runtime-manager-adr-contract.test.ts
```
Expected: FAIL (`ENOENT` for missing ADR file).
**Step 3: Write minimal implementation**
Add ADR with:
- runtime directory layout
- shim strategy
- update/uninstall model
- compatibility migration model
- failure modes and rollback
**Step 4: Run test to verify it passes**
Run:
```bash
node --import tsx --test tests/docs/runtime-manager-adr-contract.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add tests/docs/runtime-manager-adr-contract.test.ts docs/adr/2026-03-03-runtime-manager-global-install.md docs/adr/2026-03-03-global-installer-contract-and-manifest.md
git commit -m "docs: define runtime manager global install contract"
```
---
## Phase 2: Implement Runtime Manager Library
### Task 2: Add runtime manager core module with strict validation
**Files:**
- Create: `src/lib/runtime-manager.ts`
- Create: `tests/lib/runtime-manager.test.ts`
**Step 1: Write failing test**
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
import { getRuntimePaths, normalizeVersion } from '../../src/lib/runtime-manager';
test('normalizeVersion supports semver and rejects empty', () => {
assert.equal(normalizeVersion('1.2.3'), '1.2.3');
assert.throws(() => normalizeVersion(''));
});
test('getRuntimePaths builds ~/.beadboard/runtime/<version> layout', () => {
const p = getRuntimePaths('/tmp/home', '1.2.3');
assert.match(p.runtimeRoot, /runtime\/1\.2\.3$/);
assert.match(p.shimDir, /\.beadboard\/bin$/);
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/lib/runtime-manager.test.ts
```
Expected: FAIL (`Cannot find module '../../src/lib/runtime-manager'`).
**Step 3: Write minimal implementation**
Implement exports:
- `normalizeVersion(version: string): string`
- `getRuntimePaths(home: string, version: string)`
- `resolveInstallHome(env)`
Keep pure and side-effect free.
**Step 4: Run test to verify it passes**
Run:
```bash
node --import tsx --test tests/lib/runtime-manager.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add src/lib/runtime-manager.ts tests/lib/runtime-manager.test.ts
git commit -m "feat(installer): add runtime manager core library"
```
---
## Phase 3: Move Launcher to Runtime-Aware Execution
### Task 3: Update launcher to run from managed runtime root
**Files:**
- Modify: `install/beadboard.mjs`
- Modify: `tests/scripts/beadboard-launcher.test.ts`
- Create: `tests/scripts/beadboard-launcher-runtime.test.ts`
**Step 1: Write failing test**
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import path from 'node:path';
const execFileAsync = promisify(execFile);
const launcherPath = path.resolve('install/beadboard.mjs');
test('status --json reports runtime root and install mode', async () => {
const { stdout } = await execFileAsync(process.execPath, [launcherPath, 'status', '--json']);
const payload = JSON.parse(stdout);
assert.ok(payload.runtimeRoot);
assert.ok(payload.installMode);
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/scripts/beadboard-launcher-runtime.test.ts
```
Expected: FAIL (missing fields).
**Step 3: Write minimal implementation**
In launcher:
- derive runtime home/version
- include `runtimeRoot`, `installMode`, `shimTarget` in JSON status
- preserve existing `start/open/status` behavior
**Step 4: Run tests to verify pass**
Run:
```bash
node --import tsx --test tests/scripts/beadboard-launcher.test.ts
node --import tsx --test tests/scripts/beadboard-launcher-runtime.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add install/beadboard.mjs tests/scripts/beadboard-launcher.test.ts tests/scripts/beadboard-launcher-runtime.test.ts
git commit -m "feat(launcher): add runtime-aware status metadata"
```
---
## Phase 4: Replace Repo-Bound Shim Targets
### Task 4: Make install wrappers point at runtime-managed target
**Files:**
- Modify: `install/install.sh`
- Modify: `install/install.ps1`
- Modify: `tests/scripts/install-wrappers-contract.test.ts`
- Modify: `tests/scripts/install-sh-smoke.test.ts`
- Create: `tests/scripts/install-legacy-migration.test.ts`
**Step 1: Write failing migration test**
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
// simulate legacy shim text and verify installer rewrites to runtime target
test('installer migrates legacy repo-bound shim to runtime-managed shim', async () => {
assert.fail('implement');
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/scripts/install-legacy-migration.test.ts
```
Expected: FAIL.
**Step 3: Write minimal implementation**
In both wrappers:
- install runtime metadata file (`~/.beadboard/runtime/current.json`)
- rewrite `bb`/`beadboard` shims to resolve runtime target first
- detect old shim signatures and replace atomically
**Step 4: Run wrapper tests**
Run:
```bash
node --import tsx --test tests/scripts/install-wrappers-contract.test.ts
node --import tsx --test tests/scripts/install-sh-smoke.test.ts
node --import tsx --test tests/scripts/install-legacy-migration.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add install/install.sh install/install.ps1 tests/scripts/install-wrappers-contract.test.ts tests/scripts/install-sh-smoke.test.ts tests/scripts/install-legacy-migration.test.ts
git commit -m "feat(installer): migrate shims to runtime-managed targets"
```
---
## Phase 5: Add npm-Global Entry and Self-Management Commands
### Task 5: Add CLI entrypoint commands (`self-update`, `uninstall`, `doctor`)
**Files:**
- Modify: `package.json`
- Create: `bin/beadboard.js`
- Create: `src/cli/beadboard-cli.ts`
- Create: `tests/cli/beadboard-cli.test.ts`
**Step 1: Write failing CLI tests**
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
import { runCli } from '../../src/cli/beadboard-cli';
test('doctor returns structured install diagnostics', async () => {
const out = await runCli(['doctor', '--json']);
assert.equal(out.ok, true);
assert.ok(out.installMode);
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/cli/beadboard-cli.test.ts
```
Expected: FAIL (missing module/command).
**Step 3: Write minimal implementation**
Add commands:
- `beadboard doctor --json`
- `beadboard self-update` (placeholder behavior with explicit output if publish not configured)
- `beadboard uninstall` (remove shims/runtime with `--yes` guard)
Add `bin` mapping in `package.json`.
**Step 4: Run test to verify pass**
Run:
```bash
node --import tsx --test tests/cli/beadboard-cli.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add package.json bin/beadboard.js src/cli/beadboard-cli.ts tests/cli/beadboard-cli.test.ts
git commit -m "feat(cli): add global entrypoint with doctor/update/uninstall commands"
```
---
## Phase 6: Driver Alignment + Remediation UX Finalization
### Task 6: Update driver preflight/remediation copy for npm-global-first flow
**Files:**
- Modify: `skills/beadboard-driver/scripts/lib/driver-lib.mjs`
- Modify: `skills/beadboard-driver/scripts/session-preflight.mjs`
- Modify: `tests/skills/beadboard-driver/resolve-bb.test.ts`
- Modify: `tests/skills/beadboard-driver/session-preflight.test.ts`
**Step 1: Write failing test assertion**
Add expectation:
- remediation mentions `npm i -g beadboard` as primary
- install script remains fallback
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts
node --import tsx --test tests/skills/beadboard-driver/session-preflight.test.ts
```
Expected: FAIL (copy mismatch).
**Step 3: Write minimal implementation**
Update remediation strings to:
1. npm-global install
2. fallback installer wrapper
3. BB_REPO override guidance
**Step 4: Re-run tests**
Run:
```bash
node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts
node --import tsx --test tests/skills/beadboard-driver/session-preflight.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add skills/beadboard-driver/scripts/lib/driver-lib.mjs skills/beadboard-driver/scripts/session-preflight.mjs tests/skills/beadboard-driver/resolve-bb.test.ts tests/skills/beadboard-driver/session-preflight.test.ts
git commit -m "feat(driver): prefer npm-global remediation with installer fallback"
```
---
## Phase 7: CI, Docs, and Release Readiness
### Task 7: Expand CI smoke and operator docs for global model
**Files:**
- Modify: `.github/workflows/installer-smoke.yml`
- Modify: `README.md`
- Modify: `docs/adr/2026-03-03-runtime-manager-global-install.md`
- Create: `docs/ops/global-install-rollout.md`
- Modify: `tests/scripts/installer-ci-contract.test.ts`
- Modify: `tests/docs/installer-quickstart-contract.test.ts`
**Step 1: Write failing test updates**
Add assertions for:
- npm-global command in docs
- runtime home path mentions
- CI step validating `beadboard doctor --json`
**Step 2: Run tests to verify fail**
Run:
```bash
node --import tsx --test tests/scripts/installer-ci-contract.test.ts
node --import tsx --test tests/docs/installer-quickstart-contract.test.ts
```
Expected: FAIL.
**Step 3: Write minimal implementation**
Update docs and workflow:
- install paths
- migration notes
- recovery playbook
- supported platforms matrix
**Step 4: Re-run tests**
Run:
```bash
node --import tsx --test tests/scripts/installer-ci-contract.test.ts
node --import tsx --test tests/docs/installer-quickstart-contract.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add .github/workflows/installer-smoke.yml README.md docs/adr/2026-03-03-runtime-manager-global-install.md docs/ops/global-install-rollout.md tests/scripts/installer-ci-contract.test.ts tests/docs/installer-quickstart-contract.test.ts
git commit -m "docs(ci): finalize global install runtime docs and smoke coverage"
```
---
## Phase 8: Final Verification + Bead Closeout
### Task 8: Full-gate verification and close
**Files:**
- Modify: `beadboard` issue notes via `bd update`
- Modify: `NEXT_SESSION_PROMPT.md`
**Step 1: Run full gates**
Run:
```bash
npm run typecheck
npm run lint
npm run test
```
Expected: PASS; if unrelated failures exist, capture exact files/tests.
**Step 2: Run targeted installer acceptance checks**
Run:
```bash
node --import tsx --test tests/lib/runtime-manager.test.ts
node --import tsx --test tests/scripts/beadboard-launcher-runtime.test.ts
node --import tsx --test tests/scripts/install-legacy-migration.test.ts
node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts
```
Expected: PASS.
**Step 3: Update beads with evidence**
Run:
```bash
bd update <bead-id> --notes "<commands run + pass/fail details>"
bd close <bead-id> --reason "<completed outcome>"
```
**Step 4: Update handoff**
Modify:
- `NEXT_SESSION_PROMPT.md` with shipped state + residual risks + next bead.
**Step 5: Commit**
```bash
git add NEXT_SESSION_PROMPT.md
git commit -m "chore: close runtime-manager rollout with verification evidence"
```
---
## References and Required Skills During Execution
1. `@test-driven-development`
2. `@verification-before-completion`
3. `@linus-beads-discipline`
4. `@beadboard-driver`
5. `@executing-plans` (required for implementation phase)
---
Plan complete and saved to `docs/plans/2026-03-03-global-install-runtime-manager.md`. Two execution options:
**1. Subagent-Driven (this session)** - I dispatch fresh subagent per task, review between tasks, fast iteration
**2. Parallel Session (separate)** - Open new session with executing-plans, batch execution with checkpoints
Which approach?