helpers: cwd-dependent forge-block resolution + unchecked exit codes read as empty data in caller glue #33

Closed
opened 2026-08-13 12:06:37 +00:00 by jbr870 · 1 comment
Owner

Observation

Helpers walk up from $PWD for a project CLAUDE.md with a forge: block. Invoked from outside the project tree (typically the session scratchpad dir), they die correctly — helper: no project CLAUDE.md with a forge: block found from /tmp/claude-1000/... upward on stderr, non-zero exit, nothing on stdout (_lib.sh _die, verified at HEAD) — but the calling glue routinely swallows that: X="$(helper ...)" with the error unexamined, then jq over the empty string yields null, which reads exactly like "no such comment/decision".

Observed in three of the five 2026-08-12 verity transcripts (slot1 ×1, slot3 ×3, slot6 ×1), and reported first-hand by the verity-release session the same week: "a bare null on stdout — which reads exactly like 'no such decision' if you're only parsing stdout. I hit this earlier today."

Recurrence

4 sessions (3 transcript-confirmed + 1 first-hand). The helper behaves correctly; the gap is caller-side: no canonical rule says helper invocations must run with cwd inside the project (or set DEVWORK_PROJECT_ROOT) and must check the exit code before parsing stdout.

Hypothesis

One rule in the canonical helper-invocation text (phase-outcome.md preamble, where "invoke the shims, never inline adapter mechanics" already lives): run bin/* helpers with the project as cwd — after any cd away (scratchpad work), cd back or set DEVWORK_PROJECT_ROOT; and never parse a helper's stdout without its exit status (out="$(helper ...)" || { …fail loudly… }). Emitted-glue examples in skills should model the rc check.

Suggested lane

meta-amend (one canonical-doc rule + example shape).

Filed by /meta-retrospective 2026-08-13 (transcript sweep over tailscale SSH).

## Observation Helpers walk up from `$PWD` for a project CLAUDE.md with a `forge:` block. Invoked from outside the project tree (typically the session scratchpad dir), they die correctly — `helper: no project CLAUDE.md with a forge: block found from /tmp/claude-1000/... upward` on stderr, non-zero exit, nothing on stdout (`_lib.sh` `_die`, verified at HEAD) — but the calling glue routinely swallows that: `X="$(helper ...)"` with the error unexamined, then `jq` over the empty string yields `null`, which reads exactly like "no such comment/decision". Observed in three of the five 2026-08-12 verity transcripts (slot1 ×1, slot3 ×3, slot6 ×1), and reported first-hand by the verity-release session the same week: "a bare `null` on stdout — which reads exactly like 'no such decision' if you're only parsing stdout. I hit this earlier today." ## Recurrence 4 sessions (3 transcript-confirmed + 1 first-hand). The helper behaves correctly; the gap is caller-side: no canonical rule says helper invocations must run with cwd inside the project (or set `DEVWORK_PROJECT_ROOT`) and must check the exit code before parsing stdout. ## Hypothesis One rule in the canonical helper-invocation text (`phase-outcome.md` preamble, where "invoke the shims, never inline adapter mechanics" already lives): run `bin/*` helpers with the project as cwd — after any `cd` away (scratchpad work), `cd` back or set `DEVWORK_PROJECT_ROOT`; and never parse a helper's stdout without its exit status (`out="$(helper ...)" || { …fail loudly… }`). Emitted-glue examples in skills should model the rc check. ## Suggested lane meta-amend (one canonical-doc rule + example shape). *Filed by /meta-retrospective 2026-08-13 (transcript sweep over tailscale SSH).*
Author
Owner

Outcome — amended (commit e3a5369)

Added the third invocation rule to phase-outcome.md's preamble blockquote (the canonical home of "never inline adapter mechanics" and "jq only"): run helpers with the project as cwd (cd back or export DEVWORK_PROJECT_ROOT after any scratchpad excursion), and never trust an empty/null stdout without rc 0 — with the capture-and-check fence as the required shape.

Gate: lint clean. Desk-check vs originating scenario: slot3's three scratchpad-cwd failures produced stderr dies that caller glue read as empty results; with this rule the capture-and-check shape turns each into a loud rc≠0 stop naming stderr. Validates on the next dogfood runs.

## Outcome — amended (commit e3a5369) Added the third invocation rule to phase-outcome.md's preamble blockquote (the canonical home of "never inline adapter mechanics" and "jq only"): run helpers with the project as cwd (cd back or export DEVWORK_PROJECT_ROOT after any scratchpad excursion), and never trust an empty/`null` stdout without rc 0 — with the capture-and-check fence as the required shape. Gate: lint clean. Desk-check vs originating scenario: slot3's three scratchpad-cwd failures produced stderr dies that caller glue read as empty results; with this rule the capture-and-check shape turns each into a loud rc≠0 stop naming stderr. Validates on the next dogfood runs.
Sign in to join this conversation.
No description provided.