feat: add PR walkthrough generation workflow (#2933)
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# ADR-2026-08-22-agent-owned-pr-walkthrough-rendering: Keep PR walkthrough rendering agent owned and provider neutral
|
||||
|
||||
**Status:** accepted
|
||||
**Date:** 2026-08-22
|
||||
**Area:** workflow, infra, security
|
||||
|
||||
## Context
|
||||
|
||||
PR walkthrough generation starts with OpenCode, but the selected agent runner
|
||||
may change later. The agent must be able to correct invalid walkthrough data
|
||||
after seeing renderer errors, while the secret-bearing `pull_request_target`
|
||||
workflow must not grant a model broad write access or permission to execute
|
||||
pull-request-owned code.
|
||||
|
||||
## Decision
|
||||
|
||||
The provider-neutral `.agents/skills/pr-walkthrough/SKILL.md` owns the complete
|
||||
generation procedure. The selected agent creates the walkthrough data, invokes
|
||||
the trusted renderer, inspects any validation error, and revises the data until
|
||||
both the JSON and HTML outputs exist.
|
||||
|
||||
Each managed agent runner supplies the narrowest adapters needed to perform
|
||||
that procedure. The OpenCode runner exposes base-commit-controlled
|
||||
`render_pr_walkthrough` and `read_pr_file` custom tools. The renderer tool
|
||||
accepts walkthrough JSON, binds authoritative pull-request identity from the
|
||||
GitHub event, removes model-owned link overrides, and runs only the trusted
|
||||
renderer against fixed output paths. The file tool reads only bounded regular
|
||||
UTF-8 blobs from the immutable pull request head Git object. It never checks
|
||||
the untrusted head out in the secret-bearing worktree. The runner does not
|
||||
grant generic edit, shell, or external-directory access. A future Codex or
|
||||
other runner must preserve these boundaries, follow the same skill, and
|
||||
produce the same artifacts.
|
||||
|
||||
The generated artifact is one HTML file, not a fully offline bundle. Its fixed
|
||||
shell loads Tailwind, Mermaid, Marked, DOMPurify, Shiki, and fonts from
|
||||
exact-version or commit-pinned HTTPS URLs. Those providers are an explicit
|
||||
runtime trust dependency. Pull request data cannot choose or override these
|
||||
asset URLs.
|
||||
|
||||
Provider installation, credentials, and invocation remain workflow adapter
|
||||
concerns. They must not be embedded in the walkthrough skill or renderer
|
||||
contract.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The agent can repair malformed data during its own turn instead of returning
|
||||
invalid JSON for a later workflow step to reject.
|
||||
- Replacing OpenCode changes the runner adapter and workflow wiring, not the
|
||||
skill, JSON schema, renderer, object key, or hosting contract.
|
||||
- The OpenCode adapter adds a small trusted custom tool, but avoids broad model
|
||||
access to shell execution or repository writes.
|
||||
- Reviewers need network access to the trusted external asset hosts when they
|
||||
open a walkthrough; R2 stores no separate runtime asset bundle.
|
||||
- Workflow verification checks the agent-produced files rather than rebuilding
|
||||
HTML from the agent's final response.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- **Render after the agent exits:** simpler workflow plumbing, but the agent
|
||||
cannot see renderer failures and repair its data.
|
||||
- **Allow generic edit and shell tools:** lets the agent run the renderer
|
||||
directly, but also permits source mutation and execution of untrusted PR
|
||||
content in a secret-bearing workflow.
|
||||
- **Make the skill OpenCode-specific:** avoids an adapter abstraction, but
|
||||
couples the durable walkthrough instructions to the first selected provider.
|
||||
@@ -0,0 +1,56 @@
|
||||
# ADR-2026-08-22-pr-walkthrough-description-link: Own a top-level PR walkthrough callout
|
||||
|
||||
**Status:** accepted
|
||||
**Date:** 2026-08-22
|
||||
**Area:** workflow, infra, security, GitHub
|
||||
|
||||
## Context
|
||||
|
||||
The hosted walkthrough must be easy to discover from the pull request after
|
||||
publication and after merge. Pull request bodies have no universal heading
|
||||
structure, and contributors or other automations may edit them throughout the
|
||||
pull request lifecycle. Updating an arbitrary section would either make the
|
||||
link hard to find or risk replacing content the walkthrough workflow does not
|
||||
own.
|
||||
|
||||
## Decision
|
||||
|
||||
After R2 publication and public validation succeed, a separate workflow job
|
||||
places a marker-delimited walkthrough callout at the beginning of the pull
|
||||
request body. The callout uses a GitHub alert and a prominent link to the
|
||||
stable SHA-keyed walkthrough URL. A label rerun may replace the bytes at that
|
||||
URL for the same head SHA.
|
||||
|
||||
The updater owns only the content between its start and end markers. A rerun
|
||||
replaces that block in place, so the current generated walkthrough is linked
|
||||
without duplicating content. If no owned block exists, the updater prepends
|
||||
one and preserves the existing body below it. Malformed, unbalanced, or
|
||||
duplicate marker blocks fail closed instead of rewriting the description.
|
||||
|
||||
The GitHub write runs only after the publish job succeeds. It receives the
|
||||
minimum `pull-requests: write` permission, checks out only the immutable base
|
||||
commit for its trusted helper, validates the event PR number and head SHA
|
||||
against the URL, and receives no model or R2 credentials.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The walkthrough is the first content reviewers see, independent of the
|
||||
contributor's chosen headings.
|
||||
- Label-triggered regeneration updates one stable callout rather than adding
|
||||
comments or duplicate body sections.
|
||||
- Contributor-authored and third-party body content remains outside the
|
||||
walkthrough workflow's ownership boundary.
|
||||
- A successful upload can still be left without a body link if the GitHub
|
||||
update fails; the workflow reports that failure and the hosted object remains
|
||||
available from the publication summary.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- **Insert under an existing heading:** preserves the opening paragraph, but
|
||||
depends on a heading that many pull requests do not have and is less visible.
|
||||
- **Append at the bottom:** avoids changing the opening, but existing generated
|
||||
sections and preview blocks can make the walkthrough difficult to find.
|
||||
- **Post a pull request comment:** leaves the description untouched, but the
|
||||
durable entry point is easily buried by review activity.
|
||||
- **Rewrite the full body from a template:** provides total layout control, but
|
||||
would overwrite content owned by contributors and other automations.
|
||||
@@ -0,0 +1,65 @@
|
||||
# ADR-2026-08-22-pr-walkthrough-r2-hosting: Host PR walkthrough HTML in Cloudflare R2
|
||||
|
||||
**Status:** accepted
|
||||
**Date:** 2026-08-22
|
||||
**Area:** infra, workflow, security
|
||||
|
||||
## Context
|
||||
|
||||
The PR walkthrough workflow generates a single deployable HTML file that should
|
||||
remain available after a pull request merges. The file is generated once when
|
||||
the PR is opened, with an explicit label-triggered rerun, so the hosting layer
|
||||
needs durable object storage rather than a short-lived CI artifact. The initial
|
||||
retention target is several months, with future support for generating a new
|
||||
object on every push if the model cost is acceptable.
|
||||
|
||||
## Decision
|
||||
|
||||
Publish walkthrough HTML files to a dedicated Cloudflare R2 bucket named
|
||||
`kandev-pr-walkthroughs`, exposed through the custom domain
|
||||
`walkthrough.kandev.ai`. Store objects under
|
||||
`pr/<pull-request-number>/<head-sha>.html` and publish only the HTML, not the
|
||||
JSON generation artifact.
|
||||
|
||||
The GitHub Actions publication job uses a bucket-scoped R2 S3-compatible
|
||||
Object Read & Write credential. The generation job receives no R2 credential;
|
||||
it hands its trusted-renderer output to the publication job through a GitHub
|
||||
Actions artifact. The publication job validates both the stored object and
|
||||
its public HTTPS URL, then writes the URL to the workflow summary without
|
||||
receiving GitHub write permission. A separate post-publication job may consume
|
||||
the validated URL under the ownership rules in the PR description-link ADR.
|
||||
|
||||
The bucket uses a lifecycle rule for the `pr/` prefix. The initial rule
|
||||
expires objects 180 days after upload so a walkthrough normally survives the
|
||||
period before its PR merges. Exact retention measured from merge time is
|
||||
deferred to a later merge-promotion design.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Walkthrough URLs survive PR merge and GitHub artifact retention.
|
||||
- R2 lifecycle cleanup is automatic and applies to all current and future
|
||||
SHA-keyed walkthrough objects.
|
||||
- Label reruns can replace the object bytes for the same PR head without
|
||||
changing its stable, commit-derived URL. The URL identifies the snapshot
|
||||
scope, but it is not a content-addressed immutable object.
|
||||
- Per-push generation can be enabled later by adding `synchronize`; each head
|
||||
SHA will receive its own object and lifecycle cleanup will remove old ones.
|
||||
- The public bucket must contain only sanitized walkthrough HTML and must be
|
||||
served from a dedicated subdomain rather than the main app origin. A page may
|
||||
load externally hosted scripts, styles, and fonts through exact-version URLs
|
||||
controlled by the trusted shell. These third-party hosts are an explicit
|
||||
runtime trust dependency. R2 still stores one HTML object per walkthrough
|
||||
and no separately deployed asset bundle.
|
||||
- GitHub Actions needs two R2 secrets in addition to non-secret bucket,
|
||||
endpoint, and base-URL variables.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- **Cloudflare Pages:** It can serve the HTML and is already used elsewhere in
|
||||
the repository, but its deployment history requires explicit cleanup rather
|
||||
than a simple per-object lifecycle rule.
|
||||
- **Sprites preview environment:** It can serve static files, but it couples
|
||||
retention and access to the preview environment instead of providing a
|
||||
dedicated artifact store.
|
||||
- **GitHub Actions artifacts:** They are useful as the generation-to-upload
|
||||
handoff, but they are not the durable public URL and retention mechanism.
|
||||
@@ -191,3 +191,6 @@ Read individual ADRs for full context. Create new ones via `/record decision` or
|
||||
| 2026-08-20-acp-client-non-underscore-extension-methods | [Route Non-Underscore Inbound Client Methods to the Extension Handler](2026-08-20-acp-client-non-underscore-extension-methods.md) | accepted | backend, protocol | 2026-08-20 |
|
||||
| 2026-08-22-persistent-fork-approval-labels | [Persist Fork Approval Labels Across Pushes](2026-08-22-persistent-fork-approval-labels.md) | accepted | infra, workflow, security | 2026-08-22 |
|
||||
| 2026-08-22-plugin-version-retention | [Keep Exactly One Superseded Plugin Version](2026-08-22-plugin-version-retention.md) | accepted | backend | 2026-08-22 |
|
||||
| 2026-08-22-pr-walkthrough-r2-hosting | [Host PR walkthrough HTML in Cloudflare R2](2026-08-22-pr-walkthrough-r2-hosting.md) | accepted | infra, workflow, security | 2026-08-22 |
|
||||
| 2026-08-22-agent-owned-pr-walkthrough-rendering | [Keep PR walkthrough rendering agent owned and provider neutral](2026-08-22-agent-owned-pr-walkthrough-rendering.md) | accepted | workflow, infra, security | 2026-08-22 |
|
||||
| 2026-08-22-pr-walkthrough-description-link | [Own a top-level PR walkthrough callout](2026-08-22-pr-walkthrough-description-link.md) | accepted | workflow, infra, security, GitHub | 2026-08-22 |
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
---
|
||||
spec: docs/specs/pr-walkthrough/spec.md
|
||||
created: 2026-08-22
|
||||
status: implementing
|
||||
---
|
||||
|
||||
# Implementation Plan: Pull Request Walkthrough Generation
|
||||
|
||||
## Overview
|
||||
|
||||
Adapt the upstream `pr-walkthrough` skill into Kandev's `.agents/skills`
|
||||
layout and retain its standard-library renderer and focused tests. Keep the
|
||||
skill provider-neutral and make the selected agent own the complete
|
||||
data-render-repair loop. A dedicated OpenCode walkthrough workflow supplies a
|
||||
narrow trusted rendering tool, verifies the agent-built outputs, and hands
|
||||
them to a separate R2 publication job. The publication job uploads only the
|
||||
HTML to the preconfigured `kandev-pr-walkthroughs` bucket. A final trusted job
|
||||
places the validated custom-domain URL in a marker-owned callout at the top of
|
||||
the pull request description.
|
||||
|
||||
The workflow must not execute PR-owned code or use PR-owned renderer assets.
|
||||
The agent reads the PR as untrusted source material and has no generic edit,
|
||||
shell, network, subagent, commit, push, or GitHub-write capability. Its managed
|
||||
rendering adapter may write only the fixed walkthrough outputs and run only the
|
||||
trusted renderer.
|
||||
|
||||
---
|
||||
|
||||
## Repository harness
|
||||
|
||||
### `pr-walkthrough` skill and renderer
|
||||
|
||||
Add the provider-neutral skill under
|
||||
`.agents/skills/pr-walkthrough/`. Adapt the upstream skill's default/base
|
||||
branch instructions to Kandev and require the selected agent to own the full
|
||||
data-render-repair loop. Keep the renderer and its fixed HTML shell in
|
||||
`references/`, with the example data and standard-library unit tests. The
|
||||
renderer remains independent of Kandev application code.
|
||||
|
||||
### Trusted rendering adapter
|
||||
|
||||
Add a small standard-library helper that accepts walkthrough JSON from a
|
||||
managed agent tool, binds trusted pull-request identity, removes model-owned
|
||||
URL overrides, writes the fixed JSON path, and invokes the trusted renderer.
|
||||
The OpenCode-specific tool is a thin adapter around this helper. Other agent
|
||||
runners follow the same skill and output contract through their native tools
|
||||
or an equivalent narrow adapter.
|
||||
|
||||
---
|
||||
|
||||
## CI workflow
|
||||
|
||||
### Dedicated OpenCode generation job
|
||||
|
||||
Add `.github/workflows/pr-walkthrough.yml` as a dedicated workflow using the
|
||||
`pull_request_target` event family. Gate it with the independent
|
||||
`PR_WALKTHROUGH_ENABLED` variable, not `OPENCODE_REVIEW_ENABLED`, and keep it
|
||||
decoupled from the code-review App token. The job runs for `opened`,
|
||||
`reopened`, and `ready_for_review`, plus a label-triggered rerun only when the
|
||||
label is `generate-pr-walkthrough`. The workflow does not subscribe to
|
||||
`synchronize` in this increment.
|
||||
|
||||
Use the shared base-controlled setup action for OpenCode. Select
|
||||
`opencode-go/muse-spark-1.2-contributor#high`. The pinned OpenCode 1.17.7 model
|
||||
catalog declares reasoning support and built-in `high` and `xhigh` variants for
|
||||
this model, so no custom provider override is needed.
|
||||
|
||||
The job will:
|
||||
|
||||
1. Check out `github.event.pull_request.head.sha` with full history and no
|
||||
persisted credentials.
|
||||
2. Compute the base-to-head context from the event SHAs.
|
||||
3. Materialize the skill, renderer, shell, and managed rendering adapter from
|
||||
the trusted base SHA, never from the PR head.
|
||||
4. Run OpenCode with an inline agent definition that permits reads and the
|
||||
narrow `render_pr_walkthrough` tool only.
|
||||
5. Require OpenCode to iterate through renderer errors until it produces
|
||||
`docs/pr-walkthrough/pr-<number>.json` and `.html` itself.
|
||||
6. Require non-empty JSON and HTML files and preserve OpenCode logs and the
|
||||
generated files as an uploaded CI artifact, including on failure.
|
||||
|
||||
The job keeps R2 credentials out of generation. The artifact is the only
|
||||
handoff to the publication job. Add `docs/pr-walkthrough/` to `.gitignore`.
|
||||
|
||||
### Cloudflare R2 publication job
|
||||
|
||||
Add a dependent publication job that runs only after successful generation.
|
||||
The job must not check out or execute PR-owned files. It downloads the HTML
|
||||
artifact, uploads only the HTML through the R2 S3-compatible API, and validates
|
||||
the public object.
|
||||
|
||||
The publication contract is:
|
||||
|
||||
- Bucket variable: `CLOUDFLARE_R2_BUCKET`, expected value
|
||||
`kandev-pr-walkthroughs`.
|
||||
- Endpoint variable: `CLOUDFLARE_R2_ENDPOINT`, normally
|
||||
`https://<account-id>.r2.cloudflarestorage.com` for the default bucket
|
||||
jurisdiction.
|
||||
- Base URL variable: `CLOUDFLARE_R2_BASE_URL`, expected value
|
||||
`https://walkthrough.kandev.ai`.
|
||||
- Access-key secret: `CLOUDFLARE_R2_ACCESS_KEY_ID`.
|
||||
- Secret-key secret: `CLOUDFLARE_R2_SECRET_ACCESS_KEY`.
|
||||
- Existing account variable: `CLOUDFLARE_ACCOUNT_ID` remains available for
|
||||
endpoint construction and is not a secret.
|
||||
- Object key: `pr/<pull-request-number>/<head-sha>.html`.
|
||||
- Object metadata: `Content-Type: text/html; charset=utf-8` and a short cache
|
||||
lifetime that does not outlive lifecycle deletion or same-head reruns.
|
||||
|
||||
Use an S3-compatible upload command such as `aws s3 cp` with the endpoint,
|
||||
`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_DEFAULT_REGION=auto`
|
||||
environment variables. Do not pass the bucket-scoped R2 Object Read & Write
|
||||
credentials to `cloudflare/wrangler-action`; those credentials are for the R2
|
||||
S3-compatible API. The existing Pages `CLOUDFLARE_API_TOKEN` remains separate.
|
||||
|
||||
After upload, run an object metadata check and a bounded-retry public HTTPS GET
|
||||
against the reported URL. Export the validated URL for the dependent link job
|
||||
and write it to the workflow job summary. Do not upload the JSON artifact to
|
||||
the public bucket. The publication job itself has no GitHub write permission.
|
||||
|
||||
### Pull request description link job
|
||||
|
||||
Add a final job after successful publication. Give only this job
|
||||
`pull-requests: write`; do not give it model or R2 credentials. Check out the
|
||||
exact base SHA without persisted credentials and run a trusted
|
||||
standard-library helper that validates the GitHub PR number/head SHA and exact
|
||||
hosted URL. The helper allows the live PR head to advance while publication
|
||||
finishes because the SHA-keyed walkthrough remains the intended initial
|
||||
snapshot.
|
||||
|
||||
The helper prepends a GitHub alert between `kandev-pr-walkthrough-start` and
|
||||
`kandev-pr-walkthrough-end` markers. On a rerun it replaces only that leading
|
||||
block. It fails on unbalanced, duplicate, or non-leading markers instead of
|
||||
rewriting content it does not own.
|
||||
|
||||
The Cloudflare setup is an operator prerequisite rather than repository
|
||||
automation: the bucket is `kandev-pr-walkthroughs`, the custom domain is
|
||||
`walkthrough.kandev.ai`, and the bucket has an Object Lifecycle rule for the
|
||||
`pr/` prefix. The initial plan assumes a 180-day expiration from upload so
|
||||
walkthroughs normally survive the time before merge. Exact post-merge
|
||||
retention would require a later merge-promotion job.
|
||||
|
||||
### Workflow contract coverage
|
||||
|
||||
Add `.github/scripts/pr-walkthrough-workflow-contract_test.py` and wire it into
|
||||
`.github/workflows/lint-action-pinning.yml`. The contract test will assert the
|
||||
event gates, exact base checkout, immutable head-object fetch, constrained
|
||||
PR-head file reads, narrow OpenCode permissions, trusted metadata binding,
|
||||
generation output paths,
|
||||
artifact handoff, R2 publication command, required secret/variable names,
|
||||
public URL validation, isolated PR-body write permissions, trusted helper use,
|
||||
and the independently gated dedicated workflow boundary.
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
- **What:** Renderer accepts valid walkthrough data, escapes source, converts
|
||||
patches, validates edges and risk data, fills the HTML shell, and rejects
|
||||
malformed fields.
|
||||
**File:** `.agents/skills/pr-walkthrough/references/test_build.py`
|
||||
**How:** Run the upstream-adapted Python standard-library unit suite.
|
||||
- **What:** The managed renderer binds trusted PR metadata, removes model URL
|
||||
overrides, produces both fixed outputs, and leaves no partial files after a
|
||||
renderer failure.
|
||||
**File:** `scripts/pr-walkthrough-render.test.py`
|
||||
**How:** Run the Python standard-library unit suite.
|
||||
- **What:** The workflow keeps PR content read-only and uses trusted base
|
||||
assets while producing ignored JSON/HTML artifacts and publishing only the
|
||||
rendered HTML to R2.
|
||||
**File:** `.github/scripts/pr-walkthrough-workflow-contract_test.py`
|
||||
**How:** Run the static workflow contract test.
|
||||
- **What:** The publication job uses the intended bucket, endpoint, object key,
|
||||
content type, lifecycle-compatible cache policy, and public URL check without
|
||||
exposing R2 credentials to generation.
|
||||
**File:** `.github/scripts/pr-walkthrough-workflow-contract_test.py`
|
||||
**How:** Run the static workflow contract test and a configured CI dry run.
|
||||
- **What:** The PR body helper prepends one prominent callout, updates it
|
||||
idempotently, validates event identity, preserves contributor content, and
|
||||
rejects malformed ownership markers.
|
||||
**File:** `scripts/pr-walkthrough-pr-body.test.py`
|
||||
**How:** Run the Python standard-library unit suite.
|
||||
- **What:** New skill and reference files satisfy Kandev harness limits.
|
||||
**File:** `.agents/skills/pr-walkthrough/SKILL.md` and `references/*`
|
||||
**How:** Run the harness linter on the new skill directory.
|
||||
- **What:** All workflow action refs remain immutable and the workflow edit has
|
||||
no whitespace errors.
|
||||
**File:** `.github/workflows/pr-walkthrough.yml` and
|
||||
`.github/workflows/opencode-code-review.yml`
|
||||
**How:** Run the action-pinning tests/linter and `git diff --check`.
|
||||
|
||||
---
|
||||
|
||||
## E2E Tests
|
||||
|
||||
There is no Kandev UI flow in this increment. The generated HTML is validated
|
||||
structurally by the renderer and workflow checks, and the publication job
|
||||
performs a live public GET against the R2 custom domain. Full browser
|
||||
interaction testing remains out of scope.
|
||||
|
||||
---
|
||||
|
||||
## Verification Results
|
||||
|
||||
Completed 2026-08-22:
|
||||
|
||||
- `python3 scripts/pr-walkthrough-render.test.py`: 2 tests passed.
|
||||
- `cd .agents/skills/pr-walkthrough/references && python3 -m unittest test_build`: 56 tests passed.
|
||||
- `python3 .github/scripts/pr-walkthrough-workflow-contract_test.py`: 18 tests passed.
|
||||
- `python3 scripts/pr-walkthrough-pr-body.test.py`: 8 tests passed.
|
||||
- `python3 scripts/lint-harness-files.test.py`: 19 tests passed.
|
||||
- `python3 .github/scripts/lint-harness-files.py .agents/skills/pr-walkthrough`: passed.
|
||||
- `python3 .github/scripts/lint-action-pinning_test.py`: 9 tests passed.
|
||||
- `python3 .github/scripts/lint-action-pinning.py`: 19 workflows passed.
|
||||
- `actionlint` passed for the dedicated walkthrough, OpenCode review, and
|
||||
action-pinning workflows.
|
||||
- Pinned OpenCode 1.17.7 resolved the default-deny walkthrough agent with only
|
||||
read, glob, grep, and the trusted renderer enabled. Its refreshed model
|
||||
catalog reports Muse Spark 1.2 Contributor reasoning support and native
|
||||
`high` and `xhigh` variants.
|
||||
- The OpenCode custom tool bundled successfully with Bun after marking the
|
||||
runtime-provided `@opencode-ai/plugin` import external.
|
||||
- `git diff --check`: passed.
|
||||
- The example JSON rendered a 1,161-line HTML file with no unreplaced tokens.
|
||||
- `zizmor` was run through `mise x zizmor@1.25.2`; the repository-wide audit
|
||||
remains non-zero because of existing findings in unrelated workflows, and
|
||||
both targeted base-controlled workflows retain the expected
|
||||
`pull_request_target` dangerous-trigger finding.
|
||||
- Chromium is not installed, so the optional headless DOM check was not run.
|
||||
- Operator provisioning is complete. A live R2 upload and public GET remain
|
||||
pending the first eligible PR workflow run.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Waves And Parallel Candidates
|
||||
|
||||
Wave 1:
|
||||
|
||||
- [x] [task-01-walkthrough-skill-renderer](task-01-walkthrough-skill-renderer.md)
|
||||
- [ ] [task-02-agent-rendering-contract](task-02-agent-rendering-contract.md)
|
||||
|
||||
Wave 2:
|
||||
|
||||
- [x] [task-03-workflow-artifact-generation](task-03-workflow-artifact-generation.md)
|
||||
|
||||
Wave 3:
|
||||
|
||||
- [x] [task-04-r2-html-publication](task-04-r2-html-publication.md)
|
||||
|
||||
Wave 4:
|
||||
|
||||
- [x] [task-05-dedicated-workflow-pr-link](task-05-dedicated-workflow-pr-link.md)
|
||||
|
||||
Execute sequentially in the primary conversation unless the user explicitly
|
||||
authorizes parallel implementation.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Whether to enable `synchronize` generation later remains a product/cost
|
||||
decision. The SHA-keyed R2 object contract already supports it.
|
||||
- Whether retention must be exactly 90 days after merge remains deferred. The
|
||||
initial lifecycle is 180 days after upload.
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
id: "01-walkthrough-skill-renderer"
|
||||
title: "Walkthrough skill renderer"
|
||||
status: done
|
||||
wave: 1
|
||||
depends_on: []
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/pr-walkthrough/spec.md"
|
||||
---
|
||||
|
||||
# Task 01: Walkthrough skill renderer
|
||||
|
||||
Adapt the final upstream `pr-walkthrough` skill into Kandev's `.agents/skills`
|
||||
layout. Keep the JSON-driven renderer, fixed HTML shell, example data, and
|
||||
standard-library tests. Add the CI output-mode instructions without changing
|
||||
the skill's boundary from explanation to code review.
|
||||
|
||||
- **Acceptance:** The skill documents both normal generation and the exact CI
|
||||
JSON output contract, with Kandev-compatible paths and no hosting steps.
|
||||
- **Acceptance:** The renderer produces a non-empty HTML file from the example
|
||||
JSON and rejects malformed required fields, invalid edges, invalid risk data,
|
||||
and unreplaced template tokens.
|
||||
- **Acceptance:** Code and agent-controlled Markdown are escaped or sanitized
|
||||
by the generated page before insertion into HTML.
|
||||
- **Verification:**
|
||||
|
||||
```text
|
||||
cd .agents/skills/pr-walkthrough/references && python3 -m unittest test_build
|
||||
python3 .github/scripts/lint-harness-files.py .agents/skills/pr-walkthrough
|
||||
git diff --check
|
||||
```
|
||||
|
||||
- **Files likely touched:**
|
||||
`.agents/skills/pr-walkthrough/SKILL.md`,
|
||||
`.agents/skills/pr-walkthrough/references/build.py`,
|
||||
`.agents/skills/pr-walkthrough/references/example.json`,
|
||||
`.agents/skills/pr-walkthrough/references/shell.html`,
|
||||
`.agents/skills/pr-walkthrough/references/test_build.py`.
|
||||
- **Dependencies:** None.
|
||||
- **Parallelism:** sequential, because the workflow consumes this skill.
|
||||
- **Inputs:** The spec's generation contract, permissions, failure modes, and
|
||||
scenarios; the final upstream PR walkthrough skill and renderer.
|
||||
- **Output contract:** Report changed files, unit/lint results, generated sample
|
||||
artifact path, and any renderer or dependency risks. Mark the task done after
|
||||
implementation and verification.
|
||||
|
||||
## Results
|
||||
|
||||
Implemented the adapted upstream skill, fixed renderer shell, example JSON,
|
||||
and standard-library tests under `.agents/skills/pr-walkthrough/`. Added the
|
||||
exact CI marker-block output mode, kept hosting outside the skill, and aligned
|
||||
the shell with the landing site's Figtree, Geist Mono, indigo, dark-surface,
|
||||
compact-radius, and mobile-navigation language.
|
||||
|
||||
Verification: 56 renderer tests passed; the example rendered a non-empty
|
||||
1,161-line HTML file; harness lint passed for the new skill directory.
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
id: "02-agent-rendering-contract"
|
||||
title: "Provider-neutral agent rendering contract"
|
||||
status: in_progress
|
||||
wave: 1
|
||||
depends_on: ["01-walkthrough-skill-renderer"]
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/pr-walkthrough/spec.md"
|
||||
---
|
||||
|
||||
# Task 02: Provider-neutral agent rendering contract
|
||||
|
||||
Make the walkthrough agent responsible for the complete JSON, rendering, and
|
||||
repair loop without coupling the skill to OpenCode. Supply a narrow managed
|
||||
adapter when a CI runner cannot safely receive generic write or shell access.
|
||||
|
||||
- **Acceptance:** The provider-neutral skill requires the selected agent to
|
||||
render its walkthrough, inspect renderer failures, correct the data, and
|
||||
finish only after both JSON and HTML outputs exist.
|
||||
- **Acceptance:** The managed rendering helper binds PR number, title, URL,
|
||||
repository, base branch, and head branch from trusted host metadata. It
|
||||
removes model-supplied PR/file URL overrides before invoking the trusted
|
||||
renderer.
|
||||
- **Acceptance:** The OpenCode adapter accepts only complete walkthrough JSON,
|
||||
invokes only the fixed trusted helper, and returns renderer errors so the
|
||||
agent can retry. OpenCode retains denied generic edit and Bash permissions.
|
||||
- **Verification:**
|
||||
|
||||
```text
|
||||
python3 scripts/pr-walkthrough-render.test.py
|
||||
bun build .github/scripts/opencode-pr-walkthrough-tool.ts --external @opencode-ai/plugin --target=bun --outfile /tmp/kandev-opencode-pr-walkthrough-tool.js
|
||||
```
|
||||
|
||||
- **Files likely touched:** `.agents/skills/pr-walkthrough/SKILL.md`,
|
||||
`scripts/pr-walkthrough-render`, `scripts/pr-walkthrough-render.test.py`,
|
||||
`.github/scripts/opencode-pr-walkthrough-tool.ts`.
|
||||
- **Dependencies:** Task 01.
|
||||
- **Parallelism:** sequential because the workflow task consumes this adapter.
|
||||
|
||||
## Results
|
||||
|
||||
Added the trusted metadata-binding renderer helper and the OpenCode custom tool
|
||||
adapter. The helper writes only the fixed PR JSON/HTML paths, strips model URL
|
||||
overrides, and leaves no published outputs after renderer validation fails.
|
||||
The provider-neutral skill wording remains pending because `.agents/**` is
|
||||
read-only in the implementing session.
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
id: "03-workflow-artifact-generation"
|
||||
title: "Workflow artifact generation"
|
||||
status: done
|
||||
wave: 2
|
||||
depends_on: ["01-walkthrough-skill-renderer", "02-agent-rendering-contract"]
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/pr-walkthrough/spec.md"
|
||||
---
|
||||
|
||||
# Task 03: Workflow artifact generation
|
||||
|
||||
Add a dedicated PR walkthrough workflow with a generation job. The job checks
|
||||
out the immutable base commit, fetches the PR head only as a Git object, uses
|
||||
base-commit copies of the skill and helpers, gives OpenCode narrow trusted
|
||||
rendering and PR-file tools, and uploads the agent-generated files and logs for
|
||||
the separate R2 publication job.
|
||||
|
||||
- **Acceptance:** A non-draft authorized pull request event invokes OpenCode,
|
||||
creates distinct `docs/pr-walkthrough/pr-<number>.json` and `.html` files,
|
||||
and uploads them as an artifact. The generation job runs on `opened`,
|
||||
`reopened`, `ready_for_review`, and the `generate-pr-walkthrough` label, but
|
||||
not on `synchronize`.
|
||||
- **Acceptance:** The job cannot execute PR-owned scripts through OpenCode or
|
||||
modify source. OpenCode may invoke only the base-controlled rendering
|
||||
adapter, which writes the fixed walkthrough outputs. The job does not request
|
||||
`id-token`, hosting credentials, R2 credentials, or GitHub write permissions.
|
||||
- **Acceptance:** A contract test covers the trigger, immutable base checkout,
|
||||
head-object fetch, constrained PR-file reads, trusted base assets, read-only
|
||||
agent permissions, output paths, artifact upload, and generation-only
|
||||
boundary. The artifact handoff is explicit for the dependent R2 publication
|
||||
job.
|
||||
- **Acceptance:** The workflow uses `PR_WALKTHROUGH_ENABLED`, the
|
||||
`opencode-go/muse-spark-1.2-contributor#high` model reference and the model's
|
||||
native high-reasoning variant. The normal review workflow remains
|
||||
independently gated by `OPENCODE_REVIEW_ENABLED`.
|
||||
- **Verification:**
|
||||
|
||||
```text
|
||||
python3 .github/scripts/pr-walkthrough-workflow-contract_test.py
|
||||
python3 .github/scripts/lint-action-pinning_test.py
|
||||
python3 .github/scripts/lint-action-pinning.py
|
||||
zizmor .github/workflows/pr-walkthrough.yml
|
||||
git diff --check
|
||||
```
|
||||
|
||||
- **Files likely touched:** `.github/workflows/pr-walkthrough.yml`,
|
||||
`.github/workflows/lint-action-pinning.yml`,
|
||||
`.github/scripts/pr-walkthrough-workflow-contract_test.py`, `.gitignore`.
|
||||
- **Dependencies:** Tasks 01 and 02.
|
||||
- **Parallelism:** sequential, because it edits the shared OpenCode workflow
|
||||
and consumes both earlier artifacts.
|
||||
- **Inputs:** The spec's permissions, failure modes, persistence guarantees,
|
||||
and scenarios; tasks 01 and 02; current OpenCode workflow gates and action
|
||||
pinning conventions.
|
||||
- **Output contract:** Report changed files, contract/security check results,
|
||||
artifact paths from any local dry run, and any unavailable external CI
|
||||
dependency. Mark the task done after implementation and verification. This
|
||||
task does not publish to R2; task 04 owns that boundary.
|
||||
|
||||
## Results
|
||||
|
||||
Added the isolated same-repository OpenCode generation job to the dedicated
|
||||
`pull_request_target` walkthrough workflow. It materializes the skill,
|
||||
renderer, shell,
|
||||
and narrow rendering adapter from the base SHA. OpenCode builds and repairs the
|
||||
JSON/HTML through that adapter while generic edit and Bash remain denied. The
|
||||
job preserves logs and outputs as a uniquely keyed artifact, including on
|
||||
failure.
|
||||
|
||||
The three OpenCode jobs now share a base-SHA-materialized setup action with one
|
||||
version and checksum. The walkthrough generation worktree stays on the trusted
|
||||
base commit. A bounded helper reads regular UTF-8 files from the immutable PR
|
||||
head Git object without exposing PR-controlled filesystem entries. Walkthrough
|
||||
artifacts use a visible staging directory and fail when empty. Workflow-level
|
||||
per-PR concurrency serializes generation, publication, and linking, and the
|
||||
walkthrough label no longer runs the normal code-review job.
|
||||
|
||||
The walkthrough has its own enable variable and uses the Muse Spark contributor
|
||||
model with its native high-reasoning variant. The pinned OpenCode 1.17.7 CLI
|
||||
catalog reports reasoning support plus built-in `high` and `xhigh` variants,
|
||||
and the CLI resolved the default-deny agent permissions.
|
||||
|
||||
Verification: workflow contract tests passed; action pinning tests and linter
|
||||
passed; `actionlint` passed; `git diff --check` passed. The targeted `zizmor`
|
||||
audit still reports the workflow's existing `pull_request_target` warning.
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
id: "04-r2-html-publication"
|
||||
title: "Publish walkthrough HTML to Cloudflare R2"
|
||||
status: done
|
||||
wave: 3
|
||||
depends_on: ["03-workflow-artifact-generation"]
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/pr-walkthrough/spec.md"
|
||||
---
|
||||
|
||||
# Task 04: Publish walkthrough HTML to Cloudflare R2
|
||||
|
||||
Add the dependent publication job that downloads the successful generation
|
||||
artifact and uploads only the rendered HTML to the already-configured
|
||||
Cloudflare R2 bucket. Keep the R2 credentials isolated to this job and use the
|
||||
custom domain as the externally visible URL.
|
||||
|
||||
- **Acceptance:** The publication job runs only after successful walkthrough
|
||||
generation and only for an authorized, non-draft same-repository pull
|
||||
request. A label-triggered rerun publishes the current head without enabling
|
||||
`synchronize` generation.
|
||||
- **Acceptance:** The job downloads the generation artifact but does not check
|
||||
out or execute PR-owned files and does not expose R2 credentials to the
|
||||
generation job.
|
||||
- **Acceptance:** The job uploads only the HTML file through the R2
|
||||
S3-compatible API using a bucket-scoped Object Read & Write token. It does
|
||||
not use the existing Pages `CLOUDFLARE_API_TOKEN`.
|
||||
- **Acceptance:** The object key is
|
||||
`pr/<pull-request-number>/<head-sha>.html`, with HTML content type and a
|
||||
short cache policy suitable for label-triggered reruns.
|
||||
- **Acceptance:** The job verifies the object with an R2 metadata request and
|
||||
verifies the public URL
|
||||
`https://walkthrough.kandev.ai/pr/<pull-request-number>/<head-sha>.html`
|
||||
with an HTTPS GET before reporting publication success.
|
||||
- **Acceptance:** The job writes the public URL to the workflow job summary
|
||||
and exports it to the dependent trusted link job, but receives no GitHub
|
||||
write permission and does not publish the JSON artifact.
|
||||
- **Acceptance:** The workflow contract test covers the dependency, event
|
||||
gate, artifact download, S3 endpoint, bucket/key construction, secret and
|
||||
variable names, content metadata, public URL check, and the absence of
|
||||
generation-job credentials.
|
||||
|
||||
- **Verification:**
|
||||
|
||||
```text
|
||||
python3 .github/scripts/pr-walkthrough-workflow-contract_test.py
|
||||
python3 .github/scripts/lint-action-pinning_test.py
|
||||
python3 .github/scripts/lint-action-pinning.py
|
||||
zizmor .github/workflows/pr-walkthrough.yml
|
||||
git diff --check
|
||||
```
|
||||
|
||||
- **Files likely touched:** `.github/workflows/pr-walkthrough.yml`,
|
||||
`.github/scripts/pr-walkthrough-workflow-contract_test.py`, and possibly
|
||||
`.github/workflows/lint-action-pinning.yml` if the contract test wiring is
|
||||
added in this task.
|
||||
- **Dependencies:** Task 03 and the operator-provisioned R2 bucket,
|
||||
`walkthrough.kandev.ai` custom domain, `pr/` lifecycle rule, GitHub Actions
|
||||
secrets, and GitHub Actions variables.
|
||||
- **Parallelism:** sequential, because this job consumes task 03's artifact and
|
||||
edits the shared OpenCode workflow.
|
||||
- **Inputs:** The spec's R2 publication contract and retention semantics; the
|
||||
existing Cloudflare Pages workflow's pinned-action conventions; the
|
||||
bucket-scoped R2 S3 credentials configured in GitHub.
|
||||
- **Output contract:** Report changed workflow and contract-test files,
|
||||
security/static-check results, the generated object URL from any configured
|
||||
CI dry run, and any unavailable external Cloudflare dependency. Keep the
|
||||
task pending until implementation begins.
|
||||
|
||||
## Results
|
||||
|
||||
Added the dependent artifact-only publication job. It uploads only the HTML
|
||||
file to the configured R2 S3 endpoint using the bucket-scoped credentials,
|
||||
checks object metadata, performs a bounded-retry public HTTPS GET and byte
|
||||
comparison, and writes and exports the custom URL without receiving GitHub
|
||||
write permission.
|
||||
|
||||
Verification: workflow contract tests passed; the publication job has no
|
||||
checkout step, and R2 secrets are scoped only to the upload step. Operator
|
||||
provisioning is complete; a live upload remains pending the first eligible PR
|
||||
workflow run.
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: "05-dedicated-workflow-pr-link"
|
||||
title: "Separate walkthrough workflow and link it from the PR body"
|
||||
status: done
|
||||
wave: 4
|
||||
depends_on: ["04-r2-html-publication"]
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/pr-walkthrough/spec.md"
|
||||
---
|
||||
|
||||
# Task 05: Separate walkthrough workflow and link it from the PR body
|
||||
|
||||
Move walkthrough generation and publication out of the OpenCode review
|
||||
workflow. Give walkthroughs their own enable variable and model configuration,
|
||||
then add a post-publication job that makes the hosted page prominent without
|
||||
taking ownership of contributor-authored PR content.
|
||||
|
||||
- **Acceptance:** `.github/workflows/pr-walkthrough.yml` contains generation,
|
||||
R2 publication, and PR linking. `.github/workflows/opencode-code-review.yml`
|
||||
contains no walkthrough jobs.
|
||||
- **Acceptance:** `PR_WALKTHROUGH_ENABLED` controls the walkthrough workflow
|
||||
independently of `OPENCODE_REVIEW_ENABLED`.
|
||||
- **Acceptance:** The walkthrough runner selects
|
||||
`opencode-go/muse-spark-1.2-contributor#high`, which selects the model's
|
||||
native high-reasoning variant in pinned OpenCode 1.17.7.
|
||||
- **Acceptance:** Only the final job receives `pull-requests: write`. It runs
|
||||
after public R2 validation, uses the trusted base-commit helper, and receives
|
||||
no OpenCode or R2 credential.
|
||||
- **Acceptance:** The helper places one marker-owned GitHub alert at the top of
|
||||
the PR description, replaces it idempotently on rerun, preserves all other
|
||||
content, and fails closed on invalid markers or mismatched PR identity.
|
||||
- **Verification:**
|
||||
|
||||
```text
|
||||
python3 .github/scripts/pr-walkthrough-workflow-contract_test.py
|
||||
python3 scripts/pr-walkthrough-pr-body.test.py
|
||||
actionlint .github/workflows/pr-walkthrough.yml
|
||||
zizmor .github/workflows/pr-walkthrough.yml
|
||||
git diff --check
|
||||
```
|
||||
|
||||
- **Files likely touched:** `.github/workflows/pr-walkthrough.yml`,
|
||||
`.github/workflows/opencode-code-review.yml`,
|
||||
`.github/scripts/pr-walkthrough-workflow-contract_test.py`,
|
||||
`scripts/pr-walkthrough-pr-body`, and
|
||||
`scripts/pr-walkthrough-pr-body.test.py`.
|
||||
|
||||
## Results
|
||||
|
||||
Created the dedicated workflow and independent gate. Selected Muse Spark's
|
||||
native high-reasoning variant and validated it in the pinned 1.17.7 model
|
||||
catalog. Added the marker-owned PR-body helper and a
|
||||
minimum-permission link job after R2 public validation. Contract and helper
|
||||
tests pass.
|
||||
@@ -321,6 +321,7 @@ System pages (Radarr/Sonarr-style) for status, disk usage, database maintenance,
|
||||
| [executor-profile-env-precedence](executor-profile-env-precedence/spec.md) | building |
|
||||
| [automations-yaml-export](automations-yaml-export/spec.md) | building |
|
||||
| [task-launch-failure-recovery](task-launch-failure-recovery/spec.md) | draft |
|
||||
| [pr-walkthrough](pr-walkthrough/spec.md) | building |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
---
|
||||
status: building
|
||||
created: 2026-08-22
|
||||
owner: kandev
|
||||
---
|
||||
|
||||
# Pull Request Walkthrough Generation
|
||||
|
||||
## Why
|
||||
|
||||
Reviewers can inspect a diff in GitHub, but a large pull request still takes
|
||||
time to understand. Kandev should generate a visual explanation that gives a
|
||||
reviewer the change context, architecture, important code paths, risk, and
|
||||
review focus before they read the full diff.
|
||||
|
||||
This increment generates the walkthrough HTML in CI and publishes the HTML to
|
||||
the dedicated Cloudflare R2 walkthrough bucket. The hosted file remains
|
||||
available after the pull request merges and expires through the bucket
|
||||
lifecycle policy.
|
||||
|
||||
**Decisions:**
|
||||
[ADR-2026-08-22-pr-walkthrough-r2-hosting](../../decisions/2026-08-22-pr-walkthrough-r2-hosting.md),
|
||||
[ADR-2026-08-22-agent-owned-pr-walkthrough-rendering](../../decisions/2026-08-22-agent-owned-pr-walkthrough-rendering.md),
|
||||
[ADR-2026-08-22-pr-walkthrough-description-link](../../decisions/2026-08-22-pr-walkthrough-description-link.md)
|
||||
|
||||
## What
|
||||
|
||||
- A project skill named `pr-walkthrough` is available to compatible agents.
|
||||
- The skill produces one JSON data file and one HTML file for a pull request:
|
||||
`docs/pr-walkthrough/pr-<number>.json` and
|
||||
`docs/pr-walkthrough/pr-<number>.html`.
|
||||
- The JSON describes the pull request, why it exists, an optional architecture
|
||||
diagram, key code changes, data or storage, risk, trade-offs, and review
|
||||
focus.
|
||||
- The renderer builds the HTML from the JSON and fixed renderer assets. It
|
||||
escapes code and prose, validates required fields, validates node edges, and
|
||||
rejects unreplaced template tokens.
|
||||
- The generated page contains a vertical reviewer story, a dark and light
|
||||
theme, architecture and data diagrams when supplied, highlighted code, diff
|
||||
tinting, GitHub file links, an interactive code canvas, and a linear fallback
|
||||
list.
|
||||
- The configured workflow agent generates and renders the walkthrough for a
|
||||
non-draft same-repository pull request when it is opened, reopened, or marked
|
||||
ready for review. OpenCode is the initial runner, but the skill and artifact
|
||||
contract do not depend on it. A maintainer can explicitly retrigger
|
||||
generation by adding the `generate-pr-walkthrough` label.
|
||||
- Walkthrough automation lives in `.github/workflows/pr-walkthrough.yml` and
|
||||
is enabled independently with the `PR_WALKTHROUGH_ENABLED` repository
|
||||
variable. It does not share the `OPENCODE_REVIEW_ENABLED` code-review gate.
|
||||
- The initial runner uses
|
||||
`opencode-go/muse-spark-1.2-contributor#high`. OpenCode 1.17.7 reports native
|
||||
reasoning support for the model and resolves `#high` to its built-in high
|
||||
reasoning variant.
|
||||
- The workflow preserves the generated JSON and HTML as CI artifacts and
|
||||
uploads only the HTML to the `kandev-pr-walkthroughs` R2 bucket.
|
||||
- Each published object uses the key
|
||||
`pr/<pull-request-number>/<head-sha>.html` and is served at
|
||||
`https://walkthrough.kandev.ai/pr/<pull-request-number>/<head-sha>.html`.
|
||||
- The initial workflow does not regenerate on `synchronize`. Future
|
||||
per-push generation can add that event without changing the object contract.
|
||||
- After public validation succeeds, a separate minimum-permission job prepends
|
||||
a prominent marker-owned walkthrough callout to the pull request
|
||||
description. A rerun replaces only that callout and preserves the rest of
|
||||
the description.
|
||||
|
||||
## Generation contract
|
||||
|
||||
The walkthrough agent compares the exact pull request head SHA with the exact
|
||||
base SHA. It creates the JSON, invokes the trusted renderer, and corrects its
|
||||
data until both outputs pass renderer validation. The generated `pr` object
|
||||
includes the pull request number, title, URL, repository slug, base branch,
|
||||
head branch, and diff statistics when they are available. The managed runner
|
||||
binds identity and links to trusted event metadata before rendering.
|
||||
|
||||
Each code change includes a real repository-relative file path, a concise
|
||||
explanation, and at least one real code or rendered-Markdown block. Code
|
||||
excerpts come from the pull request head or its diff. The agent does not invent
|
||||
source code or present a review verdict.
|
||||
|
||||
The walkthrough is an explanation, not a code review. It does not approve,
|
||||
request changes, post findings, or claim that the pull request is safe to
|
||||
merge.
|
||||
|
||||
## Permissions
|
||||
|
||||
- The workflow may read pull request metadata and repository contents.
|
||||
- The selected agent reads the trusted base checkout and attached diff. A
|
||||
narrow base-controlled tool may read bounded regular UTF-8 files directly
|
||||
from the immutable pull request head Git object. The agent may not run
|
||||
arbitrary shell commands, read outside the trusted worktree, modify source files,
|
||||
invoke subagents, fetch external URLs, commit, push, or publish GitHub
|
||||
changes. A managed runner may expose a narrow trusted-renderer tool that
|
||||
writes only the fixed walkthrough JSON and HTML paths.
|
||||
- The workflow checks out only the trusted base commit in the secret-bearing
|
||||
worktree. It fetches the event head SHA as a Git object without checking it
|
||||
out, and uses base-commit copies of the walkthrough skill, renderer, managed
|
||||
rendering adapter, and constrained file reader. Pull request changes cannot
|
||||
replace those instructions or executable generation components.
|
||||
- The agent invokes the fixed renderer before it finishes. The workflow only
|
||||
verifies and packages the ignored walkthrough output directory.
|
||||
- The R2 publishing job receives only the bucket-scoped S3-compatible R2
|
||||
credentials required to upload the rendered HTML. The generation job does
|
||||
not receive R2 credentials.
|
||||
- The PR-link job receives `pull-requests: write`, but no model or R2
|
||||
credential. It checks out the immutable base commit and uses a trusted helper
|
||||
that validates the PR number, event head SHA, and exact walkthrough URL
|
||||
before constructing the GitHub API update.
|
||||
- The public bucket contains only generated walkthrough HTML. It does not
|
||||
publish the JSON source artifact.
|
||||
|
||||
## Failure modes
|
||||
|
||||
- If the selected agent command fails, exits non-zero, or does not produce both
|
||||
required walkthrough files, generation fails and the workflow records the
|
||||
diagnostic output in its CI artifacts.
|
||||
- If the JSON is missing required fields, contains invalid edges or risk data,
|
||||
includes a reserved renderer token, or otherwise violates the renderer
|
||||
contract, the renderer fails and no HTML is treated as generated.
|
||||
- If the renderer cannot read its fixed shell or cannot write the output file,
|
||||
the workflow fails rather than exposing a partial page.
|
||||
- If optional browser validation is unavailable on the runner, the workflow
|
||||
still validates the generated file structurally and reports live browser
|
||||
rendering as unverified. HTML generation itself remains a required check.
|
||||
- If the R2 upload, object metadata validation, or bounded-retry public URL
|
||||
check fails, the workflow fails and does not report the walkthrough as
|
||||
published.
|
||||
- If the PR body contains malformed, duplicate, or non-leading walkthrough
|
||||
markers, the link job fails closed and does not rewrite contributor content.
|
||||
|
||||
## Persistence guarantees
|
||||
|
||||
Generated JSON and HTML are ignored working-tree artifacts. They do not merge
|
||||
into `main` or become Kandev application state. The HTML is also published to
|
||||
R2 independently of GitHub artifact retention, so it remains available after
|
||||
the pull request merges. The initial lifecycle deletes objects under `pr/`
|
||||
after 180 days from upload; this is intentionally measured from generation,
|
||||
not from merge time.
|
||||
|
||||
## Scenarios
|
||||
|
||||
- **GIVEN** a non-draft same-repository pull request is opened, reopened, or
|
||||
marked ready for review, **WHEN** the walkthrough job runs, **THEN** it
|
||||
creates non-empty JSON and HTML artifacts and publishes the HTML under the
|
||||
current head SHA in R2.
|
||||
- **GIVEN** a maintainer adds the `generate-pr-walkthrough` label, **WHEN** the
|
||||
label-triggered walkthrough job runs, **THEN** it regenerates the current PR
|
||||
head and updates the corresponding R2 object and job-summary URL.
|
||||
- **GIVEN** a pull request receives a new head commit, **WHEN** the
|
||||
walkthrough workflow runs for the synchronize event, **THEN** no
|
||||
walkthrough generation occurs in the initial increment.
|
||||
- **GIVEN** two pull requests use different numbers, **WHEN** both jobs run,
|
||||
**THEN** each output filename and R2 object key is distinct and neither run
|
||||
overwrites the other's result.
|
||||
- **GIVEN** two walkthrough triggers target the same pull request, **WHEN** the
|
||||
newer workflow starts, **THEN** per-PR workflow concurrency cancels the older
|
||||
pipeline so publication and PR linking cannot race across runs.
|
||||
- **GIVEN** the agent submits malformed JSON or a review verdict instead of the
|
||||
walkthrough contract, **WHEN** the managed renderer rejects it, **THEN** the
|
||||
agent must correct the data; the job fails without publishing if no valid
|
||||
output is produced.
|
||||
- **GIVEN** a generated code excerpt contains HTML characters or a Mermaid
|
||||
label contains unsafe markup, **WHEN** the page is rendered, **THEN** the
|
||||
source is escaped or sanitized and no executable PR-controlled markup is
|
||||
inserted by the renderer.
|
||||
- **GIVEN** a draft pull request or an unauthorized fork event, **WHEN** the
|
||||
workflow is triggered, **THEN** no walkthrough agent job runs.
|
||||
- **GIVEN** a successfully generated HTML file, **WHEN** the publishing job
|
||||
uploads it, **THEN** a public GET to the reported URL returns the same HTML
|
||||
with an HTML content type.
|
||||
- **GIVEN** a validated public walkthrough URL, **WHEN** the link job runs,
|
||||
**THEN** the first block in the PR description is a prominent walkthrough
|
||||
callout and all existing contributor content remains below it.
|
||||
- **GIVEN** the walkthrough label regenerates the same or a newer PR head,
|
||||
**WHEN** the link job runs again, **THEN** it replaces the owned callout
|
||||
without duplicating markers or changing the remaining description.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Publishing walkthrough screenshots or using the screenshot media branch.
|
||||
- Generating a new walkthrough on every push. The initial `synchronize` path
|
||||
remains disabled to control model usage.
|
||||
- Retaining a walkthrough for an exact period measured from merge time. The
|
||||
initial lifecycle is measured from upload time.
|
||||
- Adding a Kandev UI page for externally generated PR walkthroughs.
|
||||
- Replacing Kandev's existing in-app `changes-walkthrough` behavior.
|
||||
- Adding a merge-approval or automated code-review verdict to the page.
|
||||
Reference in New Issue
Block a user