From 1da94fe39424e8ab0be9fd25fde64026c8cb05e9 Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Sun, 17 May 2026 05:59:55 -0700 Subject: [PATCH 1/6] agents|refactor: Emit complete YAML frontmatter from `resolve-frontmatter.sh` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `resolve-frontmatter.sh` now emits the complete YAML frontmatter block (including `---` delimiters) by default, accepting `--skill`, `--interactive`, `--model`, `--extra KEY=VALUE`, `--extra-list KEY=v1,v2,...`, `--override KEY=VALUE`, and `--format yaml|json` flags. The JSON path under `--format json` is byte-equivalent to the previous default output. YAML emission auto-quotes values containing YAML-reserved glyphs (`#`, `:` followed by whitespace, brackets, braces, leading sigils, embedded quotes) while keeping URLs and timestamps bare. Missing `--skill` or `--interactive` in YAML mode is a hard error; JSON mode keeps those flags optional for caller symmetry. The 22 simple skill and subagent sites that previously included `_partials/frontmatter-via-script.md` now invoke the script in YAML mode with a single Bash call sentence, passing site-specific extensions through `--extra` / `--extra-list` / `--override` rather than as documented prose bullets. The `refine-plan` and `wrap-up` sites — which need case-branched or list-of-objects composition that has no clean CLI expression — opt into `--format json` with a one-sentence rationale linking to a new `Bespoke frontmatter composition` subsection in `artifact-conventions.md`. The obsolete `_partials/frontmatter-via-script.md` is removed. Inline YAML frontmatter examples that duplicated the canonical schema in eight `## Output format` sections now reference the canonical example in `artifact-conventions.md` and show only the artifact-body structure. --- .../_partials/frontmatter-via-script.md | 6 - .../__tests__/resolve_frontmatter_test.sh | 346 ++++++++++++++++++ .../content/scripts/resolve-frontmatter.sh | 346 ++++++++++++++++-- .../skills/_data/artifact-conventions.md | 9 + .../content/skills/create-devlog/SKILL.md | 47 ++- .../content/skills/create-ticket/SKILL.md | 9 +- .../content/skills/design-and-plan/SKILL.md | 7 +- .../content/skills/ex-post-facto/SKILL.md | 9 +- .../content/skills/orchestrate/SKILL.md | 6 +- .../skills/plan-orchestrable-steps/SKILL.md | 7 +- packages/agents/content/skills/plan/SKILL.md | 24 +- .../content/skills/refine-plan/SKILL.md | 8 +- .../content/skills/respond-to-review/SKILL.md | 39 +- .../content/skills/review-branch/SKILL.md | 38 +- .../agents/content/skills/save-plan/SKILL.md | 7 +- .../content/skills/summarize-change/SKILL.md | 42 +-- .../content/skills/summarize-chat/SKILL.md | 27 +- .../agents/content/skills/wrap-up/SKILL.md | 9 +- .../content/subagents/aspect-code-reviewer.md | 9 +- .../aspect-silent-failure-reviewer.md | 9 +- .../content/subagents/aspect-test-reviewer.md | 9 +- .../subagents/code-simplification-reviewer.md | 9 +- .../subagents/orchestrated-architect.md | 26 +- .../content/subagents/orchestrated-coder.md | 9 +- .../content/subagents/orchestrated-planner.md | 9 +- .../subagents/orchestrated-reviewer.md | 9 +- .../agents/content/subagents/plan-reviewer.md | 26 +- packages/agents/content/subagents/planner.md | 9 +- .../content/subagents/savings-analyzer.md | 19 +- 29 files changed, 828 insertions(+), 301 deletions(-) delete mode 100644 packages/agents/content/_partials/frontmatter-via-script.md diff --git a/packages/agents/content/_partials/frontmatter-via-script.md b/packages/agents/content/_partials/frontmatter-via-script.md deleted file mode 100644 index 5155a758..00000000 --- a/packages/agents/content/_partials/frontmatter-via-script.md +++ /dev/null @@ -1,6 +0,0 @@ -Run `resolve-frontmatter.sh` via the Bash tool. It emits a JSON object with the universal artifact fields (`branch`, `commit`, `baseSha`, `pr`, `ticket_id`, `ticket_ref`, `platform`, `timestamp`, `run_id`). Use those values verbatim for the matching YAML keys. Optional fields the script omits from its output (`baseSha`, `pr`, `ticket_id`, `ticket_ref`, `run_id`) must be omitted from the frontmatter too — do not emit `null` or empty strings. - -If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. - -Set these skill-specific values inline (not in the script's output): - diff --git a/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh b/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh index 7de4bb4c..4b9afeff 100644 --- a/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh +++ b/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh @@ -143,3 +143,349 @@ When call resolve_base_sha "HEAD" The output should equal "$ref" End End + +Describe "needs_yaml_quoting" +It "returns true for empty values" +When call needs_yaml_quoting "" +The status should be success +End + +It "returns false for plain alphanumerics" +When call needs_yaml_quoting "foo123" +The status should be failure +End + +It "returns true for values where a colon is followed by whitespace" +When call needs_yaml_quoting "key: value" +The status should be success +End + +It "returns false for values where a colon is followed by a non-space character" +When call needs_yaml_quoting "key:value" +The status should be failure +End + +It "returns true for values ending in a trailing colon" +When call needs_yaml_quoting "trailing:" +The status should be success +End + +It "returns true for values containing pound sign" +When call needs_yaml_quoting "#537" +The status should be success +End + +It "returns true for values with leading whitespace" +When call needs_yaml_quoting " leading" +The status should be success +End + +It "returns true for values with trailing whitespace" +When call needs_yaml_quoting "trailing " +The status should be success +End + +It "returns true for values beginning with a hyphen" +When call needs_yaml_quoting "-leading" +The status should be success +End + +It "returns true for values beginning with a question mark" +When call needs_yaml_quoting "?leading" +The status should be success +End + +It "returns true for values beginning with a colon" +When call needs_yaml_quoting ":leading" +The status should be success +End + +It "returns false for URLs (no special chars under predicate)" +When call needs_yaml_quoting "https://github.com/x/y/pull/1" +The status should be failure +End + +It "returns true for values containing brackets" +When call needs_yaml_quoting "a[b]c" +The status should be success +End + +It "returns true for values containing braces" +When call needs_yaml_quoting "{key}" +The status should be success +End + +It "returns true for values containing commas" +When call needs_yaml_quoting "a,b" +The status should be success +End + +It "returns true for values containing pipe" +When call needs_yaml_quoting "a|b" +The status should be success +End + +It "returns true for values containing backtick" +When call needs_yaml_quoting "a\`b" +The status should be success +End +End + +Describe "yaml_quote" +It "leaves bare values unquoted" +When call yaml_quote "foo123" +The output should equal "foo123" +End + +It "wraps unsafe values in single quotes" +When call yaml_quote "#537" +The output should equal "'#537'" +End + +It "doubles embedded single quotes inside the wrapper" +When call yaml_quote "it's" +The output should equal "'it''s'" +End + +It "quotes empty values as empty string" +When call yaml_quote "" +The output should equal "''" +End + +It "leaves URLs unquoted" +When call yaml_quote "https://github.com/x/y/pull/1" +The output should equal "https://github.com/x/y/pull/1" +End +End + +Describe "emit_yaml_flow_list" +It "emits an empty flow list for empty values" +When call emit_yaml_flow_list "items" "" +The output should equal "items: []" +End + +It "emits single-element flow lists in bracket form" +When call emit_yaml_flow_list "commits" "a1b2c3d" +The output should equal "commits: [a1b2c3d]" +End + +It "splits comma-separated values into list elements" +When call emit_yaml_flow_list "commits" "a1b2c3d,e4f5g6h" +The output should equal "commits: [a1b2c3d, e4f5g6h]" +End + +It "auto-quotes elements that contain unsafe glyphs" +When call emit_yaml_flow_list "refs" "main,#537" +The output should equal "refs: [main, '#537']" +End +End + +Describe "add_extra" +add_extra_setup() { + unset extra_keys extra_values extra_kinds + declare -ga extra_keys=() + declare -gA extra_values=() + declare -gA extra_kinds=() +} + +BeforeEach "add_extra_setup" + +It "records insertion order across mixed extra kinds" +add_extra "scalar" "title=hello" extra_keys extra_values extra_kinds +add_extra "list" "commits=a,b" extra_keys extra_values extra_kinds +add_extra "scalar" "scope=root" extra_keys extra_values extra_kinds +When call test "${#extra_keys[@]}" -eq 3 +The status should be success +End + +It "splits the key on the first equals sign" +add_extra "scalar" "title=a=b=c" extra_keys extra_values extra_kinds +When call echo "${extra_values[title]}" +The output should equal "a=b=c" +End + +It "tracks the kind per key" +add_extra "list" "commits=a,b" extra_keys extra_values extra_kinds +When call echo "${extra_kinds[commits]}" +The output should equal "list" +End + +It "fails when the argument has no equals sign" +When run add_extra "scalar" "bad_arg" extra_keys extra_values extra_kinds +The status should be failure +The stderr should include "missing '='" +End + +It "fails when the key is empty" +When run add_extra "scalar" "=value" extra_keys extra_values extra_kinds +The status should be failure +The stderr should include "empty key" +End +End + +Describe "apply_override" +apply_override_setup() { + unset overrides + declare -gA overrides=() +} + +BeforeEach "apply_override_setup" + +It "returns the resolved value when no override is registered" +When call apply_override "branch" "main" overrides +The output should equal "main" +End + +It "returns the override when one is registered" +overrides[branch]="custom-branch" +When call apply_override "branch" "main" overrides +The output should equal "custom-branch" +End + +It "force-omits when the override value is empty" +overrides[run_id]="" +When call apply_override "run_id" "20260516Z" overrides +The output should equal "" +End +End + +Describe "emit_yaml" +emit_yaml_setup() { + unset yaml_keys yaml_values yaml_kinds + declare -ga yaml_keys=() + declare -gA yaml_values=() + declare -gA yaml_kinds=() +} + +BeforeEach "emit_yaml_setup" + +It "wraps the output in --- delimiters" +When call emit_yaml \ + "create-devlog" "2026-05-16T00:00:00Z" "deadbee" "true" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The line 1 of output should equal "---" +The output should end with "---" +End + +It "emits provenance block in canonical order" +When call emit_yaml \ + "create-devlog" "2026-05-16T00:00:00Z" "deadbee" "true" "claude-opus" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should include "provenance:" +The output should include "skill: create-devlog" +The output should include "timestamp: 2026-05-16T00:00:00Z" +The output should include "baseSha: deadbee" +The output should include "isInteractive: true" +The output should include "model: claude-opus" +End + +It "omits provenance.baseSha when empty" +When call emit_yaml \ + "skill-x" "2026-05-16T00:00:00Z" "" "false" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should not include "baseSha" +End + +It "omits provenance.model when empty" +When call emit_yaml \ + "skill-x" "2026-05-16T00:00:00Z" "deadbee" "false" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should not include "model" +End + +It "emits isInteractive as a bare boolean" +When call emit_yaml \ + "skill-x" "2026-05-16T00:00:00Z" "deadbee" "false" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should include "isInteractive: false" +The output should not include "isInteractive: 'false'" +End + +It "emits canonical top-level fields after provenance" +When call emit_yaml \ + "skill-x" "2026-05-16T00:00:00Z" "deadbee" "false" "" \ + "537" "#537" "main" "abc1234" "https://github.com/x/y/pull/1" "20260516-143946Z" \ + yaml_keys yaml_values yaml_kinds +The output should include "ticket_id: 537" +The output should include "ticket_ref: '#537'" +The output should include "branch: main" +The output should include "commit: abc1234" +The output should include "pr: https://github.com/x/y/pull/1" +The output should include "run_id: 20260516-143946Z" +End + +It "omits empty top-level fields" +When call emit_yaml \ + "skill-x" "2026-05-16T00:00:00Z" "deadbee" "false" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should not include "ticket_id" +The output should not include "ticket_ref" +The output should not include "pr:" +The output should not include "run_id" +End + +It "emits scalar extensions after canonical fields" +yaml_keys+=("title") +yaml_values[title]="My change" +yaml_kinds[title]="scalar" +When call emit_yaml \ + "summarize-change" "2026-05-16T00:00:00Z" "deadbee" "true" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should include "title: My change" +End + +It "emits flow-list extensions after canonical fields" +yaml_keys+=("commits") +yaml_values[commits]="a1b2c3d,e4f5g6h" +yaml_kinds[commits]="list" +When call emit_yaml \ + "create-devlog" "2026-05-16T00:00:00Z" "deadbee" "true" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should include "commits: [a1b2c3d, e4f5g6h]" +End + +emit_three_ordered() { + emit_yaml \ + "summarize-change" "2026-05-16T00:00:00Z" "deadbee" "true" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +} + +It "preserves insertion order across mixed scalar and list extensions" +yaml_keys+=("scope") +yaml_values[scope]="agents" +yaml_kinds[scope]="scalar" +yaml_keys+=("commits") +yaml_values[commits]="a1b2c3d" +yaml_kinds[commits]="list" +yaml_keys+=("type") +yaml_values[type]="feat" +yaml_kinds[type]="scalar" +When call emit_three_ordered +The output should include "scope: agents" +The output should include "commits: " +The output should include "type: feat" +# Ensure scope < commits < type ordering. Glob brackets need escaping; +# match the contiguous block including newlines via a structural pattern. +The output should match pattern "*scope: agents*commits:*type: feat*" +End + +It "auto-quotes values containing colons" +yaml_keys+=("title") +yaml_values[title]="Add: feature" +yaml_kinds[title]="scalar" +When call emit_yaml \ + "summarize-change" "2026-05-16T00:00:00Z" "deadbee" "true" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should include "title: 'Add: feature'" +End +End diff --git a/packages/agents/content/scripts/resolve-frontmatter.sh b/packages/agents/content/scripts/resolve-frontmatter.sh index d3eba6ec..b448448f 100755 --- a/packages/agents/content/scripts/resolve-frontmatter.sh +++ b/packages/agents/content/scripts/resolve-frontmatter.sh @@ -1,43 +1,54 @@ #!/usr/bin/env bash -# Emit the canonical artifact-frontmatter fields as a single JSON object. +# Emit canonical artifact-frontmatter fields as YAML (default) or JSON. # # Reads `.agents/{sanitized-branch}.branch-manifest.json` (produced by the # `get-session-context` skill) for session-level fields and runs git + -# platform-specific PR lookup for the rest. Skills consume the JSON output -# to populate the universal portion of their artifact frontmatter without +# platform-specific PR lookup for the rest. Skills consume the output to +# populate the universal portion of their artifact frontmatter without # repeating the underlying shell logic. # # Usage: -# resolve-frontmatter.sh +# resolve-frontmatter.sh --skill NAME --interactive true|false [...] +# resolve-frontmatter.sh --format json # resolve-frontmatter.sh --help # -# Output (stdout): a JSON object with these keys, all optional except -# `branch`, `commit`, `platform`, and `timestamp`: +# Flags: +# --skill NAME provenance.skill value (required in yaml mode). +# --interactive true|false provenance.isInteractive value (required in yaml mode). +# --model ID provenance.model value (optional). +# --extra KEY=VALUE Append a scalar extension key (repeatable). +# Values may contain `=`; split on the first `=`. +# --extra-list KEY=v1,v2,… Append a flow-list extension key (repeatable). +# Values are split on `,`. +# --override KEY=VALUE Force a canonical/provenance field to VALUE. +# Empty VALUE force-omits the key. +# --format yaml|json Output format (default `yaml`). +# --help, -h Show usage and exit 0. # -# branch Raw branch name from session context. -# commit Short SHA of HEAD. -# baseSha Short SHA of `origin/{default_branch}`. Omitted if the -# ref is unresolvable (shallow clone, no remote, etc.). -# pr Full PR URL for the current branch. Omitted when no PR -# exists (lookup returned empty) AND on lookup failure -# (timeout, non-zero exit, auth error, etc.). -# ticket_id From session context. Omitted when null. -# ticket_ref From session context. Omitted when null. -# platform `github` or `bitbucket`, from session context. -# timestamp Current UTC ISO 8601 timestamp. -# run_id Active orchestrated-run ID, read from -# `.claude/tmp/active-run-dir` (basename) when present. -# Omitted otherwise. +# Output (yaml mode, stdout): a complete YAML frontmatter block including +# `---` delimiters. Field order: +# +# provenance: +# skill, timestamp, baseSha, isInteractive, model (camelCase) +# ticket_id, ticket_ref, branch, commit, pr, run_id (snake_case) +# {--extra / --extra-list extensions in insertion order} +# +# Output (json mode, stdout): a single JSON object — backward-compatible +# with prior `--format json` (default) callers. Keys: branch, commit, +# baseSha, pr, ticket_id, ticket_ref, platform, timestamp, run_id. +# +# Omission rules (both formats): any key whose resolved value is empty is +# omitted entirely. `--override KEY=` with empty value force-omits even +# when the script resolved a value. # # Warnings (stderr): when PR resolution fails (not when it returns empty), # emits the canonical warning `Note: PR lookup failed; proceeding without -# pr field.` so the caller can surface it in agent text output. Empty -# output is the normal no-PR case and emits no warning. +# pr field.` so the caller can surface it in agent text output. # # Exit codes: -# 0 Always when a JSON object is emitted (even with reduced fields). -# 1 Missing branch manifest — caller must invoke `get-session-context` -# first. Not in a git repo. Missing `jq`. +# 0 Success. +# 1 Missing branch manifest (run `get-session-context` first), not in a +# git repo, missing `jq`, or required-arg violation in yaml mode. set -euo pipefail @@ -49,26 +60,93 @@ show_usage() { local exit_code="${1:-1}" cat <&2 - show_usage 1 + while [[ "$#" -gt 0 ]]; do + case "$1" in + --help | -h) + show_usage 0 + ;; + --format) + [[ "$#" -ge 2 ]] || fail "missing value for --format" + format="$2" + shift 2 + ;; + --skill) + [[ "$#" -ge 2 ]] || fail "missing value for --skill" + skill="$2" + shift 2 + ;; + --interactive) + [[ "$#" -ge 2 ]] || fail "missing value for --interactive" + interactive="$2" + interactive_set=true + shift 2 + ;; + --model) + [[ "$#" -ge 2 ]] || fail "missing value for --model" + model="$2" + shift 2 + ;; + --extra) + [[ "$#" -ge 2 ]] || fail "missing value for --extra" + add_extra "scalar" "$2" extra_keys extra_values extra_kinds + shift 2 + ;; + --extra-list) + [[ "$#" -ge 2 ]] || fail "missing value for --extra-list" + add_extra "list" "$2" extra_keys extra_values extra_kinds + shift 2 + ;; + --override) + [[ "$#" -ge 2 ]] || fail "missing value for --override" + add_override "$2" overrides + shift 2 + ;; + *) + echo "$PROG: unexpected argument: $1" >&2 + show_usage 1 + ;; + esac + done + + case "$format" in + yaml | json) ;; + *) fail "unknown --format: $format (expected yaml|json)" ;; + esac + + if [[ "$format" == "yaml" ]]; then + [[ -n "$skill" ]] || fail "--skill is required in yaml mode" + [[ "$interactive_set" == "true" ]] || fail "--interactive is required in yaml mode" + case "$interactive" in + true | false) ;; + *) fail "--interactive must be true or false (got: $interactive)" ;; + esac fi + # -- Resolve session-level values -- require_commands jq git local branch @@ -92,8 +170,73 @@ main() { run_id=$(resolve_run_id) timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ) - emit_json "$branch" "$commit" "$base_sha" "$pr_url" \ - "$ticket_id" "$ticket_ref" "$platform" "$timestamp" "$run_id" + # -- Apply overrides -- + branch=$(apply_override "branch" "$branch" overrides) + commit=$(apply_override "commit" "$commit" overrides) + base_sha=$(apply_override "baseSha" "$base_sha" overrides) + pr_url=$(apply_override "pr" "$pr_url" overrides) + ticket_id=$(apply_override "ticket_id" "$ticket_id" overrides) + ticket_ref=$(apply_override "ticket_ref" "$ticket_ref" overrides) + platform=$(apply_override "platform" "$platform" overrides) + timestamp=$(apply_override "timestamp" "$timestamp" overrides) + run_id=$(apply_override "run_id" "$run_id" overrides) + + if [[ "$format" == "json" ]]; then + emit_json "$branch" "$commit" "$base_sha" "$pr_url" \ + "$ticket_id" "$ticket_ref" "$platform" "$timestamp" "$run_id" + else + emit_yaml \ + "$skill" "$timestamp" "$base_sha" "$interactive" "$model" \ + "$ticket_id" "$ticket_ref" "$branch" "$commit" "$pr_url" "$run_id" \ + extra_keys extra_values extra_kinds + fi +} + +# Append an extension key/value to the caller's ordered list. Splits the +# argument once on the first `=`. `kind` is either `scalar` or `list`. +add_extra() { + local kind="$1" arg="$2" + local -n keys_ref="$3" + local -n values_ref="$4" + local -n kinds_ref="$5" + local key value + if [[ "$arg" != *"="* ]]; then + fail "--extra/--extra-list argument missing '=': $arg" + fi + key="${arg%%=*}" + value="${arg#*=}" + [[ -n "$key" ]] || fail "--extra/--extra-list argument has empty key" + if [[ -z "${kinds_ref[$key]:-}" ]]; then + keys_ref+=("$key") + fi + values_ref["$key"]="$value" + kinds_ref["$key"]="$kind" +} + +# Record an override key=value. Empty value force-omits the key on emit. +add_override() { + local arg="$1" + local -n overrides_ref="$2" + local key value + if [[ "$arg" != *"="* ]]; then + fail "--override argument missing '=': $arg" + fi + key="${arg%%=*}" + value="${arg#*=}" + [[ -n "$key" ]] || fail "--override argument has empty key" + overrides_ref["$key"]="$value" +} + +# Return the overridden value when the key has been overridden, otherwise +# the resolved value. The empty-string override force-omits. +apply_override() { + local key="$1" resolved="$2" + local -n overrides_ref="$3" + if [[ -n "${overrides_ref[$key]+set}" ]]; then + printf '%s' "${overrides_ref[$key]}" + else + printf '%s' "$resolved" + fi } # Print short SHA of `default_branch` (e.g., `origin/main`) or empty if @@ -281,6 +424,137 @@ emit_json() { ' } +# Emit the canonical YAML frontmatter block, including `---` delimiters. +# Empty values are omitted. Field order is fixed: provenance block, then +# canonical top-level fields, then caller-supplied extensions in +# insertion order. +emit_yaml() { + local skill="$1" timestamp="$2" base_sha="$3" interactive="$4" model="$5" + local ticket_id="$6" ticket_ref="$7" branch="$8" commit="$9" pr_url="${10}" run_id="${11}" + local -n yaml_extra_keys="${12}" + local -n yaml_extra_values="${13}" + local -n yaml_extra_kinds="${14}" + + printf '%s\n' "---" + + # provenance block + printf '%s\n' "provenance:" + emit_yaml_indented_scalar "skill" "$skill" + emit_yaml_indented_scalar "timestamp" "$timestamp" + [[ -n "$base_sha" ]] && emit_yaml_indented_scalar "baseSha" "$base_sha" + # isInteractive is a boolean — emit unquoted true|false. + printf ' %s: %s\n' "isInteractive" "$interactive" + [[ -n "$model" ]] && emit_yaml_indented_scalar "model" "$model" + + # canonical top-level fields + [[ -n "$ticket_id" ]] && emit_yaml_scalar "ticket_id" "$ticket_id" + [[ -n "$ticket_ref" ]] && emit_yaml_scalar "ticket_ref" "$ticket_ref" + emit_yaml_scalar "branch" "$branch" + emit_yaml_scalar "commit" "$commit" + [[ -n "$pr_url" ]] && emit_yaml_scalar "pr" "$pr_url" + [[ -n "$run_id" ]] && emit_yaml_scalar "run_id" "$run_id" + + # extension fields in insertion order + local key kind value + for key in "${yaml_extra_keys[@]+"${yaml_extra_keys[@]}"}"; do + value="${yaml_extra_values[$key]}" + kind="${yaml_extra_kinds[$key]}" + if [[ "$kind" == "list" ]]; then + emit_yaml_flow_list "$key" "$value" + else + emit_yaml_scalar "$key" "$value" + fi + done + + printf '%s\n' "---" +} + +# Emit a top-level YAML `key: value` line, auto-quoting the value as needed. +emit_yaml_scalar() { + local key="$1" value="$2" + printf '%s: %s\n' "$key" "$(yaml_quote "$value")" +} + +# Emit an indented YAML `key: value` line inside the provenance block. +emit_yaml_indented_scalar() { + local key="$1" value="$2" + printf ' %s: %s\n' "$key" "$(yaml_quote "$value")" +} + +# Emit a top-level YAML flow list: `key: [v1, v2, v3]`. Empty value emits +# an empty flow list `key: []`. Elements are split on `,` and each is +# passed through `yaml_quote`. +emit_yaml_flow_list() { + local key="$1" raw="$2" + if [[ -z "$raw" ]]; then + printf '%s: []\n' "$key" + return + fi + local IFS=',' + # shellcheck disable=SC2206 + local -a parts=( $raw ) + local out="" i + for ((i = 0; i < ${#parts[@]}; i++)); do + if [[ "$i" -gt 0 ]]; then + out+=", " + fi + out+="$(yaml_quote "${parts[$i]}")" + done + printf '%s: [%s]\n' "$key" "$out" +} + +# Return the value either bare or single-quoted depending on YAML +# auto-quoting rules. The predicate quotes when the value contains any of +# the unsafe glyphs (`# : [ ] { } , & * ! | > < ? % @ \` ` `), has leading +# or trailing whitespace, is empty, or begins with `-` / `?` / `:`. +# Embedded single quotes are doubled inside the quoted form. +yaml_quote() { + local v="$1" + if needs_yaml_quoting "$v"; then + local escaped="${v//\'/\'\'}" + printf "'%s'" "$escaped" + else + printf '%s' "$v" + fi +} + +# Decide whether `v` needs single-quote wrapping. Returns 0 (true) when +# quoting is required, 1 otherwise. +# +# YAML parses `:` as a key indicator only when followed by whitespace or +# end-of-value, so URLs like `https://...` are safe bare. `#` is always +# treated as a comment introducer in YAML 1.1/1.2 (the spec is permissive +# about the preceding context), so we quote any value containing `#`. +needs_yaml_quoting() { + local v="$1" + # Empty values must be quoted. + [[ -z "$v" ]] && return 0 + # Leading or trailing whitespace. + [[ "$v" =~ ^[[:space:]] ]] && return 0 + [[ "$v" =~ [[:space:]]$ ]] && return 0 + # Leading sigils that YAML interprets specially. + case "$v" in + -* | \?* | :*) return 0 ;; + esac + # Colon followed by whitespace anywhere is a key indicator. + [[ "$v" =~ :[[:space:]] ]] && return 0 + # Trailing colon is the value-end form of a key indicator. + [[ "$v" == *: ]] && return 0 + # Glyphs anywhere in the value that demand quoting. + case "$v" in + *'#'* | *'['* | *']'* | *'{'* | *'}'* \ + | *','* | *'&'* | *'*'* | *'!'* | *'|'* | *'>'* | *'<'* \ + | *'?'* | *'%'* | *'@'* | *'`'* | *"'"* | *'"'*) + return 0 + ;; + esac + # Values that look like YAML booleans/null or numbers stay bare per the + # current predicate. The predicate's value space is the schema's + # canonical fields and known extensions; expand here if a future + # extension introduces ambiguity. + return 1 +} + # Require each named command to be on PATH. Exits 1 with a clear message # when one is missing. require_commands() { diff --git a/packages/agents/content/skills/_data/artifact-conventions.md b/packages/agents/content/skills/_data/artifact-conventions.md index bf89f028..1bc402de 100644 --- a/packages/agents/content/skills/_data/artifact-conventions.md +++ b/packages/agents/content/skills/_data/artifact-conventions.md @@ -205,6 +205,15 @@ The table below lists only the universal fields. Artifact-specific extensions (` Skills resolve `pr` at write time via the shared dispatch documented in [`pr-resolution.md`](pr-resolution.md). On failure, the `pr:` line is omitted and the skill emits the canonical warning text — the artifact write itself is never blocked. +### Bespoke frontmatter composition + +Most skills and subagents produce frontmatter by running `resolve-frontmatter.sh` in its default YAML mode and prepending the output verbatim. Two sites are deliberate exceptions and opt into `--format json` to compose the YAML block themselves: + +- `refine-plan` — the `provenance:` block is case-branched on the input artifact's existing provenance (preserving `skill`, `baseSha`, `isInteractive`, and `iteration` from the original authoring skill, with fallbacks when the input has no provenance). The shell flag surface cannot express this conditional logic cleanly. +- `wrap-up` (deferred-findings artifact) — `tickets_created` is a list of `{id, items}` objects, a structure that has no clean CLI expression and is best composed in the skill's own logic. + +These two sites read the script's JSON output, then write the YAML frontmatter themselves. The pattern is intentional, not a workaround — keep new skills on the YAML mode path unless they have a similarly structural reason to deviate. + ## Plan provenance This artifact uses the [universal artifact frontmatter](#universal-artifact-frontmatter) plus the following artifact-specific extension: diff --git a/packages/agents/content/skills/create-devlog/SKILL.md b/packages/agents/content/skills/create-devlog/SKILL.md index 46fe2030..d2cfefae 100644 --- a/packages/agents/content/skills/create-devlog/SKILL.md +++ b/packages/agents/content/skills/create-devlog/SKILL.md @@ -17,24 +17,9 @@ Summarize changes made in recent commits or the working tree. ## Output format -The devlog file begins with YAML frontmatter conforming to the canonical schema (see [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the devlog-specific section, [Devlog frontmatter](../_data/artifact-conventions.md#devlog-frontmatter)) followed by the markdown body: +The devlog file begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the devlog-specific extensions in [Devlog frontmatter](../_data/artifact-conventions.md#devlog-frontmatter). The body following the frontmatter has this structure: ```markdown ---- -provenance: - skill: create-devlog - timestamp: 2026-04-18T15:30:00Z - baseSha: 4f8b158 - isInteractive: true -ticket_id: '426' -ticket_ref: '#426' -run_id: 20260419-012539Z -branch: 426/feat/example-branch -commit: 1d2c3b4 -pr: https://github.com/williamthorsen/codeassembly/pull/591 -commits: [a1b2c3d, e4f5g6h] ---- - # Devlog: {Concise description} **Date**: {YYYY-MM-DD HH:MM UTC} @@ -97,16 +82,28 @@ Follow [artifact conventions](../_data/artifact-conventions.md). The devlog frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema plus the devlog-specific extensions listed in [Devlog frontmatter](../_data/artifact-conventions.md#devlog-frontmatter). - +Resolve `$run_id_arg` from the `--run-id={id}` argument (empty when not supplied). Resolve the `commits` extension according to the mode: + +- No argument (last commit): `commits_arg=$(git log -n 1 --format=%h)`. +- `` (last N commits): `commits_arg=$(git log -n N --format=%h | paste -sd, -)`. +- `working-tree`: do not pass `--extra-list commits=...`. + +Run via Bash, substituting the resolved arguments: + +```bash +resolve-frontmatter.sh \ + --skill create-devlog \ + --interactive true \ + --model "$MODEL_ID" \ + --extra-list "commits=$commits_arg" \ + --override "run_id=$run_id_arg" +``` + +(Omit the `--extra-list commits=...` argument entirely in `working-tree` mode; quoting `run_id=$run_id_arg` ensures the empty-value force-omit case works when no `--run-id` was supplied.) + +Prepend the script's output verbatim to the artifact body. Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `create-devlog`. -- `provenance.isInteractive`: always `true`. -- `run_id`: **override** the script's value. Emit only if `--run-id={id}` was supplied as an argument; the caller is the source of truth. Do not use the script's breadcrumb-derived value, and do not perform any filesystem discovery. -- `commits` (devlog-specific extension; distinct from the universal `commit` field): - - No argument (last commit): single short SHA from `git log -n 1 --format=%h`. - - `` (last N commits): list of N short SHAs from `git log -n {N} --format=%h`. - - `working-tree`: omit the `commits` field entirely. - +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ### Filename examples diff --git a/packages/agents/content/skills/create-ticket/SKILL.md b/packages/agents/content/skills/create-ticket/SKILL.md index 8065f961..883985a0 100644 --- a/packages/agents/content/skills/create-ticket/SKILL.md +++ b/packages/agents/content/skills/create-ticket/SKILL.md @@ -151,12 +151,11 @@ If a plan is also saved in step 7, it uses the same frontmatter shape with `prov The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `create-ticket`. -- `provenance.isInteractive`: always `true`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill create-ticket --interactive true --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ### 7. Save plan (if present) diff --git a/packages/agents/content/skills/design-and-plan/SKILL.md b/packages/agents/content/skills/design-and-plan/SKILL.md index c5fea1c5..26add7d9 100644 --- a/packages/agents/content/skills/design-and-plan/SKILL.md +++ b/packages/agents/content/skills/design-and-plan/SKILL.md @@ -204,10 +204,9 @@ Present the plan to the user. Revise until approved. 2. Resolve frontmatter fields for both artifacts. The frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. - - - `provenance.skill`: always `design-and-plan`. - - `provenance.isInteractive`: always `true`. - + Run `resolve-frontmatter.sh --skill design-and-plan --interactive true` via Bash. Prepend the output verbatim to each artifact body. + + If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. 3. Save both artifacts following `save-artifact` naming conventions: - Ticket: `{YYYYMMDD-HHMMSSZ}_{slug}_ticket.md` diff --git a/packages/agents/content/skills/ex-post-facto/SKILL.md b/packages/agents/content/skills/ex-post-facto/SKILL.md index 88d62673..cab0aedc 100644 --- a/packages/agents/content/skills/ex-post-facto/SKILL.md +++ b/packages/agents/content/skills/ex-post-facto/SKILL.md @@ -79,12 +79,11 @@ pr: '{full PR URL, omit if not resolved}' The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `ex-post-facto`. -- `provenance.isInteractive`: always `true`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill ex-post-facto --interactive true --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Saving diff --git a/packages/agents/content/skills/orchestrate/SKILL.md b/packages/agents/content/skills/orchestrate/SKILL.md index dd76ad4c..e57d12a7 100644 --- a/packages/agents/content/skills/orchestrate/SKILL.md +++ b/packages/agents/content/skills/orchestrate/SKILL.md @@ -743,11 +743,9 @@ Include: This section governs the frontmatter resolution for both orchestrator-written artifacts — the run-manifest (step 5) and the run-summary (Phase 5) — which use identical field-resolution logic. - +Run `resolve-frontmatter.sh --skill orchestrate --interactive false` via Bash. Prepend the output verbatim to the artifact body. -- `provenance.skill`: always `orchestrate`. -- `provenance.isInteractive`: always `false`. - +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. The orchestrator's `provenance.model` is omitted — the run-summary aggregates work from many subagents, each with its own model recorded in its own artifact. The summary itself is composed by the orchestrator and is not a single-model artifact. diff --git a/packages/agents/content/skills/plan-orchestrable-steps/SKILL.md b/packages/agents/content/skills/plan-orchestrable-steps/SKILL.md index 0b5e1f3e..50284f8f 100644 --- a/packages/agents/content/skills/plan-orchestrable-steps/SKILL.md +++ b/packages/agents/content/skills/plan-orchestrable-steps/SKILL.md @@ -86,10 +86,9 @@ When the user approves the plan: 1. Resolve frontmatter fields. The frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. - - - `provenance.skill`: always `plan-orchestrable-steps`. - - `provenance.isInteractive`: always `true`. - + Run `resolve-frontmatter.sh --skill plan-orchestrable-steps --interactive true` via Bash. Prepend the output verbatim to the artifact body. + + If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. 2. Add a frontmatter header to the latest plan markdown snapshot. List `{artifact-dir}/*_planner_orchestration-plan.md` files, sort lexicographically descending, and take the first (most recent by timestamp prefix). If no matching files are found, skip the frontmatter header step — the planner did not produce a markdown snapshot. Read the file, prepend the resolved frontmatter, and write back. diff --git a/packages/agents/content/skills/plan/SKILL.md b/packages/agents/content/skills/plan/SKILL.md index 2e859969..ab684641 100644 --- a/packages/agents/content/skills/plan/SKILL.md +++ b/packages/agents/content/skills/plan/SKILL.md @@ -15,23 +15,11 @@ Create a structured plan document for analysis, design, or implementation work. ## Output format -The plan begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema and the [plan provenance](../_data/artifact-conventions.md#plan-provenance) extension. `provenance.model` is omitted — plans authored via this skill are co-authored interactively, not solely AI-generated. +The plan begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the [plan provenance](../_data/artifact-conventions.md#plan-provenance) extension; field-resolution steps live in the [Frontmatter resolution](#frontmatter-resolution) section below. `provenance.model` is omitted — plans authored via this skill are co-authored interactively, not solely AI-generated. -```markdown ---- -provenance: - skill: plan - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: true -ticket_id: '{ticket ID from session context, omit if null}' -ticket_ref: '{ticket display ref, omit if null}' -branch: '{branch name from session context}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -run_id: '{run id, omit when not in an orchestrated run}' ---- +The body following the frontmatter has this structure: +```markdown # Plan: {Descriptive title} **Date**: {YYYY-MM-DD HH:MM UTC} @@ -77,11 +65,9 @@ Resolve artifact directory based on context. The artifact frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. - +Run `resolve-frontmatter.sh --skill plan --interactive true` via Bash. Prepend the output verbatim to the artifact body. -- `provenance.skill`: always `plan`. -- `provenance.isInteractive`: always `true`. - +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ### Run context diff --git a/packages/agents/content/skills/refine-plan/SKILL.md b/packages/agents/content/skills/refine-plan/SKILL.md index c58c08f5..fd029637 100644 --- a/packages/agents/content/skills/refine-plan/SKILL.md +++ b/packages/agents/content/skills/refine-plan/SKILL.md @@ -151,11 +151,13 @@ Stamp the revised plan with frontmatter conforming to the [universal artifact fr The stamp writes the full canonical schema in one atomic write: the `provenance:` block plus the top-level canonical fields. The top-level fields come from the script; the `provenance:` block is computed from `{input-provenance}` plus the stamping logic below. - +This site uses `--format json` because the `provenance:` block is case-branched on the input artifact's existing provenance — see [artifact-conventions.md](../_data/artifact-conventions.md#bespoke-frontmatter-composition). -The `provenance:` block is **not** populated from the script. Construct it manually per the case branches below. +Run `resolve-frontmatter.sh --format json` via Bash. It emits a JSON object with the universal artifact fields (`branch`, `commit`, `baseSha`, `pr`, `ticket_id`, `ticket_ref`, `platform`, `timestamp`, `run_id`). Use those values verbatim for the matching YAML keys. Optional fields the script omits from its output (`baseSha`, `pr`, `ticket_id`, `ticket_ref`, `run_id`) must be omitted from the frontmatter too — do not emit `null` or empty strings. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. - +The `provenance:` block is **not** populated from the script. Construct it manually per the case branches below. **Round-trip preservation:** the top-level canonical fields (`branch`, `commit`, `pr`, etc.) are always re-resolved from current session context via the script — they are not carried forward from `{input-provenance}`. This is correct: the output is a new artifact at a new point in time on a potentially different branch. The `provenance:` block's camelCase convention (`baseSha`, `isInteractive`, `refinedBy`) is preserved as-is on both read and write — there is no rename. Provenance fields carried forward from the input are `skill`, `baseSha`, `isInteractive`, and `iteration` (per the case branches). diff --git a/packages/agents/content/skills/respond-to-review/SKILL.md b/packages/agents/content/skills/respond-to-review/SKILL.md index 9e31e40d..08572292 100644 --- a/packages/agents/content/skills/respond-to-review/SKILL.md +++ b/packages/agents/content/skills/respond-to-review/SKILL.md @@ -32,13 +32,21 @@ This skill bridges the gap between receiving a code review and implementing fixe The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. Set `$review_filename` to the bare filename of the review being responded to (e.g., `09_reviewer_review.md`). -- `provenance.skill`: always `respond-to-review`. -- `provenance.isInteractive`: always `true`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. -- `responding_to` (response-artifact extension): the bare filename of the review being responded to (e.g., `09_reviewer_review.md`). - +Run via Bash: + +```bash +resolve-frontmatter.sh \ + --skill respond-to-review \ + --interactive true \ + --model "$MODEL_ID" \ + --extra "responding_to=$review_filename" +``` + +Prepend the script's output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Locating the review @@ -126,24 +134,11 @@ Per [artifact conventions](../_data/artifact-conventions.md#disposition-rules): When `ticket_ref` is null (no ticket on the branch), omit the `{ticket_ref}: ` portion so the heading reads `# Change summary: {description}`. -The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. The `responding_to` field is a response-artifact-specific extension that records the review being addressed. +The artifact begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section above. The `responding_to` field is a response-artifact-specific extension that records the review being addressed. -```markdown ---- -provenance: - skill: respond-to-review - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: true - model: '{model id}' -ticket_id: '{ticket ID from session context, omit if null}' -ticket_ref: '{ticket display ref, omit if null}' -branch: '{branch name from session context}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -responding_to: '{filename of the review being responded to}' ---- +The body following the frontmatter has this structure: +```markdown # Change summary: {ticket_ref}: {description} ## Changes made diff --git a/packages/agents/content/skills/review-branch/SKILL.md b/packages/agents/content/skills/review-branch/SKILL.md index aba7aff6..782db29c 100644 --- a/packages/agents/content/skills/review-branch/SKILL.md +++ b/packages/agents/content/skills/review-branch/SKILL.md @@ -42,12 +42,21 @@ This skill is the canonical home of the shared review process. `review-pr` invok The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. Resolve `$author` from `git log --format='%an' "$default_branch..HEAD" | sort -u | paste -sd, -` (unique authors of the commits under review). -- `provenance.skill`: always `review-branch`. -- `provenance.isInteractive`: always `true`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run via Bash: + +```bash +resolve-frontmatter.sh \ + --skill review-branch \ + --interactive true \ + --model "$MODEL_ID" \ + --extra "author=$author" +``` + +Prepend the script's output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Review guidelines @@ -78,24 +87,11 @@ Section-header icons (🚨, ⚠️, 📋, 🧠, ☝️, 🔍) come from the cano When `ticket_ref` is null (no ticket on the branch), omit the `{ticket_ref}: ` portion so the heading reads naturally without it — e.g., `# Code review: {description}`. -The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema: +The artifact begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section above. Pass `--extra "author=$author"` to the script to populate the review-artifact `author` field. -```markdown ---- -provenance: - skill: review-branch - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: true - model: '{model id}' -ticket_id: '{ticket ID from session context, omit if null}' -ticket_ref: '{ticket display ref, omit if null}' -branch: '{branch name from session context}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -author: '{commit author(s)}' ---- +The body following the frontmatter has this structure: +```markdown # Code review: {ticket_ref}: {description in imperative mood} ## Summary of changes diff --git a/packages/agents/content/skills/save-plan/SKILL.md b/packages/agents/content/skills/save-plan/SKILL.md index d5e3aaf6..41b2d1ec 100644 --- a/packages/agents/content/skills/save-plan/SKILL.md +++ b/packages/agents/content/skills/save-plan/SKILL.md @@ -22,10 +22,9 @@ Save the plan from the current conversation as a ticket-scoped artifact. Useful The frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema plus the [plan provenance](../_data/artifact-conventions.md#plan-provenance) extensions. - - - `provenance.skill`: always `plan-mode`. - - `provenance.isInteractive`: always `true`. - + Run `resolve-frontmatter.sh --skill plan-mode --interactive true` via Bash. Prepend the output verbatim to the artifact body. + + If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. 5. **Save** as ticket-level artifact: diff --git a/packages/agents/content/skills/summarize-change/SKILL.md b/packages/agents/content/skills/summarize-change/SKILL.md index d022ce15..0e7c17ed 100644 --- a/packages/agents/content/skills/summarize-change/SKILL.md +++ b/packages/agents/content/skills/summarize-change/SKILL.md @@ -32,27 +32,11 @@ If expected information is missing, stop and ask the developer. ## Output format -The artifact begins with a single YAML frontmatter block that unifies canonical fields from the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) with change-summary-specific consumer fields documented at [Change-summary frontmatter](../_data/artifact-conventions.md#change-summary-frontmatter). Ordering: `provenance:` first, then top-level canonical fields, then consumer fields. `commit:` and `ticket_id:` appear exactly once each. +The artifact begins with a single YAML frontmatter block that unifies canonical fields from the canonical schema with change-summary-specific consumer fields — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the consumer-field extensions in [Change-summary frontmatter](../_data/artifact-conventions.md#change-summary-frontmatter). Ordering: `provenance:` first, then top-level canonical fields, then consumer fields. `commit:` and `ticket_id:` appear exactly once each. Field-resolution steps live in the [Canonical-field resolution](#canonical-field-resolution) section below. -```markdown ---- -provenance: - skill: summarize-change - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: true - model: '{model id}' -branch: '{branch name from session context}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -ticket_id: '{ticket ID from session context, omit if null}' -ticket_ref: '{ticket display ref, omit if null}' -run_id: '{run id, omit when not in an orchestrated run}' -title: '{bare title without `ticket_ref` prefix}' -scope: '{scope inferred from commit prefixes, or omitted if ambiguous}' -type: '{work type inferred from commit prefixes, or omitted if ambiguous}' ---- +The body following the frontmatter has this structure: +```markdown # {ticket_ref} {title} ## What @@ -115,11 +99,23 @@ The block is structured as: ### Canonical-field resolution - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. Resolve `$title`, `$scope`, and `$type` from the consumer-field inference below (omit a flag if the corresponding value cannot be inferred unambiguously). + +Run via Bash: + +```bash +resolve-frontmatter.sh \ + --skill summarize-change \ + --interactive true \ + --model "$MODEL_ID" \ + --extra "title=$title" \ + --extra "scope=$scope" \ + --extra "type=$type" +``` + +Prepend the script's output verbatim to the artifact body. -- `provenance.skill`: always `summarize-change`. -- `provenance.isInteractive`: always `true`. - +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ### Consumer-field inference diff --git a/packages/agents/content/skills/summarize-chat/SKILL.md b/packages/agents/content/skills/summarize-chat/SKILL.md index 28f72e88..3d63b95a 100644 --- a/packages/agents/content/skills/summarize-chat/SKILL.md +++ b/packages/agents/content/skills/summarize-chat/SKILL.md @@ -23,23 +23,11 @@ Replace `/Users/{username}/` with `~/` in file paths. Remove similar personal in ## Output format -The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. See [Frontmatter resolution](#frontmatter-resolution) below for field resolution. +The artifact begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section below. -```markdown ---- -provenance: - skill: summarize-chat - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: true - model: '{model id}' -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' ---- +The body following the frontmatter has this structure: +```markdown # {Descriptive title} ## Problem @@ -83,12 +71,11 @@ Use these to mark significant sections: The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. + +Run `resolve-frontmatter.sh --skill summarize-chat --interactive true --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. -- `provenance.skill`: always `summarize-chat`. -- `provenance.isInteractive`: always `true`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Saving diff --git a/packages/agents/content/skills/wrap-up/SKILL.md b/packages/agents/content/skills/wrap-up/SKILL.md index 79a9d51b..28b823a0 100644 --- a/packages/agents/content/skills/wrap-up/SKILL.md +++ b/packages/agents/content/skills/wrap-up/SKILL.md @@ -331,14 +331,19 @@ Prepend YAML frontmatter, then the markdown body. **Frontmatter** — see [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) for the canonical schema and [Deferred-findings frontmatter](../_data/artifact-conventions.md#deferred-findings-frontmatter) for the artifact-specific extensions. - +This site uses `--format json` because `tickets_created` is a list-of-objects extension that has no clean CLI expression — see [artifact-conventions.md](../_data/artifact-conventions.md#bespoke-frontmatter-composition). + +Run `resolve-frontmatter.sh --format json` via Bash. It emits a JSON object with the universal artifact fields (`branch`, `commit`, `baseSha`, `pr`, `ticket_id`, `ticket_ref`, `platform`, `timestamp`, `run_id`). Use those values verbatim for the matching YAML keys. Optional fields the script omits from its output (`baseSha`, `pr`, `ticket_id`, `ticket_ref`, `run_id`) must be omitted from the frontmatter too — do not emit `null` or empty strings. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. + +Set these skill-specific values inline (not in the script's output): - `provenance.skill`: always `wrap-up`. - `provenance.isInteractive`: always `true`. - `run_id`: **override** the script's value — reuse the run ID Phase 1a captured (also passed to `/create-devlog --run-id` in Phase 3). Emit only when wrap-up was invoked from an orchestrated session. - `session_type` (deferred-findings extension): the classification produced by Phase 1a's session-type detection (`orchestrated`, `interactive-dev`, `review`, or `research`). - `tickets_created` (deferred-findings extension): list of `{id, items}` entries cross-referencing each created ticket to the wrap-up item IDs it addresses. `items` is always a list. Omit when empty. - **Body** — emit the tickets-created cross-reference and the dropped-findings record: diff --git a/packages/agents/content/subagents/aspect-code-reviewer.md b/packages/agents/content/subagents/aspect-code-reviewer.md index 64b8c359..09782eae 100644 --- a/packages/agents/content/subagents/aspect-code-reviewer.md +++ b/packages/agents/content/subagents/aspect-code-reviewer.md @@ -69,12 +69,11 @@ If the review concluded with no findings, the finalized form omits the `### Find The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `aspect-code-reviewer`. -- `provenance.isInteractive`: always `false`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill aspect-code-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Scope diff --git a/packages/agents/content/subagents/aspect-silent-failure-reviewer.md b/packages/agents/content/subagents/aspect-silent-failure-reviewer.md index 586b5b30..c2acfc92 100644 --- a/packages/agents/content/subagents/aspect-silent-failure-reviewer.md +++ b/packages/agents/content/subagents/aspect-silent-failure-reviewer.md @@ -63,12 +63,11 @@ If the review concluded with no findings (or no error-handling code was present) The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `aspect-silent-failure-reviewer`. -- `provenance.isInteractive`: always `false`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill aspect-silent-failure-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Scope diff --git a/packages/agents/content/subagents/aspect-test-reviewer.md b/packages/agents/content/subagents/aspect-test-reviewer.md index 0597f810..c3565498 100644 --- a/packages/agents/content/subagents/aspect-test-reviewer.md +++ b/packages/agents/content/subagents/aspect-test-reviewer.md @@ -72,12 +72,11 @@ If the review concluded with no findings (or no source files required test cover The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `aspect-test-reviewer`. -- `provenance.isInteractive`: always `false`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill aspect-test-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Scope diff --git a/packages/agents/content/subagents/code-simplification-reviewer.md b/packages/agents/content/subagents/code-simplification-reviewer.md index def4d65f..610b7cd2 100644 --- a/packages/agents/content/subagents/code-simplification-reviewer.md +++ b/packages/agents/content/subagents/code-simplification-reviewer.md @@ -64,12 +64,11 @@ If the review concluded with no findings, the finalized form omits the `### Find The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `code-simplification-reviewer`. -- `provenance.isInteractive`: always `false`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill code-simplification-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Scope diff --git a/packages/agents/content/subagents/orchestrated-architect.md b/packages/agents/content/subagents/orchestrated-architect.md index 4c3fd48a..bd110b34 100644 --- a/packages/agents/content/subagents/orchestrated-architect.md +++ b/packages/agents/content/subagents/orchestrated-architect.md @@ -58,24 +58,9 @@ Classify the task into exactly one impact level: Write your analysis to the file path provided in your task prompt using the {tool:Write} tool. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). -The document MUST include: +The body following the frontmatter MUST include: ```markdown ---- -provenance: - skill: orchestrated-architect - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: false - model: '{model id}' -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -run_id: '{run id}' ---- - ### Impact level: {none|low|medium|high} ### Summary @@ -128,12 +113,11 @@ If the plan's assumptions all check out, omit this section. The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. + +Run `resolve-frontmatter.sh --skill orchestrated-architect --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. -- `provenance.skill`: Always `orchestrated-architect`. -- `provenance.isInteractive`: Always `false`. -- `provenance.model`: The model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Principles diff --git a/packages/agents/content/subagents/orchestrated-coder.md b/packages/agents/content/subagents/orchestrated-coder.md index 3600951a..a7a79bed 100644 --- a/packages/agents/content/subagents/orchestrated-coder.md +++ b/packages/agents/content/subagents/orchestrated-coder.md @@ -137,12 +137,11 @@ completed The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `orchestrated-coder`. -- `provenance.isInteractive`: always `false`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill orchestrated-coder --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Reviewer-context sidecar diff --git a/packages/agents/content/subagents/orchestrated-planner.md b/packages/agents/content/subagents/orchestrated-planner.md index e0c7fd2e..db1b99be 100644 --- a/packages/agents/content/subagents/orchestrated-planner.md +++ b/packages/agents/content/subagents/orchestrated-planner.md @@ -148,12 +148,11 @@ Write the plan JSON file to the path provided in the task prompt. Format: The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: Always `orchestrated-planner`. -- `provenance.isInteractive`: Always `false`. -- `provenance.model`: The model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill orchestrated-planner --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Constraints diff --git a/packages/agents/content/subagents/orchestrated-reviewer.md b/packages/agents/content/subagents/orchestrated-reviewer.md index fd93a5c3..a51acb5a 100644 --- a/packages/agents/content/subagents/orchestrated-reviewer.md +++ b/packages/agents/content/subagents/orchestrated-reviewer.md @@ -73,12 +73,11 @@ If the review concluded with no findings, the finalized form omits the `### Find The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: always `orchestrated-reviewer`. -- `provenance.isInteractive`: always `false`. -- `provenance.model`: the model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill orchestrated-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Finding format diff --git a/packages/agents/content/subagents/plan-reviewer.md b/packages/agents/content/subagents/plan-reviewer.md index c9af000d..1cb3069d 100644 --- a/packages/agents/content/subagents/plan-reviewer.md +++ b/packages/agents/content/subagents/plan-reviewer.md @@ -65,26 +65,11 @@ Each finding is tagged with a resolution type: ## Output format -Write the review to the output path provided in your task prompt. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). +Write the review to the output path provided in your task prompt. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) — see the [Frontmatter](#frontmatter) section below for field resolution. **Section organization:** Sections are grouped by **resolution type** (auto vs user), not by finding category (C vs X). Place every `auto`-tagged finding -- whether C or X -- in "Auto-resolvable findings". Place every `user`-tagged finding -- whether C or X -- in "Decision gaps". Every finding in "Decision gaps" must include a **Question** field. ```markdown ---- -provenance: - skill: plan-reviewer - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: false - model: '{model id}' -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -run_id: '{run id, omit if not in a run}' ---- - # Plan review ## Summary @@ -152,12 +137,11 @@ If the plan has no findings at all, write: The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. + +Run `resolve-frontmatter.sh --skill plan-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. -- `provenance.skill`: Always `plan-reviewer`. -- `provenance.isInteractive`: Always `false`. -- `provenance.model`: The model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Principles diff --git a/packages/agents/content/subagents/planner.md b/packages/agents/content/subagents/planner.md index f12ce01a..3356122b 100644 --- a/packages/agents/content/subagents/planner.md +++ b/packages/agents/content/subagents/planner.md @@ -138,12 +138,11 @@ run_id: '{run id, omit if not in a run}' The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). - +Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. -- `provenance.skill`: Always `planner`. -- `provenance.isInteractive`: Always `false`. -- `provenance.model`: The model identifier you are executing under. Read this from your system-prompt environment block — the line `model named ... model ID is ...`. - +Run `resolve-frontmatter.sh --skill planner --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. + +If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. ## Resumption diff --git a/packages/agents/content/subagents/savings-analyzer.md b/packages/agents/content/subagents/savings-analyzer.md index 6ce0b8cb..c78df9cf 100644 --- a/packages/agents/content/subagents/savings-analyzer.md +++ b/packages/agents/content/subagents/savings-analyzer.md @@ -64,24 +64,11 @@ Tag every suggestion: ## Output format -Write a markdown artifact with this structure. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). +Write a markdown artifact with this structure. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) — see the [Frontmatter](#frontmatter) section below for field resolution. -``` ---- -provenance: - skill: savings-analyzer - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: false - model: '{model id}' -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -run_id: '{run id}' ---- +The body following the frontmatter MUST include: +``` # Savings analysis ## Summary From 20297c638bf13168dded9f93e2fcd016ffc0175a Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Sun, 17 May 2026 06:19:20 -0700 Subject: [PATCH 2/6] agents|fix: Omit empty scalar extensions in YAML frontmatter output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `resolve-frontmatter.sh --extra KEY=` now omits the field entirely from the emitted YAML, matching the documented omission rule. Previously, passing an empty scalar extension produced `KEY: ''` in the output, contradicting the script's stated behavior. Flow-list extensions (`--extra-list`) continue to emit `KEY: []` for empty values — that distinction is intentional and is now called out in an inline comment. The `summarize-change` and `create-devlog` skill snippets switch to bash conditional expansion (`${var:+--extra "key=$var"}`) so they are self-evidently correct regardless of the script's omission behavior. The snippets previously passed `--extra` flags unconditionally, which would have exercised the empty-extension bug in common code paths (mixed-prefix commits for `summarize-change`, working-tree mode for `create-devlog`). Additional hardening: - `emit_yaml_flow_list` now disables globbing around the word-split, preventing silent filesystem expansion of list elements containing `*`, `?`, or `[...]`. - Duplicate `--extra` / `--extra-list` keys now emit a warning to stderr before overwriting the previous value, matching the script's lenient philosophy for optional fields. Tests: - `main()` argument-validation paths (missing `--skill`, missing `--interactive`, `--format xml`, invalid `--interactive` value) now have explicit coverage. - An end-to-end `main()` test drives a stubbed git environment and asserts that `--extra`, `--extra-list`, and `--override KEY=` flow through to the emitted YAML. - `needs_yaml_quoting` gains test cases for the previously-untested glyphs `*`, `&`, `!`, `>`, `<`, `%`, `@`, and `"`. - A JSON-mode snapshot test pins the complete byte-equivalent output for a fully-populated argument set, protecting the backward-compatibility guarantee. --- .../__tests__/resolve_frontmatter_test.sh | 155 ++++++++++++++++++ .../content/scripts/resolve-frontmatter.sh | 18 ++ .../content/skills/create-devlog/SKILL.md | 4 +- .../content/skills/summarize-change/SKILL.md | 6 +- 4 files changed, 179 insertions(+), 4 deletions(-) diff --git a/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh b/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh index 4b9afeff..02a48103 100644 --- a/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh +++ b/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh @@ -75,6 +75,26 @@ It "emits run_id when present" When call emit_json "main" "abc1234" "" "" "" "" "github" "2026-05-16T00:00:00Z" "20260516-143946Z" The output should include '"run_id": "20260516-143946Z"' End + +It "emits a fully-populated argument set in canonical key order" +expected_json() { + cat <<'JSON' +{ + "branch": "main", + "commit": "abc1234", + "platform": "github", + "timestamp": "2026-05-16T00:00:00Z", + "baseSha": "deadbee", + "pr": "https://github.com/x/y/pull/1", + "ticket_id": "537", + "ticket_ref": "#537", + "run_id": "20260516-143946Z" +} +JSON +} +When call emit_json "main" "abc1234" "deadbee" "https://github.com/x/y/pull/1" "537" "#537" "github" "2026-05-16T00:00:00Z" "20260516-143946Z" +The output should equal "$(expected_json)" +End End Describe "warn_pr_failure" @@ -229,6 +249,46 @@ It "returns true for values containing backtick" When call needs_yaml_quoting "a\`b" The status should be success End + +It "returns true for values containing an asterisk" +When call needs_yaml_quoting "a*b" +The status should be success +End + +It "returns true for values containing an ampersand" +When call needs_yaml_quoting "a&b" +The status should be success +End + +It "returns true for values containing an exclamation mark" +When call needs_yaml_quoting "a!b" +The status should be success +End + +It "returns true for values containing a greater-than sign" +When call needs_yaml_quoting "a>b" +The status should be success +End + +It "returns true for values containing a less-than sign" +When call needs_yaml_quoting "a/dev/null +} + +cleanup_main_validation() { + popd >/dev/null + rm -rf "$tmpdir" +} + +BeforeEach "setup_main_validation" +AfterEach "cleanup_main_validation" + +It "exits non-zero with a diagnostic when --skill is missing in yaml mode" +When run main --format yaml --interactive true +The status should be failure +The stderr should include "--skill is required" +End + +It "exits non-zero with a diagnostic when --interactive is missing in yaml mode" +When run main --format yaml --skill foo +The status should be failure +The stderr should include "--interactive is required" +End + +It "exits non-zero with a diagnostic when --format is xml" +When run main --format xml --skill foo --interactive true +The status should be failure +The stderr should include "unknown --format" +End + +It "exits non-zero with a diagnostic when --interactive value is neither true nor false" +When run main --format yaml --skill foo --interactive maybe +The status should be failure +The stderr should include "--interactive must be true or false" +End +End + +Describe "main end-to-end" +setup_main_e2e() { + tmpdir=$(mktemp -d) + pushd "$tmpdir" >/dev/null + # Initialize a minimal git repository so `current_branch` and + # `git rev-parse --short HEAD` succeed. + git init --quiet --initial-branch=main . + git config user.email "test@example.com" + git config user.name "Test" + git commit --allow-empty --quiet -m "initial" + # Write the branch manifest the script reads for session-level fields. + mkdir -p .agents + cat >.agents/main.branch-manifest.json <<'JSON' +{ + "platform": "github", + "ticket_id": "537", + "ticket_ref": "#537", + "default_branch": "HEAD" +} +JSON + # Stub `gh` to return empty (no PR), so the test is hermetic. + mkdir -p bin + cat >bin/gh <<'SH' +#!/usr/bin/env bash +exit 0 +SH + chmod +x bin/gh + ORIGINAL_PATH="$PATH" + PATH="$tmpdir/bin:$PATH" +} + +cleanup_main_e2e() { + PATH="$ORIGINAL_PATH" + popd >/dev/null + rm -rf "$tmpdir" +} + +BeforeEach "setup_main_e2e" +AfterEach "cleanup_main_e2e" + +It "emits extension fields end-to-end and force-omits run_id via --override KEY=" +When run main \ + --skill foo \ + --interactive true \ + --extra "alpha=1" \ + --extra-list "tags=a,b" \ + --override "run_id=" +The status should be success +The output should include "skill: foo" +The output should include "isInteractive: true" +The output should include "alpha: 1" +The output should include "tags: [a, b]" +The output should not include "run_id" +End +End diff --git a/packages/agents/content/scripts/resolve-frontmatter.sh b/packages/agents/content/scripts/resolve-frontmatter.sh index b448448f..a63e4e8a 100755 --- a/packages/agents/content/scripts/resolve-frontmatter.sh +++ b/packages/agents/content/scripts/resolve-frontmatter.sh @@ -208,6 +208,8 @@ add_extra() { [[ -n "$key" ]] || fail "--extra/--extra-list argument has empty key" if [[ -z "${kinds_ref[$key]:-}" ]]; then keys_ref+=("$key") + else + warn "duplicate --extra/--extra-list key '$key': replacing previous value" fi values_ref["$key"]="$value" kinds_ref["$key"]="$kind" @@ -460,8 +462,12 @@ emit_yaml() { value="${yaml_extra_values[$key]}" kind="${yaml_extra_kinds[$key]}" if [[ "$kind" == "list" ]]; then + # Flow-list extensions intentionally emit `key: []` for empty values + # (see `emit_yaml_flow_list`); only scalar extensions are subject to + # the canonical-field omission rule. emit_yaml_flow_list "$key" "$value" else + [[ -n "$value" ]] || continue emit_yaml_scalar "$key" "$value" fi done @@ -491,8 +497,13 @@ emit_yaml_flow_list() { return fi local IFS=',' + # Disable globbing around the word-split so list elements containing + # glob metacharacters (`*`, `?`, `[...]`) are not expanded against the + # filesystem before `yaml_quote` sees them. + set -f # shellcheck disable=SC2206 local -a parts=( $raw ) + set +f local out="" i for ((i = 0; i < ${#parts[@]}; i++)); do if [[ "$i" -gt 0 ]]; then @@ -570,6 +581,13 @@ fail() { exit 1 } +# Print a non-fatal warning to stderr. Used for soft-failure conditions +# where the script should continue with degraded behavior (e.g., a +# duplicate optional-extension key that overwrites an earlier value). +warn() { + echo "$PROG: warning: $1" >&2 +} + # Guard against execution when sourced (e.g., from shellspec tests). if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" diff --git a/packages/agents/content/skills/create-devlog/SKILL.md b/packages/agents/content/skills/create-devlog/SKILL.md index d2cfefae..7982a5d4 100644 --- a/packages/agents/content/skills/create-devlog/SKILL.md +++ b/packages/agents/content/skills/create-devlog/SKILL.md @@ -95,11 +95,11 @@ resolve-frontmatter.sh \ --skill create-devlog \ --interactive true \ --model "$MODEL_ID" \ - --extra-list "commits=$commits_arg" \ + ${commits_arg:+--extra-list "commits=$commits_arg"} \ --override "run_id=$run_id_arg" ``` -(Omit the `--extra-list commits=...` argument entirely in `working-tree` mode; quoting `run_id=$run_id_arg` ensures the empty-value force-omit case works when no `--run-id` was supplied.) +The `${commits_arg:+--extra-list "commits=$commits_arg"}` form expands to the flag only when `$commits_arg` is non-empty, so the `working-tree` mode (where `$commits_arg` is unset) naturally omits the `commits` field rather than emitting `commits: []`. Quoting `run_id=$run_id_arg` ensures the empty-value force-omit case works when no `--run-id` was supplied. Prepend the script's output verbatim to the artifact body. Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. diff --git a/packages/agents/content/skills/summarize-change/SKILL.md b/packages/agents/content/skills/summarize-change/SKILL.md index 0e7c17ed..3b939eb7 100644 --- a/packages/agents/content/skills/summarize-change/SKILL.md +++ b/packages/agents/content/skills/summarize-change/SKILL.md @@ -109,10 +109,12 @@ resolve-frontmatter.sh \ --interactive true \ --model "$MODEL_ID" \ --extra "title=$title" \ - --extra "scope=$scope" \ - --extra "type=$type" + ${scope:+--extra "scope=$scope"} \ + ${type:+--extra "type=$type"} ``` +The `${var:+--extra "key=$var"}` form expands to the flag only when `$var` is non-empty, so an unresolved `scope` or `type` is naturally omitted from the emitted frontmatter. + Prepend the script's output verbatim to the artifact body. If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. From 3cecd443cebc3e4184e6b7c375b681ab00c787bd Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Sun, 17 May 2026 06:33:37 -0700 Subject: [PATCH 3/6] agents|test: Cover frontmatter helpers and drop remaining inline YAML Add shellspec coverage for three previously-untested behaviors in `resolve-frontmatter.sh`: the empty-scalar omission guard inside `emit_yaml`, both `fail` paths in `add_override` (missing `=`, empty key), and the duplicate-key overwrite path in `add_extra` (warning to stderr, single keys-array entry, last value wins). Remove the inline YAML frontmatter examples from `planner.md`, `orchestrated-planner.md`, `ex-post-facto/SKILL.md`, and the run-manifest and run-summary blocks in `orchestrate/SKILL.md`. Each section now points to the canonical example in `artifact-conventions.md` instead of duplicating the schema. Skill-tier files link to the canonical doc; subagent-tier files use a bare prose reference to satisfy the no-outbound-link policy enforced for files installed directly under the platform home. --- .../__tests__/resolve_frontmatter_test.sh | 58 +++++++++++++++++++ .../content/skills/ex-post-facto/SKILL.md | 16 +---- .../content/skills/orchestrate/SKILL.md | 32 +--------- .../content/subagents/orchestrated-planner.md | 17 +----- packages/agents/content/subagents/planner.md | 17 +----- 5 files changed, 63 insertions(+), 77 deletions(-) diff --git a/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh b/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh index 02a48103..3f79dce2 100644 --- a/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh +++ b/packages/agents/content/scripts/__tests__/resolve_frontmatter_test.sh @@ -381,6 +381,20 @@ When run add_extra "scalar" "=value" extra_keys extra_values extra_kinds The status should be failure The stderr should include "empty key" End + +It "warns and overwrites when the same key is added twice" +add_extra "scalar" "key=first" extra_keys extra_values extra_kinds +add_extra_again() { + add_extra "scalar" "key=second" extra_keys extra_values extra_kinds + # Emit observable state on stdout for the assertion to inspect. + echo "value=${extra_values[key]}" + echo "count=${#extra_keys[@]}" +} +When call add_extra_again +The stderr should include "duplicate" +The output should include "value=second" +The output should include "count=1" +End End Describe "apply_override" @@ -548,6 +562,50 @@ When call emit_yaml \ yaml_keys yaml_values yaml_kinds The output should include "title: 'Add: feature'" End + +It "omits scalar extensions whose value is empty" +yaml_keys+=("empty_field") +yaml_values[empty_field]="" +yaml_kinds[empty_field]="scalar" +When call emit_yaml \ + "summarize-change" "2026-05-16T00:00:00Z" "deadbee" "true" "" \ + "" "" "main" "abc1234" "" "" \ + yaml_keys yaml_values yaml_kinds +The output should not include "empty_field" +End +End + +Describe "add_override" +add_override_setup() { + unset overrides + declare -gA overrides=() +} + +BeforeEach "add_override_setup" + +It "records the override when the argument is well-formed" +add_override "branch=custom" overrides +When call echo "${overrides[branch]}" +The output should equal "custom" +End + +It "records an empty value (force-omit) when the argument is KEY=" +add_override "run_id=" overrides +When call test "${overrides[run_id]+set}" = "set" +The status should be success +End + +It "fails when the argument has no equals sign" +When run add_override "bad_arg" overrides +The status should be failure +The stderr should include "missing '='" +End + +It "fails when the key is empty" +When run add_override "=value" overrides +The status should be failure +The stderr should include "empty key" +End End Describe "main" diff --git a/packages/agents/content/skills/ex-post-facto/SKILL.md b/packages/agents/content/skills/ex-post-facto/SKILL.md index cab0aedc..63ccdfd8 100644 --- a/packages/agents/content/skills/ex-post-facto/SKILL.md +++ b/packages/agents/content/skills/ex-post-facto/SKILL.md @@ -20,23 +20,9 @@ git diff $DEFAULT_BRANCH...HEAD ## Output structure -The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. See [Frontmatter resolution](#frontmatter-resolution) below for field resolution. +The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. See [Frontmatter resolution](#frontmatter-resolution) below for field resolution. The frontmatter conforms to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). ```markdown ---- -provenance: - skill: ex-post-facto - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: true - model: '{model id}' -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' ---- - # {Title} ## Description diff --git a/packages/agents/content/skills/orchestrate/SKILL.md b/packages/agents/content/skills/orchestrate/SKILL.md index e57d12a7..e86c2b58 100644 --- a/packages/agents/content/skills/orchestrate/SKILL.md +++ b/packages/agents/content/skills/orchestrate/SKILL.md @@ -254,23 +254,9 @@ Before writing each artifact: format `{seq}` as two zero-padded digits (`{NN}`), - **Skipped or conditional artifacts**: do not consume a sequence number. `{seq}` only increments when an artifact is actually written. - **Subagents**: receive the full write-target path as an argument. They do not manage sequence numbers themselves. -5. **Write run-manifest artifact** to `{run-dir}/{NN}_orchestrator_run-manifest.md`. The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema (resolved per the [run-manifest and run-summary frontmatter resolution](#run-manifest-and-run-summary-frontmatter-resolution) section below): +5. **Write run-manifest artifact** to `{run-dir}/{NN}_orchestrator_run-manifest.md`. The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema (resolved per the [run-manifest and run-summary frontmatter resolution](#run-manifest-and-run-summary-frontmatter-resolution) section below). The frontmatter conforms to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). ```markdown ---- -provenance: - skill: orchestrate - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: false -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -run_id: '{run id}' ---- - # Run manifest | Field | Value | @@ -663,23 +649,9 @@ Dispatch the savings-analyzer subagent as a background Task and immediately proc - `ticket_id` and `ticket_ref` — from session context. Omit either when null. - `run_id` — the run ID for the current orchestrated run. -Write run-summary artifact to `{run-dir}/{NN}_orchestrator_run-summary.md`. The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema: +Write run-summary artifact to `{run-dir}/{NN}_orchestrator_run-summary.md`. The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. The frontmatter conforms to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). ```markdown ---- -provenance: - skill: orchestrate - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: false -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -run_id: '{run id}' ---- - # Orchestration summary ## Task diff --git a/packages/agents/content/subagents/orchestrated-planner.md b/packages/agents/content/subagents/orchestrated-planner.md index db1b99be..9b96911a 100644 --- a/packages/agents/content/subagents/orchestrated-planner.md +++ b/packages/agents/content/subagents/orchestrated-planner.md @@ -44,24 +44,9 @@ You will receive: ## Output: Plan (Markdown) -Write the plan Markdown file to the path provided in the task prompt. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). Format: +Write the plan Markdown file to the path provided in the task prompt. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). The frontmatter conforms to the canonical schema — see the canonical example in the `artifact-conventions` data doc. Format: ```markdown ---- -provenance: - skill: orchestrated-planner - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: false - model: '{model id}' -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -run_id: '{run id}' ---- - # Implementation Plan ## Overview diff --git a/packages/agents/content/subagents/planner.md b/packages/agents/content/subagents/planner.md index 3356122b..c869f701 100644 --- a/packages/agents/content/subagents/planner.md +++ b/packages/agents/content/subagents/planner.md @@ -74,24 +74,9 @@ Write the machine-readable plan to `{plan-json-path}`: ## Output: orchestration-plan.md -Write the human-readable plan to `{plan-md-path}`. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). The JSON sidecar does not carry frontmatter. +Write the human-readable plan to `{plan-md-path}`. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). The JSON sidecar does not carry frontmatter. The frontmatter conforms to the canonical schema — see the canonical example in the `artifact-conventions` data doc. ```markdown ---- -provenance: - skill: planner - timestamp: '{ISO 8601 UTC timestamp}' - baseSha: '{short SHA of origin/main, omit if unresolvable}' - isInteractive: false - model: '{model id}' -ticket_id: '{ticket id, omit if absent}' -ticket_ref: '{ticket display ref, omit if absent}' -branch: '{current branch name}' -commit: '{short hash of HEAD}' -pr: '{full PR URL, omit if not resolved}' -run_id: '{run id, omit if not in a run}' ---- - # Implementation Plan ## Overview From b3e8211725f8b51b726d3b415ed26ff7b8ebf95d Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Sun, 17 May 2026 06:41:20 -0700 Subject: [PATCH 4/6] agents|refactor: Factor key=value parsing and remove redundant quoting branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the shared `KEY=VALUE` argument-parsing logic from `add_extra` and `add_override` in `resolve-frontmatter.sh` into a `parse_key_value` helper that takes the raw argument plus a flag-name string for error messages and writes the parsed key and value back through nameref output parameters. Both call sites now invoke the helper instead of duplicating the four-line missing-`=`/empty-key guard block. Remove the redundant `\?*` arm from the leading-sigil `case` in `needs_yaml_quoting`. The downstream glyphs-anywhere case already covers `?` at any position, including the leading one, so the early branch only obscured that the two `case` blocks are not disjoint. The sibling `-*` and `:*` arms remain — they catch sigils with leading-only semantics not covered by the second case. Behavior is unchanged. Existing shellspec coverage for `add_extra`, `add_override`, and `needs_yaml_quoting` (including the leading-`?` case) continues to pass. --- .../content/scripts/resolve-frontmatter.sh | 159 +++++++++--------- 1 file changed, 75 insertions(+), 84 deletions(-) diff --git a/packages/agents/content/scripts/resolve-frontmatter.sh b/packages/agents/content/scripts/resolve-frontmatter.sh index a63e4e8a..cec62009 100755 --- a/packages/agents/content/scripts/resolve-frontmatter.sh +++ b/packages/agents/content/scripts/resolve-frontmatter.sh @@ -1,11 +1,9 @@ #!/usr/bin/env bash # Emit canonical artifact-frontmatter fields as YAML (default) or JSON. # -# Reads `.agents/{sanitized-branch}.branch-manifest.json` (produced by the -# `get-session-context` skill) for session-level fields and runs git + -# platform-specific PR lookup for the rest. Skills consume the output to -# populate the universal portion of their artifact frontmatter without -# repeating the underlying shell logic. +# Reads `.agents/{sanitized-branch}.branch-manifest.json` (produced by the `get-session-context` skill) for +# session-level fields and runs git + platform-specific PR lookup for the rest. Skills consume the output to +# populate the universal portion of their artifact frontmatter without repeating the underlying shell logic. # # Usage: # resolve-frontmatter.sh --skill NAME --interactive true|false [...] @@ -192,7 +190,21 @@ main() { fi } -# Append an extension key/value to the caller's ordered list. Splits the +# Parses a `KEY=VALUE` argument into the caller's `key` and `value` variables. +# The `flag` argument is used only for error messages. Fails when `=` is missing or the key portion is empty. +parse_key_value() { + local arg="$1" flag="$2" + local -n key_out="$3" + local -n value_out="$4" + if [[ "$arg" != *"="* ]]; then + fail "$flag argument missing '=': $arg" + fi + key_out="${arg%%=*}" + value_out="${arg#*=}" + [[ -n "$key_out" ]] || fail "$flag argument has empty key" +} + +# Appends an extension key/value to the caller's ordered list. Splits the # argument once on the first `=`. `kind` is either `scalar` or `list`. add_extra() { local kind="$1" arg="$2" @@ -200,12 +212,7 @@ add_extra() { local -n values_ref="$4" local -n kinds_ref="$5" local key value - if [[ "$arg" != *"="* ]]; then - fail "--extra/--extra-list argument missing '=': $arg" - fi - key="${arg%%=*}" - value="${arg#*=}" - [[ -n "$key" ]] || fail "--extra/--extra-list argument has empty key" + parse_key_value "$arg" "--extra/--extra-list" key value if [[ -z "${kinds_ref[$key]:-}" ]]; then keys_ref+=("$key") else @@ -215,22 +222,17 @@ add_extra() { kinds_ref["$key"]="$kind" } -# Record an override key=value. Empty value force-omits the key on emit. +# Records an override key=value. Empty value force-omits the key on emit. add_override() { local arg="$1" local -n overrides_ref="$2" local key value - if [[ "$arg" != *"="* ]]; then - fail "--override argument missing '=': $arg" - fi - key="${arg%%=*}" - value="${arg#*=}" - [[ -n "$key" ]] || fail "--override argument has empty key" + parse_key_value "$arg" "--override" key value overrides_ref["$key"]="$value" } -# Return the overridden value when the key has been overridden, otherwise -# the resolved value. The empty-string override force-omits. +# Returns the overridden value when the key has been overridden, otherwise the resolved value. +# The empty-string override force-omits. apply_override() { local key="$1" resolved="$2" local -n overrides_ref="$3" @@ -241,16 +243,15 @@ apply_override() { fi } -# Print short SHA of `default_branch` (e.g., `origin/main`) or empty if -# unresolvable. A shallow clone or missing remote silently degrades to empty. +# Prints short SHA of `default_branch` (e.g., `origin/main`) or empty if unresolvable. +# A shallow clone or missing remote silently degrades to empty. resolve_base_sha() { local ref="$1" git rev-parse --short "$ref" 2>/dev/null || true } -# Print the PR URL for the current branch, or empty if no PR or lookup -# failed. Distinguishes empty-output (silent) from failure (canonical -# warning emitted to stderr). +# Prints the PR URL for the current branch, or empty if no PR or lookup failed. +# Distinguishes empty-output (silent) from failure (canonical warning emitted to stderr). resolve_pr_url() { local platform="$1" local branch="$2" @@ -261,8 +262,8 @@ resolve_pr_url() { esac } -# GitHub PR lookup via `gh pr list`. Uses --state all so closed and merged -# PRs are resolvable for post-merge artifact writes. +# Looks up a GitHub PR via `gh pr list`. +# Uses --state all so closed and merged PRs are resolvable for post-merge artifact writes. resolve_github_pr() { local branch="$1" if ! command -v gh >/dev/null 2>&1; then @@ -280,7 +281,7 @@ resolve_github_pr() { printf '%s' "$result" } -# Bitbucket PR lookup via Bitbucket Cloud REST API. +# Looks up a Bitbucket PR via Bitbucket Cloud REST API. resolve_bitbucket_pr() { local branch="$1" if ! command -v curl >/dev/null 2>&1; then @@ -310,9 +311,8 @@ resolve_bitbucket_pr() { printf '%s' "$result" } -# Resolve a Bitbucket auth header from environment or macOS keychain. Echoes -# the full `Authorization:` header value or returns non-zero when no -# credentials are available. +# Resolves a Bitbucket auth header from environment or macOS keychain. +# Echoes the full `Authorization:` header value or returns non-zero when no credentials are available. bitbucket_auth_header() { if [[ -n "${BITBUCKET_BOT_USERNAME:-}" && -n "${BITBUCKET_BOT_TOKEN:-}" ]]; then local basic @@ -334,18 +334,16 @@ bitbucket_auth_header() { return 1 } -# Emit the canonical PR-lookup-failed warning to stderr. The first argument -# is an internal diagnostic that is also written to stderr after the -# canonical line so debug context is available without affecting callers -# that match the canonical phrasing. +# Emits the canonical PR-lookup-failed warning to stderr. +# The first argument is an internal diagnostic that is also written to stderr after the canonical line, +# so that debug context is available without affecting callers that match the canonical phrasing. warn_pr_failure() { echo "$CANONICAL_WARNING" >&2 echo " ($1)" >&2 } -# Resolve the active run ID by reading the breadcrumb written by the -# orchestrate engine at `.claude/tmp/active-run-dir`. Empty when no -# orchestrated run is active. +# Resolves the active run ID by reading the breadcrumb written by the orchestrate engine at +# `.claude/tmp/active-run-dir`. Empty when no orchestrated run is active. resolve_run_id() { local breadcrumb=".claude/tmp/active-run-dir" [[ -r "$breadcrumb" ]] || return 0 @@ -354,8 +352,8 @@ resolve_run_id() { basename "$run_dir" } -# Run a command with a timeout. Uses `timeout` or `gtimeout` when available, -# otherwise runs without a timeout (the agent's Bash-tool timeout still +# Run a command with a timeout. +# Uses `timeout` or `gtimeout` when available; otherwise, runs without a timeout (the agent's Bash-tool timeout still # applies). The command and its arguments are passed verbatim. run_with_timeout() { local secs="$1" @@ -369,12 +367,12 @@ run_with_timeout() { fi } -# Get the current branch name. Returns non-zero outside a git repository. +# Gets the current branch name. Returns non-zero outside a git repository. current_branch() { git rev-parse --abbrev-ref HEAD 2>/dev/null } -# Read the branch manifest for the given branch. Echoes the JSON content. +# Reads the branch manifest for the given branch. Echoes the JSON content. # Returns non-zero when the manifest is missing. read_manifest() { local branch="$1" @@ -385,8 +383,8 @@ read_manifest() { cat "$path" } -# Sanitize a branch name for filesystem use: replace `/` with `-` and trim -# any trailing `-` characters. Mirrors `get-session-context` behavior. +# Sanitizes a branch name for filesystem use: replace `/` with `-` and trims any trailing `-` characters. +# Mirrors `get-session-context` behavior. sanitize_branch() { local branch="$1" branch="${branch//\//-}" @@ -394,9 +392,8 @@ sanitize_branch() { printf '%s' "$branch" } -# Construct the JSON output from resolved values. Optional fields are -# omitted (rather than emitted as null or empty) so consumers can rely on -# `has(field)` semantics. +# Constructs the JSON output from resolved values. +# Optional fields are omitted (rather than emitted as null or empty) so consumers can rely on `has(field)` semantics. emit_json() { local branch="$1" commit="$2" base_sha="$3" pr_url="$4" local ticket_id="$5" ticket_ref="$6" platform="$7" timestamp="$8" run_id="$9" @@ -426,10 +423,8 @@ emit_json() { ' } -# Emit the canonical YAML frontmatter block, including `---` delimiters. -# Empty values are omitted. Field order is fixed: provenance block, then -# canonical top-level fields, then caller-supplied extensions in -# insertion order. +# Emits the canonical YAML frontmatter block, including `---` delimiters. Empty values are omitted. +# Field order is fixed: Provenance block, canonical top-level fields, caller-supplied extensions in insertion order. emit_yaml() { local skill="$1" timestamp="$2" base_sha="$3" interactive="$4" model="$5" local ticket_id="$6" ticket_ref="$7" branch="$8" commit="$9" pr_url="${10}" run_id="${11}" @@ -439,7 +434,7 @@ emit_yaml() { printf '%s\n' "---" - # provenance block + # Provenance block printf '%s\n' "provenance:" emit_yaml_indented_scalar "skill" "$skill" emit_yaml_indented_scalar "timestamp" "$timestamp" @@ -448,7 +443,7 @@ emit_yaml() { printf ' %s: %s\n' "isInteractive" "$interactive" [[ -n "$model" ]] && emit_yaml_indented_scalar "model" "$model" - # canonical top-level fields + # Canonical top-level fields [[ -n "$ticket_id" ]] && emit_yaml_scalar "ticket_id" "$ticket_id" [[ -n "$ticket_ref" ]] && emit_yaml_scalar "ticket_ref" "$ticket_ref" emit_yaml_scalar "branch" "$branch" @@ -456,15 +451,14 @@ emit_yaml() { [[ -n "$pr_url" ]] && emit_yaml_scalar "pr" "$pr_url" [[ -n "$run_id" ]] && emit_yaml_scalar "run_id" "$run_id" - # extension fields in insertion order + # Extension fields in insertion order local key kind value for key in "${yaml_extra_keys[@]+"${yaml_extra_keys[@]}"}"; do value="${yaml_extra_values[$key]}" kind="${yaml_extra_kinds[$key]}" if [[ "$kind" == "list" ]]; then # Flow-list extensions intentionally emit `key: []` for empty values - # (see `emit_yaml_flow_list`); only scalar extensions are subject to - # the canonical-field omission rule. + # (see `emit_yaml_flow_list`); only scalar extensions are subject to the canonical-field omission rule. emit_yaml_flow_list "$key" "$value" else [[ -n "$value" ]] || continue @@ -487,9 +481,8 @@ emit_yaml_indented_scalar() { printf ' %s: %s\n' "$key" "$(yaml_quote "$value")" } -# Emit a top-level YAML flow list: `key: [v1, v2, v3]`. Empty value emits -# an empty flow list `key: []`. Elements are split on `,` and each is -# passed through `yaml_quote`. +# Emits a top-level YAML flow list: `key: [v1, v2, v3]`. +# Empty value emits an empty flow list `key: []`. Elements are split on `,` and each is passed through `yaml_quote`. emit_yaml_flow_list() { local key="$1" raw="$2" if [[ -z "$raw" ]]; then @@ -497,9 +490,8 @@ emit_yaml_flow_list() { return fi local IFS=',' - # Disable globbing around the word-split so list elements containing - # glob metacharacters (`*`, `?`, `[...]`) are not expanded against the - # filesystem before `yaml_quote` sees them. + # Disable globbing around the word-split so that list elements containing glob metacharacters (`*`, `?`, `[...]`) + # are not expanded against the filesystem before `yaml_quote` sees them. set -f # shellcheck disable=SC2206 local -a parts=( $raw ) @@ -514,10 +506,12 @@ emit_yaml_flow_list() { printf '%s: [%s]\n' "$key" "$out" } -# Return the value either bare or single-quoted depending on YAML -# auto-quoting rules. The predicate quotes when the value contains any of -# the unsafe glyphs (`# : [ ] { } , & * ! | > < ? % @ \` ` `), has leading -# or trailing whitespace, is empty, or begins with `-` / `?` / `:`. +# Returns the value either bare or single-quoted depending on YAML auto-quoting rules. +# The predicate quotes when the value +# - contains any of the unsafe glyphs (`# : [ ] { } , & * ! | > < ? % @ \` ` `), +# - has leading or trailing whitespace, +# - is empty, or +# - begins with `-` / `?` / `:`. # Embedded single quotes are doubled inside the quoted form. yaml_quote() { local v="$1" @@ -529,13 +523,11 @@ yaml_quote() { fi } -# Decide whether `v` needs single-quote wrapping. Returns 0 (true) when -# quoting is required, 1 otherwise. +# Decides whether `v` needs single-quote wrapping. Returns 0 (true) when quoting is required, 1 otherwise. # -# YAML parses `:` as a key indicator only when followed by whitespace or -# end-of-value, so URLs like `https://...` are safe bare. `#` is always -# treated as a comment introducer in YAML 1.1/1.2 (the spec is permissive -# about the preceding context), so we quote any value containing `#`. +# YAML parses `:` as a key indicator only when followed by whitespace or end-of-value, +# so URLs like `https://...` are safe bare. `#` is always treated as a comment introducer in YAML 1.1/1.2 (the spec is +# permissive about the preceding context), so we quote any value containing `#`. needs_yaml_quoting() { local v="$1" # Empty values must be quoted. @@ -543,9 +535,10 @@ needs_yaml_quoting() { # Leading or trailing whitespace. [[ "$v" =~ ^[[:space:]] ]] && return 0 [[ "$v" =~ [[:space:]]$ ]] && return 0 - # Leading sigils that YAML interprets specially. + # Leading sigils that YAML interprets specially. `?` as a leading + # character is handled by the glyphs-anywhere case below. case "$v" in - -* | \?* | :*) return 0 ;; + -* | :*) return 0 ;; esac # Colon followed by whitespace anywhere is a key indicator. [[ "$v" =~ :[[:space:]] ]] && return 0 @@ -559,15 +552,13 @@ needs_yaml_quoting() { return 0 ;; esac - # Values that look like YAML booleans/null or numbers stay bare per the - # current predicate. The predicate's value space is the schema's - # canonical fields and known extensions; expand here if a future - # extension introduces ambiguity. + # Values that look like YAML booleans/null or numbers stay bare per the current predicate. + # The predicate's value space is the schema's canonical fields and known extensions; + # expand here if a future extension introduces ambiguity. return 1 } -# Require each named command to be on PATH. Exits 1 with a clear message -# when one is missing. +# Require each named command to be on PATH. Exits 1 with a clear message when one is missing. require_commands() { local cmd for cmd in "$@"; do @@ -581,9 +572,9 @@ fail() { exit 1 } -# Print a non-fatal warning to stderr. Used for soft-failure conditions -# where the script should continue with degraded behavior (e.g., a -# duplicate optional-extension key that overwrites an earlier value). +# Prints a non-fatal warning to stderr. +# Used for soft-failure conditions where the script should continue with degraded behavior (e.g., a duplicate +# optional-extension key that overwrites an earlier value). warn() { echo "$PROG: warning: $1" >&2 } From 17a95266c838de55fea8e18c0422827fc4f138af Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Sun, 17 May 2026 07:21:40 -0700 Subject: [PATCH 5/6] agents|docs: De-stale pr-resolution.md output references The contract document referred to the script's "JSON output" in three places, but `resolve-frontmatter.sh` now defaults to YAML mode and the omission/warning semantics are format-agnostic. Replace with format- neutral wording so the contract reads correctly regardless of which `--format` the caller selects. --- packages/agents/content/skills/_data/pr-resolution.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/agents/content/skills/_data/pr-resolution.md b/packages/agents/content/skills/_data/pr-resolution.md index 3bf504d1..2ee5aa70 100644 --- a/packages/agents/content/skills/_data/pr-resolution.md +++ b/packages/agents/content/skills/_data/pr-resolution.md @@ -1,13 +1,13 @@ # PR resolution -Shared contract for resolving the `pr:` frontmatter field at artifact write time. Skills do not invoke `gh` or `curl` directly — they call `scripts/resolve-frontmatter.sh`, which implements this contract once and emits the resolved `pr:` value (along with all other canonical frontmatter fields) as part of its JSON output. This document defines the contract the script obeys; consumers do not embed dispatch snippets directly. +Shared contract for resolving the `pr:` frontmatter field at artifact write time. Skills do not invoke `gh` or `curl` directly — they call `scripts/resolve-frontmatter.sh`, which implements this contract once and emits the resolved `pr:` value (along with all other canonical frontmatter fields) as part of its output. This document defines the contract the script obeys; consumers do not embed dispatch snippets directly. ## Contract - Resolution runs at artifact write time, against the current branch. - The Bash invocation uses a **5-second timeout** (`timeout: 5000` on the Bash tool when calling the script; the script itself wraps the platform CLI in `timeout`/`gtimeout` when available). -- On **empty output** (no PR exists for the branch — the lookup succeeded), the script omits the `pr` field from its JSON output. Consumers must omit the `pr:` line from frontmatter. Do **not** emit a warning. This is the normal pre-PR case. -- On **failure** (CLI unavailable, auth error, network error, timeout, or any non-zero exit), the script omits the `pr` field from its JSON output and emits the canonical warning to stderr. Consumers must omit the `pr:` line **and** surface the warning in the agent's text output. **Never block the artifact write.** +- On **empty output** (no PR exists for the branch — the lookup succeeded), the script omits the `pr` field from its output. Consumers must omit the `pr:` line from frontmatter. Do **not** emit a warning. This is the normal pre-PR case. +- On **failure** (CLI unavailable, auth error, network error, timeout, or any non-zero exit), the script omits the `pr` field from its output and emits the canonical warning to stderr. Consumers must omit the `pr:` line **and** surface the warning in the agent's text output. **Never block the artifact write.** ### Canonical warning text From 5a15cff891251a73a6c72330135f7fce379b02d5 Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Sun, 17 May 2026 08:50:42 -0700 Subject: [PATCH 6/6] agents|refactor: Fix capitalization --- .../content/skills/create-devlog/SKILL.md | 6 ++--- .../content/skills/create-ticket/SKILL.md | 2 +- .../content/skills/ex-post-facto/SKILL.md | 4 +-- .../content/skills/orchestrate/SKILL.md | 26 +++++++++---------- packages/agents/content/skills/plan/SKILL.md | 2 +- .../content/skills/refine-plan/SKILL.md | 2 +- .../content/skills/respond-to-review/SKILL.md | 4 +-- .../content/skills/review-branch/SKILL.md | 4 +-- .../content/skills/summarize-change/SKILL.md | 4 +-- .../content/skills/summarize-chat/SKILL.md | 4 +-- .../agents/content/skills/wrap-up/SKILL.md | 2 +- .../content/subagents/aspect-code-reviewer.md | 2 +- .../aspect-silent-failure-reviewer.md | 2 +- .../content/subagents/aspect-test-reviewer.md | 2 +- .../subagents/code-simplification-reviewer.md | 2 +- .../subagents/orchestrated-architect.md | 2 +- .../content/subagents/orchestrated-coder.md | 2 +- .../content/subagents/orchestrated-planner.md | 4 +-- .../subagents/orchestrated-reviewer.md | 2 +- .../agents/content/subagents/plan-reviewer.md | 4 +-- packages/agents/content/subagents/planner.md | 4 +-- .../content/subagents/savings-analyzer.md | 4 +-- 22 files changed, 45 insertions(+), 45 deletions(-) diff --git a/packages/agents/content/skills/create-devlog/SKILL.md b/packages/agents/content/skills/create-devlog/SKILL.md index 7982a5d4..bd865aa8 100644 --- a/packages/agents/content/skills/create-devlog/SKILL.md +++ b/packages/agents/content/skills/create-devlog/SKILL.md @@ -17,7 +17,7 @@ Summarize changes made in recent commits or the working tree. ## Output format -The devlog file begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the devlog-specific extensions in [Devlog frontmatter](../_data/artifact-conventions.md#devlog-frontmatter). The body following the frontmatter has this structure: +The devlog file begins with YAML frontmatter conforming to the canonical schema; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the devlog-specific extensions in [Devlog frontmatter](../_data/artifact-conventions.md#devlog-frontmatter). The body following the frontmatter has this structure: ```markdown # Devlog: {Concise description} @@ -86,7 +86,7 @@ Resolve `$run_id_arg` from the `--run-id={id}` argument (empty when not supplied - No argument (last commit): `commits_arg=$(git log -n 1 --format=%h)`. - `` (last N commits): `commits_arg=$(git log -n N --format=%h | paste -sd, -)`. -- `working-tree`: do not pass `--extra-list commits=...`. +- `working-tree`: Do not pass `--extra-list commits=...`. Run via Bash, substituting the resolved arguments: @@ -101,7 +101,7 @@ resolve-frontmatter.sh \ The `${commits_arg:+--extra-list "commits=$commits_arg"}` form expands to the flag only when `$commits_arg` is non-empty, so the `working-tree` mode (where `$commits_arg` is unset) naturally omits the `commits` field rather than emitting `commits: []`. Quoting `run_id=$run_id_arg` ensures the empty-value force-omit case works when no `--run-id` was supplied. -Prepend the script's output verbatim to the artifact body. Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Prepend the script's output verbatim to the artifact body. Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. If the script's stderr contains `Note: PR lookup failed; proceeding without pr field.`, surface that line in your text output once. diff --git a/packages/agents/content/skills/create-ticket/SKILL.md b/packages/agents/content/skills/create-ticket/SKILL.md index 883985a0..2a6250f9 100644 --- a/packages/agents/content/skills/create-ticket/SKILL.md +++ b/packages/agents/content/skills/create-ticket/SKILL.md @@ -151,7 +151,7 @@ If a plan is also saved in step 7, it uses the same frontmatter shape with `prov The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill create-ticket --interactive true --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/skills/ex-post-facto/SKILL.md b/packages/agents/content/skills/ex-post-facto/SKILL.md index 63ccdfd8..9b4690e8 100644 --- a/packages/agents/content/skills/ex-post-facto/SKILL.md +++ b/packages/agents/content/skills/ex-post-facto/SKILL.md @@ -20,7 +20,7 @@ git diff $DEFAULT_BRANCH...HEAD ## Output structure -The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. See [Frontmatter resolution](#frontmatter-resolution) below for field resolution. The frontmatter conforms to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). +The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. See [Frontmatter resolution](#frontmatter-resolution) below for field resolution. The frontmatter conforms to the canonical schema; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). ```markdown # {Title} @@ -65,7 +65,7 @@ The artifact begins with YAML frontmatter conforming to the [universal artifact The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill ex-post-facto --interactive true --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/skills/orchestrate/SKILL.md b/packages/agents/content/skills/orchestrate/SKILL.md index e86c2b58..5b0f5ce4 100644 --- a/packages/agents/content/skills/orchestrate/SKILL.md +++ b/packages/agents/content/skills/orchestrate/SKILL.md @@ -254,7 +254,7 @@ Before writing each artifact: format `{seq}` as two zero-padded digits (`{NN}`), - **Skipped or conditional artifacts**: do not consume a sequence number. `{seq}` only increments when an artifact is actually written. - **Subagents**: receive the full write-target path as an argument. They do not manage sequence numbers themselves. -5. **Write run-manifest artifact** to `{run-dir}/{NN}_orchestrator_run-manifest.md`. The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema (resolved per the [run-manifest and run-summary frontmatter resolution](#run-manifest-and-run-summary-frontmatter-resolution) section below). The frontmatter conforms to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). +5. **Write run-manifest artifact** to `{run-dir}/{NN}_orchestrator_run-manifest.md`. The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema (resolved per the [run-manifest and run-summary frontmatter resolution](#run-manifest-and-run-summary-frontmatter-resolution) section below). The frontmatter conforms to the canonical schema; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). ```markdown # Run manifest @@ -332,9 +332,9 @@ The `{externalPlan}` flag and extracted plan content were already determined in Emit a `phase_decision` event for every known phase. Iterate through the complete set of known phases (`architecture`, `planning`, `implementation`, `review-cycle`) — not just the phases present in the pipeline: -1. **Phase absent from pipeline**: emit `phase_decision` with `run: false` and `reason: "absent"`. -2. **Phase present with requirement `required`**: phase always runs. Emit `phase_decision` with `run: true` and `reason: "executed"`. -3. **Phase present with requirement `optional`**: apply phase-specific skip logic (below). Emit `phase_decision` with `run: true/false` and `reason: "executed"` or `"skipped: {reason}"`. +1. **Phase absent from pipeline**: Emit `phase_decision` with `run: false` and `reason: "absent"`. +2. **Phase present with requirement `required`**: Phase always runs. Emit `phase_decision` with `run: true` and `reason: "executed"`. +3. **Phase present with requirement `optional`**: Apply phase-specific skip logic (below). Emit `phase_decision` with `run: true/false` and `reason: "executed"` or `"skipped: {reason}"`. For each phase decision, call MCP tool `emit_event`: @@ -374,11 +374,11 @@ When `{planTrust}` is `"high"` and Planning is skipped, the orchestrator produce 1. **Check for JSON companion:** If the external plan file has a JSON companion (same directory, same base name or `orchestration-plan.json`), read it and use it as `{plan-json-content}`. Skip markdown parsing — the JSON is already structured. 2. **Parse markdown to JSON** (if no companion): Parse the external plan's `### Task N:` sections. For each task section, extract: - - `title`: text after `### Task N: ` - - `files`: lines under `**Files:**` (strip `- Create: `, `- Modify: `, `- Test: ` prefixes) - - `dependsOn`: parse `**Depends on:** Step N` or `**Depends on:** Steps N, M` references, converting to integer IDs - - `acceptanceCriteria`: bullet items under `**Acceptance criteria:**` - - `description`: remaining text in the section (between the title and the first recognized sub-heading) + - `title`: Text after `### Task N: ` + - `files`: Lines under `**Files:**` (strip `- Create: `, `- Modify: `, `- Test: ` prefixes) + - `dependsOn`: Parse `**Depends on:** Step N` or `**Depends on:** Steps N, M` references, converting to integer IDs + - `acceptanceCriteria`: Bullet items under `**Acceptance criteria:**` + - `description`: Remaining text in the section (between the title and the first recognized sub-heading) If a task section lacks any of these sub-headings, use empty values: `[]` for arrays, `""` for strings. @@ -400,7 +400,7 @@ When `{planTrust}` is `"high"` and Planning is skipped, the orchestrator produce } ``` -3. **Guard: zero-steps fallback.** If `{plan-json-content}` has an empty `steps` array (length 0): +3. **Guard: Zero-steps fallback.** If `{plan-json-content}` has an empty `steps` array (length 0): a. Emit a corrective `phase_decision` event for planning: @@ -408,7 +408,7 @@ When `{planTrust}` is `"high"` and Planning is skipped, the orchestrator produce Call MCP tool emit_event with: runDir: {run-dir} event: { event: "phase_decision", phase: "planning", run: true, - reason: "fallback: high-trust plan produced zero steps — running planning in adoption mode" } + reason: "fallback: High-trust plan produced zero steps — running planning in adoption mode" } ``` b. Set `{planTrust}` to `"medium"`. Do not increment `{seq}`. @@ -444,7 +444,7 @@ When `{planTrust}` is `"high"` and Planning is skipped, the orchestrator produce phase: initialization ``` -6. **Store paths:** Store full paths as `{plan-md-path}` and `{plan-json-path}` for downstream phases. Note: the `phase_decision` for planning was already emitted in the "Skip logic" section above with `reason: "skipped: high-trust plan (skill: {provenance.skill}, baseSha matches main)"`. Do not emit a second `phase_decision` here. +6. **Store paths:** Store full paths as `{plan-md-path}` and `{plan-json-path}` for downstream phases. Note: The `phase_decision` for planning was already emitted in the "Skip logic" section above with `reason: "skipped: high-trust plan (skill: {provenance.skill}, baseSha matches main)"`. Do not emit a second `phase_decision` here. ## Authority hierarchy @@ -649,7 +649,7 @@ Dispatch the savings-analyzer subagent as a background Task and immediately proc - `ticket_id` and `ticket_ref` — from session context. Omit either when null. - `run_id` — the run ID for the current orchestrated run. -Write run-summary artifact to `{run-dir}/{NN}_orchestrator_run-summary.md`. The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. The frontmatter conforms to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). +Write run-summary artifact to `{run-dir}/{NN}_orchestrator_run-summary.md`. The artifact begins with YAML frontmatter conforming to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. The frontmatter conforms to the canonical schema; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter). ```markdown # Orchestration summary diff --git a/packages/agents/content/skills/plan/SKILL.md b/packages/agents/content/skills/plan/SKILL.md index ab684641..75fe3df6 100644 --- a/packages/agents/content/skills/plan/SKILL.md +++ b/packages/agents/content/skills/plan/SKILL.md @@ -15,7 +15,7 @@ Create a structured plan document for analysis, design, or implementation work. ## Output format -The plan begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the [plan provenance](../_data/artifact-conventions.md#plan-provenance) extension; field-resolution steps live in the [Frontmatter resolution](#frontmatter-resolution) section below. `provenance.model` is omitted — plans authored via this skill are co-authored interactively, not solely AI-generated. +The plan begins with YAML frontmatter conforming to the canonical schema; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the [plan provenance](../_data/artifact-conventions.md#plan-provenance) extension; field-resolution steps live in the [Frontmatter resolution](#frontmatter-resolution) section below. `provenance.model` is omitted — plans authored via this skill are co-authored interactively, not solely AI-generated. The body following the frontmatter has this structure: diff --git a/packages/agents/content/skills/refine-plan/SKILL.md b/packages/agents/content/skills/refine-plan/SKILL.md index fd029637..8e3505aa 100644 --- a/packages/agents/content/skills/refine-plan/SKILL.md +++ b/packages/agents/content/skills/refine-plan/SKILL.md @@ -151,7 +151,7 @@ Stamp the revised plan with frontmatter conforming to the [universal artifact fr The stamp writes the full canonical schema in one atomic write: the `provenance:` block plus the top-level canonical fields. The top-level fields come from the script; the `provenance:` block is computed from `{input-provenance}` plus the stamping logic below. -This site uses `--format json` because the `provenance:` block is case-branched on the input artifact's existing provenance — see [artifact-conventions.md](../_data/artifact-conventions.md#bespoke-frontmatter-composition). +This site uses `--format json` because the `provenance:` Block is case-branched on the input artifact's existing provenance — see [artifact-conventions.md](../_data/artifact-conventions.md#bespoke-frontmatter-composition). Run `resolve-frontmatter.sh --format json` via Bash. It emits a JSON object with the universal artifact fields (`branch`, `commit`, `baseSha`, `pr`, `ticket_id`, `ticket_ref`, `platform`, `timestamp`, `run_id`). Use those values verbatim for the matching YAML keys. Optional fields the script omits from its output (`baseSha`, `pr`, `ticket_id`, `ticket_ref`, `run_id`) must be omitted from the frontmatter too — do not emit `null` or empty strings. diff --git a/packages/agents/content/skills/respond-to-review/SKILL.md b/packages/agents/content/skills/respond-to-review/SKILL.md index 08572292..2bd209a1 100644 --- a/packages/agents/content/skills/respond-to-review/SKILL.md +++ b/packages/agents/content/skills/respond-to-review/SKILL.md @@ -32,7 +32,7 @@ This skill bridges the gap between receiving a code review and implementing fixe The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. Set `$review_filename` to the bare filename of the review being responded to (e.g., `09_reviewer_review.md`). +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Set `$review_filename` to the bare filename of the review being responded to (e.g., `09_reviewer_review.md`). Run via Bash: @@ -134,7 +134,7 @@ Per [artifact conventions](../_data/artifact-conventions.md#disposition-rules): When `ticket_ref` is null (no ticket on the branch), omit the `{ticket_ref}: ` portion so the heading reads `# Change summary: {description}`. -The artifact begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section above. The `responding_to` field is a response-artifact-specific extension that records the review being addressed. +The artifact begins with YAML frontmatter conforming to the canonical schema; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section above. The `responding_to` field is a response-artifact-specific extension that records the review being addressed. The body following the frontmatter has this structure: diff --git a/packages/agents/content/skills/review-branch/SKILL.md b/packages/agents/content/skills/review-branch/SKILL.md index 782db29c..a9737c3e 100644 --- a/packages/agents/content/skills/review-branch/SKILL.md +++ b/packages/agents/content/skills/review-branch/SKILL.md @@ -42,7 +42,7 @@ This skill is the canonical home of the shared review process. `review-pr` invok The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. Resolve `$author` from `git log --format='%an' "$default_branch..HEAD" | sort -u | paste -sd, -` (unique authors of the commits under review). +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Resolve `$author` from `git log --format='%an' "$default_branch..HEAD" | sort -u | paste -sd, -` (unique authors of the commits under review). Run via Bash: @@ -87,7 +87,7 @@ Section-header icons (🚨, ⚠️, 📋, 🧠, ☝️, 🔍) come from the cano When `ticket_ref` is null (no ticket on the branch), omit the `{ticket_ref}: ` portion so the heading reads naturally without it — e.g., `# Code review: {description}`. -The artifact begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section above. Pass `--extra "author=$author"` to the script to populate the review-artifact `author` field. +The artifact begins with YAML frontmatter conforming to the canonical schema; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section above. Pass `--extra "author=$author"` to the script to populate the review-artifact `author` field. The body following the frontmatter has this structure: diff --git a/packages/agents/content/skills/summarize-change/SKILL.md b/packages/agents/content/skills/summarize-change/SKILL.md index 3b939eb7..7bfcaced 100644 --- a/packages/agents/content/skills/summarize-change/SKILL.md +++ b/packages/agents/content/skills/summarize-change/SKILL.md @@ -32,7 +32,7 @@ If expected information is missing, stop and ask the developer. ## Output format -The artifact begins with a single YAML frontmatter block that unifies canonical fields from the canonical schema with change-summary-specific consumer fields — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the consumer-field extensions in [Change-summary frontmatter](../_data/artifact-conventions.md#change-summary-frontmatter). Ordering: `provenance:` first, then top-level canonical fields, then consumer fields. `commit:` and `ticket_id:` appear exactly once each. Field-resolution steps live in the [Canonical-field resolution](#canonical-field-resolution) section below. +The artifact begins with a single YAML frontmatter block that unifies canonical fields from the canonical schema with change-summary-specific consumer fields; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the consumer-field extensions in [Change-summary frontmatter](../_data/artifact-conventions.md#change-summary-frontmatter). Ordering: `provenance:` first, then top-level canonical fields, then consumer fields. `commit:` and `ticket_id:` appear exactly once each. Field-resolution steps live in the [Canonical-field resolution](#canonical-field-resolution) section below. The body following the frontmatter has this structure: @@ -99,7 +99,7 @@ The block is structured as: ### Canonical-field resolution -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. Resolve `$title`, `$scope`, and `$type` from the consumer-field inference below (omit a flag if the corresponding value cannot be inferred unambiguously). +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Resolve `$title`, `$scope`, and `$type` from the consumer-field inference below (omit a flag if the corresponding value cannot be inferred unambiguously). Run via Bash: diff --git a/packages/agents/content/skills/summarize-chat/SKILL.md b/packages/agents/content/skills/summarize-chat/SKILL.md index 3d63b95a..8bdff4f1 100644 --- a/packages/agents/content/skills/summarize-chat/SKILL.md +++ b/packages/agents/content/skills/summarize-chat/SKILL.md @@ -23,7 +23,7 @@ Replace `/Users/{username}/` with `~/` in file paths. Remove similar personal in ## Output format -The artifact begins with YAML frontmatter conforming to the canonical schema — see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section below. +The artifact begins with YAML frontmatter conforming to the canonical schema; see the canonical example in [artifact-conventions.md](../_data/artifact-conventions.md#universal-artifact-frontmatter) and the field-resolution steps in the [Frontmatter resolution](#frontmatter-resolution) section below. The body following the frontmatter has this structure: @@ -71,7 +71,7 @@ Use these to mark significant sections: The artifact's frontmatter conforms to the [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) schema. -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill summarize-chat --interactive true --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/skills/wrap-up/SKILL.md b/packages/agents/content/skills/wrap-up/SKILL.md index 28b823a0..be849b08 100644 --- a/packages/agents/content/skills/wrap-up/SKILL.md +++ b/packages/agents/content/skills/wrap-up/SKILL.md @@ -331,7 +331,7 @@ Prepend YAML frontmatter, then the markdown body. **Frontmatter** — see [universal artifact frontmatter](../_data/artifact-conventions.md#universal-artifact-frontmatter) for the canonical schema and [Deferred-findings frontmatter](../_data/artifact-conventions.md#deferred-findings-frontmatter) for the artifact-specific extensions. -This site uses `--format json` because `tickets_created` is a list-of-objects extension that has no clean CLI expression — see [artifact-conventions.md](../_data/artifact-conventions.md#bespoke-frontmatter-composition). +This site uses `--format json` because `tickets_created` is a list-of-objects extension that has no clean CLI expression; see [artifact-conventions.md](../_data/artifact-conventions.md#bespoke-frontmatter-composition). Run `resolve-frontmatter.sh --format json` via Bash. It emits a JSON object with the universal artifact fields (`branch`, `commit`, `baseSha`, `pr`, `ticket_id`, `ticket_ref`, `platform`, `timestamp`, `run_id`). Use those values verbatim for the matching YAML keys. Optional fields the script omits from its output (`baseSha`, `pr`, `ticket_id`, `ticket_ref`, `run_id`) must be omitted from the frontmatter too — do not emit `null` or empty strings. diff --git a/packages/agents/content/subagents/aspect-code-reviewer.md b/packages/agents/content/subagents/aspect-code-reviewer.md index 09782eae..f37dcce7 100644 --- a/packages/agents/content/subagents/aspect-code-reviewer.md +++ b/packages/agents/content/subagents/aspect-code-reviewer.md @@ -69,7 +69,7 @@ If the review concluded with no findings, the finalized form omits the `### Find The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill aspect-code-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/aspect-silent-failure-reviewer.md b/packages/agents/content/subagents/aspect-silent-failure-reviewer.md index c2acfc92..f9512f28 100644 --- a/packages/agents/content/subagents/aspect-silent-failure-reviewer.md +++ b/packages/agents/content/subagents/aspect-silent-failure-reviewer.md @@ -63,7 +63,7 @@ If the review concluded with no findings (or no error-handling code was present) The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill aspect-silent-failure-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/aspect-test-reviewer.md b/packages/agents/content/subagents/aspect-test-reviewer.md index c3565498..bcb9751d 100644 --- a/packages/agents/content/subagents/aspect-test-reviewer.md +++ b/packages/agents/content/subagents/aspect-test-reviewer.md @@ -72,7 +72,7 @@ If the review concluded with no findings (or no source files required test cover The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill aspect-test-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/code-simplification-reviewer.md b/packages/agents/content/subagents/code-simplification-reviewer.md index 610b7cd2..17478abb 100644 --- a/packages/agents/content/subagents/code-simplification-reviewer.md +++ b/packages/agents/content/subagents/code-simplification-reviewer.md @@ -64,7 +64,7 @@ If the review concluded with no findings, the finalized form omits the `### Find The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill code-simplification-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/orchestrated-architect.md b/packages/agents/content/subagents/orchestrated-architect.md index bd110b34..36988cf2 100644 --- a/packages/agents/content/subagents/orchestrated-architect.md +++ b/packages/agents/content/subagents/orchestrated-architect.md @@ -113,7 +113,7 @@ If the plan's assumptions all check out, omit this section. The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill orchestrated-architect --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/orchestrated-coder.md b/packages/agents/content/subagents/orchestrated-coder.md index a7a79bed..d760f8d2 100644 --- a/packages/agents/content/subagents/orchestrated-coder.md +++ b/packages/agents/content/subagents/orchestrated-coder.md @@ -137,7 +137,7 @@ completed The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill orchestrated-coder --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/orchestrated-planner.md b/packages/agents/content/subagents/orchestrated-planner.md index 9b96911a..e17e8184 100644 --- a/packages/agents/content/subagents/orchestrated-planner.md +++ b/packages/agents/content/subagents/orchestrated-planner.md @@ -44,7 +44,7 @@ You will receive: ## Output: Plan (Markdown) -Write the plan Markdown file to the path provided in the task prompt. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). The frontmatter conforms to the canonical schema — see the canonical example in the `artifact-conventions` data doc. Format: +Write the plan Markdown file to the path provided in the task prompt. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). The frontmatter conforms to the canonical schema; see the canonical example in the `artifact-conventions` data doc. Format: ```markdown # Implementation Plan @@ -133,7 +133,7 @@ Write the plan JSON file to the path provided in the task prompt. Format: The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill orchestrated-planner --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/orchestrated-reviewer.md b/packages/agents/content/subagents/orchestrated-reviewer.md index a51acb5a..e95f3ecd 100644 --- a/packages/agents/content/subagents/orchestrated-reviewer.md +++ b/packages/agents/content/subagents/orchestrated-reviewer.md @@ -73,7 +73,7 @@ If the review concluded with no findings, the finalized form omits the `### Find The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill orchestrated-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/plan-reviewer.md b/packages/agents/content/subagents/plan-reviewer.md index 1cb3069d..1895395b 100644 --- a/packages/agents/content/subagents/plan-reviewer.md +++ b/packages/agents/content/subagents/plan-reviewer.md @@ -65,7 +65,7 @@ Each finding is tagged with a resolution type: ## Output format -Write the review to the output path provided in your task prompt. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) — see the [Frontmatter](#frontmatter) section below for field resolution. +Write the review to the output path provided in your task prompt. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc); see the [Frontmatter](#frontmatter) section below for field resolution. **Section organization:** Sections are grouped by **resolution type** (auto vs user), not by finding category (C vs X). Place every `auto`-tagged finding -- whether C or X -- in "Auto-resolvable findings". Place every `user`-tagged finding -- whether C or X -- in "Decision gaps". Every finding in "Decision gaps" must include a **Question** field. @@ -137,7 +137,7 @@ If the plan has no findings at all, write: The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill plan-reviewer --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/planner.md b/packages/agents/content/subagents/planner.md index c869f701..333b7262 100644 --- a/packages/agents/content/subagents/planner.md +++ b/packages/agents/content/subagents/planner.md @@ -74,7 +74,7 @@ Write the machine-readable plan to `{plan-json-path}`: ## Output: orchestration-plan.md -Write the human-readable plan to `{plan-md-path}`. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). The JSON sidecar does not carry frontmatter. The frontmatter conforms to the canonical schema — see the canonical example in the `artifact-conventions` data doc. +Write the human-readable plan to `{plan-md-path}`. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) (see [Frontmatter](#frontmatter) below for field resolution). The JSON sidecar does not carry frontmatter. The frontmatter conforms to the canonical schema; see the canonical example in the `artifact-conventions` data doc. ```markdown # Implementation Plan @@ -123,7 +123,7 @@ Write the human-readable plan to `{plan-md-path}`. The artifact begins with YAML The artifact's frontmatter conforms to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc). -Source `$MODEL_ID` from your system-prompt environment block — the line `model named ... model ID is ...`. +Source `$MODEL_ID` from your system-prompt environment block: the line `model named ... model ID is ...`. Run `resolve-frontmatter.sh --skill planner --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. diff --git a/packages/agents/content/subagents/savings-analyzer.md b/packages/agents/content/subagents/savings-analyzer.md index c78df9cf..27939ad2 100644 --- a/packages/agents/content/subagents/savings-analyzer.md +++ b/packages/agents/content/subagents/savings-analyzer.md @@ -64,7 +64,7 @@ Tag every suggestion: ## Output format -Write a markdown artifact with this structure. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc) — see the [Frontmatter](#frontmatter) section below for field resolution. +Write a markdown artifact with this structure. The artifact begins with YAML frontmatter conforming to the universal artifact frontmatter schema (defined in the `artifact-conventions` shared data doc); see the [Frontmatter](#frontmatter) section below for field resolution. The body following the frontmatter MUST include: @@ -101,7 +101,7 @@ Resolve fields before writing the artifact: - `provenance.timestamp`: Current UTC time in ISO 8601 format. - `provenance.baseSha`: Passed in via your dispatch prompt — the orchestrator resolves `git rev-parse --short origin/main` for the run-summary and forwards it. Omit if not provided. - `provenance.isInteractive`: Always `false`. -- `provenance.model`: The model identifier you are executing under. Read this from your system-prompt environment block — look for the line `model named ... model ID is ...` and use the model ID value. +- `provenance.model`: The model identifier you are executing under. Read this from your system-prompt environment block: Look for the line `model named ... model ID is ...` and use the model ID value. - `ticket_id`, `ticket_ref`: Passed in via your dispatch prompt. Omit when absent. - `branch`: Passed in via your dispatch prompt. - `commit`: Passed in via your dispatch prompt — the short HEAD SHA at run time.