Files
management/schema.yaml
T
Thore CimbalandClaude Opus 5 0a9543703d feat: harvest states for recurring patterns, end to end
Slice 1, the tracer bullet: schema, generator and one migrated page, so the
whole chain runs before eleven more depend on it.

Two fields were missing entirely. The v0.3.1 merge did not carry over
wiki-page's status and harvested_in, so the mechanism ADR-0009 decides was
not actually available here. A field-by-field comparison against the
vendored baseline found exactly those two and nothing else - the gap the
previous run predicted when it noted that reconciling the extended files is
a manual step with nothing to contradict it.

The generator gains a clustered section and writes the signpost; collect()
and apply_rules() already existed, so the change is a filter and a fifth
rule rather than a second reader or a new checker.

Running it corrected one of my own design errors immediately. The "without
state" group was written as a warning, and it flagged two perfectly correct
pages: status is optional per ADR-0009 and only meaningful on a page that
tracks a pattern. A permanent complaint with no subject trains people to
ignore the section, so the group is now neutral - with the downside written
into the code, since a pattern that lost its state now looks like an
ordinary page.

Eight controls, seven of them deliberate breaks: both generated files go
stale on a hand edit, an invented state value is refused, the new value is
accepted and clusters correctly, and harvested or declined without a named
version now fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00

219 lines
8.6 KiB
YAML
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.
# schema.yaml — single source of truth for artifact frontmatter.
# Stage 1 of ADR-0004: scripts/validate.py checks generically against this
# file. Extending the framework's metadata means editing THIS file, not code.
# Agents: never invent fields or status values; propose a schema change.
#
# PROJEKTERWEITERUNGEN gegenüber neckbeard v0.3.1 (Original:
# docs/sources/upstream/neckbeard-v0.3.1/schema.yaml; Design:
# docs/design/done/2026-08-11-neckbeard-migration.md, ADR-0012/0013):
# * issue: Pflichtfelder milestone (M1M5) + priority; Status-Enum um
# next/waiting erweitert; due/host/area/wartegrund/gitlab_iid;
# Regel waiting_requires_reason; globale Regel wip_limit (max. 2
# in-progress) in validate.py.
# * issue.projekt (ADR-0019): Herkunfts-Projekt adoptierter
# Komponenten-Issues; zusammen mit gitlab_iid die Spiegel-Adresse.
# Fehlt das Feld, ist management gemeint.
# * component: neuer Typ unter docs/components/ (Dateiname = Slug).
# * wiki-page: Area-Enum um "vision" erweitert; Status-Enum um
# "declined" erweitert (Rahmenwerk geprueft, deckt nicht ab — das
# Muster bleibt bestehen). Begruendung steht am Feld selbst.
# ⚠️ status/harvested_in fehlten nach dem v0.3.1-Merge ganz und sind
# hier nachgetragen; HERKUNFT.md kann das nicht mitfuehren, weil sie
# unter docs/sources/ gesperrt ist, waehrend ADR-0024 ihre Tabelle
# fuer massgeblich erklaert. Widerspruch, als Befund notiert.
# * project_section_marker auf unsere Marke gesetzt; ledger und
# verdict unverändert von upstream übernommen (v0.3.1).
version: 1
scope:
# Files considered artifacts. Templates and raw sources are exempt.
include:
- "PROJECT.md"
- "docs/**/*.md"
exclude:
- "**/template.md"
- "docs/sources/**"
- "vendor/**"
# Files whose inline links are checked, but which need no frontmatter
# (root-level prose: README, AGENTS, WORKFLOW, generated STATUS, ...).
link_only:
- "*.md"
# Files an adopting project holds byte-identical against its vendored
# baseline (AGENTS.md §5). They are copied into repositories that do not
# have this repo's docs/, so they must carry no repo-relative link — a
# link that resolves here and nowhere else makes the adoption path
# unfollowable. Mention an ADR by its identifier instead.
vendored:
- "AGENTS.md"
- "WORKFLOW.md"
- "CLAUDE.md"
# An adopting project appends its own always-on rules below a marker in a
# vendored file (AGENTS.md §5). Everything from the marker on belongs to
# that project, is never copied anywhere, and may link freely — the rule
# above applies only to the upstream part. Adopters set their own marker.
project_section_marker: "<!-- projektabschnitt -->"
# Frontmatter fields whose values are links. Values starting with
# http://, https:// or mailto: are treated as external and only
# format-checked; everything else must be a repo-root-relative path
# to an existing file.
link_fields: [related, sources, supersedes, superseded_by, judged]
types:
project:
dir: "."
filename: "^PROJECT\\.md$"
required: [type, language, size_s_exception, purpose, audience]
fields:
language: { enum: [de, en] }
size_s_exception: { kind: bool }
purpose: { kind: str }
audience: { kind: str }
adr:
dir: "docs/adr"
filename: "^\\d{4}-[a-z0-9-]+\\.md$"
required: [type, id, status, date]
fields:
id: { pattern: "^\\d{4}$" }
status: { enum: [proposed, accepted, superseded] }
date: { kind: date }
supersedes: { kind: link, nullable: true }
superseded_by: { kind: link, nullable: true }
related: { kind: links }
rules:
# status: superseded requires superseded_by to point at the successor.
- superseded_requires_pointer
design:
dir: "docs/design"
filename: "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$"
required: [type, status, date, size]
fields:
status: { enum: [gate-1, gate-2, gate-3, gate-4, gate-5, done] }
size: { enum: [L] }
date: { kind: date }
related: { kind: links }
rules:
# status: done if and only if the file lives under docs/design/done/.
- done_iff_in_done_dir
aar:
dir: "docs/aar"
filename: "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$"
required: [type, status, date]
fields:
status: { enum: [open, harvested] }
date: { kind: date }
related: { kind: links }
issue:
dir: "docs/issues"
filename: "^\\d{4}-[a-z0-9-]+\\.md$"
required: [type, id, status, created, milestone, priority]
fields:
id: { pattern: "^\\d{4}$" }
status: { enum: [open, next, in-progress, waiting, done, rejected] }
created: { kind: date }
milestone: { enum: [M1, M2, M3, M4, M5] }
priority: { enum: [high, medium, low] }
due: { kind: date, nullable: true }
host: { enum: [cfgmon, overmind, matrix, game], nullable: true }
area: { enum: [security, infrastructure, database, element], nullable: true }
wartegrund: { kind: str, nullable: true }
projekt: { enum: [gitops, threadnet-web, threadnet-call], nullable: true }
gitlab_iid: { pattern: "^\\d+$", nullable: true }
related: { kind: links }
rules:
# status: waiting requires a named reason (old rule: "nur mit
# benanntem Grund").
- waiting_requires_reason
component:
dir: "docs/components"
filename: "^[A-Za-z0-9.-]+\\.md$"
required: [type, slug, anzeigename, phase]
fields:
slug: { kind: str }
anzeigename: { kind: str }
phase: { enum: [active, staged, external] }
gitlab: { kind: str }
mirror: { kind: str, nullable: true }
# Repo traegt fremde Historie (z. B. durch einen Upstream-Merge). Der Wert ist
# die Begruendung, kein Schalter: Die Git-Hygiene-Pruefung nimmt hier Commits
# aus, die nicht von eigenen Identitaeten committet wurden — wer das erklaert,
# soll sagen, woher die fremden Commits stammen.
fremdhistorie: { kind: str, nullable: true }
related: { kind: links }
rules:
# The canonical slug is the filename — no second naming scheme.
- slug_matches_filename
# One per session. The envelope is validated here; the gate rows and
# ladder entries in the body are outside what this engine can express
# (it has no notion of a list of records) and belong to scripts/judge.py.
# That seam is deliberate — see ADR-0010.
ledger:
dir: "docs/ledger"
filename: "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$"
required: [type, date, size, status]
fields:
date: { kind: date }
size: { enum: [S, M, L] }
status: { enum: [open, closed] }
related: { kind: links }
# The output of a judged run. Categories are the two-bucket
# classification the harvest assessment established: a finding is either
# the model not following a clear rule, or a gap in the framework.
verdict:
dir: "docs/verdict"
filename: "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$"
required: [type, date, outcome, judged]
fields:
date: { kind: date }
outcome: { enum: [clean, model-failure, framework-gap, both] }
related: { kind: links }
wiki-page:
dir: "docs/wiki"
filename: "^[a-z0-9-]+\\.md$"
required: [type, area]
fields:
area:
enum:
- index
- architecture
- admin
- deployment
- user-guide
- requirements
- faq
- stolpersteine
- vision
# Aus neckbeard v0.3.1 (ADR-0009): Ernte-Stand eines wiederkehrenden
# Musters. `harvested` heisst, eine VEROEFFENTLICHTE Version deckt es
# ab — uebergeben ist nicht geerntet —, und `harvested_in` nennt sie.
#
# PROJEKTERWEITERUNG: vierter Wert `declined`. Ein Muster, das das
# Rahmenwerk geprueft und nicht uebernommen hat, ist weder offen (es
# ist entschieden) noch geerntet (es kommt nie). Es als `open` zu
# fuehren behauptet einen Vorgang, den es nicht mehr gibt. Bewusst
# NICHT `rejected` wie beim Issue-Typ: ein abgelehntes Issue ist
# erledigt, ein `declined` Muster besteht weiter und bleibt unseres.
status: { enum: [open, partly, harvested, declined] }
harvested_in: { kind: str }
sources: { kind: links }
related: { kind: links }
rules:
# Pages other than the index should be linked from somewhere
# (reported as WARNING, not error — see validate.py).
- warn_if_orphan
# PROJEKTERWEITERUNG: harvested und declined behaupten beide eine
# gefallene Entscheidung und muessen die Version nennen, in der sie
# fiel. Ohne das ist "geerntet" nicht von "uebergeben" zu trennen.
- entschiedener_stand_braucht_beleg