feat: add governed fast work-record sync

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a053ff-1d6f-7fe2-ac1c-a6eb40a42a0c
This commit is contained in:
tegwick 2026-08-30 22:38:54 +02:00
parent 7ea7690dfc
commit 58414404d6
10 changed files with 1280 additions and 32 deletions

View file

@ -6,6 +6,7 @@ import argparse
import json
import os
import sys
from datetime import date
from pathlib import Path
from repo_manager.commands.rapp import add_rapp_parser
@ -129,6 +130,67 @@ def main(argv: list[str] | None = None) -> int:
help="Patch file only (invalid as full applied evidence; for tests)",
)
p_task = sub.add_parser("task", help="Governed file-backed task mutations")
task_sub = p_task.add_subparsers(dest="task_command")
p_task_add = task_sub.add_parser("add", help="Append a task to a workplan")
p_task_add.add_argument("--path", default=".")
p_task_add.add_argument("--workplan-id", required=True)
p_task_add.add_argument("--task-id", default=None)
p_task_add.add_argument("--title", required=True)
p_task_add.add_argument("--description", required=True)
p_task_add.add_argument("--status", choices=["wait", "todo", "progress", "done", "cancel"], default="todo")
p_task_add.add_argument("--priority", choices=["low", "medium", "high", "critical"], default="medium")
p_task_add.add_argument("--needs-human", action="store_true")
p_task_add.add_argument("--intervention-note", default=None)
p_task_add.add_argument("--blocking-reason", default=None)
p_task_update = task_sub.add_parser("update", help="Update task metadata and lifecycle")
p_task_update.add_argument("--path", default=".")
p_task_update.add_argument("--task-id", required=True)
p_task_update.add_argument("--title", default=None)
p_task_update.add_argument("--description", default=None)
p_task_update.add_argument("--status", choices=["wait", "todo", "progress", "done", "cancel"], default=None)
p_task_update.add_argument("--priority", choices=["low", "medium", "high", "critical"], default=None)
p_task_update.add_argument("--needs-human", action=argparse.BooleanOptionalAction, default=None)
p_task_update.add_argument("--intervention-note", default=None)
p_task_update.add_argument("--blocking-reason", default=None)
for task_parser in (p_task_add, p_task_update):
task_parser.add_argument("--reason", default="rmgr CLI")
task_parser.add_argument("--correlation-id", default=None)
task_parser.add_argument("--idempotency-key", default=None)
task_parser.add_argument("--expected-head-sha", default=None)
task_parser.add_argument("--slug", default=None)
task_parser.add_argument("--push", action="store_true")
task_parser.add_argument("--no-commit", action="store_true")
p_adhoc = sub.add_parser("adhoc", help="Daily repository-qualified ad-hoc work")
adhoc_sub = p_adhoc.add_subparsers(dest="adhoc_command")
p_adhoc_add = adhoc_sub.add_parser("add", help="Create/reuse today's ad-hoc and append a task")
p_adhoc_add.add_argument("--path", default=".")
p_adhoc_add.add_argument("--title", required=True)
p_adhoc_add.add_argument("--description", required=True)
p_adhoc_add.add_argument("--prefix", default=None)
p_adhoc_add.add_argument("--date", type=date.fromisoformat, default=None)
p_adhoc_add.add_argument("--status", choices=["wait", "todo", "progress", "done", "cancel"], default="todo")
p_adhoc_add.add_argument("--priority", choices=["low", "medium", "high", "critical"], default="medium")
p_adhoc_add.add_argument("--owner", default="codex")
p_adhoc_add.add_argument("--reason", default="rmgr CLI")
p_adhoc_add.add_argument("--correlation-id", default=None)
p_adhoc_add.add_argument("--idempotency-key", default=None)
p_adhoc_add.add_argument("--expected-head-sha", default=None)
p_adhoc_add.add_argument("--slug", default=None)
p_adhoc_add.add_argument("--push", action="store_true")
p_adhoc_add.add_argument("--no-commit", action="store_true")
p_sync = sub.add_parser("sync", help="Fast authoritative forge projection reconciliation")
p_sync.add_argument("--path", default=".")
p_sync.add_argument("--slug", default=None)
p_sync.add_argument("--api-base", default=os.environ.get("STATE_HUB_API_BASE", "http://127.0.0.1:8000"))
p_sync.add_argument("--expected-instance-label", default="railliance01")
p_sync.add_argument("--push", action="store_true", help="Push ahead commits before reconciliation")
p_sync.add_argument("--no-commit-identifiers", action="store_true")
p_sync.add_argument("--acknowledge-retirements", action="store_true")
p_sync.add_argument("--no-queue", action="store_true")
p_wp = sub.add_parser("workplan", help="Governed file-backed workplan mutations")
wp_sub = p_wp.add_subparsers(dest="workplan_command")
p_wp_create = wp_sub.add_parser("create", help="Create a workplan file")
@ -581,6 +643,77 @@ def main(argv: list[str] | None = None) -> int:
print(json.dumps(result.to_dict(), indent=2))
return 0 if result.status == "applied" else 1
if args.command == "task":
if not args.task_command:
p_task.print_help()
return 2
from repo_manager.commands.task import mutate_task
result = mutate_task(
Path(args.path),
args.task_id,
operation=args.task_command,
workplan_id=getattr(args, "workplan_id", None),
title=args.title,
description=args.description,
status=args.status,
priority=args.priority,
needs_human=args.needs_human,
intervention_note=args.intervention_note,
blocking_reason=args.blocking_reason,
correlation_id=args.correlation_id,
reason=args.reason,
commit=not args.no_commit,
push=args.push,
expected_head_sha=args.expected_head_sha,
idempotency_key=args.idempotency_key,
repo_slug=args.slug,
)
print(json.dumps(result.to_dict(), indent=2))
return 0 if result.status == "applied" else 1
if args.command == "adhoc":
if args.adhoc_command != "add":
p_adhoc.print_help()
return 2
from repo_manager.commands.task import add_adhoc_task
result = add_adhoc_task(
Path(args.path),
args.title,
args.description,
prefix=args.prefix,
on_date=args.date,
status=args.status,
priority=args.priority,
owner=args.owner,
correlation_id=args.correlation_id,
reason=args.reason,
commit=not args.no_commit,
push=args.push,
expected_head_sha=args.expected_head_sha,
idempotency_key=args.idempotency_key,
repo_slug=args.slug,
)
print(json.dumps(result.to_dict(), indent=2))
return 0 if result.status == "applied" else 1
if args.command == "sync":
from repo_manager.projection_sync import sync_repository_projection
result = sync_repository_projection(
Path(args.path),
api_base=args.api_base,
repo_slug=args.slug,
expected_instance_label=args.expected_instance_label,
commit_identifiers=not args.no_commit_identifiers,
push=args.push,
acknowledge_retirements=args.acknowledge_retirements,
queue_on_unavailable=not args.no_queue,
)
print(json.dumps(result, indent=2))
return 0 if result.get("ok") else 1
if args.command == "workplan":
from repo_manager.commands.workplan import mutate_workplan