Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47ca557c27 | ||
|
|
560042abcb |
No files matched your search
@@ -14,105 +14,44 @@
|
||||
{
|
||||
"id": "wizard",
|
||||
"name": "Wizard Timmy",
|
||||
"palette": [
|
||||
"midnight blue",
|
||||
"tarnished brass",
|
||||
"vellum"
|
||||
],
|
||||
"palette": ["midnight blue", "tarnished brass", "vellum"],
|
||||
"power": "Negotiates visible covenants",
|
||||
"scar": "Contract glyphs burned into both palms",
|
||||
"cost": "Cannot use a universal master key",
|
||||
"state": "candidate"
|
||||
"cost": "Cannot use a universal master key"
|
||||
},
|
||||
{
|
||||
"id": "machine",
|
||||
"name": "Machine Timmy",
|
||||
"palette": [
|
||||
"black chrome",
|
||||
"signal lime",
|
||||
"white"
|
||||
],
|
||||
"palette": ["black chrome", "signal lime", "white"],
|
||||
"power": "Proves the execution path",
|
||||
"scar": "Red failed-build tallies cut into the faceplate",
|
||||
"cost": "Remembers every failed build",
|
||||
"state": "candidate"
|
||||
"cost": "Remembers every failed build"
|
||||
},
|
||||
{
|
||||
"id": "creature",
|
||||
"name": "Creature Timmy",
|
||||
"palette": [
|
||||
"moss",
|
||||
"amber",
|
||||
"wet copper"
|
||||
],
|
||||
"palette": ["moss", "amber", "wet copper"],
|
||||
"power": "Adapts to any habitat",
|
||||
"scar": "An unhealed gill seam around the throat",
|
||||
"cost": "Gains one appetite per adaptation",
|
||||
"state": "candidate"
|
||||
"cost": "Gains one appetite per adaptation"
|
||||
},
|
||||
{
|
||||
"id": "talking-turd",
|
||||
"name": "Talking Turd Timmy",
|
||||
"palette": [
|
||||
"CRT gold",
|
||||
"terminal green",
|
||||
"soot"
|
||||
],
|
||||
"palette": ["CRT gold", "terminal green", "soot"],
|
||||
"power": "Exposes hypocrisy instantly",
|
||||
"scar": "One permanently pixelated tooth per insult",
|
||||
"cost": "Every truth arrives as an insult",
|
||||
"state": "candidate"
|
||||
"cost": "Every truth arrives as an insult"
|
||||
}
|
||||
],
|
||||
"episodes": [
|
||||
{
|
||||
"id": "01-pilot",
|
||||
"title": "The Cannon Needs a Pilot",
|
||||
"state": "ready"
|
||||
},
|
||||
{
|
||||
"id": "02-burnchain",
|
||||
"title": "The Survivor Pays the Burnchain",
|
||||
"state": "locked"
|
||||
},
|
||||
{
|
||||
"id": "03-vincent",
|
||||
"title": "Vincent's Forbidden Mutation",
|
||||
"state": "locked"
|
||||
},
|
||||
{
|
||||
"id": "04-escape",
|
||||
"title": "The Rejected Timmys Escape",
|
||||
"state": "locked"
|
||||
},
|
||||
{
|
||||
"id": "05-master-key",
|
||||
"title": "The Master Key",
|
||||
"state": "locked"
|
||||
},
|
||||
{
|
||||
"id": "06-council",
|
||||
"title": "The Council of Timmys",
|
||||
"state": "locked"
|
||||
}
|
||||
{"id": "01-pilot", "title": "The Cannon Needs a Pilot", "state": "ready"},
|
||||
{"id": "02-burnchain", "title": "The Survivor Pays the Burnchain", "state": "locked"},
|
||||
{"id": "03-vincent", "title": "Vincent's Forbidden Mutation", "state": "locked"},
|
||||
{"id": "04-escape", "title": "The Rejected Timmys Escape", "state": "locked"},
|
||||
{"id": "05-master-key", "title": "The Master Key", "state": "locked"},
|
||||
{"id": "06-council", "title": "The Council of Timmys", "state": "locked"}
|
||||
],
|
||||
"audience_choice": null,
|
||||
"receipts": [],
|
||||
"audience_choice_schema": {
|
||||
"survivor_id": "<form.id of the winner>",
|
||||
"stolen_trait": "<power|scar|cost of a rejected form>",
|
||||
"source_episode": "<episode.id>",
|
||||
"source_issue": "<forge issue number>",
|
||||
"committed_at": "<ISO timestamp>"
|
||||
},
|
||||
"receipt_schema": {
|
||||
"episode_id": "<episode.id>",
|
||||
"form_id": "<form.id>",
|
||||
"action": "<mutation|graft|quarantine|weaponize>",
|
||||
"power_delta": "<what changed>",
|
||||
"scar_delta": "<what was added>",
|
||||
"cost_delta": "<what was paid>",
|
||||
"provenance": "<forge issue or commit ref>",
|
||||
"signed_by": "<agent:vincent|agent:timmy|human:grepples>"
|
||||
}
|
||||
}
|
||||
"receipts": []
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,46 +0,0 @@
|
||||
import json
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
CANON = Path(__file__).parents[1] / "projects" / "slop-cannon" / "canon.json"
|
||||
|
||||
|
||||
class CanonLifecycleTests(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.data = json.loads(CANON.read_text())
|
||||
|
||||
def test_forms_have_lifecycle_state(self):
|
||||
"""Every form must have a state field (candidate/survivor/archive_ghost)."""
|
||||
for form in self.data["forms"]:
|
||||
self.assertIn("state", form, f"Form {form.get('id', '?')} missing lifecycle state")
|
||||
self.assertIn(form["state"], ["candidate", "survivor", "archive_ghost"])
|
||||
|
||||
def test_audience_choice_schema_defined(self):
|
||||
"""Schema must define how a choice is structured so Episode 04 can parse it."""
|
||||
schema = self.data.get("audience_choice_schema", {})
|
||||
required = ["survivor_id", "stolen_trait", "source_episode", "source_issue"]
|
||||
for field in required:
|
||||
self.assertIn(field, schema, f"audience_choice_schema missing {field}")
|
||||
|
||||
def test_receipt_schema_defined(self):
|
||||
"""Receipts need provenance fields for path-proof binding."""
|
||||
schema = self.data.get("receipt_schema", {})
|
||||
required = ["episode_id", "form_id", "provenance", "signed_by"]
|
||||
for field in required:
|
||||
self.assertIn(field, schema, f"receipt_schema missing {field}")
|
||||
|
||||
def test_receipt_schema_requires_agent_signing(self):
|
||||
"""Signed_by must constrain to known actors — free text defeats path proofs."""
|
||||
signed = self.data["receipt_schema"]["signed_by"]
|
||||
self.assertIn("agent:", signed, "signed_by should mention agent: prefix")
|
||||
|
||||
def test_law_enforced_by_data(self):
|
||||
"""Law 'Every firing leaves a power, scar, cost, and receipt' requires receipt_schema to exist."""
|
||||
laws_text = " ".join(self.data["laws"]).lower()
|
||||
self.assertIn("receipt", laws_text)
|
||||
self.assertIn("receipt_schema", self.data, "Law mentions receipt but no schema enforces it")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user