Skip to content

fix(cli): harden adf-patch / serve / hook error paths surfaced by telemetry triage#195

Open
stackbilt-admin wants to merge 2 commits into
mainfrom
fix/telemetry-triage-cli-error-paths
Open

fix(cli): harden adf-patch / serve / hook error paths surfaced by telemetry triage#195
stackbilt-admin wants to merge 2 commits into
mainfrom
fix/telemetry-triage-cli-error-paths

Conversation

@stackbilt-admin
Copy link
Copy Markdown
Member

Origin

Found by sweeping local .charter/telemetry/events.ndjson across org repos that run the Charter CLI. Telemetry records command metadata only (no content); the actionable signal is the exit code + errorName — an uncaught throw (TypeError/Error/unnamed) at exit 2 is a real crash, a CLIError is a clean rejection.

Changes, by confidence

Lead Outcome Confidence
adf patch --ops Fixed. Malformed ops ([null], non-object) crashed captureOpBefore with an uncaught TypeError because it ran before applyPatches validated. Now validated against a Zod PatchOperationArraySchema at the parse boundary → clean CLIError, file untouched. Reproduced the exact crash before & after. ✅ Confirmed (reproduced)
serve --ai-dir Hardened. "Not found" errors now echo the resolved --ai-dir/manifest path, so a wrong path is obvious instead of retried blind. 🟡 Plausible cause, low-risk
hook install Hardened. fs.mkdirSync/writeFileSync and git-dir resolution failures escaped as raw Error; both now wrap into a CLIError. The widened guard makes "hook install yields clean errors, not raw throws" true regardless of which surface fails. 🟡 Root cause unconfirmed (no stack in telemetry)
stamp-policies No change — investigated and confirmed working as designed (clean handled exit-2 with recovery hint).

Design notes

  • The Zod ops schema lives in the CLI, keeping @stackbilt/adf zero-dep. A compile-time drift guard asserts z.infer stays mutually assignable with adf's public PatchOperation type — they can't silently diverge. Migrating the schema into @stackbilt/adf as source-of-truth is a deliberate follow-up, not folded in here.
  • zod ^3.24.1 declared in the CLI package.json (was already imported but undeclared).

Verification

  • tsc --noEmit clean
  • Full suite 575/575 (49 files), +7 new tests covering all four adf-patch repro cases, both serve guards, and both hook failure surfaces (the hook write-failure test uses a real fs failure, not a mock)

🤖 Generated with Claude Code

Kurt Overmier and others added 2 commits June 1, 2026 17:05
A malformed `--ops` element (e.g. `[null]` or a non-object) crashed the
before-capture pass with an uncaught TypeError instead of a clean error,
because `captureOpBefore` ran before `applyPatches` validated the ops.
Surfaced by sweeping local `.charter/telemetry` across org repos — a live
TypeError in `adf patch --ops` had gone unreported for months.

Validate the parsed ops against a Zod `PatchOperationArraySchema` right
after JSON.parse, before any capture. Malformed ops now produce a clean
CLIError naming the offending `ops[i]`; the target file is left untouched.

The schema lives in the CLI (keeps `@stackbilt/adf` zero-dep) with a
compile-time drift guard asserting `z.infer` stays mutually assignable with
adf's public `PatchOperation` type — so the two cannot silently diverge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two error-path gaps found via local telemetry triage:

- `serve`: when `--ai-dir` (or its manifest) is missing, the error now
  echoes the resolved path. Repos showed `serve --name --ai-dir` rejected
  and retried-then-abandoned because the message never said which path was
  checked.

- `hook install`: filesystem failures creating/writing the hook file, and
  git failures resolving the hooks dir, escaped as raw Errors. Both are now
  wrapped into a CLIError with an actionable message.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant