scaffold.py
python
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