From 7b0e73efaba77e768060185bcda397fba6dc47fb Mon Sep 17 00:00:00 2001 From: Santi Pochat <31376442+sapochat@users.noreply.github.com> Date: Mon, 3 Aug 2026 05:44:41 -0700 Subject: [PATCH] [verified] fix: validate required study stage files --- src/deep_naming_study/study.py | 24 ++++++++++++++---------- tests/test_study.py | 5 +++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/deep_naming_study/study.py b/src/deep_naming_study/study.py index e314d5c..97606e6 100644 --- a/src/deep_naming_study/study.py +++ b/src/deep_naming_study/study.py @@ -4,6 +4,15 @@ REQUIRED = ["project", "purpose", "audience", "voice", "source_inspiration", "mechanism", "dead_lanes", "rubric"] STAGES = ["01-source-notes", "02-theory-passes", "03-merged-slate", "04-blind-critics", "05-collisions", "06-simulations", "07-synthesis"] +TEMPLATES = { + "01-source-notes/README.md": "# Source notes\n\nRecord primary language, mandate, tensions, and citations.\n", + "02-theory-passes/README.md": "# Independent theory passes\n\nOne file per theory. Do not expose prior favorites.\n", + "03-merged-slate/slate.md": "# Merged slate\n\nTrack each candidate and its theory. Do not count votes.\n", + "04-blind-critics/README.md": "# Blind critics\n\nGive critics the brief and slate without domains or prior rankings.\n", + "05-collisions/README.md": "# Collisions\n\nCheck exact phrases, phonetics, associations, then domains.\n", + "06-simulations/template.md": "# Candidate simulation\n\n## Five issue titles\n\n## Descriptor\n\n## About paragraph\n\n## Masthead\n\n## Spoken introduction\n", + "07-synthesis/final.md": "# Final synthesis\n\n## Judgment: solved | close | wrong field\n\n## Recovered mechanism\n\n## Leads\n\n## Killed lanes\n\n## Next test\n", +} def validate_brief(brief: dict) -> list[str]: @@ -24,16 +33,7 @@ def scaffold_study(brief: dict, destination: str | Path) -> Path: (root / "brief.json").write_text(json.dumps(brief, indent=2) + "\n") for stage in STAGES: (root / stage).mkdir() - templates = { - "01-source-notes/README.md": "# Source notes\n\nRecord primary language, mandate, tensions, and citations.\n", - "02-theory-passes/README.md": "# Independent theory passes\n\nOne file per theory. Do not expose prior favorites.\n", - "03-merged-slate/slate.md": "# Merged slate\n\nTrack each candidate and its theory. Do not count votes.\n", - "04-blind-critics/README.md": "# Blind critics\n\nGive critics the brief and slate without domains or prior rankings.\n", - "05-collisions/README.md": "# Collisions\n\nCheck exact phrases, phonetics, associations, then domains.\n", - "06-simulations/template.md": "# Candidate simulation\n\n## Five issue titles\n\n## Descriptor\n\n## About paragraph\n\n## Masthead\n\n## Spoken introduction\n", - "07-synthesis/final.md": "# Final synthesis\n\n## Judgment: solved | close | wrong field\n\n## Recovered mechanism\n\n## Leads\n\n## Killed lanes\n\n## Next test\n", - } - for rel, content in templates.items(): + for rel, content in TEMPLATES.items(): (root / rel).write_text(content) return root @@ -52,4 +52,8 @@ def validate_study(root: str | Path) -> list[str]: for stage in STAGES: if not (root / stage).is_dir(): errors.append(f"missing stage directory: {stage}") + for rel in TEMPLATES: + path = root / rel + if path.parent.is_dir() and not path.is_file(): + errors.append(f"missing stage file: {rel}") return errors diff --git a/tests/test_study.py b/tests/test_study.py index 32e85c5..be63369 100644 --- a/tests/test_study.py +++ b/tests/test_study.py @@ -17,5 +17,10 @@ def test_missing_stage(self): (root / "04-blind-critics" / "README.md").unlink() (root / "04-blind-critics").rmdir() self.assertTrue(any("04-blind-critics" in e for e in validate_study(root))) + def test_missing_stage_file(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) / "study"; scaffold_study(BRIEF, root) + (root / "06-simulations" / "template.md").unlink() + self.assertIn("missing stage file: 06-simulations/template.md", validate_study(root)) if __name__ == "__main__": unittest.main()