import { classes, inquiryDomains, materialModes, evidenceStates } from './catalog.mjs';

const pick = (card, path, fallback='') => path.split('.').reduce((v,k)=>v?.[k],card) ?? fallback;
const bullets = value => Array.isArray(value) ? value.map(v=>`- ${typeof v==='string'?v:(v.summary??v.name??v.id)}`).join('\n') : `- ${value}`;

export function cardMarkdown(card) {
  const meanings=card.meanings ?? {};
  const layers=card.epistemicLayers ?? card.epistemic_layers ?? [];
  const rels=card.relationships ?? [];
  const sources=card.sourceIds ?? card.sources ?? [];
  const classId=card.primaryClass ?? card.class;
  const primaryDomain=pick(card,'domains.primary',card.primaryDomain ?? card.domains?.[0]);
  const secondaryDomains=pick(card,'domains.secondary',card.secondaryDomains ?? card.domains?.slice(1) ?? []);
  const integration=typeof card.integration==='string'?card.integration:card.integration?.practice;
  const version=typeof card.version==='object' ? (card.version.number ?? card.version.schema ?? '1.0.0') : (card.version ?? '1.0.0');
  return `---
id: ${card.id}
title: "${card.title.replace(/"/g,'\\"')}"
primary_class: ${classId}
primary_domain: ${primaryDomain}
secondary_domains: [${secondaryDomains.join(', ')}]
material_modes: [${(card.materialModes??[]).join(', ')}]
version: ${version}
---

# ${card.title}

> **${card.archetype}.** ${card.essence}

## Origin

${pick(card,'origin.summary')}

## What It Is

${card.whatItIs ?? `${card.essence} In the Oracle it is classified as ${card.className ?? classId}, while its evidence layers determine how literal, historical, fictional, experiential, or symbolic a particular interpretation may be.`}

## Cultural History

${card.culturalHistory}

## Claim and Evidence Layers

  ${layers.map(layer=>`### ${layer.label ?? layer.layer ?? layer.id}\n\n- **Claim layer:** ${layer.layer ?? layer.label ?? 'unspecified'}\n- **Material context:** ${(layer.materialModes ?? (layer.mode ? [layer.mode] : card.materialModes ?? [])).join(', ') || 'original-synthesis'}\n- **Evidence state:** ${layer.evidenceState ?? layer.status}\n- **Summary:** ${layer.summary ?? layer.statement ?? 'No claim summary supplied.'}\n- **Sources:** ${(layer.sourceIds??layer.sources??[]).join(', ') || 'No external source assigned; editorial synthesis only.'}`).join('\n\n')}

## Oracle

- **Essence:** ${card.essence}
- **Archetype:** ${card.archetype}
- **Gift:** ${card.gift}
- **Shadow:** ${card.shadow}
- **Guidance:** ${pick(card,'divination.guidance',card.guidance)}
- **Healing / integration:** ${pick(card,'divination.integration',integration)}
- **Question:** ${pick(card,'divination.question',card.question)}

## Meanings

### Psychological

${meanings.psychological}

### Social

${meanings.social}

### Spiritual

${meanings.spiritual}

### Technological

${meanings.technological}

### Political

${meanings.political}

## Divination

- **Gift state:** ${pick(card,'divination.gift',pick(card,'divination.upright',card.gift))}
- **Shadow state:** ${pick(card,'divination.shadow',card.shadow)}
- **Threshold state:** ${pick(card,'divination.threshold',pick(card,'divination.signal'))}
- **Practical move:** ${pick(card,'divination.practicalMove',integration)}

## Game and Story Function

${(card.gameRoles??[]).map(role=>`- ${typeof role==='string'?role:`**${role.role}:** ${role.effect}`}`).join('\n')}

**Story seed:** ${card.storySeed ?? `Let ${card.title} alter what a character believes is possible, then force a choice between its Gift and Shadow.`}

## Relationships

${rels.length?bullets(rels):'- Relationships are maintained in the canonical graph; see `data/relationships.json`.'}

## Visual Direction

${pick(card,'visual.direction',pick(card,'visual.centralMotif'))} The art must be original, text-free, square, rounded-corner enamel with one immediately legible silhouette.

## Safety and Integration

${(card.safetyFlags??card.safety?.flags??[]).length?`Flags: ${(card.safetyFlags??card.safety?.flags??[]).join(', ')}. ${card.safetyNote??card.safety?.use??'Use symbolically without treating the Tile as factual verification or personal instruction.'}`:'No elevated safety flag. Keep claims and metaphor visibly distinct.'}

## Sources and Research Notes

- Source IDs: ${sources.join(', ') || 'original-synthesis'}
- Research note: ${card.researchNotes ?? card.research?.openQuestions?.join(' ') ?? 'Continue source review at the next release.'}
`;
}

const topic = (title, thesis, ids, cards) => {
  const selected=cards.filter(c=>ids.some(id=>[c.primaryClass??c.class,pick(c,'domains.primary',c.primaryDomain),...(c.materialModes??[])].includes(id)) || ids.some(id=>c.id.includes(id))).slice(0,80);
  const examples=selected.slice(0,3);
  return `# ${title}

## Purpose

${thesis} This chapter is an index and a method: it shows how to explore the subject without collapsing documented history, current research, reported experience, fiction, community belief, and Oracle synthesis into one truth claim.

## Reading Method

1. **Locate the Tile.** Read its Primary Class to learn what kind of thing it is, then its Inquiry Domains to learn which questions it can illuminate.
2. **Name the material mode.** A science-fiction, mythology, political-mythology, experiential-report, or established-science label identifies context; it is not a score of spiritual or personal importance.
3. **Read claims layer by layer.** Begin with the literal-and-empirical layer, then the cultural-and-historical layer, and only then the symbolic-and-divinatory layer. An evidence state applies to one bounded claim, not to the Tile as a whole.
4. **Open the sources.** A concept-specific citation can support a history or bounded claim. A class-level contextual citation only locates the Tile in a wider field. Neither one certifies every interpretation.
5. **Follow typed edges.** Compare the exact verb and rationale. “Fictionalizes,” “opposes,” “echoes,” and “investigated-by” are not interchangeable. A resemblance edge is not proof of historical influence.
6. **Integrate through action.** Use Gift, Shadow, Threshold, and the integration practice to identify one grounded choice that remains useful even if a favored ontology is wrong.

## Worked Comparison

${examples.length ? examples.map((card,index)=>`${index+1}. **${card.title}** is a ${card.className} Tile centered on ${card.domainNames?.[0] ?? card.primaryDomain}. Its literal layer is **${card.epistemicLayers?.find(layer=>layer.layer==='literal-and-empirical')?.status ?? 'not-applicable'}**; its symbolic question is: “${card.question}”`).join('\n') : 'No matching core Tiles were selected for this release.'}

Compare these examples by asking what changes when the class, evidence state, or edge verb changes. Do not treat the most vivid Tile as the most factual. When a reading crosses into health, identity, conspiracy, or current politics, show the card’s safety flags and preserve an alternate explanation.

## System Links

- [How to Use](HOW-TO-USE.md) for reference, reading, and game workflows.
- [Graph](GRAPH.md) for edge semantics and provenance.
- [Divination](DIVINATION.md) for reflective reading rules.
- [Glossary](GLOSSARY.md) for Classes, Domains, Modes, and Evidence States.
- [Source Registry](../research/SOURCE-REGISTRY.md) for source tiers, scope, and rights.

## Canonical Tiles

${selected.map(c=>`- [${c.title}](../cards/${c.primaryClass??c.class}/${c.id}.md) — ${c.essence}`).join('\n')}
`;
};

export function buildManualFiles(cards, systems, longlist) {
  const intro=`# Ultimate Science Fiction Oracle

## What This Is

The Ultimate Science Fiction Oracle is a symbolic atlas of futures, hidden worlds, cosmic myths, technologies, conspiracies, spiritual cosmologies, and imagined realities. It is designed for reference, reflective divination, story construction, and structured games. It does not ask users to choose between flat disbelief and total certainty. Its working posture is: **Maybe. Let’s see where this connects.**

The release contains **${cards.length} complete core Tiles** selected from **${longlist.length.toLocaleString()} evaluated candidates**, connected by **${systems.relationships.length.toLocaleString()} typed relationships**, **${systems.lineages.length} lineages**, **${systems.spreads.length} spreads**, and **${systems.games.length} game systems**. Deterministic seeds make draws and generators reproducible. Source IDs, edge rationales, safety flags, and evidence states remain visible in both human-readable and machine-readable forms.

## The Four Axes

- A **Primary Class** answers what kind of thing a Tile is: Force, Cosmology, Being, World, Technology, Artifact, Faction, Program, System, Event, State, Myth, or Hypothesis.
- An **Inquiry Domain** answers which human question the Tile illuminates. Domains overlap; Classes do not.
- A **Material Mode** names the context carrying the idea, such as established science, science fiction, mythology, experiential report, or original synthesis. Mode is not a truth score.
- An **Evidence State** belongs to a bounded claim. A single Tile may contain documented history, unverified extraordinary claims, and valid symbolic use at the same time.

## Chaos and Meaning

The central polarity is **Chaos ↔ Meaning**. Chaos preserves possibility before a structure is selected; Meaning allows experience to become interpretable and shareable. Either pole can become a shadow: chaos can defeat orientation, while meaning can harden into a self-sealing explanation. The Oracle makes the tension playable by requiring sources, alternatives, typed relationships, and a grounded integration step.

## What It Is Not

The Oracle is not a diagnostic instrument, prophecy service, intelligence assessment, substitute for qualified care, or proof of extraordinary claims. Cultural importance does not establish literal truth. A vivid coincidence is not automatically a causal link. No reading should direct harassment, collective blame, treatment changes, dangerous action, or certainty about hidden actors.

## Start Here

1. Read [How to Use](HOW-TO-USE.md).
2. Learn the axes in [Glossary](GLOSSARY.md).
3. Inspect one card’s evidence layers and source IDs.
4. Follow one relationship in [Graph](GRAPH.md) and read its exact verb and rationale.
5. Try a one-card reading in [Divination](DIVINATION.md), ending with one grounded action.
6. For fiction or play, use [Story Generation](STORY-GENERATION.md), [Spreads](SPREADS.md), or [Game Systems](GAME-SYSTEMS.md).
`;
  const files={
    'INTRODUCTION.md':intro,
    'COSMOLOGY.md':topic('Cosmology','Cosmologies are world-scale models, not default facts. Each Tile separates historical tradition, community belief, fiction, current science, and Oracle synthesis.',['cosmologies','reality-ontology'],cards),
    'HOW-TO-USE.md':`# How to Use

## Reference Workflow

Search by title, alias, Class, Domain, Mode, or Evidence State. Open the Tile and read in this order: Essence; Origin; Claim and Evidence Layers; Sources; Meanings; then Relationships. This prevents a compelling symbolic interpretation from being mistaken for a verified literal claim. When a source is class-level context rather than concept-specific evidence, the research note says so explicitly.

For comparison, choose two Tiles and inspect the exact relationship verb. Ask: Is this a documented influence, a fictionalization, a contrast, a subtype, a symbolic echo, or an editorial synthesis? Open the cited source when a factual decision depends on the answer. Similarity alone is never evidence of historical transmission.

## Reflective Reading Workflow

1. Ask a decision-bearing question in which you retain agency: “What should I examine before choosing?” is better than “What will happen?”
2. Choose a spread and record the seed. Reusing the seed reproduces the draw.
3. For each position, read the Tile’s Signal, Gift, Shadow, and position prompt.
4. If a Tile is contested, health-related, conspiratorial, identity-laden, or experiential, read its evidence layer and safety boundary aloud before interpreting it.
5. Name at least one alternative lens. A symbolic reading can remain useful without certifying a literal cause.
6. Finish with the integration practice and one action that improves information, care, consent, or agency.

Example: if **Wormhole** appears at a Threshold, the reading may ask whether the relationship between two endpoints can be redesigned. It does not predict available spacetime travel. The literal layer and sources keep the engineering claim separate from the structural metaphor.

## Story and Game Workflow

Choose a seed Tile, then follow graph neighbors whose edge verbs serve the desired structure: a World can provide a setting; a Faction can govern or oppose; a Technology can enable; an Event can precipitate; a State can complicate a witness. A generator may take one distant surprise only when it exposes the bridge used to reach it. Pin useful choices, reroll the remainder, and retain the structured recipe with the prose output.

Games use declarative operations such as draw, connect, cite-source, challenge-claim, gain, spend, transform, lock, and unlock. The rules do not execute arbitrary code. Clarity, Trust, Agency, Stability, Secrecy, and Evidence Debt make inquiry and consequence playable without rewarding mere certainty.

## Safety Stop Conditions

Stop or reframe when a reading is being used to diagnose, replace care, identify a hidden persecutor, assign collective guilt, prove special ancestry, justify surveillance, or treat coincidence as instruction. Ground in observable facts, consult qualified support where needed, and preserve the user’s ability to decline the reading. See [Epistemic and Safety Policy](../research/EPISTEMIC-AND-SAFETY.md).
`,
    'SCIENCE-FICTION.md':topic('Science Fiction','Science fiction is used as cultural memory, experimental philosophy, and story lineage—not as disguised proof. Franchise references remain scholarly and critical; all Oracle art is original.',['science-fiction','myths'],cards),
    'DISCLOSURE.md':topic('Disclosure','Disclosure is modeled as an information threshold with institutional, psychological, social, and ethical consequences—not as the automatic confirmation of one ontology.',['disclosure','ufology-disclosure-lore'],cards),
    'STARSEEDS.md':topic('Starseeds','Starseed identities are documented as living spiritual cosmologies. The Oracle preserves their internal distinctions while rejecting racial hierarchy, inherited superiority, or factual certainty unsupported by evidence.',['starseed','spiritual-cosmology'],cards),
    'SECRET-SPACE-PROGRAM.md':topic('Secret Space Program','Secret Space Program lore is mapped as a narrative system of hidden capability, divided memory, covert labor, and breakaway power. Documented aerospace secrecy and extraordinary lore remain separate layers.',['secret-space','conspiracy-mythology'],cards),
    'HIGH-STRANGENESS.md':topic('High Strangeness','High strangeness keeps multiple hypotheses active when reports resist a single ontology. Trickster effects, witness context, folklore, and material evidence are not flattened together.',['experiential-report','high-strangeness','hypotheses'],cards),
    'CONSCIOUSNESS.md':topic('Consciousness','Consciousness Tiles distinguish established neuroscience, active research, philosophy, reported experience, esoteric practice, and spiritual interpretation.',['mind-consciousness','states'],cards),
    'TECHNOLOGIES.md':topic('Technologies','Technology Tiles ask what becomes repeatable, who controls the infrastructure, what evidence supports the capability, and what new dependency is created.',['technologies','technology-infrastructure'],cards),
    'BEINGS.md':topic('Beings and Roles','Beings include fictional entities, folklore types, community-described intelligences, machine beings, and social roles. A Tile’s class does not certify its existence.',['beings','intelligence-otherness'],cards),
    'WORLDS.md':topic('Worlds','Worlds include physical places, speculative habitats, fictional settings, secret-location lore, and constructed realities.',['worlds','civilization-ecology'],cards),
    'FACTIONS.md':topic('Factions','Factions coordinate actors around authority, identity, resource, mission, or belief. Living movements are treated without collective blame or unsupported allegations.',['factions'],cards),
    'PROGRAMS.md':topic('Programs','Programs have sponsors, objectives, operations, and traces. Documented operations and later mythic expansions use different evidence layers.',['programs'],cards),
    'CONTROL-SYSTEMS.md':topic('Control Systems','Control is traced through observation, access, classification, incentives, enclosure, attention, identity, and infrastructure. Counter-lineages preserve agency and distributed resilience.',['systems','power-control'],cards),
    'BLOODLINES-AND-LINEAGES.md':topic('Bloodlines and Lineages','Bloodline stories organize continuity, inheritance, legitimacy, secrecy, and destiny. The Oracle rejects racial hierarchy, ethnic blame, antisemitic tropes, and supernatural claims about protected groups.',['bloodline','dynasty','lineage'],cards),
    'BIOHACKING.md':topic('Biohacking','Biohacking spans established tools, active research, speculative medicine, wellness markets, and miracle-repair lore. No Tile provides medical advice.',['body-evolution','biohacking','gene','med-bed','structured-water'],cards),
    'TIME.md':topic('Time','Time Tiles explore physics, fiction, memory, altered experience, and narrative causality without treating evocative analogy as experimental proof.',['time-causality','timeline','time-'],cards),
    'SIMULATION.md':topic('Simulation','Simulation is a philosophical hypothesis, a fictional lineage, a computational metaphor, and a political-psychological frame. These layers are kept distinct.',['simulation','reality-ontology'],cards),
    'ASCENSION.md':topic('Ascension','Ascension is mapped as spiritual cosmology, symbolic transformation, group narrative, and game threshold. It is not presented as verified physiology.',['ascension','liberation-transcendence'],cards),
    'POST-SCARCITY.md':topic('Post-Scarcity','Post-scarcity asks which constraints technology can remove, which institutions remain necessary, and how abundance changes meaning, power, and obligation.',['post-scarcity','abundance'],cards),
    'MEANING-MAKING.md':topic('Meaning-Making','Humans build stories, theories, maps, models, rituals, and games around uncertainty. The Oracle values meaning while preserving falsifiability and revision.',['meaning-making','apophenia','discernment'],cards),
    'DIVINATION.md':`# Divination

## Purpose

Divination in this Oracle is structured reflection under uncertainty. It composes symbols, questions, and graph relationships to reveal assumptions and choices; it does not predict the future or verify a metaphysical claim. Every draw is reproducible from its seed, and every synthesis must expose the Tiles, positions, and typed edges it used.

## Reading Protocol

1. Form a question about a decision, relationship, constraint, or possibility you can influence.
2. Select a spread whose positions fit that question. Use One-Card Reality Check when urgency or certainty is already high.
3. Record the seed, draw, and orientation. The Oracle uses Gift, Shadow, and Threshold rather than reversed cards.
4. Read the Signal first: why might this Tile be relevant now? Then read Gift and Shadow as simultaneous potentials.
5. Read the position prompt. A World at “Cost” functions differently from the same World at “Promise.”
6. Inspect evidence and safety before literalizing any health, political, conspiratorial, spiritual, or anomalous claim.
7. Trace only relationships present in the canonical graph. State whether each edge is sourced or original synthesis.
8. End with Guidance, Integration, and one concrete next action.

## Example

Question: “What should our group examine before publishing this extraordinary claim?” A three-position draw might place **Witness** at Origin, **Evidence Assembly** at Threshold, and **Trust** at Integration. The synthesis can explore testimony, source discipline, and relationship repair. It may not declare the witness correct, diagnose them, or invent an edge. The practical move could be to separate observations from interpretations, document provenance, and agree in advance on what would weaken the claim.

## Safety and Epistemic Boundary

Evidence state is always visible for contested Tiles, and the reading must offer an alternate lens. Divination must never direct medication changes, delay care, accuse a hidden actor, encourage dangerous investigation, rank ancestry or human worth, or convert coincidence into proof. If a reading heightens fear, compulsion, or loss of agency, stop the symbolic work and return to present facts, trusted relationships, and qualified support.

Use [Spreads](SPREADS.md) for layouts, [Graph](GRAPH.md) for legal synthesis edges, and [How to Use](HOW-TO-USE.md) for the full workflow.
`,
    'SPREADS.md':`# Spreads\n\n${systems.spreads.map(s=>`## ${s.title}\n\n${s.intention??s.purpose}\n\n${s.positions.map((p,i)=>`${i+1}. **${p.label??p.title??p.id}:** ${p.prompt}`).join('\n')}\n`).join('\n')}`,
    'GAME-SYSTEMS.md':`# Game Systems\n\nAll games use a small shared vocabulary—Clarity, Discernment, Trust, Agency, Stability, and Secrecy—and a declarative effect language. No arbitrary executable code is stored in game data.\n\n${systems.games.map(g=>`## ${g.title}\n\n${g.premise??g.objective}\n\n- Players: ${Array.isArray(g.players)?g.players.join('–'):g.players}\n- Duration: ${Array.isArray(g.minutes)?`${g.minutes.join('–')} minutes`:g.duration}\n- Win: ${g.terminalCondition??g.resolution?.win}\n- Loss: ${g.resolution?.loss??'not applicable'}\n`).join('\n')}`,
    'STORY-GENERATION.md':`# Story Generation

## Composition Model

A story begins from one seed Tile and fills declared roles such as Character, World, Faction, Technology, Artifact, System, Event, or State. Adjacent graph nodes supply coherent options. One distant surprise is permitted only when the output exposes the bridge connecting it to the seed. Every premise includes a contradiction so that the setting generates choices rather than static lore.

## Method

1. Choose a generator and seed. The same seed and data version must return the same structured result.
2. Draw the anchor Tile, then fill required roles using class and domain constraints.
3. Prefer graph neighbors whose typed verbs explain their dramatic function: “governs,” “enables,” “opposes,” “conceals,” and “precipitates” produce different stories.
4. Add one polarity that cannot be solved by eliminating one side.
5. Add a cost, dependency, or excluded voice from the Shadow meanings.
6. Add one source- or evidence-aware boundary when the premise touches living communities, current politics, health, conspiracy claims, or franchise material.
7. Emit both readable prose and a recipe containing seed, card IDs, edge IDs, pinned slots, fallback reasons, and checksum.

## Example Pattern

Seed a World, follow “governed-by” to a Faction, “enabled-by” to a Technology, and “precipitated-by” to an Event. Add a State for the protagonist and an Artifact that can reveal or distort the conflict. The premise should name why these pieces belong together: “On an orbital habitat whose water is governed as a monopoly, a maintenance worker finds a custody record that could expose the system just as a contact event disrupts supply.”

## Revision and Safety

Users may pin any slot and reroll the rest. Rerolls never erase provenance. Franchise-derived Tiles support criticism and abstraction, not copied characters, logos, dialogue, or plot. Conspiracy and spiritual material must remain labeled by mode and evidence state; story utility is not factual endorsement. See [Graph](GRAPH.md), [Game Systems](GAME-SYSTEMS.md), and [Science Fiction](SCIENCE-FICTION.md).
`,
    'GRAPH.md':`# Graph

## Purpose

The graph stores cards, claim layers, sources, and playable systems as distinct records. Card-to-card edges use registered verbs; **related_to is forbidden** because it hides the actual basis of connection. Historical influence, source context, fictionalization, symbolic resemblance, opposition, containment, transformation, and governance are different relationships and must remain queryable.

## Edge Contract

Every edge records an ID, type, family, directionality, source card, target card, readable label, rationale, basis, confidence, and provenance object. A sourced edge may cite only an external source registered on both endpoint cards. Original synthesis uses the explicit “original-synthesis” source and never impersonates an outside authority. Symmetric edges are normalized deterministically; directed edges preserve source and target.

Every core Tile must have at least four neighbors, at least three edge families, a cross-Domain bridge, and an original-synthesis edge. Sourced edges are used when an external source materially supports the relationship; they are not manufactured merely to give every Tile one. Validation rejects orphans, unknown cards, unregistered verbs, generic related-to edges, missing provenance, and any sourced-edge citation that is not registered on both endpoints.

## Reading an Edge

Read the verb before the two titles. **Monolith catalyzes Evolution** is a claim about a fictional lineage, bounded by its source. **Wormhole enables Stargate** is an editorial/scientific metaphor about mechanism, not a claim that either technology exists. Open the rationale and provenance whenever the distinction matters.

## Lineages and Generation

A lineage is an ordered path with card IDs and edge IDs. Forward reading asks what each threshold makes possible; reverse reading identifies enabling assumptions; diagnostic reading stops at the first implausible transition. Generators may traverse registered edges and must record fallback whenever class fit substitutes for graph adjacency.

## Registered Families

${systems.edgeTypes.map(e=>e.family).filter((v,i,a)=>a.indexOf(v)===i).join(', ')}.

Machine-readable relationships live in \`data/relationships.json\`; lineages live in \`data/lineages.json\`; edge definitions are in \`data/edge-types.json\`.
`,
    'GLOSSARY.md':`# Glossary\n\n## Primary Classes\n\n${classes.map(c=>`- **${c.name}:** ${c.definition}`).join('\n')}\n\n## Inquiry Domains\n\n${inquiryDomains.map(d=>`- **${d.name}:** ${d.question}`).join('\n')}\n\n## Material Modes\n\n${materialModes.map(m=>`- **${m}:** a declared origin/status context, not a truth score.`).join('\n')}\n\n## Evidence States\n\n${evidenceStates.map(e=>`- **${e}:** a claim-level evidence designation.`).join('\n')}\n`
  };
  return files;
}

const designDocument=({title,purpose,invariants,production,states,qa})=>`# ${title}

## Purpose

${purpose}

## Invariants

${invariants.map(item=>`- ${item}`).join('\n')}

## Production Rules

${production.map(item=>`- ${item}`).join('\n')}

## States, Scale, and Accessibility

${states.map(item=>`- ${item}`).join('\n')}

## Quality Gate

${qa.map(item=>`- [ ] ${item}`).join('\n')}

## Rights and Provenance

Every asset must be original or used under documented permission. Record card ID, prompt or source file, creator/tool, model version where relevant, date, dimensions, color profile, source references, rights status, and checksum. Franchise-derived entries use critical symbolic abstraction and may not reproduce protected logos, characters, costumes, frames, screenshots, or production art. Sacred and community-held imagery requires context and authority; do not turn it into generic decoration.
`;

export function buildDesignFiles() {
  const commonStates=['Meaning and class remain legible at 96, 256, 512, 1024, and 2048 pixels.','Selected, hover, focus, disabled, and high-attention states add interface contours outside the master art; they never destructively recolor the source.','No state, class, claim, or evidence condition depends on color alone; accessible live text supplies title, class, evidence, and safety information.'];
  return {
    'VISUAL-CONSTITUTION.md':designDocument({title:'Visual Constitution',purpose:'The Oracle is one family of deep-space instruments: strange, lucid, warm, and alive. Visual richness must clarify the central mechanism rather than compete with it.',invariants:['Every Tile is a true rounded-square object with midnight enamel, warm-metal cloisonné, one restrained class accent, a dominant central symbol, and four cardinal registration points.','One silhouette or mechanism carries the primary meaning. Ornament may support hierarchy but may not invent a second subject.','Lighting is shared: warm upper-left key, narrow cool reflected edge, restrained occlusion, and no atmospheric bloom that destroys contour.','Art contains no words. Titles, evidence states, sources, and safety notices are live interface text.','No photobashing, muddy dithering, random occult decoration, or untraceable borrowed imagery.'],production:['Begin with the card’s class, semantic kernel, evidence mode, and visual prohibited list.','Draw the central mechanism in monochrome before material rendering; reject it if it is confused with six neighboring Tiles.','Apply class border grammar, then enamel and metal passes, then restrained accent and depth.','Export a lossless 2048 master, derived PNG sizes, a thumbnail proof sheet, and metadata record.'],states:commonStates,qa:['Silhouette is recognizable without title at 96 pixels.','Class border is structurally correct and not merely color-coded.','One light direction, clean alpha, no embedded type, and no clipped studs.','Grayscale, contrast, and six-neighbor confusion tests pass.','Rights and provenance record is complete.']}),
    'TILE-SYSTEM.md':designDocument({title:'Tile System',purpose:'This specification makes every Tile interchangeable across print, web, desktop, and game surfaces while preserving one coherent visual family.',invariants:['Canonical raster master is 2048 × 2048 RGBA in sRGB; SVG is preferred when the image is truly vector-native.','Outer corner radius is 12% of width; safe area is 8%; the central symbol occupies 48–64%; cardinal studs register at the same optical positions.','Outer silhouette, border grammar, central mechanism, and accent hierarchy remain separable layers.','Derived files are generated from the master, never upscaled from a smaller export.'],production:['Deliver 2048, 1024, 512, and 256 PNG; include SVG only when paths are clean and no raster texture is embedded deceptively.','Use deterministic filenames: card ID, version, size, and variant. Preserve alpha and do not bake a checkerboard into the image.','Keep master art free of hover glows, focus rings, labels, badges, and evidence chips; those are interface layers.','Validate optical centering rather than relying only on geometric centering.'],states:commonStates,qa:['Pixel dimensions, alpha, color profile, and filename contract pass.','Corner mask and 8% safe area pass.','Central symbol remains 48–64% and is not cropped by border grammar.','Exports match the 2048 master at each scale with no resampling halos.','Metadata and checksum are attached.']}),
    'ENAMEL-SPECIFICATION.md':designDocument({title:'Enamel Specification',purpose:'Enamel gives the Tiles depth, warmth, and material continuity without sacrificing small-scale clarity.',invariants:['Void and midnight enamel are dark, subtly varied surfaces—not pure flat black and not noisy star fields.','Cloisons are continuous, optically corrected, and thick enough to remain visible at 96 pixels.','Enamel cells use restrained value variation; highlights describe fired glass, not glossy plastic.','Texture never crosses metal boundaries or creates false edges.'],production:['Construct metal cloisons first, then fill enamel cells beneath them.','Use a shared upper-left light and narrow lower-right reflected edge.','At 2048, keep structural cloisons at least 12 pixels unless an optical test proves a thinner line survives all exports.','Avoid diffusion dithering; use clean gradients and low-amplitude material texture.'],states:commonStates,qa:['No pinholes, color leakage, broken cloisons, banding, or muddy noise.','Enamel depth survives grayscale.','Highlights remain inside material boundaries.','96-pixel export preserves cell separation.','Material provenance and any texture licenses are recorded.']}),
    'BORDERS.md':designDocument({title:'Class Borders',purpose:'Border grammar communicates Primary Class before the user reads a label. It is a structural language, not decorative variety.',invariants:[...classes.map(c=>`${c.name}: ${c.border}; accent ${c.accent}.`),'All Classes retain the shared rounded-square silhouette and four cardinal registration points.','Classification never depends on accent color alone.'],production:['Build borders as reusable master components with locked registration anchors.','Reserve the central 48–64% symbol field; class grammar should frame rather than invade it.','Test every border against at least three light and three dark central motifs.','Version a border change separately because it affects the entire class.'],states:commonStates,qa:['Every class is distinguishable in grayscale and outline-only views.','Border weight and stud scale are optically consistent across all thirteen Classes.','No border resembles a protected franchise insignia.','Focus and selected contours remain distinguishable from class structure.','Component version and affected cards are recorded.']}),
    'ICONOGRAPHY.md':designDocument({title:'Iconography',purpose:'Iconography turns each concept into one memorable mechanism that can be recognized before its label is read.',invariants:['Prefer mechanisms over illustrations: aperture for observation, interlocked paths for relation, broken ring for threshold, reliquary for artifact, lattice for governance.','The symbol describes the card’s specific semantic kernel rather than repeating a generic rocket, brain, eye, hand, circuit, or star.','Cultural and sacred motifs are not universal clip art; retain context or choose an original abstraction.','Franchise cards abstract the idea under study instead of copying a mark or character.'],production:['Write a one-sentence central-motif brief and three thumbnail silhouettes.','Compare thumbnails with the nearest six concepts by class and meaning.','Choose one dominant shape, one secondary relation, and one restrained accent behavior.','Document rejected motifs when they risk confusion, appropriation, or protected expression.'],states:commonStates,qa:['A blind reviewer can distinguish the Tile from its nearest neighbors.','Symbol matches card essence, not just title association.','No text, logos, portraits, or decorative symbol soup.','Shape remains meaningful in monochrome.','Cultural review and rights notes are complete.']}),
    'COLOR.md':designDocument({title:'Color',purpose:'Color establishes the shared cosmic instrument family, provides restrained class differentiation, and supports accessible interface states.',invariants:['Core tokens: Void #020305; Midnight #061019; Bone #F6E9C8; Warm Gold #E5B554; Aged Brass #85511D.','Class accents are subordinate to silhouette and border grammar.','Selected state uses cyan plus a doubled contour; warning and high-attention states add shape or label, never color alone.','Interface body text meets WCAG AA; critical small text should target AAA where practical.'],production:['Author masters in sRGB and embed the profile.','Check accents against both Midnight and Warm Gold; avoid vibration and clipped saturation.','Use Bone for readable labels, not as a large enamel fill unless conceptually necessary.','Create grayscale and common color-vision-deficiency proofs for each new class batch.'],states:commonStates,qa:['Contrast tests pass for text and controls.','No two adjacent concepts rely only on hue to differ.','Accent area remains restrained and does not overpower the central mechanism.','Print proof preserves dark-field separation.','Color profile and token version are recorded.']}),
    'METALS.md':designDocument({title:'Metals',purpose:'Metal carries structure, hierarchy, registration, and age while enamel carries conceptual depth.',invariants:['Primary metal is warm gold with bright edge, amber body, umber occlusion, and a narrow cool reflected accent.','Aged brass signals archive or legacy; silver is reserved for explicit counterforce or exception, not arbitrary variety.','Chrome, plastic-looking bevels, blown highlights, and conflicting light directions are prohibited.','Metal weight remains consistent across border, cloisons, studs, and central mechanism.'],production:['Use a shared metal ramp and lighting rig across every Tile.','Separate structural metal from decorative engraving so the latter can be removed at thumbnail scale.','Keep brightest highlights below clipping and protect edge definition against dark backgrounds.','Render contact shadows where layers overlap; do not use ambient blur as a substitute for construction.'],states:commonStates,qa:['Metal reads consistently beside six existing masters.','Highlights follow the shared upper-left light.','No clipped whites, crushed occlusion, or jagged bevels.','96-pixel proof retains structural hierarchy.','Any material texture source and license are recorded.']}),
    'TYPOGRAPHY.md':designDocument({title:'Typography',purpose:'Typography communicates title, evidence, rules, and provenance outside the artwork with warmth and high legibility.',invariants:['No words are baked into Tile art.','Use a warm display serif for names and a legible humanist sans for evidence, controls, rules, and navigation.','Minimum interface body is 16 px; minimum metadata is 12 px with sufficient contrast and line height.','Evidence state, safety, and source status are never encoded through type color alone.'],production:['Keep titles as live text with responsive wrapping and a stable accessible name.','Use sentence case for explanatory text and consistent casing for taxonomy labels.','Reserve display face for short names; never use it for dense rules or source notes.','Test long names, diacritics, screen zoom, and localized expansion before release.'],states:commonStates,qa:['200% zoom and keyboard focus preserve reading order.','Long titles do not collide with art or badges.','Font licenses and fallback stacks are documented.','Dynamic text meets contrast and minimum-size rules.','Screen-reader labels expose title, class, evidence state, and action.']}),
    'CARD-BACK.md':designDocument({title:'Card Back',purpose:'The back conceals identity during physical or digital draws while expressing the Oracle’s Chaos ↔ Meaning polarity.',invariants:['The design is rotationally symmetric and contains no marks that identify class, card, orientation, batch, or print position.','An open particulate field resolves into a registered constellation without labeling either pole.','The back is clearly distinct from every front while sharing enamel, metal, corners, and cardinal registration.','No central symbol resembles a canonical Tile.'],production:['Build one master for the release and derive all print/digital variants from it.','Proof imposition, bleed, trim, and duplex registration with a physical sample.','For digital draws, use the same back at all sizes and animate only interface layers.','Record print vendor profiles separately from the sRGB canonical master.'],states:commonStates,qa:['Rotation and mirrored print tests reveal no marked-card cues.','Front/back registration passes physical proof.','Thumbnail remains recognizably a back, not an unknown front.','Animation respects reduced-motion settings.','Version and print-profile provenance are recorded.']}),
    'PACKAGING.md':designDocument({title:'Packaging',purpose:'Packaging keeps the 480-Tile system usable as an archive, divination tool, and game kit rather than an undifferentiated collection.',invariants:['Organize the core into thirteen Class folios inside one archival case.','Include an epistemic key, graph legend, source guide, spreads, game sheets, safety statement, and blank research slips.','Physical and digital contents carry the same release number and checksum manifest.','Packaging may summarize but may not hide evidence or safety boundaries.'],production:['Size folios for sleeved Tiles and include tactile or textual Class labels independent of color.','Provide a complete inventory and replacement-file path.','Digital exports include cards, graph, sources, schemas, manuals, accessibility text, and validation report.','Use archival-safe materials and document print tolerances.'],states:commonStates,qa:['All 480 Tiles reconcile with manifest and folios.','Class labels are accessible without color.','Rules and safety sheet are present.','Digital checksums reproduce.','Vendor, materials, rights, and batch information are recorded.']}),
    'IMAGE-GENERATION-GUIDE.md':designDocument({title:'Image Generation Guide',purpose:'This workflow uses image generation as a controlled production stage while preserving originality, auditability, and system consistency.',invariants:['Prompt order is: card reality and evidence mode; semantic kernel; central mechanism; dominant silhouette; class border; enamel and metal; restrained accent; scale target; exclusions.','Generate without words, logos, characters, likenesses, screenshots, or production-art references.','A generated image is a draft until silhouette, class grammar, rights, material, and thumbnail QA pass.','Reference images may guide material or layout only when rights and intended use are documented.'],production:['Save full prompt, negative constraints, model/tool, model version, seed if available, date, input references, and output checksum.','Composite or retouch only through non-destructive layers and record those changes.','Reject style drift: compare against the Visual Constitution and six neighboring masters.','Create 2048 canonical art; never claim vector status for raster output.'],states:commonStates,qa:['Prompt and provenance record are complete.','No protected or sacred imagery is reproduced without authority.','Border, lighting, and material match the system.','96-pixel, grayscale, and neighbor tests pass.','Human editorial approval is recorded.']}),
    'FLAGSHIP-MASTER.md':designDocument({title:'Flagship Master Plate',purpose:'The flagship plate explores the central Chaos ↔ Meaning polarity as packaging and visual-direction reference. It is not a canonical drawable Tile.',invariants:['The composition moves from an open particulate field toward a registered constellation and archive map.','It remains text-free, original, rounded-square, midnight enamel, and warm gold.','Cyan, violet, and ember are restrained accents rather than competing focal points.','The plate may inspire material and atmosphere but may not replace Class-specific border grammar.'],production:['Reference file: ../reference-tiles/masters/chaos-meaning-cover.png.','Current source: Codex ImageGen, generated 2026-08-24, 1254 × 1254, without reference images.','Before production use, create or approve a 2048 master and record every transformation from the current reference.','Prompt summary: chaotic cosmic field resolving across a central aperture into coherent constellation and archive structure.'],states:commonStates,qa:['Plate is clearly labeled non-canonical in inventories.','No franchise, logo, character, occult cliché, or lettering appears.','Material and lighting pass the shared constitution.','Production master, derived sizes, and checksum exist.','Rights and approval status are explicit.']})
  };
}

export function buildResearchFiles(cards, longlist, sources) {
  const decisions=Object.groupBy?Object.groupBy(longlist,x=>x.decision):longlist.reduce((a,x)=>((a[x.decision]??=[]).push(x),a),{});
  const classCounts=Object.fromEntries(classes.map(c=>[c.id,cards.filter(card=>(card.class??card.primaryClass)===c.id).length]));
  const domainCounts=Object.fromEntries(inquiryDomains.map(d=>[d.id,cards.filter(card=>(card.domains??[]).includes(d.id)).length]));
  const modeCounts=Object.fromEntries(materialModes.map(mode=>[mode,cards.filter(card=>(card.materialModes??[]).includes(mode)).length]));
  const conceptSourced=cards.filter(card=>!card.research?.unsourced).length;
  const contextOnly=cards.length-conceptSourced;
  return {
    'TAXONOMY-JUSTIFICATION.md':`# Taxonomy Justification

## Why Four Axes

The Oracle separates **Primary Class**, **Inquiry Domain**, **Material Mode**, and **Evidence State** because they answer different questions. Class asks what kind of thing a Tile is. Domain asks what question it illuminates. Mode asks which tradition or medium carries it. Evidence State evaluates one bounded claim. Collapsing these axes would make a fictional World appear less meaningful, an experiential report appear scientifically established, or a well-documented movement certify the hidden claims it circulates.

## Primary-Class Rule

A Tile receives exactly one Primary Class based on its most durable grammatical role in a sentence. A Technology is a repeatable capability; a Program deliberately coordinates action; a System persists beyond one actor; an Event makes a before and after; a Hypothesis explains observations. When a title could occupy two Classes, editors ask which role preserves the most distinct relationships and game use. Secondary interpretations belong in Domains, meanings, or graph edges rather than duplicate cards.

${classes.map(c=>`- **${c.name} (${classCounts[c.id]}/${c.target}):** ${c.definition}`).join('\n')}

## Domains, Modes, and Claims

Domains are many-to-many and may change as research improves. Material Modes may coexist: a concept can have a documented historical record, a science-fiction lineage, and a political mythology. Evidence State never applies globally to a Tile. Each claim layer records its own status, summary, and source IDs.

## Maintenance Decision

Before adding a card, compare aliases, archetype, mechanism, evidence boundary, graph role, and visual silhouette. Merge terminology when these remain the same; create a new node only when at least three materially differ. Record any reclassification as a versioned migration so stable IDs and saved readings remain resolvable.
`,
    'CURATION-METHOD.md':`# Curation Method

## Candidate Record

The evaluated longlist contains **${longlist.length.toLocaleString()} serious candidates**: ${Object.entries(decisions).map(([k,v])=>`${k}: ${v.length}`).join('; ')}. Every record retains title, essence, proposed Class and Domains, Modes, provisional relationships, readiness, safety review, eight score dimensions, weighted score, rationale, date, and promotion gate. Rejected or merged ideas remain visible so the same decision can be audited instead of repeatedly rediscovered.

## Scoring

The explicit editorial rubric weights canonical fit 18%, distinctiveness 16%, evidentiary clarity 14%, cross-Domain reach 12%, symbolic depth 12%, gameplay utility 12%, safety readiness 10%, and visual legibility 6%. Every dimension carries a candidate-specific criterion note, and the weighted score is computed from those declared judgments rather than a hash or quota position. Scores organize review; they do not replace editorial judgment. A high total cannot override an unsafe health claim, a duplicated archetype, absent provenance, or copyrighted expression.

## Dispositions

- **Core:** complete content, safety, source context, visual direction, and graph requirements.
- **Expansion-ready:** independent function is clear; one focused evidence/graph/art pass remains.
- **Research-reserve:** promising, but sources, community review, mechanism, or differentiation is incomplete.
- **Merged:** terminology or presentation belongs inside an existing Tile.
- **Deferred:** preserve for specialist or community review without implying readiness.
- **Excluded:** self-sealing, unsafe, non-distinct, or irreducibly dependent on protected expression.

## Promotion Gate

Promotion requires stable ID, distinct essence/archetype/Gift/Shadow, concept-appropriate Modes, claim-level Evidence States, a real external context source, original-synthesis provenance, safety review, four typed neighbors across three families, a cross-Domain bridge, and a 96-pixel visual proof. Regenerate data deterministically, run schema and graph validation, compare coverage, and record the release checksum.
`,
    'SOURCE-REGISTRY.md':`# Source Registry

## Use Contract

Sources are organized into five tiers: Tier 1 primary, official, archival, standard, or peer-reviewed; Tier 2 scholarly or strong institutional secondary; Tier 3 reputable synthesis; Tier 4 community or proponent primary testimony; Tier 5 discovery-only or original editorial synthesis. Tier measures source role, not whether every claim in a source is true.

A **concept-specific source** supports a bounded history, definition, community account, or empirical claim for that Tile. A **class-level contextual source** situates a Tile in a wider field but does not establish that Tile’s literal claims and does not automatically create a graph relationship. A sourced edge may cite only IDs registered on both endpoint cards, and only when that source materially supports the stated relation. Original divinatory text cites original-synthesis and never impersonates outside authority.

Editors must record publisher, date, type, scope, stable URL, access date, rights note, and the exact claim or context supported. Official agencies are sources for their own records and definitions, not automatically neutral arbiters of every contested interpretation. Community testimony documents community meaning, not scientific consensus. Quotes and embedded media require separate rights review.

## Maintenance

At each release, test URLs, preserve archived copies where rights allow, flag changed or withdrawn sources, and never silently replace a contradictory record. Add a new source ID rather than changing the identity of an old citation. Re-run endpoint intersection and source-coverage audits after every registry change.

${sources.map(s=>`## ${s.title}\n\n- ID: \`${s.id}\`\n- Publisher: ${s.publisher}\n- Date: ${s.date}\n- Type / tier: ${s.kind}, Tier ${s.tier}\n- Scope: ${s.scope}\n- URL: ${s.url}\n- Rights: ${s.rights}`).join('\n\n')}
`,
    'EPISTEMIC-AND-SAFETY.md':`# Epistemic and Safety Policy

## Epistemic Rules

Extraordinary claims are not verified by cultural importance, testimony volume, institutional secrecy, technical vocabulary, or symbolic usefulness. Established science, active research, documented history, speculation, fiction, experience, and symbolism remain visible as different layers. A contextual citation must not be described as concept-specific evidence. Contradictory evidence is retained rather than harmonized into a hidden synthesis.

## High-Attention Areas

Health Tiles may not diagnose, recommend treatment changes, delay qualified care, or market miracle repair. Experiential and mental-health-adjacent Tiles validate distress and meaning without certifying a frightening external cause. Political and conspiracy Tiles avoid allegations about living people, collective guilt, antisemitic or racialized tropes, dehumanization, and self-sealing enemy models. Identity and ancestry Tiles may not rank blood, race, disability, or human worth. Living spiritual traditions require their own context and community authority.

## Reading Controls

Show Evidence State and safety flags before interpretation. Offer an alternate lens. Use decision-bearing questions that preserve agency. End with grounded action. Stop when a reading increases compulsion, fear, persecutory certainty, dangerous investigation, or dependence on the Oracle. Direct users toward present facts, trusted people, and qualified care where appropriate.

## Editorial Review

Flag potentially harmful titles by topic and by generated prose. Review literal, cultural, and symbolic layers independently. Confirm sources actually support the claim stated. Test graph edges for defamation or false causation. Review art for hate imagery, protected expression, diagnosis, and cultural appropriation. Record reviewer, rationale, date, required boundary, and next review.
`,
    'CONTRADICTION-MODEL.md':`# Contradiction Model

## Principle

A Tile can carry several incompatible claims because the Oracle models discourse, not a single compulsory ontology. Contradiction is represented explicitly through claim layers, evidence states, sources, and typed graph edges. It is not resolved by averaging every account into “somewhere in between.”

## Examples

**Fifteen-Minute City** contains a documented urban-planning practice and a separate political mythology of enforced enclosure. Evidence for the planning model does not validate the control narrative. **MKULTRA** contains documented state abuse and later expansions; verified misconduct must not certify every subsequent allegation. **Med Bed** is a useful symbolic technology of complete repair, while current miracle-device claims are contradicted by available evidence and can cause health harm.

## Data Rule

Each claim records layer, Material Modes, Evidence State, summary, and source IDs. Relationships carry a rationale and provenance kind. “Contrasts-with,” “later-mythologized-as,” “fictionalizes,” and “contextualizes” allow disagreement without inventing a generic relationship. When sources disagree, retain both and narrow the claim to what each can actually establish.

## Reading Rule

State which layer is being read. Offer at least one rival explanation. Name what evidence would increase or decrease confidence. Preserve symbolic value without using it as literal proof. If no concept-specific source exists, mark the gap and treat the history as a research lead.
`,
    'COVERAGE-AUDIT.md':`# Coverage Audit

## Release Snapshot

The core contains **${cards.length} Tiles**. Class counts: ${Object.entries(classCounts).map(([k,v])=>`${k} ${v}`).join('; ')}. Domain participation: ${Object.entries(domainCounts).map(([k,v])=>`${k} ${v}`).join('; ')}. Material-mode participation: ${Object.entries(modeCounts).filter(([,v])=>v).map(([k,v])=>`${k} ${v}`).join('; ')}.

All cards have a real external contextual source and original-synthesis provenance. Graph relationships prioritize semantic truth: sourced edges appear where a shared citation supports the relationship, while other defensible editorial relations remain explicitly original synthesis. **${conceptSourced}** cards currently have at least one concept-specific external source; **${contextOnly}** retain an explicit concept-specific research gap. Contextual coverage is not reported as claim evidence.

## Subject Coverage

The release includes classic and contemporary science fiction, space opera, cyberpunk, AI, first contact, Disclosure, Secret Space Program lore, starseed cosmologies, high strangeness, consciousness, biohacking, ancient-world narratives, control systems, time, simulation, post-scarcity, apocalypse, utopia, and transhumanism. Anchor lineages include Mythic Past, Aspirational Future, Dune, The Matrix, Monolith/2001, Frankenstein, War of the Worlds, Star Trek, Star Wars, Neuromancer, Solaris, and contemporary AI.

## Audit Method

For each release, compare actual Class counts with targets; Domain and Mode distribution; concept-specific source coverage; safety flags; edge degree and family diversity; cross-Domain bridges; lineage completeness; and visual confusion at thumbnail scale. Investigate clusters that rely on one source, one community voice, one era, or one geography. Coverage means meaningful differentiated treatment, not merely title presence.

## Open Work

Prioritize concept-specific sourcing for context-only cards, primary/community sources for living traditions, non-Western science-fiction histories, disability and care perspectives, and source renewal for current movements. New coverage must not be achieved by flattening culturally distinct ideas into generic analogues.
`,
    'FUTURE-ESOTERIC-SOURCE-NOTE.md':`# Future Esoteric Source Note

## Current Status

The brief names **Future Esoteric: The Unseen Realms** as a supplied reference corpus. No copy was present in the supplied attachments or project workspace during this build. The Oracle therefore preserves a dedicated ingestion slot but does not claim to have mined, summarized, quoted, or evaluated that unavailable corpus. No current card, edge, score, or evidence state depends on it.

## Intake and Rights Record

When the corpus is supplied, register the exact title, edition, creator, publisher, publication date, stable identifier, file checksum, access date, and access conditions. Record whether it is public, licensed, privately supplied for research, or restricted. Copyright ownership and access do not automatically grant quotation, redistribution, image extraction, model-training, or publication rights. Keep a source copy outside generated output when redistribution is not authorized, and expose only the metadata and compliant notes.

## Claim-Level Extraction

Create an extraction ledger with page or section location, bounded claim, speaker or tradition, material mode, proposed evidence state, target card IDs, and reviewer note. Distinguish authorial assertion, quotation of another source, community self-description, historical claim, reported experience, speculation, and symbolic teaching. Do not convert resemblance into historical influence, and do not treat a modern synthesis as the authoritative meaning of a living tradition.

## Canon Mapping

Map supported histories or community meanings to existing cards first. Create a new candidate only when its essence, archetype, evidence boundary, graph role, divinatory use, and visual silhouette are materially independent. Potential aliases belong in the existing node. Contradictory sources remain visible; ingestion may narrow or complicate a claim but may not silently delete counterevidence or upgrade an Evidence State.

## Review and Release Gate

Require rights review, source-tier assignment, tradition or community review where relevant, safety review, and a citation check that each note says no more than the source supports. Re-run content, schema, candidate, graph, safety, contradiction, and coverage validation. Compare checksums and deterministic outputs, document every changed card or edge, and publish the ingestion only in a new versioned release with a reversible migration record.
`
  };
}

export function buildSchemas() {
  const draft='https://json-schema.org/draft/2020-12/schema';
  const base=(id,title)=>({$schema:draft,$id:id,title});
  const nonempty={type:'string',minLength:1};
  const identifier={type:'string',pattern:'^[a-z0-9][a-z0-9-]*$'};
  const classId={enum:classes.map(c=>c.id)};
  const domainId={enum:inquiryDomains.map(d=>d.id)};
  const modeId={enum:materialModes};
  const evidenceId={enum:evidenceStates};
  const stringList={type:'array',items:nonempty};
  const pairOfIntegers={type:'array',minItems:2,maxItems:2,prefixItems:[{type:'integer',minimum:0},{type:'integer',minimum:0}],items:false};
  const effectOperations=['draw','reveal','place','connect','cite-source','challenge-claim','gain','spend','advance-track','retreat-track','tag','branch','vote','swap','transform','lock','unlock'];
  const effectProperties={
    op:{enum:effectOperations},
    amount:{type:'integer'}, card:nonempty, claim:nonempty, condition:nonempty, count:{type:'integer',minimum:0}, edgeType:nonempty,
    first:nonempty, into:nonempty, question:nonempty, resource:nonempty, second:nonempty, stake:nonempty, tag:nonempty,
    target:nonempty, threshold:nonempty, to:nonempty, track:nonempty, zone:nonempty,
    from:{oneOf:[nonempty,{type:'object',additionalProperties:false,properties:{classes:{type:'array',uniqueItems:true,items:classId},exclude:nonempty}}]},
    then:{type:'array',items:{$ref:'#/$defs/effect'}},
    otherwise:{type:'array',items:{$ref:'#/$defs/effect'}},
  };
  const effect={type:'object',additionalProperties:false,required:['op'],properties:effectProperties};
  const effectDocument={...base('game-effect.schema.json','Declarative Game Effect'),$defs:{effect},$ref:'#/$defs/effect'};
  const schemas={
    'card.schema.json':{
      ...base('card.schema.json','Oracle Card'),type:'object',additionalProperties:false,
      required:['id','ordinal','title','aliases','class','className','domains','primaryDomain','secondaryDomains','domainNames','materialModes','epistemicLayers','sources','origin','culturalHistory','essence','archetype','gift','shadow','meanings','divination','guidance','integration','question','storySeed','gameRoles','polarities','relationships','relationshipRequirements','visual','safety','research','version'],
      properties:{
        id:identifier,ordinal:{type:'integer',minimum:1},title:{type:'string',minLength:2},aliases:stringList,class:classId,className:nonempty,
        domains:{type:'array',minItems:1,maxItems:3,uniqueItems:true,items:domainId},primaryDomain:domainId,
        secondaryDomains:{type:'array',maxItems:2,uniqueItems:true,items:domainId},domainNames:{type:'array',minItems:1,maxItems:3,uniqueItems:true,items:nonempty},
        materialModes:{type:'array',minItems:1,uniqueItems:true,items:modeId},
        epistemicLayers:{type:'array',minItems:3,items:{type:'object',additionalProperties:false,required:['id','layer','materialModes','status','statement','sourceIds'],properties:{id:identifier,layer:nonempty,materialModes:{type:'array',uniqueItems:true,items:modeId},status:evidenceId,statement:{type:'string',minLength:20},sourceIds:{type:'array',uniqueItems:true,items:identifier}}}},
        sources:{type:'array',minItems:2,uniqueItems:true,items:identifier},
        origin:{type:'object',additionalProperties:false,required:['summary','provenanceRule','traditionCare'],properties:{summary:{type:'string',minLength:20},provenanceRule:{type:'string',minLength:20},traditionCare:{type:'string',minLength:20}}},
        culturalHistory:{type:'string',minLength:20},essence:{type:'string',minLength:10},archetype:nonempty,gift:{type:'string',minLength:5},shadow:{type:'string',minLength:5},
        meanings:{type:'object',additionalProperties:false,required:['psychological','social','spiritual','technological','political'],properties:{psychological:{type:'string',minLength:20},social:{type:'string',minLength:20},spiritual:{type:'string',minLength:20},technological:{type:'string',minLength:20},political:{type:'string',minLength:20}}},
        divination:{type:'object',additionalProperties:false,required:['signal','upright','shadow','positionPrompts'],properties:{signal:{type:'string',minLength:20},upright:{type:'string',minLength:20},shadow:{type:'string',minLength:20},positionPrompts:{type:'object',additionalProperties:false,required:['origin','present','threshold','integration'],properties:{origin:nonempty,present:nonempty,threshold:nonempty,integration:nonempty}}}},
        guidance:{type:'string',minLength:20},integration:{type:'object',additionalProperties:false,required:['practice','boundary','completionSignal'],properties:{practice:{type:'string',minLength:20},boundary:{type:'string',minLength:10},completionSignal:{type:'string',minLength:20}}},question:nonempty,storySeed:{type:'string',minLength:20},
        gameRoles:{type:'array',minItems:1,uniqueItems:true,items:identifier},polarities:{type:'array',minItems:1,uniqueItems:true,items:identifier},relationships:{type:'array',uniqueItems:true,items:identifier},
        relationshipRequirements:{type:'object',additionalProperties:false,required:['minimumDegree','minimumEdgeFamilies','requireCrossDomain','requireSourcedEdge','requireOriginalSynthesisEdge','status'],properties:{minimumDegree:{type:'integer',minimum:0},minimumEdgeFamilies:{type:'integer',minimum:0},requireCrossDomain:{type:'boolean'},requireSourcedEdge:{type:'boolean'},requireOriginalSynthesisEdge:{type:'boolean'},status:nonempty,actualDegree:{type:'integer',minimum:0},actualEdgeFamilies:{type:'integer',minimum:0},edgeFamilies:{type:'array',uniqueItems:true,items:identifier},hasCrossDomain:{type:'boolean'},hasSourcedEdge:{type:'boolean'},hasOriginalSynthesisEdge:{type:'boolean'}}},
        visual:{type:'object',additionalProperties:false,required:['alt','centralMotif','borderGrammar','accent','material','typography','prohibited'],properties:{alt:nonempty,centralMotif:nonempty,borderGrammar:nonempty,accent:{type:'string',pattern:'^#[0-9A-Fa-f]{6}$'},material:nonempty,typography:nonempty,prohibited:stringList}},
        safety:{type:'object',additionalProperties:false,required:['level','flags','boundaries','use'],properties:{level:{enum:['standard','reviewed','high-attention']},flags:{type:'array',uniqueItems:true,items:identifier},boundaries:{type:'array',minItems:1,uniqueItems:true,items:nonempty},use:nonempty}},
        research:{type:'object',additionalProperties:false,required:['confidence','editorialStatus','sourceCoverage','contextualSourceCoverage','contextualSourceId','unsourced','sourceGap','openQuestions','nextReview'],properties:{confidence:nonempty,editorialStatus:nonempty,sourceCoverage:{type:'integer',minimum:0},contextualSourceCoverage:{type:'integer',minimum:0},contextualSourceId:{type:['string','null']},unsourced:{type:'boolean'},sourceGap:{type:['string','null']},openQuestions:{type:'array',minItems:1,items:nonempty},nextReview:{type:'string',format:'date'}}},
        version:{type:'object',additionalProperties:false,required:['number','status','dated','schema','contentHash'],properties:{number:{type:'string',pattern:'^\\d+\\.\\d+\\.\\d+$'},status:nonempty,dated:{type:'string',format:'date'},schema:nonempty,contentHash:{type:'string',pattern:'^[0-9a-f]{8}$'}}},
      },
    },
    'relationship.schema.json':{
      ...base('relationship.schema.json','Typed Relationship'),type:'object',additionalProperties:false,
      required:['id','type','relationship','family','directionality','source','target','label','rationale','basis','confidence','confidenceLabel','derivation','reviewStatus','assertiveness','substantive','provenance'],
      properties:{id:identifier,type:identifier,relationship:identifier,family:identifier,directionality:{enum:['directed','symmetric']},source:identifier,target:identifier,label:nonempty,rationale:{type:'string',minLength:20},basis:{enum:['sourced','original-synthesis']},confidence:{type:'number',minimum:0,maximum:1},confidenceLabel:{enum:['source-guided','high','editorial']},derivation:{oneOf:[{type:'object',additionalProperties:false,required:['kind','specificationId'],properties:{kind:{const:'curated'},specificationId:identifier}},{type:'object',additionalProperties:false,required:['kind','ruleId','features'],properties:{kind:{const:'rule'},ruleId:identifier,features:{type:'object',additionalProperties:false,minProperties:1,properties:{sourceIds:{type:'array',minItems:1,uniqueItems:true,items:identifier},classId:classId,domainIds:{type:'array',minItems:1,uniqueItems:true,items:domainId}}}}},{type:'object',additionalProperties:false,required:['kind','lineageId','step'],properties:{kind:{const:'lineage'},lineageId:identifier,step:{type:'integer',minimum:0}}}]},reviewStatus:{enum:['reviewed','rule-validated']},assertiveness:{enum:['assertive','literal-classification']},substantive:{type:'boolean'},provenance:{type:'object',additionalProperties:false,required:['kind','basis','sourceIds','note'],properties:{kind:{enum:['sourced','original-synthesis']},basis:{enum:['sourced','original-synthesis']},sourceIds:{type:'array',minItems:1,uniqueItems:true,items:identifier},note:{type:'string',minLength:20}}}},
    },
    'lineage.schema.json':{
      ...base('lineage.schema.json','Lineage'),type:'object',additionalProperties:false,required:['id','title','thesis','inquiryDomainId','cardIds','edgeIds','missingCardNames','reading'],
      properties:{id:identifier,title:nonempty,thesis:{type:'string',minLength:20},inquiryDomainId:domainId,cardIds:{type:'array',minItems:2,uniqueItems:true,items:identifier},edgeIds:{type:'array',uniqueItems:true,items:identifier},missingCardNames:{type:'array',uniqueItems:true,items:nonempty},reading:{type:'object',additionalProperties:false,required:['forward','reverse','diagnostic'],properties:{forward:{type:'string',minLength:20},reverse:{type:'string',minLength:20},diagnostic:{type:'string',minLength:20}}}},
    },
    'spread.schema.json':{
      ...base('spread.schema.json','Spread'),type:'object',additionalProperties:false,required:['id','title','cards','purpose','positions'],
      properties:{id:identifier,title:nonempty,cards:{type:'integer',minimum:1},purpose:{type:'string',minLength:20},positions:{type:'array',minItems:1,items:{type:'object',additionalProperties:false,required:['id','title','prompt','allowedClasses'],properties:{id:identifier,title:nonempty,prompt:{type:'string',minLength:10},allowedClasses:{type:'array',uniqueItems:true,items:classId}}}}},
    },
    'game.schema.json':{
      ...base('game.schema.json','Executable Oracle Game'),type:'object',additionalProperties:false,
      required:['id','title','players','minutes','mode','objective','resources','setup','turn','resolution','resourceDefinitions','trackDefinitions','zoneDefinitions','edgeTypeDefinitions','actionTrackDeltas','resolutionPredicates','fixtures'],
      properties:{
        id:identifier,title:nonempty,players:pairOfIntegers,minutes:pairOfIntegers,mode:nonempty,objective:{type:'string',minLength:20},resources:{type:'array',minItems:1,uniqueItems:true,items:identifier},
        setup:{type:'array',minItems:1,items:{$ref:'#/$defs/effect'}},
        turn:{type:'array',minItems:1,items:{type:'object',additionalProperties:false,required:['id','label','cost','effects'],properties:{id:identifier,label:nonempty,cost:{type:'array',items:{type:'object',additionalProperties:false,required:['resource','amount'],properties:{resource:identifier,amount:{type:'integer',minimum:0}}}},effects:{type:'array',minItems:1,items:{$ref:'#/$defs/effect'}}}}},
        resolution:{type:'object',additionalProperties:false,required:['win','loss'],properties:{win:{type:'string',minLength:10},loss:{type:'string',minLength:10}}},
        resourceDefinitions:{type:'array',minItems:1,items:{$ref:'#/$defs/track'}},trackDefinitions:{type:'array',minItems:1,items:{$ref:'#/$defs/track'}},
        zoneDefinitions:{type:'array',items:{type:'object',additionalProperties:false,required:['id','kind','initialCount'],properties:{id:nonempty,kind:{const:'state-reference'},initialCount:{type:'integer',minimum:0}}}},
        edgeTypeDefinitions:{type:'array',minItems:1,uniqueItems:true,items:identifier},
        actionTrackDeltas:{type:'object',additionalProperties:{type:'object',additionalProperties:{type:'integer'}}},
        resolutionPredicates:{type:'object',additionalProperties:false,required:['win','loss'],properties:{win:{$ref:'#/$defs/predicateGroup'},loss:{$ref:'#/$defs/predicateGroup'}}},
        fixtures:{type:'object',additionalProperties:false,required:['win','loss'],properties:{win:{type:'array',minItems:1,items:{$ref:'#/$defs/fixtureStep'}},loss:{type:'array',minItems:1,items:{$ref:'#/$defs/fixtureStep'}}}},
      },
      $defs:{
        effect,
        track:{type:'object',additionalProperties:false,required:['id','name','range','initial','kind'],properties:{id:identifier,name:nonempty,range:pairOfIntegers,initial:{type:'integer'},kind:{enum:['resource','progress','risk']},meaning:nonempty}},
        predicate:{type:'object',additionalProperties:false,required:['track','operator','value'],properties:{track:identifier,operator:{enum:['>=','<=','>','<','=','==']},value:{type:'number'}}},
        predicateGroup:{type:'object',additionalProperties:false,minProperties:1,maxProperties:1,properties:{all:{type:'array',minItems:1,items:{$ref:'#/$defs/predicate'}},any:{type:'array',minItems:1,items:{$ref:'#/$defs/predicate'}}}},
        fixtureStep:{oneOf:[identifier,{type:'object',additionalProperties:false,required:['actionId','branchChoice'],properties:{actionId:identifier,branchChoice:{enum:['then','otherwise']}}}]},
      },
    },
    'game-effect.schema.json':effectDocument,
  };
  for (const [filename,classValue,title] of [['faction.schema.json','factions','Faction Card'],['technology.schema.json','technologies','Technology Card'],['world.schema.json','worlds','World Card']]) {
    schemas[filename]={...base(filename,title),allOf:[{$ref:'card.schema.json'},{properties:{class:{const:classValue}}}]};
  }
  return schemas;
}
