docs(adrs): refresh index, status, and snapshot - #3863
Conversation
📝 WalkthroughWalkthroughThe changes update ADR naming, registration, and lifecycle projections. They replace the 2026-Q2 architecture snapshot with a current Shipyard read-model covering platform structure, runtime topology, security, clients, governance, and cross-references. ChangesArchitecture documentation refresh
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
docs/architecture/snapshot-2026-Q2.md (2)
138-151: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚖️ Poor tradeoffsuggestion (non-blocking): Keep this snapshot out of guarded runtime validation.
This file is a derived read-model, not proof that implementation has shipped. The ADR validator checks only
docs/adrs/**/*.md, and the projection generator was removed. Do not require ADR frontmatter, projection freshness, or offline-path checks for this snapshot.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/architecture/snapshot-2026-Q2.md` around lines 138 - 151, Exclude snapshot-2026-Q2.md from guarded runtime validation and any checks for ADR frontmatter, projection freshness, or offline-path compliance. Keep those validations scoped to docs/adrs/**/*.md, since this snapshot is a derived read-model and not implementation evidence.Source: Path instructions
51-66: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy liftsuggestion [non-blocking]: Add a projection-drift check for the quarterly snapshot.
The snapshot is intentionally hand-curated, and
tools/adr-projections/project.pyonly validates frontmatter. Add a CI check that compares its lifecycle counts, tier counts, and Proposed list withdocs/adrs/STATUS.md,docs/adrs/INDEX.md, and ADR frontmatter.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/architecture/snapshot-2026-Q2.md` around lines 51 - 66, Add a CI projection-drift validation for the quarterly snapshot that compares its lifecycle counts, tier counts, and Proposed list against docs/adrs/STATUS.md, docs/adrs/INDEX.md, and ADR frontmatter. Extend the existing tools/adr-projections/project.py validation or add a focused check, and ensure mismatches fail CI while preserving the snapshot’s intentional hand-curated presentation.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/adrs/README.md`:
- Line 9: Update the ADR filename convention documentation to use lowercase “a”
in amendment filenames, changing the pattern to NNNN-a<amendment>-short-slug.md
so it matches ADR_FILE_RE and repository usage.
In `@docs/adrs/STATUS.md`:
- Around line 5-11: Update the ADR projection tooling around
tools/adr-projections/project.py so it generates STATUS.md, INDEX.md, and
GRAPH.md rather than only validating frontmatter, or introduce a dedicated
generator for those projections. Add a required CI workflow check that runs the
generator and fails on git diff --exit-code when any generated projection is out
of date.
In `@docs/architecture/snapshot-2026-Q2.md`:
- Around line 253-256: Update the ADR reference in the retention paragraph so
the displayed range “ADRs 0137–0142” matches its destination, either by linking
the complete range or by changing the label to reference ADR 0139 only.
- Around line 144-145: Obtain and record approval from a distinct reviewer
principal for the CP-touching content in snapshot-2026-Q2.md before merging,
covering the documented financial merge, audit, retention, legal hold, identity,
authorization, custody, and compliance boundaries.
---
Nitpick comments:
In `@docs/architecture/snapshot-2026-Q2.md`:
- Around line 138-151: Exclude snapshot-2026-Q2.md from guarded runtime
validation and any checks for ADR frontmatter, projection freshness, or
offline-path compliance. Keep those validations scoped to docs/adrs/**/*.md,
since this snapshot is a derived read-model and not implementation evidence.
- Around line 51-66: Add a CI projection-drift validation for the quarterly
snapshot that compares its lifecycle counts, tier counts, and Proposed list
against docs/adrs/STATUS.md, docs/adrs/INDEX.md, and ADR frontmatter. Extend the
existing tools/adr-projections/project.py validation or add a focused check, and
ensure mismatches fail CI while preserving the snapshot’s intentional
hand-curated presentation.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ce0b2856-0816-4735-8aaa-eb323901ac0c
📒 Files selected for processing (4)
docs/adrs/INDEX.mddocs/adrs/README.mddocs/adrs/STATUS.mddocs/architecture/snapshot-2026-Q2.md
| Each ADR uses a minimal five-section template: **Status, Context, Decision, Consequences, References**. | ||
|
|
||
| Files are named `NNNN-short-slug.md` starting at `0001`. | ||
| Files are named `NNNN-short-slug.md`; amendment records use `NNNN-A<amendment>-short-slug.md`. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
pnpm kb query "What amendment filename case does the ADR projection tooling enforce?"
rg -n --glob '*.md' 'NNNN-[Aa]<amendment>|[0-9]{4}-[Aa][0-9]+-' docs/adrs
rg -n 'amend|filename|slug' tools/adr-projections/project.pyRepository: Harborline-Software/shipyard
Length of output: 15547
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- README convention and table ---'
sed -n '1,14p;60,72p' docs/adrs/README.md
printf '%s\n' '--- Projection filename rules ---'
sed -n '90,125p;370,420p' tools/adr-projections/project.py
printf '%s\n' '--- Matching files and case behavior ---'
python3 - <<'PY'
from pathlib import Path
import re
source = Path("tools/adr-projections/project.py").read_text()
match = re.search(r'ADR_FILE_RE\s*=\s*re\.compile\(\s*r(["\'])(.*?)\1', source)
if not match:
raise SystemExit("ADR_FILE_RE definition not found")
pattern = match.group(2)
compiled = re.compile(pattern)
names = [
"0046-a1-historical-keys-projection.md",
"0046-A1-historical-keys-projection.md",
"0046-a6-social-recovery-seed-delivery-protocol.md",
"0046-A6-social-recovery-seed-delivery-protocol.md",
]
print(f"ADR_FILE_RE={pattern}")
for name in names:
print(f"{name}: {'match' if compiled.match(name) else 'no match'}")
for path in sorted(Path("docs/adrs").glob("0046-*")):
print(f"existing: {path.name}")
PY
printf '%s\n' '--- Relevant CI/test references ---'
rg -n 'adr-projections|project\.py|docs/adrs/README|ADR_FILE_RE' .github tools _shared -g '*.yml' -g '*.yaml' -g '*.py' -g '*.sh' -g '*.md' 2>/dev/null | head -120Repository: Harborline-Software/shipyard
Length of output: 29914
issue [non-blocking]: Use lowercase amendment filenames in the ADR convention.
ADR_FILE_RE rejects uppercase amendment filenames, while the repository uses lowercase a. Change the convention to NNNN-a<amendment>-short-slug.md.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/adrs/README.md` at line 9, Update the ADR filename convention
documentation to use lowercase “a” in amendment filenames, changing the pattern
to NNNN-a<amendment>-short-slug.md so it matches ADR_FILE_RE and repository
usage.
| ## Draft (1) | ||
|
|
||
| - ADR 0046 — [Historical-Keys Projection for Signature Survival under Operator-Key Rotation](./0046-a1-historical-keys-projection.md) — 2026-04-29 | ||
| - ADR 0055 — [Dynamic Forms Substrate](./0055-dynamic-forms-substrate.md) — 2026-04-29 | ||
| - ADR 0131 — [Creative-Content Lifecycle — the Agent-Governance Overlay for the Creative Corps](./0131-creative-content-lifecycle-doctrine.md) — 2026-06-18 | ||
|
|
||
| ## Proposed (22) | ||
|
|
||
| - ADR 0046-A1 — [Historical-Keys Projection for Signature Survival under Operator-Key Rotation](./0046-a1-historical-keys-projection.md) — 2026-04-29 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
pnpm kb query "Where is the canonical ADR projection generator and its required CI check specified?"
rg -n 'adr-projections|project\.py|git diff --check|paths-ignore|skip' \
--glob '*.yml' --glob '*.yaml' --glob '*.md' --glob '*.py' .
git diff --checkRepository: Harborline-Software/shipyard
Length of output: 50385
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
pnpm kb query "What workflow currently validates tools/adr-projections/project.py, and what does the shared policy require for generated documentation projections?"
printf '\n-- candidate files --\n'
git ls-files \
'_shared/engineering/code-review-policy.yaml' \
'tools/adr-projections/*' \
'.github/workflows/*' \
'docs/adrs/STATUS.md' \
'docs/adrs/INDEX.md' \
'docs/adrs/GRAPH.md' \
| sed -n '1,160p'
printf '\n-- generator references in workflows and ADR tooling docs --\n'
rg -n -C 3 \
'tools/adr-projections/project\.py|check-only|STATUS\.md|INDEX\.md|GRAPH\.md|paths-ignore|docs-only' \
.github/workflows tools/adr-projections docs/adrs _shared/engineering/code-review-policy.yaml \
--glob '*.yml' --glob '*.yaml' --glob '*.md' --glob '*.py' \
| sed -n '1,260p'Repository: Harborline-Software/shipyard
Length of output: 30081
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '-- generator outline --'
ast-grep outline tools/adr-projections/project.py
printf '%s\n' '-- generator implementation --'
sed -n '1,280p' tools/adr-projections/project.py
printf '%s\n' '-- tooling README --'
sed -n '1,220p' tools/adr-projections/README.md
printf '%s\n' '-- policy --'
sed -n '1,260p' _shared/engineering/code-review-policy.yamlRepository: Harborline-Software/shipyard
Length of output: 10296
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '-- generator entry point --'
sed -n '540,690p' tools/adr-projections/project.py
printf '%s\n' '-- projection tests --'
sed -n '1,260p' tools/adr-projections/test_project.py
printf '%s\n' '-- relevant workflow sections --'
sed -n '1,220p' .github/workflows/ci.yml
for file in .github/workflows/docs.yml .github/workflows/deep-review-gate.yml; do
if test -f "$file"; then
printf '\n-- %s --\n' "$file"
sed -n '1,220p' "$file"
fi
done
printf '%s\n' '-- policy rules --'
rg -n -C 5 'A1|A2|A7|A8|change_type|generated|projection|docs' \
_shared/engineering/code-review-policy.yaml \
| sed -n '1,260p'Repository: Harborline-Software/shipyard
Length of output: 39732
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '-- changed files --'
git status --short
git diff --stat -- docs/adrs/STATUS.md tools/adr-projections/project.py .github/workflows
printf '%s\n' '-- STATUS diff --'
git diff --unified=3 -- docs/adrs/STATUS.md | sed -n '1,260p'
printf '%s\n' '-- projection-related workflow references --'
rg -n -C 4 'adr-projections|project\.py|STATUS\.md|INDEX\.md|GRAPH\.md' \
.github/workflows \
|| trueRepository: Harborline-Software/shipyard
Length of output: 249
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
from collections import Counter, defaultdict
root = Path(".")
adr_dir = root / "docs" / "adrs"
status_path = adr_dir / "STATUS.md"
# Read repository files as data. Do not import or execute repository code.
frontmatter = re.compile(r"\A---\r?\n(.*?)\r?\n---\r?\n?", re.S)
field = re.compile(r"^(?P<key>[A-Za-z][\w-]*):\s*(?P<value>.*)$")
def scalar(value):
value = value.split(" #", 1)[0].strip().strip("'\"")
return value
docs = {}
for path in sorted(adr_dir.glob("[0-9][0-9][0-9][0-9]*.md")):
match = frontmatter.match(path.read_text())
if not match:
continue
data = {}
for line in match.group(1).splitlines():
m = field.match(line)
if m:
data[m.group("key")] = scalar(m.group("value"))
if "id" in data and "status" in data:
docs[path] = data
expected = Counter(d["status"] for d in docs.values())
actual = Counter()
members = defaultdict(list)
for line in status_path.read_text().splitlines():
heading = re.match(r"^## (Draft|Proposed|Accepted|Superseded|Deprecated|Withdrawn) \((\d+)\)$", line)
if heading:
current = heading.group(1)
actual[current] += 0
continue
item = re.match(r"^- ADR ([0-9]{4}(?:-A[0-9]+)?) — .* \(\./([^)]*)\) — ", line)
if item and current:
actual[current] += 1
members[current].append(item.group(1))
print("frontmatter_status_counts:", dict(sorted(expected.items())))
print("status_projection_counts:", dict(sorted(actual.items())))
print("count_mismatches:", {
key: (expected.get(key, 0), actual.get(key, 0))
for key in sorted(set(expected) | set(actual))
if expected.get(key, 0) != actual.get(key, 0)
})
PYRepository: Harborline-Software/shipyard
Length of output: 496
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import ast
import re
from collections import Counter, defaultdict
status_path = Path("docs/adrs/STATUS.md")
adr_dir = Path("docs/adrs")
frontmatter = re.compile(r"\A---\r?\n(.*?)\r?\n---\r?\n?", re.S)
field = re.compile(r"^(?P<key>[A-Za-z][\w-]*):\s*(?P<value>.*)$")
heading = re.compile(r"^## (?P<status>Draft|Proposed|Accepted|Superseded|Deprecated|Withdrawn) \((?P<count>\d+)\)$")
item = re.compile(r"^- ADR (?P<id>[0-9]{4}(?:-A[0-9]+)?) — \[(?P<title>.*?)\]\(\./(?P<link>[^)]+)\) — (?P<date>\d{4}-\d{2}-\d{2})$")
def scalar(value):
return value.split(" #", 1)[0].strip().strip("'\"")
frontmatter_docs = {}
for path in sorted(adr_dir.glob("[0-9][0-9][0-9][0-9]*.md")):
match = frontmatter.match(path.read_text(encoding="utf-8"))
if not match:
continue
data = {}
for line in match.group(1).splitlines():
m = field.match(line)
if m:
data[m.group("key")] = scalar(m.group("value"))
if "id" in data and "status" in data:
frontmatter_docs[path.name] = data
projected = defaultdict(list)
declared_counts = {}
current = None
unparsed_items = []
for line_no, line in enumerate(status_path.read_text(encoding="utf-8").splitlines(), 1):
m = heading.match(line)
if m:
current = m.group("status")
declared_counts[current] = int(m.group("count"))
continue
if line.startswith("- ADR "):
m = item.match(line)
if not m or current is None:
unparsed_items.append((line_no, line))
else:
projected[current].append((m.group("id"), m.group("link"), m.group("date")))
actual_counts = {status: len(entries) for status, entries in projected.items()}
frontmatter_counts = Counter(data["status"] for data in frontmatter_docs.values())
print("frontmatter_status_counts:", dict(sorted(frontmatter_counts.items())))
print("declared_projection_counts:", dict(sorted(declared_counts.items())))
print("parsed_projection_counts:", dict(sorted(actual_counts.items())))
print("unparsed_status_items:", unparsed_items[:5], "total=", len(unparsed_items))
membership_mismatches = []
for status, entries in projected.items():
for adr_id, link, projected_date in entries:
data = frontmatter_docs.get(link)
if data is None:
membership_mismatches.append((status, adr_id, link, "missing frontmatter file"))
continue
if data["status"] != status:
membership_mismatches.append((status, adr_id, link, f"frontmatter status={data['status']}"))
if projected_date != data["date"]:
membership_mismatches.append((status, adr_id, link, f"frontmatter date={data['date']}"))
print("membership_mismatches:", membership_mismatches[:20], "total=", len(membership_mismatches))
source = Path("tools/adr-projections/project.py").read_text(encoding="utf-8")
tree = ast.parse(source)
functions = [node.name for node in tree.body if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
writes = []
for node in ast.walk(tree):
if isinstance(node, ast.Call):
name = getattr(node.func, "attr", None) or getattr(node.func, "id", None)
if name in {"write_text", "write_bytes", "open", "rename", "replace"}:
writes.append((name, node.lineno))
print("project_functions:", functions)
print("file_write_calls:", writes)
PYRepository: Harborline-Software/shipyard
Length of output: 967
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '-- status and source declarations --'
sed -n '1,24p' docs/adrs/STATUS.md
sed -n '248,340p' docs/adrs/0071-adr-portfolio-system.md
sed -n '1,12p' docs/adrs/_FRONTMATTER.md
printf '%s\n' '-- projection-materialization policy --'
rg -n -C 8 \
'projection-materialization|generated|single source|frontmatter|check-only' \
_shared/engineering/code-review-policy.yaml \
| sed -n '1,260p'Repository: Harborline-Software/shipyard
Length of output: 13569
issue [blocking]: Reconcile the ADR projection tool and its CI gate.
tools/adr-projections/project.py only validates frontmatter. It does not generate STATUS.md, INDEX.md, or GRAPH.md. No workflow runs this tool. Restore the generator or add a separate generator, then add a required CI check that regenerates all three projections and fails when git diff --exit-code detects drift.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/adrs/STATUS.md` around lines 5 - 11, Update the ADR projection tooling
around tools/adr-projections/project.py so it generates STATUS.md, INDEX.md, and
GRAPH.md rather than only validating frontmatter, or introduce a dedicated
generator for those projections. Add a required CI workflow check that runs the
generator and fails on git diff --exit-code when any generated projection is out
of date.
Source: Path instructions
| The embedded node, local store, and offline financial merge share one authority model | ||
| ([ADR 0115](../adrs/0115-embedded-local-node-runtime-offline-financial-merge.md)). |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
pnpm kb query "Does _shared/engineering/code-review-policy.yaml A2 require an independent reviewer for documentation snapshots that describe financial, audit, security, compliance, identity, authorization, custody, or offline merge boundaries?"Repository: Harborline-Software/shipyard
Length of output: 2390
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- policy references ---'
rg -n -C 5 'A2|independent reviewer|CP-touch|financial|audit|security|compliance|change_type' _shared/engineering/code-review-policy.yaml _shared/engineering/code-review.md
printf '%s\n' '--- snapshot range 130-155 ---'
cat -n docs/architecture/snapshot-2026-Q2.md | sed -n '130,155p'
printf '%s\n' '--- snapshot range 230-285 ---'
cat -n docs/architecture/snapshot-2026-Q2.md | sed -n '230,285p'
printf '%s\n' '--- referenced ADRs ---'
rg -n 'ADR 0115|ADR 0139|0115-embedded-local-node-runtime-offline-financial-merge|0139' docs/architecture/snapshot-2026-Q2.md docs/adrs
printf '%s\n' '--- changed-file scope ---'
git status --short
git diff --stat
git diff --name-onlyRepository: Harborline-Software/shipyard
Length of output: 50385
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- snapshot lines 130-155 ---'
sed -n '130,155p' docs/architecture/snapshot-2026-Q2.md
printf '%s\n' '--- snapshot lines 235-285 ---'
sed -n '235,285p' docs/architecture/snapshot-2026-Q2.md
printf '%s\n' '--- snapshot diff ---'
git diff --unified=5 -- docs/architecture/snapshot-2026-Q2.md
printf '%s\n' '--- referenced ADR matches ---'
rg -n -C 3 'ADR 0115|ADR 0139|0115-embedded-local-node-runtime-offline-financial-merge|0139' docs/architecture/snapshot-2026-Q2.md docs/adrs || true
printf '%s\n' '--- policy rule excerpts ---'
sed -n '101,115p;349,355p' _shared/engineering/code-review-policy.yamlRepository: Harborline-Software/shipyard
Length of output: 50385
issue (blocking): Obtain independent review for CP-touching content.
This snapshot documents financial merge, audit, retention, legal hold, identity, authorization, custody, and compliance boundaries. Record approval from a distinct reviewer principal before merge.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/architecture/snapshot-2026-Q2.md` around lines 144 - 145, Obtain and
record approval from a distinct reviewer principal for the CP-touching content
in snapshot-2026-Q2.md before merging, covering the documented financial merge,
audit, retention, legal hold, identity, authorization, custody, and compliance
boundaries.
Source: Path instructions
| Retention is payload-agnostic and spans records, events, audit, and blobs. Event-log compaction must | ||
| preserve convergence safety; sensitive-data classes constrain eligible storage; legal hold is a | ||
| fail-closed gate before shred or crypto-shred | ||
| ([ADRs 0137–0142](../adrs/0139-sensitive-data-classification-compliance-eligible-storage.md)). |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
issue [minor]: Make the ADR range link match its label.
The link text names ADRs 0137–0142, but the target is only ADR 0139. Link the full range or change the label to identify ADR 0139 only.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/architecture/snapshot-2026-Q2.md` around lines 253 - 256, Update the ADR
reference in the retention paragraph so the displayed range “ADRs 0137–0142”
matches its destination, either by linking the complete range or by changing the
label to reference ADR 0139 only.
Regenerate ADR projections from current frontmatter and update the ADR register through 0169. Refresh the architecture snapshot to describe current Shipyard topology and governance.
de9f5f7 to
ba56020
Compare
Refresh ADR projections and the architecture snapshot. Validated with git diff --check.