Skip to content

Fix audit findings + add export command - #5

Merged
Booyaka101 merged 1 commit into
mainfrom
fix/audit-findings-export
Jul 19, 2026
Merged

Fix audit findings + add export command#5
Booyaka101 merged 1 commit into
mainfrom
fix/audit-findings-export

Conversation

@Booyaka101

Copy link
Copy Markdown
Owner

Implements fixes for a completed audit of gemcatch and adds the export command. Bumps to 0.3.0. No new dependencies.

All 50 offline checks pass (39 existing + 11 new), including new coverage for every finding below. Run with npm test.

Fixes (by audit number)

1 — HIGH: research --watch marked a live task failed on any poll error

  • watchTask now rides out poll errors: a poll that throws is retried on the next interval (like watchBatch), and gives up only after GEMCATCH_WATCH_MAX_FAILS (default 10) consecutive failures.
  • research's catch only marks the task failed when the submit failed (no interaction_id yet). Once submitted, a watch/poll error never overwrites the status, so the daemon keeps the task and collects the result.

2 — HIGH: a wedged/expired (404) interaction never retired

  • In the poll path (refresh, shared by syncPass), a 404 retires the task locally: setStatus(id, 'incomplete', {error: 'interaction not found (expired or deleted)'}). It leaves the active set, so daemon --exit-when-idle converges.
  • watchTask/watchBatch gained a safety bound: they stop with a clear message and non-zero exit after a bounded run of consecutive poll failures (or a stalled batch) instead of looping forever.

3 — MED: the default SDK transport was never tested

  • Added an offline test that injects a stubbed @google/genai client and drives submit → poll → completed plus one friendly() error straight through sdkInteractions(), confirming shape() reads an SDK-shaped response (id, output_text, usage) and friendly() surfaces Google's real message.

4 — MED: prune -d <negative> deleted all finished tasks

  • --days is validated Number.isFinite(days) && days >= 0; a negative or non-numeric value is rejected before the cutoff is computed.

5 — MED: a completed-but-empty result was never cached, then 404'd

  • get and watch gate the cache hit on presence (result != null), not truthiness, so an empty-text completion is served from SQLite instead of re-polled.

6 — LOW/MED: batch dropped any line starting with #

  • # is a comment only when followed by whitespace (/^#\s/), so #1 cause of X? survives. Skipped blank/comment lines are counted on stderr.

7 — smaller correctness

  • watch -i / daemon -i reject non-positive intervals.
  • list -n 0 returns zero rows (gate on != null); -n rejects negatives.
  • A second daemon Ctrl-C force-exits (130).
  • One-shot commands close the store on exit (no lingering -wal/-shm).
  • stderr-written colour keys off process.stderr.isTTY (+ NO_COLOR), so redirecting one stream no longer strips/leaks ANSI on the other.
  • README documents that GEMCATCH_RPM pacing is per-process.

8 — FEATURE: gemcatch export

  • gemcatch export [--tag <t>] [--status <s>] [--format md|json] [-o <file>] — concatenate finished results, each under its prompt (with id/date), to stdout or a file. Default md. The "gather" that pairs with batch's "scatter".
  • Bonus: gemcatch digest --tag <t> synthesizes a tag's completed results through one Gemini call; the summary lands under <tag>-digest.

Tests added

Docs: README (export/digest, flag validation, per-process RPM note, 404 retirement) and CHANGELOG [0.3.0].

🤖 Generated with Claude Code

Addresses a completed audit and bumps to 0.3.0.

HIGH
- research --watch no longer marks a live, already-submitted task `failed`
  on a transient poll error mid-watch. The watch loop rides out poll errors
  (retry next interval, bounded), and only a failed *submit* (no
  interaction_id yet) is ever marked failed, so the daemon can still collect
  the result. (#1)
- A 404 poll now retires the task locally to `incomplete`, so a wedged or
  expired interaction leaves the active set and `daemon --exit-when-idle`
  converges. `watch`/`batch -w` give up after GEMCATCH_WATCH_MAX_FAILS
  consecutive failures instead of spinning forever. (#2)

MED
- Cover the default @google/genai SDK transport with a stubbed client
  (submit -> poll -> completed and one friendly() error); the suite no
  longer only exercises the REST fallback. (#3)
- prune -d <negative>/non-numeric is rejected instead of putting the cutoff
  in the future and wiping every finished task. (#4)
- A completed-but-empty result is served from cache (gate on presence, not
  truthiness) instead of re-polling and 404ing after 24h. (#5)
- batch treats `#` as a comment only when followed by whitespace, so
  `#1 cause of X?` survives; skipped blank/comment lines are counted on
  stderr. (#6)

Smaller correctness (#7)
- watch/daemon reject non-positive -i; list -n 0 returns zero rows and -n
  rejects negatives; a second daemon Ctrl-C force-exits (130); one-shot
  commands close the store (no lingering -wal/-shm); stderr-written colour
  keys off process.stderr.isTTY so redirecting one stream can't strip/leak
  ANSI on the other.

Feature (#8)
- `gemcatch export [--tag] [--status] [--format md|json] [-o <file>]` — the
  "gather" for batch's "scatter": concatenate finished results under their
  prompts, to stdout or a file. Plus `gemcatch digest --tag <t>` to
  synthesize a tag's completed results through one Gemini call.

Docs: README (export/digest, flag validation, per-process RPM note, 404
retirement) and CHANGELOG [0.3.0]. All offline tests pass, including new
coverage for #1-#8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Booyaka101
Booyaka101 merged commit dd40d22 into main Jul 19, 2026
7 checks passed
@Booyaka101
Booyaka101 deleted the fix/audit-findings-export branch July 19, 2026 11:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant