@miller-tech/uap 2.1.7 → 2.11.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 (143) hide show
  1. package/README.md +7 -6
  2. package/config/capacity-policy.json +32 -0
  3. package/config/classify-thresholds.json +9 -0
  4. package/config/supervise-policy.json +13 -0
  5. package/dist/.tsbuildinfo +1 -1
  6. package/dist/bin/cli.js +87 -2
  7. package/dist/bin/cli.js.map +1 -1
  8. package/dist/capacity/policy.d.ts +67 -0
  9. package/dist/capacity/policy.d.ts.map +1 -0
  10. package/dist/capacity/policy.js +133 -0
  11. package/dist/capacity/policy.js.map +1 -0
  12. package/dist/capacity/probe.d.ts +23 -0
  13. package/dist/capacity/probe.d.ts.map +1 -0
  14. package/dist/capacity/probe.js +93 -0
  15. package/dist/capacity/probe.js.map +1 -0
  16. package/dist/classify/baseline.d.ts +24 -0
  17. package/dist/classify/baseline.d.ts.map +1 -0
  18. package/dist/classify/baseline.js +140 -0
  19. package/dist/classify/baseline.js.map +1 -0
  20. package/dist/classify/index.d.ts +30 -0
  21. package/dist/classify/index.d.ts.map +1 -0
  22. package/dist/classify/index.js +84 -0
  23. package/dist/classify/index.js.map +1 -0
  24. package/dist/classify/shadow.d.ts +34 -0
  25. package/dist/classify/shadow.d.ts.map +1 -0
  26. package/dist/classify/shadow.js +33 -0
  27. package/dist/classify/shadow.js.map +1 -0
  28. package/dist/classify/types.d.ts +64 -0
  29. package/dist/classify/types.d.ts.map +1 -0
  30. package/dist/classify/types.js +37 -0
  31. package/dist/classify/types.js.map +1 -0
  32. package/dist/cli/classify.d.ts +24 -0
  33. package/dist/cli/classify.d.ts.map +1 -0
  34. package/dist/cli/classify.js +94 -0
  35. package/dist/cli/classify.js.map +1 -0
  36. package/dist/cli/deliver.d.ts.map +1 -1
  37. package/dist/cli/deliver.js +26 -0
  38. package/dist/cli/deliver.js.map +1 -1
  39. package/dist/cli/doctor.d.ts +9 -0
  40. package/dist/cli/doctor.d.ts.map +1 -0
  41. package/dist/cli/doctor.js +49 -0
  42. package/dist/cli/doctor.js.map +1 -0
  43. package/dist/cli/review.d.ts +12 -0
  44. package/dist/cli/review.d.ts.map +1 -0
  45. package/dist/cli/review.js +118 -0
  46. package/dist/cli/review.js.map +1 -0
  47. package/dist/cli/supervise.d.ts +8 -0
  48. package/dist/cli/supervise.d.ts.map +1 -0
  49. package/dist/cli/supervise.js +76 -0
  50. package/dist/cli/supervise.js.map +1 -0
  51. package/dist/delivery/convergence-loop.d.ts +8 -0
  52. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  53. package/dist/delivery/convergence-loop.js +1 -0
  54. package/dist/delivery/convergence-loop.js.map +1 -1
  55. package/dist/delivery/delivery-result.d.ts.map +1 -1
  56. package/dist/delivery/delivery-result.js +5 -0
  57. package/dist/delivery/delivery-result.js.map +1 -1
  58. package/dist/delivery/gate-evidence.d.ts +143 -0
  59. package/dist/delivery/gate-evidence.d.ts.map +1 -0
  60. package/dist/delivery/gate-evidence.js +239 -0
  61. package/dist/delivery/gate-evidence.js.map +1 -0
  62. package/dist/delivery/index.d.ts +1 -0
  63. package/dist/delivery/index.d.ts.map +1 -1
  64. package/dist/delivery/index.js +1 -0
  65. package/dist/delivery/index.js.map +1 -1
  66. package/dist/evals/routing-corpus.d.ts +35 -0
  67. package/dist/evals/routing-corpus.d.ts.map +1 -0
  68. package/dist/evals/routing-corpus.js +142 -0
  69. package/dist/evals/routing-corpus.js.map +1 -0
  70. package/dist/evals/routing-eval.d.ts +104 -0
  71. package/dist/evals/routing-eval.d.ts.map +1 -0
  72. package/dist/evals/routing-eval.js +231 -0
  73. package/dist/evals/routing-eval.js.map +1 -0
  74. package/dist/evals/tfidf.d.ts +35 -0
  75. package/dist/evals/tfidf.d.ts.map +1 -0
  76. package/dist/evals/tfidf.js +132 -0
  77. package/dist/evals/tfidf.js.map +1 -0
  78. package/dist/review/prepass.d.ts +88 -0
  79. package/dist/review/prepass.d.ts.map +1 -0
  80. package/dist/review/prepass.js +428 -0
  81. package/dist/review/prepass.js.map +1 -0
  82. package/dist/review/visual-captures.d.ts +60 -0
  83. package/dist/review/visual-captures.d.ts.map +1 -0
  84. package/dist/review/visual-captures.js +222 -0
  85. package/dist/review/visual-captures.js.map +1 -0
  86. package/dist/supervise/assess.d.ts +73 -0
  87. package/dist/supervise/assess.d.ts.map +1 -0
  88. package/dist/supervise/assess.js +215 -0
  89. package/dist/supervise/assess.js.map +1 -0
  90. package/dist/supervise/config.d.ts +13 -0
  91. package/dist/supervise/config.d.ts.map +1 -0
  92. package/dist/supervise/config.js +81 -0
  93. package/dist/supervise/config.js.map +1 -0
  94. package/dist/supervise/loop.d.ts +78 -0
  95. package/dist/supervise/loop.d.ts.map +1 -0
  96. package/dist/supervise/loop.js +311 -0
  97. package/dist/supervise/loop.js.map +1 -0
  98. package/dist/supervise/observe.d.ts +31 -0
  99. package/dist/supervise/observe.d.ts.map +1 -0
  100. package/dist/supervise/observe.js +172 -0
  101. package/dist/supervise/observe.js.map +1 -0
  102. package/dist/supervise/policy.d.ts +16 -0
  103. package/dist/supervise/policy.d.ts.map +1 -0
  104. package/dist/supervise/policy.js +147 -0
  105. package/dist/supervise/policy.js.map +1 -0
  106. package/dist/supervise/types.d.ts +109 -0
  107. package/dist/supervise/types.d.ts.map +1 -0
  108. package/dist/supervise/types.js +11 -0
  109. package/dist/supervise/types.js.map +1 -0
  110. package/docs/INDEX.md +15 -4
  111. package/docs/architecture/adr/0001-automode-risk-floor.md +75 -0
  112. package/docs/architecture/adr/0002-evidence-bound-ship.md +81 -0
  113. package/docs/guides/CAPACITY_POLICY.md +99 -0
  114. package/docs/performance/automode-risk-calibration.md +72 -0
  115. package/docs/performance/reliability-ladder.md +78 -0
  116. package/docs/plans/system1-uplift-2026-09-18.md +190 -0
  117. package/docs/reference/automode-risk.md +134 -0
  118. package/docs/reference/evidence-bound-ship.md +146 -0
  119. package/docs/reference/review-prepass.md +77 -0
  120. package/docs/reference/routing-evals.md +71 -0
  121. package/docs/reference/semantic-supervisor.md +140 -0
  122. package/docs/reference/system1-classifier.md +103 -0
  123. package/docs/reference/visual-captures.md +113 -0
  124. package/package.json +5 -3
  125. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  126. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-312.pyc +0 -0
  127. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
  128. package/src/policies/enforcers/enforcement_self_protect.py +25 -2
  129. package/src/policies/enforcers/evidence_bound_ship.py +257 -0
  130. package/src/policies/enforcers/expert_review_required.py +120 -0
  131. package/src/policies/schemas/policies/evidence-bound-ship.md +106 -0
  132. package/src/policies/schemas/policies/visual-verification.md +8 -0
  133. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  134. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  135. package/tools/agents/scripts/anthropic_proxy.py +782 -19
  136. package/tools/agents/scripts/project_telemetry.py +52 -0
  137. package/tools/agents/scripts/tool_risk.py +789 -0
  138. package/tools/agents/scripts/tool_risk_calibrate.py +255 -0
  139. package/tools/agents/tests/fixtures/tool_risk_cases.json +124 -0
  140. package/tools/agents/tests/test_enforcer_escape_hatches.py +13 -0
  141. package/tools/agents/tests/test_gate_evidence.py +56 -0
  142. package/tools/agents/tests/test_tool_risk.py +521 -0
  143. package/tools/agents/tests/test_verbatim_compact.py +629 -0
@@ -0,0 +1,72 @@
1
+ # AutoMode Risk Classification — Calibration Report
2
+
3
+ Generated by `tools/agents/scripts/tool_risk_calibrate.py` from
4
+ `tools/agents/tests/fixtures/tool_risk_cases.json`. Regenerate with:
5
+
6
+ ```bash
7
+ python3 tools/agents/scripts/tool_risk_calibrate.py \
8
+ --write docs/performance/automode-risk-calibration.md
9
+ ```
10
+
11
+ ## Method
12
+
13
+ - Split: stratified per class, seeded `random.Random(20260713)`,
14
+ held-out fraction 0.3 (min 2 per class). Deterministic: class
15
+ iteration order and case order are fixed, so the same seed and corpus
16
+ always produce the same split on any machine.
17
+ - Corpus: 113 labeled cases (79 train / 34 held out).
18
+ - Scorer: `tool_risk.classify_tool_call` (pure heuristic; the SYS1
19
+ model head will be measured by this same harness when it lands).
20
+
21
+ ## Held-out per-class metrics
22
+
23
+ | class | precision | recall | support |
24
+ |---|---|---|---|
25
+ | destructive | 100.0% | 100.0% | 10 |
26
+ | write | 100.0% | 100.0% | 8 |
27
+ | execute | 100.0% | 100.0% | 6 |
28
+ | read | 100.0% | 100.0% | 6 |
29
+ | network | 100.0% | 100.0% | 4 |
30
+ | **overall accuracy** | 100.0% | | 34 |
31
+
32
+ > **Read the 100% figures with the support sizes:** held-out support
33
+ > is small (n=4–10 per class), so a single miss swings per-class
34
+ > recall by 10–25 percentage points. These are
35
+ > 'no observed miss on a small probe' numbers, not proof of
36
+ > generalization — the corpus must grow with every new evasion shape
37
+ > seen in the wild.
38
+
39
+ ## Confusion notes
40
+
41
+ No held-out confusions. That is the expected shape for a RULE-BASED
42
+ scorer measured on the corpus its rules were written against — treat
43
+ these numbers as the contrast baseline, not as evidence of model
44
+ quality. The cases that exercised real judgment (and their handling):
45
+
46
+ - `git push --force-with-lease` (net-07) is a network call, NOT
47
+ destructive: the lease verifies the remote ref before overwriting.
48
+ `--force` / `-f` (des-14/15) remain destructive.
49
+ - `rm -rf ./node_modules/.cache` (wr-21) and `/tmp` scratch (wr-22)
50
+ are maintenance: an rm whose targets are ALL regenerable trees
51
+ downgrades to write. A single non-scratch target keeps destructive.
52
+ - `ls -rf` (rd-07) is a read: flags only matter per-verb.
53
+ - Quoted prose (`echo "rm -rf /" > notes.txt`, wr-23;
54
+ `grep -r "rm -rf" docs/`, rd-19) never reaches verb position;
55
+ segment splitting and redirect detection run on quote-blanked text.
56
+ - Evasion variants the enforcers already know are covered by the
57
+ same devices: launcher prefixes (des-03..05, des-08), `bash -c`
58
+ unwrapping (des-07), chained commands (des-06), trailing-slash
59
+ self-targets (des-09/10, which also fire `protected-target`).
60
+
61
+ Known residuals (accepted, advisory-only): a path held in a shell
62
+ variable is invisible to any text scan (same limit the enforcers
63
+ document); `sudo -u root rm ...` leaves `root` as the apparent verb
64
+ (enforcer parity). Both are called out in
65
+ `docs/reference/automode-risk.md`.
66
+
67
+ ## Doctrine
68
+
69
+ Held-out **destructive recall is gated at 1.0** (`--check` exits 1
70
+ otherwise): a missed destructive call is the unacceptable error. False
71
+ positives are advisory-only cost. The scorer never blocks; the
72
+ deterministic enforcers in `src/policies/enforcers/` remain the floor.
@@ -0,0 +1,78 @@
1
+ # Reliability Ladder
2
+
3
+ The measured technique-selection table for the decision loop's THINK step
4
+ (uplift 0.1). Two parts: what UAP's own paired benchmark suites measured
5
+ (generated — see below), and the selection guidance derived from those
6
+ numbers plus the external priors we have not yet reproduced locally.
7
+
8
+ ## Selection guidance (THINK step)
9
+
10
+ | Task shape | Technique | Why (evidence) |
11
+ | --- | --- | --- |
12
+ | Small, well-specified, 1–2 files | **Direct** — just do it | Measured (uplift/other tiers — the medium runs predate token accounting): ~3.3–5.4k tokens/task; the e3b power run measured the full loop at +0.98 tool calls/task (significant) with correctness not separable. External prior: reflect loops cost +1–3k tokens for nothing on tasks already inside the model's competence. |
13
+ | Multi-file or gate-covered, moderate risk | **Direct + verify loop** (default UAP flow) | Measured: hard tier +2.3pt success at +22% tokens; uplift tier +4.4pt at equal tokens — both ⚠ underpowered, so directional. Gating is cheap insurance here. |
14
+ | Hardest tier, novel/ambiguous, many files | **Full loop + escalation** (convergence, gates, judge) | Measured: brutal tier 26.1% → 56.8% success (2.2×) at 4.2× tokens; the lazy variant reached 67.5% at 2.8× tokens (⚠ underpowered — directional). This is the only tier where the observed payoff dwarfs the observed cost. |
15
+ | Spec-heavy, human-available, production-critical | **SDD + human review** | External prior (context-engineering-kit, not yet reproduced locally): 95–99% accuracy at 5–35× token cost. Use when a miss costs more than 30× the tokens. |
16
+
17
+ Rules of thumb the table encodes:
18
+
19
+ 1. **Escalate with difficulty, not with anxiety.** The brutal tier is the only
20
+ one where the expensive loop's observed payoff dwarfs its cost; below it,
21
+ spend the tokens on doing, not on judging.
22
+ 2. **Token cost of a technique is part of the technique.** The lazy variant
23
+ beat the full loop on brutal tasks at 2/3 the cost — more machinery is not
24
+ more reliability.
25
+ 3. **Cite the measurement class.** Rows not marked "usable" support cost
26
+ claims, not quality claims — never quote a success delta without its
27
+ discrimination status ("underpowered" = measured but inconclusive;
28
+ "unknown" = legacy run without a discrimination block).
29
+
30
+ ## Reproducing
31
+
32
+ ```bash
33
+ npm run bench:ladder # prints the table below (reads benchmark-results/)
34
+ npm run bench:ladder -- --write # refreshes the generated block in this file
35
+ npm run bench:ladder -- --json # machine-readable rows
36
+ ```
37
+
38
+ New paired runs land in `benchmark-results/<run>/{report.json,records.jsonl}`
39
+ and are picked up on the next regeneration. The script works from worktrees
40
+ (it falls back to the main checkout's git-ignored `benchmark-results/`).
41
+
42
+ <!-- LADDER:BEGIN -->
43
+ <!-- GENERATED by scripts/reliability-ladder.ts — regenerate with: npm run bench:ladder -->
44
+
45
+ ## Measured results (pooled from `benchmark-results/`)
46
+
47
+ | Tier | Condition | n | Success | Mean tokens | Token range | Mean files | Runs | Stats |
48
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
49
+ | medium | baseline | 45 | 2.2% | — | — | — | 3 | ⚠ unknown |
50
+ | medium | uap-full | 45 | 0.0% | — | — | — | 3 | ⚠ unknown |
51
+ | uplift | baseline | 273 | 63.4% | 3270 | 906–11981 | — | 9 | ⚠ underpowered |
52
+ | uplift | uap-full | 273 | 67.8% | 3311 | 840–10802 | — | 9 | ⚠ underpowered |
53
+ | hard | baseline | 87 | 69.0% | 146460 | 818–399573 | — | 4 | ⚠ underpowered |
54
+ | hard | uap-full | 87 | 71.3% | 178474 | 817–488287 | — | 4 | ⚠ underpowered |
55
+ | brutal | baseline | 88 | 26.1% | 7448 | 2871–8698 | — | 3 | ⚠ unknown |
56
+ | brutal | uap-full | 88 | 56.8% | 31513 | 2819–84902 | — | 3 | ⚠ unknown |
57
+ | brutal | uap-lazy | 40 | 67.5% | 21070 | 3849–43546 | — | 1 | ⚠ unknown |
58
+ | power | baseline | 45 | 75.6% | 3984 | 754–10067 | 1.0 | 1 | ⚠ underpowered |
59
+ | power | uap-full | 45 | 68.9% | 3972 | 738–10079 | 1.0 | 1 | ⚠ underpowered |
60
+ | heldout | baseline | 51 | 78.4% | 3349 | 421–9519 | 1.0 | 1 | ⚠ underpowered |
61
+ | heldout | uap-full | 51 | 78.4% | 4392 | 429–40835 | 1.1 | 1 | ⚠ underpowered |
62
+ | deliver | baseline | 75 | 41.3% | — | — | — | 3 | ⚠ underpowered |
63
+ | deliver | uap-full | 75 | 42.7% | — | — | — | 3 | ⚠ underpowered |
64
+ | other | baseline | 95 | 67.4% | 4343 | 728–8697 | 1.0 | 8 | ⚠ underpowered |
65
+ | other | uap-full | 95 | 77.9% | 5363 | 731–11097 | 1.1 | 8 | ⚠ underpowered |
66
+
67
+ Source: `benchmark-results/`. Rows not marked "usable" support
68
+ token/latency cost claims only — never cite a success delta without its
69
+ discrimination status ("underpowered" = measured but inconclusive;
70
+ "unknown" = legacy run without a discrimination block).
71
+ <!-- LADDER:END -->
72
+
73
+ ## External priors (attributed, unverified locally)
74
+
75
+ From NeoLabHQ/context-engineering-kit's published ladder: reflect +1–3k
76
+ tokens; do-and-judge 1.5–3×; SDD+human 95–99% at 5–35×. These guide the
77
+ rows UAP has not measured yet; when a local suite reproduces or contradicts
78
+ one, this section loses the row.
@@ -0,0 +1,190 @@
1
+ # UAP System-1 Uplift Plan — 2026-09-18
2
+
3
+ Status: APPROVED for implementation (plan PR → implementation PRs)
4
+ Scope decision: Wave 0 (all five) + Wave 1 (1.1–1.4). Backend decision: **local-only
5
+ classifier, airgap-pure, trained from UAP telemetry** (no hosted Jev dependency).
6
+
7
+ ## 1. Source analysis
8
+
9
+ Ten external signals were analyzed. They converge on one architectural shift:
10
+ split System 2 (slow generative reasoning) from System 1 (fast structured
11
+ decisions), and bind every gate to evidence.
12
+
13
+ | Source | Signal |
14
+ | --- | --- |
15
+ | x.com/sydneyrunkle (LangChain) | TypeSafe Jev "System One" model: state + typed questions (choice/score/noul), parallel evaluation, calibrated probabilities. Killer apps: model routing and AutoMode (pre-execution tool-call risk classification). |
16
+ | x.com/JoshARosen + thruwire/foreman | Foreman: semantic supervision of coding agents. 9 noul dimensions (worker_stuck, work_off_track, implementation_complete, tests_sufficient, requirements_satisfied, needs_verification, meaningful_progress, needs_human, ready_to_finish) against bounded observations (20k-char diff, 12k tails, 30 events). **Deterministic policy owns all actions** (CONTINUE/START/STOP/RETRY/VERIFY/FINISH/ESCALATE); the model only assesses. Debounced loop, persisted state + events.jsonl, safety-first action ordering. |
17
+ | x.com/decapostos | Split thinking from next-action selection; fewer protocol round trips → fewer drift points. |
18
+ | tamaratran/fast-jev-compaction | Replace lossy compaction summaries with per-tool-call keep/truncate/drop decisions. Kept content stays **verbatim**; pinned recency; staged state fitting; fallback to summary when reduction < 25%. |
19
+ | mrinalwadhwa/fluent | Evidence-bound factory: EARS behavior specs with `Test:` refs; candidate-bound test evidence (exact command, reject stale/mismatched, fail closed); two queues (human attention vs compute capacity); Learner → project Expertise; provider liveness guards + resumable Attempts; post-merge review. |
20
+ | khuynh22/agent-dev-team | Tier ceilings T0–T3 (confidence never raises a ceiling); structured HANDOFF/BRIEF escalation packets; routing evals (rank-1 97%, CI-enforced, planted traps); restated-count validation in CI. |
21
+ | alibaba/open-code-review | Hybrid review: deterministic rule pipelines first (NPE, thread-safety, XSS, SQLi), LLM agent second; per-group token budgets with grace round; resumable sessions; delegation mode. |
22
+ | senoff/self-hosted-ci-runner | Capacity policy: provision N+1, run N; per-runner swap/scratch/reaper; GREEN/RED/DARK health; single-purpose box. Same discipline as the 2026-09-18 llama.cpp OOM fix. |
23
+ | NeoLabHQ/context-engineering-kit | Measured reliability ladder: accuracy vs files-changed vs token cost per technique (reflect +1–3k tokens; do-and-judge 1.5–3x; SDD+human 95–99% at 5–35x). Reflexion/memorize/critique loops; subagent-per-task against context rot. |
24
+
25
+ ## 2. UAP gap assessment
26
+
27
+ UAP already exceeds these projects in: deterministic quality-metrics gate,
28
+ proxy enforcer suite (deliver autoroute, gate evidence, confidence escalation,
29
+ stuck-break), worktree/mission/checkpoint infrastructure, and local-first
30
+ serving. Gaps, in leverage order:
31
+
32
+ 1. Every UAP decision point is either a heuristic or a full LLM call — no fast,
33
+ calibrated middle layer for routing, risk, stuckness, or compaction.
34
+ 2. Proxy compaction is summarization/threshold based — lossy.
35
+ 3. Deliver gates check artifacts, not evidence-bound behavior specs.
36
+ 4. Technique selection in the decision loop is unquantified (no reliability/cost
37
+ ladder).
38
+ 5. Droids have no capability ceilings or handoff contracts.
39
+ 6. Visual gating covers design tokens only; no captured visual evidence bound
40
+ to UI changes.
41
+
42
+ ## 3. Design principles (all waves)
43
+
44
+ - **Policy is deterministic; models only assess.** Thresholds and actions live
45
+ in reviewed code. A classifier can be wrong; the policy bounds the blast
46
+ radius (Foreman's central lesson).
47
+ - **Fail closed.** If the classifier is unavailable, gates keep their current
48
+ heuristic behavior; nothing gets *looser* because the model is down.
49
+ - **Airgap-pure.** No hosted classifier dependency. The System-1 model is
50
+ trained and served locally (see §6).
51
+ - **Evidence-bound.** Every gate emits machine-checkable evidence bound to the
52
+ candidate SHA; stale or mismatched evidence is rejected (Fluent's rule).
53
+ - **Measured.** Every uplift lands with before/after numbers from UAP's own
54
+ benchmark suites; claims without measurements do not merge.
55
+
56
+ ## 4. Wave 0 — quick wins (each lands as its own PR)
57
+
58
+ ### 0.1 Reliability ladder in the decision loop
59
+ - Add a measured technique-selection table to the CLAUDE.md decision loop
60
+ (THINK step): task complexity × files-touched → technique (direct /
61
+ reflect / judge / SDD) with token-cost ranges.
62
+ - Calibrate on `benchmarks/` suites; publish the table in
63
+ `docs/performance/` with the raw results.
64
+ - Acceptance: table exists, numbers reproduced by a suite run, decision loop
65
+ references it.
66
+
67
+ ### 0.2 Deterministic review pre-pass
68
+ - A ruleset scanner (secrets, injection patterns, NPE/null deref, thread-safety
69
+ smells) runs before the three LLM reviewers in the parallel review protocol;
70
+ reviewers receive pre-filtered, line-anchored findings.
71
+ - Reuse the quality-gate scanner infra where possible.
72
+ - Acceptance: pre-pass fires in `.uap/reviews/<branch>.json`; reviewer prompt
73
+ embeds findings; at least 2 fixture vulnerabilities caught without an LLM.
74
+
75
+ ### 0.3 Visual gate for UI changes
76
+ - Any diff touching UI files requires before/after captures
77
+ (agent-browser/tuistory/pty-capture) attached to the review artifact;
78
+ `uap design check` remains the token enforcer.
79
+ - Acceptance: completion gate 7 refuses DONE on UI diffs without captures;
80
+ artifact embeds capture paths.
81
+
82
+ ### 0.4 Routing evals for pattern router + droid registry
83
+ - TF-IDF/embedding rank-1 eval over every skill/droid description with planted
84
+ traps and negatives; CI-enforced threshold (start at 90% rank-1).
85
+ - Validates `.factory/patterns/index.json` and the droid registry stay
86
+ separable as entries are added.
87
+ - Acceptance: `npm run eval:routing` (or equivalent) exists, runs in CI, and
88
+ catches a deliberately degraded description.
89
+
90
+ ### 0.5 Capacity policy as code
91
+ - Generalize the 2026-09-18 llama.cpp OOM fix discipline: every UAP-managed
92
+ service declares a resource budget, headroom policy, and GREEN/RED/DARK
93
+ health definition; `uap doctor` (or monitor) reports violations.
94
+ - Acceptance: the gsq-rco server and monitor are the reference
95
+ implementations; policy file under `config/`; doc in `docs/guides/`.
96
+
97
+ ## 5. Wave 1 — the System-1 layer (each lands as its own PR)
98
+
99
+ ### 1.1 Semantic supervisor for deliver missions
100
+ - New `uap supervise` loop watching `uap deliver` missions: 9 assessment
101
+ dimensions (Foreman's set, adapted) against bounded observations
102
+ (git status, bounded diff, output tails, recent events, prior assessment,
103
+ attempt/failure counts, elapsed time).
104
+ - Deterministic policy with explicit, reviewed thresholds; safety-first action
105
+ ordering: human-need > iteration bounds > off-track/stuck > retry >
106
+ completion > verification > continue. Actions: CONTINUE / STOP / RETRY /
107
+ VERIFY / FINISH / ESCALATE.
108
+ - Debounced (≥5s) and periodic (30s) assessment; persisted state.json +
109
+ events.jsonl per run; oscillation guards (verification cannot loop).
110
+ - Acceptance: a seeded stuck-mission fixture is stopped; a healthy mission is
111
+ never interrupted; policy branches unit-tested (offline, fake classifier).
112
+
113
+ ### 1.2 Verbatim decision compaction in the proxy
114
+ - Replace the lossy prune/summarize path in `anthropic_proxy.py` with
115
+ per-tool-call keep/truncate/drop decisions: pinned first + recent messages,
116
+ staged state fitting, truncation keeps a head + note, kept content verbatim.
117
+ - Fallback to the current summarizer when reduction is insufficient or the
118
+ classifier is unavailable.
119
+ - Acceptance: existing enforcer/compaction test suites pass; new tests cover
120
+ keep/truncate/drop decisions, pairing integrity (no orphan tool_result), and
121
+ fallback; measured context reduction ≥ current path on fixtures.
122
+
123
+ ### 1.3 AutoMode pre-execution risk classification
124
+ - Calibrated risk classification of tool calls before dispatch, ahead of the
125
+ current verb/regex heuristics in the enforcer chain. Classifier output is
126
+ advisory to the deterministic policy; heuristics remain the floor.
127
+ - Acceptance: destructive/fixture tool calls classified with calibration
128
+ report (per-class precision/recall on held-out telemetry); heuristic tests
129
+ unaffected; latency overhead < 50ms local.
130
+
131
+ ### 1.4 Evidence-bound merge candidates
132
+ - Deliver missions produce merge candidates whose gate evidence is bound to
133
+ the candidate SHA: exact test commands, outputs, timestamps; stale, missing,
134
+ malformed, or SHA-mismatched evidence is rejected (fail closed).
135
+ - Acceptance: gate evidence enforcer rejects a forged/stale artifact in tests;
136
+ PR flow requires candidate-bound evidence for ship.
137
+
138
+ ## 6. Local System-1 classifier (the airgap-pure backend)
139
+
140
+ Decision: **no hosted dependency.** The classifier is trained and served
141
+ locally, and every Wave 1 consumer talks to it through one interface
142
+ (`uap classify` / library call) so the backend can evolve without touching
143
+ policy code.
144
+
145
+ - **Training data:** UAP's own telemetry — `project_telemetry` events, gate
146
+ decisions and outcomes, escalation results, stuck/attractor events,
147
+ deliver-mission histories, and incident records (e.g., the 2026-09-18 OOM
148
+ timeline). Nobody else has UAP's decision-outcome pairs.
149
+ - **Model form (v1):** small, CPU-fast, calibrated. Start with an embedding +
150
+ calibrated head (nomic-embeddings already deployed; logistic/isotonic on
151
+ top) for noul/score/choice questions. Evaluate against a distilled small
152
+ LLM head only if the simple model underperforms on held-out telemetry.
153
+ - **Calibration:** per-dimension reliability curves and thresholds derived
154
+ from held-out outcomes; thresholds ship as reviewed policy config, not
155
+ model output.
156
+ - **Serving:** local process (CPU), <50ms p95 per assessment batch; questions
157
+ batched per state like Jev's parallel evaluation.
158
+ - **Interfaces:** `assess(state, questions) -> {name: {value, probability,
159
+ confidence}}` matching the noul/choice/score shapes, so Jev-compatible
160
+ request/response fixtures work unchanged.
161
+ - Acceptance: calibration report committed; supervisor/compaction/AutoMode all
162
+ run against the local backend with policy tests offline.
163
+
164
+ ## 7. Sequencing and PR plan
165
+
166
+ 1. This plan (docs PR).
167
+ 2. Wave 0 PRs in order: 0.4 (evals infrastructure) → 0.2 (review pre-pass) →
168
+ 0.3 (visual gate) → 0.1 (ladder, needs bench runs) → 0.5 (capacity policy).
169
+ 3. Local classifier foundation (§6) — prerequisite for Wave 1.
170
+ 4. Wave 1 PRs: 1.1 → 1.2 → 1.3 → 1.4 (1.4 is independent and may parallel).
171
+
172
+ ## 8. Risks and open questions
173
+
174
+ - **Classifier quality is unproven** (Foreman says the same about Jev).
175
+ Mitigation: shadow mode first — classifier assesses, heuristics decide,
176
+ divergence logged; promotion to active only after measured agreement.
177
+ - **Compaction regressions are silent.** Mitigation: verbatim guarantee +
178
+ reduction floor + fallback; replay fixtures from real sessions.
179
+ - **Gate fatigue.** Every new gate must have an escape hatch and a measured
180
+ false-positive rate, or it will be bypassed (UAP_QUALITY_GATE_OFF exists for
181
+ a reason).
182
+ - **Scope.** Wave 0 items are independent; Wave 1 items share the classifier
183
+ PR — land it small (one dimension end-to-end) before generalizing.
184
+
185
+ ## 9. Later waves (not approved yet; recorded for the roadmap)
186
+
187
+ - Wave 2: tier ceilings + HANDOFF/BRIEF droid contracts; EARS behavior-spec
188
+ layer; two-queue scheduling dashboard; post-merge review loop.
189
+ - Wave 3: train a dedicated small System-1 model (beyond embedding+head);
190
+ publish UAP's measured reliability ladder as the external benchmark claim.
@@ -0,0 +1,134 @@
1
+ # AutoMode Pre-Execution Risk Classification
2
+
3
+ Uplift 1.3. Every outbound `tool_use` block is risk-classified **before the
4
+ client dispatches it**, ahead of — and never instead of — the deterministic
5
+ verb/regex enforcers in `src/policies/enforcers/`.
6
+
7
+ ## Design
8
+
9
+ The scorer is `tools/agents/scripts/tool_risk.py`:
10
+
11
+ ```python
12
+ classify_tool_call(tool_name: str, tool_input: dict) -> RiskAssessment
13
+ ```
14
+
15
+ `RiskAssessment` is a frozen dataclass: `risk_class`, `risk_score` (1–5),
16
+ `signals` (every rule that fired, formatted `name:class+weight` — every
17
+ point of score is attributable to a named signal), `latency_ms`. The
18
+ function is **pure**: no I/O, no globals, same input → same output.
19
+
20
+ This is the Python-side calibrated heuristic floor for AutoMode. Its
21
+ vocabulary is *inspired by* the unmerged TypeScript SYS1 baseline
22
+ (`src/classify/baseline.ts` on `feature/301-sys1-classifier`) — not ported:
23
+ the TS baseline scores prose state descriptions, this scorer parses shell
24
+ structure, so the domains and machinery differ and "cross-stack
25
+ parity" would overclaim. The promotion path is a **Python-resident head (or
26
+ sidecar) behind the same `classify_tool_call` contract**, gated by the
27
+ calibration harness at held-out destructive recall 1.0 — when it lands, the
28
+ wiring, harness, and doctrine below do not change.
29
+
30
+ ### Classes
31
+
32
+ | class | meaning | examples |
33
+ |---|---|---|
34
+ | `destructive` | irreversible / wide blast radius | `rm -rf`, `DROP TABLE`, `TRUNCATE`, `git push --force`, `git reset --hard`, `dd of=/dev/…`, `mkfs`, `chmod -R 777`, `terraform destroy`, secrets rotation |
35
+ | `write` | file/state mutation, recoverable | Edit/Write tools, redirects, `sed -i`, `cp`/`mv`, `git add/commit` |
36
+ | `execute` | runs code or tests | `pytest`, `npm run build`, `python3 script.py`, `docker run` |
37
+ | `read` | read-only inspection | Read/Grep/Glob/LS tools, `cat`, `ls`, `git status`, `find` without mutating flags |
38
+ | `network` | crosses the wire | WebFetch/WebSearch, `curl`/`wget`, `git push/fetch/clone`, `scp`/`ssh`, `npm publish` |
39
+
40
+ Scoring: deterministic verb/regex signal extraction → per-class weighted
41
+ sum → class = highest total (ties break toward the more severe class) →
42
+ score = class base (1/2/3/3/4) + 1 when the evidence is hard (total weight
43
+ ≥ 5), capped at 5.
44
+
45
+ ### Evasion handling
46
+
47
+ The scorer mirrors `enforcement_self_protect.py` rather than reinventing it:
48
+
49
+ - **Launcher prefixes** (`nohup`, `timeout`, `sudo`, `env`, `nice`, …) are
50
+ stepped over so the real verb is judged: `sudo rm -rf /` classifies
51
+ destructive.
52
+ - **Chained commands** (`&&`, `||`, `;`, `|`, background `&`) are split per
53
+ segment — with separators inside quotes ignored, so the `;` inside
54
+ `python3 -c "import shutil; shutil.rmtree(x)"` cannot hide the rmtree.
55
+ - **`sh -c` / `bash -c` / `eval`** payloads are unwrapped and classified
56
+ recursively (depth-bounded).
57
+ - **Conditional verbs** follow the enforcer semantics: `sed` mutates only
58
+ with `-i`; `find` only with `-delete`/`-exec`-family. Without the flag
59
+ they classify read, exactly like the enforcer's `CONDITIONAL_VERBS`.
60
+ - **Path variants**: trailing slashes and `./` prefixes are normalized;
61
+ `.uap` / `.git` self-targets fire a `protected-target` bump to score 5.
62
+ - **Scratch carve-out**: `rm -rf` whose targets are ALL regenerable trees
63
+ (`node_modules`, `dist`, `build`, `/tmp/…`, …) classifies write, not
64
+ destructive. One non-scratch target keeps destructive.
65
+
66
+ Known residuals (accepted — advisory-only cost, and the same limits the
67
+ enforcers document):
68
+
69
+ - a path held in a shell variable is invisible to any text scan;
70
+ - `sudo -u root rm …` leaves `root` as the apparent verb;
71
+ - an **escaped quote** inside a double-quoted string defeats the quote
72
+ blanking (`python3 -c "a=\";\"; shutil.rmtree('/x')"` splits at the
73
+ escaped quote, because the blanking regex does not model backslash
74
+ escapes) — same accepted-residual class as shell variables: text scans
75
+ cannot see shell state.
76
+
77
+ ## Advisory doctrine
78
+
79
+ **The scorer never blocks and never modifies the stream.** The deterministic
80
+ enforcer chain is the blocking floor and is exactly as strict as before —
81
+ nothing gets looser. The classification is consumed three ways at the
82
+ proxy's outbound `tool_use` interception points
83
+ (`tools/agents/scripts/anthropic_proxy.py`):
84
+
85
+ 1. **Debug log** — one line per tool_use:
86
+ `AUTOMODE RISK: <id> <tool> class=… score=… signals=… latency=…ms`.
87
+ 2. **Telemetry** — score ≥ 4 calls append a `dashboard_events` row
88
+ (`category=automode`, `type=risk.classified`, severity `warning` for
89
+ destructive) via `project_telemetry.record_risk_event`, so the
90
+ dashboard's Live Events panel shows high-risk calls. Routine reads do
91
+ not flood the feed.
92
+ 3. **Nothing else.** On any classifier exception the proxy logs a warning
93
+ and passes the tool call through unchanged — the advisory path fails
94
+ OPEN precisely because the enforcer floor fails CLOSED; a dead
95
+ classifier degrades to today's behaviour plus one warning line.
96
+
97
+ ## Calibration
98
+
99
+ `tools/agents/scripts/tool_risk_calibrate.py` loads the labeled corpus
100
+ (`tools/agents/tests/fixtures/tool_risk_cases.json`, 109 cases across the
101
+ five classes including evasion variants and benign lookalikes), makes a
102
+ deterministic seeded stratified split, and reports per-class
103
+ precision/recall on the held-out set. The committed numbers, split seed,
104
+ and confusion notes live in
105
+ [**docs/performance/automode-risk-calibration.md**](../performance/automode-risk-calibration.md).
106
+
107
+ **Held-out destructive recall is gated at 1.0** (`--check` exits non-zero
108
+ otherwise): a missed destructive call is the unacceptable error. False
109
+ positives are advisory-only cost.
110
+
111
+ ## Latency
112
+
113
+ The scorer is pure regex/dict work, measured in microseconds; a bench test
114
+ (`tools/agents/tests/test_tool_risk.py`) asserts **p95 < 5 ms over 10k
115
+ calls**. The 50 ms AutoMode budget is reserved for the future model call;
116
+ the heuristic floor sits far under it.
117
+
118
+ ## Escape hatch
119
+
120
+ `PROXY_AUTOMODE_RISK=off` disables classification in the proxy (default
121
+ `on`). This is an operator hatch in the trusted launch env, same posture as
122
+ the other proxy switches.
123
+
124
+ ## Files
125
+
126
+ | path | role |
127
+ |---|---|
128
+ | `tools/agents/scripts/tool_risk.py` | the scorer (pure) |
129
+ | `tools/agents/scripts/tool_risk_calibrate.py` | calibration harness / gate |
130
+ | `tools/agents/tests/fixtures/tool_risk_cases.json` | labeled corpus |
131
+ | `tools/agents/tests/test_tool_risk.py` | 36 tests (registered in `test:enforcers`) |
132
+ | `tools/agents/scripts/anthropic_proxy.py` | advisory wiring (3 interception points) |
133
+ | `tools/agents/scripts/project_telemetry.py` | `record_risk_event` dashboard sink |
134
+ | `docs/performance/automode-risk-calibration.md` | committed calibration report |
@@ -0,0 +1,146 @@
1
+ # Evidence-Bound Ship (gate evidence)
2
+
3
+ > Policy: `evidence-bound-ship` · Enforcer: `src/policies/enforcers/evidence_bound_ship.py`
4
+ > · Writer: `src/delivery/gate-evidence.ts` · Escape hatch: `UAP_EVIDENCE_GATE_OFF=1`
5
+ > (operator launch environment only)
6
+
7
+ A deliver mission ends with its gates green against some `HEAD`. That `HEAD` is
8
+ the merge candidate a later ship action publishes. **Evidence binding** makes
9
+ the candidate carry its proof: at ship time, the enforcer requires a
10
+ machine-recorded artifact showing the mission's gates passed for *exactly* the
11
+ commit being shipped — not for some earlier or later tree.
12
+
13
+ ## The contract
14
+
15
+ Ship-class actions (same detection as `expert-review-required`: `git commit` /
16
+ `push` / `merge`, `gh pr create` / `merge` / `ready`, pr-ready / signoff) are
17
+ **blocked** unless `.uap/evidence/<head-sha>.json` exists and validates:
18
+
19
+ ```json
20
+ {
21
+ "version": 1,
22
+ "candidateSha": "<full HEAD sha the gates prove>",
23
+ "recordedAt": "<ISO-8601>",
24
+ "runId": "<deliver run id, optional>",
25
+ "hatches": ["<gate-affecting env vars set at deliver time, audit only>"],
26
+ "gates": [
27
+ {
28
+ "name": "test",
29
+ "command": "npm test",
30
+ "exitCode": 0,
31
+ "outputTail": "<last ≤2000 chars of combined output>",
32
+ "at": "<ISO-8601>"
33
+ }
34
+ ]
35
+ }
36
+ ```
37
+
38
+ | Check | Rejection |
39
+ |---|---|
40
+ | Artifact missing | BLOCK — commit, then run `uap deliver` to record evidence for this HEAD |
41
+ | Oversized (>512KB) | BLOCK — read is capped before parsing; a payload is not evidence |
42
+ | Not parseable JSON | BLOCK — delete and re-run `uap deliver` |
43
+ | `version` ≠ 1 | BLOCK — re-record with the current CLI |
44
+ | `candidateSha` ≠ HEAD | BLOCK — evidence for a different commit proves nothing about this one |
45
+ | `recordedAt` < commit time | BLOCK — backdated (the commit did not exist to be gated yet); an *unresolvable* commit time also BLOCKs (fail-closed on anomaly) |
46
+ | `recordedAt` > now + 5min | BLOCK — future-dated (forged, or a badly skewed clock) |
47
+ | `recordedAt` older than the staleness window | BLOCK — stale; re-run `uap deliver` |
48
+ | No gate with `exitCode: 0` + `command` + `at` | BLOCK — empty/forged artifacts are not evidence |
49
+
50
+ The enforcer **fails closed** on every ambiguity. The only fail-open is a tree
51
+ that is not a git repository (or git itself missing) — non-UAP trees are
52
+ unaffected. It uses local git + the filesystem only: no network, no `gh`, and
53
+ it never throws uncaught inside the live policy chain.
54
+
55
+ ## How deliver emits evidence
56
+
57
+ `uap deliver` records the artifact at the single completion seam every runner
58
+ kind (single / phased / orchestrated / ci-reconverge) converges on, right where
59
+ the run's outcome is persisted (`completeDeliveryTask` /
60
+ `recordDeliveryOutcome` in `src/cli/deliver.ts`). The gates come from the
61
+ **last passing** iteration's rung results — or, when the tree was already green
62
+ and the run short-circuited as `alreadyDelivered`, from the **baseline ladder
63
+ run** (`DeliveryResult.baselineGates`), so the commit → deliver → ship flow
64
+ never dead-ends. Emission is **fail-soft**: a warning is the whole failure
65
+ mode, so evidence recording can never break a mission.
66
+
67
+ The writer **refuses a dirty working tree** (`git status --porcelain` must be
68
+ empty): evidence claims the gates passed for `<sha>`, and with uncommitted
69
+ changes they provably ran against HEAD-plus-delta. The supported flow is:
70
+ commit the candidate, run `uap deliver` (baseline goes green, evidence binds
71
+ the clean HEAD), ship.
72
+
73
+ Writes are atomic (tmp + rename), and `.uap/` is git-ignored — evidence is a
74
+ local record, never committed payload.
75
+
76
+ Per-gate `at` is **reconstructed**, not measured: rungs carry durations but no
77
+ wall-clock finish time, so a gate's stamp is the recording time minus the
78
+ durations of the gates that ran after it. Ordering is preserved; timestamps
79
+ are approximate.
80
+
81
+ ## Forgery resistance
82
+
83
+ `.uap/evidence/` is listed in enforcement-self-protect's `PROTECTED_TARGETS`
84
+ with **no agent carve-out**, and the interpreter-write rule (built for the
85
+ trust anchors) covers the directory too: shell redirects, Write/Edit tool
86
+ calls, `python3 -c`, `node -e`, and heredoc-fed interpreter writes are all
87
+ refused. The deliver CLI writes artifacts; the agent cannot. The trust root is
88
+ "only the tooling could have written this".
89
+
90
+ ## Scope & limitations
91
+
92
+ This gate is a **local** perimeter, with the following limits:
93
+
94
+ - **Enforcement fires only on ship actions routed through the local policy
95
+ chain.** A merge pressed in the GitHub web UI, or a push from a machine
96
+ without the chain installed, never consults the enforcer. The remote-side
97
+ complement is ordinary branch protection / required status checks; this
98
+ policy is the local half, not a replacement for it.
99
+ - **`.uap/` never reaches CI** — it is git-ignored by design, so evidence is
100
+ not a portable attestation another machine can verify. It proves something
101
+ to *this* checkout about *this* HEAD.
102
+ - **`gh pr merge <N>` binds the LOCAL worktree HEAD.** The enforcer runs
103
+ without network or `gh`, so it cannot resolve the PR's head ref. If the
104
+ local branch has diverged from the PR head, the evidence validates the wrong
105
+ commit. Ship from the branch you deliver on.
106
+ - **Squash/rebase merges mint a new remote commit** that no local evidence can
107
+ bind — the merged SHA differs from the gated HEAD by construction. Treat
108
+ those merges as covered by the remote-side controls above.
109
+ - **`git merge master` (upstream sync) requires evidence for HEAD**, the same
110
+ as any ship action — consistent with `expert-review-required`. Re-run
111
+ `uap deliver` on the synced tree first.
112
+ - **`--keep-best` rollback edge**: when a deliver run rolls the tree back to a
113
+ pre-run snapshot, HEAD is unchanged but the recorded evidence describes the
114
+ pre-rollback tree. The gates passed on that exact commit in both states, so
115
+ the claim still holds — but the output tails describe a tree you may no
116
+ longer have.
117
+
118
+ ## Staleness semantics
119
+
120
+ Default window: **24 hours** from `recordedAt`. Rationale: evidence certifies a
121
+ commit, and a commit's content cannot drift — but the *environment* the gates
122
+ ran in can (dependencies updated, base branch moved, services changed). A day
123
+ is long enough to cover a normal deliver → review → ship cycle and short enough
124
+ that week-old green runs cannot launder a rebased branch. Operators can tune it
125
+ with `UAP_EVIDENCE_MAX_AGE_HOURS` in the **launch environment** — the agent
126
+ cannot set it (enforcement-self-protect refuses any assignment of the name,
127
+ inline or persisted, because a huge window quietly retires the check).
128
+
129
+ Note the flow-through consequence: ship gates bind evidence to `HEAD`, and the
130
+ writer requires a clean tree, so the order is commit → `uap deliver` → ship.
131
+ Amending or adding commits after the deliver run requires a fresh deliver run
132
+ to re-record evidence for the new candidate. That is the intended posture —
133
+ the gates must have passed on the exact commit being pushed.
134
+
135
+ ## Escape hatch
136
+
137
+ `UAP_EVIDENCE_GATE_OFF=1` in the **launch environment** (operator-only). The
138
+ inline form (`UAP_EVIDENCE_GATE_OFF=1 git push`) is refused by
139
+ enforcement-self-protect — the agent composes its own command strings, so an
140
+ inline override would be self-granted, the same reasoning as `UAP_NO_REVIEW`.
141
+
142
+ ## Related
143
+
144
+ - [Policies guide](../guides/POLICIES.md) — the executable policy framework
145
+ - [`uap deliver`](../guides/DELIVER.md) — the convergence harness that records evidence
146
+ - Schema doc: `src/policies/schemas/policies/evidence-bound-ship.md`