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 concretize skip .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 --check needs 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.