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

14 KiB

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:

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:

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
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:

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:

node --import tsx --test tests/docs/runtime-manager-adr-contract.test.ts

Expected: PASS.

Step 5: Commit

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

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:

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:

node --import tsx --test tests/lib/runtime-manager.test.ts

Expected: PASS.

Step 5: Commit

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

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:

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:

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

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

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:

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:

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

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

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:

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:

node --import tsx --test tests/cli/beadboard-cli.test.ts

Expected: PASS.

Step 5: Commit

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:

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:

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

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:

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:

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

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:

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:

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:

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

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?