@miller-tech/uap 1.221.0 → 2.0.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.
- package/README.md +19 -9
- package/config/exl3-profiles/qwen38-27b-exl3.env +68 -0
- package/config/llama-profiles/qwen38-27b-dflash2.env +6 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/benchmarks/paired/adapter.d.ts +15 -0
- package/dist/benchmarks/paired/adapter.d.ts.map +1 -1
- package/dist/benchmarks/paired/adapter.js +22 -1
- package/dist/benchmarks/paired/adapter.js.map +1 -1
- package/dist/bin/cli.js +20 -24
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/deliver.d.ts +34 -0
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +147 -20
- package/dist/cli/deliver.js.map +1 -1
- package/dist/cli/guided-setup.d.ts.map +1 -1
- package/dist/cli/guided-setup.js +15 -0
- package/dist/cli/guided-setup.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +3 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/plan.d.ts.map +1 -1
- package/dist/cli/plan.js +8 -2
- package/dist/cli/plan.js.map +1 -1
- package/dist/cli/policy.d.ts.map +1 -1
- package/dist/cli/policy.js +104 -2
- package/dist/cli/policy.js.map +1 -1
- package/dist/cli/quality.d.ts +11 -0
- package/dist/cli/quality.d.ts.map +1 -0
- package/dist/cli/quality.js +213 -0
- package/dist/cli/quality.js.map +1 -0
- package/dist/cli/wizard-config.d.ts.map +1 -1
- package/dist/cli/wizard-config.js +19 -9
- package/dist/cli/wizard-config.js.map +1 -1
- package/dist/cli/worktree.d.ts.map +1 -1
- package/dist/cli/worktree.js +8 -1
- package/dist/cli/worktree.js.map +1 -1
- package/dist/dashboard/savings.d.ts +1 -4
- package/dist/dashboard/savings.d.ts.map +1 -1
- package/dist/dashboard/savings.js +2 -52
- package/dist/dashboard/savings.js.map +1 -1
- package/dist/delivery/acceptance-judge.d.ts +10 -0
- package/dist/delivery/acceptance-judge.d.ts.map +1 -1
- package/dist/delivery/acceptance-judge.js +45 -3
- package/dist/delivery/acceptance-judge.js.map +1 -1
- package/dist/delivery/agentic-executor.d.ts +12 -1
- package/dist/delivery/agentic-executor.d.ts.map +1 -1
- package/dist/delivery/agentic-executor.js +25 -5
- package/dist/delivery/agentic-executor.js.map +1 -1
- package/dist/delivery/bash-sweep.d.ts +12 -0
- package/dist/delivery/bash-sweep.d.ts.map +1 -1
- package/dist/delivery/bash-sweep.js +8 -1
- package/dist/delivery/bash-sweep.js.map +1 -1
- package/dist/delivery/convergence-loop.d.ts.map +1 -1
- package/dist/delivery/convergence-loop.js +6 -4
- package/dist/delivery/convergence-loop.js.map +1 -1
- package/dist/delivery/execution-gate.d.ts +1 -1
- package/dist/delivery/execution-gate.d.ts.map +1 -1
- package/dist/delivery/execution-gate.js +248 -5
- package/dist/delivery/execution-gate.js.map +1 -1
- package/dist/delivery/snapshot.d.ts +16 -0
- package/dist/delivery/snapshot.d.ts.map +1 -1
- package/dist/delivery/snapshot.js +113 -2
- package/dist/delivery/snapshot.js.map +1 -1
- package/dist/delivery/user-validation.d.ts.map +1 -1
- package/dist/delivery/user-validation.js +55 -10
- package/dist/delivery/user-validation.js.map +1 -1
- package/dist/delivery/verifier-ladder.d.ts +24 -0
- package/dist/delivery/verifier-ladder.d.ts.map +1 -1
- package/dist/delivery/verifier-ladder.js +119 -2
- package/dist/delivery/verifier-ladder.js.map +1 -1
- package/dist/delivery/visual-gate.d.ts +24 -0
- package/dist/delivery/visual-gate.d.ts.map +1 -1
- package/dist/delivery/visual-gate.js +70 -9
- package/dist/delivery/visual-gate.js.map +1 -1
- package/dist/generators/claude-md.js +17 -4
- package/dist/generators/claude-md.js.map +1 -1
- package/dist/policies/liveness.d.ts +105 -0
- package/dist/policies/liveness.d.ts.map +1 -0
- package/dist/policies/liveness.js +251 -0
- package/dist/policies/liveness.js.map +1 -0
- package/dist/policies/policy-memory.d.ts.map +1 -1
- package/dist/policies/policy-memory.js +3 -4
- package/dist/policies/policy-memory.js.map +1 -1
- package/dist/quality/baseline.d.ts +29 -0
- package/dist/quality/baseline.d.ts.map +1 -0
- package/dist/quality/baseline.js +79 -0
- package/dist/quality/baseline.js.map +1 -0
- package/dist/quality/complexity.d.ts +38 -0
- package/dist/quality/complexity.d.ts.map +1 -0
- package/dist/quality/complexity.js +239 -0
- package/dist/quality/complexity.js.map +1 -0
- package/dist/quality/config.d.ts +40 -0
- package/dist/quality/config.d.ts.map +1 -0
- package/dist/quality/config.js +77 -0
- package/dist/quality/config.js.map +1 -0
- package/dist/quality/crap.d.ts +11 -0
- package/dist/quality/crap.d.ts.map +1 -0
- package/dist/quality/crap.js +54 -0
- package/dist/quality/crap.js.map +1 -0
- package/dist/quality/languages.d.ts +25 -0
- package/dist/quality/languages.d.ts.map +1 -0
- package/dist/quality/languages.js +150 -0
- package/dist/quality/languages.js.map +1 -0
- package/dist/quality/report.d.ts +38 -0
- package/dist/quality/report.d.ts.map +1 -0
- package/dist/quality/report.js +251 -0
- package/dist/quality/report.js.map +1 -0
- package/dist/quality/scanner.d.ts +32 -0
- package/dist/quality/scanner.d.ts.map +1 -0
- package/dist/quality/scanner.js +127 -0
- package/dist/quality/scanner.js.map +1 -0
- package/dist/quality/tools.d.ts +28 -0
- package/dist/quality/tools.d.ts.map +1 -0
- package/dist/quality/tools.js +189 -0
- package/dist/quality/tools.js.map +1 -0
- package/dist/types/config.d.ts +148 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +35 -0
- package/dist/types/config.js.map +1 -1
- package/docs/ATTRIBUTION.md +0 -1
- package/docs/INDEX.md +38 -4
- package/docs/analysis/MARKET_POSITION.md +179 -0
- package/docs/architecture/OVERVIEW.md +82 -18
- package/docs/architecture/PROTOCOL.md +1 -1
- package/docs/benchmarks/PAIRED_FINDINGS.md +106 -0
- package/docs/benchmarks/PAIRED_HARNESS.md +16 -0
- package/docs/benchmarks/README.md +59 -0
- package/docs/design/UAP_REACTOR.md +0 -1
- package/docs/getting-started/INSTALLATION.md +1 -1
- package/docs/guides/AUTOMATIC.md +0 -14
- package/docs/guides/AUTOMATIC_FEATURES.md +1 -9
- package/docs/guides/COORDINATION.md +1 -1
- package/docs/guides/DELIVERY_PIPELINE.md +5 -0
- package/docs/guides/DEPLOY_BATCHING.md +1 -1
- package/docs/guides/DROIDS_AND_SKILLS.md +1 -1
- package/docs/guides/LOCAL_MODELS.md +6 -1
- package/docs/guides/MCP_ROUTER.md +1 -1
- package/docs/guides/MEMORY.md +1 -1
- package/docs/guides/MULTI_MODEL.md +1 -1
- package/docs/guides/ORCHESTRATOR.md +1 -1
- package/docs/guides/POLICIES.md +19 -3
- package/docs/guides/QWEN38_EXL3.md +114 -0
- package/docs/guides/WORKTREE_WORKFLOW.md +45 -1
- package/docs/integrations/MCP_ROUTER.md +1 -4
- package/docs/plans/deliver-hardening-plan-2026-07-13.md +173 -0
- package/docs/plans/deliver-hardening-review-response-2026-07-13.md +180 -0
- package/docs/plans/k3-uap-uplift-validation.md +216 -0
- package/docs/plans/qwen38-uap-retest-plan.md +196 -0
- package/docs/reference/API.md +1 -1
- package/docs/reference/CLI.md +49 -14
- package/docs/reference/COMPARISON.md +248 -0
- package/docs/reference/CONFIGURATION.md +1 -3
- package/docs/reference/DATABASE_SCHEMA.md +1 -1
- package/docs/reference/FEATURES.md +3 -3
- package/docs/reference/PATTERNS.md +1 -1
- package/docs/reference/PLATFORMS.md +1 -1
- package/package.json +2 -2
- package/scripts/maintenance/update-skills.py +0 -22
- package/scripts/maintenance/verify-compliance.sh +3 -3
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
- package/src/policies/enforcers/adr_guard.py +0 -0
- package/src/policies/enforcers/bearer_lockdown.py +0 -0
- package/src/policies/enforcers/branch_freshness.py +0 -0
- package/src/policies/enforcers/design_token_gate.py +0 -0
- package/src/policies/enforcers/enforcement_self_protect.py +70 -0
- package/src/policies/enforcers/expert_review_required.py +22 -0
- package/src/policies/enforcers/iac_plan_destruction_check.py +0 -0
- package/src/policies/enforcers/quality_metrics_gate.py +447 -0
- package/src/policies/enforcers/ship_loop_gate.py +0 -0
- package/src/policies/enforcers/validate_plan_on_change.py +0 -0
- package/src/policies/enforcers/visual_verification.py +0 -0
- package/src/policies/enforcers/workdir_scope.py +0 -0
- package/src/policies/schemas/policies/expert-review-required.md +9 -1
- package/src/policies/schemas/policies/quality-metrics-gate.md +74 -0
- package/templates/CLAUDE.template.md +1 -1
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/templates/hooks/pre-tool-use-bash.sh +50 -3
- package/templates/hooks/pre-tool-use-edit-write.sh +31 -0
- package/templates/hooks/session-start.sh +15 -0
- package/templates/hooks/uap-policy-gate.sh +187 -5
- package/templates/stryker.config.json +14 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/tools/agents/scripts/anthropic_proxy.py +254 -28
- package/tools/agents/tests/run_acceptance.py +0 -0
- package/tools/agents/tests/test_client_disconnect.py +0 -0
- package/tools/agents/tests/test_cycle_break_exploration.py +0 -0
- package/tools/agents/tests/test_deferral_break.py +0 -0
- package/tools/agents/tests/test_delivery_enforcement_all_langs.py +0 -0
- package/tools/agents/tests/test_delivery_enforcement_exemptions.py +0 -0
- package/tools/agents/tests/test_delivery_enforcement_filepath.py +0 -0
- package/tools/agents/tests/test_delivery_enforcement_web_and_bash.py +0 -0
- package/tools/agents/tests/test_delivery_enforcement_worktree.py +0 -0
- package/tools/agents/tests/test_delivery_enforcement_write_complexity.py +0 -0
- package/tools/agents/tests/test_disconnect_watcher.py +0 -0
- package/tools/agents/tests/test_doubling_break.py +0 -0
- package/tools/agents/tests/test_empty_maxtokens_recovery.py +0 -0
- package/tools/agents/tests/test_empty_tool_loop_break.py +0 -0
- package/tools/agents/tests/test_enforcement_self_protect.py +90 -0
- package/tools/agents/tests/test_enforcer_escape_hatches.py +0 -0
- package/tools/agents/tests/test_error_loop_break.py +0 -0
- package/tools/agents/tests/test_expert_review_pr_scope.py +13 -5
- package/tools/agents/tests/test_fastpath_config.py +0 -0
- package/tools/agents/tests/test_finalize_suppression.py +0 -0
- package/tools/agents/tests/test_gate_evidence.py +0 -0
- package/tools/agents/tests/test_gate_failclosed_indirection.py +0 -0
- package/tools/agents/tests/test_gate_integrity.py +0 -0
- package/tools/agents/tests/test_live_deliver_kill.py +0 -0
- package/tools/agents/tests/test_malformed_unclosed_think.py +0 -0
- package/tools/agents/tests/test_mandate_beats_recon.py +0 -0
- package/tools/agents/tests/test_mandate_deliver.py +0 -0
- package/tools/agents/tests/test_non_stream_resp_log.py +0 -0
- package/tools/agents/tests/test_output_token_ceilings.py +0 -0
- package/tools/agents/tests/test_overflow_truncate_count_tokens.py +0 -0
- package/tools/agents/tests/test_path_containment.py +0 -0
- package/tools/agents/tests/test_path_normalizer_hardened.py +0 -0
- package/tools/agents/tests/test_repeat_call_guard.py +0 -0
- package/tools/agents/tests/test_repeat_guard.py +0 -0
- package/tools/agents/tests/test_session_admission.py +0 -0
- package/tools/agents/tests/test_stream_heartbeat.py +0 -0
- package/tools/agents/tests/test_stream_thinking_deltas.py +405 -0
- package/tools/agents/tests/test_stuck_break_hard.py +0 -0
- package/tools/agents/tests/test_stuck_break_reattach.py +0 -0
- package/tools/agents/tests/test_turn_count_breaker_periodic.py +0 -0
- package/tools/agents/tests/test_upstream_chokepoint.py +0 -0
- package/tools/agents/tests/test_upstream_model_aliases.py +0 -0
- package/tools/agents/tests/test_validate_plan_gate.py +0 -0
- package/tools/agents/tests/test_vision_passthrough.py +0 -0
- package/tools/agents/tests/test_workdir_scope_enforcer.py +0 -0
- package/dist/cli/rtk.d.ts +0 -34
- package/dist/cli/rtk.d.ts.map +0 -1
- package/dist/cli/rtk.js +0 -401
- package/dist/cli/rtk.js.map +0 -1
- package/docs/integrations/RTK.md +0 -113
- package/scripts/setup/install-rtk.sh +0 -184
- package/src/policies/enforcers/rtk_wrap.py +0 -205
- package/src/policies/schemas/policies/rtk-wrap.md +0 -50
- package/tools/agents/tests/test_rtk_wrap.py +0 -151
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Deliver/enforcement hardening plan — 2026-07-13
|
|
2
|
+
|
|
3
|
+
Motivating incident (pay2u, 2026-07-13): the `delivery-enforcement` policy blocked
|
|
4
|
+
direct source edits and routed a pre-reviewed 6-file C++ change into `uap deliver`.
|
|
5
|
+
The run declared **"Delivered — all required gates pass" after writing ZERO files**:
|
|
6
|
+
the only detected project gates were npm build/test for `apps/web`, which were
|
|
7
|
+
already green, so the loop had nothing that could fail. Meanwhile every blocked
|
|
8
|
+
edit auto-spawned a **blind** background deliver run whose mission was the literal
|
|
9
|
+
string "implement the intended change to <file>" — no content, no spec.
|
|
10
|
+
Related, earlier incident (same repo, board finding): `expert-review-required`
|
|
11
|
+
policy demanded artifacts under paths that Enforcement-Self-Protect locks agents
|
|
12
|
+
out of, while the skill it named did not exist — a compliance catch-22.
|
|
13
|
+
|
|
14
|
+
Defect sites (all paths relative to this repo):
|
|
15
|
+
|
|
16
|
+
| # | Defect | Where |
|
|
17
|
+
|---|--------|-------|
|
|
18
|
+
| 1 | "Delivered" = required gates green; when gates are green at (or near) baseline the loop succeeds without a diff. Two paths: the `baselineCheck` short-circuit (`alreadyDelivered`) and the normal ladder pass — the incident run went green via the latter in 2 turns with zero writes | `src/delivery/convergence-loop.ts` ~742 (`baselineCheck`) and ladder success path |
|
|
19
|
+
| 2 | Gate relevance to the MISSION is never established — npm web gates "verified" a C++ handler change | `src/cli/deliver.ts` gate selection, `src/delivery/execution-gate.ts` |
|
|
20
|
+
| 3 | Self-gate (anti-vacuous floor) only engages when NO project gates are detected | `src/cli/deliver.ts` ~43 (`needsSelfGate = … noRealGates \|\| forceSelfGate`) |
|
|
21
|
+
| 4 | `--no-auto` silently disables the acceptance judge (a verification rail) along with exploration aids | `src/cli/deliver.ts` auto-plan wiring |
|
|
22
|
+
| 5 | Acceptance judge can accept with an empty diff (diff-blind) | `src/delivery/acceptance-judge.ts` |
|
|
23
|
+
| 6 | Gate detection is npm-centric; polyglot repos (C++/docker, SQL migrations) get irrelevant gates | `src/delivery/execution-gate.ts` (`detectArtifactType`, package.json only) |
|
|
24
|
+
| 7 | Autoroute spawns deliver with a vacuous hint; the blocked edit's actual content (old/new strings) is discarded | `src/policies/enforcers/delivery_enforcement.py` ~158 (`deliverHint=f'implement the intended change to {rel}'`), `templates/hooks/deliver_autoroute.py` |
|
|
25
|
+
| 8 | Agentic executor has whole-file `write_file` only — no anchored edit tool; weak models re-read in loops and whole-file rewrites of 1,200-line files are high-risk | `src/delivery/agentic-executor.ts` |
|
|
26
|
+
| 9 | keep-best / protect-tests snapshots cover the whole tree — a deliver run in a SHARED worktree reverts other agents' concurrent (legitimate) edits, including their newly written tests (observed: frontend agent's new test file reverted) | `src/delivery/…` snapshot/rollback + protect-tests paths |
|
|
27
|
+
| 10 | Policy liveness: a policy can demand a compliant path that does not work (missing skill, agent-locked artifact dir, deliver that no-ops) with no detection | `src/policies/*` (no liveness concept) |
|
|
28
|
+
| 11 | Inverted self-protect: compliance artifact paths locked, but the policies DB the gate reads is not protected | pay2u operator patch `uap-policy-fix-APPLY.sh` exists; not upstreamed |
|
|
29
|
+
| 12 | Operator escape hatches are env-var-only (`UAP_DELIVER_BYPASS`, `UAP_WORKDIR_ALLOW`); env does not reliably reach hook processes in every harness, so the sanctioned override paths are effectively unreachable mid-session | `templates/hooks/uap-policy-gate.sh`, enforcers |
|
|
30
|
+
| 13 | Default executor model `qwen35-a3b` regardless of repo criticality | `src/cli/deliver.ts` defaults |
|
|
31
|
+
| 14 | Hook resolves the governing repo from the shell CWD / `cd` target, not from the operation's TARGET path — cross-repo writes are gated by one repo's policy but read the other repo's compliance state (observed: `uap plan validate` recorded in this repo was invisible to the gate anchored in pay2u) | `templates/hooks/uap-policy-gate.sh` MAIN_ROOT resolution; enforcers using relative `.uap/` state |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Options per area
|
|
36
|
+
|
|
37
|
+
### A. Kill the false-green (defects 1–3) — pick one, A2 recommended
|
|
38
|
+
|
|
39
|
+
- **A1 — No-op guard (minimum).** For coding missions, `success` requires a
|
|
40
|
+
non-empty applied-files set across the run (`filesApplied` union), unless the
|
|
41
|
+
caller passes `--allow-noop`. The `alreadyDelivered` baseline short-circuit is
|
|
42
|
+
only taken when the acceptance judge affirmatively confirms the mission is
|
|
43
|
+
already satisfied, with cited evidence. Small, surgical; does not fix gate
|
|
44
|
+
relevance (a run that writes one irrelevant file still "delivers").
|
|
45
|
+
- **A2 — Universal anti-vacuous floor (recommended).** Generalize the self-gate
|
|
46
|
+
rule that already exists for gate AUTHORING to the whole run: every deliver run
|
|
47
|
+
must have ≥1 required gate that FAILS at baseline. If all detected project
|
|
48
|
+
gates are green at turn 0, auto-engage self-gate authoring (today that happens
|
|
49
|
+
only when `noRealGates`). Change is essentially
|
|
50
|
+
`needsSelfGate = selfGateAllowed && (noRealGates || allRequiredGatesGreenAtBaseline || forceSelfGate)`
|
|
51
|
+
plus threading the baseline result into gate selection. HARD-FAIL rule: if the
|
|
52
|
+
required self-gate is still vacuous after its authoring retries (passes on the
|
|
53
|
+
unsolved repo), the RUN fails — today a vacuous gate is only a warning, which
|
|
54
|
+
re-opens the false-green door. This is the smallest change that makes
|
|
55
|
+
"delivered" always mean "something that was red is now green".
|
|
56
|
+
- **A3 — Mission-scoped gate relevance (deep).** Classify the mission → affected
|
|
57
|
+
paths → require gates whose declared scope covers those paths (needs B1's gate
|
|
58
|
+
manifests with `scope:` globs). Most correct; biggest lift; do after A2.
|
|
59
|
+
|
|
60
|
+
### B. Polyglot gates (defect 6) — B1 recommended, B2 additive
|
|
61
|
+
|
|
62
|
+
- **B1 — Project-declared gates.** `.uap.json → delivery.gates[]`:
|
|
63
|
+
`{id, name, cmd, cwd, scope: [globs], required, tier, timeoutSec}`. Declared
|
|
64
|
+
gates merge with (and outrank) detected ones. pay2u would declare:
|
|
65
|
+
docker-buildx `--target builder` for `apps/api/**`, `gen_openapi.py --check`
|
|
66
|
+
for handler files, vitest workspace for `apps/web/**`. Also gives A3 its
|
|
67
|
+
scope metadata later.
|
|
68
|
+
- **B2 — Broader detection.** Recognize Dockerfile targets, CMakeLists, cargo,
|
|
69
|
+
pyproject, migrations dirs. Heuristic; useful defaults, never as trustworthy
|
|
70
|
+
as B1.
|
|
71
|
+
|
|
72
|
+
### C. Verification rails (defects 4–5) — do both, small
|
|
73
|
+
|
|
74
|
+
- **C1 — Diff-aware acceptance.** The judge prompt always includes
|
|
75
|
+
`git diff --stat` + changed-file list since run start; hard rule before the
|
|
76
|
+
LLM call: empty diff ⇒ acceptance FAIL (for coding missions). An LLM judge
|
|
77
|
+
must never be the only thing standing between a no-op and success — make the
|
|
78
|
+
empty-diff case deterministic.
|
|
79
|
+
- **C2 — Decouple rails from `--no-auto`.** `--no-auto` disables exploration
|
|
80
|
+
aids only (candidates, ideation, practices, escalation). Acceptance judging
|
|
81
|
+
and the anti-vacuous floor are verification rails and stay on unless
|
|
82
|
+
explicitly disabled (`--no-acceptance`).
|
|
83
|
+
|
|
84
|
+
### D. Autoroute with content (defects 7–8) — D1+D3 recommended, D2 policy choice
|
|
85
|
+
|
|
86
|
+
- **D1 — Capture the blocked edit as an intent.** The PreToolUse payload already
|
|
87
|
+
contains the exact `old_string`/`new_string`/`content`. The enforcer writes the
|
|
88
|
+
FULL intent into `.uap/pending-deliver.jsonl` (file, op, old, new, ts, agent).
|
|
89
|
+
The spawned deliver mission becomes "apply the recorded edit intents for
|
|
90
|
+
<file>" and the executor gets a deterministic `apply_pending_edits` fast path
|
|
91
|
+
(anchored exact-match application, no model improvisation; model only engages
|
|
92
|
+
when an anchor no longer matches). This converts "block and hope" into a
|
|
93
|
+
queue of exact diffs applied through the sanctioned pipeline with gates —
|
|
94
|
+
today's entire incident would have been a non-event.
|
|
95
|
+
- **D2 — Spawn policy.** Default OFF for auto-spawn (log + surface the pending
|
|
96
|
+
intent to the agent in the block message: "recorded; run `uap deliver
|
|
97
|
+
--pending <file>` to apply"), batch multiple intents per file per run. Avoids
|
|
98
|
+
background fan-out entirely; the agent stays in control of when to flush.
|
|
99
|
+
- **D3 — `edit_file` tool in the agentic executor.** Anchored `{path, old, new,
|
|
100
|
+
occurrence}` replacement alongside `write_file`. Needed by D1's fallback; also
|
|
101
|
+
removes the whole-file-rewrite hazard for large files generally.
|
|
102
|
+
|
|
103
|
+
### E. Shared-workspace safety (defect 9)
|
|
104
|
+
|
|
105
|
+
- **E1 — Exclusive lease.** `deliver.lock` becomes a real mutex: a second run
|
|
106
|
+
against the same worktree refuses to start (override flag for operators).
|
|
107
|
+
Concurrent agents queue rather than interleave.
|
|
108
|
+
- **E2 — Scoped snapshots (recommended with E1).** keep-best rollback and
|
|
109
|
+
protect-tests restoration operate ONLY on files the run itself wrote (tracked
|
|
110
|
+
per-run), never tree-wide — a concurrent agent's test files can't be reverted
|
|
111
|
+
by someone else's deliver run.
|
|
112
|
+
|
|
113
|
+
### F. Policy liveness (defects 10–12, 14) — the catch-22 class
|
|
114
|
+
|
|
115
|
+
- **F1 — Compliance-path liveness check (recommended, with a sabotage guard).**
|
|
116
|
+
Every enforcer declares its compliant path as machine-checkable requirements
|
|
117
|
+
(`requires:` — commands that must exist, dirs that must be agent-writable,
|
|
118
|
+
skills that must resolve). `uap policy enable`/install runs the liveness
|
|
119
|
+
check and re-runs it on session-start; a policy whose compliant path is dead
|
|
120
|
+
raises a loud operator alert and is marked "unhealthy" in `uap policy status`.
|
|
121
|
+
IMPORTANT — auto-degrade to advisory is NOT the default: an agent could
|
|
122
|
+
deliberately break a compliant path (delete a skill, wedge deliver) to force
|
|
123
|
+
a blocking policy down to advisory. Degrade-on-dead-path is opt-in per policy
|
|
124
|
+
(`degradeOnDeadPath: true`) and only when the breakage is outside
|
|
125
|
+
agent-writable surfaces; otherwise the policy stays blocking and the operator
|
|
126
|
+
decides. Liveness probe for deliver itself: a canary mission in a temp repo
|
|
127
|
+
that must produce a 1-line diff and pass its gate.
|
|
128
|
+
- **F2 — Upstream the self-protect inversion fix.** Port pay2u's operator patch
|
|
129
|
+
(`uap-policy-fix-APPLY.sh`): protect `agents/data/memory/policies.db` (the
|
|
130
|
+
enforcement DB) while allowing agent WRITES (not edits/deletes) to compliance
|
|
131
|
+
artifact paths the gates demand (`.uap/reviews/…`).
|
|
132
|
+
- **F3 — Operator overrides via signed file.** Honor
|
|
133
|
+
`.uap/operator-overrides.json` (owner-writable only; hook verifies uid/mode)
|
|
134
|
+
carrying `{deliverBypass: true, workdirAllow: [...], expiresAt}` in addition
|
|
135
|
+
to env vars — env doesn't reach hook subprocesses in all harnesses, which
|
|
136
|
+
today makes the documented escape hatches unreachable mid-session. Time-boxed
|
|
137
|
+
by `expiresAt` so overrides can't rot open.
|
|
138
|
+
- **F4 — Anchor policy state to the TARGET repo (defect 14).** The gate hook
|
|
139
|
+
resolves MAIN_ROOT from the operation's target path (file_path) when present,
|
|
140
|
+
falling back to cwd only for path-less ops; enforcers read state via
|
|
141
|
+
`UAP_STATE_DIR` derived from that root, never a bare relative `.uap/`.
|
|
142
|
+
Cross-repo work then meets the target repo's policies with the target repo's
|
|
143
|
+
compliance state.
|
|
144
|
+
|
|
145
|
+
### G. Model routing (defect 13)
|
|
146
|
+
|
|
147
|
+
- **G1 —** `.uap.json → delivery.model` / `delivery.routing` as the
|
|
148
|
+
config-authoritative default (same authoritative-over-env pattern as
|
|
149
|
+
`delivery.enforcement`); ship a `criticality: money|normal|sandbox` shorthand
|
|
150
|
+
that maps to routing presets. Local qwen stays the default only for `sandbox`.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Recommended phasing
|
|
155
|
+
|
|
156
|
+
- **P0 (stops the bleeding; small diffs):** A2 + C1 + C2, and D2's log-only
|
|
157
|
+
default for autoroute (one-line default flip). After P0, a false-green no-op
|
|
158
|
+
is structurally impossible and blind agents stop spawning.
|
|
159
|
+
- **P1 (makes the sanctioned path actually good):** D1 + D3 + B1 + E1 + E2.
|
|
160
|
+
After P1, a blocked edit flows through deliver as an exact diff with real,
|
|
161
|
+
project-declared gates — the enforcement stops costing precision.
|
|
162
|
+
- **P2 (kills the catch-22 class):** F1 + F2 + F3 + F4 + G1.
|
|
163
|
+
- **P3 (correctness ceiling):** A3 mission-scoped gate relevance on top of B1
|
|
164
|
+
scopes.
|
|
165
|
+
|
|
166
|
+
Rollout: fix in this repo → version-bump (1.145.0 for P0) → reinstall global
|
|
167
|
+
(`npm i -g …`) → pay2u picks it up next session; add regression tests:
|
|
168
|
+
(1) deliver on a green-gated repo with a real mission must NOT return
|
|
169
|
+
`alreadyDelivered` or ladder-pass without a failing-at-baseline required gate;
|
|
170
|
+
(2) empty-diff acceptance must fail; (3) autoroute intent must round-trip
|
|
171
|
+
`old/new` verbatim; (4) policy liveness canary on the two historical
|
|
172
|
+
catch-22s; (5) vacuous required self-gate ⇒ run failure, not warning;
|
|
173
|
+
(6) cross-repo write meets the TARGET repo's policy state.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Deliver Hardening — Parallel-Review Response (2026-07-13)
|
|
2
|
+
|
|
3
|
+
Three review angles ran concurrently against the uncommitted hardening diff
|
|
4
|
+
(code-quality-reviewer, security-code-reviewer, architect-reviewer). This
|
|
5
|
+
document records every finding and its disposition. The implementation plan
|
|
6
|
+
itself is `deliver-hardening-plan-2026-07-13.md`.
|
|
7
|
+
|
|
8
|
+
## Blockers — fixed
|
|
9
|
+
|
|
10
|
+
### 1. E2 scoped restore was inert on the default executor (code-quality + architect)
|
|
11
|
+
|
|
12
|
+
`runFiles` came from `history[].filesApplied`, but the agentic executor (the
|
|
13
|
+
DEFAULT whenever the repo has inspectable content) writes through its own
|
|
14
|
+
tools behind a no-op applier, so the write-set was empty by construction: a
|
|
15
|
+
regressed run logged "nothing to do" and disposed the only good snapshot —
|
|
16
|
+
worse than the blast-radius defect E2 fixed.
|
|
17
|
+
|
|
18
|
+
**Fix:**
|
|
19
|
+
|
|
20
|
+
- `AgenticExecutorOptions.writeLedger` — a caller-owned per-run `Set` of
|
|
21
|
+
project-relative paths, recorded at the `write_file`/`edit_file`/`edit_range`
|
|
22
|
+
`writeFileSync` sites (NOT via the bash sweep's `authorised` map, which is a
|
|
23
|
+
no-op when bash is disabled — the common case).
|
|
24
|
+
- `SweepOutcome.changed` — the turn-end bash sweep now reports every
|
|
25
|
+
stat-moved path, judged or not, so shell writes reach the same ledger.
|
|
26
|
+
- deliver.ts drains the ledger in the applier seam, so
|
|
27
|
+
`history[].filesApplied` is truthful again. The isolated-task executor is
|
|
28
|
+
deliberately NOT wired in: its paths are relative to its own tree and would
|
|
29
|
+
mis-target the projectRoot-scoped restore.
|
|
30
|
+
- Empty write-set + warranted rollback now fails LOUD: the snapshot is
|
|
31
|
+
preserved with an operator-actionable message instead of being silently
|
|
32
|
+
disposed. (A whole-tree fallback restore was considered and rejected — in a
|
|
33
|
+
shared worktree it reverts other agents' work, the incident E2 exists to
|
|
34
|
+
fix.)
|
|
35
|
+
- The convergence loop's idle-breaker comment was updated: `filesApplied` is
|
|
36
|
+
no longer empty-by-construction on the agentic path.
|
|
37
|
+
|
|
38
|
+
Tests: `test/delivery/agentic-write-ledger.test.ts` (8: tool writes recorded,
|
|
39
|
+
refusals not, end-to-end through a mocked model, shell write via the sweep),
|
|
40
|
+
plus a `changed`-field test in `bash-sweep.test.ts`.
|
|
41
|
+
|
|
42
|
+
### 2. operator-overrides.json was agent-mintable (security BLOCK)
|
|
43
|
+
|
|
44
|
+
Every check on the file was satisfiable by a same-uid agent (uid==euid is
|
|
45
|
+
vacuous when the agent IS the user), and an interpreter-mediated write
|
|
46
|
+
(`python3 -c 'open(...)'`) walked past the self-protect text scan — one Bash
|
|
47
|
+
call minted `deliverBypass` + `selfProtectOff`, neutralizing all enforcement.
|
|
48
|
+
|
|
49
|
+
**Fix:**
|
|
50
|
+
|
|
51
|
+
- The hook now requires the override file (and the liveness cache, below) to
|
|
52
|
+
be **ROOT-owned** (`st_uid == 0`), regular, not group/world-writable,
|
|
53
|
+
symlink-free (O_NOFOLLOW + fstat-on-fd), and unexpired. Root ownership is
|
|
54
|
+
the only check with teeth under a same-uid agent: the operator plants the
|
|
55
|
+
file with `sudo`, the agent cannot. (When the agent itself runs as root, no
|
|
56
|
+
local check can help — enforcement is already theater there.)
|
|
57
|
+
- Defense-in-depth: `enforcement_self_protect.py` gained an interpreter-write
|
|
58
|
+
rule — a bash command naming either trust anchor
|
|
59
|
+
(`.uap/operator-overrides.json`, `.uap/policy-liveness.json`) with an
|
|
60
|
+
interpreter (python/perl/ruby/node) and a write primitive
|
|
61
|
+
(`open(…"w"/"a"/"x"/"+")`, `.write*/.dump/.rename/.unlink/…`) is blocked at
|
|
62
|
+
any wrapper depth. Reads (`open` without a mode, `json.load`, `cat`) stay
|
|
63
|
+
allowed; ordinary `.uap` runtime state stays writable.
|
|
64
|
+
- Newline/colon injection through `workdirAllow` values is rejected (a value
|
|
65
|
+
carrying `\nUAP_SELF_PROTECT_OFF=1` would have smuggled an extra export into
|
|
66
|
+
the hook's eval loop).
|
|
67
|
+
|
|
68
|
+
Tests: the override suite was restructured around the new trust model — the
|
|
69
|
+
user-owned-refusal case runs everywhere (it IS the security property); the
|
|
70
|
+
trusted-file cases are `runIf(getuid()===0)` since a non-root process cannot
|
|
71
|
+
mint a root-owned fixture by construction. New injection test. Six new
|
|
72
|
+
enforcer tests (`test_enforcement_self_protect.py`).
|
|
73
|
+
|
|
74
|
+
## High — fixed
|
|
75
|
+
|
|
76
|
+
### 3. Liveness cache trusted blindly (security)
|
|
77
|
+
|
|
78
|
+
The degrade consult now applies the same trust terms as the override file:
|
|
79
|
+
root-owned, regular, not group/world-writable, no symlink, and FRESH
|
|
80
|
+
(`checkedAt` < 24h — a fixed path silently re-tightens; staleness bound was
|
|
81
|
+
the architect's should-fix). The file was already added to PROTECTED_TARGETS
|
|
82
|
+
pre-review. Consequence, documented in the hook and tests: `uap policy
|
|
83
|
+
liveness` run as the user writes the cache for STATUS display; the gate never
|
|
84
|
+
degrades on it. No seeded policy sets `degradeOnDeadPath` today, so no live
|
|
85
|
+
behavior changes; when one does, the operator refreshes the cache elevated —
|
|
86
|
+
a deliberate act, matching the override story.
|
|
87
|
+
|
|
88
|
+
### 4. Declared-gates cwd containment (security #3, partial)
|
|
89
|
+
|
|
90
|
+
`runRung` refuses a declared `cwd` that resolves outside the project root
|
|
91
|
+
(one-line containment check, fail-closed with a clear message; test added).
|
|
92
|
+
The deeper finding — `.uap.json` is writable via interpreter-mediated bash,
|
|
93
|
+
and declared gates make that RCE — is PRE-EXISTING and honestly documented in
|
|
94
|
+
the enforcer (`HONEST SCOPE`); the same interpreter-write class now blocked
|
|
95
|
+
for the trust anchors could be extended to `.uap.json` in a follow-up. The
|
|
96
|
+
id-shadowing of detected rungs by declared ones is an intended feature
|
|
97
|
+
(documented outrank semantics), kept.
|
|
98
|
+
|
|
99
|
+
## Should-fix — fixed
|
|
100
|
+
|
|
101
|
+
- **Liveness worktree alignment (code-quality + architect):** `liveness.ts`
|
|
102
|
+
gained `mainCheckoutRoot()` (git toplevel + `.worktrees` strip, same rule
|
|
103
|
+
as the hook's `${CHECKOUT_ROOT%%/.worktrees/*}`), applied in
|
|
104
|
+
`checkPolicyLiveness`/`writeLivenessCache`/`readLivenessCache`/
|
|
105
|
+
`enabledPolicySlugs`. Session-start runs the refresh from `COORD_ROOT`.
|
|
106
|
+
Previously a worktree session (the mandated workflow) wrote the cache and
|
|
107
|
+
read the DB where the gate never looks — F1 was inert there.
|
|
108
|
+
- **Cache contract:** `version: 1` field added; the slug rule is documented
|
|
109
|
+
as one rule in three places (TS writer, `policyNameSlug`, hook re.sub).
|
|
110
|
+
- **`classifySurface`** now compares separator-terminated (`/repo-evil` no
|
|
111
|
+
longer classifies as inside `/repo`), and an unwritable dir INSIDE the repo
|
|
112
|
+
is `agent-writable` (agent could chmod it — the hardcoded `external` was a
|
|
113
|
+
sabotage hole once any policy opts into degrade).
|
|
114
|
+
- **Python execution gate** prefers `.venv/bin/python` over ambient
|
|
115
|
+
`python3` (deps live in the venv; ambient import failed healthy repos).
|
|
116
|
+
Test with a stub venv interpreter added.
|
|
117
|
+
- **`runNativeBin`** no longer counts a binary that failed to exec at all
|
|
118
|
+
(ENOENT/bad shebang/wrong arch) as pass evidence; all-unexecutable reads as
|
|
119
|
+
an honest skip-pass ("not measurable here"), not a pass.
|
|
120
|
+
- **Hook hot path:** the override parse is guarded by an existence early-out
|
|
121
|
+
— zero python spawns on the common path. (Merging the two payload parses
|
|
122
|
+
was considered and skipped: churn on a string the tests pin byte-exactly,
|
|
123
|
+
for ~one spawn on path-carrying ops only.)
|
|
124
|
+
- **F4 anchor:** walks to the nearest EXISTING ancestor directory (a Write
|
|
125
|
+
into a not-yet-created dir in another repo no longer falls back to cwd),
|
|
126
|
+
and the extractor accepts the same key set as the self-protect extractor
|
|
127
|
+
(`file_path`, `path`, `filePath`, `target`, `filename`, `file`).
|
|
128
|
+
- **deliver.ts** uses `Object.hasOwn` for the criticality lookup (prototype-
|
|
129
|
+
chain values like `"constructor"` no longer pass).
|
|
130
|
+
- **plan.ts** `stateDir` honors an ABSOLUTE `UAP_STATE_DIR` (the hook exports
|
|
131
|
+
it as `$MAIN_ROOT/.uap` since F4; `join` would have concatenated it).
|
|
132
|
+
- **acceptance-judge** labels the git section "UNCOMMITTED CHANGES IN THIS
|
|
133
|
+
TREE" with a shared-worktree caveat instead of "CHANGES IN THIS RUN" — the
|
|
134
|
+
diff is against HEAD, so in a shared worktree it lists other agents' work
|
|
135
|
+
and the old label actively misled the judge.
|
|
136
|
+
- **acceptance-judge test fixture** sets local git identity (flakes on
|
|
137
|
+
machines without global user.email).
|
|
138
|
+
- **`uap policy status` help** documents the cache-write side effect.
|
|
139
|
+
|
|
140
|
+
## Considered, not done
|
|
141
|
+
|
|
142
|
+
- **Whole-tree restore fallback for an empty write-set** — rejected: it is
|
|
143
|
+
the shared-worktree incident E2 fixes. Loud preserve instead.
|
|
144
|
+
- **Signing overrides with an HMAC/keyfile instead of root ownership** —
|
|
145
|
+
root ownership is simpler, needs no key management, and is auditable with
|
|
146
|
+
`ls -l`. Revisit if multi-user operator workflows appear.
|
|
147
|
+
- **Re-deriving `degradable` in the hook from the registry** — the registry
|
|
148
|
+
is TS; the hook is bash. Root-ownership + freshness + the opt-in bit in the
|
|
149
|
+
cache achieves the same guard without a TS eval in the hot path.
|
|
150
|
+
- **ADR adoption** — the repo convention is `docs/plans/`; the plan doc plus
|
|
151
|
+
this response satisfy the decision-record intent.
|
|
152
|
+
|
|
153
|
+
## Behavior changes operators should know (in the commit message)
|
|
154
|
+
|
|
155
|
+
1. **`UAP_STATE_DIR` is now exported by the gate hook as `$MAIN_ROOT/.uap`.**
|
|
156
|
+
In-flight per-worktree state (`plan_state.json`, `read_log.state`, IaC
|
|
157
|
+
acks) is orphaned once — a one-time fail-closed reset: sessions may be
|
|
158
|
+
re-prompted for plan review. State then lives consistently in the main
|
|
159
|
+
checkout instead of fragmenting per worktree.
|
|
160
|
+
2. **Config beats env for deliver model routing** (G1): a declared
|
|
161
|
+
`delivery.model`/`routing`/`criticality` in `.uap.json` now outranks
|
|
162
|
+
ambient `UAP_DELIVER_MODEL`/`UAP_DELIVER_ROUTING`.
|
|
163
|
+
3. **The operator-override file must be root-owned** (sudo) to be honored —
|
|
164
|
+
the original uid/mode-only check was vacuous against a same-uid agent.
|
|
165
|
+
4. **The liveness degrade path** consults only root-owned, fresh (<24h)
|
|
166
|
+
caches.
|
|
167
|
+
|
|
168
|
+
## Verification
|
|
169
|
+
|
|
170
|
+
- `tsc --noEmit` clean.
|
|
171
|
+
- All touched vitest suites green (ledger 8, bash-sweep 39, snapshot-scoped
|
|
172
|
+
10, declared-gates 9, execution-gate-polyglot 13, acceptance-judge 26,
|
|
173
|
+
policy-liveness 12, gate hook suites 22, routing 13, run-state 15).
|
|
174
|
+
- Python enforcer suite: 1212 tests; 3F+9E all in `test_uap_compliance`
|
|
175
|
+
(live-DB schema checks), byte-identical failure set on unmodified master —
|
|
176
|
+
pre-existing environmental.
|
|
177
|
+
- Hook mirror parity enforced by `test_gate_failclosed_indirection` +
|
|
178
|
+
`test_gate_failclosed_schema_diff` (these caught an unsynced mirror during
|
|
179
|
+
remediation — the parity net works).
|
|
180
|
+
- Full vitest suite: see PR body for the final run.
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Kimi K3 × UAP Uplift Validation — Factory Droid Harness Plan
|
|
2
|
+
|
|
3
|
+
**Status:** Proposed (awaiting go-ahead before any paid API runs)
|
|
4
|
+
**Date:** 2026-09-02
|
|
5
|
+
**Supersedes:** the earlier opencode-based variant of this plan (the harness is now Factory Droid, the harness this repo's own sessions run in)
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Measure UAP's uplift on a frontier open-weight model by re-running an existing,
|
|
10
|
+
publicly-anchored benchmark with UAP enabled vs disabled, holding **model,
|
|
11
|
+
harness, task set, and attempt counts fixed**. The model is **Kimi K3**
|
|
12
|
+
(Moonshot AI, 2.8T-param MoE, open-weight, July 2026); the harness is
|
|
13
|
+
**Factory Droid** in headless mode (`droid exec`); the benchmark is
|
|
14
|
+
**Terminal-Bench 2.1**.
|
|
15
|
+
|
|
16
|
+
The only thing that differs between the two arms is the UAP treatment. That
|
|
17
|
+
paired design is the entire experiment: any resolution-rate delta is
|
|
18
|
+
attributable to UAP and nothing else.
|
|
19
|
+
|
|
20
|
+
## Why Terminal-Bench 2.1
|
|
21
|
+
|
|
22
|
+
K3's published scores with harness attribution (from the MoonshotAI/Kimi-K3
|
|
23
|
+
tech report and repo):
|
|
24
|
+
|
|
25
|
+
| Benchmark | K3 score | Harness | Re-runnable? |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| **Terminal-Bench 2.1** | **88.3** | Kimi Code | Yes — public tasks, deterministic verifiers, API-feasible |
|
|
28
|
+
| SWE-bench Verified | 93.4 | Vals AI | Partially — harness not public |
|
|
29
|
+
| SWE-Marathon | 42.0 | Claude Code / Harbor | Yes, but long-horizon = expensive |
|
|
30
|
+
| PostTrainBench | 36.6 | Claude Code | Yes, but GPU-calibrated |
|
|
31
|
+
| DeepSWE / FrontierSWE / Kimi-Code-Bench | various | internal | No — harness-locked |
|
|
32
|
+
|
|
33
|
+
Terminal-Bench 2.1 (89 Dockerized terminal tasks, time-boxed, resolved only
|
|
34
|
+
when post-run verifier tests pass) is the cleanest anchor:
|
|
35
|
+
|
|
36
|
+
- Public dataset with deterministic, non-LLM verifiers — no judge drift.
|
|
37
|
+
- Already supported by our in-repo paired-harness pattern
|
|
38
|
+
(`benchmarks/terminal_bench/uap_opencode_agent.py`), which we adapt to Droid.
|
|
39
|
+
- Factory has published its own Droid-on-Terminal-Bench methodology
|
|
40
|
+
(factory.ai/news/terminal-bench, Sept 2025, TB Core v0.1.1), so our Droid
|
|
41
|
+
setup mirrors a canonical, publicly-described configuration rather than an
|
|
42
|
+
ad-hoc one.
|
|
43
|
+
|
|
44
|
+
### Comparability caveat (read before quoting numbers)
|
|
45
|
+
|
|
46
|
+
K3's 88.3 was achieved on the **Kimi Code** harness, not Droid. Factory's own
|
|
47
|
+
TB analysis found harness design moves scores as much as model choice (Droid +
|
|
48
|
+
Sonnet beat Claude Code + Opus). So:
|
|
49
|
+
|
|
50
|
+
- **Primary claim (internally valid):** paired Δ = UAP-on minus UAP-off, same
|
|
51
|
+
Droid+K3 stack. This is the number the experiment is designed to produce.
|
|
52
|
+
- **Secondary claim (cross-harness, caveated):** Droid+K3+UAP vs the published
|
|
53
|
+
88.3 Kimi-Code anchor. Report it, always with the harness difference named.
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
### Kimi K3 via BYOK (`~/.factory/settings.json` inside the task container)
|
|
58
|
+
|
|
59
|
+
Primary endpoint — Moonshot's official API (OpenAI-compatible dialect):
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"customModels": [
|
|
64
|
+
{
|
|
65
|
+
"model": "kimi-k3",
|
|
66
|
+
"displayName": "Kimi K3",
|
|
67
|
+
"baseUrl": "https://api.platform.kimi.ai/v1",
|
|
68
|
+
"apiKey": "${KIMI_API_KEY}",
|
|
69
|
+
"provider": "generic-chat-completion-api",
|
|
70
|
+
"maxOutputTokens": 65536,
|
|
71
|
+
"extraArgs": { "temperature": 1.0, "top_p": 1.0 }
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Fallback endpoint — OpenRouter (`baseUrl: https://openrouter.ai/api/v1`,
|
|
78
|
+
`model: moonshotai/kimi-k3`, `apiKey: ${OPENROUTER_API_KEY}`). Identical
|
|
79
|
+
pricing at time of writing: **$2.55 / Mtok input, $12.75 / Mtok output**.
|
|
80
|
+
|
|
81
|
+
Sampling parameters follow the K3 tech report's agentic-eval settings:
|
|
82
|
+
`temperature=1.0`, `top_p=1.0`, maximum reasoning effort. Droid's
|
|
83
|
+
`-r/--reasoning-effort` flag does **not** apply to custom models; if the
|
|
84
|
+
endpoint exposes a reasoning-effort knob (e.g. `reasoning_effort`), pass it
|
|
85
|
+
through `extraArgs` and confirm acceptance during Phase 0.
|
|
86
|
+
|
|
87
|
+
Model reference on the command line: `custom:Kimi-K3-0` (display name with
|
|
88
|
+
spaces dasherized + 0-based index into `customModels`).
|
|
89
|
+
|
|
90
|
+
### Arms
|
|
91
|
+
|
|
92
|
+
| Arm | Model route | UAP treatment |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| **A — Baseline** | Droid → K3 direct | none |
|
|
95
|
+
| **B — UAP (protocol)** | Droid → K3 direct | UAP operating protocol dropped as `AGENTS.md` in the task workdir (Droid reads `AGENTS.md` natively) — same honest treatment surface as the existing opencode paired shim: gates discipline, verify-don't-assume, stop-when-green |
|
|
96
|
+
| **C — UAP (enforced, optional)** | Droid → **UAP proxy :4100** → K3 | Arm B **plus** the UAP proxy in the request path, so deterministic enforcers (MANDATE-DELIVER, guardrails) are in the loop. Mirrors the `UAP_TB_UAP_BASE_URL` pattern of the opencode shim. |
|
|
97
|
+
|
|
98
|
+
A vs B is the primary paired comparison (cheapest, cleanest attribution).
|
|
99
|
+
C runs only if A/B shows a signal worth deepening, since it exercises the full
|
|
100
|
+
enforcement stack rather than prompt-level discipline alone.
|
|
101
|
+
|
|
102
|
+
### Droid invocation (per TB task, inside the task container)
|
|
103
|
+
|
|
104
|
+
Mirrors Factory's published TB methodology: non-interactive task mode, all
|
|
105
|
+
permissions skipped (containers are disposable), task specification passed as
|
|
106
|
+
the prompt.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
droid exec \
|
|
110
|
+
--skip-permissions-unsafe \
|
|
111
|
+
--model "custom:Kimi-K3-0" \
|
|
112
|
+
--output-format json \
|
|
113
|
+
"$(cat /task/instruction.md)"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The UAP arm additionally writes `AGENTS.md` into the task working directory
|
|
117
|
+
before the run (and, for arm C, points the model `baseUrl` at the proxy).
|
|
118
|
+
|
|
119
|
+
### New adapter: `benchmarks/terminal_bench/uap_droid_agent.py`
|
|
120
|
+
|
|
121
|
+
A custom `AbstractInstalledAgent` — the same integration point Factory used
|
|
122
|
+
for its own TB runs — with two exported classes, `DroidBaseline` and
|
|
123
|
+
`DroidUAP`, differing only in the AGENTS.md drop (and proxy base URL for arm
|
|
124
|
+
C). A companion `uap-droid-setup.sh.j2` runs inside the container:
|
|
125
|
+
|
|
126
|
+
1. Install the Droid CLI: `curl -fsSL https://app.factory.ai/cli | sh`
|
|
127
|
+
2. Write `~/.factory/settings.json` with the customModels block above
|
|
128
|
+
3. Export `FACTORY_API_KEY` (headless auth) and the K3 provider key from
|
|
129
|
+
host-passed env (`UAP_TB_KIMI_API_KEY` / `UAP_TB_OPENROUTER_API_KEY`,
|
|
130
|
+
`UAP_TB_FACTORY_API_KEY`)
|
|
131
|
+
|
|
132
|
+
Run shape (from the bench venv):
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
UAP_TB_FACTORY_API_KEY=fk-... UAP_TB_KIMI_API_KEY=sk-... \
|
|
136
|
+
.tbvenv/bin/tb run \
|
|
137
|
+
--agent-import-path uap_droid_agent:DroidBaseline \
|
|
138
|
+
--dataset terminal-bench==2.1 -t <task-id> --n-attempts 5
|
|
139
|
+
# then again with :DroidUAP, and diff the two results.json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
(Exact TB 2.1 dataset/registry name to be confirmed during Phase 0; the
|
|
143
|
+
harness moved to the Harbor registry since the opencode shim was written.)
|
|
144
|
+
|
|
145
|
+
## Protocol
|
|
146
|
+
|
|
147
|
+
Three phases, each gated on the previous one. **No paid run starts without an
|
|
148
|
+
explicit go from the user.**
|
|
149
|
+
|
|
150
|
+
### Phase 0 — Smoke (no benchmark scoring; ~$5)
|
|
151
|
+
|
|
152
|
+
Single cheap TB task (e.g. a hello-world-tier task), a few attempts, both
|
|
153
|
+
arms. Validates:
|
|
154
|
+
|
|
155
|
+
1. K3 responds correctly through Droid's `generic-chat-completion-api`
|
|
156
|
+
provider — tool calls parse, multi-turn works.
|
|
157
|
+
2. **Preserved thinking history:** K3 requires prior `reasoning_content` to be
|
|
158
|
+
passed back verbatim across turns. Whether Droid round-trips this field for
|
|
159
|
+
generic providers is unknown — inspect request logs (or the arm-C proxy
|
|
160
|
+
log) to confirm. If it does not round-trip, note the degradation, keep both
|
|
161
|
+
arms identical (the paired comparison remains valid), and consider the
|
|
162
|
+
Anthropic-compatible Kimi endpoint with `provider: "anthropic"` as a
|
|
163
|
+
variant.
|
|
164
|
+
3. Container networking: container → provider egress; container → host proxy
|
|
165
|
+
for arm C.
|
|
166
|
+
4. Headless auth: `FACTORY_API_KEY` works non-interactively in the container.
|
|
167
|
+
|
|
168
|
+
### Phase 1 — Pilot (~10–12 tasks × 3 attempts × 2 arms; est. $40–90)
|
|
169
|
+
|
|
170
|
+
Stratified sample across TB 2.1 categories (coding, build/deps, data/ML,
|
|
171
|
+
systems, security). Purpose: shake out adapter bugs, measure per-run token
|
|
172
|
+
burn to firm up the Phase 2 budget, and check variance. No conclusions drawn.
|
|
173
|
+
|
|
174
|
+
### Phase 2 — Full sweep (89 tasks × 5 attempts × 2 arms)
|
|
175
|
+
|
|
176
|
+
Five attempts per task matches both Factory's published TB convention and the
|
|
177
|
+
existing paired-harness format. Seeds fixed where the endpoint allows;
|
|
178
|
+
temperature/top_p held at the tech-report values in both arms.
|
|
179
|
+
|
|
180
|
+
### Analysis (format per `docs/benchmarks/PAIRED_FINDINGS.md`)
|
|
181
|
+
|
|
182
|
+
- Per-task paired resolution rates; Δ with bootstrap 95% CI.
|
|
183
|
+
- McNemar 2×2 on discordant task-attempt pairs.
|
|
184
|
+
- Secondary: tokens/cost per resolved task, wall-clock, gate-loop behavior
|
|
185
|
+
(arm B should show more build/test iterations and fewer one-shot stops).
|
|
186
|
+
- Report both the paired Δ (primary) and the Droid+K3+UAP absolute score vs
|
|
187
|
+
the 88.3 Kimi-Code anchor (secondary, harness caveat attached).
|
|
188
|
+
|
|
189
|
+
## Cost estimate
|
|
190
|
+
|
|
191
|
+
Rough per-run burn for TB-scale tasks under Droid (environment bootstrap +
|
|
192
|
+
multi-turn tool loop): assume 0.3–1.5M input tokens and 20–80k output tokens
|
|
193
|
+
per attempt. At $2.55/$12.75 per Mtok that is ~**$1.00–4.85 per attempt**.
|
|
194
|
+
Full sweep = 89 × 5 × 2 = 890 attempts → **~$900–4,300**. Pilot firms this up
|
|
195
|
+
before any full-sweep commitment. Prompt caching, if the endpoint honors it
|
|
196
|
+
for generic providers, cuts the input side substantially.
|
|
197
|
+
|
|
198
|
+
## Risks and open questions
|
|
199
|
+
|
|
200
|
+
| Risk | Impact | Mitigation |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| `reasoning_content` not round-tripped by Droid's generic provider | K3 degrades below published behavior | Phase 0 log inspection; both arms affected equally (paired Δ still valid); try Anthropic-compatible endpoint variant |
|
|
203
|
+
| Custom models get Droid's generic tool scaffolding, not per-model tuning | Absolute score likely below Kimi Code's 88.3 even before UAP | Expected and acceptable — the paired Δ is the claim; report absolute with caveat |
|
|
204
|
+
| Headless `FACTORY_API_KEY` auth inside containers | Adapter blocked | Phase 0 smoke |
|
|
205
|
+
| K3 rate limits / TTFT under parallel attempts | Wall-clock blowup on TB's aggressive timeouts | Bounded concurrency (xargs -P), retry-with-backoff in the adapter |
|
|
206
|
+
| Exact TB 2.1 registry/dataset name changed under Harbor | Run command fails | Confirm in Phase 0 against the current terminal-bench docs |
|
|
207
|
+
| Endpoint pricing moves | Budget | Re-quote at Phase 2 kickoff |
|
|
208
|
+
|
|
209
|
+
## Deliverables when executed
|
|
210
|
+
|
|
211
|
+
1. `benchmarks/terminal_bench/uap_droid_agent.py` + `uap-droid-setup.sh.j2`
|
|
212
|
+
(the adapter; not part of this plan PR)
|
|
213
|
+
2. Phase 0/1/2 run artifacts under `benchmark-results/` in the established
|
|
214
|
+
paired format
|
|
215
|
+
3. A findings doc modeled on `docs/benchmarks/PAIRED_FINDINGS.md` with the
|
|
216
|
+
paired Δ, CI, McNemar table, and the caveated cross-harness comparison
|