Applies the seven-campaign saga direction spec (021c248) to the docs it
was written to protect. No new milestone, no new content, no BOM line.
docs/roadmap.md — a "Guarded direction" section, and a ⛨ saga note on the
four milestones that would otherwise foreclose the shape:
M3 don't hardcode the title flow so a saga entry path can't be added
M4 keep construct()'s `creation` a plain Dictionary; the scene must not
become its only producer
M5 the level curve is no longer open — L20 across 7 tiers; enemy stat
blocks carry tier; no scaling multiplier, ever
M9 the save is two objects (Saga + Campaign); humiliations move to the
Saga or they die with the campaign
races-and-classes spec — the deferred "XP curve → M5" line now inherits the
ceiling rather than choosing one.
canon-roadmap — what the saga asks of content: a tier field when the
item/quest namespaces land, and a fixed-vs-variable mark on each BOM line
when the BOM is written. Tiers 2-7 and the other six kings go to the
Backlog.
Content build --check green; no generated content touched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A direction spec: one character, seven campaigns (one per demon king),
one saga — then it ends. The character, companions, deeds and level carry
between campaigns; the world does not.
Deliberately adds no milestone and no BOM line. Its real payload is a set
of guardrails on milestones that are about to be built blank and would
otherwise foreclose the shape:
- M5 owes a level curve — design it to a ceiling of 20 across 7 tiers,
with tiered enemy stat blocks and never a scaling multiplier.
- M9 owes a save format — two objects (Saga + Campaign), not one blob.
Humiliations move to the Saga or they die with the campaign.
- M3/M4 — don't foreclose a saga entry path; don't couple construct() to
the creation scene.
- Build tool — add tier:1..7 when the item/quest namespaces land.
Difficulty is fixed at world-construction time from the campaign index,
never during play. That prohibition is the whole design; a world that
re-levels with the player is a treadmill.
The canon-log contract does not change: party[].disposition,
humiliations[] and established_facts[] already exist, and player still
admits no number. The Saga becomes the source; the canon log a projection
of it. The seam is one optional fourth arg to NewGame.construct().
Greywater at tier 1 under Ghaul is, unintentionally, already campaign 1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Content (lore, economy, items, quests, map) was untracked and unbounded.
Bound it: a second roadmap (content/roadmap.md) with Spine / Bill of
Materials / Backlog, demand-driven by the milestone that consumes it.
- Vertical slice = one town + one dungeon (adopt + deepen Greywater).
- Ghaul the Yoke is the ledger chain's patron, so the quest chain and
the cosmology are one object, not two.
- Economy splits per §18: the loop is code (M8), the numbers are content.
Currency = 1g/100s/10,000c, copper as the stored atom; lands before M7.
- Build tool needs `item` + `quest` namespaces; locations already exist.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the GitHub Actions workflow (repo is on Gitea; it never ran). The
--check gate now lives in pytest (test_real_content.py), and the world-building
skill's verify step states there is no CI and adds the client suite — since
content/world/ is a client input, not just build output.
The repo lives on a Gitea server and CI/CD is a long way off, so
.github/workflows/content-build.yml never ran. A workflow nobody executes is
worse than none: it read as a safety net that wasn't there.
Removing it moves the `--check` gate somewhere real. It is now enforced by
tools/content_build/tests/test_real_content.py, which runs check() against the
live bible under pytest — so the gate is a test you actually run, not a job
that never fired.
The world-building skill's verify step now says plainly that nothing runs these
for you, and adds the client suite to the checklist: content/world/ is a CLIENT
INPUT (content_db.gd loads it, test_content_db.gd asserts on it), so a content
change can break the client. That is exactly what the Duncarrow purge did, and
with no CI there is nothing else to catch it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Delete the Duncarrow scaffolding; preserve the infrastructure it carried.
- rule.disposition-ladder reparented into content/lore/mechanics.md (required
exactly once by the build; moved atomically with the deletion)
- content/lore/specimen.md: synthetic status=candidate disposition-gate specimen
(validated by --check, emitted nowhere), teaching both an earned warm reveal
and a never-gated core secret
- duncarrow.md + 23 generated JSON + test_duncarrow.py removed
- world-building skill repointed: structure -> specimen.md, voice -> cosmology.md;
3 evals rewritten cosmology-aware
- repaired the client consumers the purge broke: content_db no longer errors on
an absent content dir, and the no-body-on-client secrecy guard is asserted
structurally so it cannot pass vacuously
--check exit 0, pytest 45, client GUT 149/149.
The purge left `gate: never` — the rung no disposition unlocks — demonstrated
nowhere. Duncarrow's Crell chain had taught it; specimen.md only taught `warm`.
Adds secret.specimen-drowned-clerk (secrecy 4, gate: never), so the specimen
now shows an ascending 2 -> 4 chain and a reason `never` exists: the NPC holds
it, but saying it aloud would hang him. Mirrored in the skill's worked example,
which now states the rule — if any rung *should* buy the secret, gate it at
`trusted` instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The purge plan assumed content/world/** was pure build output. The client
reads it: content_db.gd loads content/world/topics/ (now absent, since every
topic was Duncarrow-generated) and its tests asserted on deleted ids.
- content_db: a missing content dir is an empty dir, not an error (§13)
- test_content_db: repoint at cosmology canon; drop deleted npc.mera-fenn
- test_content_db: assert the no-body-on-client invariant structurally, so it
cannot pass vacuously when the topic set is empty
- schema.md: finish repointing the worked example onto specimen.md's ids
- restore a local real-bible --check smoke test (was only in CI)
- roadmap: record that generated content has client consumers
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move rule.disposition-ladder into content/lore/mechanics.md and delete the
Duncarrow scaffolding (source + 23 generated JSON). Remove the redundant
test_duncarrow.py (routing/secrecy already covered by test_emit.py synthetic
fixtures); repoint a cosmetic slug-test id.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Delete the Duncarrow scaffolding while preserving the schema/disposition-gate
infrastructure it carries: reparent rule.disposition-ladder into a new
mechanics.md, stand up a synthetic status:candidate specimen (validated but
never emitted), repoint the skill/tests/evals. Zero real game content; the
in-flight NPC redesign and the hand-authored POC seed JSON stay out of scope.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Overarching cosmology: the Warden (distant good god), the Sent, and the
Seven demon kings, authored as canon; world-building skill updated to
reconcile all future content against the backdrop (incl. FTH-axis
Bonesetter/Bloodsworn calling mapping). Duncarrow decoupled as
scaffolding, not removed. Build --check exit 0, pytest 47/47.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
- faction.the-seven now relates down to all seven lords (spec §4 intent;
bidirectional discoverability), regenerated the-seven.json.
- canon-roadmap: clarify Bloodsworn resolves the warlock's former [CANON-TBD] name.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Adds the FTH-axis calling mapping to the cosmology spec: Bonesetter
channels the Warden, the Bloodsworn (names the [CANON-TBD] warlock
calling) pacts one of the Seven, Hedge-Mage is a pointed outsider,
martials unaligned. Luck-immunity intent kept as flavor and flagged for
the M5/Improviser reconcile (§7-sensitive), not built here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Establishes the good-god/seven-demon-lords cosmology as the game's
overarching content theme, mapped onto the content schema as canon
entities, with the world-building skill updated to reconcile all future
authoring against it. Duncarrow scaffolding decoupled now, purged in a
follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
The scripts were tracked but their .gd.uid siblings weren't. Godot 4.4+ .uid
files pin each script's uid://, so leaving them out lets each clone regenerate
different uids and break uid:// references in scenes/resources. Matches the 159
.uid files already tracked in the repo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Author gritty, disposition-gated Margreave content (lore bible + hand-authored
quest/item/location/origin JSON) that reconciles against existing canon before
writing, holds the world's hard-by-default / warm-when-earned NPC tone, and keeps
the content_build --check gate green.
- SKILL.md: read-before-write workflow, read-only on existing canon (propose
diffs, require approval), build+check+pytest as the done bar.
- references/schema.md: full bible + JSON contract, secrecy scale, gate model.
- references/tone.md: tone contract + NPC disposition-warmth model.
- scripts/canon_index.py: dumps existing ids for reconciliation.
- evals/: 3 test cases (all green in isolated-worktree runs).
Un-ignore /.claude/skills/ so project skills are versioned like code; all other
.claude/ dirs (root + nested api/client) stay ignored.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Lock the NPC identifier: full namespaced id (npc.mera-fenn) is canonical
everywhere — stored data, client dict key, and the /npc/speak wire. The API
load_npc changes from a filename lookup to an id-indexed load (matching the
client's ContentDB), reads persona from content/server, and resolves the
client's reveal-set fact_ids to topic bodies. Legacy Fenn keeps working; no
migration required. First task of the runtime-consumption cycle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Content & canon build tool: compiles content/lore/*.md (Markdown bible +
fenced YAML) into validated client (content/world) + server-only
(content/server) JSON, split by role so spoilers/personas never ship in the
client. Validation gate aborts on schema violations; --check is the CI
freshness/validity gate. Promotes Duncarrow as the acceptance test (secret
body proven server-only). ContentDB loads the new canon/ + topics/ dirs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
- --check now detects orphaned/stale build artifacts in build-owned dirs
(client canon/ + topics/ fully owned; client npcs/ shared with legacy
hand-authored files, only npc.* ids are treated as build-owned)
- resolve() enforces the schema rule that canon entities must be secrecy 0
- resolve() and ladder_rungs() guard against malformed value TYPES (non-int
secrecy, non-list related/rungs) raising BuildError instead of crashing
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Schema lock built bottom-up from the Duncarrow specimen (candidate-town.md):
- disposition rungs as bands over the engine integer (code owns numbers)
- town fact = engine reveal-topic (authoring layer over §6, no new runtime)
- MD bible -> build -> JSON; split by role (game vs server), secrecy as lint
- canon home: content/lore (source) -> content/world + content/server (built)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
Settles the §17 class-name reconcile (charter §8 vs mock callings) in
favor of the gritty callings, and gives every race trait a concrete rule.
Feeds M4 character creation; combat ability kit deferred to M5.
- Resolution model: deterministic value vs DC, LCK selects the borderline
band (§7) — flat mods, no random d20, no advantage.
- 5 saves (LCK owns none); death saves = LCK.
- Races: features only, no stat mods; Nightsight ruling for darkvision.
- 7 callings (charter-7), 2 names [CANON-TBD]; casters=MP, martials=cooldowns.
- Flags two contract migrations for M4: canon-log gains `race`; deserter
origin's allowed_classes migrates to new calling ids.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
M3 — Title screen + unified app version. The entry point: editor-first
Title screen (warm shader background, pulsing glow, drifting embers, the
CODE OF CONQUEST logo, keyboard/mouse menu, footer) set as the boot scene;
New Game -> the Main Window shell, Quit exits, rest inert. Suite 146/146.
Plus a single /VERSION source of truth (0.01-alpha): the API reads it at
startup (+ GET /version), the client bakes it into project.godot via
scripts/sync-version and reads it through the Version helper. No runtime
coupling; both sides agree. Adds TitleLogo/TitleKicker theme variations.
The hex-removal correction set _glow.color = Palette.BLOOD (opaque), which
with the 0.5-0.8 modulate pulse read as a near-solid red slab. Restore the
intended low alpha via Color(Palette.BLOOD, 0.22) — RGB still from Palette,
alpha a numeric param (same idiom as the ember ramp). Whole-branch review
catch. Title tests 5/5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>