Commit Graph
4 Commits
Author SHA1 Message Date
KRATOS bd7cc5665f fix: behavioral-audit follow-ups (exec -p, cron/version/specialist UX, update_plan coercion, Makefile) (#286)
From the 2026-06-21 behavioral audit. Each verified by tests + the real binary.

- exec: accept the advertised `-p`/`-p=` short flag in the exec subcommand parser;
  previously only `--prompt` worked even though top-level help advertises `-p`,
  so the documented `zero exec -p "..."` form errored (internal/cli/exec_parse.go).
- version: `version --help` prints a usage line instead of the version string
  (internal/cli/app.go).
- specialist: `specialist edit <builtin>` explains builtins are read-only instead
  of the misleading "specialist not found" (internal/cli/specialist.go).
- cron: `pause`/`resume`/`rm` now print a success confirmation (rm's verb arg was
  previously dead) (internal/cli/cron.go).
- update_plan: coerce non-canonical statuses (e.g. "done", "in-progress") to the
  nearest canonical value instead of failing the whole call, and keep at most one
  in_progress item (internal/tools/update_plan.go + tests).
- build: add a Makefile (build/test/lint/...) backing AGENTS.MD's `make`/`make lint`.

Investigated but intentionally NOT changed (guarded by existing tests):
- write_stdin AdvertiseInAuto — TestCoreToolsExposeShellTools asserts it stays
  visible in auto mode for polling/interrupts.
- --list-tools not resolving config (so tool_search is absent there) —
  TestRunExecListsMCPToolsWithoutProviderResolution asserts list-tools must not
  resolve provider config.

Out of scope: LICENSE (owner decision), provider-resolve startup crash (PR #283),
OAuth default storage + MCP auto-reconnect (design choices).
2026-06-21 15:57:38 +05:30
gnanam1990 f7c9094e1b cli/cron: honor explicit expr over recipe; don't clobber pause mid-run
- `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).
2026-06-11 15:02:09 +05:30
6fb0231df8 TUI scrollback re-architecture, Lime chat surface, and deep-audit fixes (#158)
* docs(design): add Lime TUI prototype and rebuild spec

* tui: remove the zeroline skin system

One design remains in the tree ahead of the Lime rebuild: the zeroline
package, its TUI view/skin plumbing (Options.Skin/ThemeVariant/ThemeDark,
model skin branches, theme picker), and the zeroline CLI subcommand are
gone. /theme keeps its shell-only message.

* tui: replace the cyan palette with the Lime token table

theme.go is rebuilt around the Lime design tokens (docs/design/
zero_tui_lime.html): near-black panels, one lime accent, solid tint
stand-ins for the prototype's rgba overlays. Existing render sites map
onto the new role styles (ink/line/userPrompt/toolName/toolArg) without
layout changes; no hex exists outside theme.go.

* tui: build the Lime chat-surface components

Title bar with brand badge + context window, empty state replacing the
deleted splash (block-art 0, tagline, 1-3 starter suggestions), stream
blocks (user prompt gutter, muted interim with cursor, final-answer
accent rail, done line, bordered notes), tool cards with diff/read/
bash/grep bodies and MiniDot run spinner, borderless composer with
run-state hints, and the grouped status line. Final answers are marked
at append time; resolved tool calls collapse into their result cards.

* tui: fix Stage 2 review findings

Adversarial review of the chat-surface commit confirmed 15 issues, all
fixed here:
- wrapPlainText sliced wide runes by rune count: CJK/emoji prose could
  panic the TUI or emit lines ~2x the measure; split at width instead
- a cancelled run's streamingText leaked into the next turn's interim
  block; cancelRun now clears it
- orphaned tool-call cards (cancelled/errored turns) re-animated with
  the spinner on any later run; the glyph is now scoped to the call's
  own runID
- starter-suggestion digit capture was active while the chips were off
  screen (/clear mid-run); the guard now mirrors the render condition
- prompt submits while a deferred Ctrl+C quit is draining could start a
  run the quit would orphan; submission is blocked while exiting
- the running placeholder advertised ctrl+c (which quits) as the
  interrupt key; it now says esc
- read-card parser expected 'N: text' but read_file emits 'N | text',
  so the gutter and L-range never rendered on real output
- toolCard borders were one column short of the body rows
- diff tint bands now span the full row; NEW FILE renders green on
  addBg; preamble/no-newline lines are unnumbered meta and no longer
  shift hunk numbering
- card bodies and system notes paint the panel surface; the head gains
  the separate faintest arg column (grep target + pattern)
- title-bar overflow now truncates the minimal candidate instead of
  rendering the deleted design's 'ZERO … READY' line
- untracked the stray snake-game.html (left on disk, ignored)

* tui: restyle the interactive surfaces for Lime

Permission prompts render as the amber permission card (PERMISSION
badge, risk readout, key chips); resolved prompts collapse to one line
(allowed once/always/denied · tool) and unprompted allows fold into the
tool card's [auto] tag. Ask-user and spec-review prompts share the card
language with line borders. Autocomplete and picker overlays move onto
the panel/selection tints with the accent ❯ marker; model-picker rows
gain the provider dot and ctx · KEY_ENV metadata from the registry and
provider catalog. /resume renders its list as stacked session cards
(id + age, title, meta). Key handling is untouched everywhere.

* tui: adaptive width tiers, doc updates, final polish

Width tiers re-evaluate from the live width: ≥100 renders the full
spec; 80–99 drops the tool-arg column, header ctx, and the status
'interactive' group; 58–79 drops the diff/read gutters, bares the
badge, and keeps provider+tokens+mode; <58 renders a single-segment
header, rail-less cards, and a mode-only status line. Table-driven
tests cover {58,70,80,100,120} plus a frame-wide invariant that no
emitted line exceeds the terminal at any tier. Card bodies expand tabs
so width math holds. Dead splash-chip machinery (startupCommandNames,
startupOrder) is gone; README's TUI section now describes Lime.

* tui: fix final-review findings across Stages 3-4

A second adversarial review confirmed 14 issues, all addressed:
- permission collapse and tool-card pairing are now run-scoped (rcKey):
  providers with repeating synthetic ToolCallIDs (Gemini gemini_tool_N)
  could attribute a decision or result to a different run's call
- rehydrated always-grants no longer mislabel as 'allowed once'
  (GrantMatched fallback)
- the width invariant now actually holds everywhere: empty-state
  tagline/hint, ask-user rows, permission detail blobs, pending image
  chips, and the sessions trailing hint are wrapped or fitted; the
  invariant test renders the empty state and all of those rows at
  widths 24-120
- every card (permission, ask-user, spec-review, sessions, notes)
  drops side borders on tiny terminals, not just tool cards
- /resume card fields are sanitized against separator bytes from
  user-controlled titles
- the picker's selected row paints its gap so the selBg band is solid
- dead splash helpers (normalizedStartupWidth, countLines, nonEmpty,
  unreachable rowWelcome branch) and stale /theme comments are gone
- README no longer claims transcript scrolling keys or Tab-accept
  behavior that never existed; chatWidth documents its 24-col floor

Tag tui-lime-s4 moves to this commit.

* audit: deep-audit report, TUI scrollback re-architecture, and cross-module fixes

TUI: settled-row flush frontier prints history to native terminal
scrollback (tea.Println, ordered + ack-serialized); View() renders only
the live tail. Full diffs (400-line flush cap) and OSC 8 file:// links
land in history; streamed interim text, cancellation markers, ask_user
exchanges, and composer input history are preserved; run-scoped dedup
keys fix Gemini's repeating tool-call ids; /exit, Ctrl+C-after-Esc, and
cross-session late flushes no longer orphan or contaminate checkpoints.

Cross-module: zero search panic + offset mapping, usage report
robustness, 529 retry, SSE keep-alive watchdog, registry corrections,
update --check on dev builds, MCP ping + mcp list --json secret
redaction, sandbox.network config knob, doctor apiKey presence, atomic
config writes, C1-safe notifications, rune-safe truncation everywhere,
gofmt + vet CI gates, dead glamour dependency dropped.

Full audit report: docs/audit/2026-06-10-deep-audit.md (175 findings).

Co-Authored-By: Claude Fable 5 <[email protected]>

* tui: hide malformed ask_user tool errors

* fix tui malformed tool retries

* fix max-turn finalization

* upgrade tui agent guardrails

* fix coderabbit guardrail findings

---------

Co-authored-by: Claude Fable 5 <[email protected]>
Co-authored-by: Vasanthdev2004 <[email protected]>
2026-06-10 17:18:30 +05:30
KRATOS 0f8ea50101 zero cron: dep-free file-backed scheduled agents (foreground runner) (#155)
* 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).
2026-06-10 13:21:33 +05:30