feat(consistency): bootstrap empty repo projections
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
tegwick 2026-08-22 12:07:08 +02:00
parent 52a7d6bb73
commit 03c7924b7f
3 changed files with 187 additions and 7 deletions

View file

@ -1154,7 +1154,12 @@ def _check_work_record_index_freshness(
)
def check_repo(api_base: str, repo_slug: str, repo_path_override: str | None = None) -> ConsistencyReport:
def check_repo(
api_base: str,
repo_slug: str,
repo_path_override: str | None = None,
bootstrap_empty_projection: bool = False,
) -> ConsistencyReport:
"""Run all consistency checks for a registered repo."""
repo = _api_get(api_base, f"/repos/{repo_slug}", return_error=True)
if isinstance(repo, dict) and "_error" in repo:
@ -1180,6 +1185,28 @@ def check_repo(api_base: str, repo_slug: str, repo_path_override: str | None = N
repo_path: str = resolve_repo_path(repo, repo_path_override)
report = ConsistencyReport(repo_slug=repo_slug, repo_path=repo_path)
if bootstrap_empty_projection:
projected = _api_get(api_base, "/workplans", {"repo_id": repo_id}, return_error=True)
if not isinstance(projected, list):
report.add(
severity="FAIL",
check_id="C-36",
message="Could not prove that the repository projection is empty",
fixable=False,
)
return report
if projected:
report.add(
severity="FAIL",
check_id="C-36",
message=(
"Empty-projection bootstrap refused: repository already has "
f"{len(projected)} projected workplan(s)"
),
fixable=False,
)
return report
if not repo_path:
report.add(
severity="FAIL", check_id="C-00",
@ -1328,14 +1355,21 @@ def check_repo(api_base: str, repo_slug: str, repo_path_override: str | None = N
ws = _api_get(api_base, f"/workplans/{ws_id}")
if ws is None:
wp_id = str(meta.get("id", "")).strip()
if wp_id and ws_id == _derived_work_record_uuid(wp_id):
if wp_id and (
ws_id == _derived_work_record_uuid(wp_id) or bootstrap_empty_projection
):
# A deterministic file identifier missing from a replaceable
# projection is registration work, not a stale reference.
report.add(
severity="WARN",
check_id="C-06",
message=(
f"Derived workplan {ws_id[:8]}… is absent from this projection"
f"Authoritative workplan {ws_id[:8]}… is absent from this "
+ (
"explicitly empty projection"
if bootstrap_empty_projection
else "projection"
)
),
file_path=fname,
db_id=ws_id,
@ -2755,10 +2789,16 @@ def fix_repo(
repo_slug: str,
repo_path_override: str | None = None,
no_writeback: bool = False,
bootstrap_empty_projection: bool = False,
) -> ConsistencyReport:
"""Run checks then apply all auto-fixable issues. Returns updated report."""
report = check_repo(api_base, repo_slug, repo_path_override)
if any(i.check_id == "C-00" for i in report.failures):
report = check_repo(
api_base,
repo_slug,
repo_path_override,
bootstrap_empty_projection=bootstrap_empty_projection,
)
if any(i.check_id in {"C-00", "C-36"} for i in report.failures):
return report
# RMGR-WP-0002: optional repo-manager reconcile proxy (index rebuild) for pilots
@ -3675,6 +3715,14 @@ def main() -> None:
f"(default: {DEFAULT_REMOTE_ALL_MAX_SECONDS}; 0 disables)")
parser.add_argument("--no-writeback", action="store_true", dest="no_writeback",
help="Disable DB→file status writeback (C-15) while keeping other fixes")
parser.add_argument(
"--bootstrap-empty-projection",
action="store_true",
help=(
"Rebuild authoritative legacy UUIDs only after proving the target "
"repository has zero projected workplans"
),
)
parser.add_argument("--archive-closed", action="store_true",
help="Move closed root workplans to workplans/archived/YYMMDD-*.md")
parser.add_argument("--archive-workplan", metavar="ID_OR_FILE", default=None,
@ -3712,6 +3760,11 @@ def main() -> None:
import os as _os
no_wb = getattr(args, "no_writeback", False)
do_fix = args.fix or args.remote
if args.bootstrap_empty_projection and (not do_fix or args.all or args.remote):
parser.error(
"--bootstrap-empty-projection requires --fix for one local repository "
"and cannot be combined with --all or --remote"
)
# --here: infer slug from git remote URL, then run as single-repo check/fix
if args.here is not None:
@ -3730,7 +3783,15 @@ def main() -> None:
inferred_slug, git_root = inferred
print(f" Detected: {inferred_slug} ({git_root})")
if do_fix:
reports = [fix_repo(args.api_base, inferred_slug, git_root, no_writeback=no_wb)]
reports = [
fix_repo(
args.api_base,
inferred_slug,
git_root,
no_writeback=no_wb,
bootstrap_empty_projection=args.bootstrap_empty_projection,
)
]
else:
reports = [check_repo(args.api_base, inferred_slug, git_root)]
if args.archive_closed:
@ -3792,7 +3853,13 @@ def main() -> None:
reports = [report]
elif do_fix:
reports = [
fix_repo(args.api_base, slug, path_override, no_writeback=no_wb)
fix_repo(
args.api_base,
slug,
path_override,
no_writeback=no_wb,
bootstrap_empty_projection=args.bootstrap_empty_projection,
)
for slug in repo_slugs
]
else: