Releases: quant-risk/radiant-harness
Release list
v3.7.13: phase redirect --purge + great-grandchildren pid tree + fleet status --html
v3.7.13 — phase redirect --purge + great-grandchildren pid tree + fleet status --html
Three backlog items closed in one sprint.
Added
radiant phase redirect --purge=<ticket-id>— explicit cleanup of stale follow redirect without nuking state dir. Removes ONLYredirect.jsonfor the named ticket; spec, tasks, other redirects untouched. Exits 1 on missing file so CI / lints catch accidental purges.--list+--purgemutually exclusive.- Nested pid tree — great-grandchildren layer —
PidTreegainsGreatGrandchildrenPids+GreatGrandchildrenAlive+GreatGrandchildrenCount. New.pid.great-grandchildrensidecar.refreshChildTreeSidecarswrites all 3 sidecars (children + grandchildren + great-grandchildren) in a single pgrep pass. Crashed-evidence string inCoordinator.Status()includes great-grandchildren counts. radiant fleet status <run-id> --html— self-contained HTML report with visual nested pid tree (parent → child → grandchild → great-grandchild) in Unicode box-drawing glyphs inside<pre>. Color-coded by liveness (alive=green, dead=red, vacuous=muted).--html-out=<path>writes to disk. No external CSS / JS / CDN — renders offline in any browser or mail client. HTML escape enforced (XSS defense).
Tests
11 new tests pin the contract (4 CLI redirect purge, 6 fleet status HTML, 7 pidtree great-grandchildren). 32/32 packages pass. Backward compatible — all existing tests still green.
Process-learnings
pidAlive+ sentinel16777215(abovepid_maxon every reasonable host) is the test pattern for great-grandchildren alive-counting.TaskPidTreeearly-returns on empty children sidecar; great-grandchildren are not read when children is empty (matches reality: no children = no descendants possible).purgeFollowRedirectW(writer-injecting form) is the testable shape; cobra subcommand usespurgeFollowRedirect(hardcoded stdout/stderr) wrapper.FormatStatusHTMLis offline-safe by construction (no http(s):// URLs, no @import, no<script src=>).- HTML escape is mandatory — fleet goal / title / evidence may contain operator-supplied strings; the
&<>"'replacer handles XSS via injected metadata.
v3.7.12 — phase redirect --list + phase follow + grandchildren pid tree
"## v3.7.12 — phase redirect --list + phase follow + grandchildren pid tree\n\nThree backlog items closed in one sprint:\n\n### A. radiant phase redirect --list\n\nNew flag on the existing redirect subcommand. Scans .radiant-harness/state/possess-*/ for redirect.json files and emits either a formatted table (default — columns: OLD, NEXT, CREATED_AT, PATH) or NDJSON (--list --json, one object per line). Skips corrupt files silently (defense against operator-edited files with bad JSON) and skips non-possess-* directories.\n\n\n$ radiant phase redirect --list\nOLD NEXT CREATED_AT PATH\n--------------------------------------------------------------------------------\nv3711-A-real v3711-B-real 2026-06-30T15:33Z .radiant-harness/state/possess-v3711-A-real/redirect.json\n2 redirect(s) in /tmp/watch-test/.radiant-harness/state\n\n\n### B. radiant phase follow <anchor-ticket-id>\n\nAlias for radiant phase watch --follow=<ticket> with the anchor as a positional argument (easier to type + tab-complete in a shell). Reuses the same flag set as watchCmd (--interval, --max-poll, --json, --on-change-exit). Semantics byte-identical to --follow=<anchor> on the watch command.\n\n\n$ radiant phase follow 04b82c5a2f19ab36 --interval=5s\n$ radiant phase follow 04b82c5a2f19ab36 --on-change-exit --max-poll=30s\n$ radiant phase follow 04b82c5a2f19ab36 --json | jq -c .status\n\n\n### C. Recursive pid tree — grandchildren\n\nPidTree struct now exposes GrandchildrenPids + GrandchildrenAlive + GrandchildrenCount in addition to the v3.7.10 Children* fields. New helpers readGrandchildrenPids + writeGrandchildrenPids provide the on-disk format (newline-separated integers at .radiant-harness/fleet/pids/agent-<...>.pid.grandchildren). refreshChildAndGrandchildrenSidecars writes both sidecars on each refresh. Coordinator.Status() enriches the crashed-evidence string:\n\n\nagent pid 12345 not alive; 2 children recorded, 1 still alive; 5 grandchildren recorded, 3 still alive\n\n\n### Tests\n\n13 new tests pin the contract (100% PASS):\n- 6 CLI tests in cmd/radiant/v3_7_12_cli_test.go: --list empty/with-entries/JSON/corrupt-skip/non-possess-skip, follow help + state-read\n- 7 pidtree tests in internal/fleet/pidtree_grandchildren_test.go: GC all-alive / GC dead / no GC sidecar / write-read roundtrip / path layout / refresh writes both / grandchild found\n\ngo test ./... PASS (32/32 packages)\n\n### Backlog (v3.7.13)\n\n- Real CI host reproducing fleet cross-process need (still gated)\n- Per-task nested-pid-tree dashboard HTML report\n- radiant phase redirect --purge=<ticket> for explicit cleanup\n- Recursive fleet pid tree (great-grandchildren)\n"
v3.7.11 — --on-change-exit + --follow + docs/HOSTS.md
"## v3.7.11 — --on-change-exit, --follow, docs/HOSTS.md\n\nThree backlog items closed — observability + offline docs for the async opt-in matrix shipped in v3.7.10.\n\n### A. radiant phase watch --on-change-exit\n\nExit 0 immediately after the FIRST change observed AFTER the initial snapshot. Useful for "wait until anything changes" notifications without a full watch. Combine with --max-poll to bound the wait window (--on-change-exit --max-poll=30s).\n\n### B. radiant phase watch --follow=<anchor> + radiant phase redirect <old> <new>\n\n--follow=<anchor> tracks the anchor ticket's state initially. When a redirect.json appears at .radiant-harness/state/possess-<anchor>/redirect.json with a {next_ticket: \"...\"} payload, the watch transparently switches to the new ticket's state.json.\n\nradiant phase redirect <old-ticket> <new-ticket> is the new subcommand that writes the redirect.json (atomic write, RFC3339 created_at timestamp for forensics).\n\nUse case: resume re-dispatches with a NEW ticket id; the operator wants to keep watching without manually updating the CLI invocation.\n\n### C. docs/HOSTS.md (new)\n\nOffline-readable per-host opt-in matrix for all 13 Light-mode hosts + the "no agent detected" case. Documents how/when/when-not-to opt in, how to verify, and governance rules for flipping verdicts. Companion to radiant doctor --async-host (which already shipped in v3.7.10).\n\n### Tests\n\n9 new tests pin the contract (100% PASS):\n- on-change-exit transition/max-poll semantics (exits 0/1)\n- redirect.json file format (write/read round-trip, missing/corrupt handling, RFC3339 payload shape)\n- follow redirect detection mid-watch\n\ngo test ./... PASS (32/32 packages)\n\n### Backlog (v3.7.12)\n\n- Real CI host reproducing fleet cross-process need (still gated)\n- radiant phase redirect --list\n- radiant phase follow alias\n- Recursive fleet pid tree (grandchildren)\n"
v3.7.10 — --watch + nested pid tree + async-host opt-in matrix
"## v3.7.10 — --watch, nested pid tree, async-host opt-in matrix\n\nThree backlog items closed in one sprint, all observability/lifecycle:\n\n### A. radiant phase watch <task-id> CLI\n\nNew CLI namespace under radiant phase. Polls the persisted possessState and re-emits the phase summary only when (status, current_phase, subprocess_alive, subprocess_pid) changes. Exits 0 when the run reaches a terminal state (done / cancelled / error / crashed); exits 1 after --max-poll elapses; exits 130 on SIGINT/SIGTERM (clean Ctrl-C handling). --json emits one structured object per change in NDJSON format.\n\nCompanion to MCP radiant_phase_status for hosts that want streaming without round-tripping through the MCP transport (which does not stream).\n\n### B. Per-task nested pid tree\n\nTaskLive.tree field exposes parent_alive + children_pids + children_alive + child_count so a host can distinguish "parent died cleanly" from "parent died; N helpers orphaned". Dispatcher.spawnAgent spawns a refreshChildPidsLoop goroutine that pgrep -P's the parent every 5s and writes the children sidecar at .radiant-harness/fleet/pids/agent-<...>.pid.children.\n\n### C. Async-subprocess opt-in matrix + diagnostic\n\nTwo CLI flags on radiant mcp serve:\n- --async-subprocess (loop async subprocess)\n- --fleet-async-subprocess (fleet async subprocess)\n\nPrecedence: CLI flag > env var > default off. radiant doctor --async-host (v3.7.10+) scores all 13 known hosts — only Hermes is currently flagged RecommendAsync=true (TUI gates tool-call completion on subprocess exit); the rest default to inline. Exits non-zero when a recommendation is unmade so CI/lint can catch regressions.\n\n### Tests\n\n22 new tests pin the contract (100% PASS):\n- internal/fleet/pidtree_test.go (13): pid sidecar roundtrip, refresh loop lifecycle, parent/children liveness combinations\n- cmd/radiant/v3_7_10_test.go (9): phase watch terminal/max-poll/JSON/no-reemit semantics, status CLI shape, doctor exit-code contract, envBool parsing\n\ngo test ./... PASS (32/32 packages)\n\n### Backlog (v3.7.11)\n\n- --watch --on-change-exit flag — exit on FIRST change after initial snapshot\n- --follow=<ticket> — follow another ticket after a re-dispatch\n- Per-host opt-in matrix in docs/HOSTS.md for offline reading\n- Real CI host reproducing fleet cross-process need (still gated)"
v3.7.9 — Fleet async primitives (parity with loop)
Fleet async primitives — parity with loop (A+B+C)
Three layers combined in one release:
A. MCP wrappers
radiant_fleet_status(run_id, workdir?)— returns structuredFleetStatusJSON with task counts, per-task liveness, dispatcher liveness, conflict list, raw JSON dump, and automatic next-step hintradiant_fleet_resume(run_id, workdir?, model?, auto_route?)— wrapsDispatcher.ResumeAllso hosts can drive the fleet from the MCP wire (no shell-out)
B. Liveness probe
Coordinator.WithLivenessDir(workdir)— opt-inFleetStatusgainsDispatcherAlive+DispatcherPid+TaskLivenessmapTaskCrashedlifecycle: tasks the store considersassignedbut whose pid file points at a dead process escalate toTaskCrashed(parity with v3.7.8 loopphaseStatusSummarycrashed branch)Store.CrashTask(taskID, evidence)persists the crashed state cross-process
C. Subprocess gate
DispatchConfig.AsyncSubprocess+DispatchConfig.Workdir- When enabled,
RunAllforksradiant fleet-async-runner <run-id>and returns immediately (parity with v3.7.7 loop async-runner) - Inline remains the default; opt-in via
RADIANT_FLEET_ASYNC_SUBPROCESS=1
Pid files (always-on)
- Per-task:
.radiant-harness/fleet/pids/agent-<runID>-<taskID>.pid— written byspawnAgentbeforecmd.Start, removed viadeferaftercmd.Wait - Per-dispatcher:
.radiant-harness/fleet/pids/dispatcher-<runID>.pid— written by the fleet-async-runner on boot sanitizePidComponentdefends against path traversal in run/task IDs
Tests
- 22 new tests pin the contract (10 pidfile + 5 coordinator + 7 MCP)
go test ./...PASS (32/32 packages, 0 FAIL)make audit-docsPASS (46 refs / 57 cmds)make audit-skillsPASS (6 hint map / 69 bundled skills)
Backlog (v3.7.10)
- Real host opt-in for
RADIANT_FLEET_ASYNC_SUBPROCESS=1(gated by reproduction) --watchflag forradiant_phase_status- Per-task nested pid tracking (recursive liveness)
v3.7.8 — Async gate pid/liveness probe
[3.7.8] — 2026-06-30 — Async gate pid/liveness probe
radiant_phase_status now cross-references the persisted
subprocess pid against the running OS process list, so a host
agent can tell the difference between "the phase is still
running" and "the subprocess crashed without writing an error".
Changed
phaseStatusSummary(returned inradiant_phase_statusMCP
response) gains two additive fields populated from
.radiant-harness/pids/<ticket>.pid:subprocess_alive(bool) —kill -0on the recorded pid.subprocess_pid(int) — the recorded pid itself.
- Status escalation: when the recorded phase is
in_progress
butsubprocess_alive=false, status moves fromin_progress
tocrashedand the next-step line tells the host to re-call
mcp__radiant__run_gate(phase=...)to resume from where the
crash happened. - Next-step annotation when alive:
in_progressruns that have
a live subprocess now showsubprocess pid=N alive — last polled at <RFC3339>so the operator can confirm the child is
actually running. - Format helper (
content[1].text) gains asubprocess:line
when a pid is recorded (pid=N alive/pid=N dead). - No wire-format change for existing callers: the new fields are
omitempty, so older consumers that JSON-parse
content[0].textor the existing summary fields keep working
unchanged.
Why this matters
The subprocess path shipped in v3.7.7 (opt-in via
RADIANT_ASYNC_SUBPROCESS=1) spawns a child radiant async-runner per phase and tracks it via a pid file. Without a
liveness check, a crashed child looks identical to a slow one
— the host sees in_progress indefinitely and never knows to
re-call the gate. v3.7.8 closes that gap by reading the pid
file and doing kill -0 on every phase_status call.
Verified
cmd_mcp_possess_test.go::TestPhaseStatusSummary_*7/7 PASS:DoneRunShape,MidRunShape,ErrorShape,
CancelledShape(existing, v3.7.6) — unchanged behaviour.SubprocessAlive(NEW) — pid file pointing at the test
process; SubprocessAlive=true, Status staysin_progress,
NextStep mentions the live pid.SubprocessCrashed(NEW) — pid file pointing at a stale
pid (16777215, abovepid_maxon Linux/macOS);
SubprocessAlive=false, Status escalates tocrashed,
NextStep instructs re-callrun_gate.NoPidFile(NEW) — no pid file written; SubprocessAlive=
false, SubprocessPid=0, Status staysin_progress(no
false-positivecrashedon the default inline path).
go test ./cmd/radiant ./internal/...PASS../scripts/run.shPASS, 8/8 + 2 SKIP doctor.
Migration
No migration required. subprocess_alive and subprocess_pid
are additive on the summary JSON. Hosts that don't care about
subprocess liveness keep working unchanged.
Install
```bash
curl -fsSL https://raw.githubusercontent.com/quant-risk/radiant-harness/main/install.sh | bash
```
Or pin v3.7.8:
```bash
curl -fsSL https://raw.githubusercontent.com/quant-risk/radiant-harness/main/install.sh | RADIANT_VERSION=3.7.8 bash
```
Cross-platform binaries
| OS | Arch | File |
|---|---|---|
| Linux | amd64 | radiant-linux-amd64 |
| Linux | arm64 | radiant-linux-arm64 |
| macOS | amd64 | radiant-darwin-amd64 |
| macOS | arm64 | radiant-darwin-arm64 |
| Windows | amd64 | radiant-windows-amd64.exe |
| Windows | arm64 | radiant-windows-arm64.exe |
Verify with `shasum -a 256 -c SHA256SUMS` against `dist/SHA256SUMS`.
v3.7.7 — Subprocess-backed async gate primitives
[3.7.7] — 2026-06-30 — Subprocess-backed async gate primitives
The async gate primitives (radiant_possess_async +
radiant_run_gate) ship two execution paths in v3.7.7. The inline
path is unchanged; the new subprocess path is opt-in via
RADIANT_ASYNC_SUBPROCESS=1 and forks a radiant async-runner
child process so phases can outlive the parent's tool-call window.
Added
radiant async-runner --phase=<p> --ticket=<id> --workdir=<w> --task=<task>— the worker subcommand. Validates args, writes
.radiant-harness/pids/<ticket>.pidon entry, delegates to the
sameselfDriven<Phase>helpers the inline path uses, removes
the pid file on exit. Hidden from--help; gated by
RADIANT_INTERNAL=1so a fresh shell can't trigger it.subprocessAsyncGate+subprocessPossessAsync— implementations
ofinternal/possess.AsyncGateandinternal/possess.PossessAsync
that forkradiant async-runnerviaos/exec. Selected via
RADIANT_ASYNC_SUBPROCESS=1; the inline path is the default.RADIANT_BINenv var honored byasyncRunnerSelfPath— tests
point the subprocess at a one-time-built binary (rather than
the test binary, which would deadlock).selectedPossessAsync()+selectedAsyncGate()helpers in
cmd_mcp_possess_async.go— single source of truth for the
inline-vs-subprocess selection; bothmcpPossessAsyncand
mcpRunGateroute through them.
Why subprocess mode is opt-in, not the default
The inline path closes the synchronous-host deadlock today
(Hermes TUI) because the self-driven phases complete in
<500 ms before the tool call returns. A subprocess fork adds
~50 ms of overhead and the management machinery (pid files,
kill -0 liveness, crash recovery) is only worth paying for
when a real host need reproduces. Per
docs/PROPOSAL-v3.7.2-async-primitives.md § v3.7.6 update,
subprocess mode is wired but not the default — turn it on
when a sampling-backed sync-host or fleet cross-process worktree
need reproduces. The inline path stays the default to keep
existing deployments unchanged.
Crash recovery
The subprocess path writes state.json after each phase
(identical to the inline path), so a crash mid-Execute is
recoverable: the next mcp__radiant__run_gate call observes
execute is already in_progress and resumes from there. The
pid file is best-effort cleanup on exit; a stale pid file
indicates "this ticket was running" for operator-side diagnosis
and is not relied on for correctness.
Verified
cmd_async_runner_test.go5/5 PASS:TestSubprocessAsync_DiscoveryRunsInChild— subprocess
gate Spawn lands state.json with discover=done.TestSubprocessAsync_FourPhasesEndToEnd— full 4-phase
possess_async through the subprocess path lands the
canonical scaffold (spec.md / tasks.md / scripts/run.sh).TestSubprocessAsync_PidFileLifecycle— write/read/remove
semantics of the pid file helpers.TestSubprocessAsync_CrashRecovery— planted in_progress
state.json resumes correctly on next run_gate call.TestSubprocessAsync_SpawnsChildProcess— direct
invocation ofradiant async-runneras a real subprocess.
go test ./cmd/radiant/...PASS../scripts/run.shPASS (8/8 + 2 SKIP doctor).- Build clean;
make audit-docsPASS.
Migration
No migration required. Subprocess mode is opt-in. Set
RADIANT_ASYNC_SUBPROCESS=1 in the radiant mcp serve
process to enable; unset (or omit) to keep the inline path.
Install
curl -fsSL https://raw.githubusercontent.com/quant-risk/radiant-harness/main/install.sh | bashOr pin v3.7.7 explicitly:
curl -fsSL https://raw.githubusercontent.com/quant-risk/radiant-harness/main/install.sh | RADIANT_VERSION=3.7.7 bashEnable subprocess mode (opt-in)
The inline path is unchanged. To turn on the subprocess-backed
gate primitives, set RADIANT_ASYNC_SUBPROCESS=1 in the
radiant mcp serve process.
Cross-platform binaries
| OS | Arch | File |
|---|---|---|
| Linux | amd64 | radiant-linux-amd64 |
| Linux | arm64 | radiant-linux-arm64 |
| macOS | amd64 | radiant-darwin-amd64 |
| macOS | arm64 | radiant-darwin-arm64 |
| Windows | amd64 | radiant-windows-amd64.exe |
| Windows | arm64 | radiant-windows-arm64.exe |
Verify with shasum -a 256 -c SHA256SUMS against dist/SHA256SUMS.
v3.7.6 — Consolidation: status UX, host matrix, doc/backlog cleanup
[3.7.6] — 2026-06-30 — Consolidation: status UX, host matrix, doc/backlog cleanup
v3.7.6 is a consolidation release. It ships the v3.7.3-v3.7.5 backlog
of doc/backlog cleanups (the three commits 913f7d3, cec74bc,
9fe13cf since v3.7.5), tightens the radiant_phase_status contract
so a host agent can resume a run without re-parsing state.json,
extends the cross-agent install matrix to include Google Gemini CLI
(13th host), and turns scripts/run.sh into a single-call validation
entrypoint that exercises the full install/test/audit matrix.
No sampling-loop behaviour changed. No MCP wire format changed. The
summary field on radiant_phase_status is additive — older callers
that JSON-parse content[0].text keep working unchanged.
Highlights
radiant_phase_statusreturns a structuredsummary. The
v3.7.6 response carriesresult.summarywithstatus,
current_phase,next_step,resume_command,pending_files,
pending_marker_count,last_gate, and a compact per-phase map.
The raw state.json dump is still present incontent[0].textfor
backwards compatibility. This closes the "host agent has to read
state.json itself to know whether to retry" gap that v3.7.2 left
open. Five new contract tests pin the four phases × {done,
in_progress, error, cancelled} matrix.- Google Gemini CLI added to the host matrix.
setup-mcp --agent=geminiwrites~/.gemini/settings.json(user) or
./.gemini/settings.json(project) with the standard
mcpServersJSON shape — same as Claude/Cursor. Detection
signal:GEMINI_CLI,GEMINI_PROJECT_ROOT,GEMINI_API_KEY.
Restart hint:relaunch gemini-cli. The 12-agent matrix in
scripts/host-agent-matrix.jsonis now 13;make test-agents
asserts all 13 sandbox installs. scripts/run.shis the canonical validation entrypoint.
v3.7.6 extends the previous 4-command script to cover the full
matrix:mcp self-test,doctor,doctor --mcp(both
informational when no host is wired),go test ./cmd/radiant ./internal/...,go test ./...,audit-docs,audit-skills,
audit-install,test-agents,test-dropin. Doctor is
surfaced as SKIP (not FAIL) in a fresh shell, so the script
is reliable from CI and from inside a host session.radiant_run_gate+radiant_possess_asyncdocumented.
AGENTS-FOR-TASKS.md § MCP tools now lists all 6 primitives
(was 4 —radiant_run_gateandradiant_possess_asyncwere
on the wire but missing from the docs table).- External MenuFlex case spec removed. Commit
913f7d3—
the user-case spec did not belong in the harness repo. The
removal is recorded for audit; nothing else in the repo
referenced the case. - Backlog consolidation. Commit
cec74bcconsolidates the
docs (docs/ROADMAP.md,docs/STATE.md,docs/PROPOSAL-v3.7.2-async-primitives.md)
and closes the placeholder spec/doc open work into explicit
status records. Commit9fe13cfdocuments the final validation
pass. - Async subprocess deferred. The v3.7.2 PROPOSAL called for a
real background subprocess forradiant_possess_async. The
current implementation runs phases synchronously in-process
(the offline self-driven path is already short — typical run is
<500 ms, so a subprocess adds little). v3.7.6 keeps the existing
inline path and adds a follow-up spec for the cases that would
justify a real subprocess (long-running Claude Code sampling
loops, cross-process worktree ops).
Changed
internal/hostdetect/hostdetect.go: addedAgentGeminiCLIconstant- signature (
GEMINI_CLI/GEMINI_PROJECT_ROOT/GEMINI_API_KEYenv,
gemini/gemini-cliparent binaries,SupportsSampling=true).
- signature (
cmd/radiant/cmd_doctor.go::mcpConfigPath+probeRadiantEntry:
addedgemini-clicase →~/.gemini/settings.jsonwith the
standardmcpServersJSON shape.cmd/radiant/cmd_setup_mcp.go::mcpConfigFor: addedgeminicase
using the existingmergeMCPJSONhelper (same JSON layout as
Claude/Cursor). Updated the unknown-agent error message.cmd/radiant/cmd_mcp_runtime.go::mcpPhaseStatus: returns
result.summary(structuredphaseStatusSummary+ a rich text
block incontent[1].text) alongside the raw state dump.scripts/host-agent-matrix.json: 13th agent row forgemini.scripts/run.sh: rewritten as a fail-collecting matrix; doctor
steps surfaced as SKIP, real gates (audit-install, test-agents,
test-dropin, fullgo test ./...) now exercised.AGENTS-FOR-TASKS.md: MCP tools table now lists all 6
primitives with parameters and a sync-host alternative
workflow. Agent list includesgemini.INSTALL.md+README.md: agent table updated to 13 entries
with the Gemini row + detect signal.
Added
cmd/radiant/cmd_mcp_runtime.go:phaseStatusSummary,
phaseMini,phaseGateSummarytypes +buildPhaseStatusSummaryformatPhaseStatusSummary+extractLastGatehelpers.
cmd/radiant/cmd_mcp_possess_test.go: 5 new tests pinning the
v3.7.6 contract (TestPhaseStatusSummary_DoneRunShape,
TestPhaseStatusSummary_MidRunShape,
TestPhaseStatusSummary_ErrorShape,
TestPhaseStatusSummary_CancelledShape,
TestMCPPhaseStatus_ReturnsSummaryField).internal/hostdetect/hostdetect_test.go::TestDetect_GeminiCLI.
Verified
./scripts/run.shPASS (8 PASS, 2 SKIP — doctor + doctor --mcp
in a host-less shell are correctly informational, not failures).make audit-installPASS (canonicalcurl | bashpath will
PASS once v3.7.6 is published — the SKIP in the validation
above was because the local tree was 3 commits ahead of v3.7.5).make test-agentsPASS (13/13 agents simulated install).make test-dropinPASS against v3.7.6.go test ./cmd/radiant ./internal/...PASS.go test ./...PASS (full module).radiant mcp self-testPASS, exposes 6 MCP tools.- Build clean (
go vet ./...clean,go build ./...clean).
Migration
No migration required. The summary field on
radiant_phase_status is additive; older callers that JSON-parse
content[0].text keep working unchanged. Hosts that key off
the raw state.json shape continue to see the same fields.
Hosts running v3.7.6 inside a Gemini CLI session: run
radiant setup-mcp --agent=gemini --global once, then relaunch gemini-cli for the MCP server entry to land.
Install
curl -fsSL https://raw.githubusercontent.com/quant-risk/radiant-harness/main/install.sh | bashOr pin v3.7.6 explicitly:
curl -fsSL https://raw.githubusercontent.com/quant-risk/radiant-harness/main/install.sh | RADIANT_VERSION=3.7.6 bashCross-platform binaries
| OS | Arch | File |
|---|---|---|
| Linux | amd64 | radiant-linux-amd64 |
| Linux | arm64 | radiant-linux-arm64 |
| macOS | amd64 | radiant-darwin-amd64 |
| macOS | arm64 | radiant-darwin-arm64 |
| Windows | amd64 | radiant-windows-amd64.exe |
| Windows | arm64 | radiant-windows-arm64.exe |
Verify with shasum -a 256 -c SHA256SUMS against dist/SHA256SUMS.
v3.7.5: documented drop-in self-driven E2E
- Publishes the current HEAD that includes the automated drop-in self-driven E2E test and documentation.\n- Keeps the v3.7.4 binary behavior: MCP radiant_possess returns Self-driven handoff on non-sampling hosts.\n- Includes cross-platform binaries and SHA256SUMS for install.sh latest-release installs.\n\nValidation before release:\n- make test-dropin\n- go test ./...\n- radiant mcp self-test
v3.7.4: drop-in self-driven Codex handoff
- Makes the public drop-in flow work for hosts without MCP sampling by returning a clear Self-driven handoff from radiant_possess.\n- Updates README/INSTALL/install.sh language so agents use mcp__radiant__possess and continue with native tools when sampling is unavailable.\n- Includes cross-platform binaries and SHA256SUMS for install.sh latest-release installs.\n\nValidation:\n- go test ./...\n- radiant mcp self-test\n- make audit-install