Skip to content

ci: clean-environment first-run e2e — install.sh/install.ps1 → tau setup --yes → tau doctor --selftest - #235

Open
javimosch wants to merge 38 commits into
masterfrom
mago/task-233
Open

javimosch wants to merge 38 commits into
masterfrom
mago/task-233

Conversation

@javimosch

Copy link
Copy Markdown
Owner

Summary

Adds a clean-environment first-run e2e to CI that locks the documented install → onboard → self-verify path for every released platform.

ci workflow — new first-run job (5-leg matrix: linux x86_64/aarch64, macos x86_64/aarch64, windows x86_64):

  1. Builds tau, then stages it as a local release tree — srv/latest/download/<asset> + SHA256SUMS.txt, the exact layout the installers fetch.
  2. Installs it through the shipped installer: install.sh on POSIX legs, install.ps1 on the Windows leg — against a file:// TAU_BASE_URL, so nothing touches the network.
  3. Runs scripts/e2e-first-run.sh (POSIX) / scripts/e2e-first-run.ps1 (Windows) against the installed binary in a scratch HOME with every credential source scrubbed:
    • tau setup --yes --offline — unattended onboarding; asserts config.json is scaffolded and the verdict records ok/yes/non_interactive. A fake provider key stands in for a user who already exported one; --offline keeps the leg hermetic.
    • tau doctor --offline --selftest with zero credentials — asserts the selftest check row reports "ok" (the feature's credential-free promise; the overall verdict honestly exits non-zero with no key, so the assertion is on the check row, not the exit code).
    • The same doctor run with the fake key resolvable — asserts exit 0 (fully green report).

release workflow — the smoke and smoke-windows install-verify jobs now assert the same selftest check row on the shipped artifacts, so published binaries are verified to self-test before release.

scripts/e2e-first-run.{sh,ps1} — the e2e drivers are standalone scripts so the same check runs locally: ./scripts/e2e-first-run.sh ./zig-out/bin/tau.

first-run smoke group — 27 offline checks pinning the driver scripts, the ci.yml/release.yml wiring (drift guards), and an end-to-end run of the POSIX driver against the built binary.

Stacked base

This branch merges the prerequisite chains the e2e exercises:

Merge conflicts resolved as unions (README, completions, CHANGELOG, args.zig — adapted parseArgvEnv to task-193's plain-slice parse signature — main.zig guide text, smoke.sh group registry/functions).

Verified

  • zig build test — 392/392
  • ./scripts/smoke.sh (full offline suite) — 625/625, 1 skipped
  • ./scripts/smoke.sh --group first-run — 27/27
  • Full local chain: staged release tree → install.sh via file:// → e2e-first-run.sh → PASS
  • The Windows leg (install.ps1 + e2e-first-run.ps1) is exercised in CI; doctor --selftest is platform-neutral (in-process loopback stub + env-scrubbed child spawn).

Closes #233 (mago task #233)

mikayassa and others added 30 commits September 25, 2026 20:43
New subcommand writes ~/.config/tau/config.json as JSONC: it scans the
provider table's env_keys (plus TAU_API_KEY), selects the first provider
with a detected key (or an explicit --provider), and emits a commented
starter that names every provider's env vars -- never copying secret
values. Refuses to overwrite an existing file unless --force; --stdout
previews without writing.

configfile.load now strips // and /* */ comments before parsing so the
scaffolded file (and hand-commented configs) load cleanly; string-aware
scanning keeps "//" inside values (e.g. URLs) intact.

Also wires help/guide entries, bash/zsh completions, an offline smoke
group, and fixes a stray literal backslash before the Examples header in
--help output.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
docs/troubleshooting.md maps each user-visible error message (by exit
code and envelope type) to its cause and fix. To make the doc
discoverable, every {"err":...} envelope now carries a "docs" field
pointing at the published URL — the constant lives in version.zig as the
single source of truth. README's Troubleshooting section links the doc
and the embedded guide's see_also lists it.
Unit-test run() end-to-end against a hermetic tmp HOME (same convention
as configfile.zig's TestHome): fresh write produces a commented config
that loads without warning, existing configs are refused without --force
(contents untouched), --force regenerates the scaffold, --stdout writes
nothing, and a missing HOME exits 110.
…134)

Pin the user-visible error contract so DX regressions are caught in CI:

- golden test asserting formatErrorJson output byte-for-byte, including
  the "docs" link to the troubleshooting FAQ
- source scan asserting every hand-rolled {"err":...} literal in
  main/agent/acp/fleet/helpers keeps a "docs" field
- doc tests asserting docs/troubleshooting.md indexes every exit code,
  embeds its own canonical URL, and is linked from the README
Extend shell completion support beyond bash/zsh:

- completions/tau.fish — fish completion with subcommand-aware context
  helpers, per-candidate descriptions, and @file injection that strips
  the sigil, delegates to the builtin file completer, and re-adds '@'
- completions/tau.ps1 — PowerShell Register-ArgumentCompleter script
  (PS 5.1+/pwsh 7+) with tooltip descriptions, quoted '@file' handling
  (bare '@x' is splat syntax in PowerShell), and a Get-ChildItem
  fallback when CompletionCompleters is unavailable
- completions/tau.bash, completions/_tau — parity fixes: add the missing
  'guide' subcommand and 'calculator' tool, and offer fleet flags before
  the sub-subcommand (valid per args.zig ordering)
- completions/README.md — fish/pwsh install docs, file/shell matrix,
  the PowerShell @file quoting caveat
- scripts/smoke.sh — new offline 'completions' group asserting every
  --help-json flag, subcommand, and enum value appears in all four
  completion files (drift guard)
- README.md + CONTRIBUTING.md — point to completions/

Verified: fish 3.7 and pwsh 7.4 exercised end-to-end via
complete --do-complete / TabExpansion2; bash tested via COMP_WORDS
simulation; zsh via zpty. 274/274 zig tests, 13/13 completions smoke
checks, no regressions in help/flags/fleet-flags groups.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…able install.sh

Adopters shouldn't need a Zig toolchain to try tau. Push a v* tag and the
release workflow cross-compiles all four targets (linux/macos x86_64+aarch64)
from a single Linux runner, publishes tau-<os>-<arch>.tar.gz assets plus
SHA256SUMS.txt, and creates the GitHub release via gh CLI (no third-party
actions beyond checkout/artifact upload). A guard step fails the run when the
tag doesn't match src/version.zig; suffixed tags publish as pre-releases.
workflow_dispatch runs build-only for validation.

install.sh (POSIX sh) detects OS/arch, resolves the latest or --version-pinned
asset URL, verifies the checksum when sha256sum/shasum is available, and
installs to ~/.local/bin. --dry-run and TAU_OS/TAU_ARCH overrides make the
platform mapping testable offline, exercised by a new 27-check 'release' smoke
group that also drift-guards the workflow file.

Verified: all 4 targets cross-compile with zig 0.16.0; 27/27 release smoke
checks green; README + CHANGELOG updated.
Adds a smoke job to release.yml that runs after build and gates release:
each dist tarball is staged behind a local HTTP stand-in for the GitHub
releases URL, installed end-to-end through install.sh on the matching
runner (ubuntu-latest, ubuntu-24.04-arm, macos-15-intel, macos-latest),
and verified with tau --version/--help. Covers all four shipped assets.

install.sh gains a documented TAU_BASE_URL env override so the installer
can be pointed at any base URL exposing the releases layout — this is
what the smoke job (and local smoke tests via file://) use.

Fixes a latent checksum-verification bug the e2e check surfaced: the
release job generated SHA256SUMS.txt with ./-prefixed filenames while
install.sh greps " <asset>$" (space before the name), so no line ever
matched and every real install would abort. The workflow now emits clean
filenames and the installer grep tolerates an optional ./ prefix.

Verified: release smoke group 49/49 green, including a full offline
file:// install (download → checksum → extract → install → version/help)
and a checksum-mismatch rejection. release.yml parses as valid YAML.
CI adopters can now install tau with a single `uses: javimosch/tau@<ref>`
line. The composite action shells out to the repo's own install.sh via
$GITHUB_ACTION_PATH (so installer version matches the action ref),
exposes version/install-dir/base-url inputs mapped to the installer's
TAU_* env overrides, appends the install dir to $GITHUB_PATH, and
surfaces tau-path + version step outputs.

release.yml gains a smoke-action job that stages the linux-x86_64
tarball behind a local HTTP stand-in and runs `uses: ./` end-to-end
(download → checksum → install → outputs/PATH checks), gating the
release publish on the action working as advertised.

scripts/smoke.sh's release group pins the action surface: composite
type, install.sh invocation, all three inputs and their env mapping,
GITHUB_PATH/GITHUB_OUTPUT wiring, outputs, and a conditional YAML parse.

Verified: release smoke group 69/69; action run block exercised
offline against a staged file:// release tree (install + outputs
correct); action.yml and release.yml parse as YAML.

Stacked on mago/task-147 (install.sh + release smoke job, PRs
#146/#150) — this PR includes those commits until they merge.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Validates the whole setup chain in one JSON report on stdout:
config file parse (missing vs invalid vs unknown provider),
provider/model/endpoint resolution, API key provenance (source only —
never the key), curl on PATH, endpoint reachability (~5s probe), and an
optional --deep authenticated probe (max_tokens=1). --offline marks
network checks "skip". Exit 0 when no check fails, 1 otherwise; unknown
args exit 80.

An unknown provider is carried into the report as a failed
provider_resolution check instead of exiting 80, so doctor stays useful
exactly when setup is broken.

Supporting changes: Config gains doctor_offline/doctor_deep/
doctor_bad_provider plus config_path (set by configfile.load when a file
is read); resolveApiKeyInfo exposes key provenance (resolveApiKey now
delegates to it — same precedence contract, existing tests unchanged).

9 new unit tests in doctor.zig, 4 in args.zig (288/288 green); new
'doctor' smoke group (33 offline checks); help_text Diagnostics section;
AGENTS.md cheatsheet + file map; README/docs/configuration.md/CHANGELOG
pointers.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…um chain in release.yml

install.sh previously warned-and-continued when SHA256SUMS.txt was
unreachable or no sha256sum/shasum existed — a silent trust path for
tampered or corrupted binaries. Verification is now mandatory: the
install aborts when the manifest can't be fetched, the asset isn't
listed in it, no checksum tool is available, or the digest mismatches.
TAU_SKIP_CHECKSUM=1 is a documented opt-out for air-gapped mirrors.

release.yml closes the loop end-to-end: the build job emits a
per-asset .tar.gz.sha256 alongside each tarball, both smoke jobs verify
it against the downloaded artifact before staging, and the release job
re-checks SHA256SUMS.txt before publishing so a malformed manifest can
never ship (it would brick every install under fail-closed semantics).

Smoke release group grows 49 -> 83 checks: drift guards for the new
workflow steps plus e2e cases for manifest-miss, unlisted-asset, and
the skip-checksum bypass. README + CHANGELOG updated.
…codes

Adds the coverage issue #165 asks for on top of the `tau doctor`
implementation (PR #160 / mago/task-127):

- zig: checkConfigFile now exercised through real file I/O via a throwaway
  HOME (missing file, valid file, invalid JSON, unreadable/dir-as-file),
  checkEndpoint and checkAuthProbe failure paths pinned against a refused
  local port, keyHint/providerListHint contents, ok:true report shape with
  warnings, and curlHttpCode's spawn-failure null.
- smoke: doctor group 33 -> 63 checks — curl missing from PATH cascade
  (--deep so both network checks report the curl gate), warn-doesn't-fail
  exit 0, positional-arg exit 80 with stderr message, and a local python
  stub server driving endpoint_reachable (401 counts as reachable) plus
  --deep auth_probe for HTTP 200/401/429 including a key-material leak
  assertion.
- CHANGELOG: Tests section entry for #165.

Verified: zig build test 297/297, smoke doctor group 63/63.
--debug already streamed [DEBUG] lines to stderr; it now also mirrors them
to ~/.config/tau/debug/<UTC-timestamp>-<suffix>.log (TAU_DEBUG_LOG overrides)
so users can attach the file to bug reports. New src/debuglog.zig owns the
log: it masks the resolved API key plus every other configured/env key,
Bearer tokens, api_key-style JSON fields, and *_KEY=/_TOKEN= env-style
assignments (***REDACTED***); stderr keeps the raw output. The file is
created owner-only (0600) with a provenance header (version, provider,
model, endpoint, key_resolved) and each entry is capped at 8 KiB.

Error envelopes gain an optional "debug_log" field linking the active log
(formatErrorJson + the /goal-session literal emitter — the only literal on
the .run path where a log can exist), and startup emits {"debug_log":...}
on stderr. Failure to create the file degrades to a warn envelope; the run
continues on stderr-only debug output.

Also makes the help text's "perf stats" claim real: a [DEBUG] perf line
(tokens_out, elapsed_ms, exit_code) is emitted once per run.

Stacked on mago/task-130 (PR #131) — both touch the err envelope format;
stacking keeps the docs field consistent.

Closes #167

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ediation hints

Every {"err":...} envelope now carries code, type, message, recoverable,
a remediation hint, and the docs link — emitted from one shared formatter
(src/errors.zig) instead of six hand-rolled literals across main/agent/acp/
fleet/helpers.

- errors.zig: canonical Spec registry (codes 1/80/82/105/106/110/111),
  specFor() lookup, format()/printErr()/printOut() emitters that splice in
  debuglog's debug_log field, and an OOM fallback that keeps the shape.
- Codes and wire `type` tags are documented as stable (never renumbered or
  renamed) in a new "Stable error catalog" table in docs/troubleshooting.md.
- Auth failures keep their dynamic hint (names the provider env var); all
  other codes fall back to the catalog hint.
- Snapshot tests pin every catalog entry byte-for-byte; sync tests assert
  the catalog covers docs/troubleshooting.md and --help-json exit_codes;
  the literal-emitter source scan now also requires the hint field.
- Smoke: envelope assertions now check recoverable/hint/docs (82-path gated
  on has_key since fleet's auth check fires first in keyless envs).

Stacked on mago/task-167 (PR #168) — it owns the current envelope shape
(docs + debug_log fields). docs/integration.md (PR #145) already declares
new fields non-breaking; its "minimal {code,message} envelope" line goes
stale once both land and should be updated there.

Closes #171

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…stable tags

Adds a `homebrew` job to the release workflow that regenerates
Formula/tau.rb from the published SHA256SUMS.txt manifest (new
scripts/generate-homebrew-formula.sh, runnable standalone) and pushes it
to javimosch/homebrew-tap via the contents API, so macOS/Linux users can
`brew install javimosch/tap/tau` and track releases with `brew upgrade`.

Pre-release tags (v*-*) are skipped so the tap only tracks stable
versions, and the publish step warns + skips when the HOMEBREW_TAP_TOKEN
secret is absent rather than failing the release.

Docs: README gets a Homebrew install option, release notes mention brew,
CHANGELOG + AGENTS.md file map updated. Smoke release group grows to 118
checks covering the generator (render, v-strip, fail-closed paths) and
workflow drift guards.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- New docs/providers.md: per-provider credential setup (env vars, config
  keys map, endpoints, default models), the 6-level key-resolution chain,
  and exit-106 auth troubleshooting (missing key, silent keyless exit,
  rejected key, shadowed TAU_API_KEY, TAU_ENDPOINT auth mismatches).
- errors.zig: Spec gains a `docs` field (default: troubleshooting URL) and
  Opts a docs override; the auth_failed spec points envelopes at
  providers.md so every code-106 error links the credential guide.
- version.zig: providers_doc_url constant next to troubleshooting_doc_url.
- Cross-links: troubleshooting.md (example envelope, catalog docs column,
  auth section), README quickstart + troubleshooting table,
  configuration.md Providers section, `tau guide` see_also.
- Pins: golden snapshot updated; new tests pin the 106 -> providers.md
  link and that providers.md documents every provider's name, env keys,
  endpoint, and default model; smoke dry-run group gains a keyless
  fleet-run envelope check.
…Windows smoke

Adds the Windows install path end-to-end:

- release.yml: new x86_64-windows-gnu build target shipping
  tau-windows-x86_64.zip (Windows convention); SHA256SUMS.txt now covers
  .zip assets; new smoke-windows job installs the zip through install.ps1
  on a native windows-latest runner (file:// TAU_BASE_URL staging) and
  verifies tau.exe --version/--help before the release can publish.
- install.ps1: mirrors the install.sh contract — -Version/-Dir/-DryRun/
  -Help (both -name and --name forms, manual $args parse since pwsh does
  not bind --switches), TAU_VERSION/TAU_INSTALL_DIR/TAU_ARCH/TAU_BASE_URL/
  TAU_SKIP_CHECKSUM overrides, fail-closed Get-FileHash SHA256
  verification, Expand-Archive install to %LOCALAPPDATA%\Programs\tau with
  a user-PATH note. throw/return instead of exit so irm|iex installs
  don't kill the caller's shell; file:// URLs resolve via System.Uri so
  the staging trick works on every pwsh host.
- scripts/generate-winget-manifest.sh: renders the three-file
  microsoft/winget-pkgs manifest (version/installer/defaultLocale) from
  the published checksums; the winget job validates it, uploads it as a
  workflow artifact, and auto-submits via wingetcreate when
  WINGET_PKGS_TOKEN is configured (warn-and-skip otherwise).
- scripts/generate-scoop-manifest.sh: renders bucket/tau.json for
  javimosch/scoop-bucket; the scoop job publishes via gh api contents PUT
  behind SCOOP_BUCKET_TOKEN, warn-and-skip while the token is unset or the
  bucket repo does not exist.
- smoke.sh release group: +install.ps1 surface checks (pwsh-gated),
  workflow drift guards for the new jobs, generator contract tests, and a
  file:// install.ps1 e2e exercising the full fail-closed checksum ladder.
- README quickstart + release notes + CHANGELOG document the Windows path.

Stacked on mago/task-175 (the release chain this extends).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…tor)

`tau setup` runs the full first-run flow as one NDJSON transcript:
scaffolds ~/.config/tau/config.json (init semantics — keeps an existing
file instead of failing), installs shell completions for the $SHELL-
detected shell (--shell bash|zsh|fish|pwsh|all overrides), and embeds the
`tau doctor` health report, ending with a {"step":"setup","ok":...}
verdict. Exit 0 when every step is ok/skipped, 1 on any failure.
Idempotent: rc/profile edits carry a "# tau completions" marker so
re-runs don't duplicate lines.

- build.zig generates a `completions` module embedding completions/*
  via addWriteFiles, so `tau setup` installs scripts without a checkout
- doctor.zig: split collectReport() out of run() for reuse; fix
  config_file check to strip JSONC comments (init scaffolds parse ok now)
- --provider/--model/--api-key/--offline/--deep pass through to the
  doctor step; unknown providers ride in as a failed check (doctor
  convention) while init falls back to detection
- completions: add init/doctor/setup coverage to bash, zsh, fish, pwsh
- 6 new unit tests + 41-check smoke group; docs: README, AGENTS.md,
  CHANGELOG, completions/README, docs/configuration.md

Closes #181
…ind confirmation

`tau doctor --fix` plans and applies remediations for fixable check
outcomes: scaffold a missing config (init semantics, creates ~/.config/tau),
move an unparseable config to config.json.bak and re-scaffold, tighten POSIX
permission bits (0700 dir / 0600 file — the file can hold API keys), and
install/refresh shell completions. Every fix requires confirmation: --yes
(also -y, implies --fix) applies all non-interactively, a TTY stdin gets a
per-fix y/N prompt on stderr, and anything else is reported as skipped. The
emitted report gains a "fixes" array ({id,check,status,detail}) and reflects
the post-fix state when anything was applied; the exit code follows the
final report.

Two new always-on diagnostics back the remediations: config_permissions
(warn on group/other access, POSIX-only) and shell_completions (warn on
missing/outdated scripts or absent rc hooks for the detected/--shell shell).

Also exports the completion install helpers in setup.zig (installCompletion,
completionState, resolveShells, rc/script accessors) so doctor and setup
share one code path, and completes the new flags in all four shell scripts.

Verified: 324/324 zig tests (8 new covering planning, confirmation, every
fix kind, and round-trips), 84/84 doctor smoke checks including the new
--fix sections (skip-without-confirm, apply-with---yes, .bak restore),
setup/init/completions groups green, x86_64-windows-gnu cross-compile.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…trix

Adds .github/workflows/ci.yml which runs `zig build test` and both offline
smoke suites on a native runner for each released platform (linux
x86_64/aarch64, macos x86_64/aarch64, windows x86_64) on PRs, master pushes,
and v* tags — so platform regressions fail before a release is cut.
`workflow_call` is exposed so the release workflow can gate on the matrix.

Portability fixes to make that matrix green:

- args.zig: parse() now takes a plain argv slice instead of
  std.process.Args, whose POSIX-only `.vector` field broke `zig build test`
  compilation for windows-gnu. main.zig materializes the platform argv
  (WTF-16 on Windows, UTF-8 on POSIX) into a UTF-8 slice itself.
- smoke.sh: the acp group now pins both documented daemon contracts —
  exit 0 + running:false on POSIX, exit 111 err envelope on Windows —
  instead of failing on the Windows leg.
- smoke.sh + smoke-features.sh: pass --api-key fake to checks exercising
  post-auth validation (fleet run without --goal, /goal without --session),
  so they are deterministic in keyless CI environments — key resolution
  precedes those checks and would exit 106 first.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
--json replaces the single-object report with line-delimited events matching
the documented stream contract: {"check":{...},"done":false} per check as it
finishes, {"fix":{...},"done":false} per remediation under --fix, and a
terminal {"ok","version","summary","fixes"?,"done":true} verdict. A check
re-diagnosed after an applied fix re-emits with its post-fix status. Exit
codes, the default report shape, and `tau setup`'s embedded report are
unchanged.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Bakes build provenance into the binary via a build.zig options module:
git commit (short hash, "-dirty" on uncommitted changes, "unknown" for
tarball builds), UTC build date (honors SOURCE_DATE_EPOCH so release
binaries stay byte-reproducible), arch-os-abi target triple, and the zig
compiler version — all exposed from version.zig's new `build` struct.

Surfaced in three places so a bug report can pin down the exact binary:
- `tau version --verbose` prints a JSON object (key:value lines under
  --mode text); bare `tau version` stays identical to -v/--version
- `tau doctor` report and --json terminal verdict carry a "build" object
- the --debug log header gains a `# build:` line via debuglog.writeHeader

Includes version-subcommand args tests, version.zig formatter tests,
doctor build-field pins, a 16-check `version` smoke group, completions
for all four shells, and AGENTS.md/CHANGELOG updates.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… bundle

Stacked on mago/task-204 (doctor --json/--fix chain + version.build +
debuglog).

`tau doctor --report` prints a Markdown bundle for pasting into a GitHub
issue: verdict line, build metadata table (version/commit/target/build
date/zig), the checks table, a Fixes table when --fix ran, effective
settings (provider/model/endpoint/context_window + key source — never the
key), and the config file verbatim in a fenced jsonc block. The config
dump is passed through debuglog.redactAlloc with every credential source
collected (resolved key, --api-key, config api_key + keys.*, TAU_API_KEY,
all provider env vars, builtins), so key material can never leak into the
report. Paths stay visible, matching the --debug log convention.

--report + --json is rejected at parse time with exit 80 (mutually
exclusive). Exit codes and the default JSON report are unchanged.

debuglog.zig: registerSecrets refactored onto a new pub collectSecrets so
the same source enumeration serves both the --debug log and --report.

Also: help_text + guide commands row, all four completion scripts +
completions README, troubleshooting.md diagnostic-toolkit row, unit tests
(formatReportMarkdown shape/redaction, appendMdCell escaping, arg parse +
mutex), and 19 new offline smoke checks in the doctor group.

368/368 zig tests, 113/113 doctor smoke checks green.
mikayassa and others added 8 commits October 16, 2026 01:33
… + credential sweep

--probe emits two rows per registry provider: probe_<name> (endpoint
reachability — any HTTP response counts) and probe_<name>_auth (key resolved
with the same precedence a run configured for that provider would use, then a
one-token authenticated POST when a key is found). Only the key's source
label is ever reported — never the key itself. The configured provider probes
its resolved endpoint/model (TAU_ENDPOINT/--model honored); auth rows skip
when the endpoint probe failed, when offline, when curl is missing, or when
no credential resolves. Rows stream as NDJSON check events under --json and
appear in the --report checks table. Without --probe a single skipped
provider_probes row advertises the sweep.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ng onboarding

Adds a "clients" step between completions and doctor in `tau setup`:

- `--client zed|vscode|jetbrains` writes a minimal settings file
  (agent_servers.tau / acp.agents.tau) with the absolute path of the
  running binary, so GUI editors that lack PATH lookups still resolve it.
- Existing settings files are never rewritten — they're JSONC and a
  parse-and-rewrite would destroy comments. If a "tau" entry is already
  present (checked via JSONC-aware parse) the entry reports
  already_installed; otherwise a paste-ready fragment lands in
  ~/.config/tau/snippets/<client>.jsonc and the entry reports "manual".
- `--client nvim` always reports manual: Neovim ACP plugins are Lua
  config, so setup writes ~/.config/tau/snippets/nvim.lua with both the
  avante.nvim and CodeCompanion blocks plus an editor_detected flag.
- Without --client the step is skipped and only reports which editor
  config dirs exist; detection never writes.
- Manual outcomes pass the setup verdict; only IO failures fail it.

Wired: args.zig enum validation (with "code" alias), Config.setup_client,
help text, guide row, all four completion scripts, AGENTS.md, README,
CHANGELOG, and a docs/acp.md shortcut note. 7 new unit tests cover
parsing, paths, JSONC-aware tau-key detection, and the three write
outcomes; 30 new offline smoke checks cover the flag end-to-end
(324/324 unit, 73/73 setup group).

Closes #219

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…check against a loopback stub

Adds a `selftest` check row that spawns the running tau binary with a
fully scrubbed environment — every provider env key, TAU_API_KEY,
TAU_ENDPOINT and TAU_DEBUG_LOG stripped, HOME pointed at a throwaway
dir so no real config can leak in — plus a stub TAU_API_KEY and a
TAU_ENDPOINT aimed at an in-process loopback HTTP stub (std.Io.net,
no python/sh dependency, Windows-safe). The child must return the
canned marker end-to-end (one-shot, --no-tools --no-stream) for the
row to report ok; exit code and truncated stderr/stdout excerpts are
surfaced on failure.

The stub serves up to 4 connections (the provider HTTP layer retries)
and is reaped by a done-flag + self-connect kick so a blocked accept()
never deadlocks the check.

Deliberately not gated on --offline: the stub is loopback-only, so
`tau doctor --offline --selftest` is a fully hermetic install check.
Skips only when curl is unavailable (the child shells out to curl).

Wires --selftest through args parsing, Config, help_text, the guide
table, all four shell completions, smoke coverage (env-scrub and
isolation assertions included), README/AGENTS.md/troubleshooting/
configuration docs, and CHANGELOG.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…attended onboarding

tau setup never prompts and never reads stdin — the new flags make that
contract explicit for devcontainers, dotfiles scripts, and CI:

- `--yes`/`-y` approves all write steps; `--non-interactive` declares the
  unattended run. Both are recorded in the verdict as
  "non_interactive":true/"yes":true so scripts can assert on them.
- Env equivalents honored at parse time with flag > env > detect
  precedence: TAU_YES, TAU_NONINTERACTIVE (truthy = 1/true/yes/on),
  TAU_SETUP_SHELL, TAU_SETUP_CLIENT (validated like the flags; invalid
  values exit 80).
- CI is deliberately not auto-detected — scripts opt in explicitly.
- Shared validSetupShell/validSetupClient validators + envTruthy helper;
  5 new parse tests + an unattended() unit test.
- Help text, embedded guide, all four completions, README, CHANGELOG,
  AGENTS.md, and docs/configuration.md env table updated.
- 30 new smoke checks in the setup group: flag/env-triggered mode, flag
  beats env, falsy env ignored, invalid env -> 80, stdin-closed run.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
# Conflicts:
#	README.md
#	completions/tau.ps1
#	src/config.zig
#	src/main.zig
# Conflicts:
#	CHANGELOG.md
#	scripts/smoke.sh
# Conflicts:
#	CHANGELOG.md
#	scripts/smoke.sh
#	src/args.zig
…or --selftest)

Adds a `first-run` job to the ci workflow that locks the documented
first-run path for every released platform (linux x86_64/aarch64, macos
x86_64/aarch64, windows x86_64):

- builds tau, stages it as a local release tree
  (srv/latest/download/<asset> + SHA256SUMS.txt — the exact layout the
  installers fetch), and installs it through the shipped installer —
  install.sh on POSIX legs, install.ps1 on the Windows leg — against a
  file:// TAU_BASE_URL so nothing touches the network;
- then drives scripts/e2e-first-run.{sh,ps1} against the INSTALLED binary
  in a scratch HOME with every credential source scrubbed:
  `tau setup --yes --offline` (unattended onboarding: config scaffolded,
  verdict ok/yes/non_interactive), `tau doctor --offline --selftest`
  fully credential-free (asserts the selftest check row reports ok — the
  feature's promise — while the keyless verdict honestly exits non-zero),
  and the same doctor run with a fake resolvable key (asserts exit 0).

The release workflow's install-smoke jobs now assert the same selftest
check row on the shipped artifacts, so published binaries are verified to
self-test too. A new `first-run` smoke group pins the drivers, the ci.yml
wiring, and runs the POSIX driver end-to-end against zig-out/bin/tau.

Branch carries the prerequisite chain merges: mago/task-222 (doctor
--selftest), mago/task-230 (setup --yes/--non-interactive), mago/task-182
(install.sh + install.ps1 + release.yml), mago/task-193 (ci matrix).

Verified: zig build test 392/392, full offline smoke 625/625, and the
complete install.sh file:// → e2e-first-run.sh chain run locally.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bdc0d888-41fe-49be-93cb-7fab1e3e97f9
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T23:38:30.256786Z 9552c8c PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9552c8c777

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/doctor.zig
return .{ .name = "endpoint_reachable", .status = .skip, .message = "skipped (curl unavailable)" };

const code = curlHttpCode(io, gpa, &.{
"curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use a Windows-compatible curl output sink

On native Windows, every non---offline doctor run reaches this command, but curl documents nul rather than /dev/null as the Windows output target (curl docs). The output-file open fails, curlHttpCode returns null, and a reachable endpoint is reported as failed; this also makes the doctor step in a normal tau setup fail. Select NUL by target platform (or discard captured output without a pathname) for this and the equivalent auth/probe calls.

AGENTS.md reference: AGENTS.md:L383-L387

Useful? React with 👍 / 👎.

Comment thread src/doctor.zig
@@ -0,0 +1,2221 @@
// `tau doctor` — offline-first setup diagnostics. Validates the whole setup

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Split doctor diagnostics into compliant modules

This newly added module is 2,221 lines, far beyond the repository's under-500-LOC requirement. Split the diagnostics checks, serialization, remediation, and self-test responsibilities into focused modules; retaining this monolith violates the documented project structure and makes future changes difficult to review and test.

AGENTS.md reference: AGENTS.md:L571-L571

Useful? React with 👍 / 👎.

Comment thread src/setup.zig
@@ -0,0 +1,1123 @@
// `tau setup` — one-shot onboarding: scaffold ~/.config/tau/config.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Split onboarding code into compliant modules

This newly added onboarding module is 1,123 lines, exceeding the repository's under-500-LOC requirement by more than double. Separate completion installation, editor-client configuration, and setup orchestration into focused modules before merging so this new subsystem follows the required project structure.

AGENTS.md reference: AGENTS.md:L571-L571

Useful? React with 👍 / 👎.

Comment thread src/debuglog.zig
@@ -0,0 +1,504 @@
//! Redacted diagnostic log file for bug reports (`--debug`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Split the debug logger below the file-size limit

The newly added debug logger is 504 lines, which exceeds the repository's strict under-500-LOC source-file limit. Move the masking implementation or the logging lifecycle into a focused module so this file complies with the documented limit.

AGENTS.md reference: AGENTS.md:L571-L571

Useful? React with 👍 / 👎.

Comment thread src/setup.zig
.zsh => try std.fmt.allocPrint(arena, "{s}/.zshrc", .{home}),
// pwsh profile location on Linux/macOS. Windows users can copy the
// same line into the profile shown by `pwsh -NoProfile -c '$PROFILE'`.
.pwsh => try std.fmt.allocPrint(arena, "{s}/.config/powershell/Microsoft.PowerShell_profile.ps1", .{home}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Write the PowerShell hook to the active Windows profile

On native Windows, tau setup --shell pwsh appends the source line to <HOME>/.config/powershell/..., which PowerShell does not load: the current-user pwsh profile is under $HOME\Documents\PowerShell (Microsoft documentation). Setup reports success, but newly opened Windows pwsh sessions never source tau.ps1; resolve the Windows profile path (and account for Windows PowerShell's Documents\WindowsPowerShell path) instead.

AGENTS.md reference: AGENTS.md:L281-L282

Useful? React with 👍 / 👎.

Comment thread src/setup.zig
pub fn clientSettingsPath(arena: std.mem.Allocator, home: []const u8, k: ClientKind) !?[]u8 {
return switch (k) {
.zed => try std.fmt.allocPrint(arena, "{s}/.config/zed/settings.json", .{home}),
.vscode => try std.fmt.allocPrint(arena, "{s}/.config/Code/User/settings.json", .{home}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Select VS Code settings paths by platform

tau setup --client vscode uses this Linux-only path on macOS and Windows, so it reports installation after creating a file VS Code never reads. VS Code's documented user settings paths are ~/Library/Application Support/Code/User/settings.json on macOS and %APPDATA%\Code\User\settings.json on Windows (VS Code documentation). Choose the settings directory by target platform so the ACP entry is actually discovered.

AGENTS.md reference: AGENTS.md:L300-L302

Useful? React with 👍 / 👎.

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.

Add clean-environment CI e2e that installs tau via install.sh/install.ps1 then runs tau setup --yes and tau doctor --selftest to lock the first-run path

2 participants