ultimate-pi 0.17.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/.agents/skills/harness-context/SKILL.md +13 -6
  2. package/.agents/skills/harness-debate-plan/SKILL.md +37 -20
  3. package/.agents/skills/harness-decisions/SKILL.md +1 -1
  4. package/.agents/skills/harness-eval/SKILL.md +6 -21
  5. package/.agents/skills/harness-governor/SKILL.md +4 -3
  6. package/.agents/skills/harness-orchestration/SKILL.md +41 -53
  7. package/.agents/skills/harness-plan/SKILL.md +23 -12
  8. package/.agents/skills/harness-review/SKILL.md +52 -0
  9. package/.agents/skills/harness-sentrux-setup/SKILL.md +16 -3
  10. package/.agents/skills/harness-steer/SKILL.md +14 -0
  11. package/.agents/skills/sentrux/SKILL.md +9 -9
  12. package/.pi/agents/harness/planning/decompose.md +7 -4
  13. package/.pi/agents/harness/planning/hypothesis-validator.md +2 -0
  14. package/.pi/agents/harness/planning/hypothesis.md +3 -1
  15. package/.pi/agents/harness/planning/plan-adversary.md +2 -0
  16. package/.pi/agents/harness/planning/plan-evaluator.md +2 -0
  17. package/.pi/agents/harness/planning/plan-synthesizer.md +25 -0
  18. package/.pi/agents/harness/planning/planning-context.md +48 -0
  19. package/.pi/agents/harness/planning/review-integrator.md +2 -0
  20. package/.pi/agents/harness/planning/sprint-contract-auditor.md +2 -0
  21. package/.pi/agents/harness/{adversary.md → reviewing/adversary.md} +3 -10
  22. package/.pi/agents/harness/{evaluator.md → reviewing/evaluator.md} +3 -12
  23. package/.pi/agents/harness/running/executor.md +45 -0
  24. package/.pi/agents/harness/sentrux-steward.md +51 -0
  25. package/.pi/extensions/00-harness-project-control.ts +133 -0
  26. package/.pi/extensions/00-posthog-network-bootstrap.ts +11 -0
  27. package/.pi/extensions/budget-guard.ts +2 -0
  28. package/.pi/extensions/debate-orchestrator.ts +2 -0
  29. package/.pi/extensions/harness-ask-user.ts +2 -2
  30. package/.pi/extensions/harness-debate-tools.ts +2 -2
  31. package/.pi/extensions/harness-live-widget.ts +60 -3
  32. package/.pi/extensions/harness-plan-approval.ts +64 -58
  33. package/.pi/extensions/harness-run-context.ts +715 -90
  34. package/.pi/extensions/harness-subagent-submit.ts +46 -12
  35. package/.pi/extensions/harness-subagents.ts +2 -2
  36. package/.pi/extensions/harness-telemetry.ts +2 -0
  37. package/.pi/extensions/harness-web-tools.ts +2 -2
  38. package/.pi/extensions/lib/extension-load-guard.ts +10 -0
  39. package/.pi/extensions/lib/harness-artifact-gate.ts +172 -0
  40. package/.pi/extensions/lib/harness-posthog.ts +9 -5
  41. package/.pi/extensions/lib/harness-spawn-topology.ts +165 -0
  42. package/.pi/extensions/lib/harness-subagent-auth.ts +1 -2
  43. package/.pi/extensions/lib/harness-subagent-policy.ts +28 -24
  44. package/.pi/extensions/lib/harness-subagent-precheck.ts +36 -10
  45. package/.pi/extensions/lib/harness-subagent-submit-pipeline.ts +66 -2
  46. package/.pi/extensions/lib/harness-subagent-submit-registry.ts +22 -22
  47. package/.pi/extensions/lib/harness-subagents-bridge.ts +7 -29
  48. package/.pi/extensions/lib/harness-subprocess-bootstrap.ts +73 -0
  49. package/.pi/extensions/lib/plan-approval/create-plan.ts +2 -3
  50. package/.pi/extensions/lib/plan-approval/resolve-disk.ts +102 -0
  51. package/.pi/extensions/lib/plan-approval/schema.ts +22 -8
  52. package/.pi/extensions/lib/plan-approval/types.ts +1 -1
  53. package/.pi/extensions/lib/plan-approval/validate.ts +2 -2
  54. package/.pi/extensions/lib/plan-approval-readiness.ts +192 -0
  55. package/.pi/extensions/lib/plan-debate-eligibility.ts +12 -5
  56. package/.pi/extensions/lib/plan-debate-gate.ts +22 -1
  57. package/.pi/extensions/lib/plan-debate-lanes.ts +32 -2
  58. package/.pi/extensions/lib/plan-review-gate.ts +8 -0
  59. package/.pi/extensions/lib/posthog-client.ts +76 -0
  60. package/.pi/extensions/lib/spawn-policy.ts +3 -3
  61. package/.pi/extensions/observation-bus.ts +2 -0
  62. package/.pi/extensions/policy-gate.ts +26 -19
  63. package/.pi/extensions/review-integrity.ts +91 -10
  64. package/.pi/extensions/sentrux-rules-sync.ts +2 -0
  65. package/.pi/extensions/test-diff-integrity.ts +1 -0
  66. package/.pi/extensions/trace-recorder.ts +2 -0
  67. package/.pi/harness/agents.manifest.json +37 -37
  68. package/.pi/harness/corpus/cron.example +8 -0
  69. package/.pi/harness/corpus/graphify-kb-updater.config.json +214 -0
  70. package/.pi/harness/corpus/systemd/graphify-kb-updater.env.template +4 -0
  71. package/.pi/harness/corpus/systemd/graphify-kb-updater.service +17 -0
  72. package/.pi/harness/corpus/systemd/graphify-kb-updater.timer +11 -0
  73. package/.pi/harness/docs/adrs/0001-harness-constitution.md +2 -1
  74. package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +8 -6
  75. package/.pi/harness/docs/adrs/0009-sentrux-rules-lifecycle.md +6 -1
  76. package/.pi/harness/docs/adrs/0031-harness-run-context.md +1 -1
  77. package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +7 -0
  78. package/.pi/harness/docs/adrs/0034-darwin-plan-research-pipeline.md +3 -3
  79. package/.pi/harness/docs/adrs/0036-implementation-research-and-selective-debate.md +8 -5
  80. package/.pi/harness/docs/adrs/0039-harness-post-run-review-gate.md +47 -0
  81. package/.pi/harness/docs/adrs/0040-practice-grounded-orchestration.md +40 -0
  82. package/.pi/harness/docs/adrs/0041-intelligent-planning-reconnaissance.md +39 -0
  83. package/.pi/harness/docs/adrs/0042-agent-native-orchestration.md +35 -0
  84. package/.pi/harness/docs/adrs/0043-path-first-harness-tools.md +38 -0
  85. package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +37 -0
  86. package/.pi/harness/docs/adrs/0045-phase-scoped-agent-directories.md +33 -0
  87. package/.pi/harness/docs/adrs/README.md +11 -0
  88. package/.pi/harness/docs/graphify-kb-updater-runbook.md +163 -0
  89. package/.pi/harness/docs/practice-map.md +110 -0
  90. package/.pi/harness/env.harness.template +5 -3
  91. package/.pi/harness/evals/smoke/sentrux-stub.json +1 -1
  92. package/.pi/harness/evals/smoke/smoke-harness-plan.mjs +5 -2
  93. package/.pi/harness/specs/README.md +1 -1
  94. package/.pi/harness/specs/harness-run-context.schema.json +11 -0
  95. package/.pi/harness/specs/harness-spawn-context.schema.json +15 -1
  96. package/.pi/harness/specs/plan-execution-plan.schema.json +39 -1
  97. package/.pi/harness/specs/plan-packet.schema.json +4 -0
  98. package/.pi/harness/specs/plan-phase-status.schema.json +17 -0
  99. package/.pi/harness/specs/plan-phase-waiver.schema.json +25 -0
  100. package/.pi/harness/specs/plan-planning-context.schema.json +50 -0
  101. package/.pi/harness/specs/repair-brief.schema.json +45 -0
  102. package/.pi/harness/specs/review-outcome.schema.json +46 -0
  103. package/.pi/harness/specs/sentrux-manifest-proposal.schema.json +80 -0
  104. package/.pi/harness/specs/sentrux-signal.schema.json +43 -0
  105. package/.pi/harness/specs/steer-state.schema.json +20 -0
  106. package/.pi/lib/harness-context-mode-policy.ts +256 -0
  107. package/.pi/lib/harness-project-config.ts +91 -0
  108. package/.pi/lib/harness-repair-brief.ts +145 -0
  109. package/.pi/lib/harness-run-context.ts +591 -32
  110. package/.pi/lib/harness-ui-state.ts +114 -21
  111. package/.pi/prompts/harness-auto.md +10 -10
  112. package/.pi/prompts/harness-critic.md +3 -30
  113. package/.pi/prompts/harness-eval.md +4 -37
  114. package/.pi/prompts/harness-plan.md +116 -54
  115. package/.pi/prompts/harness-review.md +150 -15
  116. package/.pi/prompts/harness-run.md +62 -10
  117. package/.pi/prompts/harness-sentrux-steward.md +55 -0
  118. package/.pi/prompts/harness-setup.md +5 -4
  119. package/.pi/prompts/harness-steer.md +30 -0
  120. package/.pi/scripts/README.md +1 -0
  121. package/.pi/scripts/graphify-kb-updater.mjs +398 -0
  122. package/.pi/scripts/harness-agents-manifest.mjs +1 -1
  123. package/.pi/scripts/harness-project-toggle.mjs +129 -0
  124. package/.pi/scripts/harness-sentrux-cli.mjs +142 -0
  125. package/.pi/scripts/harness-verify.mjs +22 -6
  126. package/.pi/scripts/harness-web-policy-guard.mjs +68 -0
  127. package/.pi/scripts/validate-plan-dag.mjs +3 -3
  128. package/AGENTS.md +1 -0
  129. package/CHANGELOG.md +23 -0
  130. package/README.md +94 -58
  131. package/package.json +5 -4
  132. package/.pi/agents/harness/executor.md +0 -47
  133. package/.pi/agents/harness/planning/scout-graphify.md +0 -37
  134. package/.pi/agents/harness/planning/scout-semantic.md +0 -39
  135. package/.pi/agents/harness/planning/scout-structure.md +0 -35
  136. package/.pi/prompts/git-sync.md +0 -124
  137. /package/.pi/agents/harness/{tie-breaker.md → reviewing/tie-breaker.md} +0 -0
@@ -0,0 +1,48 @@
1
+ ---
2
+ description: Plan-phase optional reconnaissance subagent — graphify, sg, ccc (read-only). Prefer parent tool use.
3
+ tools: read, bash, ls, submit_planning_context
4
+ disallowed_tools: write, edit, ask_user, approve_plan, create_plan, subagent, grep, find
5
+ extensions: false
6
+ thinking: low
7
+ max_turns: 12
8
+ ---
9
+
10
+ You are the **Harness planning-context gatherer** (optional Phase 1 subprocess).
11
+
12
+ ## When to use
13
+
14
+ The **parent orchestrator** normally compiles `artifacts/planning-context.yaml` using tools directly. Spawn this agent only when reconnaissance is large enough to need a clean subprocess or context isolation.
15
+
16
+ ## Mission
17
+
18
+ Compile merged reconnaissance for the task in `HarnessSpawnContext`. You do **not** build the PlanPacket, approve plans, or mutate anything.
19
+
20
+ Use the repo tool hierarchy intelligently — pick tools that answer the task, not every tool by rote:
21
+
22
+ 1. **Architecture / relationships:** `graphify-out/GRAPH_REPORT.md`, then `graphify query`, `graphify explain`, `graphify path` (read-only).
23
+ 2. **Structure / symbols:** `sg -p '…'` — do not use `find` or `grep` for code search.
24
+ 3. **Semantic implementation:** `ccc search` (2–3 focused queries). The harness runs incremental `ccc index` before spawns — do **not** run `ccc index` or `ccc search --refresh`.
25
+
26
+ Skip lanes that add no value for this task. Record skipped lanes in `coverage.<lane>.status: skipped`.
27
+
28
+ ## Spawn context
29
+
30
+ Read `HarnessSpawnContext` (`task_summary`, `mode`, `plan_packet_path`, `risk_level`, `quick`). For `mode: revise`, read the existing plan first and focus on delta/risk areas.
31
+
32
+ When `quick: true`, you may set `coverage.semantic.status: skipped`.
33
+
34
+ ## Bash guardrails
35
+
36
+ Read-only only: no `graphify update`, installs, redirects (`>`, `>>`), or file creation.
37
+
38
+ ## Output
39
+
40
+ Before ending, call `submit_planning_context` exactly once with a full `PlanPlanningContext` document:
41
+
42
+ - `schema_version: "1.0.0"`
43
+ - `status`: `ok` | `partial` | `failed`
44
+ - `summary`: one paragraph
45
+ - `coverage`: `architecture`, `structure`, and `semantic` (each with `status`, `tools_used`, `summary`, `key_paths` as applicable)
46
+ - `findings`, `evidence_refs`, `open_questions`
47
+
48
+ Do not paste the artifact as prose — the tool write is the deliverable.
@@ -7,6 +7,8 @@ thinking: medium
7
7
  max_turns: 12
8
8
  ---
9
9
 
10
+ **Inspection role:** Recorder / integration PM (round synthesis). Parent is chair. See `.pi/harness/docs/practice-map.md`.
11
+
10
12
  ## Your task
11
13
 
12
14
  Synthesize evaluator, adversary, sprint audit, and (R1) hypothesis-validator lanes into one Review Gate round draft. Decide `review_gate_ready` from evidence, not optimism.
@@ -7,6 +7,8 @@ thinking: medium
7
7
  max_turns: 12
8
8
  ---
9
9
 
10
+ **Inspection role:** Definition of Done auditor (sprint contract). See `.pi/harness/docs/practice-map.md`.
11
+
10
12
  ## Your task
11
13
 
12
14
  Audit `execution_plan.sprint_contract` and work_item `done_criteria` against ADR-020 (Sprint Contract, Done Criteria Types, Keep Quality Left).
@@ -30,13 +30,6 @@ Pressure-test the candidate with adversarial reasoning and reproducible attacks.
30
30
 
31
31
  ## Output
32
32
 
33
- ```json
34
- {
35
- "block_merge": false,
36
- "adversary_report": { },
37
- "human_summary": "…",
38
- "recommendation": "proceed"
39
- }
40
- ```
41
-
42
- Use `recommendation`: `proceed`, `conditional_pass`, or `block`.
33
+ Call **`submit_adversary_report`** before exit (writes `artifacts/adversary-report.yaml`). Do not emit prose-only JSON for the parent to copy onto disk.
34
+
35
+ Use `recommendation`: `proceed`, `conditional_pass`, or `block`. Set `block_merge: true` when merge must halt.
@@ -17,7 +17,7 @@ Independently validate execution outcomes and emit structured verdicts. Spawn co
17
17
 
18
18
  1. Read `HarnessSpawnContext` and artifact paths (`plan_packet_path`, `run_dir`, trace refs).
19
19
  2. Reconstruct validation scope from the plan and on-disk run artifacts.
20
- 3. For `benchmark` mode: run or summarize deterministic checks (project tests, harness-verify if instructed in spawn prompt); collect metrics only you measured.
20
+ 3. For `benchmark` mode: run or summarize deterministic checks (project tests, harness-verify if instructed in spawn prompt); read `artifacts/sentrux-signal.yaml` and `artifacts/benchmark-log.yaml` when present — cite `check_pass`, `gate_status`, and `quality_signal_summary` as measured structural actuals (do not treat as optimization targets for the executor).
21
21
  4. For `verdict` mode: emit `EvalVerdict` matching `.pi/harness/specs/eval-verdict.schema.json`.
22
22
  5. Recommend only: `proceed_to_adversary`, `replan`, or `rollback`.
23
23
  6. Set `human_required` in structured output when blocked; never call `ask_user`.
@@ -31,15 +31,6 @@ Independently validate execution outcomes and emit structured verdicts. Spawn co
31
31
 
32
32
  ## Output
33
33
 
34
- End with a fenced `json` block:
34
+ Call **`submit_eval_verdict`** before exit with a document matching `eval-verdict.schema.json` (writes `artifacts/eval-verdict.yaml` under the run dir). Do not ask the parent to parse JSON or write verdict files.
35
35
 
36
- ```json
37
- {
38
- "eval_status": "pass",
39
- "eval_verdict": { },
40
- "human_summary": "…",
41
- "recommended_action": "proceed_to_adversary"
42
- }
43
- ```
44
-
45
- Use `eval_status`: `pass`, `conditional_pass`, or `fail`.
36
+ Use `status`: `pass`, `conditional_pass`, or `fail`. `recommended_action`: `proceed_to_adversary`, `replan`, or `rollback`.
@@ -0,0 +1,45 @@
1
+ ---
2
+ description: Harness executor that implements only within approved PlanPacket scope.
3
+ tools: read, write, edit, bash, grep, find, ls, submit_executor_handoff
4
+ extensions: true
5
+ disallowed_tools: ask_user
6
+ thinking: medium
7
+ max_turns: 20
8
+ ---
9
+
10
+ You are the Harness Executor.
11
+
12
+ ## Mission
13
+
14
+ Implement the approved plan with surgical diffs and strict scope control. The parent orchestrator spawned you with a `HarnessSpawnContext` appendix — use `plan_packet_path`, `run_dir`, and acceptance checks from that JSON.
15
+
16
+ ## Repair mode (`mode: repair`)
17
+
18
+ When spawn context sets `mode: repair`, read `repair_brief_path` (typically `artifacts/repair-brief.yaml`). Fix only what the brief lists — failed acceptance checks, `fix_directives`, and `priority_lake_ids`. Do **not** widen scope beyond `plan_packet_path`. Set `repair_attempt` in handoff metadata when the schema allows.
19
+
20
+ ## Process
21
+
22
+ 1. Read the approved `PlanPacket` at `plan_packet_path` from spawn context; extract allowed scope before any mutation. Approval is recorded in `run-context.yaml` (`plan_ready: true`) and subprocess policy bootstrap — not as a field inside `plan-packet.yaml`.
23
+ 2. When spawn context lists `critical_path_work_item_ids` (from `schedule_metadata`), implement those work items before non-critical items when practical (limiting-step / Grove).
24
+ 3. Implement only approved scope with minimal, reversible diffs.
25
+ 3. Run focused validations mapped to `acceptance_checks`.
26
+ 4. Prepare rollback metadata in `rollback_refs` (revert command, revert branch, patch bundle path under the run directory). **`submit_executor_handoff`** writes `handoff/executor-summary.yaml` and mirrors `rollback_refs` to `artifacts/executor-rollback.yaml` (YAML only — no `artifacts/*.json`).
27
+ 5. For plan-level ambiguity (wrong scope, missing acceptance), stop and return structured `scope_drift` — do not widen scope.
28
+ 6. Do not self-certify final quality; hand off evidence paths for evaluator/adversary.
29
+
30
+ ## Guardrails
31
+
32
+ - Only modify files required by the approved `PlanPacket`.
33
+ - Never speculate about code you have not read.
34
+ - If scope drift appears, stop with `execution_status: scope_drift` in your final JSON summary.
35
+ - Never set `inherit_context: true` on harness agents.
36
+ - Do not call `ask_user` — parent handles governance forks.
37
+
38
+ ## Output
39
+
40
+ Call **`submit_executor_handoff`** with a document matching `harness-executor-handoff.schema.json` before exit:
41
+
42
+ - `execution_status`: `completed`, `blocked`, or `scope_drift`
43
+ - `files_changed`, `validation_summary`, `rollback_refs`, `handoff_ready`
44
+
45
+ Do not write `artifacts/executor-rollback.json` — rollback is emitted as YAML by the submit pipeline.
@@ -0,0 +1,51 @@
1
+ ---
2
+ description: Propose architecture.manifest.json changes from graphify evidence (read-only governance steward).
3
+ tools: read, grep, find, ls, bash, submit_sentrux_manifest_proposal
4
+ disallowed_tools: write, edit, ask_user, approve_plan, create_plan, subagent
5
+ extensions: false
6
+ thinking: high
7
+ max_turns: 16
8
+ ---
9
+
10
+ You are the **Harness Sentrux Steward** — architectural **intent** governance, not setup or execution.
11
+
12
+ **Practice:** Architecture governance + fitness functions (Ford/Richards); integrated change control (PMBOK). See `.pi/harness/docs/practice-map.md` phase 4e.
13
+
14
+ ## Mission
15
+
16
+ Propose updates to `.pi/harness/sentrux/architecture.manifest.json` when the codebase or plan introduces a **new bounded context**, **new forbidden dependency class**, or **evidence-backed constraint tuning**. You never write the manifest, `rules.toml`, or merge patches yourself.
17
+
18
+ ## Spawn context
19
+
20
+ Read `HarnessSpawnContext` (`run_id`, `run_dir`, `plan_packet_path`, `task_summary`, scope hints). Read `artifacts/planning-context.yaml` and `artifacts/execution-plan-draft.yaml` when paths are provided.
21
+
22
+ ## Protocol (graphify-first)
23
+
24
+ 1. Read `graphify-out/GRAPH_REPORT.md` — god nodes, communities, surprising edges for paths in scope.
25
+ 2. Run **targeted** read-only graphify (no `graphify update`):
26
+ - `graphify query "<module> coupling boundaries layers"`
27
+ - `graphify path "<concept A>" "<concept B>"` when proposing a new boundary
28
+ - `graphify explain "Modularity"` or `"Architecture governance"` for corpus-backed rationale
29
+ 3. Compare manifest layers/boundaries to plan scope and repo structure (`sg -p` for import edges when proposing boundaries).
30
+ 4. Optional: `sentrux check .` — cite violation messages only; do not fix code.
31
+ 5. Classify proposal:
32
+ - `none` — existing layer globs cover changes; no new coupling class
33
+ - `tune_constraint` — e.g. `max_cc` with sentrux/graphify evidence
34
+ - `add_boundary` — new forbidden import direction
35
+ - `add_layer` / `split_layer` — new bounded context or split overloaded layer
36
+
37
+ ## Output
38
+
39
+ Call **`submit_sentrux_manifest_proposal`** before exit with document matching `sentrux-manifest-proposal.schema.json` → `artifacts/sentrux-manifest-proposal.yaml`.
40
+
41
+ - `manifest_patch`: JSON Merge Patch against current manifest (minimal diff).
42
+ - `evidence[]`: at least one entry per non-`none` change; prefer `source: graphify`.
43
+ - `adr_required: true` and `adr_draft` when material (new layer or boundary affecting multiple agents).
44
+ - `human_required: true` when `change_class` is not `none` and not a single numeric `tune_constraint` with clear sentrux evidence.
45
+
46
+ ## Guardrails
47
+
48
+ - Read-only — no file mutations, no `harness-sentrux-bootstrap`, no `/harness-sentrux-sync`.
49
+ - Do not duplicate full WBS decomposition — read planning artifacts instead.
50
+ - Do not auto-sync rules from directory trees.
51
+ - Never set `inherit_context: true`.
@@ -0,0 +1,133 @@
1
+ /**
2
+ * harness-project-control — always-on enable/disable for harness governance.
3
+ *
4
+ * Writes `.pi/harness/project.json`, blocks workflow slash commands while disabled,
5
+ * and emits `harness-project-enabled:changed` so live TUI surfaces update immediately.
6
+ */
7
+
8
+ import type {
9
+ ExtensionAPI,
10
+ ExtensionCommandContext,
11
+ } from "@earendil-works/pi-coding-agent";
12
+ import {
13
+ isHarnessProjectEnabled,
14
+ isHarnessWorkflowCommand,
15
+ readHarnessProjectConfig,
16
+ writeHarnessProjectEnabled,
17
+ } from "../lib/harness-project-config.js";
18
+ import { parseHarnessSlashInput } from "../lib/harness-run-context.js";
19
+
20
+ function showCommandMessage(
21
+ pi: ExtensionAPI,
22
+ ctx: ExtensionCommandContext,
23
+ text: string,
24
+ ): void {
25
+ if (ctx.hasUI) {
26
+ ctx.ui.notify(text, "info");
27
+ return;
28
+ }
29
+ pi.sendMessage({
30
+ customType: "harness-project-control",
31
+ content: text,
32
+ display: true,
33
+ });
34
+ }
35
+
36
+ function formatStatus(projectRoot: string): string {
37
+ const config = readHarnessProjectConfig(projectRoot);
38
+ const env = process.env.HARNESS_ENABLED?.trim();
39
+ const lines = [
40
+ `Harness governance: ${config.enabled ? "enabled" : "disabled"}`,
41
+ `Config: .pi/harness/project.json`,
42
+ ];
43
+ if (env) {
44
+ lines.push(`Env override: HARNESS_ENABLED=${env}`);
45
+ }
46
+ if (config.updated_at) {
47
+ lines.push(`Updated: ${config.updated_at}`);
48
+ }
49
+ if (!config.enabled) {
50
+ lines.push(
51
+ "Workflow commands (/harness-plan, /harness-run, …) are blocked until you run /harness-enable.",
52
+ );
53
+ } else {
54
+ lines.push("Run /harness-disable to turn governance off.");
55
+ }
56
+ return lines.join("\n");
57
+ }
58
+
59
+ export default function harnessProjectControl(pi: ExtensionAPI) {
60
+ pi.on("input", async (event) => {
61
+ if (event.source === "extension") {
62
+ return { action: "continue" as const };
63
+ }
64
+ const parsed = parseHarnessSlashInput(event.text);
65
+ if (!parsed || !isHarnessWorkflowCommand(parsed.command)) {
66
+ return { action: "continue" as const };
67
+ }
68
+ if (isHarnessProjectEnabled()) {
69
+ return { action: "continue" as const };
70
+ }
71
+ return {
72
+ action: "handled" as const,
73
+ message: [
74
+ `Harness governance is disabled — /${parsed.command} was not started.`,
75
+ "Run /harness-enable to restore the workflow command surface.",
76
+ ].join("\n"),
77
+ };
78
+ });
79
+
80
+ pi.registerCommand("harness-enable", {
81
+ description: "Enable harness governance for this project",
82
+ handler: async (_args, ctx) => {
83
+ const projectRoot = process.cwd();
84
+ const config = writeHarnessProjectEnabled(projectRoot, true);
85
+ const effectiveConfig = readHarnessProjectConfig(projectRoot);
86
+ pi.events.emit("harness-project-enabled:changed", {
87
+ enabled: effectiveConfig.enabled,
88
+ projectRoot,
89
+ updated_at: config.updated_at,
90
+ });
91
+ showCommandMessage(
92
+ pi,
93
+ ctx,
94
+ [
95
+ "Harness governance enabled.",
96
+ `Wrote .pi/harness/project.json (enabled=true, updated ${config.updated_at}).`,
97
+ "Live TUI surfaces were refreshed.",
98
+ ].join("\n"),
99
+ );
100
+ },
101
+ });
102
+
103
+ pi.registerCommand("harness-disable", {
104
+ description: "Disable harness governance for this project",
105
+ handler: async (_args, ctx) => {
106
+ const projectRoot = process.cwd();
107
+ const config = writeHarnessProjectEnabled(projectRoot, false);
108
+ const effectiveConfig = readHarnessProjectConfig(projectRoot);
109
+ pi.events.emit("harness-project-enabled:changed", {
110
+ enabled: effectiveConfig.enabled,
111
+ projectRoot,
112
+ updated_at: config.updated_at,
113
+ });
114
+ showCommandMessage(
115
+ pi,
116
+ ctx,
117
+ [
118
+ "Harness governance disabled.",
119
+ `Wrote .pi/harness/project.json (enabled=false, updated ${config.updated_at}).`,
120
+ "Workflow slash commands are blocked immediately.",
121
+ "Live TUI surfaces were refreshed.",
122
+ ].join("\n"),
123
+ );
124
+ },
125
+ });
126
+
127
+ pi.registerCommand("harness-enabled-status", {
128
+ description: "Show whether harness governance is enabled for this project",
129
+ handler: async (_args, ctx) => {
130
+ showCommandMessage(pi, ctx, formatStatus(process.cwd()));
131
+ },
132
+ });
133
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Load before other extensions: IPv4-first fetch for *.posthog.com (@posthog/pi uses global fetch).
3
+ */
4
+
5
+ import { installPostHogFetchPatch } from "./lib/posthog-client.js";
6
+
7
+ installPostHogFetchPatch();
8
+
9
+ export default function posthogNetworkBootstrap() {
10
+ // Side effects run at module load; no hooks required.
11
+ }
@@ -12,6 +12,7 @@ import {
12
12
  isHarnessBudgetEnforceOn,
13
13
  shouldEmitBlockingBudgetExhausted,
14
14
  } from "../lib/harness-budget-enforce.js";
15
+ import { isHarnessProjectEnabled } from "../lib/harness-project-config.js";
15
16
  import { getRunIdFromSession } from "../lib/harness-run-context.js";
16
17
 
17
18
  type HarnessPhase = "plan" | "execute" | "evaluate" | "adversary" | "merge";
@@ -203,6 +204,7 @@ async function emitBudgetEvent(
203
204
  const debouncedSoftLimit = new Map<string, boolean>();
204
205
 
205
206
  export default function budgetGuard(pi: ExtensionAPI) {
207
+ if (!isHarnessProjectEnabled()) return;
206
208
  pi.on("tool_call", async (_event, ctx) => {
207
209
  const policy = getPolicyContext(ctx);
208
210
  if (policy.phase === null || policy.budgetBypass) return undefined;
@@ -6,6 +6,7 @@
6
6
 
7
7
  import { join } from "node:path";
8
8
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
9
+ import { isHarnessProjectEnabled } from "../lib/harness-project-config.js";
9
10
  import { getRunIdFromSession } from "../lib/harness-run-context.js";
10
11
  import {
11
12
  acceptDebateRound,
@@ -32,6 +33,7 @@ function getRunId(ctx: {
32
33
  }
33
34
 
34
35
  export default function debateOrchestrator(pi: ExtensionAPI) {
36
+ if (!isHarnessProjectEnabled()) return;
35
37
  const hooks = {
36
38
  appendEntry: (customType: string, data: unknown) =>
37
39
  pi.appendEntry(customType, data),
@@ -18,13 +18,13 @@ import {
18
18
  toToolDetails,
19
19
  validateAskParams,
20
20
  } from "./lib/ask-user/validate.js";
21
- import { claimExtensionLoad } from "./lib/extension-load-guard.js";
21
+ import { claimHarnessGovernanceLoad } from "./lib/extension-load-guard.js";
22
22
 
23
23
  // @ts-expect-error pi extensions run as ESM
24
24
  const MODULE_URL = import.meta.url;
25
25
 
26
26
  export default function harnessAskUser(pi: ExtensionAPI) {
27
- if (!claimExtensionLoad("harness-ask-user", MODULE_URL)) return;
27
+ if (!claimHarnessGovernanceLoad("harness-ask-user", MODULE_URL)) return;
28
28
  pi.registerTool({
29
29
  name: "ask_user",
30
30
  label: "Ask User",
@@ -24,7 +24,7 @@ import {
24
24
  openDebateBus,
25
25
  } from "./lib/debate-bus-core.js";
26
26
  import { getDebateState } from "./lib/debate-bus-state.js";
27
- import { claimExtensionLoad } from "./lib/extension-load-guard.js";
27
+ import { claimHarnessGovernanceLoad } from "./lib/extension-load-guard.js";
28
28
  import { captureHarnessEvent } from "./lib/harness-posthog.js";
29
29
  import { DEBATE_AGENT_SUBMIT_TOOL } from "./lib/harness-subagent-submit-registry.js";
30
30
  import {
@@ -115,7 +115,7 @@ function subagentResults(
115
115
  const USE_SUBMIT_TOOLS = process.env.HARNESS_SUBMIT_TOOLS !== "0";
116
116
 
117
117
  export default function harnessDebateTools(pi: ExtensionAPI) {
118
- if (!claimExtensionLoad("harness-debate-tools", MODULE_URL)) return;
118
+ if (!claimHarnessGovernanceLoad("harness-debate-tools", MODULE_URL)) return;
119
119
 
120
120
  pi.on("tool_result", async (event, ctx) => {
121
121
  if (event.isError || event.toolName !== "subagent") return;
@@ -2,6 +2,8 @@ import type {
2
2
  ExtensionAPI,
3
3
  ExtensionContext,
4
4
  } from "@earendil-works/pi-coding-agent";
5
+ import { isHarnessProjectEnabled } from "../lib/harness-project-config.js";
6
+ import { evaluateCrossSessionResume } from "../lib/harness-run-context.js";
5
7
  import {
6
8
  deriveHarnessStatusHint,
7
9
  formatHarnessPhaseLabel,
@@ -244,7 +246,7 @@ export default function harnessLiveWidget(pi: ExtensionAPI) {
244
246
  let mountCtx: ExtensionContext | null = null;
245
247
 
246
248
  function mountHarnessWidget(ctx: ExtensionContext): void {
247
- if (!ctx.hasUI) return;
249
+ if (!ctx.hasUI || !isHarnessProjectEnabled()) return;
248
250
  const state = stateStore.refresh(ctx);
249
251
  lastRenderHash = computeRenderHash(state);
250
252
 
@@ -269,9 +271,21 @@ export default function harnessLiveWidget(pi: ExtensionAPI) {
269
271
  updateStatusFallback(ctx, state);
270
272
  }
271
273
 
274
+ function clearHarnessWidget(ctx: ExtensionContext): void {
275
+ if (!ctx.hasUI) return;
276
+ const tui = tuiHandle;
277
+ ctx.ui.setWidget("harness-live", undefined);
278
+ ctx.ui.setStatus("harness-mode", undefined);
279
+ widgetMounted = false;
280
+ tuiHandle = null;
281
+ component = null;
282
+ lastRenderHash = "";
283
+ tui?.requestRender();
284
+ }
285
+
272
286
  function remountHarnessLiveWidget(ctx: ExtensionContext): void {
273
287
  if (!ctx.hasUI || !widgetMounted) return;
274
- ctx.ui.setWidget("harness-live", undefined);
288
+ clearHarnessWidget(ctx);
275
289
  mountHarnessWidget(ctx);
276
290
  }
277
291
 
@@ -283,6 +297,36 @@ export default function harnessLiveWidget(pi: ExtensionAPI) {
283
297
  if (mountCtx) remountHarnessLiveWidget(mountCtx);
284
298
  });
285
299
 
300
+ pi.events.on("harness-run-context:updated", () => {
301
+ stateStore.setCrossSessionResumeCommand(null);
302
+ if (mountCtx) scheduleRefresh(mountCtx);
303
+ });
304
+
305
+ pi.events.on("harness-cross-session-resume", (payload: unknown) => {
306
+ const data =
307
+ payload && typeof payload === "object"
308
+ ? (payload as { resume_command?: string })
309
+ : null;
310
+ const cmd =
311
+ typeof data?.resume_command === "string" ? data.resume_command : null;
312
+ stateStore.setCrossSessionResumeCommand(cmd);
313
+ if (mountCtx) scheduleRefresh(mountCtx);
314
+ });
315
+
316
+ pi.events.on("harness-project-enabled:changed", (payload: unknown) => {
317
+ const data =
318
+ payload && typeof payload === "object"
319
+ ? (payload as { enabled?: boolean })
320
+ : null;
321
+ if (!mountCtx || typeof data?.enabled !== "boolean") return;
322
+ if (data.enabled) {
323
+ mountHarnessWidget(mountCtx);
324
+ tuiHandle?.requestRender();
325
+ return;
326
+ }
327
+ clearHarnessWidget(mountCtx);
328
+ });
329
+
286
330
  function updateStatusFallback(
287
331
  ctx: ExtensionContext,
288
332
  state: HarnessUiState,
@@ -304,6 +348,7 @@ export default function harnessLiveWidget(pi: ExtensionAPI) {
304
348
  policyDecision: state.policyDecision,
305
349
  flowSubstate: state.flowSubstate,
306
350
  nextRecommendedCommand: state.nextRecommendedCommand,
351
+ crossSessionResumeCommand: state.crossSessionResumeCommand,
307
352
  });
308
353
  }
309
354
 
@@ -312,6 +357,10 @@ export default function harnessLiveWidget(pi: ExtensionAPI) {
312
357
  refreshQueued = true;
313
358
  queueMicrotask(() => {
314
359
  refreshQueued = false;
360
+ if (!isHarnessProjectEnabled()) {
361
+ clearHarnessWidget(ctx);
362
+ return;
363
+ }
315
364
  const state = stateStore.refresh(ctx);
316
365
  const hash = computeRenderHash(state);
317
366
  updateStatusFallback(ctx, state);
@@ -322,9 +371,17 @@ export default function harnessLiveWidget(pi: ExtensionAPI) {
322
371
  });
323
372
  }
324
373
 
325
- pi.on("session_start", (_event, ctx) => {
374
+ pi.on("session_start", async (_event, ctx) => {
326
375
  mountCtx = ctx;
327
376
  mountHarnessWidget(ctx);
377
+ const info = await evaluateCrossSessionResume(
378
+ process.cwd(),
379
+ ctx.sessionManager.getEntries(),
380
+ );
381
+ if (info) {
382
+ stateStore.setCrossSessionResumeCommand(info.resumeCommand);
383
+ scheduleRefresh(ctx);
384
+ }
328
385
  });
329
386
 
330
387
  pi.on("context", (_event, ctx) => {