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

5.5 KiB
Raw Blame History

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/tickonJobCompletemakeRecordExecutionCallbackcronStore.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.