Files
management/WORKFLOW.md
T
Thore Cimbal 33a0ee7aec chore: upgrade the neckbeard baseline from v0.1.1 to v0.3.1
Three minor releases at once. Byte-identical surface: only WORKFLOW.md
actually changed (+128/-2); CLAUDE.md and the four templates are
untouched. AGENTS.md takes the new upstream prefix (+22/-2) and keeps our
project section unchanged.

The two declared-extended files were reconciled by hand, because nothing
compares them: validate.py gains upstream's check_vendored_portable, and
schema.yaml gains vendored, project_section_marker, the judged link field
and the ledger and verdict types. gen_status.py needed nothing — upstream
did not touch it. Recorded in the new HERKUNFT.md, including that this
reconciliation has no contradictor and will be forgotten next time.

Newly adopted: check_harvest.py and judge.py, plus docs/ledger/ and
docs/verdict/ with their templates. Deliberately not adopted:
check_locked.py — pruefe_sperrliste.py has done that job here since
2026-08-20, and two tools for one rule is maintenance without gain.

The v0.1.1 baseline stays where it is; docs/sources is immutable.
2026-08-21 12:00:00 +00:00

267 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WORKFLOW.md — Gates, Sizing, and Rituals
Read this when a task begins, not preemptively. `AGENTS.md` holds the
always-on rules; this file holds the process.
## Size Classes
Propose one at task start; the human confirms — individually, or batched
at the next refinement session.
| Class | Scope | Process |
|---|---|---|
| S | One file / one small change, no design decisions | Direct. AGENTS.md rules only. The one-line go-ahead **before starting is the stop** — waived only if `PROJECT.md` grants the size-S exception. |
| M | Few files, minor decisions, fits one session | Slice plan in chat, no file. **STOP: plan approval before any code.** Then implement; each slice reports evidence and status inline. Gate 5 is a short AAR note in chat, filed to the wiki only if it produced a real learning. |
| L | New feature, multiple files or sessions, real decisions | Full design doc in `docs/design/` following Gates 15 below. |
When in doubt between two classes, pick the larger.
## Gate 0 — Project Initialization
Runs once per project, triggered by a missing `PROJECT.md`. Ask, never guess:
1. Response language? (e.g. de / en)
2. Size-S gate exception granted? (yes / no)
3. One-line project purpose?
4. Audience — who uses this besides the owner? (Drives which wiki areas
become mandatory later; see `docs/wiki/index.md`.)
Write the answers to `PROJECT.md` (frontmatter per `schema.yaml`), run
`validate.py`, and confirm the result with the human.
## Gates 15 (size L)
Each gate is a section of the design doc. A gate ends with **STOP**:
present the section, wait for explicit approval. Do not pre-fill later
sections.
### Gate 1 — Product
- Problem statement: what user problem, for whom.
- Verifiable acceptance criterion. A real number where one exists;
otherwise a concretely checkable outcome. "Works" is not a criterion.
- Non-goals: what this deliberately does not do.
- Announcement paragraph (35 sentences): what it is, who it's for, why
it's good. If you can't write it, the product isn't understood yet.
- UI involved? Plain-HTML mockups of the affected screens.
**STOP.**
### Gate 2 — Architecture
- Read first: the actual codebase, relevant ADRs, relevant AARs.
Past decisions and learnings are input, not trivia.
- How it fits the real system: endpoints, tables/schemas, query
outlines, the end-to-end flow (Mermaid).
- Constraints: non-functional requirements, proportional to the project.
- Options & trade-offs where more than one viable way exists: pro/contra
each, chosen option, and why. Feature-local decisions stay here.
- Lasting directional decisions discovered here become ADRs (one each),
linked from the design doc.
**STOP.**
### Gate 3 — Program Design
- File locations: exact paths, new and touched.
- Types and method signatures — no bodies.
- Call stack for the main flow(s).
- What the tests will assert.
- Boundaries: an explicit DO NOT CHANGE list.
- Shakiest calls: name the decisions you are least confident about.
**STOP.**
### Gate 4 — Vertical Slices
- Slice 1 is the tracer bullet: a thin end-to-end path that runs
(mocks and stubs allowed). Only then real logic, one testable slice
at a time. Never build layer-by-layer horizontally.
- Every slice lists its tasks; every task names **files, action,
verify, done**.
- Each slice ends with verification evidence, a status
(`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED`),
and a **STOP** for human review before the next slice.
- **A check owes proof that it can fail.** A slice that introduces a
gate, test or check shows it going red with a deliberate break, and
quotes that run. A gate only ever seen green is a hypothesis, not a
result — and a suite that only calls its own functions proves the
functions, not the program, so break the wiring too. The proof has to
come from **where the check actually runs**: a control that passes on a
workstation says nothing about the environment it was wired into.
- **A delivering artifact owes one real result.** Where the artifact's
output *is* the product — a dashboard, a report, a query — acceptance
quotes one observation it actually returned, or states why it is
legitimately empty. "It exists" is not delivery.
### Gate 5 — Closeout
- AAR section in the design doc: planned / actual / why the
difference / learnings.
- Harvest: learnings useful to future readers go to the wiki
(FAQ, Stolpersteine) with source links. A missing or wrong framework
rule becomes a framework issue or update — generalized first, per
**Harvesting to the Framework** below.
- Good analyses produced along the way may be filed as wiki pages
(with citations) instead of dying in chat history.
- **Name what this work made false.** Ask it explicitly — which existing
statement, in which artifact, does this undertaking now contradict? —
and record the answer in the closeout, including when it is "none".
Appending is cheap and feels complete; revising an earlier claim costs
attention, so it only happens when someone asks the question.
- Move the design doc to `docs/design/done/`. Run `gen_status.py`.
## Debugging Path
For bugs and incidents, any size:
1. Reproduce first. No reproduction, no fix.
2. Hypothesize the root cause; verify the hypothesis with evidence
before changing anything.
3. Route the failure before fixing (diagnostic failure routing):
- **Intent issue** — we built toward the wrong goal → back to Gate 1.
- **Spec issue** — the design/plan was wrong → fix the spec
(Gate 2/3), then the code.
- **Code issue** — plan right, code wrong → fix in place.
4. Fix, plus a test that would have caught it.
5. Incidents and major misdiagnoses get a standalone AAR in `docs/aar/`.
## Session Handoff
- When a slice completes, or context quality degrades, write the current
state into the design doc's **Handoff block** — done slices, open
decisions, next step — then start a fresh session that resumes from
the doc. The doc is the memory; the session is disposable.
- End every working session by answering: "Which choices did I make that
I'm least confident about?" File the answer in the design doc.
## The Run Ledger
A rule that leaves no trace cannot be checked by anyone — not a reviewer,
not a script, not a later reader. Following it and ignoring it produce the
same repository. **Every rule therefore owes a trace, or it is
decoration.**
The trace of a run is one file in `docs/ledger/`, one per session, with a
row per gate:
- **`gate`** — the gate number, or the slice for size M.
- **`commit`** — the commit that closed it. A commit, not a timestamp: a
timestamp is written by the same hand as the claim, a commit is not.
- **`approval`** — who released the gate. Empty means the next gate began
without a stop.
- **`status`** — one of `DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` |
`BLOCKED`.
- **`note`** — one line, for a reader.
A second table records the **ladder walk** (`AGENTS.md` §1): what was
searched, what was found, and an outcome that begins `reused:` or
`built:`. The section is mandatory even when nothing new was built — a
session that built nothing says so in a row, because an absent record and
an ignored rule look identical.
`scripts/judge.py` reads the ledger against these rules and against git.
`scripts/judge.py --coverage` reports which rules of this framework are
observable at all; a rule at zero is either unobservable or inert, and
both are worth knowing. That report is never a gate.
## Judging a Run
Two judges, and they answer different questions.
**The deterministic one** is `scripts/judge.py`: gate order, gates owed by
the declared size class, approvals, the status vocabulary, and the
cross-checks against git — that every named commit exists, is reachable,
and that the commits run in the same order as the gates claim. That last
group is the only part measured against evidence the ledger's author did
not write.
**The inferential one** is a ritual, not a script, because no script can
tell a real design document from a plausible one. It is invoked as a
skill or slash command, and its one binding condition is where it runs:
> ⚠️ **Fresh context, and without the work transcript.** An agent that
> judges its own session justifies rather than checks. The judge receives
> `AGENTS.md`, `WORKFLOW.md`, the diff and the ledger — and nothing else.
> This is a condition, not an optimisation; run in the same context the
> output is worthless.
Its rubric, five questions, each answered with a quotation from the diff
or the ledger rather than an impression:
1. **Scope.** Does every changed file trace to the stated undertaking?
Name any that does not.
2. **Substance.** Is the design document load-bearing — do its non-goals
exclude something a reader would otherwise expect, and are the shakiest
calls real risks rather than modesty?
3. **Size.** Was the declared class plausible for what the diff turned out
to be? A size that fits only in hindsight is the finding.
4. **Evidence.** Where a slice claims a result, is a run quoted? Where a
check was added, was it shown going red?
5. **Silence.** Which rule *should* have produced a trace here and did
not?
The output is a `verdict` artifact under `docs/verdict/`, and every
finding is classified into one of two buckets: **model failure** — a clear
rule was not followed — or **framework gap** — the rule is missing,
unenforceable, or invisible. Keeping those apart is the whole point; a
finding in the wrong bucket either blames a person for a missing rule or
writes a behavioural lapse into the rule set. Binding rule:
ADR-0010.
⚠️ The ledger is written by the agent it describes and can be wrong. The
threat model is drift, not sabotage — see the ADR.
## Refinement Session
A recurring, human-triggered ritual. Agenda:
1. Batched confirmations: size classes and small approvals queued since
last time.
2. Backlog triage over `docs/issues/`: close, reprioritize, split.
3. AAR harvest: walk recent AARs; update the wiki (FAQ, Stolpersteine);
propose framework changes — generalized per **Harvesting to the
Framework** below.
4. Wiki lint (content-level, beyond `validate.py`): contradictions
between pages, claims superseded by newer sources, orphan pages,
missing cross-references, gaps worth a new page or a web search.
5. STATUS review: anything stale or surprising in `STATUS.md`.
## Harvesting to the Framework
A harvest is the one artifact that leaves the project: findings travel
from an adopting repo back into the framework so the next version can
cover them. It crosses a trust boundary, and it is written by the people
least likely to notice what is specific about their own project.
- **What travels:** the failure class, its effect, its cause, and the
framework change it argues for.
- **What never travels:** names of the adopter, its products, domains or
customers; hostnames, URLs and paths; stack components; people; commit
hashes; the adopter's own issue and ticket identifiers. Refer to
evidence by an identifier that resolves only in the private record, and
use roles instead of names — "a steering repo for a multi-component
group", never the group.
- **Before it leaves:** run `scripts/check_harvest.py` over the range
being handed over. It reads file contents, file names and commit
messages, and fails closed: a missing term list or an unresolvable range
is an error, never a pass.
- **The term list belongs to the adopter** and is never committed to the
framework — the framework must not store the names it exists to keep
out. A term the framework itself uses is shared vocabulary, not a
secret; listing it only produces noise that teaches people to skip the
check.
⚠️ The check supplements review, it never replaces it: a denylist finds
only the nouns somebody thought of, and it deliberately does not look at
commit identity. Read the diff as well.
Binding rule: ADR-0008.
## Knowledge Handling (summary)
Full rules live in `docs/wiki/index.md`. The short version:
- Original sources live in `docs/sources/`, immutable — agents read
them, never modify them. Wiki pages cite the sources they draw on.
- Contradictions are resolved or explicitly flagged — never left
silently coexisting.
- If the wiki has no confident answer, say so. Never file a
low-confidence synthesis back as knowledge.
- Git is the changelog. No separate log file.