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.
- package/.agents/skills/harness-context/SKILL.md +13 -6
- package/.agents/skills/harness-debate-plan/SKILL.md +37 -20
- package/.agents/skills/harness-decisions/SKILL.md +1 -1
- package/.agents/skills/harness-eval/SKILL.md +6 -21
- package/.agents/skills/harness-governor/SKILL.md +4 -3
- package/.agents/skills/harness-orchestration/SKILL.md +41 -53
- package/.agents/skills/harness-plan/SKILL.md +23 -12
- package/.agents/skills/harness-review/SKILL.md +52 -0
- package/.agents/skills/harness-sentrux-setup/SKILL.md +16 -3
- package/.agents/skills/harness-steer/SKILL.md +14 -0
- package/.agents/skills/sentrux/SKILL.md +9 -9
- package/.pi/agents/harness/planning/decompose.md +7 -4
- package/.pi/agents/harness/planning/hypothesis-validator.md +2 -0
- package/.pi/agents/harness/planning/hypothesis.md +3 -1
- package/.pi/agents/harness/planning/plan-adversary.md +2 -0
- package/.pi/agents/harness/planning/plan-evaluator.md +2 -0
- package/.pi/agents/harness/planning/plan-synthesizer.md +25 -0
- package/.pi/agents/harness/planning/planning-context.md +48 -0
- package/.pi/agents/harness/planning/review-integrator.md +2 -0
- package/.pi/agents/harness/planning/sprint-contract-auditor.md +2 -0
- package/.pi/agents/harness/{adversary.md → reviewing/adversary.md} +3 -10
- package/.pi/agents/harness/{evaluator.md → reviewing/evaluator.md} +3 -12
- package/.pi/agents/harness/running/executor.md +45 -0
- package/.pi/agents/harness/sentrux-steward.md +51 -0
- package/.pi/extensions/00-harness-project-control.ts +133 -0
- package/.pi/extensions/00-posthog-network-bootstrap.ts +11 -0
- package/.pi/extensions/budget-guard.ts +2 -0
- package/.pi/extensions/debate-orchestrator.ts +2 -0
- package/.pi/extensions/harness-ask-user.ts +2 -2
- package/.pi/extensions/harness-debate-tools.ts +2 -2
- package/.pi/extensions/harness-live-widget.ts +60 -3
- package/.pi/extensions/harness-plan-approval.ts +64 -58
- package/.pi/extensions/harness-run-context.ts +715 -90
- package/.pi/extensions/harness-subagent-submit.ts +46 -12
- package/.pi/extensions/harness-subagents.ts +2 -2
- package/.pi/extensions/harness-telemetry.ts +2 -0
- package/.pi/extensions/harness-web-tools.ts +2 -2
- package/.pi/extensions/lib/extension-load-guard.ts +10 -0
- package/.pi/extensions/lib/harness-artifact-gate.ts +172 -0
- package/.pi/extensions/lib/harness-posthog.ts +9 -5
- package/.pi/extensions/lib/harness-spawn-topology.ts +165 -0
- package/.pi/extensions/lib/harness-subagent-auth.ts +1 -2
- package/.pi/extensions/lib/harness-subagent-policy.ts +28 -24
- package/.pi/extensions/lib/harness-subagent-precheck.ts +36 -10
- package/.pi/extensions/lib/harness-subagent-submit-pipeline.ts +66 -2
- package/.pi/extensions/lib/harness-subagent-submit-registry.ts +22 -22
- package/.pi/extensions/lib/harness-subagents-bridge.ts +7 -29
- package/.pi/extensions/lib/harness-subprocess-bootstrap.ts +73 -0
- package/.pi/extensions/lib/plan-approval/create-plan.ts +2 -3
- package/.pi/extensions/lib/plan-approval/resolve-disk.ts +102 -0
- package/.pi/extensions/lib/plan-approval/schema.ts +22 -8
- package/.pi/extensions/lib/plan-approval/types.ts +1 -1
- package/.pi/extensions/lib/plan-approval/validate.ts +2 -2
- package/.pi/extensions/lib/plan-approval-readiness.ts +192 -0
- package/.pi/extensions/lib/plan-debate-eligibility.ts +12 -5
- package/.pi/extensions/lib/plan-debate-gate.ts +22 -1
- package/.pi/extensions/lib/plan-debate-lanes.ts +32 -2
- package/.pi/extensions/lib/plan-review-gate.ts +8 -0
- package/.pi/extensions/lib/posthog-client.ts +76 -0
- package/.pi/extensions/lib/spawn-policy.ts +3 -3
- package/.pi/extensions/observation-bus.ts +2 -0
- package/.pi/extensions/policy-gate.ts +26 -19
- package/.pi/extensions/review-integrity.ts +91 -10
- package/.pi/extensions/sentrux-rules-sync.ts +2 -0
- package/.pi/extensions/test-diff-integrity.ts +1 -0
- package/.pi/extensions/trace-recorder.ts +2 -0
- package/.pi/harness/agents.manifest.json +37 -37
- package/.pi/harness/corpus/cron.example +8 -0
- package/.pi/harness/corpus/graphify-kb-updater.config.json +214 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.env.template +4 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.service +17 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.timer +11 -0
- package/.pi/harness/docs/adrs/0001-harness-constitution.md +2 -1
- package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +8 -6
- package/.pi/harness/docs/adrs/0009-sentrux-rules-lifecycle.md +6 -1
- package/.pi/harness/docs/adrs/0031-harness-run-context.md +1 -1
- package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +7 -0
- package/.pi/harness/docs/adrs/0034-darwin-plan-research-pipeline.md +3 -3
- package/.pi/harness/docs/adrs/0036-implementation-research-and-selective-debate.md +8 -5
- package/.pi/harness/docs/adrs/0039-harness-post-run-review-gate.md +47 -0
- package/.pi/harness/docs/adrs/0040-practice-grounded-orchestration.md +40 -0
- package/.pi/harness/docs/adrs/0041-intelligent-planning-reconnaissance.md +39 -0
- package/.pi/harness/docs/adrs/0042-agent-native-orchestration.md +35 -0
- package/.pi/harness/docs/adrs/0043-path-first-harness-tools.md +38 -0
- package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +37 -0
- package/.pi/harness/docs/adrs/0045-phase-scoped-agent-directories.md +33 -0
- package/.pi/harness/docs/adrs/README.md +11 -0
- package/.pi/harness/docs/graphify-kb-updater-runbook.md +163 -0
- package/.pi/harness/docs/practice-map.md +110 -0
- package/.pi/harness/env.harness.template +5 -3
- package/.pi/harness/evals/smoke/sentrux-stub.json +1 -1
- package/.pi/harness/evals/smoke/smoke-harness-plan.mjs +5 -2
- package/.pi/harness/specs/README.md +1 -1
- package/.pi/harness/specs/harness-run-context.schema.json +11 -0
- package/.pi/harness/specs/harness-spawn-context.schema.json +15 -1
- package/.pi/harness/specs/plan-execution-plan.schema.json +39 -1
- package/.pi/harness/specs/plan-packet.schema.json +4 -0
- package/.pi/harness/specs/plan-phase-status.schema.json +17 -0
- package/.pi/harness/specs/plan-phase-waiver.schema.json +25 -0
- package/.pi/harness/specs/plan-planning-context.schema.json +50 -0
- package/.pi/harness/specs/repair-brief.schema.json +45 -0
- package/.pi/harness/specs/review-outcome.schema.json +46 -0
- package/.pi/harness/specs/sentrux-manifest-proposal.schema.json +80 -0
- package/.pi/harness/specs/sentrux-signal.schema.json +43 -0
- package/.pi/harness/specs/steer-state.schema.json +20 -0
- package/.pi/lib/harness-context-mode-policy.ts +256 -0
- package/.pi/lib/harness-project-config.ts +91 -0
- package/.pi/lib/harness-repair-brief.ts +145 -0
- package/.pi/lib/harness-run-context.ts +591 -32
- package/.pi/lib/harness-ui-state.ts +114 -21
- package/.pi/prompts/harness-auto.md +10 -10
- package/.pi/prompts/harness-critic.md +3 -30
- package/.pi/prompts/harness-eval.md +4 -37
- package/.pi/prompts/harness-plan.md +116 -54
- package/.pi/prompts/harness-review.md +150 -15
- package/.pi/prompts/harness-run.md +62 -10
- package/.pi/prompts/harness-sentrux-steward.md +55 -0
- package/.pi/prompts/harness-setup.md +5 -4
- package/.pi/prompts/harness-steer.md +30 -0
- package/.pi/scripts/README.md +1 -0
- package/.pi/scripts/graphify-kb-updater.mjs +398 -0
- package/.pi/scripts/harness-agents-manifest.mjs +1 -1
- package/.pi/scripts/harness-project-toggle.mjs +129 -0
- package/.pi/scripts/harness-sentrux-cli.mjs +142 -0
- package/.pi/scripts/harness-verify.mjs +22 -6
- package/.pi/scripts/harness-web-policy-guard.mjs +68 -0
- package/.pi/scripts/validate-plan-dag.mjs +3 -3
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +23 -0
- package/README.md +94 -58
- package/package.json +5 -4
- package/.pi/agents/harness/executor.md +0 -47
- package/.pi/agents/harness/planning/scout-graphify.md +0 -37
- package/.pi/agents/harness/planning/scout-semantic.md +0 -39
- package/.pi/agents/harness/planning/scout-structure.md +0 -35
- package/.pi/prompts/git-sync.md +0 -124
- /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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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);
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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 (!
|
|
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 {
|
|
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 (!
|
|
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
|
|
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) => {
|