#!/usr/bin/env python3 """Scaffold ESS thin-slice artifacts for Ops Loop (docs only).""" import argparse from pathlib import Path ROOT = Path(__file__).resolve().parents[1] def write(path: Path, text: str): path.parent.mkdir(parents=True, exist_ok=True) path.write_text(text.strip() + "\n", encoding="utf-8") print("wrote", path.relative_to(ROOT)) def main(): p = argparse.ArgumentParser() p.add_argument("--ecr", required=True) # 024 p.add_argument("--slug", required=True) # banner p.add_argument("--title", required=True) p.add_argument("--capability", required=True) p.add_argument("--bc", required=True) p.add_argument("--concept", required=True) p.add_argument("--predecessor", required=True) # ECR-023 p.add_argument("--migration", required=True) # 000025 p.add_argument("--perm", required=True) p.add_argument("--apis", required=True, help="comma paths like GET /admin/cms/banners|GET /admin/cms/banners/{id}") p.add_argument("--non-goals", required=True) p.add_argument("--seed", default="") p.add_argument("--new-perm", action="store_true") args = p.parse_args() ecr = f"ECR-{args.ecr}" bd = f"BD-2026-{args.ecr}" spec = f"ops-{args.slug}.md" apis = [a.strip() for a in args.apis.split("|")] write(ROOT / f".ai/product/feature-spec/{spec}", f""" # Feature Spec: {args.capability} · {args.concept}(Ops · {ecr}) > Status: `Active`(Loop continuous · **{ecr}**) > Parent: WAVE0-FROZEN · Predecessor: {args.predecessor} Closed > Capability: `{args.capability}` · BC: `{args.bc}` > 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md` ## Non-goals {args.non_goals} ## L2 Domain | 概念 | 语义 | |------|------| | `{args.concept}` | 本切片只读目录;code 唯一(若适用) | ## L3 API | Method | Path | 权限 | 语义 | |--------|------|------|------| """ + "\n".join( f"| {a.split()[0]} | `{a.split()[1]}` | `{args.perm}` | 只读 |" for a in apis ) + f""" ## Migration `{args.migration}`:表 + 种子(若有){' + 授予 ' + args.perm if args.new_perm else '(权限复用)'} ## L4 AC | ID | Then | |----|------| | AC-F-01 | list 含种子或空列表合法 | | AC-F-02 | 已知 id get 200 | | AC-F-03 | 未知 id → 404 | | AC-S-01 | 无 Admin → 401 | | AC-S-02 | 无权限 → 403 | | AC-P-01 | list < 500ms | | AC-O-01 | N/A 只读 | contract_diff: `docs/CONTRACT_DIFF/{ecr}.yaml` """) write(ROOT / f"docs/ECR/{ecr}-{args.slug}.md", f""" # {ecr} **Title:** {args.title} **Status:** **Approved**(Coding authorized · Loop continuous) **Parent:** WAVE0-FROZEN · **Predecessor:** {args.predecessor} Closed **Change Level:** L2 ## Change {args.concept} 只读 · `{args.migration}` · `{args.perm}` ## Forbidden {args.non_goals} ## Linked Spec `{spec}` · {bd} · CONTRACT_DIFF/{ecr}.yaml """) write(ROOT / f"docs/BACKEND_DESIGN/{bd}-{args.slug}.md", f""" # Backend Design: {ecr} {args.concept} | ID | {bd} | | Status | Approved | | Coding | Loop authorized | | Level | L2 | | Migration | YES {args.migration} | ## Backend Change Boundary ```text Domain: {args.concept} (read) App: AdminHandler → admin.Service → AdminRepo API: {'; '.join(apis)} Permission: {args.perm} Migration: {args.migration} ``` ## Out of boundary {args.non_goals} Rollback: down migration + remove routes/UI """) api_yaml = "\n".join( f" - method: {a.split()[0]}\n path: /api/v1{a.split()[1]}\n change: added" for a in apis ) perm_line = f" - code: {args.perm}\n change: {'added' if args.new_perm else 'unchanged'}" write(ROOT / f"docs/CONTRACT_DIFF/{ecr}.yaml", f""" ecr: {ecr} capability: {args.capability} bounded_context: {args.bc} parent: WAVE0-FROZEN predecessor: {args.predecessor} change: type: additive breaking_change: false migration_required: true compatibility_notes: > Adds {args.concept} read catalog. Forbidden: UGC / real payment. apis: {api_yaml} perms: {perm_line} """) write(ROOT / f"docs/PRODUCT_SPEC/{ecr}-{args.slug}.md", f"# PRODUCT_SPEC — {ecr}\n\n对齐 {spec} · Approved · Loop · L2 · {args.concept} 只读") write(ROOT / f"docs/ENGINEERING_SPEC/{ecr}-{args.slug}.md", f"# ENGINEERING_SPEC — {ecr}\n\n1. migration {args.migration}\n2. AdminRepo/Service/Handler\n3. OpenAPI + admin-h5\n4. Integration · Closed") write(ROOT / f"docs/HANDOFF/{ecr}-architect-to-engineer.md", f"# HANDOFF — {ecr} Architect → Engineer\n\nLoop continuous · Approved + Coding. Migration {args.migration}. Forbidden: UGC/真支付.") write(ROOT / f"docs/STATE/{ecr}.md", f"# STATE — {ecr}\n\n| Status | **Approved** · Coding |\n| Phase | coding |\n| Spec | {spec} |\n| Updated | 2026-08-08 |") write(ROOT / f"docs/TASKS/TASK-{args.ecr}-{ecr.replace('-','')}.yaml", f""" id: TASK-{args.ecr}-{ecr.replace('-','')} ecr: {ecr} title: {args.title} role: engineer status: active change_level: L2 parent: WAVE0-FROZEN predecessor: {args.predecessor} acceptance: - Spec AC mapped - {args.concept} read only - No UGC / payment """) if __name__ == "__main__": main()