Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fa6dfac
fix(secrets,worktrees): fix secret redaction leakage and prune stale …
euxaristia Jul 10, 2026
cfba521
fix(secrets,worktrees): close tail-leak edge case and worktree data-l…
euxaristia Jul 11, 2026
548cdfe
fix(worktrees): check exit codes on removal, fail closed on inspectio…
euxaristia Jul 11, 2026
008220b
fix(secrets,worktrees): catch appended-suffix keys and scope pruning …
euxaristia Jul 12, 2026
e70513f
fix(worktrees): make nested-activity test exercise the deep walk it c…
euxaristia Jul 12, 2026
59939e8
fix(worktrees): lock zero-created worktrees and treat ignored files a…
euxaristia Jul 14, 2026
fc516b5
fix(worktrees): release Zero's Prepare lock so Clean can reclaim fini…
euxaristia Jul 14, 2026
477dc28
fix(worktrees): normalize release path, aggregate Clean errors, unloc…
euxaristia Jul 14, 2026
ab34fb6
style(worktrees): align aggregation test comments to gofmt output
euxaristia Jul 15, 2026
c83e81f
fix(worktrees,cli): restore reuse lease and scope unlock to owned locks
euxaristia Jul 15, 2026
cf0a1cf
fix(worktrees,cli): reject in-use leases, validate before cleanup, ad…
euxaristia Jul 15, 2026
e90ef18
test(worktrees): canonicalize test roots to physical spelling
euxaristia Jul 15, 2026
10e9f9c
fix(worktrees): preserve orphaned commits, verify release ownership, …
euxaristia Jul 18, 2026
aafb4b4
fix(worktrees): recoverable PID leases and reclaimable released workt…
euxaristia Jul 18, 2026
6c9a9ae
fix(worktrees): address review feedback on lease detection and releas…
euxaristia Jul 19, 2026
8419e9a
fix(worktrees): canonicalize paths for clean/release ownership checks
euxaristia Jul 19, 2026
b225c8a
fix(worktrees,secrets): address jatmn review findings on #632
euxaristia Jul 22, 2026
88ca1fe
fix(worktrees): prove Prepare ownership with a git-admin marker
euxaristia Jul 22, 2026
cd5991e
fix(cli): complete worktrees release in shell completions
euxaristia Jul 22, 2026
21bfdbc
test(worktrees,cli): add coverage for linked-worktree Clean, forged l…
euxaristia Jul 22, 2026
ca65097
fix(redaction,worktrees): sort extra secret values by length descendi…
euxaristia Jul 23, 2026
d106116
fix(secrets,worktrees): restore Anthropic key redaction and handle le…
euxaristia Jul 23, 2026
91e859d
fix(secrets): add bash output redaction regression test for Anthropic…
euxaristia Jul 24, 2026
66d3152
fix(worktrees): touch worktree mtime on reuse to prevent stale pruning
euxaristia Jul 30, 2026
9e8ec47
fix(worktrees): properly handle os.Chtimes error on reused worktree path
euxaristia Jul 30, 2026
f384f4e
fix(worktrees,secrets): unlock using porcelain entry path, set RepoRo…
euxaristia Jul 31, 2026
b8783dd
fix(tools): correct redaction placeholder assertion in Anthropic key …
euxaristia Jul 31, 2026
29f1d87
test(worktrees): physicalize test paths for macOS tempdir symlink res…
euxaristia Jul 31, 2026
c9a347a
fix(secrets,worktrees): address CodeRabbit review findings
euxaristia Aug 1, 2026
7be0e6f
fix(secrets,worktrees): restore broad key redaction and legacy Clean …
euxaristia Aug 1, 2026
ebc83b7
fix(secrets,redaction): always redact known OpenAI key prefixes
euxaristia Aug 1, 2026
d2030d6
Preserve digit-free legacy keys during redaction.
euxaristia Aug 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions internal/cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type appDeps struct {
runSandboxSetupHelper func(path string, args []string, stdout io.Writer, stderr io.Writer) error
registerMCPTools func(context.Context, *tools.Registry, config.MCPConfig, mcp.RegisterOptions) (mcpToolRuntime, error)
prepareWorktree func(context.Context, worktrees.Options) (worktrees.Result, error)
releaseWorktree func(context.Context, worktrees.Options, string) error
detectVerifyPlan func(string) (verify.Plan, error)
runVerify func(context.Context, verify.Plan, verify.RunOptions) verify.Report
runSelfVerify func(context.Context, verify.Plan, selfverify.Options) selfverify.Report
Expand Down Expand Up @@ -187,6 +188,7 @@ func defaultAppDeps() appDeps {
return mcp.RegisterTools(ctx, registry, cfg, options)
},
prepareWorktree: worktrees.Prepare,
releaseWorktree: worktrees.Release,
detectVerifyPlan: verify.DetectPlan,
runVerify: verify.Run,
runSelfVerify: selfverify.Run,
Expand Down Expand Up @@ -532,6 +534,9 @@ func fillAppDeps(deps appDeps) appDeps {
if deps.prepareWorktree == nil {
deps.prepareWorktree = defaults.prepareWorktree
}
if deps.releaseWorktree == nil {
deps.releaseWorktree = defaults.releaseWorktree
}
if deps.detectVerifyPlan == nil {
deps.detectVerifyPlan = defaults.detectVerifyPlan
}
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/backends_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (

func TestRunBackendsJSONUsesLifecycleSnapshotWithoutConnectingMCP(t *testing.T) {
cwd := t.TempDir()
secret := "sk-proj-" + strings.Repeat("a", 24)
secret := "sk-proj-" + strings.Repeat("a", 23) + "0"
deps := appDeps{
getwd: func() (string, error) { return cwd, nil },
resolveMCPConfig: func(workspaceRoot string, _ bool) (config.MCPConfig, error) {
Expand Down Expand Up @@ -161,7 +161,7 @@ func TestRunBackendsTextAndHelp(t *testing.T) {

func TestRunBackendsDoctorJSONAndTextWithoutConnectingMCP(t *testing.T) {
cwd := t.TempDir()
secret := "sk-proj-" + strings.Repeat("b", 24)
secret := "sk-proj-" + strings.Repeat("b", 23) + "0"
deps := appDeps{
getwd: func() (string, error) { return cwd, nil },
resolveMCPConfig: func(workspaceRoot string, _ bool) (config.MCPConfig, error) {
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ var completionRoot = completionNode{
}},
{names: []string{"update"}},
{names: []string{"upgrade"}},
{names: []string{"worktrees", "worktree"}, children: leafNodes("prepare")},
{names: []string{"worktrees", "worktree"}, children: leafNodes("prepare", "release")},
{names: []string{"verify"}},
{names: []string{"trust"}, children: leafNodes("list", "remove")},
{names: []string{"eval"}, children: leafNodes("validate", "run", "bench")},
Expand Down
2 changes: 2 additions & 0 deletions internal/cli/completions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ func TestCompletionTreeCoversAliasesNestingAndCommonFlags(t *testing.T) {

assertCandidates(t, byPath[""], "sessions", "session", "plugins", "plugin", "worktrees", "worktree", "--add-dir", "--theme", "-p", "--prompt")
assertCandidates(t, byPath["exec"], "--model", "--cwd", "--worktree", "--output-format", "--resume", "--skip-permissions-unsafe")
assertCandidates(t, byPath["worktrees"], "prepare", "release")
assertCandidates(t, byPath["worktree"], "prepare", "release")
assertCandidates(t, byPath["daemon"], "start", "stop", "status", "run", "attach")
assertCandidates(t, byPath["mcp oauth"], "login", "logout", "status")
assertCandidates(t, byPath["sandbox grants"], "list", "allow", "deny", "revoke", "clear")
Expand Down
22 changes: 22 additions & 0 deletions internal/cli/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,33 @@ func runExec(args []string, stdout io.Writer, stderr io.Writer, deps appDeps) in
Name: options.worktreeName,
BaseDir: options.worktreeDir,
Now: deps.now,
// The worktree's lifetime is bound to this process (the deferred
// release below), so record the PID: if this process dies without
// releasing, Clean can expire the lease instead of skipping the
// locked worktree forever.
LeasePID: os.Getpid(),
})
if err != nil {
return writeExecFormatUsageError(stdout, stderr, options.outputFormat, err.Error())
}
workspaceRoot = preparedWorktree.Path
// When this run's own Prepare call took the worktree lock, its
// lifetime is bound to this function: release the lock once it returns
// so Clean can reclaim the worktree later if it goes stale. A reused
// worktree whose lock an external `zero worktrees prepare` caller
// still holds reports LockAcquired=false; releasing it here would
// clear that caller's lease and expose its workspace to Clean, so the
// matching release stays that caller's responsibility. A failed unlock
// leaves a lock Clean will permanently skip, so it must not pass
// silently; the run's primary result has already been emitted by the
// time the defer runs, so surface it as a diagnostic.
if preparedWorktree.LockAcquired {
defer func() {
if releaseErr := deps.releaseWorktree(context.Background(), worktrees.Options{Cwd: trustRoot}, preparedWorktree.Path); releaseErr != nil {
fmt.Fprintf(stderr, "zero: failed to release worktree lock on %s: %s\n", redactCLIString(preparedWorktree.Path), redactCLIString(releaseErr.Error()))
}
}()
}
}

registry := newCoreRegistry(workspaceRoot)
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/extensions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func TestRunPluginsListsJSONAndText(t *testing.T) {
}

func TestRunHooksListsRedactedJSONAndText(t *testing.T) {
secret := "sk-proj-" + strings.Repeat("a", 24)
secret := "sk-proj-" + strings.Repeat("a", 23) + "0"
result := hooks.LoadResult{
Config: hooks.Config{
Enabled: true,
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/hooks_manage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestRunHooksAddRejectsUnknownEvent(t *testing.T) {

func TestRunHooksAddJSONRedactsSecretArgs(t *testing.T) {
cwd := t.TempDir()
secret := "sk-proj-" + strings.Repeat("z", 24)
secret := "sk-proj-" + strings.Repeat("z", 23) + "0"
var stdout, stderr bytes.Buffer
code := runHooksAdd([]string{"h1", "--event", "beforeTool", "--command", "sh", "--arg", "-c", "--arg", "echo " + secret, "--json"}, &stdout, &stderr, hooksManageDeps(cwd))
if code != exitSuccess {
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/mcp_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ func TestRunMCPRemovePreservesUnrelatedConfigFields(t *testing.T) {
func TestRunMCPListRedactsURLCredentialsAndSensitiveQueryParams(t *testing.T) {
cwd := t.TempDir()
serverURL := "https://user:password@remote.example/mcp?access_token=secret-token&api_key=secret-key&safe=value#access_token=fragment-secret"
commandSecret := "sk-proj-" + strings.Repeat("a", 24)
commandSecret := "sk-proj-" + strings.Repeat("a", 23) + "0"
deps := appDeps{
getwd: func() (string, error) { return cwd, nil },
resolveMCPConfig: func(workspaceRoot string, _ bool) (config.MCPConfig, error) {
Expand Down
Loading
Loading