Fixes from the code review on the critical/high audit branch:
- tools/apply_patch: parseHunkCounts now reads only the range section between
the opening and closing "@@". A crafted section heading like
"@@ -1,1 +1,1 @@ +1,999999" could otherwise overwrite the line count, wedge
the parser in hunk mode, and swallow later file headers so they escaped
validatePatchPaths / recheckPatchWriteTargets — a workspace-confinement bypass.
- providerhealth: the SSRF-hardened dial now tries every validated address in
order instead of pinning addrs[0], so a dual-stack/multi-record provider isn't
failed by a single dead record. Extracted dialValidatedAddrs for coverage.
- hooks/dispatch: a beforeTool hook killed by its deadline now fails CLOSED
(vetoes the tool) instead of fail-open. A launch failure (missing binary)
still fails open so a misconfigured hook can't wedge every tool call.
- cron: Store.Get returns a distinct ErrJobNotFound sentinel; cron_run only
treats that as "removed during run" and still records/persists on a transient
read error instead of silently dropping the run.
- providers/anthropic: closeOpen finalizes thinking buffers still open at stream
end (SSE ended after thinking_delta/signature_delta but before
content_block_stop), so they survive in the done event's ReasoningBlocks for
replay. Finalized in index order.
- providers/gemini: corrected the stale thinkingConfig comment (no IncludeThoughts
field; thought summaries are suppressed by skipping part.Thought parts).
Tests: regression coverage for each fix, plus the requested nitpicks —
EOF-terminated MCP line, recipe-prompt propagation assertion, checked
file.Close() in session store tests, and a shortened bash process-group timeout
test via the bashWaitDelay hook.
Note: declined the suggestion to replay a "signature" on redacted_thinking
blocks — Anthropic's redacted_thinking carries only an opaque "data" field; the
signature is for regular thinking blocks, and the stream never populates one for
redacted blocks.
- `cron add "<expr>" --recipe X` silently dropped the explicit schedule because
the recipe set expr before the positional arg was consumed. Consume the
positional expr first so it overrides the recipe default.
- cron_run persisted the job copy read at tick start, so a pause or removal that
happened during the run was clobbered back to active. Re-read before persisting
and preserve an external pause/removal (best-effort; full atomicity needs a
lock).
* cron: dep-free 5-field cron expression parser
* cron: next-run evaluator with Vixie DOM/DOW semantics
* cron: preset recipes + loop.md prompt resolution (cap + symlink reject)
* cron: file-backed job store (metadata.json + runs.jsonl)
* cli: zero cron add/list/rm/pause/resume over the cron store
* cli: zero cron run foreground scheduler (--once/--catch-up, exec per fire)
* cron: fix adversarial-review findings (DST loop, traversal, runaway re-fire, +)
- Next: fix infinite loop on DST spring-forward gaps (advance hours by absolute
addition + a forward-progress guard); widen the search window to 9 years so a
Feb-29 schedule across a century non-leap year (2096->2104) isn't reported as
impossible.
- store: reject path-traversal job ids (Get/Update/Remove/AppendRun) so
'cron rm ../..' can't delete outside the store; List now surfaces corrupt jobs
as a warning instead of silently dropping them.
- runner: reject impossible schedules at add even with --run-now; auto-pause a
job whose schedule can no longer advance (was re-firing every tick); only
skip-reschedule STRICTLY-overdue jobs on startup (keep exactly-due ones);
capture stderr into RunRecord.Error on non-zero exit; pass the prompt via the
inline --prompt= form so dash-leading prompts aren't misparsed.
- Regression tests for each (DST, leap gap, traversal, corrupt-list, pause,
reconcile, dash-prompt).
* cron: address #155 review (store-error surfacing, resume guard, Runs traversal, +)
- fireJob/reconcile/cronRun now surface AppendRun/Update failures to stderr
instead of discarding them (a failed write no longer silently re-fires/loses
history); fireJob takes stderr.
- cron add rejects extra positional arguments instead of silently ignoring them.
- cronResume rejects an unparseable/impossible schedule instead of reactivating a
job with a stale/zero NextRunAt.
- store.AppendRun returns the Close() error (buffered-write failures surface on
close); store.Runs now validates the id (path-traversal guard, matching the
other store methods).
- tests: Runs traversal rejection, cron add extra-args, resume-impossible.
* cron: check store errors in cron CLI tests
CodeRabbit (critical): the cron CLI tests ignored errors from store.List/Add/Get, so a store failure would surface as a misleading assertion on a zero-value job/slice instead of a clear failure. Check every store op's error across the test file (not only the two flagged sites).
* cron: DefaultRoot falls back to os.UserHomeDir when HOME is unset
Vasanth (P1): cron's DefaultRoot mirrored neither sessions.DefaultRoot nor reality — with no XDG_DATA_HOME and no HOME it produced a RELATIVE ".local/share/zero/cron" under the caller's cwd, so on Windows/restricted shells cron jobs scattered per working directory and could write repo-local data. Now falls back to os.UserHomeDir() like sessions.DefaultRoot. Adds TestDefaultRootEmptyHomeFallsBackToUserHome (asserts the path stays absolute).