Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions .github/workflows/claude-pr-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ name: claude-pr-review
# review is advisory; no check is required.
#
# Authentication is Anthropic workload identity federation (no Anthropic key in this repository). A dispatch or a
# schedule run on main presents the subject the federation rule accepts, as harness-audit.yml does. A pull_request
# run would not (its subject is `repo:OWNER/REPO:pull_request`) and would run the pull request's own copy of this
# schedule run on main presents the immutable subject
# `repo:seathatflowsinourveins@234074349/native-agent-stack@1376766892:ref:refs/heads/main`.
# Acceptance still depends on the Console rule matching that subject. A pull_request run's derived form is
# `repo:OWNER@OWNER-ID/REPO@REPO-ID:pull_request`: native REST sub_claim_prefix plus the separately documented
# pull_request suffix (retained OIDC snapshot:332-336, revision 35d79cb1, decision 2026-10-10).
# It would run the pull request's own copy of this
# file, and workflow_run is a trigger this repository's zizmor gate and policy refuse, so the schedule is how every
# pull request is reached. The resolve job, which holds no token, picks the heads and checks each through the API;
# the review job checks again with its own read before any token is requested. Both run only while the repository
Expand Down Expand Up @@ -444,8 +448,10 @@ jobs:
(map(select(.type == "system" and .subtype == "init")) | first) as $init |
(map(select(.type == "result")) | last) as $result |
if $result == null then error("no result") else . end |
(if $result.is_error == true and $result.total_cost_usd == 0 and $result.modelUsage == {}
then "zero_cost_error_without_model_usage" else null end) as $error_class |
if ($result.total_cost_usd | type == "number" and . >= 0) and ($result.num_turns | count) and
($result.modelUsage | type == "object" and length > 0)
(($result.modelUsage | type == "object" and length > 0) or $error_class != null)
then . else error("usage unavailable") end |
($init.tools // []) as $listed |
($listed | map(select(type == "string"))) as $tools |
Expand All @@ -457,6 +463,7 @@ jobs:
elif $result.subtype == "error_max_budget_usd" then $written else "" end) as $text |
([.[] | select(.type == "assistant") | .message.id? | select(type == "string")] | unique | length) as $turns |
{total_cost_usd: $result.total_cost_usd, num_turns: $result.num_turns, assistant_turns: $turns,
error_class: $error_class,
result_chars: ($text | length),
tools_listed: (($init.tools | type) == "array" and ($init.mcp_servers | type) == "array"),
successful_result: ($result.subtype == "success" and $result.is_error == false),
Expand Down Expand Up @@ -484,6 +491,9 @@ jobs:
"| assistant turns | messages | client cost estimate (USD) | completed | Claude Code | tools | MCP servers |",
"| ---: | ---: | ---: | --- | --- | --- | ---: |",
"| \(.assistant_turns) | \(.num_turns) | \(.total_cost_usd) | \(.successful_result) | \(.claude_code_version) | \(.tools | join(" ")) | \(.mcp_servers) |",
(if .error_class != null
then "", "Error class: \(.error_class). The result reports zero client cost and no model usage; this does not establish the cause."
else empty end),
(if .budget_stop
then "", "Budget stop: the client stopped the run at its \($budget) USD budget (error_max_budget_usd), before a final report."
else empty end),
Expand All @@ -495,6 +505,7 @@ jobs:
"$out/usage.json"
} >> "$GITHUB_STEP_SUMMARY"
failed="$(jq -r --argjson budget "$budget" --argjson bound "$cost_bound" '[
(if .error_class != null then "error class: \(.error_class)" else empty end),
(if .successful_result != true and .budget_stop != true
then "the run did not end in success or in a budget stop" else empty end),
(if .session_started != true then "no session start record" else empty end),
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/claude-pr-toolkit-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,13 @@ jobs:
if type != "array" then error("unsupported execution shape") else . end |
(map(select(.type == "system" and .subtype == "init")) | first) as $init |
map(select(.type == "result")) as $results |
(if any($results[]; .is_error == true and .total_cost_usd == 0 and .modelUsage == {})
then "zero_cost_error_without_model_usage" else null end) as $error_class |
(($results | length) > 0 and all($results[]; usable)) as $complete |
([.[] | select(.type == "assistant") | .message | select(type == "object" and (.usage | type) == "object")]
| group_by(.id) | map(first)) as $messages |
if $complete or ($messages | length) > 0 then . else error("usage unavailable") end |
if $complete or ($messages | length) > 0 or $error_class != null
then . else error("usage unavailable") end |
($init.tools // []) as $listed |
($listed | map(select(type == "string"))) as $tools |
([.[] | select(.type == "result") | .result // "" | select(type == "string" and . != "")] | last // "")
Expand All @@ -345,6 +348,7 @@ jobs:
([.[] | select(.type == "assistant" and (.parent_tool_use_id == null)) | .message.id?
| select(type == "string")] | unique | length) as $turns |
{complete: $complete,
error_class: $error_class,
total_cost_usd: (if $complete then ([$results[].total_cost_usd] | max) else null end),
num_turns: (if $complete then ([$results[].num_turns] | max) else null end),
assistant_turns: $turns,
Expand Down Expand Up @@ -390,6 +394,9 @@ jobs:
"| ---: | ---: | ---: | --- | ---: | --- | --- | ---: |",
"| \(.assistant_turns) | \(.num_turns // "none") | \(.total_cost_usd // "none") | \(.successful_result) | \(.report_sections | length) | \(.claude_code_version) | \(.tools | join(" ")) | \(.mcp_servers) |",
"", "Result records: \(.result_subtypes | listed). Agents called: \(.agents_called | listed). Handbacks: \(.handbacks) of 2.",
(if .error_class != null
then "", "Error class: \(.error_class). A result reports zero client cost and no model usage; this does not establish the cause."
else empty end),
(if any(.result_subtypes[]; . == "error_max_budget_usd")
then "", "Budget stop: the client stopped the run at its \($budget) USD budget (error_max_budget_usd), with \(.handbacks) of 2 handbacks."
else empty end),
Expand All @@ -403,6 +410,7 @@ jobs:
} >> "$GITHUB_STEP_SUMMARY"
failed="$(jq -r --argjson budget "$budget" --argjson bound "$cost_bound" '
def listed: if length > 0 then join(" ") else "none" end; [
(if .error_class != null then "error class: \(.error_class)" else empty end),
(if .complete != true then "no result record, or one without usable usage: the token counts kept are a lower bound from the assistant messages, without a cost"
else empty end),
(if .successful_result != true then "the run did not end in success, or in a budget stop after both agents handed back (result records: \(.result_subtypes | listed); handbacks: \(.handbacks) of 2)"
Expand Down
15 changes: 12 additions & 3 deletions .github/workflows/harness-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ name: harness-audit

# Weekly audit of the always-loaded harness context with the owner's chosen reference, the claude-code-ultimate-guide
# audit prompt v5.0 (pinned commit and sha256 below). Authenticates by Anthropic workload identity federation: GitHub
# issues a short-lived OIDC token per run and no Anthropic key is stored in this repository. The federation rule accepts
# workflows on this repository's main and never pull requests; which workflows may request an OIDC token is governed by
# issues a short-lived OIDC token per run and no Anthropic key is stored in this repository. Main presents the immutable
# subject `repo:seathatflowsinourveins@234074349/native-agent-stack@1376766892:ref:refs/heads/main`.
# Acceptance still depends on the Console rule matching that subject; the denied historical run is not acceptance.
# The main-only design excludes pull requests; which workflows may request an OIDC token is governed by
# the reviewed write-grant inventory (id-token: write) that tests.test_workflow_policy enforces. id-token: write here is
# a reviewed exemption from the tripwire's id-token-write rule (docs/decisions/2026-10-04-ci-least-privilege.md,
# "Federation exemption (2026-10-08)").
Expand Down Expand Up @@ -159,8 +161,10 @@ jobs:
(map(select(.type == "system" and .subtype == "init")) | first) as $init |
(map(select(.type == "result")) | last) as $result |
if $result == null then error("no result") else . end |
(if $result.is_error == true and $result.total_cost_usd == 0 and $result.modelUsage == {}
then "zero_cost_error_without_model_usage" else null end) as $error_class |
if ($result.total_cost_usd | type == "number" and . >= 0) and ($result.num_turns | count) and
($result.modelUsage | type == "object" and length > 0)
(($result.modelUsage | type == "object" and length > 0) or $error_class != null)
then . else error("usage unavailable") end |
($init.tools // []) as $listed |
($listed | map(select(type == "string"))) as $tools |
Expand All @@ -172,6 +176,7 @@ jobs:
elif $result.subtype == "error_max_budget_usd" then $written else "" end) as $text |
([.[] | select(.type == "assistant") | .message.id? | select(type == "string")] | unique | length) as $turns |
{total_cost_usd: $result.total_cost_usd, num_turns: $result.num_turns, assistant_turns: $turns,
error_class: $error_class,
result_chars: ($text | length),
tools_listed: (($init.tools | type) == "array" and ($init.mcp_servers | type) == "array"),
successful_result: ($result.subtype == "success" and $result.is_error == false),
Expand All @@ -198,12 +203,16 @@ jobs:
jq -r '"| assistant turns | messages | client cost estimate (USD) | completed | result subtype | Claude Code | tools | MCP servers |",
"| ---: | ---: | ---: | --- | --- | --- | --- | ---: |",
"| \(.assistant_turns) | \(.num_turns) | \(.total_cost_usd) | \(.successful_result) | \(.result_subtype) | \(.claude_code_version) | \(.tools | join(" ")) | \(.mcp_servers) |",
(if .error_class != null
then "", "Error class: \(.error_class). The result reports zero client cost and no model usage; this does not establish the cause."
else empty end),
"", "| model | input | cache write | cache read | output |", "| --- | ---: | ---: | ---: | ---: |",
(.models[] | "| \(.model) | \(.input_tokens) | \(.cache_creation_input_tokens) | \(.cache_read_input_tokens) | \(.output_tokens) |")' \
"$out/usage.json"
} >> "$GITHUB_STEP_SUMMARY"
failed="$(jq -r --argjson bound "$bound" '
(.result_subtype == "error_max_budget_usd" and .total_cost_usd <= $bound) as $budget_stop | [
(if .error_class != null then "error class: \(.error_class)" else empty end),
(if .successful_result != true and ($budget_stop | not)
then "the run did not end in success (subtype \(.result_subtype))" else empty end),
(if .session_started != true then "no session start record" else empty end),
Expand Down
8 changes: 6 additions & 2 deletions docs/decisions/2026-10-08-claude-actions-pr-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,12 @@ pull requests give that comparison its vendor arm, and the command center reads
non-draft head.

**Trigger: a 15-minute schedule, beside the dispatch.** Two other designs were checked and rejected:
- **A `pull_request` trigger** presents the OIDC subject `repo:OWNER/REPO:pull_request` (GitHub's OpenID Connect
reference, "Filtering for pull_request events"), which the federation rule does not accept. It would also run the
- **A `pull_request` trigger** has the derived immutable form `repo:OWNER@OWNER-ID/REPO@REPO-ID:pull_request`.
The native repository OIDC REST metadata supplies `sub_claim_prefix`, and the event suffix comes from
GitHub's "Filtering for pull_request events", [retained OIDC snapshot:332](../../evidence/artifacts/claude-federation-docs-20261010/docs.github.com_actions_reference_security_oidc.txt#L332)
(revision `sha256:35d79cb17e94732a467c63e59c3a01d18029b47f4b5f9cbf15d92037164b03cd`, retrieved 2026-10-10, 37248 bytes;
immutable syntax separately at 352–359). Combining that prefix and documented suffix is a derivation, not an example
quoted from the immutable-subject section. A main-only federation rule does not accept it. It would also run the
pull request's own copy of the workflow file with the token.
- **`workflow_run`** runs main's copy with main's subject (GitHub's "Events that trigger workflows": GITHUB_REF is
the default branch). claude-code-action supports it as an automation event at the pin
Expand Down
Loading
Loading