---
name: diagnose-friction
description: >-
  Wrap a shell command with diagnostic capture. On failure, print static
  suggestions and append a structured `friction` record to the per-Story
  signals.ndjson stream. Use whenever a script in the orchestration loop
  invokes a tool whose failure shape we want the analyzer to attribute.
allowed_tools:
  - Bash
  - Read
---

# diagnose-friction

## Policy Capsule

- Invoke via the wrapping CLI `node .agents/scripts/diagnose-friction.js --task <id> --story <id> --epic <id> --cmd <command args...>`; this is the single supported entry point.
- Pass the wrapped command's stdout and stderr through **unchanged** — never reformat, redact, or buffer in a way that loses the original failure shape.
- Never mutate the wrapped command's exit code. The Skill observes; the caller decides whether the failure is fatal.
- Operate as **best-effort observation**: a write failure on the signals stream MUST NOT halt the runner. A missing signal is preferable to a stalled wave.
- On non-zero exit append a `friction` NDJSON record (`kind`, `ts`, `category`, `detail`, `exitCode`) only through the signals writer helper — never open `signals.ndjson` directly.
- Resolve Story / Epic context from `--story` and `--epic` first; fall back to parsing `parent: #<storyId>` + `Epic: #<epicId>` from the Task body when only `--task` is supplied.
- Do **not** post GitHub comments from this Skill. Friction is local NDJSON in v5; the structured-comment surface belongs to `analyze-execution`.
- Categorize failures deterministically (rebase abort, test-suite name, lint category, etc.) so the downstream analyzer can attribute friction without re-running the command.

## Role

Diagnostic interceptor. Captures the failure shape of a wrapped command
and persists it as a structured signal so `analyze-execution` can
attribute friction back to the Story / Epic without re-running the
command.

## When to use

Any orchestration call site whose failure mode is informative
(rebase aborts, test failures with classifiable suite names, lint
errors with stable categories). The wrapping script today is
`diagnose-friction.js`; this Skill documents the contract for callers
that want to dispatch via the Skill tool rather than spawn the CLI.

## Inputs

- `--cmd <command args...>` — the command to invoke and observe.
- `--story <id>` / `--epic <id>` (optional) — when both can be
  resolved, the Skill appends a `friction` signal to
  `<tempRoot>/epic-<eid>/story-<sid>/signals.ndjson` on non-zero exit.
- `--task <id>` (optional) — used as a fallback so the body parser can
  recover `parent: #<storyId>` + `Epic: #<epicId>` when neither flag
  was supplied.

## Outputs

- The wrapped command's stdout / stderr is passed through unchanged.
- On non-zero exit: a `friction` NDJSON record (kind, ts, category,
  detail, exitCode) is appended via the signals writer.
- No GitHub comments are posted — friction is a local NDJSON signal in
  v5. The analyzer (`analyze-execution`) reads the stream out-of-band.

## Procedure

```bash
node .agents/scripts/diagnose-friction.js \
  --task <id> --story <id> --epic <id> \
  --cmd <command args...>
```

The Skill's contract is "best-effort observation" — the wrapping
script never halts the runner because of a write failure. A missing
signal is preferable to a halted runner.

## Constraints

- Do **not** post GitHub comments from this Skill. Friction is local
  NDJSON in v5; the comment surface is reserved for
  `analyze-execution`'s structured rollups.
- Do **not** mutate the wrapped command's exit code. The Skill's job
  is observation; the caller decides whether the failure is fatal.
- Do **not** open `signals.ndjson` directly — use the signals writer
  helper so the file format and warn-once policy stay consistent.
