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..3f79dce2 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" @@ -143,3 +163,542 @@ 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 + +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 "abin/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 d3eba6ec..cec62009 100755 --- a/packages/agents/content/scripts/resolve-frontmatter.sh +++ b/packages/agents/content/scripts/resolve-frontmatter.sh @@ -1,43 +1,52 @@ #!/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 -# 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 +# 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 +58,93 @@ show_usage() { local exit_code="${1:-1}" cat <&2 + show_usage 1 + ;; + esac + done + + case "$format" in + yaml | json) ;; + *) fail "unknown --format: $format (expected yaml|json)" ;; + esac - if [[ "$#" -gt 0 ]]; then - echo "$PROG: unexpected argument: $1" >&2 - show_usage 1 + 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,20 +168,90 @@ 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 +} + +# 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" } -# Print short SHA of `default_branch` (e.g., `origin/main`) or empty if -# unresolvable. A shallow clone or missing remote silently degrades to empty. +# 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" + local -n keys_ref="$3" + local -n values_ref="$4" + local -n kinds_ref="$5" + local key value + parse_key_value "$arg" "--extra/--extra-list" key value + 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" +} + +# 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 + parse_key_value "$arg" "--override" key value + overrides_ref["$key"]="$value" +} + +# 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" + if [[ -n "${overrides_ref[$key]+set}" ]]; then + printf '%s' "${overrides_ref[$key]}" + else + printf '%s' "$resolved" + fi +} + +# 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" @@ -116,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 @@ -135,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 @@ -165,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 @@ -189,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 @@ -209,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" @@ -224,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" @@ -240,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//\//-}" @@ -249,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" @@ -281,8 +423,142 @@ emit_json() { ' } -# Require each named command to be on PATH. Exits 1 with a clear message -# when one is missing. +# 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}" + 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 + # 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 + + 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")" +} + +# 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 + printf '%s: []\n' "$key" + return + fi + local IFS=',' + # 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 ) + set +f + 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" +} + +# 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" + if needs_yaml_quoting "$v"; then + local escaped="${v//\'/\'\'}" + printf "'%s'" "$escaped" + else + printf '%s' "$v" + fi +} + +# 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 `#`. +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. `?` as a leading + # character is handled by the glyphs-anywhere case below. + 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() { local cmd for cmd in "$@"; do @@ -296,6 +572,13 @@ fail() { exit 1 } +# 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 +} + # Guard against execution when sourced (e.g., from shellspec tests). if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" 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/_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 diff --git a/packages/agents/content/skills/create-devlog/SKILL.md b/packages/agents/content/skills/create-devlog/SKILL.md index 46fe2030..bd865aa8 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" \ + ${commits_arg:+--extra-list "commits=$commits_arg"} \ + --override "run_id=$run_id_arg" +``` + +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 ...`. -- `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..2a6250f9 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..9b4690e8 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 @@ -79,12 +65,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 ...`. + +Run `resolve-frontmatter.sh --skill ex-post-facto --interactive true --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. -- `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 ...`. - +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..5b0f5ce4 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 | @@ -346,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`: @@ -388,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. @@ -414,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: @@ -422,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}`. @@ -458,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 @@ -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 @@ -743,11 +715,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..75fe3df6 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..8e3505aa 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..2bd209a1 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..a9737c3e 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..7bfcaced 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,25 @@ 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" \ + ${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. -- `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..8bdff4f1 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..be849b08 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..f37dcce7 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..f9512f28 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..bcb9751d 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..17478abb 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..36988cf2 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..d760f8d2 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..e17e8184 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 @@ -148,12 +133,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 ...`. + +Run `resolve-frontmatter.sh --skill orchestrated-planner --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. -- `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 ...`. - +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..e95f3ecd 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..1895395b 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..333b7262 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 @@ -138,12 +123,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 ...`. + +Run `resolve-frontmatter.sh --skill planner --interactive false --model "$MODEL_ID"` via Bash. Prepend the output verbatim to the artifact body. -- `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 ...`. - +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..27939ad2 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 @@ -114,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.