Spezifikation archivieren
Archiviere eine abgeschlossene Spezifikation, indem du <slug>.md unter dem aufgelösten Archivstamm schreibst und <resolved-spec-root>/<slug>/ entfernst, das in Git bei der source_revision des Datensatzes verbleibt. Die Quelle ist das konfigurierte Spec-Wurzelverzeichnis; das Ziel ist sein aufgelöster Archivstamm – docs/history/specs/ für das integrierte docs/specs-Wurzelverzeichnis oder <spec-root>/_archived/ für ein externes oder nicht standardmäßiges Wurzelverzeichnis, entsprechend dem Ziel von roundfix archive. Eine normale Archivierung bedeutet implementiert, verifiziert und in sich geschlossen – jede Aufgabe erledigt, QA bestanden, wenn der Aufgaben-Graph ein QA-Gate erstellt, und jeder indizierte Verweis im Besitz der Spezifikation. Eine QA-Archivierungsüberschreibung hält die explizite Ausnahme fest, ohne eine Verifizierung zu beanspruchen. Nach jeder Entscheidung trennt ein ls <spec-root>/ die aktive Arbeit von der Historie, und das Archiv bleibt durchsuchbar als Aufzeichnung dessen, was gebaut wurde und warum.
Der Auslöser ist der Spezifikationsabschluss, nicht die Veröffentlichung: Führe dies automatisch am Ende eines Roundfix Spec Run aus, sobald das QA-Gate bestanden ist, oder immer dann, wenn der Benutzer es anfordert. Merge und Release sind separate, benutzergesteuerte Schritte – der Archiv-Commit reist einfach mit dem Branch und wird innerhalb des eigenen PR des Features ausgeliefert.
Archiv-Umverlinkung
Bevor der Archivdatensatz geschrieben wird, führt die Archivierung eine Archiv-Umverlinkung durch. Sie befördert automatisch jede zitierte, nicht zum Kern gehörende Datei, auf die ein Live-Dokument aus der Besitzer-Spezifikation verweist, nach docs/references/<basename> und schreibt dann Zitate von jedem Live-Dokument auf die beförderte Kopie um; Zitate des Spezifikationsordners oder eines Kern-Artefakts zeigen auf den Archivdatensatz. Der Datensatz listet umgeschriebene Dateien unter relinked auf, wenn welche vorhanden sind. Die Quelle behält einen Besitzer, sodass eine sekundäre Spezifikation keine Kopie benötigt (ADR-0260, ADR-0083).
Der Spezifikationsordner wird durch Löschung stillgelegt. Im kopierten Ergebnis und in jeder beförderten Kopie benennt ein Ziel innerhalb des entfernten Ordners eine beförderte Kopie oder wird zu einem Git-Locator, `<path>@<commit>`; es zeigt niemals auf den Datensatz selbst. Der Commit enthält den Pfad und ist vom Standard-Branch oder einem Tag aus erreichbar. Wenn ein solcher Commit nicht existiert, benennt der Locator den Pfad und den Pull-Request des Datensatzes. Andere Ziele behalten ihr Repository-Ziel bei (ADR-0278).
Die Archivierung weigert sich zu schreiben, wenn ein Beförderungsziel bereits existiert, wenn zwei Beförderungen einen Basisnamen gemeinsam haben oder wenn ein Live-Dokument, das umgeschrieben werden soll, nicht committete Änderungen bei HEAD aufweist. Zitate in Dateien, die keine Live-Dokumente sind, oder Zitate, die nach dem Umschreiben verbleiben, sind Warnungen, die die Datei, Zeile und das Ziel nennen; die Archivierung belässt diese Dateien unverändert.
Squash-Merges
Ein Squash-Merge behält source_revision nur im Head-Ref des Pull-Requests; der Datensatz nennt diesen Pull-Request in pull_request.
Deklinierte QA-Erklärung
Ein Aufgaben-Graph, der qa: declined mit einer nicht-leeren qa_reason deklariert, archiviert, sobald jede Aufgabe abgeschlossen ist, ohne einen QA-Bericht und ohne Überschreibung, und sein Archivdatensatz hat die Disposition no-qa.
--qa-override wird für eine solche Spezifikation verweigert. Jede andere Archivierungsvorbedingung gilt weiterhin.
QA-Erledigung
Das gleiche Ergebnis erledigt die verfasste qa-Aufgabe und bestimmt, was die Archivierung hinterlässt:
| Ergebnis | Erledigt | Archiviert |
|---|---|---|
pass | Erledigt die QA-Aufgabe als completed und macht die Spezifikation archivierungsfähig, wenn der Bericht keine unzulässigen blockierten Zeilen enthält. | Der Archivdatensatz, der den QA-Bericht und das Urteil nennt; die Spezifikation, ihr QA-Bericht und die Beweise bleiben in Git bei der source_revision des Datensatzes. |
qualifizierend deklariert partial | Erledigt die QA-Aufgabe als completed, wenn keine Zeile fehlgeschlagen, übersprungen oder durch einen Fund blockiert ist, jede als blockiert deklarierte Zeile durch eine passende ## Unreachable Acceptance-Deklaration abgedeckt ist und jede umgebungsblockierte Zeile die Pre-PR Pull-Request-Zeile ist, aufgezeichnet als blocked (environment: no open Pull Request) mit der Pull-Request-Zeile in ihrer Herkunft, oder eine Außenbeweis-Zeile, die die Run-Sandbox nicht erreichen konnte, aufgezeichnet als blocked (environment: network denied: <host>) mit der Außenbeweis-Zeile in ihrer Herkunft. Keine der Zeilen benötigt eine Unreachable Acceptance-Deklaration, und ein Teil, dessen einzige unerfüllte Zeilen solche Zeilen sind, qualifiziert. | Der Archivdatensatz, der den QA-Bericht und das Urteil nennt und den satisfied-by-Datensatz der Deklarationen als unproven führt; die Spezifikation, ihr QA-Bericht und die Beweise bleiben in Git bei der source_revision des Datensatzes. |
environment-blocked | Belässt die Zeile blockiert; der Bericht kann sich dennoch als pass erledigen, wenn gleichwertige Beweise die Umgebungsrichtlinie erfüllen. | Nichts für sich; ein qualifizierender Bericht kann die Spezifikation archivieren. |
failed | Belässt die QA-Aufgabe ungelöst und verweigert die Archivierung, es sei denn, eine autorisierte Überschreibung liegt vor. | Nichts. |
missing | Belässt die QA-Aufgabe ungelöst und verweigert die Archivierung, es sei denn, eine autorisierte Überschreibung liegt vor. | Nichts. |
override | Ändert den QA-Aufgabenstatus oder das Berichtsurteil nicht; erledigt die Archivierung als explizit autorisiert trotz fehlgeschlagener oder fehlender QA. | Der Archivdatensatz mit qa_override, qa_override_approval, qa_override_reason, qa_override_qa_outcome, qa_override_qa_task_status wenn die QA-Aufgabe unvollständig ist, und qa_override_revision; die QA-Aufgabe und Berichte bleiben unverändert in Git bei der source_revision des Datensatzes. |
Zeilen, die die Autorisierung der Spezifikation offline hält
Das qualifizierende deklarierte partial oben akzeptiert auch eine Außenbeweis-Zeile, die das Gate nicht nachgeschlagen hat, weil die eigene Autorisierung der Spezifikation diesen Nachschlag verbietet, aufgezeichnet als blocked (environment: <subject> prohibited by Spec authorization) mit der Außenbeweis-Zeile in ihrer Herkunft. Das Subjekt nennt, was die Autorisierung verbietet, in ihren Worten: network für einen Netzwerknachschlag oder another repository; es ist 1 bis 80 Zeichen lang und enthält keine Klammern. Wie die netzwerkverweigerte Zeile entscheidet sie niemals über ein qualifizierendes Teil und benötigt keine Unreachable Acceptance-Deklaration, eine Zeile zählt höchstens zu einer ausgenommenen Art, und die Erledigung, roundfix qa-report accept und roundfix archive lesen es durch dieselbe Richtlinie (ADR-0276, ADR-0335).
Eine Spezifikation stornieren
Eine Spezifikation, die nicht ausgeliefert wird, wird mit roundfix cancel <slug> --reason <text> storniert, niemals von Hand gelöscht. Der Befehl schreibt einen Archivdatensatz mit der Disposition cancelled, gibt jede übernommene Backlog-Eintrag nach docs/backlog/ mit status: open und jeden übernommenen Fund nach docs/findings/ mit status: pending zurück und entfernt dann den Spezifikationsordner, während seine Bytes in Git bei source_revision bleiben. Ein stornierter Datensatz erfüllt niemals eine Voraussetzung, ersetzt niemals eine andere Spezifikation und wird von der Lieferwiederholung verweigert.
Ignorierte Dateien und eine fehlgeschlagene Entfernung
Der Archivierungsbefehl und der Stornierungsbefehl verweigern die Ausführung, während Git irgendeine Datei im Spezifikationsordner ignoriert. Jeder Befehl listet jede ignorierte Datei auf und schreibt nichts. Der Bediener muss jede Datei entfernen oder mit git add -f committen und dann wiederholen.
Wenn die endgültige Entfernung des Spezifikationsordners fehlschlägt, behalten beide Befehle jedes Schreiben, beenden mit 1 und geben den Befehl aus, der die Entfernung abschließt: chmod -R u+w '<folder>' && rm -rf '<folder>'.
Vorbedingungen – verifizieren, nicht vertrauen
Überprüfe alle drei mit frischem Befehlsnachweis, bevor du etwas anfasst:
-
Erforderliche Aufgaben abgeschlossen. Für eine normale Archivierung lese jede
task_NN.md, die in_tasks.mdaufgeführt ist; jederstatusmusscompletedsein. Für eine QA-Archivierungsüberschreibung muss jede Nicht-QA-Aufgabecompletedsein; die QA-Aufgabe kannpendingoderfailedbleiben. Wenn die QA-Aufgabe auchcompletedist, muss der neueste Bericht unzulässig bleiben oder die Überschreibung ist unnötig und wird verweigert.Befehl: führe
grep -n '^status:' <each-task-file-listed-in-_tasks.md>aus und behalte seine Ausgabe. Jeder andere Wert alsstatus: completedblockiert eine normale Archivierung. Während einer Überschreibung blockiert nur eine Nicht-QA-Aufgabe mit einem anderen Status die Archivierung; nenne diese Aufgabendatei. -
QA bestanden. Der neueste Bericht in
qa/muss den QA-Verifizierer des Repositorys bestehen. Ersetze keine Zeilensuche durch eine strukturierte Validierung. Verwende in Roundfix denselbeninternal/spec.QAVerdict-Vertrag wie der Archivierungsbefehl: wähle den neuesten Bericht nach dem Dateinamenvertragqa-report-YYYY-MM-DD[-NN].md, parse sein YAML-Frontmatter, erfordere ein unterstütztesverdict, erfordere, dass beide blockierten Zeilenfelder nicht-negative ganze Zahlen sind, wenn vorhanden, und lehneverdict: passab, wennrows_blocked_findingungleich Null ist. Behalte den Berichtspfad und das Ergebnis des Verifizierers als Nachweis. Ein fehlendesqa/-Verzeichnis, ein fehlerhafter neuester Bericht oder ein nicht bestehendes Urteil blockiert die normale Archivierung. Fahre nur unter einer expliziten QA-Archivierungsüberschreibung fort, die durchroundfix archive <slug> --qa-override --approval <source> --reason <text>durchgeführt wird. Zeichne die Überschreibung niemals durch Bearbeiten des Archivdatensatzes auf. Ein qualifizierender neuester Bericht macht eine fehlgeschlagene oder ausstehende QA-Aufgabe nicht abgeschlossen; die Überschreibung wird nur verweigert, wenn der Bericht qualifiziert und jede Aufgabe abgeschlossen ist. -
Die Spezifikation ist in sich geschlossen. Wende diese Vorbedingung an, wenn
docs/specs/<slug>/references/_index.mdexistiert oder ein symbolischer Link ist; ein symbolischer Link, einschließlich eines defekten, ist ein ungültiger Indexzustand und keine Legacy-Spezifikation. Nur eine Spezifikation, bei der dieser Pfad weder existiert noch ein symbolischer Link ist, stammt aus der Zeit vor diesem Vertrag und besteht ohne Nachrüstung historischer Artefakte. Für eine indizierte Spezifikation muss jeder indiziertepathrelativ zu_index.mdexistieren, jeder nie aktualisiertesource-Pfad muss abwesend sein, und kein Markdown-Linkziel innerhalb der Spezifikation darf aufdocs/_inbox/oderdocs/findings/zeigen.Befehle: führe zuerst diese Linkzielprüfung durch; ihre Syntax stimmt absichtlich mit Inline- oder Referenzstil-Markdown-Links überein, nicht mit Prosa, die lediglich einen der beiden Bäume nennt:
spec_dir=docs/specs/<slug> link_hits=$(grep --include='*.md' -RInE '(\]\([^)]*(docs/)?(_inbox|findings)/[^)]*\)|^[[:space:]]*\[[^]]+\]:[[:space:]]*<?[^[:space:]>]*(docs/)?(_inbox|findings)/)' "$spec_dir") link_status=$? if test "$link_status" -eq 0; then printf '%s\n' "$link_hits" echo "Selbstständigkeit fehlgeschlagen: schreibe jeden aufgeführten Link bei Übernahmeschritt 8 um" exit 1 fi test "$link_status" -eq 1 || exit "$link_status"Dann parse und validiere jede Datenzeile. Der Index gehört zur aktuellen Spezifikation:
ownermuss gleich ihrem vierstelligen Präfix sein,typemussinbox,findingoderbacklogsein, und jedersourceundpathdarf nur einmal vorkommen. Einpathmuss ein Basisname relativ zu_index.mdsein; lehne absolute Pfade,.,.., Pfadtrennzeichen und symbolische Links ab, anstatt Traversierung oder einen Link außerhalb vonreferences/zu erlauben. Führe Folgendes vom Repository-Stamm aus und behalte die normalisierten Zeilen sowie alle Diagnosen als Nachweis:slug=<slug> index="docs/specs/$slug/references/_index.md" expected_owner=${slug%%-*} parsed_index=$(mktemp) || exit 1 trap 'rm -f "$parsed_index"' EXIT HUP INT TERM if test -L "$(dirname "$index")"; then printf 'Selbstständigkeit fehlgeschlagen: references/ darf kein symbolischer Link sein\n' >&2 exit 1 fi if test -L "$index" || test ! -f "$index"; then printf 'Selbstständigkeit fehlgeschlagen: references/_index.md muss eine reguläre, nicht symbolische Linkdatei sein\n' >&2 exit 1 fi awk -F '|' -v expected_owner="$expected_owner" ' function trim(value) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", value) return value } function reject(message) { print "Selbstständigkeit fehlgeschlagen: " message > "/dev/stderr" invalid = 1 } function separator(value) { value = trim(value) return value ~ /^:?-{3,}:?$/ } /^[[:space:]]*$/ || /^[[:space:]]*#/ { next } !header { if (NF != 7 || trim($2) != "source" || trim($3) != "type" || trim($4) != "owner" || trim($5) != "adopted date" || trim($6) != "path") { reject("_index.md muss den festen Header source | type | owner | adopted date | path verwenden") } else { header = 1 } next } !divider { if (NF != 7 || !separator($2) || !separator($3) || !separator($4) || !separator($5) || !separator($6)) { reject("_index.md hat einen ungültigen Tabellentrenner") } else { divider = 1 } next } { source = trim($2) type = trim($3) owner = trim($4) adopted = trim($5) path = trim($6) if (NF != 7 || source == "" || type == "" || owner == "" || adopted == "" || path == "") { reject("ungültige Indexzeile bei Zeile " NR ": " $0) next } if (type != "inbox" && type != "finding" && type != "backlog") { reject("type muss `inbox`, `finding` oder `backlog` sein bei Zeile " NR ": " type) } if (owner != expected_owner) { reject("owner muss " expected_owner " sein bei Zeile " NR ": " owner) } if (seen_source[source]++) { reject("doppelter source bei Zeile " NR ": " source) } if (seen_path[path]++) { reject("doppelter path bei Zeile " NR ": " path) } if (adopted !~ /^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/) { reject("adopted date muss YYYY-MM-DD sein bei Zeile " NR ": " adopted) } if ((type == "inbox" && source !~ /^docs\/_inbox\/[^\/]+\.md$/) || (type == "finding" && source !~ /^docs\/findings\/[^\/]+\.md$/) || (type == "backlog" && source !~ /^docs\/backlog\/[^\/]+\.md$/)) { reject("source stimmt nicht mit type überein bei Zeile " NR ": " source) } print source "|" type "|" owner "|" adopted "|" path } END { if (!header || !divider) { reject("_index.md fehlt der feste Tabellenheader") } if (invalid) { exit 1 } } ' "$index" > "$parsed_index" || exit $? while IFS='|' read -r source type owner adopted path; do case "$path" in ""|.|..|/*|*/*|*\\*) printf 'Selbstständigkeit fehlgeschlagen: path muss ein Basisname relativ zu `_index.md` sein: %s\n' "$path" >&2 exit 1 ;; esac source_basename=${source##*/} if test "$path" != "$source_basename" ... [für die Übersetzung gekürzt]