Skip to content

feat(templates): let the action own the workflows it tells people to write - #116

Open
Svilen-Stefanov wants to merge 3 commits into
mainfrom
feat/workflow-templates
Open

feat(templates): let the action own the workflows it tells people to write#116
Svilen-Stefanov wants to merge 3 commits into
mainfrom
feat/workflow-templates

Conversation

@Svilen-Stefanov

Copy link
Copy Markdown
Contributor

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:

README users webview users
closed trigger absent present
cancel-in-progress false on close
Closing a PR mid-review run continues, analysis wasted run cancelled

Neither 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:

{{BRANCH}}  {{CREDENTIALS}}  {{DELIVERY_PERMISSION}}  {{DELIVERY_INPUT}}  {{SYNC_PR_GUARD}}

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.py turns 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.

match("sync", committed)
# -> {'branch': 'trunk', 'tier': 'byok:anthropic', 'delivery': 'pull_request'}
# -> None   # edited, so it is theirs

Nothing is parsed and nothing is inferred. A file that matches nothing has been edited, which is a fact rather than the heuristic customised settles for today (its own source admits it "cannot see a trigger edit").

credentials.byok.yml is one authored fill expanded across the provider table, so adding a provider stays a one-line change to supported-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) or replace (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

…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-review

codeboarding-review Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

CodeBoarding review

Status: 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;
Loading

download artifacts · run 33161252085

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread scripts/action/workflow_templates.py Outdated
Comment thread scripts/action/workflow_templates.py Outdated
Comment thread scripts/action/workflow_templates.py Outdated
Comment thread scripts/action/workflow_templates.py
Comment thread templates/codeboarding.yml Outdated
Comment thread templates/codeboarding-sync.yml
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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +88 to +90
if version is None:
return TEMPLATES / name
return TEMPLATES / "history" / f"v{version}" / name

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +197 to +199
result: dict[str, str] = {}
if "BRANCH" in captured:
result["branch"] = captured["BRANCH"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +201 to +205
fills = credential_fills(version)
tier = next(
(t for t, body in fills.items() if _same(body, captured.get("CREDENTIALS", ""))),
None,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member

/codeboarding

@codeboarding-review

codeboarding-review Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

CodeBoarding review

Status: 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;
Loading

download artifacts · run 33176919766

@ivanmilevtues ivanmilevtues left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}}'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blockerish: agree with the comment no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants