Files
waggle-os/docs/plans/VERIFIER-VERDICT-TIER3-2026-07-15.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

81 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tier 3 Steal Arc — Adversarial Verification Verdict (2026-07-15)
Branch `feat/steals-tier3` (commits `2d4b0ca2..f322cc2c` + fix commit below).
Verifier fleet: 6 agents (workflow `wf_ac6e9d94-dd9`); 4 completed, 2 (verify:17, verify:cross)
died on API safeguard false-positives — **those two audits were re-executed inline by the
orchestrator** (findings below marked [inline]).
## Verdicts
| Item | Fleet verdict | After fixes |
|---|---|---|
| S1 sanitizer | APPROVE (0 C/H) | APPROVE |
| #13 automation gate | **BLOCK — 1 HIGH** | fixed → APPROVE |
| #15 skill badges | APPROVE (0 C/H) | APPROVE |
| #12 compaction persist | **BLOCK — 1 HIGH + 1 MEDIUM** | fixed → APPROVE |
| #17 ai_task | [inline] **2 findings (1 HIGH-class SEC, 1 MEDIUM)** | fixed → APPROVE |
| cross-cutting | [inline] no blocking findings | APPROVE |
## Fixed in the post-verdict commit
1. **#13 HIGH — error-path transcript dump (chat.ts:2160).** The #3 launch-blocker raw-turn
persistence in the outer catch was not gated: an automated review turn failing on
context_length (likeliest failure for transcript-embedding turns) persisted the ENTIRE
review instruction + session transcript as a `user_stated` frame, then memory-lane cron
would LLM-amplify it. Fix: `&& !isAutomatedTurn`.
2. **#12 HIGH — cross-mind destructive overwrite (orchestrator.ts).** `compactionFrameIds` is
keyed by sessionId while persist routes by active mind; after a workspace switch the same
rowid can be an unrelated user frame in the new mind, and `frames.update()` overwrote it.
Verifier demonstrated with a repro test. Fix: update-in-place only when the existing frame's
content starts with this session's `[Session summary — <key>]` marker; else create fresh.
Regression test added (foreign frame with colliding id survives untouched).
3. **#12 MEDIUM — sign-gate over-trigger.** `isSelfIncapacityAssertion` over the whole
multi-section summary downgraded the entire session gist to `temporary` (recall-invisible)
whenever one boilerplate "you'll need to run X" line appeared — silently no-op'ing the
feature for long sessions. Fix: no sign-gate on compaction summaries (importance always
`normal`, provenance `source='system'`); D1 ruling amended.
4. **#12 LOW — seam consistency.** Persist now also gated `!hasCustomRunner` like every sibling
write-back seam.
5. **[inline] #17 SEC — deliverTo smuggling via job_data.** `create_schedule`'s free-form
`job_data` JSON could set `mode:'ai_task'` + `deliverTo:{platform,chatId}` (with or without
the `prompt` param), routing scheduled agent output to an arbitrary, unpaired chat — bypassing
the trusted-origin-snapshot design. Fix: `mode`/`deliverTo`/`once` are always stripped from
parsed job_data (settable only via the typed param path). Test added.
6. **[inline] #17 MEDIUM — firesTooOften bypass.** Range (`1-59 * * * *`) and step-on-range
(`0-59/2`) minute fields passed the guard. Fix: allowlist (fixed minute | `*/N` N≥5 | ≤12-item
fixed list); everything else rejected. Bypass exprs added to the test matrix. (Damage was
already bounded by the daily cap — verified ALIVE, not dead code: `executeJob`/`tick`
`onJobComplete``makeRecordExecutionCallback``cronStore.recordExecution` fires for every
run mode-agnostically, filling exactly the table `countExecutionsToday` reads; the
cron-ai-task test seeds that table and observes the skip.)
## Residual findings — documented, NO action (LOW/NIT)
- **S1 MEDIUM (pre-existing, out of scope):** 4th legacy sanitizer copy in
`packages/server/src/local/routes/memory.ts:102` (`scope=global` UI search) still zeroes
Cyrillic/diacritic queries — pre-existing (blame 2026-04-12), not benchmark-affecting.
**Backlog: 3-line swap to `buildFtsOrQuery` (needs barrel export from hive-mind-core).**
- S1 LOW ×3: mixed CJK+stopword queries reach LIKE fallback with stopword noise (narrow trigger,
fusion dampens); CJK terms silently dropped from mixed queries when ASCII tokens survive
(documented tradeoff, vector lane compensates, v2 = per-token LIKE augmentation); pure-CJK
still `[]` in MultiMind.ftsSearch / raw-detail-lane (per spec, not a regression).
- #13 LOW: `NOT LIKE '[Loop:%'` is ASCII case-insensitive in SQLite — a user frame starting
`[loop:` is also excluded from lane amplification (stays recallable; bounded).
- #13 NIT ×2: `origin` is self-inflicted opt-out only (localhost+auth, no escalation); auto
skill-capture heuristic (`sessionToolSequences`) not gated for automated turns.
- #15 LOW ×2: vault-POST cache invalidation is a near-no-op today (env/vault checks are uncached;
only bin lookups cache) — harmless, kept as forward-compat; YAML sequence form (`- KEY`) under
`requires:` not parsed (only inline `[a, b]` / comma form) — document the supported grammar.
- #15 NIT: `k in process.env` walks the prototype chain (cosmetic false-positive edge).
- #12 LOW/NIT: `frames.update()` leaves stale `memory_frame_chunks` rows under
`WAGGLE_CHUNK_RETRIEVAL=1` (off by default); injection scan threshold 0.7 lets single-category
signals through (tool-output provenance is `source='system'`, not user-trusted).
## Gate status after fixes
agent tsc 0 · server tsc clean via paths-harness · fix-affected suites 36/36
(cron-tools incl. new SEC + range-bypass cases, compaction-persist incl. cross-mind guard +
no-downgrade cases, cron-ai-task daily-cap alive). Full suites re-run pre-push.
**FINAL: APPROVE** — all HIGH resolved, 0 CRITICAL, residuals are LOW/NIT/pre-existing.