docs: rewrite AGENTS.md as user-facing 'Extending Zero' guide#237
Conversation
The previous AGENTS.md (a 19-line Go-specific instruction set) was replaced with a user-facing extension guide for Zero as an open-source CLI agent product. Covers: - Project-level AGENTS.md (where to drop one, format, tips) - Custom specialists (droids): scopes, manifest format, CLI - Skills: discovery, SKILL.md format, ZERO_SKILLS_DIR override - Hooks: lifecycle events, JSON config locations, exit code semantics - MCP: as client (config.json mcp.servers) and as server (zero serve --mcp) - Plugins: plugin.json manifest, install/enable/disable/remove - Configuration locations: built-in < user < project < CLI < env - End-to-end example: what a team commits vs what a contributor adds - Reference links to README and docs/SPECIALISTS.md, docs/PRD.md, etc. This positions AGENTS.md as the entry point for end users who want to customize Zero, mirroring the role that similar files play in Claude Code and Codex.
Zero automated PR reviewVerdict: No blockers found Blockers
Validation
ScopeHead: This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality. |
WalkthroughThis PR implements and documents project guideline file injection into system prompts. It adds git-root-to-cwd multi-file traversal with case-insensitive discovery, byte budgeting, and truncation; documents the complete feature in AGENTS.MD; and validates behavior across integration and unit tests. ChangesGuideline Injection Feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@AGENTS.MD`:
- Around line 102-108: The fenced code block in AGENTS.MD displaying the skill
directory structure is missing a language identifier on the opening fence. Add
the language specifier "plaintext" to the opening triple backticks (the fence at
the start of the directory listing example) to comply with markdown linting
standards and enable proper syntax highlighting.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: a0dd9375-ea83-4d36-b834-1782db8adadd
📒 Files selected for processing (1)
AGENTS.MD
…tyle features
Fix six claims that were wrong about Zero itself, document the new
upward path-walking the loader now does, and surface three roadmap
gaps in the relevant sections.
What changed in the doc:
- Section 1 (Drop a project AGENTS.md): rewrote the file table to
match the real priority order [AGENTS.md, ZERO.md, .zero/AGENTS.md]
and added the case-insensitive basename callout (git tracks
AGENTS.MD uppercase on case-sensitive filesystems).
- Section 1: documented the per-file (8 KiB) and total (32 KiB) caps
and the eager-load + restart-required caveat.
- Section 1: added the monorepo tip and the path-walking explanation
("walks from cwd up to git root, general-to-specific order").
- Section 2 (Custom specialists): added a roadmap note about the
planned /droids in-UI manager.
- Section 3 (Skills): corrected the discovery-root chain
(ZERO_SKILLS_DIR -> XDG_DATA_HOME/zero/skills -> ~/.local/share/zero/skills),
noted that project-level skills aren't supported yet, and called
out the plugin-declared-skill gap.
- Section 4 (Hooks): roadmap note about the planned /hooks in-UI
manager.
- Section 6 (Plugins): roadmap note about the planned /plugins in-UI
manager; cross-link to the skills gap.
- Frontmatter globs broadened to include *.json / *.toml / *.yaml /
*.yml, matching the actual file types Zero reads.
Two changes to the project guideline loader that bring Zero closer in line with D's DynamicContextDiscovery and fix a real bug. 1. Case-insensitive basename matching. The git-tracked filename is AGENTS.MD (uppercase MD), but the loader was looking for AGENTS.md (lowercase md). On a case-sensitive filesystem (Linux, the WSL filesystem, or a CI runner) this means the file was invisible to the loader. The new findProjectContextFile uses ReadDir + ToLower to resolve the on-disk filename and matches AGENTS.MD, Agents.md, agents.md, etc. to the same entry. On Windows/macOS the visible filename in the prompt label is now the actual on-disk name (AGENTS.MD), not the lookup name. 2. Upward path-walking. The loader previously did a single first-match-wins lookup at cwd. The new projectGuidelines walks from cwd up to the nearest git root, finds the first matching project context file at each level, and injects them in general-to-specific order (git root first, cwd last). A monorepo with a root AGENTS.md and per-service AGENTS.md files now sees both, with the most specific rules last. This mirrors what D does per-Read on the dynamic path, just at session start. Cap behavior: - Per-file cap is unchanged: 8 KiB. - New total cap of 32 KiB across all matched files, enforced via a rolling counter; when the budget is exhausted, further files are dropped (and the per-file limit is the lesser of the per-file cap and what's left of the total). - Truncation marker is unchanged: \n… (truncated). New helpers and tests: - projectGuidelineDirs / projectGuidelineLabel / findProjectContextFile / resolveDirCaseInsensitive / findProjectGitRoot. - 8 new tests covering case-insensitive matching, monorepo path-walking, ZERO.md fallback, .zero/AGENTS.md fallback, total cap truncation, and the helper functions. Existing TestBuildSystemPromptInjectsWorkspaceContext still passes; the label for a single-file repo is still AGENTS.md / AGENTS.MD as appropriate to the on-disk name.
Follow-up: two more commits since the original opening4a22259 docs(AGENTS): align Extending Zero with actual loader behavior + D-style featuresThe first version of this PR made six claims that were wrong about Zero itself. This commit fixes them and surfaces the new behavior introduced in 6fb0eb2:
6fb0eb2 feat(agent): make AGENTS.md loader case-insensitive + add path-walkingTwo changes to the loader in internal/agent/system_prompt.go that bring Zero closer to D's DynamicContextDiscovery model and fix a real bug. 1. Case-insensitive basename matching. The git-tracked filename is AGENTS.MD (uppercase MD), but the loader was looking for AGENTS.md (lowercase md). On a case-sensitive filesystem (Linux, WSL, or a CI runner) this made the file invisible to the loader. The new indProjectContextFile uses ReadDir + ToLower to resolve the on-disk filename and matches AGENTS.MD, Agents.md, �gents.md, etc. to the same entry. The visible filename in the prompt label is now the actual on-disk name (AGENTS.MD), not the lookup name. 2. Upward path-walking. The loader previously did a single first-match-wins lookup at cwd. The new projectGuidelines walks from cwd up to the nearest git root, finds the first matching project context file at each level, and injects them in general-to-specific order (git root first, cwd last). A monorepo with a root AGENTS.md and per-service AGENTS.md files now sees both, with the most specific rules last. This mirrors D's per-Read dynamic path discovery, just at session start. Cap behavior:
New helpers: projectGuidelineDirs, projectGuidelineLabel, indProjectContextFile, New tests (all passing, full go test ./internal/agent/ suite still green):
The existing TestBuildSystemPromptInjectsWorkspaceContext still passes; for a single-file repo the label is AGENTS.md / AGENTS.MD matching the on-disk name. What I deliberately did NOT change
|
gnanam1990
left a comment
There was a problem hiding this comment.
Review: AGENTS.md → "Extending Zero" guide
The structure and writing are a real improvement — user-facing, well-organized, good copy-paste examples. But the guide documents several commands/flags/behaviors that don't exist in the current code, which will mislead users. I grep-verified each against the tree:
- Plugin verbs (§6):
zero plugins install/enable/disablearen't implemented —runPlugins(internal/cli/extensions.go) only dispatcheslist/add/remove(rm). Usezero plugins add <path>; dropenable/disablefor plugins (those exist forhooks/mcp, but not plugins). - Specialist prompt (§2):
zero specialist create ... --prompt-file api-reviewer.md— there is no--prompt-fileflag (internal/cli/specialist.go). It's--prompt "<text>"(inline). ${env:...}(§5):"Authorization": "Bearer ${env:GITHUB_TOKEN}"— there is no${env:}expansion anywhere; MCP header/env values are sent verbatim (internal/mcp/config.go,client.gomergeProcessEnv). A user following this would transmit the literal string${env:GITHUB_TOKEN}.- Hook arg placeholders (§4):
"args": ["${tool}", "${input}"]— hook args are passed verbatim toexec.CommandContext; the tool/input payload reaches hooks as JSON on stdin, not via arg substitution (internal/hooks/dispatch.go). No${...}expansion occurs. zero doctor(§4): "usezero doctorto surface [hooks]" — doctor has no hook check (internal/doctor/): its checks are runtime / config / provider / connectivity / sandbox / lsp.
Secondary (non-blocking): the globs: / alwaysApply: frontmatter described in §1 is currently inert — the loader injects the whole file (frontmatter included) as prompt text and does no frontmatter parsing, so globs: doesn't scope anything yet. Also the README/CLI use "specialists" throughout, so the "droids" synonym reads inconsistently.
Everything else checks out — serve --mcp, mcp add … -- <cmd>, the hook event names, skills dir order, specialist/plugin paths, and the byte caps are all accurate. Requesting changes only on the items above; once the non-existent syntax is removed or fixed, this is a strong guide.
Per the review on #237, the previous "Extending Zero" guide documented several commands, flags, and conventions that the runtime does not implement. Concretely: - `zero plugins install` / `enable` / `disable` are not subcommands. `runPlugins` (internal/cli/extensions.go) only dispatches `list` / `add` / `remove`(`rm`). Replace the example with `add` and note that enable/disable are not CLI verbs today (the plugin is enabled by being present and disabled by being removed, or by setting `"enabled": false` in its manifest). - `zero specialist create ... --prompt-file api-reviewer.md` is wrong: there is no `--prompt-file` flag. `runSpecialistCreate` takes the prompt inline via `--prompt`. Show a `$(cat ...)` shell substitution as the practical way to load a file, and add `zero specialist path` to the example. - `"Authorization": "Bearer ${env:GITHUB_TOKEN}"` implies env-var expansion that does not exist: MCP header values are sent verbatim (internal/mcp/permissions.go, client.go mergeProcessEnv). Use a literal placeholder and document the three real ways to keep the token out of the file (wrapper script, command substitution, server reading its own env). - Hook `args: ["${tool}", "${input}"]` is the same mistake plus a shape mismatch: the actual hook payload (event, matcher, tool call id, tool input/output, status) is delivered as JSON on stdin (internal/hooks/dispatch.go `execCommandRunner`), not via `${...}` substitution. Drop the placeholders, add a short bash handler that reads stdin, and show the `args` field as verbatim CLI args. - "use `zero doctor` to surface them" — the doctor backend (internal/doctor/doctor.go) has no hook check; its checks are runtime / config / provider / connectivity / sandbox / lsp. Reword to point at the audit log instead. Secondary: drop the `globs:` and `alwaysApply:` example and the "Use globs: to scope which files the rules apply to" tip — the loader injects the whole file (frontmatter included) as prompt text and does not parse those keys. Add a one-liner noting the frontmatter is preserved verbatim. Also drop the parenthetical "(droids)" on §2 to match the "specialists" terminology the rest of the codebase (and the CLI) actually use, and reword the §2/§4/§6 roadmaps that promised in-UI managers via non-existent `/droids`, `/hooks`, `/plugins` slash commands.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
AGENTS.MD (1)
107-113:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd language identifier to fenced code block.
The code block displaying the skill directory structure lacks a language specifier. Add
plaintextto the opening fence to comply with Markdown linting standards.🔧 Proposed fix
-``` +```plaintext ~/.local/share/zero/skills/ run-benchmarks/ SKILL.md write-changelog/ SKILL.md</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@AGENTS.MDaround lines 107 - 113, The fenced code block displaying the
directory structure starting with ~/.local/share/zero/skills/ is missing a
language identifier. Add plaintext as the language specifier immediately after
the opening fence (changetoplaintext) to comply with Markdown linting
standards.</details> <!-- cr-comment:v1:bd4a272bd718f9f05f7c3b28 --> _Source: Linters/SAST tools_ </blockquote></details> </blockquote></details>🧹 Nitpick comments (1)
AGENTS.MD (1)
226-230: 💤 Low valueConsider restructuring for clarity on line 230.
The phrase "the env var your MCP server reads" is grammatically awkward. While "that" is technically correct for an object, consider restructuring for clarity—for instance: "A secret manager that injects the env var that your MCP server reads on its own" or simply "A secret manager that injects the secret your MCP server reads."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@AGENTS.MD` around lines 226 - 230, The phrase "the env var your MCP server reads" on line 230 lacks clarity due to awkward phrasing. Restructure this third bullet point to improve readability by inserting "that" before "your" to read "the env var that your MCP server reads" or alternatively simplify it to "the secret your MCP server reads" to make the relationship between the secret manager and the MCP server's behavior more explicit and grammatically clear.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Outside diff comments: In `@AGENTS.MD`: - Around line 107-113: The fenced code block displaying the directory structure starting with ~/.local/share/zero/skills/ is missing a language identifier. Add plaintext as the language specifier immediately after the opening fence (change ``` to ```plaintext) to comply with Markdown linting standards. --- Nitpick comments: In `@AGENTS.MD`: - Around line 226-230: The phrase "the env var your MCP server reads" on line 230 lacks clarity due to awkward phrasing. Restructure this third bullet point to improve readability by inserting "that" before "your" to read "the env var that your MCP server reads" or alternatively simplify it to "the secret your MCP server reads" to make the relationship between the secret manager and the MCP server's behavior more explicit and grammatically clear.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID:
4d154ac9-1a3e-4c42-b81e-98a89582fa92📒 Files selected for processing (1)
AGENTS.MD
gnanam1990
left a comment
There was a problem hiding this comment.
Re-review (after a6ce791)
The follow-up commit resolves every must-fix item from the previous review — I re-verified each against the current code and the updated AGENTS.MD:
- Plugins — now
zero plugins add ./github-pr-review; the non-existentinstall/enable/disableverbs are gone, with an accurate note that a plugin is enabled by being present (or"enabled": falsein its manifest). ✓ - Specialist prompt —
--prompt-filereplaced with--prompt "$(cat api-reviewer.md)", plus a usefulzero specialist path. ✓ ${env:...}— now states plainly there is no env expansion (values sent verbatim) and documents the real ways to keep a token out of the file. ✓- Hook args —
${tool}/${input}dropped; correctly explains the payload arrives as JSON on stdin, with a stdin-reading handler example. ✓ zero doctor— reworded to point at the audit log; no longer claims a doctor hook check. ✓
Secondary items also handled: the inert globs:/alwaysApply: frontmatter example is replaced with an accurate "preserved verbatim, not parsed today" note, and the (droids) synonym plus the non-existent /droids /hooks /plugins slash-command roadmaps are gone.
All clean corrections, no new claims that don't match the code. Thorough turnaround — approving.
|
@coderabbitai approve |
✅ Action performedComments resolved and changes approved. |
|
Pushed a6ce791 to address the review. Walked each item against the tree, then re-checked the rewritten sections after editing:
Secondary: dropped the |
Restore sandbox.blockUnixSockets as a resolved config and policy field, route it into the Linux helper as --block-unix-sockets, and keep zero-seccomp as a Linux compatibility wrapper packaged with release artifacts. Add the missing legacy-entrypoint test referenced by the smoke script, extend helper/release/config tests, and make CLI startup tests hermetic by stubbing MCP registration where they assert quiet output. Document the compatibility path, release helper scope, and the already-resolved #237 prompt-file overlap in the PR body. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox ./internal/cli ./internal/config ./internal/release ./cmd/zero-seccomp -run 'TestLegacySandboxEntrypointsAreExplicitAndExist|TestSandboxManagerBuildsCommandPlanThroughLinuxHelper|TestBuildLinuxSandboxCommandArgsSerializesPermissionProfile|TestParseLinuxSandboxHelperArgs|TestBuildLinuxSandboxBwrapArgsWrapsInnerSeccompStage|TestApplyConfiguredSandboxPolicyDiagnosticsFlags|TestResolveSandboxBlockUnixSocketsFromFile|TestCopyPackageFilesStagesLinuxSandboxHelper' Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: GOCACHE=/tmp/zero-go-cache scripts/sandbox-smoke.sh Tested: git diff --cached --check Tested: local leak scan found no matches
* Add sandbox architecture baseline Record target backend names, enforcement metadata, legacy sandbox entrypoints, and policy JSON baseline coverage for the sandbox rewrite. Add a focused sandbox smoke script with cross-platform compile checks and an opt-in real backend smoke path. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run 'TestTargetBackendForPlatformBaseline|TestLegacySandboxEntrypointsAreExplicitAndExist|TestBackendPlanCarriesPhase0ManagerFields|TestCommandPlanCarriesSandboxMetadata|TestPolicyOnlyDisabledFailClosedForTargetPlatforms|TestSelectBackendChoosesPlatformAdapterWithFallback|TestBackendBuildPlanDocumentsBestEffortIsolation|TestBackendCapabilitiesReflectDisabledPolicy' Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli -run 'TestRunSandboxPolicyInspectTextAndJSON|TestRunSandboxPolicyJSONGoldenIncludesManagerBaselineFields|TestRunSandboxPolicyEffectiveTextAndJSON' Tested: scripts/sandbox-smoke.sh Tested: git diff --cached --check * Introduce sandbox manager and permission profile Add a normalized permission profile and sandbox manager execution request that owns target backend selection, enforcement metadata, and fail-closed validation. Route command planning and sandbox policy output through the manager boundary while keeping legacy adapters explicit as temporary integration points. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run 'TestPermissionProfileFromPolicyBuildsWorkspaceWriteProfile|TestPermissionProfileFromDisabledPolicyDoesNotRequirePlatformSandbox|TestSandboxManagerBuildsExecutionRequestFromProfile|TestSandboxManagerFailsClosedWhenNativeRequiredAndPolicyOnlyDisabled|TestTargetBackendForPlatformBaseline|TestBackendPlanCarriesPhase0ManagerFields|TestCommandPlanCarriesSandboxMetadata|TestPolicyOnlyDisabledFailClosedForTargetPlatforms' Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli -run 'TestRunSandboxPolicyInspectTextAndJSON|TestRunSandboxPolicyJSONGoldenIncludesManagerBaselineFields|TestRunSandboxPolicyEffectiveTextAndJSON' Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/tools -run 'TestRegistryRunsBashThroughSandboxEngine|TestBashToolReportsDisabledPolicyOnlyRunner|TestBashToolBuildsWrappedSandboxExecCommand' Tested: scripts/sandbox-smoke.sh Tested: git diff --cached --check * Move sandbox backend selection into manager Make SandboxManager own platform backend selection and keep SelectBackend as a compatibility shim for existing CLI and TUI wiring. Add tests proving manager-owned selection and the compatibility wrapper agree. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run 'TestSandboxManagerSelectsPlatformBackend|TestSelectBackendDelegatesToSandboxManagerSelection|TestSelectBackendChoosesPlatformAdapterWithFallback|TestLegacySandboxEntrypointsAreExplicitAndExist|TestSandboxManagerBuildsExecutionRequestFromProfile|TestSandboxManagerFailsClosedWhenNativeRequiredAndPolicyOnlyDisabled' Tested: scripts/sandbox-smoke.sh Tested: git diff --cached --check * Route command planning through sandbox manager Make SandboxManager build command plans from sandbox execution requests, with backend-specific wrapping isolated behind a temporary adapter. Move Engine.BuildCommandPlan to a single manager boundary and mark buildLegacyCommandPlan as the explicit delete target for later backend work. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run 'TestSandboxManagerBuildsCommandPlanThroughTemporaryAdapter|TestSandboxManagerBuildsDegradedPolicyOnlyCommandPlan|TestBuildCommandPlanWrapsBubblewrap|TestBuildCommandPlanWrapsSandboxExec|TestBuildCommandPlanUsesPolicyOnlyFallback|TestBuildCommandPlanCanRejectPolicyOnlyFallback|TestLegacySandboxEntrypointsAreExplicitAndExist|TestSandboxManagerBuildsExecutionRequestFromProfile|TestSandboxManagerFailsClosedWhenNativeRequiredAndPolicyOnlyDisabled' Tested: scripts/sandbox-smoke.sh Tested: git diff --cached --check * Add Linux sandbox helper command boundary Introduce the zero-linux-sandbox command surface and typed permission-profile argv builder used by the upcoming Linux helper rewrite. Keep the helper fail-closed until enforcement is wired, and add parser tests plus smoke compilation for the new helper package. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run 'TestBuildLinuxSandboxCommandArgsSerializesPermissionProfile|TestParseLinuxSandboxHelperArgs|TestRunLinuxSandboxHelperFailsClosedUntilEnforcementIsWired' Tested: scripts/sandbox-smoke.sh Tested: git diff --cached --check * Fix sandbox policy golden path aliases Normalize both original and symlink-resolved temp paths before comparing sandbox policy JSON goldens, so macOS /private/var aliases do not fail the smoke job. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli -run 'TestRunSandboxPolicyJSONGoldenIncludesManagerBaselineFields|TestRunSandboxPolicyInspectTextAndJSON|TestRunSandboxPolicyEffectiveTextAndJSON' Tested: scripts/sandbox-smoke.sh Tested: git diff --cached --check * Fix Windows sandbox smoke test assumptions Normalize JSON-escaped Windows paths in the sandbox policy golden test and mark the fake bubblewrap backend as Linux in the command metadata test. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli -run 'TestRunSandboxPolicyJSONGoldenIncludesManagerBaselineFields|TestRunSandboxPolicyInspectTextAndJSON|TestRunSandboxPolicyEffectiveTextAndJSON' Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run 'TestCommandPlanCarriesSandboxMetadata|TestBackendPlanCarriesPhase0ManagerFields|TestPolicyOnlyDisabledFailClosedForTargetPlatforms' Tested: scripts/sandbox-smoke.sh Tested: git diff --cached --check * Stabilize sandbox policy golden test Compare the Windows sandbox policy golden as decoded JSON after path tokenization so platform-specific byte formatting does not fail smoke tests while preserving the same field-level regression coverage. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli -run TestRunSandboxPolicyJSONGoldenIncludesManagerBaselineFields Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli -run 'TestRunSandboxPolicyJSONGoldenIncludesManagerBaselineFields|TestRunSandboxPolicyInspectTextAndJSON|TestRunSandboxPolicyEffectiveTextAndJSON' Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Route Linux sandboxing through helper Move Linux command planning to invoke zero-linux-sandbox with the permission profile, command cwd, and command argv instead of exposing bubblewrap construction from the runner boundary. The helper now builds the bubblewrap argv, binds its own executable for the inner stage, applies the seccomp/no-new-privs step before exec, and preserves the original command cwd path. Package Linux releases with zero-linux-sandbox next to zero so release builds use the helper directly, while local development can fall back to go run for the helper. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli ./internal/tools ./cmd/zero-linux-sandbox Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/release ./cmd/zero-release Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Normalize Linux helper cwd test paths Compare native paths in the helper cwd regression test so Windows smoke does not fail on path separator formatting while preserving the same helper behavior assertion. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run TestLinuxHelperPlanPreservesRealExtraRootCwd Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Add Linux Landlock helper enforcement Implement the zero-linux-sandbox Landlock helper path with fail-closed profile validation, Landlock filesystem rules, and seccomp network-deny enforcement for the fallback backend. Broaden the Linux real smoke coverage to cover helper write allow/deny, deny-read, temp writes, metadata protection, network deny, and direct Landlock fallback behavior. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: scripts/sandbox-smoke.sh Tested: GOCACHE=/tmp/zero-go-cache go test ./cmd/zero-linux-sandbox ./internal/cli ./internal/tools ./internal/release ./cmd/zero-release Tested: git diff --check * Route Seatbelt profiles through permission profiles Build macOS sandbox-exec profiles from the shared PermissionProfile so read roots, write roots, temp access, deny-read, deny-write, and protected metadata are expressed by the platform profile instead of legacy write-root-only inputs. Keep the existing sandbox-exec adapter path as a compatibility wrapper while adding target macos-seatbelt capability handling. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: scripts/sandbox-smoke.sh Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli ./internal/tools Tested: git diff --check * Stabilize Seatbelt write carveouts Emit protected metadata and read-only write-root carveouts as explicit Seatbelt deny rules after the broad write allow. This preserves deny precedence while avoiding fragile allow-rule predicates in real sandbox-exec runs. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Expand macOS Seatbelt runtime allowances Restore the platform startup allowances needed by sandbox-exec-wrapped commands under restricted filesystem profiles: executable mapping, system metadata reads, pty/device access, preferences, IPC, and standard library/runtime roots. This keeps project writes constrained to the PermissionProfile roots while preventing macOS shells from aborting before user commands run. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Normalize Seatbelt deny expectations in tests Build Seatbelt deny-rule expectations from normalized profile paths so the metadata ordering test matches production behavior on Windows and Unix hosts. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: scripts/sandbox-smoke.sh Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/sandbox -o /tmp/zero-sandbox.test.exe Tested: git diff --check * Add Windows sandbox runner command planning Select the windows-restricted-token backend only when the Windows sandbox command runner is discoverable, and keep the missing-runner path as an explicit policy-only downgrade. Add the Windows runner argument contract for command cwd, workspace roots, permission profile JSON, child environment JSON, restricted-token level, and payload command parsing. Update sandbox policy output and tests to describe the missing Windows runner instead of an unimplemented adapter. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/sandbox -o /tmp/zero-sandbox.test.exe Tested: scripts/sandbox-smoke.sh Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: git diff --check * Add Windows sandbox command runner Add the zero-windows-command-runner executable and wire windows-restricted-token plans to pass sandbox home, command cwd, workspace roots, permission profile JSON, child env JSON, sandbox level, and command argv. Persist Windows capability SIDs under the resolved sandbox home, select read-only or writable-root capability SIDs from the permission profile, and create a restricted token before launching the payload with CreateProcessAsUserW. Package the Windows runner next to zero.exe and extend sandbox smoke coverage to cross-build it. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox ./internal/release ./cmd/zero-windows-command-runner ./cmd/zero-release Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-command-runner.exe ./cmd/zero-windows-command-runner Tested: scripts/sandbox-smoke.sh Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: git diff --check * Plan Windows sandbox ACL operations Derive a deterministic Windows ACL operation list from the restricted permission profile, including writable root allows, protected metadata deny-write entries, explicit deny-write paths, and deny-read materialization targets. The plan maps workspace and extra writable roots onto the persisted capability SIDs so the elevated setup helper can apply the same contract without reinterpreting the profile. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/sandbox -o /tmp/zero-sandbox.test.exe Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-command-runner.exe ./cmd/zero-windows-command-runner Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Add Windows sandbox setup boundary Add the zero-windows-sandbox-setup command, setup argument parsing, deterministic ACL plan hashing, and a setup marker contract that can detect profile changes requiring refresh. Make Windows native backend discovery require both the command runner and setup helper, package the setup helper with Windows releases, and include it in sandbox smoke builds. The setup command remains fail-closed until the Windows ACL applier is wired, so it cannot create a setup-complete marker without applying enforcement. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox ./internal/release ./cmd/zero-windows-sandbox-setup Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/sandbox -o /tmp/zero-sandbox.test.exe Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-command-runner.exe ./cmd/zero-windows-command-runner Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-sandbox-setup.exe ./cmd/zero-windows-sandbox-setup Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Apply Windows sandbox ACL setup Implement the Windows ACL setup path with grouped native DACL updates, deny-read materialization, existing DACL snapshots, and rollback for partial application failures. Write the setup marker only after ACL setup succeeds, roll back applied ACLs if marker persistence fails, and make the Windows command runner validate the setup marker before creating the restricted token. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox ./cmd/zero-windows-sandbox-setup ./cmd/zero-windows-command-runner Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/sandbox -o /tmp/zero-sandbox.test.exe Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-command-runner.exe ./cmd/zero-windows-command-runner Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-sandbox-setup.exe ./cmd/zero-windows-sandbox-setup Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Fail closed for Windows network policies Add Windows network policy validation that allows only explicit network-allow until native Windows network enforcement is implemented. Make the Windows command runner reject deny, scoped, and unset network modes before launching a restricted-token process so default network-deny cannot silently run open. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox ./cmd/zero-windows-command-runner Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/sandbox -o /tmp/zero-sandbox.test.exe Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-command-runner.exe ./cmd/zero-windows-command-runner Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Track Windows network setup state Add a canonical Windows network-policy hash and persist it in the Windows sandbox setup marker so setup refresh can detect network mode, domain, and proxy-requirement changes. Bump the setup marker schema and add tests for domain-order normalization plus network-change marker invalidation. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/sandbox -o /tmp/zero-sandbox.test.exe Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-command-runner.exe ./cmd/zero-windows-command-runner Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-sandbox-setup.exe ./cmd/zero-windows-sandbox-setup Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Add sandbox setup CLI command Add zero sandbox setup to resolve the active sandbox policy, derive the Windows setup helper next to the command runner, and invoke it with the shared Windows setup argument contract. Return text or JSON setup results, no-op cleanly for non-Windows native backends, and add tests using an injected helper runner. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/cli ./internal/sandbox Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/cli -o /tmp/zero-cli.test.exe Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go test -c ./internal/sandbox -o /tmp/zero-sandbox.test.exe Tested: GOOS=windows GOARCH=amd64 GOCACHE=/tmp/zero-go-cache go build -o /tmp/zero-windows-sandbox-setup.exe ./cmd/zero-windows-sandbox-setup Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: scripts/sandbox-smoke.sh Tested: git diff --check * Stabilize Windows ACL canonical path test * Add TUI sandbox setup command * Gate TUI sandbox setup command * Clarify sandbox doctor setup status * Add Windows WFP network setup Adds a Windows network enforcement plan with stable WFP provider, sublayer, and filter specs for deny-mode sandbox identities. The setup helper now installs or removes those filters transactionally before writing the setup marker, and the marker tracks the network enforcement plan so stale filters require refresh. Scoped Windows network mode remains fail-closed until the proxy exception path is implemented, so it cannot silently become open network. Diagnostics now report native Windows network enforcement when the restricted-token backend is active. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run 'TestValidateWindowsNetworkPolicyAllowsNativeModes|TestValidateWindowsNetworkPolicyFailsClosedForScoped|TestBuildWindowsNetworkPlanForDenyUsesCapabilityIdentity|TestBuildWindowsNetworkPlanFailsClosedForScoped|TestWindowsNetworkPlanHashIsStableAcrossEntryOrder|TestWindowsSandboxSetupMarkerRefreshesWhenNetworkChanges|TestSelectBackendChoosesPlatformAdapterWithFallback' Tested: GOCACHE=/tmp/zero-go-cache GOOS=windows GOARCH=amd64 go build -o /tmp/zero-windows-sandbox-setup.exe ./cmd/zero-windows-sandbox-setup Tested: GOCACHE=/tmp/zero-go-cache GOOS=windows GOARCH=amd64 go build -o /tmp/zero-windows-command-runner.exe ./cmd/zero-windows-command-runner Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox ./internal/cli ./internal/doctor Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: GOCACHE=/tmp/zero-go-cache scripts/sandbox-smoke.sh Tested: git diff --check * Add Windows sandbox network smoke Adds a gated Windows real sandbox smoke test that runs the compiled setup helper and command runner, verifies sandboxed writes still work, and proves network=deny blocks a loopback TCP connection from a restricted-token command. The sandbox smoke script now runs this test on Windows using the helper binaries it builds in its temporary directory. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox -run 'TestValidateWindowsNetworkPolicyAllowsNativeModes|TestBuildWindowsNetworkPlanForDenyUsesCapabilityIdentity' Tested: GOCACHE=/tmp/zero-go-cache GOOS=windows GOARCH=amd64 go test -c -o /tmp/zero-windows-sandbox.test ./internal/sandbox Tested: GOCACHE=/tmp/zero-go-cache GOOS=windows GOARCH=amd64 go build -o /tmp/zero-windows-sandbox-setup.exe ./cmd/zero-windows-sandbox-setup Tested: GOCACHE=/tmp/zero-go-cache GOOS=windows GOARCH=amd64 go build -o /tmp/zero-windows-command-runner.exe ./cmd/zero-windows-command-runner Tested: GOCACHE=/tmp/zero-go-cache scripts/sandbox-smoke.sh Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: git diff --check * Remove legacy sandbox runner paths Drop the old direct bubblewrap adapter, the standalone zero-seccomp wrapper, legacy adapter metadata, and the Phase 0 legacy inventory now that command planning goes through the sandbox manager backends. Switch platform selection and tests to target backend names: linux-bwrap, macos-seatbelt, windows-restricted-token, and explicit policy-only degradation. Keep policy as the shared permission/profile and diagnostics surface, but stop exposing legacy adapter fields in CLI policy output and tool metadata. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox ./internal/cli ./internal/tools ./internal/zerocommands ./internal/doctor Tested: GOCACHE=/tmp/zero-go-cache scripts/sandbox-smoke.sh Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: git diff --check * Address sandbox baseline review blockers Restore sandbox.blockUnixSockets as a resolved config and policy field, route it into the Linux helper as --block-unix-sockets, and keep zero-seccomp as a Linux compatibility wrapper packaged with release artifacts. Add the missing legacy-entrypoint test referenced by the smoke script, extend helper/release/config tests, and make CLI startup tests hermetic by stubbing MCP registration where they assert quiet output. Document the compatibility path, release helper scope, and the already-resolved #237 prompt-file overlap in the PR body. Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/sandbox ./internal/cli ./internal/config ./internal/release ./cmd/zero-seccomp -run 'TestLegacySandboxEntrypointsAreExplicitAndExist|TestSandboxManagerBuildsCommandPlanThroughLinuxHelper|TestBuildLinuxSandboxCommandArgsSerializesPermissionProfile|TestParseLinuxSandboxHelperArgs|TestBuildLinuxSandboxBwrapArgsWrapsInnerSeccompStage|TestApplyConfiguredSandboxPolicyDiagnosticsFlags|TestResolveSandboxBlockUnixSocketsFromFile|TestCopyPackageFilesStagesLinuxSandboxHelper' Tested: GOCACHE=/tmp/zero-go-cache go test ./... Tested: GOCACHE=/tmp/zero-go-cache scripts/sandbox-smoke.sh Tested: git diff --cached --check Tested: local leak scan found no matches
Summary
Replaces the project-specific 19-line AGENTS.md with a user-facing "Extending Zero" guide. The previous file was a Go-internal instruction set; this rewrite positions AGENTS.md as the entry point for end users who want to customize Zero, the way similar files work in Claude Code, Codex, and other CLI agents.
What's in the new AGENTS.md
Nine sections, ~290 new lines:
Why
Three previous PRs on AGENTS.md were closed because the framing kept drifting (awesome-list, then Go-specific 10-mode instructions). This rewrite keeps it as a single user-facing page that points outward to the deeper docs where they exist, and explains the rest inline where they don't.
Risk
Summary by CodeRabbit
AGENTS.mdwith a full “Extending Zero” guide, detailing guideline discovery, frontmatter fields, specialists, hooks, MCP configuration, plugins, configuration precedence, and repository examples.AGENTS.md,ZERO.md, and.zero/AGENTS.mdwith truncation safeguards.