feat(RMGR-WP-0008): add workplan and register receiving surfaces
This commit is contained in:
parent
5502afc1fd
commit
859df9aae7
15 changed files with 1501 additions and 11 deletions
|
|
@ -15,6 +15,16 @@ from repo_manager.commands.rapp import validate as rapp_validate
|
|||
from repo_manager.commands.rapp import wrap as rapp_wrap
|
||||
|
||||
|
||||
def _json_object(raw: str) -> dict:
|
||||
try:
|
||||
value = json.loads(raw)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise argparse.ArgumentTypeError(str(exc)) from exc
|
||||
if not isinstance(value, dict):
|
||||
raise argparse.ArgumentTypeError("value must be a JSON object")
|
||||
return value
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="rmgr",
|
||||
|
|
@ -62,6 +72,98 @@ def main(argv: list[str] | None = None) -> int:
|
|||
help="Patch file only (invalid as full applied evidence; for tests)",
|
||||
)
|
||||
|
||||
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")
|
||||
p_wp_create.add_argument("--path", default=".")
|
||||
p_wp_create.add_argument("--workplan-id", required=True)
|
||||
p_wp_create.add_argument("--title", required=True)
|
||||
p_wp_create.add_argument("--goal", required=True)
|
||||
p_wp_create.add_argument(
|
||||
"--status",
|
||||
choices=["proposed", "ready", "active", "blocked", "backlog", "finished", "archived"],
|
||||
default="proposed",
|
||||
)
|
||||
p_wp_create.add_argument("--owner", default="codex")
|
||||
p_wp_create.add_argument("--domain", default=None)
|
||||
p_wp_create.add_argument("--topic-slug", default=None)
|
||||
p_wp_create.add_argument("--filename", default=None)
|
||||
|
||||
p_wp_update = wp_sub.add_parser("update", help="Update workplan metadata or status")
|
||||
p_wp_update.add_argument("--path", default=".")
|
||||
p_wp_update.add_argument("--workplan-id", required=True)
|
||||
p_wp_update.add_argument("--title", default=None)
|
||||
p_wp_update.add_argument(
|
||||
"--status",
|
||||
choices=["proposed", "ready", "active", "blocked", "backlog", "finished", "archived"],
|
||||
default=None,
|
||||
)
|
||||
p_wp_update.add_argument("--owner", default=None)
|
||||
p_wp_update.add_argument("--domain", default=None)
|
||||
p_wp_update.add_argument("--topic-slug", default=None)
|
||||
|
||||
p_wp_archive = wp_sub.add_parser(
|
||||
"delete",
|
||||
help="Recoverable delete: set archived and move to workplans/archived",
|
||||
)
|
||||
p_wp_archive.add_argument("--path", default=".")
|
||||
p_wp_archive.add_argument("--workplan-id", required=True)
|
||||
p_wp_archive.add_argument(
|
||||
"--confirm",
|
||||
action="store_true",
|
||||
help="Confirm moving the workplan to the dated archive",
|
||||
)
|
||||
|
||||
for wp_parser in (p_wp_create, p_wp_update, p_wp_archive):
|
||||
wp_parser.add_argument("--reason", default="rmgr CLI")
|
||||
wp_parser.add_argument("--correlation-id", default=None)
|
||||
wp_parser.add_argument("--idempotency-key", default=None)
|
||||
wp_parser.add_argument("--expected-head-sha", default=None)
|
||||
wp_parser.add_argument("--slug", default=None)
|
||||
wp_parser.add_argument("--push", action="store_true")
|
||||
wp_parser.add_argument("--no-commit", action="store_true")
|
||||
|
||||
register_kinds = [
|
||||
"sbom-inventory",
|
||||
"repo-goals",
|
||||
"upstream-contributions",
|
||||
"technical-debt",
|
||||
"extension-points",
|
||||
"register-entries",
|
||||
]
|
||||
p_register = sub.add_parser("register", help="Repository-owned register spine")
|
||||
register_sub = p_register.add_subparsers(dest="register_command")
|
||||
p_reg_list = register_sub.add_parser("list", help="List indexed register entries")
|
||||
p_reg_list.add_argument("--path", default=".")
|
||||
p_reg_list.add_argument("--kind", choices=register_kinds, default=None)
|
||||
p_reg_list.add_argument("--slug", default=None)
|
||||
p_reg_put = register_sub.add_parser("put", help="Create or update a register entry")
|
||||
p_reg_put.add_argument("--path", default=".")
|
||||
p_reg_put.add_argument("--kind", required=True, choices=register_kinds)
|
||||
p_reg_put.add_argument("--entry-id", required=True)
|
||||
p_reg_put.add_argument("--title", default=None)
|
||||
p_reg_put.add_argument("--status", default=None)
|
||||
p_reg_put.add_argument("--data-json", type=_json_object, default=None)
|
||||
p_reg_defer = register_sub.add_parser("defer", help="Defer a register entry")
|
||||
p_reg_defer.add_argument("--path", default=".")
|
||||
p_reg_defer.add_argument("--kind", required=True, choices=register_kinds)
|
||||
p_reg_defer.add_argument("--entry-id", required=True)
|
||||
p_reg_defer.add_argument("--status", default="deferred")
|
||||
p_reg_note = register_sub.add_parser("note", help="Append a note to a register entry")
|
||||
p_reg_note.add_argument("--path", default=".")
|
||||
p_reg_note.add_argument("--kind", required=True, choices=register_kinds)
|
||||
p_reg_note.add_argument("--entry-id", required=True)
|
||||
p_reg_note.add_argument("--note", required=True)
|
||||
p_reg_note.add_argument("--author", default="codex")
|
||||
for register_parser in (p_reg_put, p_reg_defer, p_reg_note):
|
||||
register_parser.add_argument("--reason", default="rmgr CLI")
|
||||
register_parser.add_argument("--correlation-id", default=None)
|
||||
register_parser.add_argument("--idempotency-key", default=None)
|
||||
register_parser.add_argument("--expected-head-sha", default=None)
|
||||
register_parser.add_argument("--slug", default=None)
|
||||
register_parser.add_argument("--push", action="store_true")
|
||||
register_parser.add_argument("--no-commit", action="store_true")
|
||||
|
||||
add_rapp_parser(sub)
|
||||
|
||||
p_conf = sub.add_parser("conform", help="Check a repository against flavor standards")
|
||||
|
|
@ -152,6 +254,72 @@ 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 == "workplan":
|
||||
from repo_manager.commands.workplan import mutate_workplan
|
||||
|
||||
if not args.workplan_command:
|
||||
p_wp.print_help()
|
||||
return 2
|
||||
operation = "archive" if args.workplan_command == "delete" else args.workplan_command
|
||||
result = mutate_workplan(
|
||||
Path(args.path),
|
||||
args.workplan_id,
|
||||
operation=operation,
|
||||
title=getattr(args, "title", None),
|
||||
goal=getattr(args, "goal", None),
|
||||
status=getattr(args, "status", None),
|
||||
owner=getattr(args, "owner", None),
|
||||
topic_slug=getattr(args, "topic_slug", None),
|
||||
domain=getattr(args, "domain", None),
|
||||
filename=getattr(args, "filename", None),
|
||||
confirm_archive=getattr(args, "confirm", False),
|
||||
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 == "register":
|
||||
if not args.register_command:
|
||||
p_register.print_help()
|
||||
return 2
|
||||
if args.register_command == "list":
|
||||
from repo_manager.observe import observe_repository
|
||||
|
||||
_snapshot, index = observe_repository(Path(args.path), slug=args.slug)
|
||||
prefix = f"register:{args.kind}" if args.kind else "register:"
|
||||
entries = [r.__dict__ for r in index.work_records if r.kind.startswith(prefix)]
|
||||
print(json.dumps({"ok": True, "entries": entries}, indent=2))
|
||||
return 0
|
||||
|
||||
from repo_manager.commands.register import mutate_register_entry
|
||||
|
||||
result = mutate_register_entry(
|
||||
Path(args.path),
|
||||
args.kind,
|
||||
args.entry_id,
|
||||
operation="upsert" if args.register_command == "put" else args.register_command,
|
||||
title=getattr(args, "title", None),
|
||||
status=getattr(args, "status", None),
|
||||
data=getattr(args, "data_json", None),
|
||||
note=getattr(args, "note", None),
|
||||
note_author=getattr(args, "author", None),
|
||||
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 == "rapp":
|
||||
if args.rapp_command == "init":
|
||||
result = rapp_init(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue