scaffold.py python
165 lines 5.3 KB
Raw
sha256:0e9549ec7b463911bc08b7d586dc320b1ac9b1f5c943ee7e3865dcc6cb0f6f83 chore(governance): sync handover+roadmap to 84db8c8 (drift:… Human 2 days ago
1 """Scaffold a side-check freeze artifact for ad-hoc / Thinking honesty gates.
2
3 Does **not** open a ``docs.lanes`` entry. Reuses the same §6 freeze declaration
4 shape that ``ok review --freeze`` already validates.
5 """
6
7 from __future__ import annotations
8
9 import re
10 from dataclasses import dataclass
11 from datetime import date
12 from pathlib import Path
13
14
15 DEFAULT_REVIEWS_DIR = "docs/reviews"
16
17 _SLUG_RE = re.compile(r"[^a-z0-9]+")
18
19
20 def slugify_topic(topic: str) -> str:
21 """Return a filesystem-safe lowercase slug for a side-check topic."""
22 cleaned = _SLUG_RE.sub("-", topic.strip().lower()).strip("-")
23 return cleaned or "side-check"
24
25
26 @dataclass(frozen=True)
27 class ScaffoldResult:
28 """Outcome of creating or reusing a side-check artifact."""
29
30 path: Path
31 rel_path: str
32 created: bool
33
34
35 def render_side_check_markdown(
36 *,
37 topic: str,
38 phase_id: str,
39 scope: str,
40 output_path: str,
41 date_stamp: str | None = None,
42 ) -> str:
43 """Render a minimal §6.1 freeze artifact suitable for ``ok review --freeze``."""
44 day = date_stamp or date.today().isoformat()
45 scope_body = scope.strip() or (
46 "Describe the work under review: intent, files touched, fail-closed rules, "
47 "and the seven-tier test plan (unit, integration, e2e, stress, data-integrity, "
48 "performance, security)."
49 )
50 return (
51 f"# Side check — {topic}\n"
52 f"\n"
53 f"**Date:** {day} \n"
54 f"**Kind:** ad-hoc Check OK (not a roadmap lane) \n"
55 f"**Honesty:** same Freeze-Contract + build-verification path as roadmap phases\n"
56 f"\n"
57 f"## Freeze-contract declaration\n"
58 f"\n"
59 f"```yaml\n"
60 f"phase: {phase_id}\n"
61 f"outputs:\n"
62 f" - id: side-check\n"
63 f" path: {output_path}\n"
64 f" frozen: true\n"
65 f"frozen_inputs: []\n"
66 f"```\n"
67 f"\n"
68 f"## Scope\n"
69 f"\n"
70 f"{scope_body}\n"
71 f"\n"
72 f"## Ground-truth edge\n"
73 f"\n"
74 f"Downstream Auto / implementation sessions may treat this document as ground truth\n"
75 f"for the scoped work without re-deriving the contract. This is **not** a new\n"
76 f"``docs.lanes`` baton — promote to a lane only if the concern becomes durable.\n"
77 f"\n"
78 f"## Test matrix (seven tiers)\n"
79 f"\n"
80 f"| Tier | Expectation |\n"
81 f"| --- | --- |\n"
82 f"| unit | Core logic covered |\n"
83 f"| integration | CLI / module seams covered |\n"
84 f"| e2e | Full operator path covered |\n"
85 f"| stress | Bounded / non-pathological under load |\n"
86 f"| data-integrity | No silent corruption of declared state |\n"
87 f"| performance | Completes within local budget |\n"
88 f"| security | No secrets, path escape, or injection surfaces |\n"
89 f"\n"
90 f"Every freeze-review finding MUST cite **file+line** (SPEC §6).\n"
91 f"\n"
92 f"## Review record\n"
93 f"\n"
94 f"| Round | Reviewer | Verdict | Resolution |\n"
95 f"| --- | --- | --- | --- |\n"
96 f"| — | — | pending | Check-if-OK scaffold created |\n"
97 f"\n"
98 )
99
100
101 def resolve_artifact_path(
102 repo_root: Path,
103 *,
104 path: str | None = None,
105 topic: str | None = None,
106 reviews_dir: str = DEFAULT_REVIEWS_DIR,
107 today: date | None = None,
108 ) -> Path:
109 """Resolve where the side-check artifact should live (absolute under repo_root)."""
110 if path:
111 candidate = Path(path)
112 if candidate.is_absolute():
113 return candidate
114 return (repo_root / candidate).resolve()
115 day = (today or date.today()).isoformat()
116 slug = slugify_topic(topic or "side-check")
117 return (repo_root / reviews_dir / f"{day}-{slug}.md").resolve()
118
119
120 def scaffold_side_check(
121 repo_root: Path,
122 *,
123 path: str | None = None,
124 topic: str | None = None,
125 scope: str = "",
126 reviews_dir: str = DEFAULT_REVIEWS_DIR,
127 overwrite: bool = False,
128 today: date | None = None,
129 ) -> ScaffoldResult:
130 """Create a side-check freeze doc if missing; return path + created flag.
131
132 Raises:
133 FileExistsError: when the target exists and ``overwrite`` is False and
134 the caller asked to force a new scaffold via a missing path that
135 already exists — actually we reuse existing files without error.
136 ValueError: when resolved path escapes ``repo_root``.
137 """
138 target = resolve_artifact_path(
139 repo_root,
140 path=path,
141 topic=topic,
142 reviews_dir=reviews_dir,
143 today=today,
144 )
145 try:
146 target.relative_to(repo_root.resolve())
147 except ValueError as exc:
148 raise ValueError("path-escape") from exc
149
150 rel = target.relative_to(repo_root.resolve()).as_posix()
151 if target.is_file() and not overwrite:
152 return ScaffoldResult(path=target, rel_path=rel, created=False)
153
154 topic_label = (topic or target.stem).strip() or "side-check"
155 phase_id = f"check-ok-{slugify_topic(topic_label)}"
156 body = render_side_check_markdown(
157 topic=topic_label,
158 phase_id=phase_id,
159 scope=scope,
160 output_path=rel,
161 date_stamp=(today or date.today()).isoformat(),
162 )
163 target.parent.mkdir(parents=True, exist_ok=True)
164 target.write_text(body, encoding="utf-8")
165 return ScaffoldResult(path=target, rel_path=rel, created=True)
File History 1 commit
sha256:6abcf1fa82a7a621ccbc945f19acdba5bc0db54569599404a1452fb4a096a199 fix(ISR): default require_independent_second_reviewer to require Human minor 2 days ago