CLAUDE.md portability baseline: zsh glob nomatch aborts and multi-line quoting hazards are missing #29

Closed
opened 2026-08-13 11:18:36 +00:00 by jbr870 · 2 comments
Owner

Observation

Two zsh hazards in skill-emitted glue recur across runs and are absent from the CLAUDE.md portability baseline (which covers mapfile, reserved variables, and python/node glue, but not these):

  1. Unquoted-glob nomatch aborts. Under zsh, an unmatched glob aborts the whole compound command before redirections apply — 2>/dev/null does not save it:
    • verity-slot2-148: "zsh unmatched-glob aborts the whole compound command despite 2>/dev/null (x2)"
    • verity-slot2-167: "unquoted grep --include=* glob fails under zsh 'no matches found' (3x, three different agents)"
    • verity-slot3-113-resume: "zsh nomatch glob aborts in orchestrator shell (2x)"
  2. Multi-line inline quoting failures. verity-slot1-42: "(eval):20: unmatched "" in integrate's phase-outcome fence; recovered by writing a .sh file and running it with bash — which is also the general escape hatch worth naming.

Recurrence

Hazard 1: 3 independent runs. Hazard 2: 1 run, but same root cause (glue executed by the operator's login shell) and same fix location.

Hypothesis

Amend the CLAUDE.md portability baseline with both rules: (a) always quote glob patterns meant for the tool, not the shell (--include='*.ts'), and never rely on redirection to survive nomatch; (b) for any fence beyond a few lines or with nested quoting, write a temp .sh and run it with bash explicitly rather than pasting a compound command. Extend scripts/lint-conventions.sh where mechanically checkable (unquoted --include=* / --exclude=* forms are grep-able).

Suggested lane

meta-amend (CLAUDE.md text + lint extension).

Filed by /meta-retrospective 2026-08-13.

## Observation Two zsh hazards in skill-emitted glue recur across runs and are absent from the CLAUDE.md portability baseline (which covers `mapfile`, reserved variables, and python/node glue, but not these): 1. **Unquoted-glob `nomatch` aborts.** Under zsh, an unmatched glob aborts the whole compound command before redirections apply — `2>/dev/null` does not save it: - verity-slot2-148: "zsh unmatched-glob aborts the whole compound command despite 2>/dev/null (x2)" - verity-slot2-167: "unquoted grep --include=* glob fails under zsh 'no matches found' (3x, three different agents)" - verity-slot3-113-resume: "zsh nomatch glob aborts in orchestrator shell (2x)" 2. **Multi-line inline quoting failures.** verity-slot1-42: "(eval):20: unmatched \"" in integrate's phase-outcome fence; recovered by writing a .sh file and running it with bash — which is also the general escape hatch worth naming. ## Recurrence Hazard 1: 3 independent runs. Hazard 2: 1 run, but same root cause (glue executed by the operator's login shell) and same fix location. ## Hypothesis Amend the CLAUDE.md portability baseline with both rules: (a) always quote glob patterns meant for the tool, not the shell (`--include='*.ts'`), and never rely on redirection to survive `nomatch`; (b) for any fence beyond a few lines or with nested quoting, write a temp `.sh` and run it with `bash` explicitly rather than pasting a compound command. Extend `scripts/lint-conventions.sh` where mechanically checkable (unquoted `--include=*` / `--exclude=*` forms are grep-able). ## Suggested lane meta-amend (CLAUDE.md text + lint extension). *Filed by /meta-retrospective 2026-08-13.*
Author
Owner

Current-text confirmation, one day old: the 2026-08-12 verity transcripts (skills deployed at ae802e0, which includes the portability baseline as written) show the unquoted-glob failure still recurring — slot3 alone: 6× no matches found: --include=*.ts plus apps/api/src/matrix/*.ts, apps/web/e2e/*.spec.ts variants; slot1/slot2/slot4/slot6 one hit each. Fourth-plus independent run, and proof the gap survives in text that postdates the original three reports. The lint-check half of the hypothesis (grep for unquoted --include=*/--exclude=* in emitted fences) should land with the amendment.

Evidence added by /meta-retrospective 2026-08-13 (transcript sweep).

Current-text confirmation, one day old: the 2026-08-12 verity transcripts (skills deployed at ae802e0, which includes the portability baseline as written) show the unquoted-glob failure still recurring — slot3 alone: 6× `no matches found: --include=*.ts` plus `apps/api/src/matrix/*.ts`, `apps/web/e2e/*.spec.ts` variants; slot1/slot2/slot4/slot6 one hit each. Fourth-plus independent run, and proof the gap survives in text that postdates the original three reports. The lint-check half of the hypothesis (grep for unquoted `--include=*`/`--exclude=*` in emitted fences) should land with the amendment. *Evidence added by /meta-retrospective 2026-08-13 (transcript sweep).*
Author
Owner

Outcome — amended (commit 930b561)

Two rules added to the CLAUDE.md portability baseline: (1) quote every glob meant for the tool (--include='*.ts', quoted path patterns) — under zsh an unmatched glob aborts the compound command before redirections apply; (2) beyond a few lines or one level of quoting, emit a temp .sh and run it with bash explicitly (also pins the interpreter). Plus a new lint-conventions.sh check for unquoted --include=/--exclude= globs in emitted fences — verified firing on a fixture and clean on the tree.

Gate: lint clean. Desk-check vs originating scenarios: slot3's 6× --include=*.ts failures are now both a written rule and a mechanical gate; slot1-42's (eval):20: unmatched " maps to the emit-a-script rule. The unquoted-path-glob half (apps/*/src) is rule-only (too noisy to lint safely) — acceptable residual. Closing.

## Outcome — amended (commit 930b561) Two rules added to the CLAUDE.md portability baseline: (1) quote every glob meant for the tool (`--include='*.ts'`, quoted path patterns) — under zsh an unmatched glob aborts the compound command before redirections apply; (2) beyond a few lines or one level of quoting, emit a temp `.sh` and run it with bash explicitly (also pins the interpreter). Plus a new lint-conventions.sh check for unquoted `--include=`/`--exclude=` globs in emitted fences — verified firing on a fixture and clean on the tree. Gate: lint clean. Desk-check vs originating scenarios: slot3's 6× `--include=*.ts` failures are now both a written rule and a mechanical gate; slot1-42's `(eval):20: unmatched "` maps to the emit-a-script rule. The unquoted-path-glob half (`apps/*/src`) is rule-only (too noisy to lint safely) — acceptable residual. Closing.
Sign in to join this conversation.
No description provided.