Skip to content

Commit ff9fb44

Browse files
committed
docs: comprehensive frontmatter schema and production gotcha integration
- Rewrite frontmatter-schema.md as indexed 12-category reference - Add quick index, permission derivation checklist, common patterns - Add compilation gotchas table with 20+ known errors and fixes - Integrate production gotchas into SKILL.md, validation.md, tools-reference.md - Add code block interpolation warning to markdown-body.md - Expand aw-analyst anti-pattern checklist (12 → 28 items)
1 parent 570ddf4 commit ff9fb44

6 files changed

Lines changed: 2105 additions & 237 deletions

File tree

agents/aw-analyst.md

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,36 @@ When analyzing, validate against the authoritative spec. If the embedded skill r
106106
1. **`issue` instead of `issues`** in trigger — must be plural
107107
2. **Missing `reaction: eyes`** on event-triggered workflows
108108
3. **`permissions: write-all`** or overly broad permissions
109-
4. **Bash `:*`** without justification
110-
5. **Safe-outputs without allowlists** — labels, reviewers, milestones should be constrained
111-
6. **Missing `title-prefix`** on `create-issue` or `create-pull-request`
112-
7. **No `max` limit** on high-volume safe-outputs
113-
8. **Prose referencing tools not declared** in frontmatter
114-
9. **Hardcoded secrets** instead of `${{ secrets.* }}`
115-
10. **MCP `container:` field** used incorrectly (must be a Docker image reference)
116-
11. **Missing `close-older-issues`** on recurring report workflows
117-
12. **No edge case handling** in prose body
109+
4. **`permissions: issues: write`** — compiler rejects write permissions; all writes go through safe-outputs
110+
5. **Bash `:*`** without justification
111+
6. **Safe-outputs without allowlists** — labels, reviewers, milestones should be constrained
112+
7. **Missing `title-prefix`** on `create-issue` or `create-pull-request`
113+
8. **No `max` limit** on high-volume safe-outputs
114+
9. **Prose referencing tools not declared** in frontmatter
115+
10. **Hardcoded secrets** instead of `${{ secrets.* }}`
116+
11. **MCP `container:` field** used incorrectly (must be a Docker image reference)
117+
12. **Missing `close-older-issues`** on recurring report workflows
118+
13. **No edge case handling** in prose body
119+
14. **`tools.github.app` with more workflow permissions than the App has** — causes HTTP 422 on token creation
120+
15. **`tools.github` alongside `gh` CLI in bash** — MCP server takes GITHUB_TOKEN ownership, blocking `gh` auth
121+
16. **`add-comment` without `discussions: false`** — silently requests `discussions:write`, fails if App lacks it
122+
17. **`${{ }}` expressions inside fenced code blocks** — not interpolated, agent gets literal strings
123+
18. **Standard `.yml` files in `.github/workflows/`** alongside gh-aw workflows — blocks App token pushes
124+
19. **Double quotes in MCP `entrypointArgs`**`gh aw compile` doesn't escape them, producing broken JSON
125+
20. **MCP server stdout before JSON-RPC handshake** — breaks gateway init; redirect install output to /dev/null
126+
127+
## Production Gotchas Reference
128+
129+
For detailed explanations, symptoms, and fixes for each production gotcha, refer to `references/production-gotchas.md`. This covers:
130+
- Expression interpolation in code blocks
131+
- GitHub App token permission inheritance
132+
- `tools.github` vs `gh` CLI conflicts
133+
- Safe-output hidden defaults (`add-comment` discussions)
134+
- Missing merge safe-output (use `post-steps` instead)
135+
- Write permission compiler rejection
136+
- `.lock.yml` workflow push exemption
137+
- `if:` frontmatter guard
138+
- `post-steps:` feature
139+
- MCP JSON escaping and stdout constraints
140+
- `gh aw mcp inspect/list` import limitation
141+
- `pull_request` trigger merge ref timing

skills/aw-author/SKILL.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,28 +110,27 @@ Options:
110110
For each selected safe-output, prompt for constraints:
111111
- **add-labels:** "Which labels are allowed?" → `allowed: [list]`
112112
- **create-issue:** "Title prefix? Auto-close older?" → `title-prefix`, `close-older-issues`
113-
- **add-comment:** "Hide older comments?" → `hide-older-comments: true`
113+
- **add-comment:** "Hide older comments?" → `hide-older-comments: true`. **Always add `discussions: false`** unless the workflow explicitly uses discussions — `add-comment` defaults to requesting `discussions:write`, which causes HTTP 422 if the App lacks that permission
114114
- **create-pull-request:** "Draft mode? Reviewers?" → `draft: true`, `reviewers: [list]`
115115

116+
**No merge safe-output exists.** If the workflow needs to merge PRs, use `post-steps:` with a fresh App token. See `references/production-gotchas.md` for the pattern.
117+
116118
Refer to `references/safe-outputs.md` for the full catalog of safe-output types and parameters.
117119

118120
### Phase 5: Security & Permissions
119121

120-
Derive minimal permissions from the selected tools and safe-outputs:
122+
Derive minimal **read-only** permissions from the selected tools and safe-outputs. **Write permissions are NOT allowed in frontmatter** — the compiler rejects them. All write operations go through safe-outputs.
121123

122124
| Component | Required Permission |
123125
|-----------|-------------------|
124126
| Read issues | `issues: read` |
125-
| Add labels / comments / close | `issues: write` |
126127
| Read PRs | `pull-requests: read` |
127-
| Create/update PRs | `pull-requests: write` |
128-
| Push to PR branch | `contents: write` |
129128
| Read code / files | `contents: read` |
130-
| Discussions | `discussions: read` or `write` |
131-
| Code scanning | `security-events: write` |
132-
| Dispatch workflows | `actions: write` |
129+
| Discussions | `discussions: read` |
133130
| Workflow introspection | `actions: read` |
134131

132+
Write operations (labels, comments, PRs, etc.) are handled entirely by safe-outputs using the App token — no `write` permissions needed in `permissions:`.
133+
135134
Ask about security posture:
136135

137136
```
@@ -177,6 +176,8 @@ Use expression syntax for dynamic values:
177176
- `${{ secrets.NAME }}` — secret references
178177
- `${{ vars.NAME }}` — variable references
179178

179+
**Code block interpolation warning:** `${{ }}` expressions in plain text ARE interpolated, but expressions inside fenced code blocks (` ```bash `, etc.) are NOT interpolated — the agent receives them as literal strings. If bash code blocks need event data, declare env vars in plain text (e.g., `ISSUE_NUMBER` from `${{ github.event.issue.number }}`) and use `$ISSUE_NUMBER` in code blocks. See `references/production-gotchas.md` for details.
180+
180181
### Phase 8: Assembly & Compilation
181182

182183
1. Assemble the complete workflow file combining frontmatter + prose body
@@ -327,13 +328,18 @@ If compilation succeeds (or the user reports a runtime/behavioral issue):
327328
- Check network access if external calls needed
328329
- Check safe-output constraints aren't too restrictive
329330
- Check MCP server configuration (env vars, args, container image)
331+
- **Check `tools.github.app` permission inheritance** — App token requests ALL workflow permissions, not just what MCP needs
332+
- **Check `tools.github` vs `gh` CLI conflict** — MCP server takes ownership of GITHUB_TOKEN, blocking `gh` CLI auth
333+
- **Check `add-comment` discussions default** — requests `discussions:write` even if unused; add `discussions: false`
334+
- **Check `.lock.yml` vs `.yml` push restriction** — standard `.yml` files block App token pushes
330335

331336
2. **Behavioral error** — the workflow runs but produces wrong results:
332337
- Review prose instructions for ambiguity
333338
- Check if the agent has sufficient context
334339
- Check tool selection matches the task
335340
- Review safe-output allowlists for missing values
336341
- Check for `event.json` fallback if event context is missing
342+
- **Check code block interpolation**`${{ }}` in fenced code blocks are NOT interpolated; agent gets literal strings
337343

338344
### Step 3: Gather Context
339345

@@ -342,7 +348,7 @@ Ask the user to provide:
342348
- Error messages or logs (from GitHub Actions)
343349
- Expected vs actual behavior
344350

345-
Use `references/validation.md` for the error catalog and common fixes.
351+
Use `references/validation.md` for the error catalog and `references/production-gotchas.md` for runtime gotchas verified through production debugging.
346352

347353
---
348354

@@ -358,6 +364,7 @@ For questions about:
358364
- **Body writing** → refer to `references/markdown-body.md`
359365
- **Errors and debugging** → refer to `references/validation.md`
360366
- **Examples** → refer to `references/examples.md`
367+
- **Runtime gotchas, App tokens, MCP pitfalls** → refer to `references/production-gotchas.md`
361368

362369
For questions not covered by embedded references, fetch the latest spec:
363370
- Full spec: `https://github.github.com/gh-aw/llms-full.txt`
@@ -374,7 +381,7 @@ These rules apply across ALL modes:
374381
1. **Trigger field is `issues` (plural), not `issue`** — this is the most common mistake
375382
2. **Event-triggered workflows need `reaction: eyes`** in the `on:` block for pre_activation permissions
376383
3. **Safe-outputs are the ONLY way to write** — the agent itself is read-only
377-
4. **Permissions must match**every safe-output type requires specific permission scopes
384+
4. **Write permissions are NOT allowed in frontmatter**the compiler rejects `write` in `permissions:`; all writes go through safe-outputs using the App token
378385
5. **`strict: false` is required** when processing untrusted/external input (public repo issues)
379386
6. **Bash defaults are safe** — only `echo`, `ls`, `pwd`, `cat`, `head`, `tail`, `grep`, `wc`, `sort`, `uniq`, `date`
380387
7. **GitHub toolsets default** to `[context, repos, issues, pull_requests]` in Actions
@@ -387,3 +394,15 @@ These rules apply across ALL modes:
387394
14. **`strict: false` is also required for custom API domains** — ecosystem identifiers (`defaults`, `github`, `containers`, `node`, `python`) work in strict mode, but custom domains like `*.datadoghq.com` require `strict: false`
388395
15. **Add `node` and `python` ecosystems** when MCP servers use `npx` or `uvx` — these ecosystems allow package registry access (npm, PyPI) needed for process-based MCP servers
389396
16. **MCP gateway logs are the primary debug source** — when MCP tool calls return vague errors (e.g., "no data found"), download `agent-artifacts/mcp-logs/{server}.log` from the workflow run to see the real error (TLS failures, auth errors, timeouts)
397+
17. **`${{ }}` expressions in fenced code blocks are NOT interpolated** — they are passed as literal strings to the agent; use env vars in code blocks instead (see `references/production-gotchas.md`)
398+
18. **`tools.github.app` requests ALL workflow permissions** — if the App lacks any permission in the `permissions:` block (e.g., `packages: read`), the App token creation fails with HTTP 422; omit `app:` for read-only MCP access
399+
19. **`tools.github` conflicts with `gh` CLI** — the MCP server takes ownership of GITHUB_TOKEN; if workflow only uses `gh` CLI via bash, remove `tools.github` entirely
400+
20. **`add-comment` defaults to `discussions:write`** — always add `discussions: false` under `add-comment:` unless discussions are explicitly needed, or the App token fails with HTTP 422
401+
21. **No `merge-pull-request` safe-output exists** — use `post-steps:` with a fresh App token to merge PRs
402+
22. **`.lock.yml` files are exempt from workflow push restrictions** — standard `.yml`/`.yaml` files block App token pushes in `.github/workflows/`; never mix standard GH Actions `.yml` files with gh-aw workflows if using App tokens for safe-outputs
403+
23. **Frontmatter `if:` guard** prevents workflow runs from starting entirely — evaluated before any jobs run, saves compute vs job-level guards
404+
24. **`post-steps:` runs after AI execution** — has access to job context (`${{ github.event.* }}`, secrets, vars); use for merging PRs, closing issues, cleanup requiring App tokens
405+
25. **`gh aw compile` does NOT escape `"` in entrypointArgs** — never use double quotes in MCP server command strings; use `grep` instead of `jq` if the expression would contain quotes
406+
26. **MCP server stdout breaks initialization** — any stdout before the JSON-RPC handshake breaks the MCP gateway; redirect all `apk add`, `pip install`, `curl` output to `/dev/null`
407+
27. **`gh aw mcp inspect/list` does NOT follow `imports:`** — only sees servers in direct frontmatter; check compiled `.lock.yml` to verify imported servers
408+
28. **`pull_request` trigger uses the merge commit workflow** — if you push a new lock.yml to main and immediately re-trigger a PR, the merge ref may use the old main; wait briefly between pushes and PR events

0 commit comments

Comments
 (0)