feat(templates): let the action own the workflows it tells people to write - #116
feat(templates): let the action own the workflows it tells people to write#116Svilen-Stefanov wants to merge 3 commits into
Conversation
…write The workflow text lived in three hand-maintained places: the webview generator, this README, and this repository's own dogfood workflows. Two of them had already drifted. The README's review workflow lacked the `closed` trigger, so anyone who set up by hand had a workflow that ran an in-flight review to completion for a pull request they had just closed. Nobody noticed, because nothing compared them. `templates/` is now the only place that text exists, as the finished file with named holes: the branch, the credential block, and the two insertions that delivery adds. The README's copy-and-paste blocks are generated from it, with a test asserting they match. Holes are what make a template both renderable and matchable, which is the point. `workflow_templates.py` renders by substituting them and recognises a committed workflow by turning the same file into a regular expression with each hole a capture group. One pass answers which version produced a file and how it was configured, with nothing parsed and nothing inferred, and a file that matches nothing has been edited: a fact rather than the heuristic "no extra steps and no extra inputs" that today's detection has to settle for. The fixtures are the reason this is trustworthy. They were rendered by the webview before any of this existed, and the templates reproduce them byte for byte; a template that merely looked right would rewrite every repository's workflow on its next update. `CHANGELOG.json` carries one sentence per version, typed `update` or `replace` so a revert can supersede rather than accumulate. It is the copy the webview shows, not a developer changelog someone later paraphrases. Nothing consumes this yet. The webview keeps generating its own text until it vendors these, which is the next change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CodeBoarding reviewStatus: 1 changed component See the full change in CodeBoarding. graph LR
n_Diff_Analysis_Engine["Diff Analysis Engine"]
n_Mermaid_Diagram_Renderer["Mermaid Diagram Renderer"]
n_OIDC_Relay_Handler["OIDC Relay Handler"]
n_Repository_Analysis_Driver["Repository Analysis Driver"]
n_OIDC_Relay_Server["OIDC Relay Server"]
n_LLM_Credential_Resolver["LLM Credential Resolver"]
n_Diff_Analysis_Engine -- "Passes computed structural diffs for diagram ge…" --> n_Mermaid_Diagram_Renderer
n_Repository_Analysis_Driver -- "Produces analysis JSON artifacts consumed for d…" --> n_Diff_Analysis_Engine
n_OIDC_Relay_Server -- "Dispatches incoming HTTP requests to relay hand…" --> n_OIDC_Relay_Handler
classDef added fill:#1f883d,stroke:#0b5d23,color:#ffffff;
classDef modified fill:#bf8700,stroke:#7d4e00,color:#ffffff;
classDef deleted fill:#cf222e,stroke:#82071e,color:#ffffff,stroke-dasharray:5 3;
class n_LLM_Credential_Resolver modified;
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2051ae4c70
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
The webview bundles its workflow generator into a browser build, so it cannot read `templates/*.yml` from disk. Without somewhere to import them from it would have to keep a second, hand-maintained copy of the text, which is the drift this whole change exists to remove. `templates/bundle.json` is the templates, their fills and the changelog as data. A test asserts it matches the .yml files it was built from, and a second one renders the fixtures from the bundle alone, the way a consumer will. That keeps the authored template the thing under review rather than a decorative original beside the JSON everyone actually uses. Rebuild with `python3 scripts/action/workflow_templates.py`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1bc2b989b4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if version is None: | ||
| return TEMPLATES / name | ||
| return TEMPLATES / "history" / f"v{version}" / name |
There was a problem hiding this comment.
Resolve the current version from the live template
When a consumer passes the advertised current version (1 from CHANGELOG.json or bundle.json) to render(..., version=1) or match(..., version=1), this always reads templates/history/v1/..., but that directory does not exist, so both APIs raise FileNotFoundError for the only valid version currently published. Route the current version to the live template or archive v1 so explicit version-based iteration can recognize the current workflow.
Useful? React with 👍 / 👎.
… have run Six findings, all real: - `credential_fills` picked each provider's `*_api_key` input, but ollama and litellm are selected by their base URL. A rendered `llm: ollama` workflow wired `ollama_api_key`, which the action's own contract refuses with `missing_provider_key`, after telling the user to create a secret that could never have worked. Fills now use the input that actually selects the provider, and endpoint-selected providers get their own wording. - Holes accept arbitrary text, so an edited delivery block still matched and was reported as a configuration we generated, letting a later update overwrite the user's edits. Every capture is now compared against the fills we authored, and the delivery holes must agree on one mode rather than one of them being read and the rest assumed. - A hole appearing twice could capture two different values, and the later one won. Editing only `branches:` and not `target_branch:` therefore looked generated. Repeated holes must now agree. - Historical templates rendered and matched with TODAY's fills, so changing a fill's wording would make every repository on an older version unrecognisable: the exact failure the frozen history exists to prevent. Fills and the provider table are versioned alongside the template. - `id-token: write` was granted to workflows running on the user's own provider key, which never mint a token. It now depends on the credentials. - A branch name containing an apostrophe produced a scalar GitHub cannot parse. Merging the OIDC and delivery permissions into one hole is not cosmetic: two adjacent holes cannot be told apart, because the first lazy group takes none of the text and the second takes all of it. `to_pattern` now refuses adjacent holes rather than producing a template that silently matches nothing. The fixture test changes shape as a result. It asserted byte-identity with the webview's output; the OIDC change is a deliberate departure from it, so it now asserts that NOTHING ELSE differs, which is the property that was actually protecting us. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7bbcad452d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| # is set the run FAILS and says so. It does not fall back to a different | ||
| # provider on CodeBoarding's hosted tier. | ||
| llm: {{LLM}} | ||
| {{SELECTOR}}: https://your-{{LLM}}-host |
There was a problem hiding this comment.
Parameterize endpoint-only provider URLs
When Ollama or LiteLLM is selected, the fresh replacement for the earlier broken key wiring emits a nonempty literal such as https://your-ollama-host rather than a configurable expression or render-time value. Credential preflight therefore accepts the endpoint as configured and proceeds with installation and analysis before failing against the nonexistent host; users must manually edit the generated workflow, after which the matcher also refuses ownership of it. Render a repository variable/secret or make the actual endpoint a recognized template parameter instead.
AGENTS.md reference: AGENTS.md:L20-L23
Useful? React with 👍 / 👎.
| result: dict[str, str] = {} | ||
| if "BRANCH" in captured: | ||
| result["branch"] = captured["BRANCH"] |
There was a problem hiding this comment.
Decode escaped branch names before returning them
When a sync workflow targets a valid ref containing an apostrophe, such as release/o'neil, rendering correctly stores it as release/o''neil, but matching returns that YAML-escaped representation verbatim. The advertised render-to-match round trip therefore changes the branch; a consumer that uses the result to update or re-render the workflow doubles the quotes again and targets a different ref. Unescape the captured single-quoted scalar before exposing it as branch.
Useful? React with 👍 / 👎.
| fills = credential_fills(version) | ||
| tier = next( | ||
| (t for t, body in fills.items() if _same(body, captured.get("CREDENTIALS", ""))), | ||
| None, | ||
| ) |
There was a problem hiding this comment.
Validate the captured review OIDC permission
When a hosted review workflow has its id-token: write line removed or changed, OIDC_PERMISSION is captured but never compared with the fill required by the resolved tier; conversely, an added OIDC grant on a BYOK review is also accepted. Both edited files therefore still match as generated—the hosted variant then fails credential preflight, while a later template update may overwrite either permission edit—so compare this capture with the tier-specific oidc.review.*.yml fill before returning a match.
Useful? React with 👍 / 👎.
|
/codeboarding |
CodeBoarding reviewStatus: 1 changed component See the full change in CodeBoarding. graph LR
n_Diff_Analysis_Engine["Diff Analysis Engine"]
n_Mermaid_Diagram_Renderer["Mermaid Diagram Renderer"]
n_OIDC_Relay_Handler["OIDC Relay Handler"]
n_Repository_Analysis_Driver["Repository Analysis Driver"]
n_OIDC_Relay_Server["OIDC Relay Server"]
n_LLM_Credential_Resolver["LLM Credential Resolver"]
n_Diff_Analysis_Engine -- "Passes computed structural diffs for diagram ge…" --> n_Mermaid_Diagram_Renderer
n_Repository_Analysis_Driver -- "Produces analysis JSON artifacts consumed for d…" --> n_Diff_Analysis_Engine
n_OIDC_Relay_Server -- "Dispatches incoming HTTP requests to relay hand…" --> n_OIDC_Relay_Handler
classDef added fill:#1f883d,stroke:#0b5d23,color:#ffffff;
classDef modified fill:#bf8700,stroke:#7d4e00,color:#ffffff;
classDef deleted fill:#cf222e,stroke:#82071e,color:#ffffff,stroke-dasharray:5 3;
class n_LLM_Credential_Resolver modified;
|
ivanmilevtues
left a comment
There was a problem hiding this comment.
blocker:
- One thing which needs to get fixed is the url comment
- empty files deliver.inpyt.push, delivery.permission.push, oidc.review.byok, oidc,sync.byok
I think for now we should probably just not merge this and keep the other changes in as they are
| with: | ||
| mode: sync | ||
| force_full: ${{ inputs.force_full || false }} | ||
| {{DELIVERY_INPUT}} target_branch: '{{BRANCH}}' |
There was a problem hiding this comment.
this here is questionable ;d
Target_branch proly has to be on a new line
| contents: read # check out the repo + read the committed baseline (no writes in review mode) | ||
| pull-requests: write # post the architecture-diff PR comment | ||
| issues: write # the /codeboarding issue_comment trigger + comment API | ||
| {{OIDC_PERMISSION}} actions: read # let a repeat review download the analysis an earlier run published, instead of re-deriving the whole PR |
There was a problem hiding this comment.
proabably actions:read should be on a new line
| # is set the run FAILS and says so. It does not fall back to a different | ||
| # provider on CodeBoarding's hosted tier. | ||
| llm: {{LLM}} | ||
| {{SELECTOR}}: https://your-{{LLM}}-host |
There was a problem hiding this comment.
blockerish: agree with the comment no?
Step 2 of the template design, on top of #104. Nothing consumes it yet.
The drift it fixes
The workflow text lived in three hand-maintained places. Two had already diverged:
closedtriggercancel-in-progressfalseNeither file was broken; they were coherent and different, and nobody noticed because nothing compared them. The README is now generated from the template, with a test.
Holes, not just placeholders
A template is the finished file with named holes:
This is the answer to "the configurable things cannot match 1:1". The same holes work in both directions: substituted to render, captured to match.
workflow_templates.pyturns a template into a regular expression with one capture group per hole, so a single pass over a committed workflow answers which version wrote it and how it was configured at once.Nothing is parsed and nothing is inferred. A file that matches nothing has been edited, which is a fact rather than the heuristic
customisedsettles for today (its own source admits it "cannot see a trigger edit").credentials.byok.ymlis one authored fill expanded across the provider table, so adding a provider stays a one-line change tosupported-providers.json.Why the fixtures matter
tests/fixtures/generated/was rendered by the webview, before any of this existed. The templates reproduce all ten byte for byte. A template that merely looked right would rewrite every repository's workflow on its next update, so this is the assertion the whole move rests on.Changelog
One sentence per version, typed
update(concatenates) orreplace(supersedes everything earlier, so a revert reads as a destination rather than a journey). It is the copy the webview will show, not a developer changelog someone later paraphrases.Tests
163 pass. New: byte-identical reproduction of every fixture, round-trip for every tier and delivery, round-trip for all 13 providers, an edit of any kind matching nothing, a credential block we never wrote rejected, CRLF tolerated, and the README asserted equal to the templates.
Next
The webview vendors these and replaces its own generator, then runs the matcher alongside the existing inference until they agree in the field.
🤖 Generated with Claude Code