* ci: harden workflow permissions, add timeouts, concurrency and coverage report
* ci: reuse make fmt-check and refine coverage table formatting
* ci: scan Windows build graph via vulncheck-windows and collapse coverage summary to single-pass awk
* ci: invoke the pinned Windows govulncheck via a local GOBIN
go install writes the binary to GOBIN, which is not necessarily on PATH.
Install into .cache/gobin and run that executable. GOOS=windows applies
only to the scan so the scanner stays a host binary.
* ci: declare contents:read on the action smoke workflow
Pull-request workflows inherit the default GITHUB_TOKEN unless they
set an explicit permission block. The smoke workflow is now covered
by the same least-privilege policy as ci.yml.
* ci: parse workflow permissions structurally and quote the Windows scanner path
The pull-request token check now ignores commented lookalikes and accepts
an inline on: [pull_request] trigger. vulncheck-windows keeps the cache
path as one shell word.
* ci: drop -race from this PR so issue 939 stays with PR 940
Coverage profile remains. The dedicated race-detector job is owned by
the open race PR.
* ci: parse workflow permissions as YAML and scope GOWORK=off
* ci: label the non-race smoke test explicitly
---------
Co-authored-by: cl-ment <[email protected]>
* ci: run the race detector on pull requests
The Makefile declares `go test ./... -race -count=1` as the project's test
command, but no workflow invokes that target. ci.yml and pr-auto-review.yml both
run plain `go test ./...`, and `-race` appears nowhere under .github/workflows,
so the detector has never run on a pull request. The concurrent paths where a
race is easiest to introduce and hardest to see in review, the turn loop,
streaming provider I/O, the session store, cron, swarm mailboxes, and the
cross-process lock paths in the credential and OAuth stores, are all unchecked.
Add a dedicated job rather than putting -race on the existing matrix step. A
race run is typically several times a plain one, and the matrix Test step is
followed by build and smoke, so folding it in would serialize behind those and
make that job the new long pole. On its own it runs alongside the Windows job,
which is the current long pole at 9m29s, and a failure reads as a race rather
than as a smoke failure.
Run `make test` instead of repeating its flags, so the project's declared test
command and what CI executes cannot drift apart again.
Refs #939
* ci: add permissions to race detector job
---------
Co-authored-by: euxaristia <[email protected]>
* fix pinned Go quality toolchain
* fix(make): use a POSIX GOTOOLCHAIN prefix for pinned tool targets
The Windows_NT branch used cmd.exe syntax (set "..." &&), but GNU Make
runs recipes through sh even on Windows (MSYS/Git Bash), where that
form fails and breaks make lint-static/deadcode/vulncheck. A plain
GOTOOLCHAIN=... prefix works everywhere, so the OS check is gone.
Also switch GO_VERSION/GO_TOOLCHAIN to recursive expansion so
'go list -m' only runs when a tool target actually needs it, instead
of at parse time for every target. Addresses PR review from CodeRabbit
and Copilot.
Co-Authored-By: Claude Fable 5 <[email protected]>
* fix(build): make pinned-tool targets shell-neutral, fix README drift
Address code review on PR #689:
- lint-static/deadcode/vulncheck previously set GOTOOLCHAIN via a
POSIX `VAR=value cmd` recipe prefix. GNU Make on Windows runs
recipes through whatever SHELL is configured, and under a native
cmd.exe setup that syntax is invalid — cmd.exe treats the whole
"GOTOOLCHAIN=go1.26.5" token as the program to run and fails before
go ever executes (reproduced locally: cmd.exe errors with
"'MYVAR' is not recognized..." for the equivalent pattern). Switched
to a target-specific `export`, which sets the variable in the child
process environment via Make itself, before is invoked —
works under sh, cmd.exe, and PowerShell alike (verified against all
three targets with SHELL := cmd.exe).
- README.md's Code Quality and Security Checks section still told
contributors to run the raw `go run ...@version` commands this PR
identifies as selecting the wrong (older) toolchain and failing to
load this Go 1.26.5 module — only CI and AGENTS.md had been updated
to the new make targets. Points steps 3-4 at `make lint-static` /
`make vulncheck` instead, matching AGENTS.md.
Verified end-to-end: `make vulncheck`, `make deadcode`, and `make
lint-static` all run successfully and reach their tools (deadcode's
advisory report and lint-static's 35 pre-existing findings match the
PR's documented baseline).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
* fix(build): support stock macOS make
---------
Co-authored-by: Claude Fable 5 <[email protected]>
go vet, gofmt, govulncheck, and deadcode don't catch unused private
functions/assignments reachable within a package but never called, or
staticcheck-style correctness/readability issues. deadcode's whole-program
reachability analysis specifically missed several of these; golangci-lint's
truncateHeadTail nitpick on #476 assumed it would fail CI, but nothing in
this repo actually ran it.
Scoped to unused, ineffassign, and staticcheck rather than the full default
battery, and continue-on-error like deadcode, so this surfaces the existing
findings across the repo (being cleaned up incrementally per #527) without
blocking in-flight PRs on a big-bang adoption.
* Bump toolchain to go1.26.4 to clear reachable stdlib CVEs
go.mod pinned toolchain go1.24.13, whose bundled standard library has 9
govulncheck-reachable CVEs (crypto/tls KeyUpdate DoS, net/http2 loop, crypto/x509 x3,
net, net/url, os, net/textproto). CI and release build via go-version-file: go.mod, so
shipped binaries inherited them.
Bumping the toolchain directive to go1.26.4 clears all nine — govulncheck ./... now
reports 0 reachable (minimum that clears them is go1.25.11; go1.26.4 matches the team's
local Go). The go directive stays at 1.24.2 (minimum language version). Verified:
go build/vet/gofmt clean, full go test ./... green.
* Add govulncheck CI gate and SHA-pin GitHub Actions
- ci.yml: new 'security' job runs govulncheck as a hard gate (fails on a reachable
vulnerability; passes now that the toolchain is bumped) plus an advisory deadcode step
that surfaces dormant code without blocking.
- Pin every GitHub Action to a full commit SHA (checkout, upload-artifact, github-script)
with a version comment, across all four workflows — previously only setup-go was pinned,
leaving the mutable v4/v7 tags as a supply-chain risk.
* Add hooks management command (add/remove/enable/disable)
Wires the previously-dormant hooks.ConfigStore (10 unreachable funcs -> 0) into the CLI:
zero hooks add <id> --event <event> --command <cmd> [--name --description --matcher --arg --user --json]
zero hooks remove|enable|disable <id> [--user --json]
Writes the project hook config by default (<cwd>/.zero/hooks.json) or the user config with
--user, reusing the store's locking + atomic writes + validation (normalizeDefinition). New
hooks are enabled; state is managed via enable/disable. JSON output is secret-scrubbed via
redaction. Mirrors the existing 'zero mcp add/remove/enable/disable' shape. Covered by
add/remove/toggle round-trip, validation, unknown-event, and JSON-redaction tests.
* Fix govulncheck CI job: pin GOTOOLCHAIN to the go.mod toolchain
go run govulncheck@latest selected the toolchain from govulncheck's own go.mod
(downgrading to go1.25.11), which then could not load our go1.26-requiring packages
(fips140only_go1.26.go), failing the security job. Resolve GOTOOLCHAIN from go.mod's
toolchain line so govulncheck and deadcode both run under go1.26.4.
* Address review: harden CI checkout, pin tool versions, validate hook event
CodeRabbit findings on #193:
- ci.yml: persist-credentials: false on all checkout steps (smoke/performance/security)
— these jobs run repo code and never push, so don't keep the token in git config.
- ci.yml: pin [email protected] and [email protected].0 instead of @latest, so the security
gate is deterministic and not subject to supply-chain drift.
- hooks: add exported IsValidEvent/KnownEvents (single source); parseEvent reuses it, and
'zero hooks add' now rejects an invalid --event with a usage error at parse time instead
of falling through to the Upsert app-error crash path.
* 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]>