specsmith 0.3.6.dev179__py3-none-any.whl → 0.3.6.dev182__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- specsmith/auditor.py +44 -4
- specsmith/cli.py +29 -7
- specsmith/differ.py +15 -1
- specsmith/exporter.py +2 -1
- specsmith/importer.py +30 -7
- specsmith/phase.py +6 -6
- specsmith/scaffolder.py +13 -2
- specsmith/templates/agents.md.j2 +249 -230
- specsmith/templates/governance/context-budget.md.j2 +2 -1
- specsmith/templates/governance/lifecycle.md.j2 +44 -0
- specsmith/templates/governance/{workflow.md.j2 → session-protocol.md.j2} +1 -1
- specsmith/templates/readme.md.j2 +1 -1
- specsmith/upgrader.py +27 -5
- {specsmith-0.3.6.dev179.dist-info → specsmith-0.3.6.dev182.dist-info}/METADATA +21 -11
- {specsmith-0.3.6.dev179.dist-info → specsmith-0.3.6.dev182.dist-info}/RECORD +19 -19
- specsmith/templates/docs/workflow.md.j2 +0 -15
- {specsmith-0.3.6.dev179.dist-info → specsmith-0.3.6.dev182.dist-info}/WHEEL +0 -0
- {specsmith-0.3.6.dev179.dist-info → specsmith-0.3.6.dev182.dist-info}/entry_points.txt +0 -0
- {specsmith-0.3.6.dev179.dist-info → specsmith-0.3.6.dev182.dist-info}/licenses/LICENSE +0 -0
- {specsmith-0.3.6.dev179.dist-info → specsmith-0.3.6.dev182.dist-info}/top_level.txt +0 -0
specsmith/auditor.py
CHANGED
|
@@ -57,7 +57,8 @@ REQUIRED_FILES = [
|
|
|
57
57
|
|
|
58
58
|
GOVERNANCE_FILES = [
|
|
59
59
|
"docs/governance/RULES.md",
|
|
60
|
-
"docs/governance/
|
|
60
|
+
"docs/governance/SESSION-PROTOCOL.md",
|
|
61
|
+
"docs/governance/LIFECYCLE.md",
|
|
61
62
|
"docs/governance/ROLES.md",
|
|
62
63
|
"docs/governance/CONTEXT-BUDGET.md",
|
|
63
64
|
"docs/governance/VERIFICATION.md",
|
|
@@ -331,7 +332,8 @@ def check_ledger_health(root: Path) -> list[AuditResult]:
|
|
|
331
332
|
_DEFAULT_THRESHOLDS: dict[str, int] = {
|
|
332
333
|
"AGENTS.md": 200,
|
|
333
334
|
"docs/governance/RULES.md": 800,
|
|
334
|
-
"docs/governance/
|
|
335
|
+
"docs/governance/SESSION-PROTOCOL.md": 400,
|
|
336
|
+
"docs/governance/LIFECYCLE.md": 200,
|
|
335
337
|
"docs/governance/ROLES.md": 300,
|
|
336
338
|
"docs/governance/CONTEXT-BUDGET.md": 300,
|
|
337
339
|
"docs/governance/VERIFICATION.md": 400,
|
|
@@ -342,12 +344,12 @@ _DEFAULT_THRESHOLDS: dict[str, int] = {
|
|
|
342
344
|
_TYPE_THRESHOLD_OVERRIDES: dict[str, dict[str, int]] = {
|
|
343
345
|
"fpga-rtl": {
|
|
344
346
|
"docs/governance/RULES.md": 1000,
|
|
345
|
-
"docs/governance/
|
|
347
|
+
"docs/governance/SESSION-PROTOCOL.md": 500,
|
|
346
348
|
"docs/governance/VERIFICATION.md": 600,
|
|
347
349
|
},
|
|
348
350
|
"yocto-bsp": {
|
|
349
351
|
"docs/governance/RULES.md": 1000,
|
|
350
|
-
"docs/governance/
|
|
352
|
+
"docs/governance/SESSION-PROTOCOL.md": 500,
|
|
351
353
|
"docs/governance/VERIFICATION.md": 500,
|
|
352
354
|
},
|
|
353
355
|
"embedded-hardware": {
|
|
@@ -579,6 +581,43 @@ def check_trace_chain_integrity(root: Path) -> list[AuditResult]:
|
|
|
579
581
|
return []
|
|
580
582
|
|
|
581
583
|
|
|
584
|
+
def check_phase_readiness(root: Path) -> list[AuditResult]:
|
|
585
|
+
"""Check AEE phase readiness (advisory — failed checks are warnings)."""
|
|
586
|
+
results: list[AuditResult] = []
|
|
587
|
+
scaffold_path = root / "scaffold.yml"
|
|
588
|
+
if not scaffold_path.exists():
|
|
589
|
+
return results
|
|
590
|
+
|
|
591
|
+
from specsmith.phase import PHASE_MAP, evaluate_phase, read_phase
|
|
592
|
+
|
|
593
|
+
phase_key = read_phase(root)
|
|
594
|
+
phase = PHASE_MAP.get(phase_key)
|
|
595
|
+
if not phase:
|
|
596
|
+
return results
|
|
597
|
+
|
|
598
|
+
passed, failed = evaluate_phase(phase, root)
|
|
599
|
+
pct = int(len(passed) / len(phase.checks) * 100) if phase.checks else 100
|
|
600
|
+
|
|
601
|
+
if failed:
|
|
602
|
+
results.append(
|
|
603
|
+
AuditResult(
|
|
604
|
+
name="phase-readiness",
|
|
605
|
+
passed=True, # advisory — don't fail the audit
|
|
606
|
+
message=(
|
|
607
|
+
f"Phase {phase.emoji} {phase.label}: {pct}% ready "
|
|
608
|
+
f"({len(failed)} check(s) remaining: {', '.join(failed[:3])})"
|
|
609
|
+
),
|
|
610
|
+
)
|
|
611
|
+
)
|
|
612
|
+
else:
|
|
613
|
+
msg = f"Phase {phase.emoji} {phase.label}: 100% ready"
|
|
614
|
+
if phase.next_phase:
|
|
615
|
+
msg += f" — run `specsmith phase next` to advance to {phase.next_phase}"
|
|
616
|
+
results.append(AuditResult(name="phase-readiness", passed=True, message=msg))
|
|
617
|
+
|
|
618
|
+
return results
|
|
619
|
+
|
|
620
|
+
|
|
582
621
|
def run_audit(root: Path) -> AuditReport:
|
|
583
622
|
"""Run all audit checks and return a report."""
|
|
584
623
|
report = AuditReport()
|
|
@@ -589,6 +628,7 @@ def run_audit(root: Path) -> AuditReport:
|
|
|
589
628
|
report.results.extend(check_tool_configuration(root))
|
|
590
629
|
report.results.extend(check_type_mismatch(root))
|
|
591
630
|
report.results.extend(check_trace_chain_integrity(root))
|
|
631
|
+
report.results.extend(check_phase_readiness(root))
|
|
592
632
|
return report
|
|
593
633
|
|
|
594
634
|
|
specsmith/cli.py
CHANGED
|
@@ -184,6 +184,11 @@ def init(config_path: str | None, output_dir: str, no_git: bool, guided: bool) -
|
|
|
184
184
|
with open(config_out, "w") as fh:
|
|
185
185
|
yaml.dump(cfg.model_dump(mode="json"), fh, default_flow_style=False, sort_keys=False)
|
|
186
186
|
|
|
187
|
+
# Ensure AEE phase is set (write_phase appends to scaffold.yml)
|
|
188
|
+
from specsmith.phase import write_phase
|
|
189
|
+
|
|
190
|
+
write_phase(target, "inception")
|
|
191
|
+
|
|
187
192
|
|
|
188
193
|
def _load_config_with_inheritance(config_path: str) -> dict[str, object]:
|
|
189
194
|
"""Load scaffold.yml, merging parent config if `extends` is set."""
|
|
@@ -641,7 +646,8 @@ def import_project(
|
|
|
641
646
|
"docs/ARCHITECTURE.md",
|
|
642
647
|
"scaffold.yml",
|
|
643
648
|
"docs/governance/RULES.md",
|
|
644
|
-
"docs/governance/
|
|
649
|
+
"docs/governance/SESSION-PROTOCOL.md",
|
|
650
|
+
"docs/governance/LIFECYCLE.md",
|
|
645
651
|
"docs/governance/ROLES.md",
|
|
646
652
|
"docs/governance/CONTEXT-BUDGET.md",
|
|
647
653
|
"docs/governance/VERIFICATION.md",
|
|
@@ -682,6 +688,11 @@ def import_project(
|
|
|
682
688
|
yaml.dump(config.model_dump(mode="json"), fh, default_flow_style=False, sort_keys=False)
|
|
683
689
|
console.print(" [green]\u2713[/green] scaffold.yml")
|
|
684
690
|
|
|
691
|
+
# Ensure AEE phase is set
|
|
692
|
+
from specsmith.phase import write_phase
|
|
693
|
+
|
|
694
|
+
write_phase(root, "inception")
|
|
695
|
+
|
|
685
696
|
# Guided architecture definition after import
|
|
686
697
|
if guided:
|
|
687
698
|
guided_files = _run_guided_architecture(config, root)
|
|
@@ -3442,14 +3453,25 @@ def phase_status(project_dir: str) -> None:
|
|
|
3442
3453
|
|
|
3443
3454
|
|
|
3444
3455
|
@phase_group.command(name="list")
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3456
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
3457
|
+
def phase_list(project_dir: str) -> None:
|
|
3458
|
+
"""List all AEE lifecycle phases with current position highlighted."""
|
|
3459
|
+
from specsmith.phase import PHASES, read_phase
|
|
3448
3460
|
|
|
3449
|
-
|
|
3461
|
+
root = Path(project_dir).resolve()
|
|
3462
|
+
current = read_phase(root)
|
|
3463
|
+
|
|
3464
|
+
console.print("[bold]AEE Project Lifecycle[/bold]\n")
|
|
3465
|
+
console.print(
|
|
3466
|
+
" inception \u2192 architecture \u2192 requirements \u2192 test_spec "
|
|
3467
|
+
"\u2192 implementation \u2192 verification \u2192 release\n"
|
|
3468
|
+
)
|
|
3450
3469
|
for i, p in enumerate(PHASES, 1):
|
|
3451
|
-
|
|
3452
|
-
|
|
3470
|
+
is_cur = p.key == current
|
|
3471
|
+
marker = "[bold cyan]\u25b6[/bold cyan]" if is_cur else " "
|
|
3472
|
+
label = f"[bold cyan]{p.label:<20s}[/bold cyan]" if is_cur else f"{p.label:<20s}"
|
|
3473
|
+
console.print(f" {marker} {i}. {p.emoji} {label} {p.description}")
|
|
3474
|
+
console.print(f"\n Current: [bold]{current}[/bold]")
|
|
3453
3475
|
|
|
3454
3476
|
|
|
3455
3477
|
main.add_command(phase_group)
|
specsmith/differ.py
CHANGED
|
@@ -14,7 +14,8 @@ from specsmith.config import ProjectConfig
|
|
|
14
14
|
|
|
15
15
|
_GOVERNANCE_FILES: list[tuple[str, str]] = [
|
|
16
16
|
("governance/rules.md.j2", "docs/governance/RULES.md"),
|
|
17
|
-
("governance/
|
|
17
|
+
("governance/session-protocol.md.j2", "docs/governance/SESSION-PROTOCOL.md"),
|
|
18
|
+
("governance/lifecycle.md.j2", "docs/governance/LIFECYCLE.md"),
|
|
18
19
|
("governance/roles.md.j2", "docs/governance/ROLES.md"),
|
|
19
20
|
("governance/context-budget.md.j2", "docs/governance/CONTEXT-BUDGET.md"),
|
|
20
21
|
("governance/verification.md.j2", "docs/governance/VERIFICATION.md"),
|
|
@@ -47,13 +48,19 @@ def run_diff(root: Path) -> list[tuple[str, str]]:
|
|
|
47
48
|
lstrip_blocks=True,
|
|
48
49
|
)
|
|
49
50
|
|
|
51
|
+
from specsmith.phase import PHASE_MAP, read_phase
|
|
50
52
|
from specsmith.tools import get_tools
|
|
51
53
|
|
|
54
|
+
phase_key = read_phase(root)
|
|
55
|
+
phase_obj = PHASE_MAP.get(phase_key, PHASE_MAP["inception"])
|
|
52
56
|
ctx = {
|
|
53
57
|
"project": config,
|
|
54
58
|
"today": date.today().isoformat(),
|
|
55
59
|
"package_name": config.package_name,
|
|
56
60
|
"tools": get_tools(config),
|
|
61
|
+
"aee_phase": phase_key,
|
|
62
|
+
"aee_phase_label": phase_obj.label,
|
|
63
|
+
"aee_phase_emoji": phase_obj.emoji,
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
results: list[tuple[str, str]] = []
|
|
@@ -101,11 +108,18 @@ def run_diff_html(root: Path) -> str:
|
|
|
101
108
|
trim_blocks=True,
|
|
102
109
|
lstrip_blocks=True,
|
|
103
110
|
)
|
|
111
|
+
from specsmith.phase import PHASE_MAP, read_phase
|
|
112
|
+
|
|
113
|
+
phase_key = read_phase(root)
|
|
114
|
+
phase_obj = PHASE_MAP.get(phase_key, PHASE_MAP["inception"])
|
|
104
115
|
ctx = {
|
|
105
116
|
"project": config,
|
|
106
117
|
"today": date.today().isoformat(),
|
|
107
118
|
"package_name": config.package_name,
|
|
108
119
|
"tools": get_tools(config),
|
|
120
|
+
"aee_phase": phase_key,
|
|
121
|
+
"aee_phase_label": phase_obj.label,
|
|
122
|
+
"aee_phase_emoji": phase_obj.emoji,
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
differ = difflib.HtmlDiff(wrapcolumn=80)
|
specsmith/exporter.py
CHANGED
|
@@ -138,7 +138,8 @@ def run_export(root: Path) -> str:
|
|
|
138
138
|
"docs/TEST_SPEC.md",
|
|
139
139
|
"docs/ARCHITECTURE.md",
|
|
140
140
|
"docs/governance/RULES.md",
|
|
141
|
-
"docs/governance/
|
|
141
|
+
"docs/governance/SESSION-PROTOCOL.md",
|
|
142
|
+
"docs/governance/LIFECYCLE.md",
|
|
142
143
|
"docs/governance/ROLES.md",
|
|
143
144
|
"docs/governance/VERIFICATION.md",
|
|
144
145
|
]
|
specsmith/importer.py
CHANGED
|
@@ -877,8 +877,8 @@ def _extract_governance_sections(root: Path) -> dict[str, str]:
|
|
|
877
877
|
"H2: All proposals require human approval.\n"
|
|
878
878
|
"H3: The ledger is append-only.\n"
|
|
879
879
|
),
|
|
880
|
-
"
|
|
881
|
-
"#
|
|
880
|
+
"session-protocol": (
|
|
881
|
+
"# Session Protocol\n\n"
|
|
882
882
|
"1. Propose changes\n2. Get approval\n"
|
|
883
883
|
"3. Execute\n4. Verify\n5. Record in ledger\n"
|
|
884
884
|
),
|
|
@@ -935,7 +935,7 @@ def _extract_governance_sections(root: Path) -> dict[str, str]:
|
|
|
935
935
|
# Classify each section into a governance category
|
|
936
936
|
category_map: list[tuple[str, list[str]]] = [
|
|
937
937
|
("rules", _RULES_KW),
|
|
938
|
-
("
|
|
938
|
+
("session-protocol", _WORKFLOW_KW),
|
|
939
939
|
("roles", _ROLES_KW),
|
|
940
940
|
("context-budget", _CTX_KW),
|
|
941
941
|
("verification", _VERIFY_KW),
|
|
@@ -944,7 +944,7 @@ def _extract_governance_sections(root: Path) -> dict[str, str]:
|
|
|
944
944
|
|
|
945
945
|
buckets: dict[str, list[tuple[str, str]]] = {
|
|
946
946
|
"rules": [],
|
|
947
|
-
"
|
|
947
|
+
"session-protocol": [],
|
|
948
948
|
"roles": [],
|
|
949
949
|
"context-budget": [],
|
|
950
950
|
"verification": [],
|
|
@@ -1007,7 +1007,7 @@ def _extract_governance_sections(root: Path) -> dict[str, str]:
|
|
|
1007
1007
|
# Build output
|
|
1008
1008
|
titles = {
|
|
1009
1009
|
"rules": "# Rules",
|
|
1010
|
-
"
|
|
1010
|
+
"session-protocol": "# Session Protocol",
|
|
1011
1011
|
"roles": "# Roles",
|
|
1012
1012
|
"context-budget": "# Context Budget",
|
|
1013
1013
|
"verification": "# Verification",
|
|
@@ -1279,12 +1279,33 @@ def generate_overlay(
|
|
|
1279
1279
|
# Otherwise use generic stubs.
|
|
1280
1280
|
gov = _extract_governance_sections(target)
|
|
1281
1281
|
_write("docs/governance/RULES.md", gov["rules"])
|
|
1282
|
-
_write("docs/governance/
|
|
1282
|
+
_write("docs/governance/SESSION-PROTOCOL.md", gov["session-protocol"])
|
|
1283
|
+
_write(
|
|
1284
|
+
"docs/governance/LIFECYCLE.md",
|
|
1285
|
+
"# Project Lifecycle\n\n"
|
|
1286
|
+
"See `specsmith phase show` for current phase readiness.\n"
|
|
1287
|
+
"See `specsmith phase list` for the full AEE lifecycle.\n",
|
|
1288
|
+
)
|
|
1283
1289
|
_write("docs/governance/ROLES.md", gov["roles"])
|
|
1284
1290
|
_write("docs/governance/CONTEXT-BUDGET.md", gov["context-budget"])
|
|
1285
1291
|
_write("docs/governance/VERIFICATION.md", gov["verification"])
|
|
1286
1292
|
_write("docs/governance/DRIFT-METRICS.md", gov["drift-metrics"])
|
|
1287
1293
|
|
|
1294
|
+
# Migrate old WORKFLOW.md files if found
|
|
1295
|
+
import shutil as _shutil
|
|
1296
|
+
|
|
1297
|
+
old_gov_wf = target / "docs" / "governance" / "WORKFLOW.md"
|
|
1298
|
+
new_sp = target / "docs" / "governance" / "SESSION-PROTOCOL.md"
|
|
1299
|
+
if old_gov_wf.exists() and not new_sp.exists():
|
|
1300
|
+
_shutil.move(str(old_gov_wf), str(new_sp))
|
|
1301
|
+
created.append(new_sp)
|
|
1302
|
+
elif old_gov_wf.exists():
|
|
1303
|
+
old_gov_wf.unlink() # new file already exists, remove old one
|
|
1304
|
+
|
|
1305
|
+
old_doc_wf = target / "docs" / "WORKFLOW.md"
|
|
1306
|
+
if old_doc_wf.exists():
|
|
1307
|
+
old_doc_wf.unlink() # replaced by LIFECYCLE.md + phase system
|
|
1308
|
+
|
|
1288
1309
|
# If existing AGENTS.md is oversized, back it up and replace with a hub.
|
|
1289
1310
|
agents_path = target / "AGENTS.md"
|
|
1290
1311
|
if agents_path.exists():
|
|
@@ -1306,8 +1327,10 @@ def generate_overlay(
|
|
|
1306
1327
|
"| ---- | ------- | ----------- |\n"
|
|
1307
1328
|
"| `docs/governance/RULES.md` | Hard rules, stop conditions, "
|
|
1308
1329
|
"project-specific rules | Every session start |\n"
|
|
1309
|
-
"| `docs/governance/
|
|
1330
|
+
"| `docs/governance/SESSION-PROTOCOL.md` | Session lifecycle, "
|
|
1310
1331
|
"save/push protocol | Every session start |\n"
|
|
1332
|
+
"| `docs/governance/LIFECYCLE.md` | AEE phase lifecycle, "
|
|
1333
|
+
"readiness gates | Every session start |\n"
|
|
1311
1334
|
"| `docs/governance/ROLES.md` | Agent role boundaries | "
|
|
1312
1335
|
"Every session start |\n"
|
|
1313
1336
|
"| `docs/governance/CONTEXT-BUDGET.md` | Context management | "
|
specsmith/phase.py
CHANGED
|
@@ -184,7 +184,7 @@ PHASES: list[Phase] = [
|
|
|
184
184
|
"ARCHITECTURE.md has content",
|
|
185
185
|
_file_min_lines("docs/ARCHITECTURE.md", 20),
|
|
186
186
|
),
|
|
187
|
-
PhaseCheck("AGENTS.md
|
|
187
|
+
PhaseCheck("AGENTS.md has substantial content", _file_min_lines("AGENTS.md", 30)),
|
|
188
188
|
PhaseCheck("Trace vault has at least 1 seal", _trace_vault_exists()),
|
|
189
189
|
],
|
|
190
190
|
commands=[
|
|
@@ -207,7 +207,7 @@ PHASES: list[Phase] = [
|
|
|
207
207
|
PhaseCheck("At least 5 requirements defined", _req_count(5)),
|
|
208
208
|
PhaseCheck("TEST_SPEC.md exists", _file_exists("docs/TEST_SPEC.md")),
|
|
209
209
|
PhaseCheck("ARCHITECTURE.md exists", _file_exists("docs/ARCHITECTURE.md")),
|
|
210
|
-
PhaseCheck("
|
|
210
|
+
PhaseCheck("REQUIREMENTS.md has content", _file_min_lines("REQUIREMENTS.md", 10)),
|
|
211
211
|
],
|
|
212
212
|
commands=[
|
|
213
213
|
"specsmith stress-test",
|
|
@@ -230,7 +230,7 @@ PHASES: list[Phase] = [
|
|
|
230
230
|
),
|
|
231
231
|
PhaseCheck("TEST coverage ≥ 80 %", _test_spec_covers_reqs(80)),
|
|
232
232
|
PhaseCheck("REQUIREMENTS.md has ≥ 5 REQs", _req_count(5)),
|
|
233
|
-
PhaseCheck("
|
|
233
|
+
PhaseCheck("LEDGER.md exists", _file_exists("LEDGER.md")),
|
|
234
234
|
],
|
|
235
235
|
commands=[
|
|
236
236
|
"specsmith validate",
|
|
@@ -267,7 +267,7 @@ PHASES: list[Phase] = [
|
|
|
267
267
|
PhaseCheck("ARCHITECTURE.md exists", _file_exists("docs/ARCHITECTURE.md")),
|
|
268
268
|
PhaseCheck("TEST coverage ≥ 80 %", _test_spec_covers_reqs(80)),
|
|
269
269
|
PhaseCheck("Trace vault has seals", _trace_vault_exists()),
|
|
270
|
-
PhaseCheck("
|
|
270
|
+
PhaseCheck("Trace vault has ≥ 2 seals", _trace_vault_exists()),
|
|
271
271
|
PhaseCheck("LEDGER.md has content", _file_min_lines("LEDGER.md", 10)),
|
|
272
272
|
],
|
|
273
273
|
commands=[
|
|
@@ -286,8 +286,8 @@ PHASES: list[Phase] = [
|
|
|
286
286
|
checks=[
|
|
287
287
|
PhaseCheck("CHANGELOG.md has version entry", _changelog_has_version()),
|
|
288
288
|
PhaseCheck("Trace vault has seals", _trace_vault_exists()),
|
|
289
|
-
PhaseCheck("
|
|
290
|
-
PhaseCheck("
|
|
289
|
+
PhaseCheck("CHANGELOG.md exists", _file_exists("CHANGELOG.md")),
|
|
290
|
+
PhaseCheck("ARCHITECTURE.md exists", _file_exists("docs/ARCHITECTURE.md")),
|
|
291
291
|
],
|
|
292
292
|
commands=[
|
|
293
293
|
"specsmith export --output docs/COMPLIANCE.md",
|
specsmith/scaffolder.py
CHANGED
|
@@ -11,6 +11,7 @@ from pathlib import Path
|
|
|
11
11
|
from jinja2 import Environment, PackageLoader, select_autoescape
|
|
12
12
|
|
|
13
13
|
from specsmith.config import ProjectConfig, ProjectType
|
|
14
|
+
from specsmith.phase import PHASE_MAP
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def scaffold_project(config: ProjectConfig, target: Path) -> list[Path]:
|
|
@@ -29,11 +30,16 @@ def scaffold_project(config: ProjectConfig, target: Path) -> list[Path]:
|
|
|
29
30
|
from specsmith.tools import get_tools
|
|
30
31
|
|
|
31
32
|
tools = get_tools(config)
|
|
33
|
+
phase_key = "inception" # new projects always start at inception
|
|
34
|
+
phase_obj = PHASE_MAP[phase_key]
|
|
32
35
|
ctx = {
|
|
33
36
|
"project": config,
|
|
34
37
|
"today": date.today().isoformat(),
|
|
35
38
|
"package_name": config.package_name,
|
|
36
39
|
"tools": tools,
|
|
40
|
+
"aee_phase": phase_key,
|
|
41
|
+
"aee_phase_label": phase_obj.label,
|
|
42
|
+
"aee_phase_emoji": phase_obj.emoji,
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
created: list[Path] = []
|
|
@@ -86,6 +92,11 @@ def scaffold_project(config: ProjectConfig, target: Path) -> list[Path]:
|
|
|
86
92
|
save_budget(target, CreditBudget()) # unlimited by default
|
|
87
93
|
created.append(target / ".specsmith" / "credit-budget.json")
|
|
88
94
|
|
|
95
|
+
# Set initial AEE phase in scaffold.yml
|
|
96
|
+
from specsmith.phase import write_phase
|
|
97
|
+
|
|
98
|
+
write_phase(target, "inception")
|
|
99
|
+
|
|
89
100
|
# Git init
|
|
90
101
|
if config.git_init:
|
|
91
102
|
subprocess.run( # noqa: S603
|
|
@@ -109,14 +120,14 @@ def _build_file_map(config: ProjectConfig) -> list[tuple[str, str]]:
|
|
|
109
120
|
("editorconfig.j2", ".editorconfig"),
|
|
110
121
|
# Modular governance
|
|
111
122
|
("governance/rules.md.j2", "docs/governance/RULES.md"),
|
|
112
|
-
("governance/
|
|
123
|
+
("governance/session-protocol.md.j2", "docs/governance/SESSION-PROTOCOL.md"),
|
|
124
|
+
("governance/lifecycle.md.j2", "docs/governance/LIFECYCLE.md"),
|
|
113
125
|
("governance/roles.md.j2", "docs/governance/ROLES.md"),
|
|
114
126
|
("governance/context-budget.md.j2", "docs/governance/CONTEXT-BUDGET.md"),
|
|
115
127
|
("governance/verification.md.j2", "docs/governance/VERIFICATION.md"),
|
|
116
128
|
("governance/drift-metrics.md.j2", "docs/governance/DRIFT-METRICS.md"),
|
|
117
129
|
# Project docs
|
|
118
130
|
("docs/architecture.md.j2", "docs/ARCHITECTURE.md"),
|
|
119
|
-
("docs/workflow.md.j2", "docs/WORKFLOW.md"),
|
|
120
131
|
("docs/requirements.md.j2", "docs/REQUIREMENTS.md"),
|
|
121
132
|
("docs/test-spec.md.j2", "docs/TEST_SPEC.md"),
|
|
122
133
|
# Scripts
|