Skip to content

docs(#929): add shell scripting defensive patterns to AGENTS.md - #930

Open
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/929-shell-defensive-patterns
Open

docs(#929): add shell scripting defensive patterns to AGENTS.md#930
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/929-shell-defensive-patterns

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Testing

  • Documentation-only change — no production code or test files modified
  • Verified code examples in the new section match existing patterns in the codebase (_gha_sanitize in ops libs, // empty in jq expressions, local rc=0 exit code pattern)
  • Secret scan passed; gitlint passed
  • Pre-commit could not run in sandbox (network policy); post-script runs authoritative pre-commit

Closes #929

Post-script verification

  • Branch is not main/master (agent/929-shell-defensive-patterns)
  • Secret scan passed (gitleaks — 1787d5ca7ce8adb87ce46d211a834b31e9fa299a..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

Add section 9 with five defensive patterns for agents creating or
modifying .sh files, addressing recurring shell scripting bugs
found during code review of PR #918 (13 review iterations, 8-10
bugs):

- 9a stdin handling: save piped input before branching logic
- 9b jq null safety: guard output with // empty or // "default"
- 9c GHA output sanitization: pass untrusted values through
  _gha_sanitize before echoing
- 9d stderr preservation: require inline comments justifying
  2>/dev/null usage
- 9e exit code propagation: capture and return $? from inner
  commands in wrapper functions

These patterns complement issue #131 (review-time shell pitfall
checks) without overlap — #131 covers trailing newlines, set -u,
mktemp, and pipe-subshell scoping while this covers stdin
consumption, jq null, GHA injection, stderr, and exit codes.

Note: pre-commit could not run in sandbox (network policy blocked
hook environment fetch). Post-script runs authoritative pre-commit.

Closes #929
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 22, 2026 00:52
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 22, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 12:53 AM UTC · Completed 1:09 AM UTC

Commit: 17a9388 · View workflow run →

@fullsend-ai-review

Copy link
Copy Markdown

Review

Findings

Medium

  • [protected-path] AGENTS.md — This PR modifies AGENTS.md, which is under protected paths governance. The PR links to issue Add shell scripting defensive patterns to AGENTS.md for code agents #929 and clearly explains the rationale (adding shell scripting defensive patterns drawn from PR feat(#916): add multi-forge support to scribe agent #918 bugs). Human approval is always required for protected-path changes, regardless of context.

  • [technical-accuracy] AGENTS.md:185 — Section 9a's "wrong" example does not demonstrate the stdin consumption failure it claims. In a standard if/else, only one branch executes — if $mode is not "a", the first branch never runs, so stdin is not consumed by process_a. process_b would correctly receive stdin. The capture-first pattern is sound defensive advice, but the example should be rewritten to show a scenario where the bug actually manifests (e.g., two sequential commands that both read stdin, or a function called inside a while-read loop), or the comments should be adjusted to explain that the pattern guards against future refactors that might add a second stdin consumer.

Low

  • [technical-accuracy] AGENTS.md:211 — Section 9b's comment # arithmetic error is only accurate under set -u or set -euo pipefail (where bash treats null as an unbound variable reference and errors). Without strict mode, $(( count + 1 )) when count="null" silently evaluates to 1 (null is treated as a variable name, which is unset and defaults to 0). Consider clarifying the comment to cover both modes, e.g., # errors under set -u; silently gives 1 otherwise.

  • [heading-naming-convention] AGENTS.md:176 — Sub-section headings use a numbered ### 9a. / ### 9b. pattern. The only existing sub-heading in the document (section 7's ### Valid SKILL.md frontmatter fields) uses an unnumbered descriptive form. Consider using unnumbered descriptive titles (e.g., ### stdin handling, ### jq null safety) for consistency, though the numbered format is a reasonable choice for a list of independent patterns.

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

Comment thread AGENTS.md

```bash
# Wrong — only the first branch consumes stdin; the second gets nothing.
if [ "$mode" = "a" ]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] technical-accuracy

Section 9a's 'wrong' example does not demonstrate the stdin consumption failure it claims. In a standard if/else, only one branch executes, so stdin is never consumed by the untaken branch. process_b would correctly receive stdin when $mode is not 'a'. The capture-first pattern is sound defensive advice, but the example should be rewritten to show a scenario where the bug actually manifests.

Suggested fix: Rewrite the 'wrong' example to show a scenario where stdin consumption actually fails (e.g., two sequential commands reading stdin, or a while-read loop), or adjust comments to explain the pattern guards against future refactors.

Comment thread AGENTS.md
# Wrong — if .count is missing, count becomes the string "null".
count=$(echo "$json" | jq -r '.count')
total=$(( count + 1 )) # arithmetic error

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] technical-accuracy

Section 9b's comment '# arithmetic error' is only accurate under set -u / set -euo pipefail. Without strict mode, $(( count + 1 )) when count='null' silently evaluates to 1 (null treated as unset variable name). The comment should clarify the behavior depends on shell strictness settings.

Suggested fix: Change the comment to '# errors under set -u; silently gives 1 otherwise (null treated as unset variable name)' or similar.

Comment thread AGENTS.md
found in code review (see PR #918) and are independently valuable
alongside the review-time shell pitfall checks proposed in issue #131.

### 9a. stdin handling

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] heading-naming-convention

Sub-section headings use a numbered ### 9a. / ### 9b. pattern that departs from the single existing sub-heading convention (### Valid SKILL.md frontmatter fields, which is unnumbered). The numbered format is a reasonable choice for independent patterns but introduces a new convention.

Suggested fix: Consider renaming sub-headings to unnumbered descriptive titles: ### stdin handling, ### jq null safety, ### GHA output sanitization, ### stderr preservation, ### Exit code propagation.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add shell scripting defensive patterns to AGENTS.md for code agents

0 participants