feat(ECR-024): OpsCMS Banner 只读并 Closed
运营横幅目录(ops_banners + admin /cms),锁定 024–040 全队列。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Executable
+172
@@ -0,0 +1,172 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user