ADR-069: A workflow file cites decisions in a form the generation job’s token can commit: by number, or in prose
Status Active · Filed 2026-09-03
Context
A merge-allocated decision carries a temporary code until the generation
job on the default branch numbers it (ADR-049), and luria concretize
then rewrites that code everywhere the record and the code globs reach —
source, tests, the composite actions, the workflow files — so that no
second spelling of the document survives (ADR-040). Workflow files are in
the code globs on purpose: their comments cite decisions, and the
reference lint should see a retired one there like anywhere else.
The job pushes with the workflow’s own token, GITHUB_TOKEN. That token
cannot create or update anything under .github/workflows/, and there is
no workflows: entry a permissions: block could grant; a personal
access token with the workflow scope, or a GitHub App token with
workflow write, can, handed to actions/checkout as token:. So when
ADR-068 was numbered, the rename reached a comment in ci.yml and the
bot’s one commit — views, rename and repairs together — was refused, and
the lint job that needs: it never ran. The record was right; the branch
was red; a second pull request was the only fix.
The constraint is the token’s, not the file’s. A person running
luria concretize locally rewrites a workflow file like any other and
pushes it like any other; only the job’s push is refused, and only on
that token. The first draft of this decision (#150) wrote “never” where
the mechanism enforced no such thing — the case ADR-070 names.
Decision
The generation job pushes with the workflow’s own token, and a workflow
file cites a decision in a form that token can commit: by number, or in
prose. A temporary code cited from a workflow file is the
workflow-temp-codes warning class (ADR-035): reported by default, with
the remedy in the headline — cite the number when the decision has one,
say it in prose, or give the job a token with workflow write and leave
the class unenforced. This repository and the scaffold name the class in
[luria.lint] fail_on, because their jobs run on the workflow token.
A project whose job checks out with a token that has workflow write leaves the class where the dial puts it and lets the bot rewrite the file; the docs say which tokens those are and what else they buy — a push made with one triggers workflow runs, so a repair commit on a pull request gets a check of its own (ADR-068).
Alternatives considered
- A lint error regardless of token — the first draft (#150). Right on the workflow token and wrong on any other, and it said “never” about a file a person can edit freely. A judgement that depends on how the job is configured is a warning class on the dial, which is what ADR-035 built the dial for.
- Push with a workflow-capable token by default. Works, and makes the scaffold depend on a secret someone has to create, scope, store and rotate before its first run succeeds. Offered, not required.
- Have
luria concretizeskip.github/workflows/. The push succeeds, and the workflow file keeps a code naming a document that no longer exists under that name — an unresolvable reference on every run until someone edits it by hand — and--checkneeds the same exception. - Drop workflow files from the code globs. The concretizer leaves them alone, and so does the reference lint: a workflow comment citing a superseded decision stops being caught, in the comments that explain why CI is shaped the way it is.
Consequences
An author citing a pending decision from a workflow comment writes prose — “the amendment to ADR-029 on where views land” — where a source file would carry the code. The scaffold’s workflow does the same. The finding fired once on the real case before the fix, naming the file, the line and the code.
A numbered citation in a workflow file that a migration renames (ADR-040) meets the same refusal on the workflow token; the migration’s pull request edits that file by hand. This decision covers the temporary code, which is the case that recurs.