@miller-tech/uap 2.11.0 → 2.16.2

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 (166) hide show
  1. package/README.md +7 -6
  2. package/config/capacity-policy.json +15 -3
  3. package/config/model-profiles/qwen38.json +42 -34
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/bin/cli.js +39 -0
  6. package/dist/bin/cli.js.map +1 -1
  7. package/dist/capacity/policy.d.ts +15 -0
  8. package/dist/capacity/policy.d.ts.map +1 -1
  9. package/dist/capacity/policy.js +23 -0
  10. package/dist/capacity/policy.js.map +1 -1
  11. package/dist/capacity/probe.d.ts.map +1 -1
  12. package/dist/capacity/probe.js +6 -1
  13. package/dist/capacity/probe.js.map +1 -1
  14. package/dist/cli/deliver.d.ts.map +1 -1
  15. package/dist/cli/deliver.js +148 -2
  16. package/dist/cli/deliver.js.map +1 -1
  17. package/dist/cli/inference.d.ts +13 -0
  18. package/dist/cli/inference.d.ts.map +1 -0
  19. package/dist/cli/inference.js +132 -0
  20. package/dist/cli/inference.js.map +1 -0
  21. package/dist/cli/loops.d.ts +39 -0
  22. package/dist/cli/loops.d.ts.map +1 -0
  23. package/dist/cli/loops.js +154 -0
  24. package/dist/cli/loops.js.map +1 -0
  25. package/dist/cli/wizard-config.d.ts.map +1 -1
  26. package/dist/cli/wizard-config.js +5 -1
  27. package/dist/cli/wizard-config.js.map +1 -1
  28. package/dist/cli/worktree.d.ts.map +1 -1
  29. package/dist/cli/worktree.js +8 -4
  30. package/dist/cli/worktree.js.map +1 -1
  31. package/dist/delivery/adversarial-attack.d.ts +99 -0
  32. package/dist/delivery/adversarial-attack.d.ts.map +1 -0
  33. package/dist/delivery/adversarial-attack.js +326 -0
  34. package/dist/delivery/adversarial-attack.js.map +1 -0
  35. package/dist/delivery/adversarial-gate.d.ts +120 -0
  36. package/dist/delivery/adversarial-gate.d.ts.map +1 -0
  37. package/dist/delivery/adversarial-gate.js +259 -0
  38. package/dist/delivery/adversarial-gate.js.map +1 -0
  39. package/dist/delivery/adversarial-sanction.d.ts +52 -0
  40. package/dist/delivery/adversarial-sanction.d.ts.map +1 -0
  41. package/dist/delivery/adversarial-sanction.js +134 -0
  42. package/dist/delivery/adversarial-sanction.js.map +1 -0
  43. package/dist/delivery/agentic-executor.d.ts +69 -22
  44. package/dist/delivery/agentic-executor.d.ts.map +1 -1
  45. package/dist/delivery/agentic-executor.js +152 -8
  46. package/dist/delivery/agentic-executor.js.map +1 -1
  47. package/dist/delivery/applier.d.ts +8 -0
  48. package/dist/delivery/applier.d.ts.map +1 -1
  49. package/dist/delivery/applier.js +3 -1
  50. package/dist/delivery/applier.js.map +1 -1
  51. package/dist/delivery/convergence-loop.d.ts +15 -0
  52. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  53. package/dist/delivery/convergence-loop.js.map +1 -1
  54. package/dist/delivery/criteria-lint.d.ts +74 -0
  55. package/dist/delivery/criteria-lint.d.ts.map +1 -0
  56. package/dist/delivery/criteria-lint.js +134 -0
  57. package/dist/delivery/criteria-lint.js.map +1 -0
  58. package/dist/delivery/deliver-toggle.d.ts +21 -0
  59. package/dist/delivery/deliver-toggle.d.ts.map +1 -0
  60. package/dist/delivery/deliver-toggle.js +40 -0
  61. package/dist/delivery/deliver-toggle.js.map +1 -0
  62. package/dist/delivery/delivery-evidence.d.ts +109 -0
  63. package/dist/delivery/delivery-evidence.d.ts.map +1 -0
  64. package/dist/delivery/delivery-evidence.js +139 -0
  65. package/dist/delivery/delivery-evidence.js.map +1 -0
  66. package/dist/delivery/epic-mission.d.ts +5 -1
  67. package/dist/delivery/epic-mission.d.ts.map +1 -1
  68. package/dist/delivery/epic-mission.js +34 -6
  69. package/dist/delivery/epic-mission.js.map +1 -1
  70. package/dist/delivery/execution-gate.d.ts.map +1 -1
  71. package/dist/delivery/execution-gate.js +90 -7
  72. package/dist/delivery/execution-gate.js.map +1 -1
  73. package/dist/delivery/index.d.ts +9 -1
  74. package/dist/delivery/index.d.ts.map +1 -1
  75. package/dist/delivery/index.js +9 -1
  76. package/dist/delivery/index.js.map +1 -1
  77. package/dist/delivery/journey-depth.d.ts +61 -0
  78. package/dist/delivery/journey-depth.d.ts.map +1 -0
  79. package/dist/delivery/journey-depth.js +91 -0
  80. package/dist/delivery/journey-depth.js.map +1 -0
  81. package/dist/delivery/master-pipeline.d.ts +50 -0
  82. package/dist/delivery/master-pipeline.d.ts.map +1 -0
  83. package/dist/delivery/master-pipeline.js +52 -0
  84. package/dist/delivery/master-pipeline.js.map +1 -0
  85. package/dist/delivery/mission-acceptance.d.ts.map +1 -1
  86. package/dist/delivery/mission-acceptance.js +54 -2
  87. package/dist/delivery/mission-acceptance.js.map +1 -1
  88. package/dist/delivery/orchestrated-mission.d.ts +4 -1
  89. package/dist/delivery/orchestrated-mission.d.ts.map +1 -1
  90. package/dist/delivery/orchestrated-mission.js +18 -9
  91. package/dist/delivery/orchestrated-mission.js.map +1 -1
  92. package/dist/delivery/plan-check.d.ts +1 -0
  93. package/dist/delivery/plan-check.d.ts.map +1 -1
  94. package/dist/delivery/plan-check.js +48 -0
  95. package/dist/delivery/plan-check.js.map +1 -1
  96. package/dist/delivery/task-orchestrator.d.ts +16 -9
  97. package/dist/delivery/task-orchestrator.d.ts.map +1 -1
  98. package/dist/delivery/task-orchestrator.js +4 -2
  99. package/dist/delivery/task-orchestrator.js.map +1 -1
  100. package/dist/delivery/task-workspace.d.ts +35 -5
  101. package/dist/delivery/task-workspace.d.ts.map +1 -1
  102. package/dist/delivery/task-workspace.js +53 -7
  103. package/dist/delivery/task-workspace.js.map +1 -1
  104. package/dist/delivery/test-oracle-additive.d.ts +26 -6
  105. package/dist/delivery/test-oracle-additive.d.ts.map +1 -1
  106. package/dist/delivery/test-oracle-additive.js +40 -15
  107. package/dist/delivery/test-oracle-additive.js.map +1 -1
  108. package/dist/delivery/user-validation.d.ts +14 -0
  109. package/dist/delivery/user-validation.d.ts.map +1 -1
  110. package/dist/delivery/user-validation.js +103 -9
  111. package/dist/delivery/user-validation.js.map +1 -1
  112. package/dist/delivery/verifier-ladder.d.ts.map +1 -1
  113. package/dist/delivery/verifier-ladder.js +24 -6
  114. package/dist/delivery/verifier-ladder.js.map +1 -1
  115. package/dist/inference/analysis.d.ts +138 -0
  116. package/dist/inference/analysis.d.ts.map +1 -0
  117. package/dist/inference/analysis.js +243 -0
  118. package/dist/inference/analysis.js.map +1 -0
  119. package/dist/inference/probe.d.ts +85 -0
  120. package/dist/inference/probe.d.ts.map +1 -0
  121. package/dist/inference/probe.js +403 -0
  122. package/dist/inference/probe.js.map +1 -0
  123. package/dist/models/types.d.ts.map +1 -1
  124. package/dist/models/types.js +45 -18
  125. package/dist/models/types.js.map +1 -1
  126. package/dist/self-tuning/profiles/qwen38.d.ts +17 -8
  127. package/dist/self-tuning/profiles/qwen38.d.ts.map +1 -1
  128. package/dist/self-tuning/profiles/qwen38.js +21 -11
  129. package/dist/self-tuning/profiles/qwen38.js.map +1 -1
  130. package/dist/utils/model-slots.d.ts +11 -5
  131. package/dist/utils/model-slots.d.ts.map +1 -1
  132. package/dist/utils/model-slots.js +11 -5
  133. package/dist/utils/model-slots.js.map +1 -1
  134. package/docs/INDEX.md +7 -1
  135. package/docs/guides/CAPACITY_POLICY.md +29 -4
  136. package/docs/guides/INFERENCE_HEALTH.md +184 -0
  137. package/docs/guides/PROXY.md +1 -0
  138. package/docs/guides/QWEN38_EXL3.md +268 -0
  139. package/docs/plans/hlg-master-architecture-uplift-2026-09-24.md +67 -0
  140. package/docs/plans/system1-uplift-2026-09-18.md +1 -2
  141. package/docs/reference/CONFIGURATION.md +10 -1
  142. package/docs/reference/CONFIGURATION_REFERENCE.md +14 -0
  143. package/docs/reference/automode-risk.md +1 -1
  144. package/docs/reference/evidence-bound-ship.md +3 -2
  145. package/docs/reference/semantic-supervisor.md +3 -3
  146. package/docs/reference/system1-classifier.md +4 -4
  147. package/package.json +2 -2
  148. package/scripts/lib/llama-upstream.sh +7 -1
  149. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  150. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-312.pyc +0 -0
  151. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
  152. package/src/policies/enforcers/expert_review_required.py +1 -4
  153. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  154. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  155. package/tools/agents/scripts/anthropic_proxy.py +374 -16
  156. package/tools/agents/scripts/confidence_escalation.py +4 -2
  157. package/tools/agents/scripts/project_telemetry.py +1 -0
  158. package/tools/agents/scripts/tool_risk.py +1 -0
  159. package/tools/agents/scripts/tool_risk_calibrate.py +1 -0
  160. package/tools/agents/scripts/toolcall_path_normalizer.py +1 -0
  161. package/tools/agents/tests/test_anthropic_proxy_streaming.py +52 -1
  162. package/tools/agents/tests/test_confidence_escalation.py +3 -2
  163. package/tools/agents/tests/test_count_tokens_scale_per_session.py +126 -0
  164. package/tools/agents/tests/test_expert_review_pr_scope.py +10 -11
  165. package/tools/agents/tests/test_lockstep_escalation.py +324 -0
  166. package/tools/agents/tests/test_thinking_template_kwargs.py +10 -4
@@ -278,6 +278,8 @@ import { createEscalationController, defaultEscalationLadder, DEFAULT_STAGNATION
278
278
  import { FilePracticeStore, defaultPracticePath, extractKeywords, retrievePracticesSemantic, } from '../delivery/practice.js';
279
279
  import { detectRungs, mergeRedetectedRungs, runLadder, runTieredLadder, tierOf, TIER_ORDER, demoteBaselineFailures, baselineRegressions } from '../delivery/verifier-ladder.js';
280
280
  import { recordGateEvidence, gateOutcomesFromResult } from '../delivery/gate-evidence.js';
281
+ import { resolveAdversarialGate, runAdversarialGate } from '../delivery/adversarial-gate.js';
282
+ import { resolveMasterPipeline, formatMasterPipelineLine } from '../delivery/master-pipeline.js';
281
283
  import { runDeployDevLadder } from '../delivery/deploy-dev-gate.js';
282
284
  import { commitPushAndWatch } from '../delivery/ci-watcher.js';
283
285
  import { snapshotTree, restoreTreeScoped, disposeSnapshot } from '../delivery/snapshot.js';
@@ -1495,6 +1497,25 @@ async function runDeliver(instruction, options) {
1495
1497
  console.log(chalk.cyan('⚖ acceptance judge on (verification rail; UAP_DELIVER_ACCEPTANCE=0 to disable)'));
1496
1498
  }
1497
1499
  }
1500
+ // Master-pipeline stage readout — the Harness/Loop/Graph composition this
1501
+ // command runs by default: worktree-isolated parallel fan-out → evidence-
1502
+ // gated convergence loops → state-hash read dedup → adversarial red-team
1503
+ // gate. Resolved once via delivery/master-pipeline.ts (which mirrors each
1504
+ // stage's own env > .uap.json > default precedence) and surfaced in the
1505
+ // startup banner and the dry-run plan, so what is printed is what the
1506
+ // run gets.
1507
+ const deliverCfgEarly = (() => {
1508
+ try {
1509
+ return (loadUapConfigRaw(projectRoot) ?? {}).deliver;
1510
+ }
1511
+ catch {
1512
+ return undefined;
1513
+ }
1514
+ })();
1515
+ const masterPipeline = resolveMasterPipeline(deliverCfgEarly);
1516
+ if (!options.dryRun) {
1517
+ console.log(chalk.cyan(`⇒ master pipeline: ${formatMasterPipelineLine(masterPipeline)}`));
1518
+ }
1498
1519
  // `--optimize` turns on every convergence aid at once. Deploy queueing is
1499
1520
  // deliberately excluded — committing applied files is a side effect the
1500
1521
  // user must opt into explicitly.
@@ -1880,6 +1901,12 @@ async function runDeliver(instruction, options) {
1880
1901
  branch: watchCi ? currentBranch(projectRoot) : null,
1881
1902
  gates: rungs.map((r) => ({ id: r.id, name: r.name, required: r.required, tier: tierOf(r) })),
1882
1903
  selfGate: needsSelfGate,
1904
+ masterPipeline: {
1905
+ parallelTasks: masterPipeline.parallelTasks,
1906
+ stateHash: masterPipeline.stateHash,
1907
+ stateHashMinBytes: masterPipeline.stateHashMinBytes,
1908
+ adversarialGate: masterPipeline.adversarial.enabled ? masterPipeline.adversarial.maxRounds : 0,
1909
+ },
1883
1910
  };
1884
1911
  if (options.json) {
1885
1912
  console.log(JSON.stringify(summary, null, 2));
@@ -1898,6 +1925,7 @@ async function runDeliver(instruction, options) {
1898
1925
  console.log(` HALO tracing: ${summary.halo ? 'on' : 'off'}`);
1899
1926
  console.log(` Coordination: ${summary.coordinate ? 'on' : 'off'}`);
1900
1927
  console.log(` Deploy queue on success: ${summary.deploy ? 'on' : 'off'}`);
1928
+ console.log(` Master pipeline: ${formatMasterPipelineLine(masterPipeline)}`);
1901
1929
  console.log(` Test protection: ${summary.protectTests ? `on (${summary.protectedTestFiles} pre-existing test/oracle file(s))` : 'off'}`);
1902
1930
  console.log(` IaC protection: ${summary.protectIac ? 'on (Dockerfile/compose/terraform writes blocked)' : 'off (permissive — IaC writes allowed)'}`);
1903
1931
  console.log(` Guidance file: ${summary.guidanceFile ?? 'none (mission runs unattended)'}`);
@@ -2139,6 +2167,12 @@ async function runDeliver(instruction, options) {
2139
2167
  // sweep record real written paths here; the applier below drains them into
2140
2168
  // ApplyResult.filesWritten so history[].filesApplied is truthful again.
2141
2169
  const agenticWriteLedger = new Set();
2170
+ // Hoisted shared protection set (HLG stage 5 hardening): the adversarial
2171
+ // gate's breaching tests are written AFTER this t0 snapshot, so a repair
2172
+ // could delete them unless the set grows. Both executors hold THIS
2173
+ // reference and check membership per write, so the stage's onBreach hook
2174
+ // can extend it before reconvergence runs.
2175
+ const sharedProtectedFiles = options.protectTests !== false ? snapshotProtection(projectRoot).protectedFiles : new Set();
2142
2176
  const executor = agentic
2143
2177
  ? createAgenticExecutor(model, {
2144
2178
  projectRoot,
@@ -2149,7 +2183,7 @@ async function runDeliver(instruction, options) {
2149
2183
  contractFiles: contractLock,
2150
2184
  writeLedger: agenticWriteLedger,
2151
2185
  // Block oracle tampering: protected test files are read-only to the agent.
2152
- protectedFiles: options.protectTests !== false ? snapshotProtection(projectRoot).protectedFiles : new Set(),
2186
+ protectedFiles: sharedProtectedFiles,
2153
2187
  // Block gate-config / IaC rigging in the agentic path too (it bypasses
2154
2188
  // the file-block applier where this protection otherwise lives).
2155
2189
  protectGateConfigs: options.protectTests !== false,
@@ -2171,7 +2205,7 @@ async function runDeliver(instruction, options) {
2171
2205
  contextTokenBudget: sessionBudget,
2172
2206
  contractFiles: contractLock,
2173
2207
  writeLedger: agenticWriteLedger,
2174
- protectedFiles: options.protectTests !== false ? snapshotProtection(projectRoot).protectedFiles : new Set(),
2208
+ protectedFiles: sharedProtectedFiles,
2175
2209
  protectGateConfigs: options.protectTests !== false,
2176
2210
  allowBash: options.allowBash === true || process.env.UAP_DELIVER_ALLOW_BASH === '1',
2177
2211
  onEvent: (e) => console.log(chalk.dim(` [repair r${e.round} ${e.kind}${e.tool ? `:${e.tool}` : ''}] ${e.detail ?? ''}`)),
@@ -3847,6 +3881,81 @@ async function runDeliver(instruction, options) {
3847
3881
  disposeSnapshot(regressSnapshot);
3848
3882
  }
3849
3883
  }
3884
+ // Adversarial red-team gate (HLG stage 5): before the verdict is accepted,
3885
+ // a skeptical verifier attacks the converged patch with edge-case tests
3886
+ // designed to break it. A breach routes back into the convergence loop as
3887
+ // ordinary gate evidence (bounded rounds); only a surviving patch proceeds
3888
+ // to the outcome/evidence bookkeeping below. Placed AFTER keep-best so the
3889
+ // attack targets the same tree the rollback rail settled on, and BEFORE
3890
+ // evidence recording so the artifact reflects the post-attack state.
3891
+ // Skipped on alreadyDelivered (no patch to attack) and on --dry-run.
3892
+ // Default ON; escape hatch: UAP_DELIVER_ADVERSARIAL_GATE=0 or
3893
+ // `.uap.json` deliver.adversarialGate=false — same path deliver.parallelTasks
3894
+ // resolves through.
3895
+ const adversarialSettings = resolveAdversarialGate(cfgRaw.deliver?.adversarialGate);
3896
+ if (result.success && !result.alreadyDelivered && adversarialSettings.enabled && !options.dryRun) {
3897
+ const adversarial = await runAdversarialGate({
3898
+ instruction,
3899
+ projectRoot,
3900
+ rungs,
3901
+ // Gate-time redetection: `rungs` was detected at t0, so a mission that
3902
+ // CREATED its test gate (greenfield) would no-surface forever. Merge
3903
+ // the fresh detection with the same policy the loop's mid-mission
3904
+ // redetection and the combined-tree verification use.
3905
+ redetectRungs: () => {
3906
+ try {
3907
+ return mergeRedetectedRungs(rungs, detectRungs(projectRoot), loopConfig.redetectFilter);
3908
+ }
3909
+ catch {
3910
+ return rungs; // detection unavailable — attack with the t0 rungs
3911
+ }
3912
+ },
3913
+ // Defense in depth for the repair: the executors' protected set was
3914
+ // snapshotted before the breaching tests existed — add them so the
3915
+ // repair agent structurally CANNOT delete/skip/weaken them (the gate's
3916
+ // own post-repair verification remains the enforcing floor).
3917
+ onBreach: (breach) => {
3918
+ for (const rel of breach.testFiles)
3919
+ sharedProtectedFiles.add(rel.toLowerCase());
3920
+ },
3921
+ // Authoring judges text; keep it on the blind executor even when the
3922
+ // loop's executor is agentic (same rule as the critic/judge).
3923
+ executor: jsonBlindExecutor,
3924
+ settings: adversarialSettings,
3925
+ initial: result,
3926
+ reconverge: (prompt) => new ConvergenceLoop({ ...loopConfig, baselineCheck: false, resumeFrom: undefined, onCheckpoint: undefined }, executor, seams).deliver(prompt),
3927
+ note: (line) => console.log(chalk.dim(` ${line}`)),
3928
+ });
3929
+ result = adversarial.result;
3930
+ result.adversarial = {
3931
+ status: adversarial.status,
3932
+ rounds: adversarial.rounds.length,
3933
+ authored: adversarial.authored,
3934
+ ran: adversarial.ran,
3935
+ failed: adversarial.failed,
3936
+ summary: adversarial.summary,
3937
+ };
3938
+ if (!options.json) {
3939
+ const color = adversarial.status === 'breached'
3940
+ ? chalk.red
3941
+ : adversarial.status === 'survived'
3942
+ ? chalk.green
3943
+ : chalk.dim;
3944
+ console.log(color(` 🛡 ${adversarial.summary}`));
3945
+ }
3946
+ }
3947
+ else if (result.success && !result.alreadyDelivered && !adversarialSettings.enabled) {
3948
+ // Record the hatch on the result so the JSON outcome and evidence say the
3949
+ // stage was deliberately off, not silently absent.
3950
+ result.adversarial = {
3951
+ status: 'disabled',
3952
+ rounds: 0,
3953
+ authored: 0,
3954
+ ran: 0,
3955
+ failed: 0,
3956
+ summary: 'adversarial gate: disabled (UAP_DELIVER_ADVERSARIAL_GATE=0 or deliver.adversarialGate=false)',
3957
+ };
3958
+ }
3850
3959
  // Surface the observed stop on the result itself: the JSON output (and the
3851
3960
  // MCP deliver tool reading it) is what the NEXT agent decides from, and a
3852
3961
  // stopped run that prints as a plain failure gets relaunched instead of
@@ -3891,6 +4000,20 @@ async function runDeliver(instruction, options) {
3891
4000
  if (result.success) {
3892
4001
  try {
3893
4002
  const gates = gateOutcomesFromResult(result, rungs);
4003
+ // HLG stage 5 telemetry: bind the adversarial attack outcome into the
4004
+ // same artifact as the run's other gate evidence, so evidence consumers
4005
+ // see what the red team did (tests authored / ran / failed / survived).
4006
+ // 'disabled' is recorded too — an absent entry must never be ambiguous
4007
+ // with a skipped one.
4008
+ if (result.adversarial) {
4009
+ gates.push({
4010
+ name: 'adversarial-gate',
4011
+ command: 'uap deliver adversarial red-team gate',
4012
+ exitCode: 0,
4013
+ outputTail: result.adversarial.summary,
4014
+ at: new Date().toISOString(),
4015
+ });
4016
+ }
3894
4017
  const evidencePath = recordGateEvidence(projectRoot, gates, { runId });
3895
4018
  if (!options.json)
3896
4019
  console.log(chalk.dim(` gate evidence: ${evidencePath}`));
@@ -3899,6 +4022,29 @@ async function runDeliver(instruction, options) {
3899
4022
  console.warn(chalk.yellow(` ⚠ gate evidence not recorded: ${e.message}`));
3900
4023
  }
3901
4024
  }
4025
+ else if (result.adversarial) {
4026
+ // Breached runs record their adversarial evidence TOO (C-stage review):
4027
+ // the stage ran and REJECTED the patch, so an absent entry would be
4028
+ // ambiguous with "stage skipped". Nonzero exitCode — the enforcer only
4029
+ // accepts 0, so this artifact can never masquerade as a pass. Fail-soft,
4030
+ // same as the success path (a dirty post-breach tree simply warns).
4031
+ try {
4032
+ const evidencePath = recordGateEvidence(projectRoot, [
4033
+ {
4034
+ name: 'adversarial-gate',
4035
+ command: 'uap deliver adversarial red-team gate',
4036
+ exitCode: 1,
4037
+ outputTail: result.adversarial.summary,
4038
+ at: new Date().toISOString(),
4039
+ },
4040
+ ], { runId });
4041
+ if (!options.json)
4042
+ console.log(chalk.dim(` gate evidence: ${evidencePath}`));
4043
+ }
4044
+ catch (e) {
4045
+ console.warn(chalk.yellow(` ⚠ gate evidence not recorded: ${e.message}`));
4046
+ }
4047
+ }
3902
4048
  // Close the HALO observe→learn loop: mine the trace tail for recurring
3903
4049
  // failure patterns after every run — no operator step required.
3904
4050
  if (isHaloTracingEnabled()) {