You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/project now runs the v2 planner and executor in one command.gophermind project <brief> (and /project <brief> in the TUI) runs the preflight, plans the brief and builds it. Unattended runs take Clarify recommendations and confirm the understanding by rule, recording confirmed_by: unattended and its hash. A failed preflight exits 6 with a numbered list of what a human must provide. --graded marks a single graded attempt. The project report carries a node-class table. scripts/build-dev-binary.sh builds the version-stamped dev binary.
Parallel leaves.executor.workers (1 to 8, default 1) and gophermind brief run --workers n run the leaves of one level at once, each in a private copy of the repository. Landing order is fixed by leaf id and the work branch stays a linear history. With 1 nothing changes.
A revise stage. When a leaf has used its whole model ladder, or answers CONTRACT_PROBLEM, the planner rewrites the node (description, contract text, tests, groups) and the build goes on from the rewrite, bounded by defaults.max_revisions. A changed signature or depends_on is flagged to a person, not rewritten. Rewrites and decisions are kept in _state/revisions/ and decisions/rv-*.md. The revise: section of gophermind.yaml (mode, model_tier, context, rewrite_tests, max_tokens, max_refusals) configures the rewrite.
gophermind brief plan and gophermind brief resume.gophermind brief plan <brief.md> and gophermind brief resume <run-id> take a brief through Load, Clarify, Contract, Decompose, Coverage, Approve and Test-writer (the run id is the brief's id), with --yes, --gate terminal|file, --fake <fixture-dir> (canned replies, no settings, network or model) and --allow-public; exit 3 means the run is waiting on a human, and plan refuses a run folder that already holds stage output and points at resume.
gophermind brief status, coverage, calls and report.gophermind brief status, coverage, calls and report [--json] <run-id> print each stage and what the run waits for (plus executor lines: status, waves done, leaves verified, blocked leaves), which nodes and tests cover each requirement, the model-call ledger (provider, model, tokens, duration, outcome; it records only sizes and SHA-256 hashes of prompts and replies, never their text), and a run's report.
gophermind.yaml is written with defaults on first use.<config dir>/gophermind.yaml is written with defaults the first time plan runs: providers (OpenAI-compatible, base_url, visibility, max_concurrent, api_key_secret naming a vault entry), models listing provider/model fallback chains per tier (strong, standard, any), privacy.mode (need_to_know lets public providers see single functions only, or private_only), rate_limits cooldowns and backoff, defaults and human.mode.
The executor: and toolchain: sections of gophermind.yaml. The executor: section (fix_attempts, repair_rounds, acceptance_repair_rounds, test_timeout_seconds, acceptance_timeout_seconds, stale_claim_seconds, output_cap_bytes, heartbeat_seconds, max_run_minutes (default 720), go_mod_cache, sandbox, proxy.listen, proxy.log, artifacts on or off and artifact_max_bytes for the scrubbed replies and check output saved under attempts/ and _state/replies/) and a toolchain: section whose PATH is the only toolchain setting a file holds (the executor adds HOME, GOCACHE, GOMODCACHE, GOPATH and TMPDIR itself; a build sees nothing from the parent environment); every number must be positive and an explicit zero retry count is rejected.
New keys under defaults:.clarify_max_calls (default 4), clarify_max_questions (default 30, 1 to 1000), enrich_batch_size (default 4, 1 to 32) and route_by_node_tier (default true); an explicit 0 is refused.
reasoning_effort per provider, with truncated and empty replies handled. A provider may set reasoning_effort (none, low, medium or high; the mini defaults to none) for thinking models that answer in a reasoning field; a truncated reply is retried once with a doubled token budget (capped at 16384) and an empty reply is a typed failure, both before failing over to the next model.
base_url_fallbacks, private-network checks and per-provider timeouts. A provider entry may list base_url_fallbacks, tried in order at preflight and at the start of a run when base_url does not answer; a private provider's base_url and fallbacks must be private-network hosts (loopback, RFC 1918, 100.64/10, link-local, IPv6 ULA, .local, .internal, .lan) and a public provider's must be public. Per-provider call_timeout_seconds overrides defaults.call_timeout and rate_limits.cooldown_after_timeout_seconds (default 60) cools down a chain entry whose call timed out; provider calls never follow redirects and ignore the proxy environment. The default mini entry gains the VPN fallback http://10.8.0.6:11434/v1 and a 1500 second call timeout.
Human gates. With --gate file (or human.mode: file) the planner writes QUESTIONS.md and APPROVAL.md into the run folder and exits 3 until they are filled in, --gate terminal asks in the terminal, and an approval is bound to the plan's hash, which covers function signatures, file paths and depends_on, so editing any of them after approval invalidates it.
Clarify in rounds, with a fact probe and a Confirm gate. Clarify runs in rounds after a fact probe of the target repo (go.mod, directory listing, exported Go names only, no commands run); every question carries a recommendation and accept takes it in the terminal or in QUESTIONS.md, then a Confirm gate asks the person to approve the planner's understanding (y/N in the terminal, or approve/reject: <reason> in UNDERSTANDING.md in file mode) before planning continues.
gophermind brief answer.gophermind brief answer <run-id> <question-id> <new answer...> changes a settled answer and restarts the planner from the stage that used it (the whole Contract stage, or one component's plan); run gophermind brief resume afterwards.
Coverage. Every top-level bullet under ## Constraints and ## Acceptance and every ### under ## Features is a requirement; gaps go back to the model for up to defaults.max_coverage_rounds, and if any remain the run stops with exit 1, lists them, and nothing is approved.
The contract stage works within small model windows. The outline is built in resumable passes (a shared pass, then batches of features), every contract prompt is sized to the smallest model in the chain (with a prompt_size event), a reply cut at the token limit is treated as a truncation and retried, dangling references and missing contract fields are repaired in bounded passes instead of ending the run, ids are normalised into one namespace where the first emission wins, and errors never quote more than a bounded id.
The contract stage plans an entry point. A brief that names a command to run or describes a CLI tool or server gets a cmd/<binary-name> package, every main package must declare func main(), a warning is emitted when either is still missing after a retry, and security.untrusted_inputs accepts the process-level sources os.Args, os.Stdin, os.Environ, os.Getenv and flag.Args.
The test writer is stricter and compile-checks what it accepts. The test writer refuses tests that import anything outside the standard library, the module and the contract packages, writes files crash-safely, never writes through a symlink, stops rather than overwrite a test file it did not write, and compile-checks each accepted test file against the contract stubs at plan time; compile refusals name only predeclared, standard library or contract-declared types and functions, and Go syntax errors report line and column.
The approval plan shows node groups and revision notices. The plan a person approves has a Node groups section (not-applicable counts per group, trust boundaries per component, nodes no requirement names, Enrich warnings, assumptions and open questions, and a note when Enrich has not run or facts were unavailable) and shows a revision notice when the planner rewrote nodes.
Acceptance root tests are held to a quality gate. The coverage stage flags tests that never exercise the built binary as acceptance_vacuous and asks the model to repair them, the planner refuses commands that cannot fail for the right reason (masked_failure, success_echo, placeholder, undefined_variable, curl_unasserted, jq_unasserted, print_only), a weak test left after the fill rounds stops the plan before approval, APPROVAL.md gains a Quality column, narrative acceptance bullets get a Go test acceptance/<id>_test.go run against a serve declaration kept in coverage.json, plain go test commands are judged with go test -json, and at Wave 0 each server-exercising root test must fail against the stubs or the run stops with acceptance_not_red.
Five new example briefs.gophermind-osx/examples/briefs gains 04-csvstat, 05-retryx, 06-hookrelay, 07-ledger-sync and 08-taskboard, between them exercising every frontmatter option; all validate with no scan warnings.
gophermind brief run builds an approved plan end to end.gophermind brief run <run-id> runs a preflight, Wave 0 (types, stubs, the deps step, a scan, the red check and one commit), a leaf loop with a fix-then-next-model ladder and failure classes, waves with integration checks that attribute a failure to the leaves that caused it, a bounded repair loop that reopens only the attributed leaves, and a final landing, printing one status line per event on stderr. With landing: commit the build works on the branch gm/<brief-id> with one exact-path commit per leaf and an empty final commit carrying the summary, then fast-forwards the base branch (if the base branch moved the run stops failed with landing_blocked and the work branch is left intact); diff_only writes changes.patch. GopherMind never pushes, forces, resets, rebases or stashes, and the executor requires the repository root.
gophermind brief run --check-env.gophermind brief run <run-id> --check-env runs the environment preflight with no model call (sandbox, go toolchain, git repository, clean tree, vault passphrase, declared secrets, provider reachability, approval, disk space), prints pass:/FAIL: per check and exits 6 when any check fails.
brief run exit codes follow the run status. 0 verified, 1 failed, 3 waiting on a human, 4 escalated, 5 interrupted, 7 for a harness fault (repository cannot be opened, sandbox refused, invalid settings, plan changed since the run started, leaf held by a live worker), recorded in the report as stop reason harness_fault so it can be told apart from a failed run.
report.json and the run summary. Every brief run writes report.json (schema_version 3; an older binary refuses a newer report) and prints a summary ending in Requirements covered: N of N and Acceptance passed: N of N; the report carries planner warnings, optional revised_leaves (node id, revisions, changed fields, tests_rewritten, flag, refusals) and rewritten_leaves with a Rewritten leaves: summary line, an optional parallel object (workers, parallel_levels, landing_conflicts) with a Parallel: line, an unfinished-leaf list when the run stops, and a report that would contain a credential is not written.
Bounded runs, interrupts and resume.executor.max_run_minutes bounds one invocation and SIGINT or SIGTERM end it the same way: claims are released and the run ends interrupted with exit 5. Running gophermind brief run <id> again resumes from the run state: finished leaves are not rebuilt, stale claims and a dead holder's lock (after a kill -9) are reclaimed instead of timing out, private trees are swept, a repair or an unfinished revision cut off by a kill is resumed without a model call, a leaf held by a live worker is refused, and a resume stops when the plan files changed, commits that are not its own sit on the work branch, the repository moved or the base branch moved.
The deps step goes through an allowlisted loopback proxy. The deps step reaches the network only through a loopback forward proxy with a host allowlist built from the brief's network entries, each provider base_url (pinned to its port) and the Go module hosts; the proxy writes proxy.log (no paths or bodies), a failure on a host marked critical or three failures in a row stops the run, and a cancelled tunnel is not counted as a failure.
An acceptance run against freshly built binaries. The plan's root tests (one per acceptance bullet, N taken from requirements.json, plain go commands accepted) run against freshly built binaries, which are rebuilt after an acceptance repair; a bullet with no root test stops the run before it starts, and the run is rejected when a test is vacuous, a test or built file was tampered with, or a stray file or a listening port is left behind. acceptance.json records each command, exit code, output size and SHA-256.
Escalation to a person. A leaf that has used its whole model ladder, or reports a contract problem, is escalated to a person through the human gate (--gate terminal|file, the file gate writing ESCALATION-<node>.md into the run folder), telling the reviewer how many times the planner rewrote the node and what it proposes; no answer ends the run with exit 4 (terminal) or 3 (file), the report records who answered, and leaf loops run one at a time with a scheduler that orders them by level.
Revisions are commits on a hash chain. A revision lands as its own commit on the work branch, subject gm(revise): <node> test, revision <n> with a GopherMind-Revision trailer, recorded as a revision event in _state/events.jsonl; the executor refuses to start a run whose revision log or hash chain is missing or broken, or whose node revision field is off the log's tip, and stops with reason plan_changed.
Parallel runs and the gate. Parallel runs ask the gate one question at a time and warn at start when executor.workers exceeds the largest provider max_concurrent; a level falls back to serial with a warning when its base copy cannot be made or the tree copy is over 256 MiB, and overlapping file paths split a level into sub-levels only when workers is above 1.
New gophermind project flags.gophermind project takes --repo <path> (overrides the brief's repo; with --resume it must match the run's recorded repo), --generate NAME=hex32|placeholder (repeatable; creates a secret the brief declares and stores it in the vault's harness scope), --require-private, --expect-head <rev> (implies --graded), --expect-binary-commit <sha> (7 to 40 hex characters), --resume, --attended (CLI only; the TUI never prompts) and --preflight-only; flags are two-dash only, no flag but --generate may repeat, and --graded needs --expect-head and can neither resume nor be attended.
gophermind project --print-state-paths and scripts/clear-project-state.sh.--print-state-paths lists everything a clean attempt must clear, and scripts/clear-project-state.sh <repo> <id> <work-branch> <archive-dir> clears it: it archives the whole .gophermind folder, bundles the work branch, verifies BASELINE (default goal-baseline) and resets the repo onto BASE_BRANCH (default main), refusing /, $HOME, relative, .. or symlinked paths.
A /project run writes project.json. A /project run writes its full report to .gophermind/<run-id>/_state/project.json (binary version and commit stamp, preflight checks, each secret's source, how ambiguity was settled, planner warning counts, the graded verdict and its reason, any coverage error) with free text capped at 200 characters, and prints a one-screen summary.
GOPHERMIND_DEBUG_DUMP_DIR (developer only). Setting it makes the planner write each model request and raw reply (mode 0600) to that directory; it is ignored with a warning when the directory is inside the repository, the run folder or the config folder, and is not to be set for a graded run (nothing enforces this).
Changed
Breaking: the pipeline contract. The server's /pipeline/state, /pipeline/events and /pipeline/report and gophermind-osx's pipeline panel (the native app) now read the run folder (nodes, runtime files, event log, report) instead of .planning/assignments.json. Every body carries schema_version 1 and every response the header X-Gophermind-Pipeline-Schema: 1. New routes: /pipeline/runs, /pipeline/node, /pipeline/artifact, /pipeline/decisions. Event streams carry ids, resume with since or Last-Event-ID, and are capped at 16 (GOPHERMIND_PIPELINE_MAX_STREAMS). The server and gophermind-osx must be updated together; a pair that disagrees shows a message in the panel and chat keeps working. (The Wails app in desktop/ has no pipeline panel and is not affected.)
Breaking: run state is files, not SQLite. A run's nodes, runtime state (<node>.runtime.json, claims and attempts), event log (_state/events.jsonl), call log (_state/calls.jsonl), saved attempts and report are all files in .gophermind/<run-id>/. blackboard.db is no longer created; a leftover one from an older version is ignored.
The pipeline is node based. Planning and building work on a tree of nodes with groups and a contract slice, replacing the task-based .planning/assignments.json pipeline.
The briefv2 package folder is now gophermind-lib/build and docs/briefv2 is docs/build (import path change only, no behavior change).
Breaking: /project takes the brief path as its only positional argument. The v0.9.0 form /project <name> <brief> no longer works and is refused with a message asking for the brief path only.
Reserved component ids. The component ids attempts and decisions are reserved alongside logs; a brief whose plan names a component that way is refused.
Executor stubs exit 86. Executor stubs print gm: not implemented to stderr and exit with status 86 instead of panicking with status 2, so an acceptance test that only checks for exit code 2 no longer passes against a stub.
Whole-file replies are stripped to the leaf. The executor strips contract-declared types and other leaves' functions from a model reply that returns the whole file (event leaf_reply_stripped, count only) and gives the model a retry note specific to what was wrong instead of a generic one.
The server's pipeline dashboard page. It shows the run list, node tree, decisions, attempts and saved check output from the run folder, resyncs state on every stream connect without applying replayed or stale frames, keeps an open artifact and scroll position across re-renders, and stops retrying a stream refused with 401, 403 or 404 (other errors back off to 30 s). invalid_brief (the brief was rejected) is a documented run status in the pipeline contract; the dashboard treats it as terminal and fetches that run's report.
Removed
gophermind phase and /phase are removed, with PhaseFlow's roadmap workflow, its embedded prompts and its .planning/ files (ROADMAP.md, STATE.md, assignments.json). There is no replacement for the roadmap loop. gophermind phase ... and /phase now print a notice and the CLI exits 2; plan and build a brief with gophermind brief plan and gophermind brief run, or with /project. Existing .planning/ folders are not read. The notice stub is removed in the release after this one.
The v1 planner and runner are removed: the plantree and orchestrate packages, /plan-v1, /plan-v1-execute, /project-execute and /questions. Typing one of them prints a notice pointing at /project <brief>; the stub goes with the /phase stub in the next release. The gophermind-phaseflow skill pack (.gophermind/skills/gophermind-phaseflow.md) and the examples/phase-flow/ai-interview example are deleted.
Fixed
gophermind brief validate false secret warnings and silently dropped requirements. It no longer warns that HTTP methods (GET, POST, DELETE, ...) or an error name after a status code (401 BAD_CREDENTIALS) look like undeclared secrets, and a brief with an unclosed code fence or a non-bullet line under ## Constraints or ## Acceptance is rejected with the line number instead of silently dropping the requirements after it; + , 1. and 1) now count as bullets.
A dependency cycle between two functions no longer fails every wave plan on each resume. The planner drops the back edge deterministically (contract order), warns by node id, and records the result.
Decompose repairs one node instead of discarding a batch. Decompose repairs the one defective node of a batch instead of discarding and re-requesting the whole batch, normalises nodes whose shape deviates (side_effects objects become strings, strings become arrays, null becomes empty, model_tier is case-folded, forbidden properties dropped, empty titles derived from the signature) with a leaf_normalized warning, reports remaining schema defects by field and keyword, rechecks pending nodes from an earlier run before any model call, and repairs node prose that names fields its declared struct does not have (ignoring qualified types and file-like words).
Executor safety fixes. An attempt is recorded as passed before a leaf is marked verified, a panic in a leaf is recovered, a claim is released on a cancelled retry, any Fatal or Panic method call the final scan would reject is refused at the reply gate, diff_only landing refuses foreign paths and secrets, and a plan whose coverage does not reach every requirement stops with coverage_incomplete.
Revision fixes. A revise reply that returns the whole node is read as a patch of what changed (a changed frozen field is refused by name, an unchanged reply is revise_noop), the test replacement temp file is named .gm-replace-<base>.<hex> and resume removes a leftover one beside a node with an open revision, a committed rewrite is no longer counted twice after a human retry, a revision commit can no longer be mistaken for a leaf commit, and ReplaceTest keeps the test file's permission bits.
Gate text is printed on one line without format characters. Gate text written by the model (proposal, reason, history) is printed on one line with bidi overrides, zero-width and other format characters dropped and is capped at 600 runes, so a reply cannot hide or reshape what a reviewer sees in a terminal or gate file.
Plan approval refuses while questions are open. Plan approval refuses while questions are still open or when the approval predates the confirmed understanding, and the confirm stage refuses a hand-edited answers.json instead of overwriting it.
gophermind-osx stream and body limits, and the pipeline panel after a late connect. The native app no longer fails on an event stream line over 64 KiB (the limit is now 8 MiB, with an explicit too-long error beyond it), reports a response body over 4 MiB as too large instead of silently truncating it, and its pipeline panel fills in after a later connect instead of staying empty until relaunch when the app started with no reachable backend or was disconnected.
Nested build directories are visible to the agent. The list_files, glob and find_symbol tools no longer skip a build directory below the repository root; only the top-level build/ is ignored, so folders such as lib/build are visible.
More model replies are repaired instead of failing the stage. The planner repairs a stray backslash inside a string, boolean, number, object or array values for given and expect, an unambiguous shortened Clarify recommendation, over-long why_it_matters and recommended_why fields truncated instead of rejected, a test file package clause checked against the last path element with the module and package import path stated in the prompt, and an executor leaf package taken from the clause name when the contract gives a directory; schema errors list the allowed properties, JSON syntax errors give line, column and kind, and enrich repair prompts state every defect in plain words.
Contract signatures and file names are normalized at the contract stage. Same-package qualifiers are dropped, types declared in exactly one other package are qualified, kebab-case type ids become Go type names, bare file names are joined with the package directory, and every function gets a file no other function or type uses so a leaf no longer overwrites shared type declarations (warnings signature_qualifier_normalized, file_path_normalized, function_file_unique; the executor applies the same rules when loading an older contracts.json).
Model-written test files are repaired before validation instead of refused. Unused imports and long comment blocks are dropped (test_import_normalized, test_comment_normalized), helpers that collide with another accepted test file are renamed, an unterminated string on a pretty-printed line is closed, and empty given or expect strings are accepted.
Test-writer refusals are specific and the prompt states the rules. Tests of a main package must be package main and not import it, comment-heavy files and files that redeclare a package name or define contract-declared code are refused with their own messages, an unexported function under test accepts only the plain package clause, a test is internal when the signature uses unexported types, imports outside std, the module and the contract packages are refused by name, covers refusals state the error count and the valid tags, and boundary is now a valid covers tag so edge-case tests that concern no single parameter are no longer refused.
Security
Every child process runs in a sandbox-exec profile. Every child process the executor runs (go build, go test and what they spawn) is wrapped in a macOS sandbox-exec profile that denies Launch Services, the pasteboard, security daemons, non-loopback network, signals outside the child's process group, secret-named files and writes under <repo>/.gophermind; the runner opens only the loopback ports a command's environment names, sweeps setsid descendants after a command ends and warns when one survives. When sandbox-exec is missing the run fails with a message, and executor.sandbox: off runs without one.
Secrets never reach a model prompt, a saved attempt or the report. The prompt packer scrubs every declared secret form (including encoded ones) from failure output, the leaf, wave and report paths use the same check, a reply whose source holds a secret (even one that appears only after decoding JSON or Go escapes) is refused as secret_in_reply and never saved, retry notes and warnings are scrubbed, and saved check output drops the line the output cap cut so a cut secret prefix is never written.
Untrusted text cannot close a prompt section. Untrusted text (the brief, model replies, repo facts) can no longer close a data section of a planner or executor prompt; section tags in it are neutralized before the prompt is built.