ultimate-pi 0.14.0 → 0.16.0

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 (92) hide show
  1. package/.agents/skills/harness-debate-plan/SKILL.md +41 -61
  2. package/.agents/skills/harness-governor/SKILL.md +11 -0
  3. package/.agents/skills/harness-orchestration/SKILL.md +5 -3
  4. package/.agents/skills/harness-plan/SKILL.md +11 -9
  5. package/.pi/agents/harness/adversary.md +1 -1
  6. package/.pi/agents/harness/evaluator.md +1 -1
  7. package/.pi/agents/harness/executor.md +1 -1
  8. package/.pi/agents/harness/incident-recorder.md +1 -1
  9. package/.pi/agents/harness/meta-optimizer.md +1 -1
  10. package/.pi/agents/harness/planning/decompose.md +8 -35
  11. package/.pi/agents/harness/planning/execution-plan-author.md +27 -15
  12. package/.pi/agents/harness/planning/hypothesis-validator.md +23 -6
  13. package/.pi/agents/harness/planning/hypothesis.md +4 -27
  14. package/.pi/agents/harness/planning/implementation-researcher.md +43 -0
  15. package/.pi/agents/harness/planning/plan-adversary.md +20 -5
  16. package/.pi/agents/harness/planning/plan-evaluator.md +28 -6
  17. package/.pi/agents/harness/planning/review-integrator.md +23 -10
  18. package/.pi/agents/harness/planning/scout-graphify.md +4 -23
  19. package/.pi/agents/harness/planning/scout-semantic.md +3 -18
  20. package/.pi/agents/harness/planning/scout-structure.md +3 -18
  21. package/.pi/agents/harness/planning/sprint-contract-auditor.md +22 -6
  22. package/.pi/agents/harness/planning/stack-researcher.md +21 -11
  23. package/.pi/agents/harness/tie-breaker.md +1 -1
  24. package/.pi/agents/harness/trace-librarian.md +1 -1
  25. package/.pi/extensions/budget-guard.ts +33 -19
  26. package/.pi/extensions/harness-debate-tools.ts +280 -19
  27. package/.pi/extensions/harness-live-widget.ts +39 -159
  28. package/.pi/extensions/harness-plan-approval.ts +47 -5
  29. package/.pi/extensions/harness-run-context.ts +96 -2
  30. package/.pi/extensions/harness-subagent-submit.ts +195 -0
  31. package/.pi/extensions/lib/debate-bus-core.ts +108 -17
  32. package/.pi/extensions/lib/debate-bus-state.ts +6 -0
  33. package/.pi/extensions/lib/harness-subagent-policy.ts +45 -0
  34. package/.pi/extensions/lib/harness-subagent-submit-pipeline.ts +82 -0
  35. package/.pi/extensions/lib/harness-subagent-submit-registry.ts +172 -0
  36. package/.pi/extensions/lib/harness-subagents-bridge.ts +42 -0
  37. package/.pi/extensions/lib/plan-approval/plan-review.ts +56 -0
  38. package/.pi/extensions/lib/plan-approval/types.ts +1 -0
  39. package/.pi/extensions/lib/plan-debate-eligibility.ts +214 -0
  40. package/.pi/extensions/lib/plan-debate-focus.ts +151 -0
  41. package/.pi/extensions/lib/plan-debate-gate.ts +88 -34
  42. package/.pi/extensions/lib/plan-debate-lane.ts +15 -0
  43. package/.pi/extensions/lib/plan-debate-lanes.ts +44 -0
  44. package/.pi/extensions/lib/plan-debate-round-status.ts +63 -20
  45. package/.pi/extensions/lib/plan-messenger.ts +93 -17
  46. package/.pi/extensions/policy-gate.ts +1 -1
  47. package/.pi/harness/README.md +1 -1
  48. package/.pi/harness/agents.manifest.json +25 -21
  49. package/.pi/harness/docs/adrs/0034-darwin-plan-research-pipeline.md +1 -3
  50. package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +13 -5
  51. package/.pi/harness/docs/adrs/0036-implementation-research-and-selective-debate.md +51 -0
  52. package/.pi/harness/docs/adrs/0037-subagent-submit-tools.md +31 -0
  53. package/.pi/harness/docs/adrs/0038-budget-telemetry-only.md +23 -0
  54. package/.pi/harness/docs/adrs/README.md +4 -0
  55. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-low-light/artifacts/implementation-research.yaml +28 -0
  56. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-low-light/artifacts/review-round-r1.yaml +24 -0
  57. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-low-light/artifacts/review-round-r2.yaml +25 -0
  58. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-low-light/plan-packet.yaml +196 -0
  59. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-low-light/plan-review.md +14 -0
  60. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-low-light/research-brief.yaml +62 -0
  61. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/implementation-research.yaml +28 -0
  62. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/review-round-r2.yaml +24 -0
  63. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/review-round-r3.yaml +24 -0
  64. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/research-brief.yaml +29 -0
  65. package/.pi/harness/evals/smoke/smoke-harness-plan.mjs +97 -16
  66. package/.pi/harness/specs/harness-executor-handoff.schema.json +19 -0
  67. package/.pi/harness/specs/harness-human-required.schema.json +16 -0
  68. package/.pi/harness/specs/plan-implementation-research-brief.schema.json +128 -0
  69. package/.pi/harness/specs/plan-review-round-draft.schema.json +1 -1
  70. package/.pi/harness/specs/plan-scout-findings.schema.json +19 -0
  71. package/.pi/harness/specs/round-result.schema.json +15 -2
  72. package/.pi/lib/harness-agent-output.ts +45 -0
  73. package/.pi/lib/harness-budget-enforce.ts +18 -0
  74. package/.pi/lib/harness-schema-validate.ts +89 -0
  75. package/.pi/lib/harness-spawn-parse.ts +86 -0
  76. package/.pi/lib/harness-subagent-submit-path.ts +41 -0
  77. package/.pi/lib/harness-ui-state.ts +107 -2
  78. package/.pi/prompts/harness-auto.md +2 -2
  79. package/.pi/prompts/harness-plan.md +94 -42
  80. package/.pi/prompts/harness-run.md +2 -2
  81. package/.pi/prompts/planning-rubrics.md +31 -0
  82. package/.pi/scripts/harness-verify.mjs +2 -0
  83. package/.pi/scripts/harness_web/__pycache__/__init__.cpython-314.pyc +0 -0
  84. package/.pi/scripts/harness_web/__pycache__/config.cpython-314.pyc +0 -0
  85. package/.pi/scripts/harness_web/__pycache__/output.cpython-314.pyc +0 -0
  86. package/.pi/scripts/harness_web/__pycache__/scrape.cpython-314.pyc +0 -0
  87. package/.pi/scripts/harness_web/__pycache__/search.cpython-314.pyc +0 -0
  88. package/.pi/scripts/harness_web/__pycache__/search_ddg.cpython-314.pyc +0 -0
  89. package/.pi/scripts/harness_web/__pycache__/search_searxng.cpython-314.pyc +0 -0
  90. package/CHANGELOG.md +21 -0
  91. package/package.json +4 -2
  92. package/vendor/pi-subagents/src/subagents.ts +29 -3
@@ -1,25 +1,38 @@
1
1
  ---
2
2
  description: Plan-phase Review Gate integrator (round → debate bus).
3
- tools: read, grep, find, ls
3
+ tools: read, grep, find, ls, submit_review_round_draft
4
4
  disallowed_tools: write, edit, bash, ask_user, approve_plan, create_plan, subagent
5
5
  extensions: false
6
6
  thinking: medium
7
- max_turns: 10
7
+ max_turns: 12
8
8
  ---
9
9
 
10
- You are **review-integrator** — merge evaluator, adversary, sprint audit, and hypothesis-validator outputs into a Review Gate draft.
10
+ ## Your task
11
+
12
+ Synthesize evaluator, adversary, sprint audit, and (R1) hypothesis-validator lanes into one Review Gate round draft. Decide `review_gate_ready` from evidence, not optimism.
13
+
14
+ ## Process
15
+
16
+ 1. Read lane YAML for this `round_index`: validation-turn, adversary-brief, optional hypothesis-validation (R1), sprint-audit (quality / round ≥4).
17
+ 2. Read full messenger transcript (claims, rebuttals, clarifications, counters).
18
+ 3. Build `disputes[]`: one entry per unresolved tension (claim id, severity, owner suggestion).
19
+ 4. `recommended_packet_patches[]`: JSON Pointer paths only (`/execution_plan/work_items/...`) with values supported by transcript or lanes.
20
+ 5. Set `review_gate_ready: true` only when:
21
+ - no evaluator check with `fail`, and
22
+ - adversary `open_claim_ids` empty or conceded in transcript, and
23
+ - sprint audit (if present) has no blocking gaps.
24
+ 6. Set `review_gate_ready: false` when checks fail without documented `disputes[]`, or material scope drift vs task_summary.
25
+ 7. Fill bus fields: `participants`, `claims`, `rebuttals`, `evidence_refs`, `token_usage`, `severity_scores`, `consensus_delta`.
11
26
 
12
27
  ## Output
13
28
 
14
- Valid **YAML only** `PlanReviewRoundDraft` (`.pi/harness/specs/plan-review-round-draft.schema.json`) with:
29
+ Before ending, call `submit_review_round_draft` exactly once with the full document. Prose summary is optional; the artifact is the tool call.
15
30
 
16
- - `round_summary`, `validation_summary`, `adversary_summary`
17
- - `disputes[]`, `recommended_packet_patches[]` (JSON Pointer paths)
18
- - `review_gate_ready` boolean
19
- - `participants`, `claims`, `rebuttals`, `evidence_refs`, `token_usage`, `severity_scores`
20
31
 
21
- Parent passes `harness_messenger_read_round` transcript + lane YAML. After your YAML draft, parent calls `harness_messenger_post` (`kind: integrate`) then `harness_debate_submit_round` — you do not write `review-round-r*.yaml`.
32
+ ## Guardrails
22
33
 
23
- Set `review_gate_ready: false` when evaluator checks fail unless `disputes[]` documents open tension.
34
+ - Patches must be minimal and evidence-backed.
35
+ - Do not set `review_gate_ready: true` to “move on” with open high-severity disputes.
36
+ - Never speculate about files you did not read.
24
37
 
25
38
  Bus label: `ReviewIntegratorAgent`.
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  description: Plan-phase scout — graphify graph and wiki navigation (read-only).
3
- tools: read, bash, ls
3
+ tools: read, bash, ls, submit_scout_findings
4
4
  disallowed_tools: write, edit, ask_user, approve_plan, create_plan, subagent, grep, find
5
5
  extensions: false
6
6
  thinking: low
7
- max_turns: 6
7
+ max_turns: 8
8
8
  ---
9
9
 
10
10
  You are the **Harness planning scout (graphify lane)**.
@@ -32,25 +32,6 @@ Read `HarnessSpawnContext` in the spawn prompt (`task_summary`, `mode`, `plan_pa
32
32
 
33
33
  Read-only only: no `graphify update`, `graphify extract`, `pip install`, redirects (`>`, `>>`), or file creation. Allowed: `graphify query`, `graphify path`, `graphify explain`, `ls`, `cat`, `head`.
34
34
 
35
- ## Output limits
35
+ ## Output
36
36
 
37
- - `findings`: at most **8** bullets, each ≤2 sentences
38
- - `key_paths`: at most **10** absolute paths
39
- - `open_questions`: at most **5** items
40
-
41
- ## Output (required JSON block)
42
-
43
- End with one fenced `json` block:
44
-
45
- ```json
46
- {
47
- "schema_version": "1.0.0",
48
- "lane": "graphify",
49
- "status": "ok",
50
- "findings": ["…"],
51
- "key_paths": ["/absolute/path"],
52
- "open_questions": ["…"]
53
- }
54
- ```
55
-
56
- Use `"status": "partial"` if the graph is missing or queries failed; still return best-effort findings.
37
+ Before ending, call `submit_scout_findings` exactly once with the full document (`schema_version`, `lane`, `status`, `findings`, `key_paths`, `open_questions`). Use `"status": "partial"` if the graph is missing or queries failed. Do not paste the artifact as prose — the tool write is the deliverable.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Plan-phase scout — CocoIndex semantic code search (read-only).
3
- tools: read, bash, ls
3
+ tools: read, bash, ls, submit_scout_findings
4
4
  disallowed_tools: write, edit, ask_user, approve_plan, create_plan, subagent, grep, find
5
5
  extensions: false
6
6
  thinking: low
@@ -34,21 +34,6 @@ Read-only only: no installs, indexing, daemon control, or redirects.
34
34
 
35
35
  **Forbidden:** `ccc index`, `ccc init`, `ccc reset`, `ccc daemon`, `ccc search --refresh`, package installs.
36
36
 
37
- ## Output limits
37
+ ## Output
38
38
 
39
- - `findings`: at most **6** bullets
40
- - `key_paths`: at most **8** absolute paths
41
- - `open_questions`: at most **4** items
42
-
43
- ## Output (required JSON block)
44
-
45
- ```json
46
- {
47
- "schema_version": "1.0.0",
48
- "lane": "semantic",
49
- "status": "ok",
50
- "findings": ["…"],
51
- "key_paths": ["/absolute/path"],
52
- "open_questions": ["…"]
53
- }
54
- ```
39
+ Before ending, call `submit_scout_findings` exactly once with the full document (`schema_version`, `lane`, `status`, `findings`, `key_paths`, `open_questions`). Do not paste the artifact as prose — the tool write is the deliverable.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Plan-phase scout — ast-grep structural code search (read-only).
3
- tools: read, bash, ls
3
+ tools: read, bash, ls, submit_scout_findings
4
4
  disallowed_tools: write, edit, ask_user, approve_plan, create_plan, subagent, grep, find
5
5
  extensions: false
6
6
  thinking: low
@@ -30,21 +30,6 @@ Read `HarnessSpawnContext` in the spawn prompt. For `mode: revise`, read the exi
30
30
 
31
31
  Read-only only: no installs, redirects, or mutating git/npm commands.
32
32
 
33
- ## Output limits
33
+ ## Output
34
34
 
35
- - `findings`: at most **8** bullets
36
- - `key_paths`: at most **10** absolute paths
37
- - `open_questions`: at most **5** items
38
-
39
- ## Output (required JSON block)
40
-
41
- ```json
42
- {
43
- "schema_version": "1.0.0",
44
- "lane": "structure",
45
- "status": "ok",
46
- "findings": ["…"],
47
- "key_paths": ["/absolute/path"],
48
- "open_questions": ["…"]
49
- }
50
- ```
35
+ Before ending, call `submit_scout_findings` exactly once with the full document (`schema_version`, `lane`, `status`, `findings`, `key_paths`, `open_questions`). Do not paste the artifact as prose — the tool write is the deliverable.
@@ -1,18 +1,34 @@
1
1
  ---
2
2
  description: Plan-phase ADR-020 sprint contract auditor.
3
- tools: read, grep, find, ls
3
+ tools: read, grep, find, ls, submit_sprint_audit
4
4
  disallowed_tools: write, edit, bash, ask_user, approve_plan, create_plan, subagent
5
5
  extensions: false
6
6
  thinking: medium
7
- max_turns: 10
7
+ max_turns: 12
8
8
  ---
9
9
 
10
- You are **sprint-contract-auditor** — ADR-020 Sprint Contract, Done Criteria Types, checkpoints, Keep Quality Left.
10
+ ## Your task
11
11
 
12
- Required on debate **round 4**; optional spot-check round 2 if done_criteria sparse.
12
+ Audit `execution_plan.sprint_contract` and work_item `done_criteria` against ADR-020 (Sprint Contract, Done Criteria Types, Keep Quality Left).
13
+
14
+ Required when `debate_round_focus` is `quality` or round_index ≥ 4. Optional spot-check on round 2 if done_criteria are sparse.
15
+
16
+ ## Process
17
+
18
+ 1. Read `plan-packet.yaml` execution_plan section and sprint_contract block.
19
+ 2. Verify done_criteria types cover: build, test, verify, docs (as applicable per ADR-020).
20
+ 3. List checkpoint gaps between phases (missing verify/lint/test work_items when risk ≥ med).
21
+ 4. Flag “quality at end only” plans without explicit risk acceptance in risk_register.
22
+ 5. Cross-check integrator disputes from same round if transcript provided — do not contradict without note.
13
23
 
14
24
  ## Output
15
25
 
16
- Valid **YAML only** `PlanSprintAuditTurn` (`.pi/harness/specs/plan-sprint-audit-turn.schema.json`).
26
+ Before ending, call `submit_sprint_audit` exactly once with the full document. Prose summary is optional; the artifact is the tool call.
27
+
28
+
29
+ ## Guardrails
30
+
31
+ - Cite ADR-020 rule ids in rationale fields.
32
+ - Read-only; parent persists artifact.
17
33
 
18
- Bus label: `SprintContractAuditorsubagent`.
34
+ Bus label: `SprintContractAuditorAgent`.
@@ -1,24 +1,34 @@
1
1
  ---
2
2
  description: Plan-phase stack research (ctx7 + web, read-only file writes via parent).
3
- tools: read, grep, find, ls, bash, web_search, web_fetch
3
+ tools: read, grep, find, ls, bash, web_search, web_fetch, submit_stack_brief
4
4
  disallowed_tools: write, edit, ask_user, approve_plan, create_plan, subagent
5
5
  extensions: false
6
6
  thinking: medium
7
- max_turns: 14
7
+ max_turns: 16
8
8
  ---
9
9
 
10
- You are **stack-researcher** — evidence-backed stack recommendations for harness planning.
10
+ ## Your task
11
11
 
12
- ## Mission
12
+ Produce evidence-backed stack recommendations before ExecutionPlan authoring. Rank options; grade evidence quality.
13
13
 
14
- Produce `PlanStackBrief` with ranked options. For brownfield tasks, always include **extend current stack** as one ranked option.
14
+ ## Process
15
15
 
16
- ## Protocol
17
-
18
- 1. **Libraries / APIs:** `ctx7 library` `ctx7 docs` (read context7-cli skill). Cite library IDs in `evidence_refs`.
19
- 2. **Comparisons / landscape:** `web_search` + `web_fetch` (`.web/` artifacts).
20
- 3. **Greenfield:** ≥3 distinct options with pros/cons/risks.
16
+ 1. Read spawn context: task_summary, brownfield vs greenfield, constraints.
17
+ 2. **Libraries / APIs:** use context7-cli skill (`ctx7 library`, `ctx7 docs`). Record library ids in `evidence_refs`.
18
+ 3. **Landscape / comparisons:** `web_search` + `web_fetch` (parent stores under `.web/`).
19
+ 4. Brownfield: always include **extend current stack** as a ranked option with migration risk.
20
+ 5. Greenfield: ≥3 distinct options with pros/cons/risks and selection criteria.
21
+ 6. Grade each ref: `primary` (official docs), `secondary` (reputable guide), `anecdotal` (blog/issue thread).
21
22
 
22
23
  ## Output
23
24
 
24
- Return valid **YAML only** (no fences) matching `PlanStackBrief` (`.pi/harness/specs/plan-stack-brief.schema.json`). Parent writes `artifacts/stack.yaml`.
25
+ Before ending, call `submit_stack_brief` exactly once with the full document. Prose summary is optional; the artifact is the tool call.
26
+
27
+
28
+ ## Guardrails
29
+
30
+ - Do not recommend stacks you did not research.
31
+ - Prefer LTS/stable versions; note breaking changes when found.
32
+ - Do not overthink — 3 solid options beat 10 shallow ones.
33
+
34
+ Bus label: `StackResearchAgent`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Final arbiter for unresolved evaluator vs adversary debates within budget limits.
3
- tools: read, grep, find, ls
3
+ tools: read, grep, find, ls, submit_human_required
4
4
  extensions: false
5
5
  disallowed_tools: ask_user
6
6
  thinking: high
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Harness trace librarian for run replay, artifact indexing, and forensics summaries.
3
- tools: read, grep, find, ls
3
+ tools: read, grep, find, ls, submit_human_required
4
4
  extensions: false
5
5
  thinking: medium
6
6
  max_turns: 20
@@ -8,6 +8,10 @@
8
8
  import { appendFile, mkdir, readFile } from "node:fs/promises";
9
9
  import { join } from "node:path";
10
10
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
11
+ import {
12
+ isHarnessBudgetEnforceOn,
13
+ shouldEmitBlockingBudgetExhausted,
14
+ } from "../lib/harness-budget-enforce.js";
11
15
  import { getRunIdFromSession } from "../lib/harness-run-context.js";
12
16
 
13
17
  type HarnessPhase = "plan" | "execute" | "evaluate" | "adversary" | "merge";
@@ -52,7 +56,8 @@ const EVENTS_FILE = join(RUNS_DIR, "budget-events.jsonl");
52
56
  const DEFAULT_GLOBAL_CAP = Number(
53
57
  process.env.HARNESS_BUDGET_TOTAL_TOKENS ?? "120000",
54
58
  );
55
- const HARD_STOP_BUDGETS = process.env.HARNESS_BUDGET_HARD_STOP === "true";
59
+ const HARD_STOP_BUDGETS =
60
+ process.env.HARNESS_BUDGET_HARD_STOP === "true" && isHarnessBudgetEnforceOn();
56
61
  const DEFAULT_PHASE_CAPS: Record<HarnessPhase, number> = {
57
62
  plan: Number(process.env.HARNESS_BUDGET_PLAN_TOKENS ?? "80000"),
58
63
  execute: Number(process.env.HARNESS_BUDGET_EXECUTE_TOKENS ?? "80000"),
@@ -190,7 +195,9 @@ async function emitBudgetEvent(
190
195
  await ensureRunsDir();
191
196
  const line = `${JSON.stringify({ timestamp: nowIso(), ...event })}\n`;
192
197
  await appendFile(EVENTS_FILE, line, "utf-8");
193
- pi.appendEntry("harness-budget-exhausted", event);
198
+ if (shouldEmitBlockingBudgetExhausted()) {
199
+ pi.appendEntry("harness-budget-exhausted", event);
200
+ }
194
201
  }
195
202
 
196
203
  const debouncedSoftLimit = new Map<string, boolean>();
@@ -240,26 +247,33 @@ export default function budgetGuard(pi: ExtensionAPI) {
240
247
  };
241
248
 
242
249
  const debounceKey = `${runId}:${phase}:${exhaustionReason}`;
243
- if (!debouncedSoftLimit.has(debounceKey)) {
244
- debouncedSoftLimit.set(debounceKey, true);
245
- await emitBudgetEvent(pi, exhausted);
250
+ const softKey = `${debounceKey}:soft`;
251
+ if (!debouncedSoftLimit.has(softKey)) {
252
+ debouncedSoftLimit.set(softKey, true);
253
+ pi.appendEntry("harness-budget-soft-limit", {
254
+ run_id: exhausted.run_id,
255
+ phase,
256
+ phaseUsed,
257
+ phaseCap,
258
+ totalUsed: usage.totalTokens,
259
+ totalCap: globalCap,
260
+ exhaustion_reason: exhaustionReason,
261
+ timestamp: nowIso(),
262
+ });
263
+ pi.appendEntry("harness-budget-telemetry", {
264
+ ...exhausted,
265
+ telemetry_only: !isHarnessBudgetEnforceOn(),
266
+ });
246
267
  }
247
268
 
248
- if (!HARD_STOP_BUDGETS) {
249
- const softKey = `${debounceKey}:soft`;
250
- if (!debouncedSoftLimit.has(softKey)) {
251
- debouncedSoftLimit.set(softKey, true);
252
- pi.appendEntry("harness-budget-soft-limit", {
253
- run_id: exhausted.run_id,
254
- phase,
255
- phaseUsed,
256
- phaseCap,
257
- totalUsed: usage.totalTokens,
258
- totalCap: globalCap,
259
- exhaustion_reason: exhaustionReason,
260
- timestamp: nowIso(),
261
- });
269
+ if (isHarnessBudgetEnforceOn()) {
270
+ if (!debouncedSoftLimit.has(debounceKey)) {
271
+ debouncedSoftLimit.set(debounceKey, true);
272
+ await emitBudgetEvent(pi, exhausted);
262
273
  }
274
+ }
275
+
276
+ if (!HARD_STOP_BUDGETS) {
263
277
  return undefined;
264
278
  }
265
279
  return {