Skip to content

chore(dag): low-severity batch from 2026-08-19 deep-dive audit #349

Description

@LeXwDeX

Source: docs/audit-dag-deepdive-2026-08-19.md (LOW batch). Each item has file:line evidence in the report; split into individual issues when picking up.

  1. NEW-1 (downgraded from Medium) loop.ts:323-325 — spawn failure boundary catchCause lacks Cause.hasInterrupts rethrow. External interrupts bypass catchCause under Effect v4 (effect-smol Effect.test.ts:1303-1321), so teardown does NOT trigger it; residual value is style parity (cf. 730-737/1055-1062/1483-1490) + defense for future self-rethrow patterns.
  2. NEW-2 loop.ts:431-451 — recovery-pause rejected for a non-terminal cause (lock timeout / store defect) abandons adoption: NodeFailed persisted but no runtime entry → events filtered (802), wake boundary requires entry (1244) → silent stall until restart, only a WARN. Verdict gate retries twice + fail-closed in the same situation.
  3. REC-1 recovery.ts:69-74 — bare cancelSession for pending nodes aborts the whole reconcile on persistent failure → workflow never adopted this process; the else branch (116-127) already has catchCause hardening — an omission.
  4. BLK-02 blocks.ts:265-270 — aggregator input_mapping key collision: foo-bar and foo_bar writers normalize (-→_) to the same key; Object.fromEntries silently overwrites — one writer's changed_files/summary lost (compounds BLK-01 escape detection). fix(dag): parallel-writer aggregator 'mechanical detection' claim is false; undeclared writes escape union+fingerprint binding #347
  5. BLK-03 blocks.ts:277-304 — a verify node rewired to ≥2 aggregators maps only the first aggregator's changed_files/fingerprint — second route's write-set escapes binding; no diagnostic.
  6. CAP-02 capture.ts:77-93,220-225; output-ref.ts:91-96 — unbounded compute on the structured-output path: pathological backtracking regex over unbounded model output can hang validation; uniqueItems O(n²); captureOutputFileRef reads arbitrary-size files whole. The draft action lets a model become a pattern author at near-zero cost.
  7. SW-L1 sweep NodeFailed events carry empty location (no InstanceRef) → live instances' summary-publisher filters them by directory → TUI gets no summary push for that settle (bootstrap refetch shows it; durable folding unaffected).
  8. SW-L2 prompt.ts:191 — sweep's cancel always dies without ambient instance (acknowledged, cause-recovered): sweep can never cancel a still-alive child session itself; the real cancel in the SW-1 mis-kill path relies on live DagLoop abortChild.
  9. F3 tui/src/context/sync.tsx:286-292,626-632 — goal.updated/cleared are ephemeral (not in durable replay); the reconnect hook refreshes DAG only → a goal.cleared missed during disconnect leaves a stale goal sidebar indefinitely (asymmetric with refreshDagSummaries).
  10. F6 dag-inspector.tsx:726-734 + config/keybind.ts — plugin-level second palette command dag.cancel.active missing from keybind Definitions/CommandMap: not rebindable, absent from keybind config schema (violates the "plugin registers only *.open" TUI guideline).
  11. F4 (record) handlers/global.ts:16-23,150 — /global/event is handleRaw + bare JSON.stringify; GlobalEventSchema is documentation-only; summary payload lacks the schema-required id — switching to schema encoding later would drop events wholesale (currently harmless).
  12. F5 (record) dag-event.ts:344-365 vs event-manifest.ts — 20 durable dag.* events are bridged onto the GlobalBus but absent from Definitions/SDK event union: wire-visible, type-invisible drift (TUI consumes only summary by design).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-triageMaintainer needs to evaluate this issue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions