Fix audit findings + add export command - #5
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements fixes for a completed audit of gemcatch and adds the
exportcommand. 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 --watchmarked a live taskfailedon any poll errorwatchTasknow rides out poll errors: a poll that throws is retried on the next interval (likewatchBatch), and gives up only afterGEMCATCH_WATCH_MAX_FAILS(default 10) consecutive failures.research's catch only marks the taskfailedwhen the submit failed (nointeraction_idyet). 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
refresh, shared bysyncPass), a 404 retires the task locally:setStatus(id, 'incomplete', {error: 'interaction not found (expired or deleted)'}). It leaves the active set, sodaemon --exit-when-idleconverges.watchTask/watchBatchgained 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
@google/genaiclient and drivessubmit → poll → completedplus onefriendly()error straight throughsdkInteractions(), confirmingshape()reads an SDK-shaped response (id,output_text,usage) andfriendly()surfaces Google's real message.4 — MED:
prune -d <negative>deleted all finished tasks--daysis validatedNumber.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
getandwatchgate 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:
batchdropped 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 -ireject non-positive intervals.list -n 0returns zero rows (gate on!= null);-nrejects negatives.Ctrl-Cforce-exits (130).-wal/-shm).process.stderr.isTTY(+NO_COLOR), so redirecting one stream no longer strips/leaks ANSI on the other.GEMCATCH_RPMpacing is per-process.8 — FEATURE:
gemcatch exportgemcatch 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. Defaultmd. The "gather" that pairs withbatch's "scatter".gemcatch digest --tag <t>synthesizes a tag's completed results through one Gemini call; the summary lands under<tag>-digest.Tests added
research --watchleaves the task active (not failed); a latergetcompletes it.incompleteanddaemon --exit-when-idleconverges; a permanently-failingwatchhits the safety bound (no infinite loop).@google/genai.prune -d -5rejected, deletes nothing.batchkeeps#1 ..., skips# ..., reports the skipped count.list -n 0returns zero rows;watch -i -5rejected.export(md/json/-o) anddigest.Docs: README (export/digest, flag validation, per-process RPM note, 404 retirement) and CHANGELOG
[0.3.0].🤖 Generated with Claude Code