21 KiB
pi-crew Next Upgrade Roadmap
Date: 2026-05-05 Source inputs:
docs/research-oh-my-pi-distillation.mddocs/source-runtime-refactor-map.md- Recent runtime hardening commits through
f5d47aa feat: surface run effectiveness evidence
This document tracks the next practical upgrades after the current scaffold/no-op subagent fix, runtime safety classification, cancellation provenance, intent audit trail, prompt pipeline artifacts, capability inventory artifacts, and run effectiveness reporting.
Current Baseline
Already implemented and pushed:
- Real child worker execution is the default.
- Implicit scaffold/no-op runs are blocked when worker execution is disabled by config/env.
- Explicit
runtime.mode=scaffoldremains available for dry-run prompt/artifact generation. - Run
summary.md,progress.md, andstatusnow expose effectiveness evidence. - Structured cancellation reasons flow through retry/cancel/team-runner/run events/metrics/UI snapshot.
cancel,cleanup,forget, andpruneaccept audit intent metadata.- Live-agent control distinguishes
steerfromfollow-upat live-control/API level. - Retry attempts have
attemptId; max-retry deadletters link to the finalattemptId. - Worker prompt pipeline and capability inventory metadata artifacts are written per task.
Priority Legend
- P0: correctness/safety issue; should be addressed before next release if feasible.
- P1: high user-visible value or reliability gain; good patch-release candidates.
- P2: larger subsystem work; should be planned and sequenced.
- P3: polish/UX/longer-term architecture.
P0 — Prevent Ineffective Completed Runs
P0.1 Enforce effectiveness policy for non-scaffold workers
Problem
summary/status now surface effectiveness evidence, but non-scaffold child-process/live-session runs can still end completed when task evidence is weak unless the existing mutation guard fires.
Target behavior
- For real workers, a run with completed tasks but no observable worker activity should be
blockedorfailed, not silentlycompleted. - Keep explicit scaffold dry-runs allowed, but label them as dry-runs.
- Policy should be configurable:
runtime.effectivenessGuard = "off" | "warn" | "block" | "fail"- default candidate:
warnfor read-only roles,blockfor mutating roles.
Suggested files
src/runtime/team-runner.tssrc/runtime/completion-guard.tssrc/state/types.tsif storing guard result on manifest/taskssrc/schema/config-schema.tssrc/config/config.tstest/unit/summary.test.tstest/unit/team-runner-merge.test.tsor newtest/unit/effectiveness-guard.test.ts
Implementation sketch
-
Extract run effectiveness calculation into a reusable exported helper, e.g.:
export interface RunEffectivenessSummary { completed: number; observable: number; noObservedWorkTaskIds: string[]; needsAttentionTaskIds: string[]; workerExecution: "enabled" | "disabled/scaffold"; severity: "ok" | "warning" | "blocked" | "failed"; } -
Use this helper for:
progress.mdsummary.mdstatus- policy enforcement before
run.completed.
-
For non-scaffold runs, if mutating tasks have no mutation/tool/model/transcript evidence:
- append
policy.actionwithreason: "ineffective_worker"; - set run
blockedorfaileddepending config; - include task IDs in
data.
- append
Acceptance criteria
- A mocked child-process run with no tool/model/transcript evidence does not report clean
completedby default. - Scaffold run still completes as explicit dry-run and displays
Worker execution: disabled/scaffold. statusclearly listsnoObservedWorkandneedsAttentiontask IDs.- Unit tests cover warn/block/fail modes.
Verification
npx tsc --noEmit
node --experimental-strip-types --test --test-concurrency=1 --test-timeout=30000 test/unit/effectiveness-guard.test.ts test/unit/summary.test.ts
npm run test:unit
P0.2 Make runtime safety visible in manifest and run events
Problem
runtime.safety exists in runtime resolution, but it is not persisted as first-class run metadata. Debugging currently requires reading events or inferred artifacts.
Target behavior
-
Manifest records resolved runtime:
{ "runtimeResolution": { "kind": "child-process", "requestedMode": "auto", "safety": "trusted", "fallback": "child-process", "reason": "..." } } -
run.runningorrun.blockedevent includes the same resolution.
Suggested files
src/state/types.tssrc/extension/team-tool/run.tssrc/runtime/background-runner.tssrc/extension/team-tool/status.tstest/unit/team-run.test.tstest/unit/runtime-resolver.test.ts
Acceptance criteria
statusshowsRuntime safety: trusted|explicit_dry_run|blocked.- Blocked disabled-worker runs persist enough evidence to explain why no subagents spawned.
- Existing manifest schema remains backward compatible.
P1 — Steering/Follow-up Semantics Beyond Live Control
P1.1 Persist separate steering and follow-up queues in mailbox state
Current state
follow-up-agent exists in live-control, but durable mailbox is still generic inbox/outbox and respond still has waiting-task semantics.
Target behavior
-
Mailbox messages can carry semantic kind:
kind?: "message" | "steer" | "follow-up" | "response" | "group_join"; priority?: "urgent" | "normal" | "low"; deliveryMode?: "interrupt" | "next_turn"; -
steer-agentappends durable steering queue entry when no live session is present. -
follow-up-agentappends durable follow-up queue entry, deliverable after task stop/resume. -
UI/status separates urgent steering from follow-up backlog.
Suggested files
src/state/mailbox.tssrc/runtime/live-agent-control.tssrc/runtime/live-agent-manager.tssrc/extension/team-tool/api.tssrc/extension/team-tool/respond.tssrc/ui/dashboard-panes/mailbox-pane.tstest/unit/mailbox-api.test.tstest/unit/live-agent-control.test.tstest/unit/respond-tool.test.ts
Acceptance criteria
- Steering and follow-up can be inspected separately.
- Existing inbox/outbox JSONL remains readable.
- Durable queue survives process/session switch.
- Realtime live delivery dedupes against durable replay.
P1.2 Clarify respond vs follow-up UX
Problem
respond is currently a waiting-task resume primitive. Users may expect it to send a general follow-up.
Target behavior
/team-respondremains only forwaitingtasks./team-follow-uporapi operation=follow-up-agentis documented as continuation prompt.- Error messages recommend the correct command.
Suggested files
src/extension/registration/commands.tssrc/extension/help.tsdocs/usage.mdtest/unit/registration-commands-coverage.test.tstest/unit/respond-tool.test.ts
P1 — Worker Lifecycle and Process Reliability
P1.3 Two-phase child process teardown
Current state
Child workers have improved post-exit stdio guards and bounded drains, but cancellation semantics can be made more deterministic.
Target behavior
Worker process cancellation returns structured status:
interface WorkerExitStatus {
exitCode: number | null;
cancelled: boolean;
timedOut: boolean;
killed: boolean;
signal?: string;
cleanupErrors: string[];
finalDrainMs: number;
}
Process lifecycle:
- graceful cancel/TERM;
- wait grace window;
- hard kill process tree;
- bounded stdout/stderr drain;
- mark session non-reusable.
Suggested files
src/runtime/child-pi.tssrc/runtime/pi-spawn.tssrc/runtime/post-exit-stdio-guard.tssrc/runtime/task-runner.tssrc/runtime/cancellation.tstest/unit/child-pi*.test.tstest/integration/mock-child-run.test.ts
Acceptance criteria
- Cancelled worker always produces terminal task event.
- Output drains are bounded.
- Status includes
cancelled/timedOut/killed. - No zombie/stale running task after cancellation.
P1.4 Reserve worker control channel before spawn
Problem
There can be a short window where a task is logically starting but cancel/steer cannot target a controller yet.
Target behavior
- Synchronously create a
WorkerRunCore/controller before async spawn. - Persist controller metadata in agent status.
- Cancel/steer requests can be queued immediately while startup is in progress.
- Controller is cleared in
finally.
Suggested files
src/runtime/task-runner.tssrc/runtime/agent-control.tssrc/runtime/live-agent-control.tssrc/runtime/crew-agent-records.tssrc/extension/team-tool/api.ts
Acceptance criteria
- Starting worker can be cancelled immediately.
- Durable control request written during startup is applied or recorded as terminal no-op with reason.
- Tests simulate control request before child process emits first output.
P1 — Cancellation and Attempt History
P1.5 Add event-tree provenance: parentEventId, attemptId, branchId
Current state
Retry attempts have attemptId, and deadletters link to final attempt. Event log has sequence and terminal fingerprints but no general event tree.
Target behavior
-
TeamEvent.metadatasupports:parentEventId?: string; attemptId?: string; branchId?: string; causationId?: string; correlationId?: string; -
Retry events, task started/completed/failed, deadletter, recovery events link by
attemptId. -
UI/status can show attempt timeline.
Suggested files
src/state/event-log.tssrc/state/types.tssrc/runtime/team-runner.tssrc/runtime/retry-executor.tssrc/runtime/recovery-recipes.tssrc/extension/team-tool/status.tstest/unit/event-metadata.test.tstest/unit/retry-executor.test.ts
Acceptance criteria
- Retry attempt events and terminal task events share attempt provenance.
- Deadletter records can be traced back to event sequence.
- Existing JSONL readers ignore missing provenance fields.
P1.6 Synthetic terminal results for cancelled in-flight operations
Problem
Run/task cancellation events are now structured, but worker/tool sub-operations can still lack synthetic terminal records if cancelled mid-operation.
Target behavior
- If a task started a worker/tool/model call and cancellation occurs, append a synthetic terminal record:
tool.cancelledorworker.cancelled- reason code/message
- startedAt/finishedAt
- attemptId if available
Suggested files
src/runtime/task-runner.tssrc/runtime/task-runner/progress.tssrc/runtime/child-pi.tssrc/runtime/cancellation.tssrc/state/contracts.tstest/unit/cancellation.test.ts
Acceptance criteria
- No started tool/model operation is left without terminal evidence after cancellation.
- Status/diagnostics can distinguish user cancel vs timeout vs shutdown.
P1 — Capability Inventory and Control Center
P1.7 Build run/project capability inventory view
Current state
Per-task capability artifacts exist. There is no unified project/run inventory UI/API yet.
Target behavior
/team-settings or new /team-control shows normalized inventory:
interface CapabilityItem {
id: string;
kind: "team" | "workflow" | "agent" | "skill" | "tool" | "hook" | "runtime" | "provider";
name: string;
source: "builtin" | "project" | "user" | "runtime";
path?: string;
state: "active" | "disabled" | "shadowed" | "missing";
disabledReason?: string;
shadowedBy?: string;
}
Suggested files
src/extension/team-tool/handle-settings.tssrc/extension/management.tssrc/agents/discover-agents.tssrc/teams/discover-teams.tssrc/workflows/discover-workflows.tssrc/runtime/skill-instructions.tsdocs/resource-formats.mdtest/unit/management.test.ts
Acceptance criteria
- Inventory is stable and sorted.
- Shadowed project/user/builtin resources are visible.
- Skill disabled/budget state is visible.
- No file path is used as the only stable ID.
P1.8 Persist capability disables by stable ID
Target behavior
- Operator can disable a skill/agent/team by capability ID.
- Disable config survives path relocation when resource identity remains stable.
- Status explains disabled reason.
Suggested files
src/config/config.tssrc/schema/config-schema.ts- discovery modules
test/unit/config-schema-validation.test.ts
P2 — Typed Hook Lifecycle
P2.1 Introduce typed hook contract
Target behavior
Define typed lifecycle gates:
before_run_startbefore_task_starttask_resultbefore_cancelbefore_forgetbefore_cleanupbefore_publishsession_before_switchrun_recovery
Each hook declares:
type HookMode = "blocking" | "non_blocking";
type HookOutcome = "allow" | "block" | "modify" | "diagnostic";
Errors are recorded in diagnostics/events, not uncontrolled exceptions.
Suggested files
- new
src/hooks/* src/extension/register.tssrc/runtime/team-runner.tssrc/extension/team-tool/cancel.tssrc/extension/team-tool/lifecycle-actions.tsdocs/resource-formats.mdtest/unit/hooks*.test.ts
Acceptance criteria
- Blocking hook can stop a run before worker start with clear event and status.
- Non-blocking hook failure records diagnostic and does not crash run.
- Hook context is redacted and bounded.
P2.2 Require intent via policy/hook for destructive actions
Current state
Intent is optional for cancel/cleanup/forget/prune.
Target behavior
-
Optional config:
{ "policy": { "requireIntentForDestructiveActions": true } } -
Actions requiring intent:
- cancel
- forget
- prune
- cleanup with force
- publish/release helpers if added
- worktree removal
Acceptance criteria
- Missing intent blocks action with actionable error.
- Existing tests can opt out or provide intent.
- Audit trail includes intent after approval.
P2 — Durable History vs Prompt Projection
P2.3 Separate durable run history projection from worker prompt text
Current state
Prompt pipeline artifacts exist, but context projection logic is still coupled to prompt construction in multiple places.
Target behavior
Introduce explicit projection functions:
transformRunContextBeforeWorkerStart(...)
convertRunHistoryToWorkerPrompt(...)
Rules:
- Durable history retains events, mailbox, artifacts, UI/runtime metadata.
- Worker prompt gets a bounded projection.
- UI/runtime events are not prompt text unless explicitly selected.
Suggested files
src/runtime/task-runner/prompt-pipeline.tssrc/runtime/task-runner/prompt-builder.tssrc/runtime/task-output-context.tssrc/runtime/task-runner.tstest/unit/task-runner-prompt-pipeline.test.ts
Acceptance criteria
- Prompt pipeline artifact identifies every projection source.
- Large event/mailbox history is summarized or referenced, not blindly embedded.
- Tests verify UI/runtime events are not injected as instructions.
P2 — Cooperative Cancellation for Internal Scans
P2.4 Add internal CancellationToken
Target behavior
A utility for long internal loops:
interface CancellationToken {
readonly aborted: boolean;
readonly reason?: CancellationReason;
heartbeat(stage?: string): void;
throwIfCancelled(): void;
wait(ms: number): Promise<void>;
}
Use it in:
- run index scans
- artifact cleanup
- mailbox validation/replay
- worktree cleanup
- diagnostic export
- large transcript/event reads
Suggested files
- new
src/runtime/cancellation-token.ts src/extension/run-index.tssrc/extension/registration/artifact-cleanup.tssrc/state/mailbox.tssrc/ui/run-snapshot-cache.tstest/unit/cancellation-token.test.ts
Acceptance criteria
- Long scan can abort within bounded cadence.
- Heartbeat stage appears in diagnostics/logs.
- Existing APIs can pass no token and keep current behavior.
P2 — Artifact Store Improvements
P2.5 Content-addressed blob artifacts
Target behavior
Large logs/transcripts/results are stored as blobs:
artifacts/blobs/sha256/<hash>
artifacts/blob-metadata/<hash>.json
Metadata includes:
- runId/taskId
- MIME/type
- producer
- original path/name
- size/hash
- redaction status
- retention policy
Suggested files
src/state/artifact-store.tssrc/runtime/task-runner.tssrc/ui/transcript-viewer.tssrc/extension/run-export.tssrc/extension/run-import.tstest/unit/artifact-store*.test.ts
Acceptance criteria
- Artifacts above threshold are blob-referenced.
- Run export/import preserves blobs.
- GC removes unreferenced blobs after retention.
- Path traversal protections remain intact.
P2 — UI and Dashboard Upgrades
P2.6 Show capability/effectiveness/cancellation panels in dashboard
Target behavior
Dashboard panes expose:
- run effectiveness score and no-observed-work tasks;
- cancellation reason and intent;
- capability inventory for selected task;
- attempt/deadletter timeline.
Suggested files
src/ui/run-dashboard.tssrc/ui/dashboard-panes/*src/ui/snapshot-types.tssrc/ui/run-snapshot-cache.tstest/unit/run-dashboard.test.ts- new pane tests
Acceptance criteria
- No heavy synchronous scans in render path.
- Pane output is width-safe.
- Snapshot cache provides precomputed compact data.
P2.7 Event-first UI stream
Target behavior
Move more live UI updates from file polling to semantic events:
task_startedtask_completedworker_statusmailbox_updatedeffectiveness_changed
Acceptance criteria
- Render scheduler remains coalesced and overlap-safe.
- UI still recovers from durable files after restart.
- File polling is fallback, not the hot path.
P2 — Raw Scan Entry Cache
P2.8 Cache raw entries, not final semantic query results
Target behavior
Shared raw scan cache for:
- runs
- artifacts
- mailbox files
- transcript chunks
- worktree roots
Then apply filters/sorts after retrieval.
Suggested files
src/runtime/manifest-cache.tssrc/ui/run-snapshot-cache.tssrc/extension/run-index.tssrc/utils/file-coalescer.ts
Acceptance criteria
- Deterministic sort order.
- State mutation invalidates relevant raw entries.
- Large workspaces do not trigger full rescans on every render/status.
P3 — Release/Install Hardening
P3.1 Tarball install smoke before publish
Target behavior
Release workflow requires:
npm run ci
npm pack --dry-run
npm pack
# install tarball in temp project
# verify pi extension load smoke
# verify npm package files and version/tag consistency
Suggested files
docs/publishing.mdpackage.jsonscripts.github/workflows/*if CI is added- optional
scripts/release-smoke.mjs
Acceptance criteria
- Packed tarball loads extension in temp Pi home.
- Version in package, changelog, tag, npm view are consistent.
- Release instructions include rollback notes.
Suggested Implementation Order
- P0.1 Effectiveness policy enforcement — prevents misleading completed runs.
- P0.2 Persist runtime safety — improves debugging for worker spawn issues.
- P1.3 Two-phase worker teardown — reduces stale/zombie worker risk.
- P1.1 Durable steering/follow-up queues — completes semantic split started at live-control level.
- P1.5 Event-tree provenance — builds on current
attemptIdwork. - P1.7 Capability inventory view — turns existing per-task artifacts into operator UX.
- P2.3 Durable history projection — reduces prompt/context risks.
- P2.4 CancellationToken — improves responsiveness of internal scans.
- P2.5 Blob artifacts — prevents log/transcript bloat.
- P2.6 Dashboard panels — surface all new evidence in UI.
Release Guidance
Before publishing a patch with these upgrades:
npx tsc --noEmit
npm run test:unit
npm run test:integration
npm pack --dry-run
For runtime/process changes also run targeted child-worker integration tests:
node --experimental-strip-types --test --test-concurrency=1 --test-timeout=60000 \
test/integration/mock-child-run.test.ts \
test/integration/mock-child-json-run.test.ts \
test/integration/phase6-runtime-hardening.test.ts
Do not publish without explicit user confirmation and a green verification pass.