feat(consistency): bootstrap empty repo projections
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
parent
52a7d6bb73
commit
03c7924b7f
3 changed files with 187 additions and 7 deletions
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue