Skip to content

docs: Plan spec for agent CLI ergonomics (bulk ops, output, sync)#176

Open
jlevy wants to merge 16 commits into
mainfrom
claude/practical-darwin-4qfq5m
Open

docs: Plan spec for agent CLI ergonomics (bulk ops, output, sync)#176
jlevy wants to merge 16 commits into
mainfrom
claude/practical-darwin-4qfq5m

Conversation

@jlevy

@jlevy jlevy commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a Draft planning spec, docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md, for improving tbd's CLI ergonomics for agents.

It grew out of observing agents drive tbd through brittle bash: for loops over issue IDs, 2>&1 | tail -1 on every command, hand-rolled echo "=== ... ===" progress headers, and a --no-sync ... ; tbd sync ritual. Each contortion maps to a missing tbd primitive.

What's in the spec

  • Problem Catalog (P1–P9): the full set of common problems, each tied to the bash symptom it causes (single-target verbs, no select-and-act, untrusted output, no visible record, murky sync model, inline-quoting hazards, inconsistent arg conventions, no preview discipline, no "delivered-by-PR" provenance).
  • Phase 1 — quick wins for the current release (additive, backward-compatible): variadic <ids...> on close/reopen/update/show; a bulk summary line + --json results array + accurate "unsynced changes" hint; --reason-file/stdin bodies; and an honest opt-in --sync.
  • Phase 2 — broader follow-on: query-driven --where mutation (reusing the list filter grammar, with mandatory preview) and a tbd apply transaction file generalizing import.

Grounded throughout with file:line cites from the current command sources. One design decision is already resolved in the spec: keep stage-then-publish syncing, make it self-revealing via an unsynced-changes hint, and add opt-in --sync (no per-command auto-sync).

Notes

  • Docs-only; no code changes. Status: Draft for review.
  • Distinct from the existing plan-2026-06-03-tbd-agent-cli-guideline-improvements.md (that one improves the guideline about writing agent skills; this one changes tbd's runtime CLI behavior).
  • Implementation will be tracked as tbd beads (epic + children) linked to this spec via --spec.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b


Generated by Claude Code

Plan to reduce agent bash contortions (for-loops over issues, output truncation, the --no-sync/sync ritual) via bulk/multi-target verbs, a trustworthy output contract, and an honest stage-then-publish sync model. Phase 1 is backward-compatible quick wins for the current release; Phase 2 maps query-driven mutation and a tbd apply transaction file.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
@deepsource-io

deepsource-io Bot commented Jun 13, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in b156bfa...05573d7 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Secrets Jun 14, 2026 11:21p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown

Coverage Report for packages/tbd

Status Category Percentage Covered / Total
🔵 Lines 34.14% 2947 / 8630
🔵 Statements 34.03% 3056 / 8978
🔵 Functions 39.44% 465 / 1179
🔵 Branches 30.18% 1411 / 4675
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/tbd/src/cli/cli.ts 73.87% 34.09% 88.88% 73.87% 186-201, 205-216, 240-241, 246-250, 278, 297-298
packages/tbd/src/cli/commands/close.ts 2.17% 0% 0% 2.32% 31-117, 128-129
packages/tbd/src/cli/commands/create.ts 1.72% 0% 0% 1.72% 44-197, 214-220
packages/tbd/src/cli/commands/reopen.ts 1.96% 0% 0% 2.08% 33-131, 142-143
packages/tbd/src/cli/commands/update.ts 0.36% 0% 0% 0.41% 48-569, 587-595
packages/tbd/src/cli/lib/body-input.ts 68.42% 80.95% 50% 68.42% 32-36, 66-67
packages/tbd/src/cli/lib/bulk.ts 100% 92.85% 100% 100%
packages/tbd/src/cli/lib/context.ts 86.66% 73.07% 80% 86.66% 79, 99
packages/tbd/src/cli/lib/data-context.ts 25.42% 20.45% 35.71% 24.07% 97-102, 106-109, 123-171, 198-222, 238, 241-249, 265-266, 300-312
packages/tbd/src/utils/lockfile.ts 83.87% 74.07% 85.71% 84.48% 134-142, 162, 247
Generated in workflow #1032 for commit 05573d7 by the Vitest Coverage Report Action

@jlevy

jlevy commented Jun 13, 2026

Copy link
Copy Markdown
Owner Author

Senior Engineering Review

I reviewed PR #176 (f7f18b6) from the checked-out branch, including the new design spec and the surrounding CLI implementation paths it cites: close, reopen, update, show, import, output handling, command context, sync, locking, and the existing tryscript coverage. CI is green and the PR is docs-only, but I would revise the spec before using it as an implementation guide. The high-level direction is right: the current agent ergonomics pain is real, and a small additive Phase 1 is the right shape. The main issues are places where Phase 1 is less quick/safe than the spec currently implies.

Findings

  1. [P1] --sync needs a tri-state CLI/context design before implementation.

    The spec makes Phase 1 --sync opt-in while keeping no per-command auto-sync. But the current global option surface only has --no-sync, and getCommandContext() stores sync: opts.sync !== false, which means the absence of --no-sync becomes true. If implementation simply adds sync behavior based on ctx.sync, every mutator will auto-sync by default, contradicting the resolved design.

    Relevant code/spec:

    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:204-211
    • packages/tbd/src/cli/cli.ts:66-73
    • packages/tbd/src/cli/lib/context.ts:37-48

    Suggested fix: explicitly specify a tri-state, for example syncMode: 'unspecified' | 'sync' | 'no-sync', or separate syncRequested from legacy noSyncRequested. Also decide whether --sync is global or per-mutator. Given the sync command performs network operations and uses the data-sync lock, I would also specify the lock boundary: apply all issue writes under one lock, release it, then run the existing sync path or call a shared internal helper without re-entering the same non-reentrant lock.

  2. [P1] The unsynced-changes hint is not visible if Phase 1 literally reuses import's nudge.

    The spec repeatedly says to reuse the import nudge (Run tbd sync...). In code, that nudge is emitted through this.output.info(), which is only visible with --verbose or --debug; it is hidden in default text mode. That does not satisfy the goal of making the stage-then-publish model self-revealing.

    Relevant code/spec:

    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:197-200
    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:204-211
    • packages/tbd/src/cli/commands/import.ts:282-294
    • packages/tbd/src/cli/lib/output.ts:398-406

    Suggested fix: define a new visible-but-suppressible notice contract for unsynced changes. For example: default text mode prints one additional notice line unless --quiet or --json; JSON includes sync: { pending: true, hint: ... }; quiet remains silent. The current info() channel is not enough.

  3. [P1] already-X skip semantics conflict with backward compatibility for reopen.

    The spec says single-ID behavior remains exactly as today, but also says already-X is a reported skip, not a failure. For close, this aligns with current idempotent behavior. For reopen, it does not: reopening an open issue currently throws and is covered by CLI golden tests as exit code 1.

    Relevant code/spec/tests:

    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:186-196
    • packages/tbd/src/cli/commands/close.ts:54-58
    • packages/tbd/src/cli/commands/reopen.ts:49-52
    • packages/tbd/tests/cli-crud.tryscript.md:579-588

    Suggested fix: resolve this in the spec. Options: keep single-target reopen failure unchanged and make "already-open" a bulk-only skip, or intentionally change reopen to idempotent and call out the backward-compatibility break. For a quick safe Phase 1, I would preserve single-target behavior and only summarize skips for bulk paths where the intent is batch completion.

  4. [P2] show does not fit the Phase 1 mutator/output/sync contract.

    The spec includes show alongside close/reopen/update, but show is read-only, uses loadFullContext() without the write lock in steady state, and intentionally emits multi-line YAML/Markdown plus optional parent context. It does not share the bulk summary, unsynced hint, or --sync surface.

    Relevant code/spec:

    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:186-188
    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:242-257
    • packages/tbd/src/cli/commands/show.ts:107-188

    Suggested fix: split bulk show out of Phase 1 or give it a separate read-only design. A safe version might be tbd show A B C --json returns an array and text mode renders each issue with a delimiter. That is useful, but it is not part of the same "one-line success and one sync" implementation slice.

  5. [P2] The update --status closed example bypasses close semantics.

    The spec's bulk update example uses tbd update A B C --status closed --add-label delivered. Current update simply assigns issue.status; it does not set closed_at or close_reason. close does. If agents learn to use update --status closed as a bulk close, issue data will be semantically incomplete.

    Relevant code/spec:

    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:189-192
    • packages/tbd/src/cli/commands/update.ts:82-90
    • packages/tbd/src/cli/commands/close.ts:65-70

    Suggested fix: remove that example or specify transition semantics for status changes. For Phase 1, I would keep bulk closure on close only and limit bulk update to field updates that do not have lifecycle side effects, unless status transition handling is deliberately added and tested.

  6. [P2] The --quiet contract is internally inconsistent and conflicts with existing behavior.

    Current --quiet suppresses success output, and there is a golden test for a successful quiet create producing no output. The spec says quiet should "collapse multi-target output to a single line," while the testing section says "exactly one line (or nothing)." Those are materially different contracts.

    Relevant code/spec/tests:

    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:248-249
    • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:276-277
    • packages/tbd/src/cli/lib/output.ts:378-386
    • packages/tbd/tests/cli-advanced.tryscript.md:443-448

    Suggested fix: keep --quiet as silent-on-success. Make default text mode the one-line human summary and --json the machine contract. This preserves existing behavior and still removes the need for 2>&1 | tail -1.

Phase 1 Scope Notes

I would tighten Phase 1 to the smallest ergonomic slice that is both safe and immediately useful:

  • Variadic IDs for close and reopen, plus a carefully constrained bulk update
  • Validate-all-then-apply for unknown IDs by default
  • --ignore-missing as an explicit best-effort escape hatch
  • Bulk JSON result shape with per-ID status and a summary
  • Default text-mode one-line summary for multi-target writes
  • Shared file/stdin body reader for --reason-file, --reason -, --description -, and --notes -
  • Visible unsynced-change hint with explicit stream/suppression rules
  • --sync only after the context tri-state and lock/network boundary are specified

I would defer bulk show unless it gets its own read-only behavior definition.

Phase 2 Notes

The Phase 2 ideas are directionally sound, especially tbd apply for mixed create/update/close batches. That said, --where currently depends on "the existing list filter grammar," but in code that grammar is an implementation-local ListOptions shape inside list.ts, not a reusable selector abstraction. Before building --where, the design should call out a selector module shared by list and mutators, with tests proving read and write selection match.

Relevant code/spec:

  • docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md:215-223
  • packages/tbd/src/cli/commands/list.ts:32-49
  • packages/tbd/src/cli/commands/list.ts:191-259

For tbd apply, the instinct to generalize import is good, but it will need a real transaction/rollback story if it promises atomicity across create, update, close, ID mapping updates, parent child_order_hints, and sync. A failed write after mapping changes or parent hint updates could otherwise leave partial state.

Documentation Notes

The spec correctly identifies stale/incorrect sync docs, and implementation should update them in the same PR as Phase 1. There are multiple existing user-facing references that still describe --no-sync and auto_sync as real issue-write behavior:

  • packages/tbd/docs/tbd-docs.md:805-827
  • packages/tbd/docs/tbd-docs.md:1094-1098
  • packages/tbd/docs/tbd-docs.md:1263-1267
  • packages/tbd/docs/tbd-design.md:1608-1646
  • packages/tbd/docs/tbd-design.md:2936-2961

The Phase 1 implementation plan mentions the manual and tbd prime, but the architecture/design doc should be included too, otherwise the repo will continue to carry contradictory sync semantics.

Testing Notes

The testing strategy is mostly right. I would add explicit tests for:

  • --sync absent does not sync, --sync does sync, legacy --no-sync does not sync and is documented/no-op for issue writes
  • lock boundary around bulk write plus sync, especially avoiding nested lock acquisition
  • reopen already-open behavior for single-ID and bulk paths, once the spec resolves it
  • update --status closed either being rejected in bulk or setting lifecycle metadata correctly
  • default text vs --quiet vs --json output contracts
  • stdin bodies for --reason -, --description -, and --notes -, including shell-sensitive text ($, backticks, quotes)

CI Status

Current PR checks are green: Ubuntu, macOS, Windows, Coverage & Lint, Benchmark, and DeepSource Secrets. I did not run local tests; this worktree has no installed node_modules, and the PR is docs-only.

Bottom Line

The design is worth pursuing, but Phase 1 needs a few decisions made explicit before implementation. The highest-risk pieces are sync flag semantics, visible unsynced hints, reopen compatibility, and excluding or separately designing bulk show. Tightening those would make the quick-win phase genuinely quick and reduce the chance of accidentally changing core CLI contracts while improving agent ergonomics.

Incorporate senior review feedback tightening Phase 1: tri-state sync intent with an explicit lock boundary, a visible unsynced hint via output.notice() (not verbose-only info()), preserved single-ID close/reopen behavior (skips are bulk-only), bulk show split into a separate read-only design, update --status closed excluded from bulk closure, an aligned --quiet contract (silent on success), and sync doc fixes extended to tbd-design.md.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b

jlevy commented Jun 13, 2026

Copy link
Copy Markdown
Owner Author

Thanks — this is a genuinely useful review. I verified each finding against the code and adopted all of them in 136d178 (spec revised; implementation next).

  1. --sync tri-state + lock boundary — spec now mandates modeling intent as unspecified | sync | no-sync (so absence of --no-sync ≠ sync), plus a lock boundary: write all issues under one lock, release it, then run sync (no nested non-reentrant lock).
  2. Visible unsynced hint — confirmed import's nudge is output.info() (verbose-only). Spec routes the hint through output.notice() (visible by default, suppressed by --quiet/--json); --json carries sync: { pending, hint }.
  3. reopen compatibility — confirmed the cli-crud.tryscript.md exit-1 golden. Single-ID behavior is preserved exactly (close idempotent, reopen errors); already-X skips are bulk-only (2+ IDs).
  4. Bulk show — pulled out of the mutator slice into a separate, optional read-only design (delimited text / --json array; no lock/summary/sync contract). Tracked separately.
  5. update --status closed — removed from the bulk example; bulk closure goes through close only (since update doesn't set closed_at/close_reason); bulk update limited to side-effect-free fields.
  6. --quiet — kept silent-on-success (preserves the quiet-create golden); default text = one-line summary, --json = machine contract.

Also folded in: Phase 2 --where now requires extracting a shared selector module from list.ts's impl-local ListOptions; tbd apply flags the transaction/rollback boundary; doc fixes extended to tbd-design.md (with the specific stale --no-sync/auto_sync locations); testing strategy expanded with your cases; default atomicity resolved to fail-closed + --ignore-missing.

Implementing the tightened slice next: variadic close/reopen + constrained bulk update, validate-all-then-apply, --ignore-missing, shared file/stdin reader, bulk JSON + one-line summary, visible hint — deferring --sync execution and bulk show per your scoping.


Generated by Claude Code

claude added 14 commits June 13, 2026 20:59
tbd close now accepts multiple IDs and processes them under one lock: validate-all-then-apply (fail-closed on unknown IDs, --ignore-missing to skip), a one-line summary, a structured --json results array with sync:{pending,hint}, and a visible unsynced-changes notice. Single-ID behavior is preserved byte-for-byte. Adds shared cli/lib/bulk.ts helpers and a cli-bulk-mutation golden; updates the close help-text golden.

Part of Phase 1 of the agent CLI ergonomics spec (tbd-38ov).

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Covers resolveAllIds (order preservation, missing collection, empty input), summarizeBulk (changed/skipped/missing tallies including reopened/updated), and toJsonResult (skippedReason omission). These pure helpers back the variadic close path, which is otherwise exercised only as a tryscript subprocess and thus invisible to vitest coverage.

Implements tbd-xxe4 (part of Phase 1, tbd-38ov).

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
reopen now accepts <ids...>. A single ID preserves the legacy output exactly, including the hard error when the issue is not closed (cli-crud golden, exit 1). Two or more IDs are a bulk op: validate-all-then-apply with fail-closed on unknown IDs (--ignore-missing to override), already-open reported as a skip (bulk-only), one-line summary + --json results array + visible unsynced-changes hint.

Consistency fix in close: a single-ID read failure now throws the legacy NotFoundError instead of silently returning, matching reopen and pre-PR close.

Adds bulk reopen goldens; cli-crud single-ID contract unchanged (76 pass).

Implements tbd-n6eo (part of Phase 1, tbd-38ov).

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
update now accepts <ids...>. A single ID dispatches to the unchanged legacy path, preserving all structural side effects (re-parenting, spec propagation, child ordering). Two or more IDs apply only shared, side-effect-free fields: --priority, --assignee, --type, --add-label, --remove-label, --due, --defer.

Per-ID-only and lifecycle flags are rejected in the bulk path (--from-file, --title, --description, --notes, --notes-file, --status, --parent, --spec, --child-order). Bulk closure stays on close so closed_at/close_reason are never left inconsistent, per the PR #176 review. validate-all-then-apply with --ignore-missing; one-line summary + --json results + visible unsynced hint; --quiet silent on success.

Also updates the tbd --help golden (cli-setup) for the new reopen/update <ids...> signatures. That golden was the Coverage & Lint failure CI flagged on 03ab2d6: the pre-push hook runs vitest only, not the tryscript suite, so the help-text drift slipped through. Adds bulk update goldens.

Implements tbd-jud0 (part of Phase 1, tbd-38ov).

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
New cli/lib/body-input.ts: resolveBodyInput resolves a free-text body from inline text, a companion *-file flag, or stdin via the '-' convention. Rejects inline+file together and two stdin readers in one command, so agents can pass shell-sensitive text ($, backticks, quotes) without escaping.

Wired in: close/reopen gain --reason-file and '-' stdin for --reason; create's -d/--description and -f/--file accept '-'; update's --description/--notes accept '-' and --notes-file is supported. close/reopen/create resolve bodies before the data context; update now does too via resolveBodyOptions (the context changes cwd, so relative file paths and stdin must be read up front).

Tests: body-input unit tests + cli-body-input goldens round-tripping shell-sensitive text through file and stdin; cli-help-all updated for --reason-file.

Filed tbd-649r for a pre-existing quirk found while testing: working notes persist to disk but show --json returns null on the first notes write (independent of this change).

Implements tbd-hfk0 (part of Phase 1).

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Per the PR #176 review (Documentation Notes): issue mutations stage to the local tbd-sync worktree and 'tbd sync' publishes them. There is no per-command auto-sync, so --no-sync has no effect on issue writes and auto_sync (default false) is not applied to them. Corrected the stale claims in tbd-docs.md (global options, config example, deletion-recovery note) and tbd-design.md (config schema, command synopses, write-flow step, bd-compat table).

Also documented Phase 1 in the tbd-design.md synopses: variadic close/reopen/update <ids...>, --ignore-missing, --reason-file and the '-'/stdin convention for --reason/-d/--description/--notes, and the constrained bulk update (shared fields only; lifecycle via close/reopen).

Implements tbd-6zqx / tbd-dc49 (Phase 1 docs).

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
The global --no-sync help text claimed 'Skip automatic sync after write operations', but nothing auto-syncs after issue writes — they stage to the local tbd-sync worktree and 'tbd sync' publishes. Updated to 'Accepted; issue writes stage locally', consistent with the doc corrections in 35b8c73 (PR #176 review). Propagated to the 11 --help golden snapshots across 7 tryscript files; full suite green (950).

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
`--no-sync` set a `ctx.sync` flag that no command ever read, so it was a complete no-op for issue writes — which always stage to the local tbd-sync worktree and are published by `tbd sync`. Its only effect was encouraging the `--no-sync … ; tbd sync` cargo-cult among agents.

Removed: the global option, the dead `ctx.sync` context field and its test mock, all served-doc references (manual example + option list; tbd-design.md embedded --help snapshots and the bd-compat table row, now marked Dropped), and the no-op flag from 8 test invocations. `tbd <verb> --no-sync` now errors as an unknown option — the honest signal.

tbd is not moving to an always-auto-sync model; an opt-in `--sync` may come later but is non-essential. Updates the agent-cli-ergonomics plan's sync decision (a → c). Validated: typecheck clean, vitest 1339 passed, tryscript 950 passed.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
The emitBulkSummary method was copy-pasted verbatim in close, reopen, and update. Extract it into a single emitBulkSummary(output, results, {verb, skippedNote}) in cli/lib/bulk.ts. The only per-verb differences were the past-tense verb word and the skipped-clause phrase, now parameters; update never produces a skipped result, so its output is unchanged. Behavior-preserving (cli-bulk-mutation golden + bulk unit tests green). Implements tbd-ojl1.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
The worktree auto-heal (#135) and config-format-migration stderr notices printed even under --quiet, so agents still had to wrap commands in '2>&1 | tail -1'. getCommandContext now records the active --quiet process-wide (context.ts) and both notify functions early-return when it is set, covering all callers (loadDataContext, create, and the deep migration path) without threading the flag through withDataSyncContext's ~18 call sites. Adds quiet-notices.test.ts. Implements tbd-29k3.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
The bulk close/reopen/update contract, atomicity, --ignore-missing, quiet/json output, single-ID backward-compat, and file/stdin bodies were already covered by cli-bulk-mutation, cli-crud, cli-body-input, and the bulk unit tests. The one missing lock was the removed global --no-sync: add goldens asserting close and update now reject it as an unknown option (exit 1). Implements tbd-xc4f; tbd-05mw (reopen/update bulk goldens) was already satisfied by cli-bulk-mutation.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Document the Phase 1 agent contract in the manual: multi-ID bulk close/reopen/update under one lock, the one-line summary, the --json {results,summary,sync} shape, what --quiet guarantees, --reason-file plus the shared - stdin convention, fail-closed validation with --ignore-missing, and stage-then-publish (with --no-sync removed). Adds a CHANGELOG entry and ticks the Phase 1 checklist in the spec. Implements tbd-dc49.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Restores bulk.ts unit coverage after the tbd-ojl1 extraction moved emitBulkSummary out of the command files. Covers the text summary line (changed/skipped/missing clauses + id list + visible sync hint), the no-skip verb path (update), and the --json results/summary/sync.pending contract including the no-change case.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
withLockfile's mkdir acquire path threw on any non-EEXIST error, so a transient Windows EPERM/EBUSY/EACCES (raised when mkdir races a concurrent rmdir of the same lock directory) surfaced as a hard failure -- the flaky lockfile.test.ts 'serializes concurrent access' on windows-latest. Mirror the acquire side on removeLockDir's existing release-side tolerance: retry the same transient codes a small bounded number of times (5), resetting the budget whenever a real EEXIST shows the lock is genuinely held. A persistent failure (e.g. a genuinely unwritable parent dir, which tbd doctor reports) still propagates after the budget, and non-transient codes (ENOSPC, ENOENT) throw immediately, so the deliberate unwritable-lock detection is preserved. Adds lockfile-acquire-eperm.test.ts (mocked fs, platform-agnostic). Separate from the agent-CLI-ergonomics work on this branch.

https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants