Record ADR conflicts and publish the estate arc42
Some checks failed
Build and publish policy-nexus image / build-and-push (push) Failing after 39s
Some checks failed
Build and publish policy-nexus image / build-and-push (push) Failing after 39s
T03 writes a rulings overlay and a readable conflict list. T04 starts relevance: five superseded, five live conflicts, estate and activity-core ADRs marked publish-after-prefix. The Coulomb estate map is now a published architecture document.
This commit is contained in:
parent
9c6a1d3ce0
commit
d1f9ac6cd7
17 changed files with 1263 additions and 126 deletions
|
|
@ -82,11 +82,30 @@ def _frontmatter(path: Path) -> dict[str, str]:
|
|||
return {key: value for key, value in meta.items() if value}
|
||||
|
||||
|
||||
def _load_rulings(path: Path | None) -> dict[tuple[str, str], dict[str, Any]]:
|
||||
if path is None or not path.exists():
|
||||
return {}
|
||||
payload = _read_json(path)
|
||||
rulings: dict[tuple[str, str], dict[str, Any]] = {}
|
||||
for ruling in payload.get("rulings", []):
|
||||
key = (ruling["source_repo"], ruling["source_path"])
|
||||
if key in rulings:
|
||||
raise ValueError(f"duplicate ruling: {key[0]}/{key[1]}")
|
||||
rulings[key] = ruling
|
||||
return rulings
|
||||
|
||||
|
||||
def build_ledger(
|
||||
inventory_path: Path, config_path: Path, *, policy_root: Path, source_root: Path
|
||||
inventory_path: Path,
|
||||
config_path: Path,
|
||||
*,
|
||||
policy_root: Path,
|
||||
source_root: Path,
|
||||
rulings_path: Path | None = None,
|
||||
) -> dict[str, Any]:
|
||||
inventory = _read_json(inventory_path)
|
||||
config = _read_json(config_path)
|
||||
rulings = _load_rulings(rulings_path)
|
||||
paths = _repo_paths(config, policy_root=policy_root, source_root=source_root)
|
||||
rows: list[dict[str, Any]] = []
|
||||
for source in inventory.get("sources", []):
|
||||
|
|
@ -123,11 +142,7 @@ def build_ledger(
|
|||
"notes": SPECIALS.get(key, ""),
|
||||
"proposed_disposition": "publish" if key in FIRST_PUBLISH else "unreviewed",
|
||||
"conflict_kinds": [],
|
||||
"successor": (
|
||||
"netkingdom-tenancy-posture"
|
||||
if key == ("the-custodian", "canon/architecture/adr-008-multi-tenancy-model.md")
|
||||
else ""
|
||||
),
|
||||
"successor": "",
|
||||
"review_notes": "",
|
||||
}
|
||||
)
|
||||
|
|
@ -150,11 +165,31 @@ def build_ledger(
|
|||
row["bare_adr_collisions"] = [
|
||||
other for other in by_bare.get(row["bare_adr"], []) if other != label
|
||||
]
|
||||
key = (row["source_repo"], row["source_path"])
|
||||
ruling = rulings.get(key)
|
||||
if ruling:
|
||||
for field in (
|
||||
"proposed_disposition",
|
||||
"conflict_kinds",
|
||||
"successor",
|
||||
"review_notes",
|
||||
):
|
||||
if field in ruling:
|
||||
row[field] = ruling[field]
|
||||
if row["id_collisions"] and 1 not in row["conflict_kinds"]:
|
||||
row["conflict_kinds"] = [*row["conflict_kinds"], 1]
|
||||
if not row["review_notes"]:
|
||||
row["review_notes"] = (
|
||||
f"Front-matter id {row['frontmatter']['id']!r} is shared; "
|
||||
"pick a repo-prefixed publication id before publish. "
|
||||
"Who rules: owning repo."
|
||||
)
|
||||
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"protocol": "docs/adr-review/protocol.md",
|
||||
"source_inventory": "source-inventory.json",
|
||||
"rulings": str(rulings_path) if rulings_path else "",
|
||||
"rows": rows,
|
||||
}
|
||||
|
||||
|
|
@ -210,6 +245,17 @@ def _summary(ledger: dict[str, Any]) -> str:
|
|||
[f"{row['source_repo']}/{row['source_path']}", *row["bare_adr_collisions"]]
|
||||
)
|
||||
lines.append(f"- `{key}`: {others}")
|
||||
conflicts = [row for row in rows if row["proposed_disposition"] == "conflict"]
|
||||
lines.extend(["", "## Conflict rows", ""])
|
||||
if not conflicts:
|
||||
lines.append("None.")
|
||||
else:
|
||||
for row in conflicts:
|
||||
kinds = ",".join(str(kind) for kind in row["conflict_kinds"]) or "-"
|
||||
lines.append(
|
||||
f"- `{row['source_repo']}/{row['source_path']}` "
|
||||
f"(kinds {kinds}): {row['review_notes']}"
|
||||
)
|
||||
lines.extend(["", "## Missing files", ""])
|
||||
if not missing_files:
|
||||
lines.append("None.")
|
||||
|
|
@ -226,6 +272,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||
parser.add_argument("--inventory", type=Path, default=Path("source-inventory.json"))
|
||||
parser.add_argument("--output", type=Path, default=Path("docs/adr-review/ledger.json"))
|
||||
parser.add_argument("--summary", type=Path, default=Path("docs/adr-review/SUMMARY.md"))
|
||||
parser.add_argument("--rulings", type=Path, default=Path("docs/adr-review/rulings.json"))
|
||||
parser.add_argument("--policy-root", type=Path)
|
||||
parser.add_argument("--source-root", type=Path)
|
||||
args = parser.parse_args(argv)
|
||||
|
|
@ -238,6 +285,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||
config_path,
|
||||
policy_root=policy_root,
|
||||
source_root=source_root,
|
||||
rulings_path=args.rulings.resolve() if args.rulings else None,
|
||||
)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue