Skip to content

Commit 75b5364

Browse files
committed
v0.52.0: GitHub Action sticky PR-comment output (gate · annotate · comment)
Add a 'comment' input/output to the Action: gate.py writes the findings summary (Markdown) to the given path; the example workflow posts it as a sticky PR comment via marocchino/sticky-pull-request-comment. Completes the CI UX alongside fail-on (gate) and SARIF (annotations). +1 test.
1 parent 789dbd4 commit 75b5364

8 files changed

Lines changed: 80 additions & 5 deletions

File tree

.github/action/gate.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ def main(env: dict[str, str] | None = None) -> int:
184184

185185
summary = render_summary(report, findings, fail_on, failed, max_sev)
186186
_write_summary(summary)
187+
188+
comment_path = (env.get("VSTACK_COMMENT") or "").strip()
189+
if comment_path:
190+
with open(comment_path, "w", encoding="utf-8") as fh:
191+
fh.write(summary)
192+
_set_output("comment", comment_path)
193+
187194
print(summary)
188195
return 1 if failed else 0
189196

.github/action/test_gate.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,36 @@ def test_sarif_from_report() -> None:
105105
results[0]["locations"][0]["physicalLocation"]["artifactLocation"]["uri"]
106106
== "traces/run.json"
107107
)
108+
109+
110+
def test_comment_file_written(tmp_path, monkeypatch) -> None:
111+
# When VSTACK_COMMENT is set, main() writes the Markdown summary to it.
112+
import json as _json
113+
114+
trace = tmp_path / "t.json"
115+
trace.write_text(
116+
_json.dumps(
117+
{
118+
"agent_id": "a",
119+
"goal": "g",
120+
"steps": [
121+
{"timestamp": "2026-01-01T00:00:00Z", "type": "observation", "content": "x"}
122+
],
123+
"outcome": "o",
124+
"success": False,
125+
}
126+
)
127+
)
128+
comment = tmp_path / "comment.md"
129+
monkeypatch.chdir(tmp_path)
130+
rc = gate.main(
131+
{
132+
"VSTACK_TRACE": str(trace),
133+
"VSTACK_FAIL_ON": "high",
134+
"VSTACK_CLIENT": "none",
135+
"VSTACK_COMMENT": str(comment),
136+
}
137+
)
138+
assert rc == 0
139+
body = comment.read_text()
140+
assert "vstack agent-quality gate" in body

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ project adheres to [Semantic Versioning](https://semver.org/) from
66
`1.0.0` onward. During the `0.x` series, minor bumps may include
77
breaking changes (see API stability promise in `vstack/__init__.py`).
88

9+
## [0.52.0] — 2026-06-23
10+
11+
The Action's CI UX is now complete: gate · annotate · comment.
12+
13+
### Added
14+
15+
- **GitHub Action `comment` input + output** — set `comment: vstack-comment.md`
16+
and the Action writes the findings summary (Markdown) to that path; the
17+
example workflow posts it as a **sticky PR comment** via
18+
`marocchino/sticky-pull-request-comment`, so findings land in the PR
19+
conversation (not just the job summary). Complements the existing build
20+
**gate** (`fail-on`) and code-scanning **annotations** (`sarif`). +1 test.
21+
22+
### Compatibility
23+
24+
- All tests pass. Opt-in; no breaking changes.
25+
926
## [0.51.0] — 2026-06-23
1027

1128
Bring-your-own-traces: import the logs you already have into a vstack trace.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ vstack ships as a composite **GitHub Action** so you can shift agent-quality lef
542542
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
543543
```
544544
545-
It writes a findings table to the job summary and exposes `max-severity`, `findings-count`, `report`, and `sarif` outputs. With `client: none` it runs the deterministic analyzers only (no API key needed) — a free smoke gate. Set `sarif: vstack.sarif` to also emit a **SARIF 2.1.0** report and upload it with `github/codeql-action/upload-sarif` — findings then show up in the **Security tab and as PR annotations**. Full example: [examples/github-action/agent-quality-gate.yml](examples/github-action/agent-quality-gate.yml).
545+
It writes a findings table to the job summary and exposes `max-severity`, `findings-count`, `report`, `sarif`, and `comment` outputs. With `client: none` it runs the deterministic analyzers only (no API key needed) — a free smoke gate. The full CI UX: **gate** the build (`fail-on`), **annotate** the PR (`sarif: vstack.sarif` → `github/codeql-action/upload-sarif` → Security tab + inline annotations), and **comment** in the PR conversation (`comment: vstack-comment.md` → a sticky comment via `marocchino/sticky-pull-request-comment`). Full example: [examples/github-action/agent-quality-gate.yml](examples/github-action/agent-quality-gate.yml).
546546

547547
Not on GitHub Actions? The same gating works from any shell — the core CLI is self-contained:
548548

_packaging/vstack/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373

7474
from __future__ import annotations
7575

76-
__version__ = "0.51.0"
76+
__version__ = "0.52.0"
7777

7878
# The diagnose() function and PATTERNS registry are lazy-imported below
7979
# so that ``import vstack`` itself stays cheap. Pattern sub-packages

action.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ inputs:
4242
github/codeql-action/upload-sarif to surface findings in code scanning).
4343
required: false
4444
default: ""
45+
comment:
46+
description: >-
47+
If set to a path, write the findings summary (Markdown) there so you can
48+
post it as a sticky PR comment (e.g. marocchino/sticky-pull-request-comment).
49+
required: false
50+
default: ""
4551
version:
4652
description: "valanistack version to install (e.g. '0.45.0'). Empty = latest."
4753
required: false
@@ -64,6 +70,9 @@ outputs:
6470
sarif:
6571
description: "Path to the written SARIF report (empty unless `sarif` input set)."
6672
value: ${{ steps.gate.outputs.sarif }}
73+
comment:
74+
description: "Path to the written Markdown comment (empty unless `comment` input set)."
75+
value: ${{ steps.gate.outputs.comment }}
6776

6877
runs:
6978
using: "composite"
@@ -106,4 +115,5 @@ runs:
106115
VSTACK_CLIENT: ${{ inputs.client }}
107116
VSTACK_SHAPE: ${{ inputs.shape }}
108117
VSTACK_SARIF: ${{ inputs.sarif }}
118+
VSTACK_COMMENT: ${{ inputs.comment }}
109119
run: python "${{ github.action_path }}/.github/action/gate.py"

examples/github-action/agent-quality-gate.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ jobs:
3535
# Full LLM-backed gate (needs a key in repo secrets):
3636
- name: vstack gate (LLM-backed)
3737
id: vstack
38-
uses: valani9/vstack@v0.48.0
38+
uses: valani9/vstack@v0.52.0
3939
with:
4040
trace: traces/latest.json
4141
client: anthropic
4242
mode: standard
4343
fail-on: high
44-
sarif: vstack.sarif # also emit SARIF for code scanning
44+
sarif: vstack.sarif # emit SARIF for code scanning
45+
comment: vstack-comment.md # write a Markdown findings summary
4546
env:
4647
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
4748

@@ -52,3 +53,10 @@ jobs:
5253
uses: github/codeql-action/upload-sarif@v3
5354
with:
5455
sarif_file: ${{ steps.vstack.outputs.sarif }}
56+
57+
# Post (and keep updating) a sticky findings comment on the PR:
58+
- name: Comment findings on PR
59+
if: always() && github.event_name == 'pull_request' && steps.vstack.outputs.comment != ''
60+
uses: marocchino/sticky-pull-request-comment@v2
61+
with:
62+
path: ${{ steps.vstack.outputs.comment }}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "valanistack"
3-
version = "0.51.0"
3+
version = "0.52.0"
44
description = "Organizational behavior, practiced on AI agents."
55
readme = "README.md"
66
requires-python = ">=3.11"

0 commit comments

Comments
 (0)