Repository navigation
Conversation
Ports cell-ontology's `/clara` PR-review workflow to uberon, using the external Cellular-Semantics/clara_workflow package for stage 1 extraction (robot diff -> changes.json) and stage 2/3 literature verification of routed targets. Adjustments vs. the CL original: - `--edit-file src/ontology/uberon-edit.obo` (OBO, not OWL). Verified empirically that `robot diff` detects format from content, not file extension, so this works with the extractor unmodified. One known gap: clara_workflow's `definition_refs()` fallback only recognises OWL functional-syntax `AnnotationAssertion(...)` lines, so it silently contributes no refs for our OBO edit file. This reduces routing coverage for structural axioms on terms whose definition wasn't touched by a given PR, but doesn't break the pipeline. Filed as a gap to fix upstream separately. - ROBOT wrapper now honors `$ROBOT_JAVA_ARGS` (set to -Xmx9G on the extractor step), matching the heap size uberon's own diff.yml/qc.yml use for robot on this ontology. The CL wrapper hardcodes no heap flag at all, which risks OOM on an ontology this much larger than CL. - `CLARA_WORKFLOW_REF` pinned to a commit rather than tracking `main`, per that repo's own adoption guidance. - Added `src/scripts/clara_select_targets.py`, copied unchanged from cell-ontology (it's ontology-agnostic: pure PMID/DOI + change-kind routing logic over `changes.json`). - Added `.github/clara-mcp.json`, a minimal MCP config scoped to just `Asta_semanticscholar` + `artl-mcp` for this workflow, rather than reusing the root dev `.mcp.json` (which also declares `ols4` and `playwright` — not wanted for an unattended CI agent, and playwright needs browser binaries not installed on the runner). Also known but not addressed here: clara_workflow's `agent_instructions.md` uses cell-type-specific wording (`cell_id`, "the cell type") in its verification instructions and output schema, which will show up verbatim in verdicts/reports for uberon terms. Left as-is for this PR; tracked as a follow-up to fix upstream. Requires the `ASTA_API_KEY` secret to be added to this repo before the stage 2/3 verification step can run (not set by this PR). Signed-off-by: @ai4c-agent Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cellular-Semantics/clara_workflow#8 drops the separate underscore-form cell_id field from verdicts.json in favor of the single CURIE-form term_id already used elsewhere in the routing payload. Updates the summary-comment step here to match, so it doesn't KeyError once the CLARA_WORKFLOW_REF pin is bumped to that commit (not done yet). Signed-off-by: @ai4c-agent Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This PR modifies some logical definitions. See details below:Number of classes with modified logical definitions: 3 http://purl.obolibrary.org/obo/UBERON_8930105No changes in inferred subclasses. http://purl.obolibrary.org/obo/UBERON_8930106No changes in inferred subclasses. http://purl.obolibrary.org/obo/UBERON_8930191Removed subclasses: 2 |
Cellular-Semantics/clara_workflow#8 merged (232e9b4): adds catalog support to the stage-1 extractor and drops the cell_id field (already matched in the previous commit here). - CLARA_WORKFLOW_REF bumped to the merge commit. - Added --catalog src/ontology/catalog-v001.xml to the stage-1 extractor invocation. This is the part that actually matters: without it, the extractor falls back to ROBOT's default network import resolution and hits the same 404 (one of uberon's import PURLs is currently broken) that motivated the catalog fix in the first place. The catalog lets ROBOT resolve every owl:imports from the files it already maps to locally-committed content, with no network call. - Updated the header comment, which described the pre-fix limitations. Signed-off-by: @ai4c-agent Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Revises UBERON:0004177's definition and cites a fabricated DOI (DOI:10.9999/test.uberon.4177, not a real reference) solely to give the CLARA review workflow a real, committed change to diff end-to-end in CI. This commit will be reverted once the test run is reviewed -- not a real content edit.
This reverts commit 70c16f3.
|
Claude finished @ubyndr's task in 43s —— View job PR review completeI submitted a REQUEST_CHANGES review on this PR. I reviewed the diff by reading the files only and did not run the workflow.
I made no code changes. The ontology-specific checks don't apply, because the PR touches no terms. |
There was a problem hiding this comment.
- Hierarchy and parents are consistent (n/a, no ontology changes)
- Definitions and supporting references look adequate (n/a)
- Obsolete or replacement handling is safe (n/a)
- OBO and ODK conventions appear preserved
- No obvious CI, auth, or workflow regression
🔴 CRITICAL: /clara can be triggered by anyone, with secrets, over untrusted PR content
clara-review.yml:49-53 only checks that the comment starts with /clara. Any GitHub user can comment on any PR, including fork PRs, and start a run that has CLAUDE_CODE_OAUTH_TOKEN, ASTA_API_KEY and a write-scoped GITHUB_TOKEN.
The job then checks out the PR head (line 91) and gives Claude Code the PR's content. That includes the PR's own CLAUDE.md and ontology text, which is a prompt-injection surface. The run also shells out to gh.
Gate the job on github.event.comment.author_association being OWNER, MEMBER or COLLABORATOR. Also consider refusing PRs whose head repo differs from the base repo.
🟡 IMPORTANT: concurrency group cancels running reviews on any comment
clara-review.yml:43-45 builds the concurrency group from github.event.issue.number at workflow level. Any comment on the PR, even a non-/clara one, joins the group.
With cancel-in-progress: true, an ordinary comment posted mid-run cancels the CLARA run. That is because the workflow starts before the job-level if skips it. Either scope the group to the job so only /clara runs join it, or use cancel-in-progress: false.
🟡 IMPORTANT: --left is the base branch tip, not the merge-base
clara-review.yml:127 and :81 use baseRefOid, which is the current master tip. If the PR branch is behind master, the diff includes unrelated master changes as reverse edits. Use git merge-base <base> <head> for --left. Full history is already fetched.
🔵 Suggestions
clara-review.yml:59:${{ inputs.pr }}is interpolated directly into the shell. Pass it viaenv:and validate that it is numeric. The same applies to the other${{ }}values insiderun:blocks.clara-review.yml:176:$(uv tool dir)/binis not the tool bin directory.uv tool dir --binis. This is harmless today because the MCP config usesuv tool run, but the line is misleading.clara-review.yml:96: ROBOT is downloaded fromreleases/latest. Pin a version, as you did forCLARA_WORKFLOW_REF. Pinuvandartl-mcpversions too.clara-review.yml:225:if-no-files-found: warnis fine. The summary step already tolerates a missingruns/.- The follow-up you already noted (cell-type wording in
agent_instructions.md) is worth tracking as an issue.
Merge recommendation: fix the author gate and the concurrency group before merge. The ASTA_API_KEY secret and the live dispatch run remain outstanding, as noted in the PR description.
Signed: @ai4c-agent
Per ai4c-reviewer's REQUEST_CHANGES review on this PR:
- CRITICAL: /clara had no check on who posted the triggering comment,
so anyone (including on a fork PR) could start a run with access to
CLAUDE_CODE_OAUTH_TOKEN, ASTA_API_KEY, and a write-scoped
GITHUB_TOKEN, and hand the PR's own untrusted content to Claude. Adds
a new check-authorization job that:
- checks out only the default branch, never PR content, before
reading anything
- for issue_comment triggers, requires the commenter be listed in
.github/ai-controllers.json (the same trusted-controller list
ai-agent.yml already uses for this exact purpose);
workflow_dispatch is trusted as-is, since GitHub itself already
requires write access to trigger it
- independently refuses any PR whose head repo differs from its
base repo (a fork), regardless of who triggered it -- a trusted
commenter doesn't make a fork's CLAUDE.md/ontology text
trustworthy once it's checked out and handed to Claude
The review job now depends on this and only runs if both checks pass.
- IMPORTANT: the concurrency group was computed at workflow level from
the PR/issue number alone, before the job-level /clara check ran.
Since issue_comment fires for every comment on the PR, an unrelated
comment posted mid-run would join the same group and (with
cancel-in-progress: true) cancel an actually-running review. Moved
concurrency to the review job itself, keyed on
needs.check-authorization.outputs.pr_num: check-authorization's own
job-level `if:` already skips (never runs, never queues) for
anything that isn't a qualifying /clara, so only a real /clara run
ever touches this group now.
- Suggestions: `inputs.pr` (and its pr_num passthrough) were
interpolated directly into `run:` shell blocks; moved to `env:` and
added numeric validation on the source input. Fixed `uv tool dir`
missing `--bin` (confirmed empirically: `uv tool dir` and
`uv tool dir --bin` return different, non-nested paths, so the old
`$(uv tool dir)/bin` pointed at a path that doesn't exist -- harmless
today only because the actual MCP call uses `uv tool run`, not PATH
lookup).
Not yet addressed from that review: --left using the base branch tip
instead of the merge-base, and pinning ROBOT/uv/artl-mcp versions.
Signed-off-by: @ai4c-agent
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @ubyndr's task in 29s —— View job Re-review of the CLARA workflow after 385e5b9I read the current
Previous findings
Remaining🟡 IMPORTANT:
🔵 Suggestions
Merge recommendation🟡 Fix the merge-base before merge. Everything else in the earlier review is addressed. The Signed: @ai4c-agent |
Per ai4c-reviewer's REQUEST_CHANGES review: --left used baseRefOid (the current master tip), so a PR branch behind master would diff in every unrelated master change since divergence as spurious reverse-edits. Computes the merge-base via the GitHub compare API's merge_base_commit.sha instead -- verified against this repo that it matches `git merge-base` exactly, both when the branch is caught up (merge-base == tip) and when it's genuinely behind (merge-base is far behind tip). No local checkout is needed for this, so it stays in the same "Look up PR refs" step, before checkout even happens. Signed-off-by: @ai4c-agent Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The uberon port of this workflow (obophenotype/uberon#3779) got a REQUEST_CHANGES review from ai4c-reviewer flagging several issues that were copied over unchanged when this file was ported there, and are still live here on master: - CRITICAL: /clara had no check on who posted the triggering comment, so anyone (including on a fork PR) could start a run with access to CLAUDE_CODE_OAUTH_TOKEN, ASTA_API_KEY, and a write-scoped GITHUB_TOKEN, and hand the PR's own untrusted content to Claude. Adds a check-authorization job that checks out only the default branch (never PR content), requires the issue_comment author be listed in this repo's own .github/ai-controllers.json (the same list ai-agent.yml already uses here), and independently refuses any PR whose head repo differs from its base repo (a fork), regardless of who triggered it. - IMPORTANT: --left used baseRefOid (the base branch's current tip), so a PR branch behind master would diff in every unrelated master change since divergence as spurious reverse-edits. Now computed via the GitHub compare API's merge_base_commit.sha, verified against the uberon repo to match `git merge-base` exactly. - IMPORTANT: the concurrency group was computed at workflow level from the PR/issue number alone, before the job-level /clara check ran, so an unrelated comment posted mid-run would join the same group and cancel an actually-running review. Moved to job-level concurrency on review, keyed on needs.check-authorization.outputs.pr_num. - Suggestions: inputs.pr (and its pr_num passthrough) moved from direct `${{ }}` shell interpolation to env:, with numeric validation added on the source input. Fixed `uv tool dir` missing `--bin` (confirmed empirically these return different, non-nested paths, so the old `$(uv tool dir)/bin` pointed at a path that doesn't exist). Not yet addressed: pinning ROBOT/uv/artl-mcp versions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @ubyndr's task in 30s —— View job Re-review after d1cb998 (merge-base fix)I read
Previous findings
Remaining 🔵 suggestions (non-blocking)
Merge recommendation🟢 No blocking issues remain, so I would approve. The Signed: @ai4c-agent |
* Pin CLARA_WORKFLOW_REF, add --catalog, fix cell_id->term_id Found while integrating the CLARA review workflow into uberon (obophenotype/uberon#3778, PR #3779). - CLARA_WORKFLOW_REF was tracking `main` unpinned. Cellular-Semantics/ clara_workflow#8 merged to main and dropped verdicts.json's cell_id field in favor of term_id, which this workflow's summary-comment step doesn't know about -- the next /clara run here with any routed targets would KeyError on cell_id. Pinning to that merge commit (232e9b4) and fixing the two cell_id reads to term_id un-breaks this and stops future upstream changes from landing silently. - Added --catalog src/ontology/catalog-v001.xml to the stage-1 extractor step. CL already has this ODK-managed catalog checked in; passing it lets ROBOT resolve every owl:imports from the locally committed files it maps to instead of the network, so this workflow isn't exposed to a future broken import PURL the way uberon's currently is. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Address ai4c-reviewer's uberon review here too: same workflow, same bugs The uberon port of this workflow (obophenotype/uberon#3779) got a REQUEST_CHANGES review from ai4c-reviewer flagging several issues that were copied over unchanged when this file was ported there, and are still live here on master: - CRITICAL: /clara had no check on who posted the triggering comment, so anyone (including on a fork PR) could start a run with access to CLAUDE_CODE_OAUTH_TOKEN, ASTA_API_KEY, and a write-scoped GITHUB_TOKEN, and hand the PR's own untrusted content to Claude. Adds a check-authorization job that checks out only the default branch (never PR content), requires the issue_comment author be listed in this repo's own .github/ai-controllers.json (the same list ai-agent.yml already uses here), and independently refuses any PR whose head repo differs from its base repo (a fork), regardless of who triggered it. - IMPORTANT: --left used baseRefOid (the base branch's current tip), so a PR branch behind master would diff in every unrelated master change since divergence as spurious reverse-edits. Now computed via the GitHub compare API's merge_base_commit.sha, verified against the uberon repo to match `git merge-base` exactly. - IMPORTANT: the concurrency group was computed at workflow level from the PR/issue number alone, before the job-level /clara check ran, so an unrelated comment posted mid-run would join the same group and cancel an actually-running review. Moved to job-level concurrency on review, keyed on needs.check-authorization.outputs.pr_num. - Suggestions: inputs.pr (and its pr_num passthrough) moved from direct `${{ }}` shell interpolation to env:, with numeric validation added on the source input. Fixed `uv tool dir` missing `--bin` (confirmed empirically these return different, non-nested paths, so the old `$(uv tool dir)/bin` pointed at a path that doesn't exist). Not yet addressed: pinning ROBOT/uv/artl-mcp versions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * TEST (delete before merge): exercise check-authorization logic via push issue_comment and workflow_dispatch both require a workflow to already exist on the default branch before GitHub allows them to trigger, so the real clara-review.yml additions in this PR can't be exercised live until merge. push has no such restriction. This temporary workflow reuses the same logic (ai-controllers.json allowlist check, fork detection, merge-base computation) against real data, to validate it works before merging. To be deleted once reviewed. * TEST (revert after review): throwaway CL:0002336 def edit + full pipeline test Revises CL:0002336's (buccal mucosa cell) definition and cites a fabricated DOI (DOI:10.9999/test.cl.0002336, not a real reference), solely to give the full clara-review pipeline (catalog-based extractor, routing, stage 2/3 verification) a real, committed change to run against in actual GitHub Actions -- not just local script tests. Adds a temporary push-triggered workflow mirroring clara-review.yml's real steps against HEAD~1 -> HEAD, since issue_comment/workflow_dispatch can't run until #3764 merges (see _test-clara-checks.yml). Does not comment on the real PR. Both the ontology edit and this test workflow will be reverted/deleted once the run is reviewed. * Revert "TEST (revert after review): throwaway CL:0002336 def edit + full pipeline test" This reverts commit 81678da. * Revert "TEST (delete before merge): exercise check-authorization logic via push" This reverts commit 7fac802. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Found by actually getting a routed target through the pipeline for
the first time (against cell-ontology, same workflow) and watching
stage 2/3 fail in ~500ms with no real work done.
claude-code-action's mode detector (src/modes/detector.ts) forces
"tag" mode -- an interactive @mention comment-responder, with its own
Todo-list/tracking-comment scaffolding -- whenever track_progress is
true on an issue_comment event, before it ever checks whether a custom
prompt was supplied. That check only happens later, and only reaches
"agent" mode (the batch/file-writing mode this workflow actually needs)
if track_progress didn't already force tag mode.
track_progress: ${{ github.event_name == 'issue_comment' }} meant every
real /clara comment trigger got tag mode instead of agent mode,
regardless of our custom prompt. Setting it to a flat `false` lets the
detector's "prompt provided on a comment event -> agent mode" rule
apply as intended.
Signed-off-by: @ai4c-agent
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @ubyndr's task in 45s —— View job Re-review after d6da5ab (
|
The action hides the real error behind an opaque "result is_error:true" by default. Enabling this to see what's actually failing in stage 2/3, which still fails instantly even after the track_progress fix (agent mode now correctly selected, confirmed via cell-ontology test run, but the claude subprocess still errors in ~500ms with no visible turns). Remove once the real cause is found and fixed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @ubyndr's task in 30s —— View job Re-review after 6c46aff (TEMP DEBUG
|
With show_full_output enabled, the real error was finally visible: "error": "model_not_found" "There's an issue with the selected model (claude-sonnet-5-5). It may not exist or you may not have access to it." claude-sonnet-5-5 isn't a real model (the valid id is claude-sonnet-5); something in the action's default-model resolution for this OAuth token was producing a bad value. Explicitly pinning --model sidesteps whatever that resolution logic is doing. This, plus the earlier track_progress fix, are the two real causes of every stage 2/3 failure seen so far -- neither related to MCP config, the security hardening, or anything else touched in this PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @ubyndr's task in 25s —— View job Re-review after 70dfeea (
|
Served its purpose: revealed the model_not_found error (fixed in the previous commit) that a plain is_error:true was hiding. No longer needed now that --model is pinned explicitly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @ubyndr's task in 19s —— View job Re-review after 1ca3b41 (remove
|
Cellular-Semantics/clara_workflow#9 merged: fixes runs/{term_id}/ using the CURIE form (colon) in artifact paths, which made actions/upload-artifact fail outright and silently skipped the summary-comment steps on every real run that reached stage 2/3 (confirmed via cell-ontology's PR #3765). Signed-off-by: @ai4c-agent Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @ubyndr's task in 37s —— View job Re-review after eb020bf (bump
|
Ports three fixes made and tested against a real PR in cell-ontology (obophenotype/cell-ontology#3772, #3773), so uberon doesn't hit the same issues on its first real run: - Bump CLARA_WORKFLOW_REF to 234ac67, which includes Cellular-Semantics/clara_workflow#10: Asta_semanticscholar's `"tools": ["*"]` was invalid (Claude Code expects an array of objects, not strings) and silently dropped the entire server from the session -- not "connected with no tools," never attempted at all. That's why stage 3 snippet_search was never available in any run so far. - Point --mcp-config at clara_workflow_ref/.mcp.json (the external package's own, now-fixed config) instead of this repo's dedicated .github/clara-mcp.json, so MCP tool wiring travels with agent_instructions.md as one versioned unit via CLARA_WORKFLOW_REF, rather than needing to be kept in sync by hand in every consuming repo. Removes the now-unused .github/clara-mcp.json. - Revert track_progress back to its original conditional. The earlier "always false" change here was a misdiagnosis: it forced agent mode to work around what looked like a tag-mode problem, but a real historical cell-ontology run (#3745, 35205839595) proves tag mode completes this task fine, Write included. The actual instant failures seen while debugging were model_not_found (fixed via --model, already in place), not a mode problem -- forcing agent mode just traded tag mode's sufficient default permissions for agent mode's insufficient ones. Signed-off-by: @ai4c-agent Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @ubyndr's task in 37s —— View job Re-review after 2988955 (port CL fixes: MCP config,
|
|
Claude finished @dosumis's task in 29s —— View job Re-review of the CLARA workflow (current HEAD, c586f3f)I checked the diff stat and grepped
Findings
Remaining 🔵 suggestions (non-blocking, unchanged)
Merge recommendation🟢 No blocking issues, so I would approve. dosumis has already approved. The Signed: @ai4c-agent |
Summary
Ports cell-ontology's
/claraPR-review workflow to uberon, using the externalCellular-Semantics/clara_workflowpackage for stage-1 extraction (robot diff→changes.json) and stage-2/3 literature verification of routed targets. Closes/addresses #3778..github/workflows/clara-review.yml, triggered by a/claraPR comment or manual dispatch.src/scripts/clara_select_targets.py, copied unchanged from cell-ontology — it's ontology-agnostic routing logic overchanges.json, no CL-specific assumptions..github/clara-mcp.json, a minimal MCP config scoped to justAsta_semanticscholar+artl-mcpfor this workflow (kept separate from the root dev.mcp.json, which also declaresols4/playwrightthat we don't want an unattended CI agent to have, and which needs browser binaries not on the runner).Deviations from the CL original (and why)
--edit-file src/ontology/uberon-edit.obo(OBO, not OWL).robot diff/robot convertdetect format from content, not file extension, so the extractor works against OBO input without modification.--catalog src/ontology/catalog-v001.xmlpassed to the stage-1 extractor. Required here, not just a nice-to-have: one of uberon'sowl:importsPURLs currently 404s, and ROBOT resolves every import over the network by default. The catalog (ODK-managed, already checked into this repo) redirects every import to the locally-committed file it maps to, so resolution is fully offline and immune to the broken PURL. This also happens to fixdefinition_refs()for OBO edit files (see "Upstream fixes" below) — it needs the same catalog to convert the edit file to functional syntax without hitting the same broken import.$ROBOT_JAVA_ARGS(set to-Xmx9Gon the extractor step), matching the heap size uberon's owndiff.yml/qc.ymlalready use forroboton this ontology. The CL wrapper hardcodes no heap flag at all, which risks OOM here given how much larger uberon is than CL.CLARA_WORKFLOW_REFpinned to a commit rather than trackingmain, per that repo's own adoption guidance.Upstream fixes (Cellular-Semantics/clara_workflow#8, merged)
Both gaps originally flagged in this PR are now fixed upstream, and
CLARA_WORKFLOW_REFis pinned to the merge commit:definition_refs()returning nothing for OBO edit files — fixed by converting the edit file to OWL functional syntax (viarobot convert, using the same catalog) before parsing, instead of reading raw OBO text.Still outstanding, not fixed:
agent_instructions.mdstill uses cell-type-specific wording (core= "the subject is the cell type itself", "Preserve the cell-type subject...") that will show up in reports/verdicts for uberon's anatomical terms. Thecell_id/term_idduplication was cleaned up (verdicts.json now uses a singleterm_idfield, matched in this PR's summary-comment step), but the prose itself wasn't re-addressed after an earlier rollback. Tracked as a follow-up.Validation done so far
robot diffcorrectly parses OBO content regardless of file extension.clara_workflowpackage's stage-1 CLI (python -m clara_workflow.stage1.extract ... --catalog src/ontology/catalog-v001.xml) against real pushed commits on this branch, including a throwaway definition edit + fabricated DOI onUBERON:0004177(added, tested, then reverted — see commit history). Confirmed: catalog resolves all of uberon's real imports offline (including the one with the broken PURL),changes.json/routing.jsoncome out correct, anddefinition_refsis now populated for OBO input.workflow_dispatchand the/claracomment trigger both require the workflow file to exist on the default branch, so this can't be exercised live until this PR merges. Planning aworkflow_dispatchdry run against a small test PR right after merging, before treating/claraas generally available.Before this can run
ASTA_API_KEYsecret still needs to be added to this repo — not set by this PR. Without it, stage-2/3 verification will fail; stage-1 extraction and routing will still work and still post a summary comment.Test plan
robot diff/robot convertwith--catalogvalidated against the real uberon repo, including its actual broken import.clara_workflowpackage against real git commits on this branch.ASTA_API_KEYsecret added to repo settings.workflow_dispatchrun right after merge, against a small test PR, to confirm stage 1 → routing → stage 2/3 → summary comment end-to-end in actual GitHub Actions.Signed-off-by: @ai4c-agent
🤖 Generated with Claude Code