Status: Active plan for issue #129

# Restore Pi 0.80.8–0.84.1 SDK compatibility and add a packed-install tripwire

## Goal

Make the 0.2.3 source compile and test-clean against Pi 0.80.8 through 0.84.1 by migrating away from removed `AuthStorage` and `ModelRegistry.create(auth, path)`, and by fixing test infra that depends on removed faux-provider and TUI APIs.

## Verified current behavior (breaking points)

| Location | Symbol / pattern | Breaks in SDK >= 0.80.8 | Replacement |
|----------|-----------------|------------------------|-------------|
| `src/agent.ts:6` | `import { AuthStorage }` | Removed | `ModelRuntime.create()` |
| `src/agent.ts:248-249` | `AuthStorage.create(...); ModelRegistry.create(auth, ...)` | Both removed | `ModelRuntime.create({ authPath, modelsPath })` |
| `src/agent.ts:650-651` | Same disk-fallback pattern in `getRegistry()` | Same | Same — wrap in `ensureModelRuntime()` |
| `tests/usage-limit-integration.test.ts:34,59,64` | `loadFaux() → registerFauxProvider` | `registerFauxProvider` moved to compat only | `createFauxCore()` from `@earendil-works/pi-ai/providers/faux` + `ModelRuntime` wiring |
| `tests/workflow-editor.test.ts:7,39-40` | `import { TUI }; new TUI(mockTerminal, false)` | `TUI` is type-only since 0.84.1 | Mock the TUI-shaped object; no runtime construction |

## Dependencies

- None (issue #128 tracks parent epics but no blocking sibling).

## Allowed paths

1. Replace `AuthStorage` + `ModelRegistry.create()` in `src/agent.ts` with `ModelRuntime.create()`.
2. Wire `ModelRuntime`-backed model resolution where the old registry was used.
3. Swap `registerFauxProvider` → `createFauxCore` + runtime registration in usage-limit test.
4. Replace runtime `new TUI(...)` with a plain mock object in the editor test.
5. Add peer-dependency floor declarations to `package.json`.
6. Extend CI with a compatibility matrix job.

## Denied / out-of-scope

- Agent authority / tool-policy enforcement (issue #130).
- Foundation workflow redesign (#131).
- Broad upstream merging to Pi SDK.
- Changing how the host `modelRegistry` surface is shaped for extensions.

## Implementation steps (dependency-ordered)

### Step 1: Migrate `src/agent.ts` — core runtime layer

**Risk:** P1 if model resolution breaks for subagents.

1. Remove `AuthStorage` and import `ModelRuntime`.
2. Keep `WorkflowAgentOptions.modelRegistry`, `AgentRunOptions.modelRegistry`, `WorkflowManager.setModelRegistry()`, `/workflows-models`, and `ExtensionContext.modelRegistry` typed/named as `ModelRegistry`; these are still the host-facing contracts.
3. Add a memoized asynchronous disk fallback: `ModelRuntime.create({ authPath, modelsPath })`, then `new ModelRegistry(runtime)`. Reset the cached promise on rejection so a transient initialization failure is retryable.
4. Add one narrow, tested compatibility helper that obtains the underlying `ModelRuntime` from a `ModelRegistry` for `createAgentSession({ modelRuntime })`. Use the SDK's public runtime accessor if available at the floor; if the facade exposes no public accessor, isolate the structural access in this one helper and fail clearly rather than silently creating a second runtime that loses dynamically registered providers.
5. Make fallback registry initialization asynchronous where required. Preserve `listAvailableModelSpecs(registry?)` as a synchronous best-effort API: a supplied/shared registry returns immediately; an absent fallback may begin initialization and return `[]` until ready rather than blocking or changing the exported signature.
6. Continue resolving model specs with `ModelRegistry.find/getAvailable/getAll`, preserving `provider/modelId` and bare-ID precedence. Do not rename registry surfaces to runtime or invent methods such as `getAvailableSnapshot()` without verifying them on both supported SDK versions.
7. Pass the extracted runtime to `createAgentSession`; do not pass the removed `modelRegistry` session option on Pi 0.80.8+.
8. Run `npm run build` and the shared-registry tests after this step against both floor and latest SDKs.

### Step 2: Migrate `tests/usage-limit-integration.test.ts` — faux provider

1. Import `createFauxCore` and `fauxAssistantMessage` from `@earendil-works/pi-ai/providers/faux` instead of loading them via the compat path.
2. Build the faux provider with `createFauxCore(options)` (returns provider + stream functions).
3. Follow the actual Pi 0.84.1 faux-provider API and the upstream migrated test pattern already inspected during the audit: create the faux core, create an explicit `ModelRuntime`, and register/pass the exact provider/model objects supported by that API. Do not leave alternative pseudo-code in the implementation PR.
4. Keep `withFakeHomeAsync` isolation; remove `registerFauxProvider` / `unregister` calls.
5. Run `npm run test:unit -- tests/usage-limit-integration.test.ts`.

### Step 3: Migrate `tests/workflow-editor.test.ts` — TUI mock

1. Change `import { TUI }` at line 7 to `import { type TUI }`.
2. Replace `createMockTui()` at line 39-40: instead of `new TUI(mockTerminal, false)`, construct a plain object conforming to the `TUI` interface shape (only methods/properties actually used by the editor code under test).
3. Remove `import { Terminal }` if the mock terminal is now inline.
4. Run `npm run test:unit -- tests/workflow-editor.test.ts`.

### Step 4: Peer-dependency floor declarations

Update `package.json` peerDependencies to explicit floors:

```json
"peerDependencies": {
  "@earendil-works/pi-ai": ">=0.80.8",
  "@earendil-works/pi-coding-agent": ">=0.80.8",
  "@earendil-works/pi-tui": ">=0.80.8",
  "typebox": "*"
},
```

`pi-ai` is a direct peer because production source imports its `Model` types and the package's tests/runtime import its faux provider API; a clean packed install must not rely on npm hoisting coding-agent's transitive copy. No upper bound — the migration targets the lowest coherent Pi SDK tuple (0.80.8) and latest (0.84.1). The earlier proposed TUI 0.80.6 floor is invalid with coding-agent 0.80.8: coding-agent requires `pi-tui ^0.80.8`, so forcing a direct 0.80.6 peer installs two nominally incompatible TUI class copies.

### Step 5: CI compatibility matrix

Extend `.github/workflows/ci.yml` with reproducible floor/latest jobs. Use a clean `npm ci` baseline and a documented helper/script or npm aliases that install the three Pi packages at a matched version **without committing the temporary lockfile mutation**. The matrix must include the coherent floor tuple (`coding-agent`/`pi-ai`/`pi-tui` 0.80.8) plus current latest. Run at least build and targeted compatibility tests in both lanes; run the full gate in the latest lane. Avoid hard-coding 0.84.1 as “latest” indefinitely—use a scheduled/current-resolution lane or a deliberate version update mechanism whose resolved versions are logged.

Extend `.github/workflows/release.yml` so publishing depends on the same compatibility and package-smoke checks. Do not duplicate divergent shell logic between CI and release; call the shared scripts.

### Step 6: Packed-install tripwire

Add a checked-in smoke script that:

1. creates `npm pack` output in a temporary directory;
2. creates an isolated temporary HOME/project;
3. installs the tarball and compatible Pi peers from scratch;
4. loads the package through Pi 0.84.1's **actual exported** resource-loader/package-discovery API (verified from current SDK docs/types—no placeholder imports);
5. asserts both package extensions load and `/foundation_ui_compliance` is registered from the packed template; and
6. exits nonzero on diagnostics, missing files, or missing command/tool registration.

Run the script from CI and release. Keep all temporary files outside the repository and always clean them up.

## Targeted tests

| File | Purpose |
|------|---------|
| `tests/agent.test.ts` | Model resolution with shared registry |
| `tests/usage-limit-integration.test.ts` | Faux provider + provider limit detection |
| `tests/workflow-editor.test.ts` | Editor hooks (TUI-free) |
| `tests/workflow-shared-registry.test.ts` | Dynamic host model routing |
| `tests/workflow-engine-floor.test.ts` | Engine floor compatibility |

## Floor / latest compatibility verification

| Component | Floor | Latest tested |
|-----------|-------|---------------|
| `@earendil-works/pi-coding-agent` | 0.80.8 | 0.84.1 |
| `@earendil-works/pi-tui` | 0.80.8 | 0.84.1 |
| `@earendil-works/pi-ai` | 0.80.8 | 0.84.1 |
| Node.js | >=22.19.0 | current LTS |

The migration is designed for 0.80.8 (lowest proven). CI matrix exercises both floor and latest.

## Acceptance checklist (mapped to issue)

- [ ] TypeScript and targeted tests pass against Pi 0.80.8
- [ ] TypeScript and targeted tests pass against Pi 0.84.1
- [ ] A dynamically registered host model remains routable by workflow subagents
- [ ] `npm test` full gate passes on latest Pi
- [ ] Packed install loads the extension and registers the bundled workflow through Pi's resource loader
- [ ] Existing CI/release workflows extended (not replaced)
- [ ] No `AuthStorage`, `ModelRegistry.create(auth,path)`, `registerFauxProvider`, or `new TUI(...)` in source

## Rollback / recovery

If the migration fails, stop the replacement release and revert the candidate PR—not to a publishable “fix” using removed APIs. Keep 0.2.3 documented as incompatible with current Pi while correcting the runtime adapter. No persisted data migration is involved.

## Deferred work

- Upstream merge of `ModelRuntime` wiring patterns to Pi SDK (file issue in Pi repo if warranted).
- Agent authority / tool-policy enforcement (#130).
- A separate interactive Pi CLI smoke may be added later; the recovery gate itself still requires a real SDK resource-loader/package-discovery smoke, not a placeholder.
