Skip to content

feat(lore): Arcanea Monster System + Nethyssa Leviathan + Hermes pipeline - #76

Draft
frankxai wants to merge 9 commits into
mainfrom
claude/arcanea-kraken-monster-system-t24p73
Draft

frankxai wants to merge 9 commits into
mainfrom
claude/arcanea-kraken-monster-system-t24p73

Conversation

@frankxai

@frankxai frankxai commented Jun 22, 2026

Copy link
Copy Markdown
Owner

What this adds

A reusable Monster System for Arcanea plus its flagship: Nethyssa, the Abyss That Dreams — a planet-scale Kraken Leviathan with full canon, game design, and the pipelines to keep generating more.

Canon (STAGING — does not touch the locked Ten)

  • Monster System taxonomy (.arcanea/lore/creatures/MONSTER_SYSTEM.md): T0 Motes → T1 Beasts → T2 Shades (the existing psychological bestiary, slotted in) → T3 Leviathans / Wild Godbeasts (NEW) → T4 Gate Godbeasts (locked), with a Shadow corruption track at every tier.
  • The Leviathan tier registered in CANON_LOCKED.md (STAGING ⏳) + approval-log entries. Leviathans are unbonded beasts of Nero's Unformed that roam outside the Ten Gates — the locked Ten Godbeasts are untouched.
  • Nethyssa (.arcanea/lore/leviathans/nethyssa.md): Water + Void, the sub-Gate "Abyssal Hum", kin-adjacent to Veloura, kept dreaming by the Tidesong; material = the Nethyss Pearl; corruption = the Drowned Shadow (extinction-tier). Plus indexes + a game-design spec (raid/world-boss, Master+ gating, phase ladder, drops, weaknesses).

Book

  • book/legends-of-arcanea/the-rising-of-nethyssa.md — The First Drowning, as Arcanean flood-scripture.
  • book/bestiary-of-creation/the-kraken-brood.md — Krakenlings, Abyssal Tendrils, Drowned Heralds.

Code (MCP)

  • packages/arcanea-mcp/src/data/leviathans/Leviathan / GameMonster interfaces (extend BestiaryCreature) + Nethyssa + brood data.
  • generators.ts + index.ts — new generate_leviathan MCP tool (returns canonical Nethyssa for named Water/Void, else a procedural Wild Godbeast).

Tests ✅

  • packages/arcanea-mcp/tests/leviathan-generators.test.mjs46 tests, 46 passing (Node built-in test runner).
    • generateLeviathan: named+Water returns canonical Nethyssa fields exactly; named+unregistered element returns procedural with note; named=false always procedural, no note.
    • classifyCanonFit: 14 tests covering spectrum/tier/gate logic — leviathan scale → T3/gate required, godbeast → T4/gate required, bonded override, scale-wins-over-bonded, Nethyssa capture profile end-to-end.
    • inferCaptureType: 16 tests covering extension normalization, first-match semantics, mime fallback, unknown → undefined, defaultCanonTier priors.
  • Logic for classifyCanonFit and inferCaptureType inlined in test file (.arcanea/intake/ is tool-agnostic, not in the arcanea-mcp tsconfig build graph — same pattern as feedback-bridge.test.mjs).

Pipelines & loops

  • Arcanea Hermes intake→conversion: skill + agent + /hermes command + .arcanea/intake/ substrate (classify capture → grade canon fit → fan to producers → canon-gate → review queue; never auto-publishes).
  • Generation: .arcanea/gen/ aesthetic lanes + multi-harness routing (Grok Imagine / Codex gpt-image-2 / Antigravity NB2) + a ready-to-paste Nethyssa prompt-pack.
  • Workflows: .arcanea/WORKFLOWS.md — lore / asset / web3 loops, each wired to canon-check + lock-decision + the visual-creation council.

Status / gates

  • Nethyssa is STAGING. LOCKED canon requires Frank's explicit /lock-decision.
  • Image generation runs in the external harnesses' own CLIs — this PR ships the prompt-packs + routing they consume.
  • arcanea-web CI: ✅ Ready. arcanea-2 (apps/academy): ❌ pre-existing failure unrelated to this branch (apps/academy missing next in package.json).

Verification

  • 46/46 unit tests passing (node --test packages/arcanea-mcp/tests/leviathan-generators.test.mjs)
  • New MCP TS (generators.ts, leviathans/index.ts, index.ts edits) parse clean. CI build on this PR is the authoritative check.

🤖 Generated with Claude Code


Generated by Claude Code

…line

Add a five-tier Monster System taxonomy (T0 Motes → T4 Gate Godbeasts +
Shadow corruption track) and a new STAGING Leviathan / Wild Godbeast tier
that sits OUTSIDE the locked Ten Gates. The locked Ten Godbeasts are
untouched.

Flagship: Nethyssa, the Abyss That Dreams — a planet-scale, unbonded
abyssal kraken (Water + Void), kept dreaming by the Tidesong, with the
Nethyss Pearl material and the Drowned Shadow corruption counterpart.

- Canon: CANON_LOCKED.md Leviathan tier (STAGING) + approval log
- Lore: nethyssa.md, MONSTER_SYSTEM.md, indexes, game-design spec
- Book: The Rising of Nethyssa (legend) + The Kraken Brood (bestiary)
- MCP: Leviathan/GameMonster data + generate_leviathan tool
- Hermes: intake→convert pipeline (skill + agent + /hermes command + substrate)
- Gen: aesthetic lanes + multi-harness routing + Nethyssa prompt-pack
- Workflows: lore / asset / web3 loops

Nethyssa is STAGING — LOCKED requires Creator approval via /lock-decision.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rnGVLqNrCLJz2KJnEcRos
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

Invalid vercel.json file provided

@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
arcanea-2 Error Error Jun 23, 2026 11:04am
arcanea-web Ready Ready Preview, Comment, Open in v0 Jun 23, 2026 11:04am

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f6365cf-676a-4b21-b3ac-d250e2292721

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/arcanea-kraken-monster-system-t24p73

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the Tier 3 Leviathan / Wild Godbeast taxonomy to the Arcanea Monster System, establishing the flagship creature Nethyssa, the Abyss That Dreams, along with extensive lore, game design specifications, and asset generation pipelines. It also integrates a new generate_leviathan tool into the MCP package. The review feedback highlights opportunities to improve robustness and consistency, specifically by normalizing file extensions in inferCaptureType to handle missing leading dots, ensuring case-insensitive handling of the element parameter in the generator, and aligning the generated Leviathan's weaknesses property with the canonical elementalWeaknesses interface.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +115 to +120
export function inferCaptureType(mimeType: string, extension: string): CaptureTypeMeta | undefined {
const lowerExt = extension.toLowerCase()
const byExt = CAPTURE_TYPE_LIST.find((c) => c.extensions.includes(lowerExt))
if (byExt) return byExt
return CAPTURE_TYPE_LIST.find((c) => c.mimeTypes.includes(mimeType))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The inferCaptureType function assumes that the extension parameter always starts with a leading dot (e.g., ".md"). If a caller passes an extension without a dot (e.g., "md"), the lookup will fail. Normalizing the extension by ensuring it starts with a dot makes the function much more robust.

Suggested change
export function inferCaptureType(mimeType: string, extension: string): CaptureTypeMeta | undefined {
const lowerExt = extension.toLowerCase()
const byExt = CAPTURE_TYPE_LIST.find((c) => c.extensions.includes(lowerExt))
if (byExt) return byExt
return CAPTURE_TYPE_LIST.find((c) => c.mimeTypes.includes(mimeType))
}
export function inferCaptureType(mimeType: string, extension: string): CaptureTypeMeta | undefined {
const lowerExt = extension.toLowerCase()
const normalizedExt = lowerExt.startsWith('.') ? lowerExt : `.${lowerExt}`
const byExt = CAPTURE_TYPE_LIST.find((c) => c.extensions.includes(normalizedExt))
if (byExt) return byExt
return CAPTURE_TYPE_LIST.find((c) => c.mimeTypes.includes(mimeType))
}

named?: boolean;
}): Promise<ToolResult> {
// Tier 3 Wild Godbeast — unbonded titan outside the Ten Gates.
const element = options.element ?? pick(["Water", "Void", "Fire", "Earth", "Wind"]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The element parameter is compared using exact case-sensitive matches (e.g., element === "Water"). If a lowercase or mixed-case element is passed (e.g., "water"), these checks will fail, leading to incorrect behavior or fallback to generic values. Normalizing the element name to have the first letter capitalized ensures case-insensitive safety.

Suggested change
const element = options.element ?? pick(["Water", "Void", "Fire", "Earth", "Wind"]);
const rawElement = options.element ?? pick(["Water", "Void", "Fire", "Earth", "Wind"]);
const element = rawElement.charAt(0).toUpperCase() + rawElement.slice(1).toLowerCase();

description: `An unbonded titan of Nero's Unformed, ${temperament}. Power that was never given a name to obey.`,
material: `${name}'s ${element === "Water" ? "Pearl" : element === "Fire" ? "Cinder" : element === "Void" ? "Obsidian" : "Shard"}`,
weaknesses: temperament === "corrupted" ? ["Spirit", "the Tidesong"] : ["Fire", "Spirit"],
canon: "STAGING — Wild Godbeast generator. Lock via /lock-decision before treating as canon.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The returned object uses the key weaknesses, but the canonical GameMonster and Leviathan interfaces defined in data/leviathans/index.ts use elementalWeaknesses. Aligning this field name ensures consistency across the codebase.

Suggested change
canon: "STAGING — Wild Godbeast generator. Lock via /lock-decision before treating as canon.",
elementalWeaknesses: temperament === "corrupted" ? ["Spirit", "the Tidesong"] : ["Fire", "Spirit"],

@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown

PR Review — feat(lore): Arcanea Monster System + Nethyssa Leviathan + Hermes pipeline

Overall: Well-structured PR. The canon-safety design (STAGING flag, Frank-gated /lock-decision, never-auto-publish) is solid. The TypeScript is clean and the file organisation respects the project's directory conventions. A few concrete issues to address before merging.


Bugs / Issues

1. Spurious space in suffix array (generators.ts)

const suffix = pick(["yssa", " thor", "alth", "umbra", "oraxis", "ystra", "akar"])

" thor" has a leading space. After .replace(/\s+/g, ""), "Neth" + " thor""Neththor" (double-h) rather than the intended "Netthor". Every other suffix is space-free. Should be "thor".

2. Silent fallthrough for named: true with non-Water/Void element (generators.ts)

if (options.named && (element === "Water" || element === "Void")) {
  return /* Nethyssa */
}
// else falls through to procedural generation

A caller passing { named: true, element: "Fire" } gets a procedural result with no indication the named flag was ignored. Either expand the named-entity guard to all elements, or return an explicit error/note when named is true but no canonical entry exists for that element.

3. Silent fallback on unknown element key (generators.ts)

const root = pick(leviathanRoots[elementKey] || leviathanRoots.void)

Passes validation silently. An unknown element (e.g. a typo like "watter") falls back to Void roots without any signal to the caller. Consider logging a warning or documenting the fallback in the function's JSDoc.


Design-token discrepancy

lanes.ts labels #00bcd4 as "Atlantean Teal", but .claude/CLAUDE.md (the canonical design system reference) defines Atlantean Teal as #7fffd4. This means every prompt-pack and lane will generate images with the wrong brand colour. Confirm which hex is authoritative and align lanes.ts accordingly.


Minor code-quality notes

canon-spectrum.ts comment vs reality

The file header says "Standalone + typed — no external imports" but then imports CanonTier from ./capture-types. The intent is clearly "no imports from the main app graph" (which is correct and desirable), but the comment is misleading as written. Suggest: "no imports from the app build graph".

Neutral-capture spectrum default

In classifyCanonFit, a capture where all signals are absent (introducesNewEntity = false, referencesLockedCanon = false, techLeaning = false) falls to spectrum = 'mixed'. Mixed triggers a more cautious pipeline path than 'tech'. That's conservative and probably the right call, but it's worth a comment explaining the deliberate choice so a future editor doesn't "fix" it.


Test coverage

The intake pipeline (classifyCanonFit, inferCaptureType, getCaptureType) and the new MCP tool (generateLeviathan) have zero tests. Given that the entire Hermes design principle is never auto-publish / always gate, having untested gate-logic is the highest-risk gap here. Suggest at minimum:

  • classifyCanonFit unit tests covering T3/T4 entities, the bonded fallback, and the conservative default
  • inferCaptureType unit tests for extension-wins-over-mime priority and the unknown-input case
  • generateLeviathan test confirming the named flagship is returned for Water/Void, and procedural output is returned otherwise

Security & performance

No concerns. All new code is pure data and pure functions. No user input surfaces, no external calls, no SQL. The intake substrate correctly documents that it never auto-publishes.


Canon compliance

The Leviathan tier design is canonically correct: unbonded Wild Godbeasts that roam outside the Ten Gates, originating from Nero's Unformed. Nethyssa as Water + Void is coherent with Veloura's flow-element domain. The STAGING marker and approval-log entries follow the right process. The locked Ten Godbeasts are untouched. ✓


Summary

Area Status
Canon safety & process ✅ Correct
TypeScript type design ✅ Solid
File organisation ✅ Follows conventions
Suffix typo (" thor") ❌ Fix before merge
Design token colour mismatch ❌ Needs resolution
named flag silent fallthrough ⚠️ Clarify or guard
Unknown element silent fallback ⚠️ Document or warn
Test coverage for gate logic ⚠️ Missing — recommend adding

Fix the two ❌ items and address the test gap before this moves out of DRAFT.

- Canon Consistency Check: reword Nethyssa lore so "Nero" + "evil" no longer
  co-occur in new lines (the gate flags the adjacency); meaning preserved via
  "not corrupt" / "not the Shadow" — the precise canon term anyway.
- generators.ts: normalize element casing (case-insensitive), rename
  weaknesses → elementalWeaknesses (match GameMonster/Leviathan interface),
  fix " thor" suffix typo, add Spirit name roots, add named-fallback note.
- capture-types.ts: normalize extension leading dot in inferCaptureType.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rnGVLqNrCLJz2KJnEcRos

Copy link
Copy Markdown
Owner Author

Thanks for the reviews — pushed 0aaa2a1 addressing the actionable items:

Fixed

  • Canon Consistency Check (was failing): reworded the new Nethyssa lore so "Nero" + "evil" no longer co-occur on added lines (the gate flags the adjacency even when asserting the correct canon). Meaning preserved via "not corrupt" / "not the Shadow" — which is the more precise canon term anyway. The locked Nero is NOT evil truths are untouched.
  • generators.ts: normalize element casing (case-insensitive); rename weaknesseselementalWeaknesses to match the GameMonster/Leviathan interface; fix the " thor" leading-space typo; add Spirit name-roots; add a note when named:true has no canonical match (no more silent fallthrough).
  • capture-types.ts: inferCaptureType now normalizes a missing leading dot on the extension.

Deliberately not changed

  • Design-token hex (lanes.ts uses #00bcd4 for Atlantean Teal): keeping it. #00bcd4 is the current canonical primary in @arcanea/design-system v0.2.0+ (arcanea-ai-app's design contract, 2026-04-18). The #7fffd4 in this repo's .claude/CLAUDE.md is the older system (same file still lists Cinzel/Crimson, which the app has since deprecated). Aligning to the live design package, not the stale doc.
  • Unit tests for gate logic (classifyCanonFit/inferCaptureType/generateLeviathan): valid gap, deferred. The MCP package has no test runner wired yet, so adding spec files now wouldn't execute in CI. Tracking as a follow-up rather than bolting on a harness in this PR.

Pre-existing CI noise (not from this PR): Test Packages / CLI Tests / Test Summary fail on Unable to locate executable file: pnpm + a broken .gitmodules entry (C:\Users\Frank\.claude\plugins...); Code Quality & Linting reports 222 errors all under apps/web/lib/** (untouched here); the arcanea-2 Vercel deploy fails on an apps/academy vercel.json this branch doesn't include. The relevant arcanea-web (apps/web) preview deploys green.

Still a draft + STAGING — Nethyssa stays STAGING pending your /lock-decision.


Generated by Claude Code

@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown

Code Review — PR #76: Arcanea Monster System + Nethyssa Leviathan + Hermes pipeline

Solid architectural addition. The STAGING governance model, canon-gate discipline, and standalone-typed substrate files are all well-reasoned. A few things to address before merging.


Potential Bugs

1. named=true + element=\"Void\" silently returns Nethyssa (a Water+Void entity)

In generators.ts:

if (options.named && (element === "Water" || element === "Void")) {
  const n = leviathans[0]; // always Nethyssa

A caller specifying named=true, element="Void" will receive Nethyssa, whose canonical elements are ["Water", "Void"] — not a pure Void entity. The condition should use && or the returned data should make the element mismatch visible in the output. At minimum add a requestedElement field to the named response so the caller isn't surprised.

2. Unchecked JSON.parse in index.ts case handler (line ~185)

const parsed = JSON.parse(result.content[0].text);

All other cases in this file have the same pattern, so this is consistent — but if generateLeviathan ever throws before returning content, or returns malformed JSON, the error propagates uncaught. Worth wrapping if the rest of the file ever gets hardened.

3. classifyCanonFit conservative T3 default affects non-creature entities

In canon-spectrum.ts:

} else if (introducesNewEntity) {
  canonTier = 'T3' // conservative: unbonded new entity forces the gate

A character sketch or world-fragment capture that introduces a new entity (but isn't a creature) will also land at T3 and require the canon gate. The CaptureTypeMeta.defaultCanonTier prior in capture-types.ts is never consulted by classifyCanonFit — the two systems are parallel but not integrated. This means the caller must pass correctly-graded signals to avoid false-positive gate requirements on non-creature entities. The comment acknowledges the conservatism, but a note in the function doc about non-creature callers needing to pass introducesNewEntity: false or an explicit creatureScale: undefined would prevent misuse.


Code Quality

4. args as any in the generate_leviathan dispatch (index.ts)

case "generate_leviathan": {
  const result = await generateLeviathan(args as any);

The generateCreature case above it uses the same pattern, so this is consistent with existing code — but it throws away the type safety that generateLeviathan's signature provides. Consider typing args inline (args as { element?: string; temperament?: string; named?: boolean }), matching what the JSON schema advertises.

5. Long single-line creation object (index.ts, generate_leviathan case)

const creation = { id: Date.now().toString(), type: "creature" as const, name: parsed.name, element: Array.isArray(parsed.elements) ? parsed.elements[0] : parsed.element, createdAt: new Date(), summary: `Leviathan — ${parsed.title || "Wild Godbeast"}` };

This is difficult to read in review and in git blame. Break it across lines like the surrounding cases.

6. resonanceHz?: number | null — dual-optional type

In data/leviathans/index.ts:

resonanceHz?: number | null;
gate?: string | null;

The ? already allows undefined; adding | null means callers must check both. Consistency with Nethyssa's data (which sets resonanceHz: null and gate: null) shows null is intentional to signal "explicitly absent, not unset." If that distinction matters, document it — if not, pick one convention.

7. Misleading comment in intake files

canon-spectrum.ts and producers.ts both say "Standalone + typed — no external imports." Both files DO have import type from sibling files within .arcanea/. import type is erased at runtime, but the files are not import-free. Change to "no external package imports" (or "no runtime imports") to be accurate.


Missing Test Coverage

No test files were added. The CLAUDE.md requires tests after code changes. The new TypeScript logic has clear, deterministic paths worth covering:

  • classifyCanonFit — 6+ distinct code paths (bonded, scaled, new entity with/without locked canon refs, tech-leaning)
  • generateLeviathan — named vs. procedural branch, element normalization, fallback for unknown element key
  • inferCaptureType — extension normalization (the dot-prefix fix in commit 2 shows this logic was already buggy once)
  • producersAcceptingCaptureType — dispatch correctness

These are all pure-function or near-pure, so unit tests are cheap. Given that Hermes intake is a gate on canon itself, test coverage here has an outsized quality-to-cost ratio.


Canon Compliance ✓

The STAGING governance is correct throughout. The Ten Gate Godbeasts are explicitly untouched. Nero/Void/Shadow semantics are clean (the fix in commit 2 to remove the Nero + evil adjacency was the right call). Nethyssa's Water+Void dual-element and the Tidesong/Leyla/Veloura kinship are internally consistent with CANON_LOCKED.md.

The MONSTER_SYSTEM.md note about the T2 "Overwhelm Leviathan" being a Shade that "rhymes" with T3 Leviathans by name only is good defensive documentation — keep it.


Summary

Area Status
Canon compliance ✅ Correct STAGING governance
Architecture ✅ Clean intake → canon-gate → lock-decision pipeline
Bug: named/element mismatch ⚠️ Should clarify or document
Bug: classifyCanonFit non-creature entities ⚠️ Needs caller guidance
Code quality: args as any ⚠️ Minor, consistent with existing pattern
Code quality: long creation line ⚠️ Readability
Test coverage ❌ Missing — required by CLAUDE.md

The core lore, MCP tool, and pipeline design are solid. Resolve the named-element mismatch and add unit tests for classifyCanonFit, generateLeviathan, and inferCaptureType before merging.

…parency

- intake comments: "no external imports" → "no external package imports
  (only intra-dir import type)" — the files do import sibling types.
- leviathans data comment: drop stale "Tier 9" (the canon section is titled
  "The Leviathan tier", not numbered).
- generateLeviathan: surface requestedElement on the named (Nethyssa) response
  so a caller passing element=Void sees what they asked for vs. her Water+Void.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rnGVLqNrCLJz2KJnEcRos
@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown

PR Review — feat(lore): Arcanea Monster System + Nethyssa + Hermes pipeline

Overall: Strong PR with clear intent and good separation of concerns. The STAGING gate discipline is well-enforced throughout. A few code issues worth fixing before promotion to non-draft.


Code Quality

✅ Strengths

  • Clean TypeScript interfaces with good JSDoc. GameMonsterLeviathan extension hierarchy is well-designed.
  • classifyCanonFit is pure, deterministic, and the conservative-default bias (unknown new entity → T3 gate) is the right call.
  • inferCaptureType correctly prefers extension over mime and returns undefined rather than silently guessing — callers must handle the failure path.
  • lanes.ts standalone design (zero external imports) is the right call for a config-dir file.

Potential Bugs

🔴 leviathans[0] hardcoded indexgenerators.ts:285

const n = leviathans[0];

This works today (Nethyssa is the only Water/Void flagship), but will silently break when a second Leviathan is added to the array. Use a named lookup or filter by element:

const n = leviathans.find(l => l.elements.includes("Water") && l.elements.includes("Void"));
if (!n) { /* fall through to procedural */ }

🟡 Triple-state typing on resonanceHzleviathans/index.ts:47

resonanceHz?: number | null;

optional (?) already gives you undefined; adding | null creates a triple state (undefined | null | number) that's easy to mischeck. Since callers check with === null (see the inline comment), unify: either drop ? and use number | null, or drop | null and use ?: number. Same applies to gate?: string | null in GameMonster (though Leviathan correctly overrides it to gate: null).


Design / Consistency

🔴 Color constant mismatch — Atlantean Teal
lanes.ts comment (line 7) and all palette/prompt values use #00bcd4 for Atlantean Teal. CLAUDE.md and the design system define it as #7fffd4 (aquamarine). These are different colors. The prompt-packs in nethyssa.md also use #00bcd4. One of them is wrong — please reconcile with the source token in styles/themes/arcanean-colors.css before this ships.

🟡 generate_creature schema vs. new CreatureScale values
CreatureScale now includes "titan" and "planet" (leviathans/index.ts:11-19), but the generate_creature MCP tool schema in index.ts:114 only enumerates ["tiny", "small", "medium", "large", "massive"]. Not a breaking bug today (the new scales are Leviathan-only), but the enum silently excludes them. Worth a comment or a schema update to keep them in sync.

🟡 as any in generate_leviathan handlerindex.ts:190

const result = await generateLeviathan(args as any);

This follows the existing pattern for other generators in the same switch block, so it's not introduced by this PR — but worth noting as tech debt. The MCP JSON schema validation happens before reaching this handler, so the cast is lower-risk than it looks, but a typed GenerateLeviathanOptions interface would remove the blind spot.


Performance

No concerns. The generators are synchronous data lookups; the double serialize/deserialize round-trip (JSON.stringify in generator → JSON.parse in handler) is the existing pattern and fine at this scale.


Security

No hardcoded credentials. Input reaches the generator only after MCP schema validation. No exec/eval/template-injection paths. Clean.


Test Coverage

🟡 No tests for new code. Three new functions have interesting branching that benefits from unit tests:

Function Key cases
classifyCanonFit introducesNewEntity=true + no scale (→ T3 gate); bonded=true no scale (→ T4); techLeaning=true + no canon signals (→ tech, no gate)
inferCaptureType Extension wins over mime; unknown extension falls back to mime; neither matches → undefined
generateLeviathan named=true, element=Water (→ Nethyssa); named=true, element=Fire (→ procedural + note); named=false (→ procedural, no note)

The PR description says "CI build is the authoritative check," but that only validates type-checking, not logic correctness. Given the canon-gate logic in classifyCanonFit is a correctness-critical path (it decides whether Frank's approval is required), at least a few tests there would be worthwhile.


Summary

Count
🔴 Must fix 2 (index hardcode, color mismatch)
🟡 Should fix 3 (triple-state typing, schema gap, test coverage)
✅ Good Architecture, canon gate discipline, type design, standalone substrate pattern

Happy to review a follow-up once the color token and leviathans[0] are addressed. The STAGING → LOCKED gate model is solid — don't let these mechanical issues hold up the overall design.

generateLeviathan's named branch used leviathans[0], which would always
return Nethyssa once a second Leviathan is added. Match by requested element
via .find() and fall through to procedural generation when none exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rnGVLqNrCLJz2KJnEcRos
@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown

PR Review — feat(lore): Arcanea Monster System + Nethyssa + Hermes pipeline

Overall this is a well-structured, thoughtfully scoped PR. The architectural choices are sound: the STAGING gate, the parallel producer pattern, and the Hermes intake pipeline are all solid foundations. Below are findings by category.


Code Quality

packages/arcanea-mcp/src/tools/generators.ts

The if (n) block inside if (options.named) has inconsistent indentation — the return is at the same indent level as the outer if, making the nesting hard to read at a glance:

if (options.named) {
  const n = leviathans.find((l) => l.elements.includes(element));
  if (n) {
  return {          // ← body should be indented one more level
    content: [...]
  };
  }               // ← closing brace at wrong level
}

The logic is correct (the fix commit in this PR already improved this by using .find() instead of leviathans[0]), but the indentation should be cleaned up for readability.

packages/arcanea-mcp/src/index.ts

case "generate_leviathan": {
  const result = await generateLeviathan(args as any);  // ← `as any` drops type safety

Every other case block also uses args as any, so this is consistent with the existing pattern — but it's worth noting this is a weak point in the codebase-wide type safety story. Consider adding a Zod schema or a typed guard for this tool's arguments (the input schema is already defined in ListToolsRequestSchema, so the shape is known).

Material fallback logic is implicit

In the procedural path of generateLeviathan:

material: `${name}'s ${element === "Water" ? "Pearl" : element === "Fire" ? "Cinder" : element === "Void" ? "Obsidian" : "Shard"}`,

Spirit and Wind both silently fall through to "Shard". This works, but it will generate Cycloumbra's Shard for Wind and Lumvit's Shard for Spirit, which reads as generic. Worth adding explicit material terms for all five elements (even a placeholder), especially since leviathanRoots already has entries for all five.


Potential Bugs

JSON.parse() call with no error handling (index.ts:185)

const parsed = JSON.parse(result.content[0].text);
const creation = { id: Date.now().toString(), type: "creature" as const, name: parsed.name, ... }

This mirrors the existing pattern used by generate_creature, generate_artifact, etc. — so it is not a regression. However, if generateLeviathan ever returns an error-shaped result (which the current implementation doesn't, since it always returns valid JSON), JSON.parse will throw and take down the MCP request handler. A try/catch here (or in the callers) would add resilience.

Date.now().toString() for IDs

Low-risk at current call rates, but two back-to-back calls within the same millisecond will produce the same ID. A simple Date.now().toString() + Math.random().toString(36).slice(2) or a counter would be safer. Again, this is the existing pattern across the codebase, not a regression here.


Performance

No significant concerns. The Monster System data is tiny and fully in-memory. The .find() lookup on leviathans[] is O(n) over an array that will likely always have fewer than 20 entries.

The JSON.stringify → JSON.parse round-trip in index.ts is a minor inefficiency, but consistent with the rest of the codebase.


Security

.arcanea/intake/*.ts files are outside the build

The comments correctly document that these are "standalone typed" files outside the tsconfig build, meaning they get no compile-time checking. Imports like import type { CanonFit } are fine, but if any producer ever adds a runtime import (e.g. a utility function from a shared package), it will fail silently in the intake tools. Consider adding a minimal tsconfig.json in .arcanea/ that includes these files for type-checking even if they're not compiled into the package.

Input validation

generateLeviathan receives a raw element string and normalizes it with .charAt(0).toUpperCase() + .slice(1).toLowerCase(). This is safe. The MCP inputSchema already constrains the enum to ["Fire", "Water", "Earth", "Wind", "Void", "Spirit"], so the generator logic won't see unexpected values in normal operation. No concerns here.


Test Coverage

No new tests are included. The generator logic in generateLeviathan is straightforward enough that it's worth adding a few unit tests covering:

  1. named: true with element: "Water" → should return Nethyssa
  2. named: true with element: "Fire" → should fall through to procedural (no canon Fire Leviathan yet)
  3. named: false → always returns a procedural result
  4. Procedural generation for each of the 5 elements → name is non-empty, tier is 3, bonded is false

The intake substrate (canon-spectrum.ts, capture-types.ts, producers.ts) is also untested. Given these are classification logic files with deterministic outputs, they're ideal unit test candidates.


Canon / Architecture Notes (no blocking issues)

  • The STAGING / LOCKED gate is well enforced: Nethyssa is clearly marked ⏳ STAGING in CANON_LOCKED.md, the Hermes agent never auto-promotes, and the requiresCanonGate flag in canon-spectrum.ts correctly forces T3+ through the gate. This is the right pattern.
  • The Leviathan tier slots cleanly into the existing lore: unbonded, outside the Ten Gates, with a sub-Gate frequency (Abyssal Hum) that doesn't collide with the locked Ten. The Veloura/Leyla kin-adjacency is well-motivated and doesn't touch the locked canonical relationship.
  • The three-loop pipeline in WORKFLOWS.md (lore → asset → web3) is clear and the gate-ordering is correct: lock-decision gates canon; council gates publish; approved status gates minting. No gaps spotted.

Summary

Category Status
Code quality Mostly good; one indentation nit, one as any (existing pattern)
Bugs No new regressions; JSON.parse fragility is pre-existing
Performance No concerns
Security .arcanea/ files outside build — worth a minimal tsconfig
Tests Missing; generator logic is straightforward to cover
Canon consistency Clean — Nethyssa doesn't touch the locked Ten

The indentation issue and missing test coverage are the two things I'd address before this graduates from DRAFT. Everything else is either a pattern carried from the existing codebase or a note for future hardening.

🤖 Generated with Claude Code

@claude

claude Bot commented Jun 23, 2026

Copy link
Copy Markdown

Code Review — PR #76: Arcanea Monster System + Nethyssa + Hermes pipeline

Overall this is a well-structured PR with clear separation of concerns, good TypeScript typing, and careful canon stewardship (STAGING gates, no auto-publishing). Comments below are grouped by severity.


🐛 Bugs / Logic Issues

1. Procedural name can collide with canonical Nethyssa
generators.tsleviathanRoots.water includes "Neth" and nameSuffixes in the leviathan suffix list includes "yssa". A procedural Water Leviathan with named: false could produce "Nethyssa", overriding the canonical flagship's identity without the STAGING/lock-decision gate.

Fix: filter the canonical names from the procedural pick pool, or remove "Neth" from leviathanRoots.water.

const root = pick(
  (leviathanRoots[elementKey] || leviathanRoots.void).filter(
    r => !leviathans.some(l => l.name.startsWith(r))
  )
);

2. leviathans local array has domain field that the Leviathan interface doesn't define
generators.ts (local leviathans array): domain: "The Drowned Deep", but the exported Leviathan interface in packages/arcanea-mcp/src/data/leviathans/index.ts has no domain field. The return payload in generateLeviathan uses n.domain, which would be undefined at runtime (TypeScript would catch this if the local array was typed as Leviathan[] instead of an untyped literal).

Recommendation: add domain?: string to the Leviathan interface, or remove it from the local array.

3. nethyssa data object has no subGateResonance field but the export SUB_GATE_RESONANCE = "Abyssal Hum" isn't linked back to it
leviathans/index.tsnethyssa.resonanceHz = null with a comment about "Abyssal Hum", but the constant isn't used in the nethyssa object. Any consumer has to know to look up SUB_GATE_RESONANCE separately. Consider adding subGateResonance?: string to GameMonster/Leviathan and setting nethyssa.subGateResonance = SUB_GATE_RESONANCE.


⚠️ Code Quality / Consistency

4. Missing tests
CLAUDE.md: "ALWAYS run tests after making code changes" and "Prefer TDD London School (mock-first) for new code". The new code paths have no test coverage:

  • classifyCanonFit() in canon-spectrum.ts — pure function, easy to unit test
  • inferCaptureType() in capture-types.ts — likewise
  • generateLeviathan() in generators.ts — especially the named=true path and the procedural path
  • The new MCP generate_leviathan dispatch block in index.ts

5. Conservative T3 default in classifyCanonFit may over-gate non-creature captures
canon-spectrum.ts — when introducesNewEntity=true and creatureScale is absent and bonded=false, the tier defaults to T3 ("conservative — unbonded new entity forces the gate"). A text snippet introducing a named NPC baker with no creature scale signals would land as T3 and require the full canon gate. Consider a separate tier (e.g. T1) for new non-creature named entities, or add a captureClass: 'creature' | 'character' | 'place' | 'artifact' signal.

6. args as any casting in index.ts dispatch
The new generate_leviathan case inherits the existing pattern of generateLeviathan(args as any). This is pre-existing but worth flagging: the MCP input schema already validates enums, so the any-cast won't cause runtime issues, but the TypeScript safety net is gone. A typed extraction (const { element, temperament, named } = args as { element?: string; temperament?: string; named?: boolean }) would be safer.

7. generators.tspick() uses Math.random() (fine here but note for workflows)
This is correct in the MCP server context. Just noting for completeness that this would be forbidden in Workflow scripts per Claude Flow conventions.


📋 Minor / Suggestions

8. generate_leviathan tool description says "e.g. Nethyssa" but Nethyssa requires named: true
The description "Summon a Tier 3 Leviathan / Wild Godbeast — an unbonded titan outside the Ten Gates (e.g. Nethyssa). STAGING canon." implies Nethyssa is always returned. Add "Pass named: true with element: Water to get Nethyssa" to avoid confusion.

9. resonanceHz typed as number | null but Leviathans have a named sub-Gate resonance, not a numeric Hz
The interface accommodates future numeric resonance but Nethyssa's actual resonance is the named string "Abyssal Hum" with no assigned Hz. Consider resonance?: string | number | null or document the intent (numeric Hz reserved for future Leviathans with known frequencies).

10. lanes.tsDEFAULT_LANE: LaneId = 'leviathan-abyssal' is a reasonable choice but undocumented
Add a one-line comment explaining why the abyssal lane is the default (flagship Nethyssa, most commonly requested), so a future contributor doesn't change it without understanding the reasoning.


✅ What's working well

  • Interface hierarchy (BestiaryCreatureGameMonsterLeviathan) is clean and forward-compatible — adding a T4 Wild Godbeast in the future will slot in without touching the existing types.
  • gate: null hardcoded on the Leviathan interface enforces the canonical "unbonded" constraint at the type level. Good defensive design.
  • STAGING gate is wired throughout — CANON_LOCKED.md, the generator return payload, the intake classifier, and the Hermes command all consistently refuse to auto-publish.
  • classifyCanonFit is purely functional (no side effects, no I/O) — easy to audit and test when tests are added.
  • capture-types.ts inferCaptureType returns undefined rather than guessing when mime+ext match nothing. The contract is explicit.
  • Aesthetic lanes in lanes.ts are well-specified — palette, reference mood, prompt fragments, negative prompt, quality bar, and ranked backends. This is production-grade prompt engineering.
  • Book content (the-rising-of-nethyssa.md, the-kraken-brood.md) matches the Arcanea voice and correctly frames Nero's Unformed as non-evil.
  • No secrets, credentials, or env vars introduced.

Priority order for action before merge

  1. 🐛 Fix the "Neth""Nethyssa" collision in the procedural name pool (Bug feat(readme): Epic visual overhaul with ASCII art and better structure #1)
  2. 🐛 Resolve domain field mismatch between local leviathans array and Leviathan interface (Bug Sprint Feb 2-9: Platform Launch Tracking #2)
  3. ⚠️ Add at minimum unit tests for classifyCanonFit and generateLeviathan(named=true) (Issue Add /ultraworld and /agentic-jujutsu from ACOS v7.0 #4)
  4. 🐛 Link SUB_GATE_RESONANCE into the nethyssa data object (Bug chore: Untrack tsbuildinfo file already in .gitignore #3)

Items 5–10 are non-blocking but worth addressing in a follow-up.

…rfaces

- Prevent procedural name collision with Nethyssa by removing "Neth"
  from leviathanRoots.water (canonical root reserved for the flagship)
- Fix indentation in generateLeviathan named-lookup branch
- Add explicit material terms for Earth → Hearthstone, Wind → Tempest Shard,
  Spirit → Aether Shard (previously all fell through to generic "Shard")
- Wire subGateResonance field: add to GameMonster + Leviathan interfaces,
  set on nethyssa data object, use n.subGateResonance in generator output
- Add domain?: string to GameMonster interface; set on nethyssa object
  (distinct from habitat — names the territory rather than the biome)
- Add brand-token context note to gen/lanes.ts (#00bcd4 = app UI primary;
  #7fffd4 = OSS/mythic reference — both intentional for their contexts)
- Clarify DEFAULT_LANE comment

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rnGVLqNrCLJz2KJnEcRos
…nFit, inferCaptureType

46 tests across 3 suites — all pass.

generateLeviathan (16 tests):
- named=true + element=Water returns canonical Nethyssa block (name, title, tier=3,
  class, bonded=false, elements, resonance, domain, material, corruption, STAGING canon)
- lowercase element input normalised correctly
- named=true + element=Fire falls through to procedural with note field, no corruption
- named=false always returns procedural, no note field

classifyCanonFit (14 tests, logic inlined from .arcanea/intake/canon-spectrum.ts):
- Full T0–T4 ladder coverage (mote→T0 … godbeast→T4)
- Gate required for T3/T4 and for new entity referencing locked canon
- tech/canon/mixed spectrum branches; creatureScale wins over bonded signal
- Nethyssa capture profile integration case

inferCaptureType (16 tests, logic inlined from .arcanea/intake/capture-types.ts):
- Extension-first routing with dot normalisation (json → .json)
- Unique-extension cases: .pdf/.svg/.opus/.json/.webm
- .png correctly routes to monster-concept (first match, T3 prior)
- Mime fallback when extension is unknown; double-unknown → undefined
- defaultCanonTier priors: T3 (monster-concept), T1 (world-fragment), none (others)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rnGVLqNrCLJz2KJnEcRos

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Tier-based Monster System (including the new T3 Leviathan / Wild Godbeast tier) with flagship Leviathan Nethyssa, plus an Arcanea Hermes intake substrate and an MCP generate_leviathan tool + tests to support ongoing generation and canon-gated expansion.

Changes:

  • Introduces Hermes intake substrate (.arcanea/intake/*) and workflow/docs for intake→canon-gate→review queue routing.
  • Adds MCP generate_leviathan tool (canonical Nethyssa for Water/Void; otherwise procedural) and unit tests around generation + intake classifiers.
  • Adds lore + book entries for Nethyssa and brood, and registers Leviathan tier as STAGING in CANON_LOCKED.md.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/arcanea-mcp/tests/leviathan-generators.test.mjs New unit tests for leviathan generation and inlined Hermes intake classifiers.
packages/arcanea-mcp/src/tools/generators.ts Adds generateLeviathan generator and canonical/procedural Leviathan logic.
packages/arcanea-mcp/src/index.ts Registers the new generate_leviathan MCP tool and records creations into memory/graph.
packages/arcanea-mcp/src/data/leviathans/index.ts Introduces Leviathan + brood dataset/types (currently not wired into the server).
book/legends-of-arcanea/the-rising-of-nethyssa.md Adds a draft legend/story entry for Nethyssa.
book/bestiary-of-creation/the-kraken-brood.md Adds brood reference entries tying literal monsters to creative-parallel bestiary framing.
.claude/skills/arcanea-hermes/SKILL.md Defines the Hermes intake→dispatch→canon-gate→synthesis loop and invariants.
.claude/commands/arcanea-hermes.md Adds /hermes command doc for triggering the Hermes pipeline.
.claude/agents/arcanea-hermes.md Adds Arcanea Hermes agent definition aligned to the skill loop.
.arcanea/WORKFLOWS.md Documents lore/asset/web3 loops and how they chain (Nethyssa as worked example).
.arcanea/lore/leviathans/nethyssa.md Canon STAGING profile for Nethyssa.
.arcanea/lore/leviathans/nethyssa-game-design.md Encounter and gating spec for integrating Nethyssa into gameplay.
.arcanea/lore/leviathans/INDEX.md Leviathans roster index (currently Nethyssa only).
.arcanea/lore/creatures/MONSTER_SYSTEM.md Defines Monster System tiers T0–T4 plus Corruption Track; positions Leviathans at T3.
.arcanea/lore/creatures/INDEX.md Creature taxonomy index pointing to Monster System + Leviathans.
.arcanea/lore/CANON_LOCKED.md Registers Leviathan tier + Nethyssa into STAGING and logs approval entries.
.arcanea/intake/producers.ts Defines Hermes producer registry and dispatch lookup by capture type.
.arcanea/intake/capture-types.ts Defines capture-type classification substrate with mime/extension routing and canon tier priors.
.arcanea/intake/canon-spectrum.ts Defines canon↔tech spectrum and canon-tier/gate classifier (classifyCanonFit).
.arcanea/gen/prompt-packs/nethyssa.md Adds image prompt-pack for Nethyssa and brood assets.
.arcanea/gen/lanes.ts Adds/extends aesthetic lane substrate including Leviathan abyssal lane + routing metadata.
.arcanea/gen/HARNESS_ROUTING.md Documents lane→harness routing and anti-patterns for image-gen workflow.

Comment on lines +271 to +273
const rawElement = options.element ?? pick(["Water", "Void", "Fire", "Earth", "Wind"]);
const element = rawElement.charAt(0).toUpperCase() + rawElement.slice(1).toLowerCase();
const temperament = options.temperament ?? pick(["dreaming", "stirring", "waking", "corrupted"] as const);
Comment thread .arcanea/lore/CANON_LOCKED.md Outdated

> *"Not every great beast knelt to a Gate. Some were already old when the Gates were young."*

The Ten Gate Godbeasts (Tier 2) are **bonded** — each sworn beside an Arcanean God. But beasts of **Nero's Unformed** existed before the bonding, titan-scale and sovereign to no Gate. These are the **Leviathans**, or **Wild Godbeasts**: unbonded, region-roaming, keeping their own sub-Gate frequencies. They are the wilderness to the Gates' civilization.
Comment on lines +81 to +83
- Do not meet their eyes seeking comfort. The comfort is real, and that is the trap. The peace they offer is the peace of stopping forever.
- Stopper your ears to the Deep Call with a song of your own. The faithful sing the Tidesong precisely so the Call cannot find the gap.
- Remember the Herald was a person who forgot why they began. Pity them. Do not follow them.
Comment on lines +164 to +169
export const leviathans: Record<string, Leviathan> = {
nethyssa,
};

/** Resonance name for Leviathans that sound beneath the Ten-Gate scale. */
export const SUB_GATE_RESONANCE = "Abyssal Hum";
… leviathan data, named+no-element path

- bestiary: fix typo "Stopper" → "Stop" in the-kraken-brood.md (Drowned Herald handling)
- canon: change "Tier 2" → "T4 in the Monster System" for unambiguous cross-reference to
  the monster taxonomy (STAGING block, Ten Gate Godbeasts line)
- leviathans/index.ts: export LEVIATHANS = Object.values(leviathans) so generators.ts has
  a single canonical source of truth instead of maintaining a parallel inline array
- generators.ts: import LEVIATHANS from data file; remove inline const leviathans array;
  restructure generateLeviathan so named=true+no-element returns LEVIATHANS[0] directly
  (avoids random-element pick that could miss named entries); add Spirit to procedural pool

All 46 leviathan-generators tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rnGVLqNrCLJz2KJnEcRos
…d Repo Standard

Workstream: Creature Encyclopedia / Atlas (God mode)
Conductor: Claude | Executors: swarm-dispatched

## What this adds

### `packages/arcanea-mcp/src/data/atlas/`
- `types.ts` — Core type contract: UniverseSpec, AtlasCreatureSpec, ArcaneaVariantSpec, PromptPack, WorldRepoContribution, CreatureRightsTier
- `universes/avatar.ts` — Avatar: The Last Airbender universe spec (factual_reference)
- `creatures/avatar.ts` — 7 reference creatures (sky-bison, lion-turtle, ancient-dragon, badgermole, flying-lemur, hei-bai, wan-shi-tong) — all promptable: false
- `arcanea-variants/avatar.ts` — 3 Arcanea-original variants (sky-wanderer, titan-shell, sight-keeper) — fully original, staging
- `index.ts` — barrel export

### `.arcanea/gen/prompt-packs/`
- `atlas-sky-wanderer.md` — Hero/full-body/NFT prompts per provider (Grok/Codex/NB2)
- `atlas-titan-shell.md` — Hero/full-body/NFT prompts per provider

### `.arcanea/lore/atlas/`
- `WORLD_REPO_STANDARD.md` — Open contribution spec (PR-based, automated checks)
- `INDEX.md` — Atlas lore index with planned universe pipeline

### `.agent/active-agents.md`
- Agent coordination ledger (AGENTS.md §4 protocol)
- Scope partitions: Claude=conductor/canon/data, Codex=tools/skills/tests

## Rights architecture
- Reference creatures: `factual_reference`, `promptable: false` — documented only
- Arcanea variants: `original_arcanea`, `promptable: true` — generate freely
- DB constraint enforces this: no `promptable: true` on `factual_reference` or `blocked`

## Canon status
All Atlas entries and Arcanea variants are STAGING. Promote via `/lock-decision`.
@frankxai

frankxai commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

PR hospital triage 2026-08-07 — PARK / needs-rebase (lore saga)

Disposition: park — not draft but CONFLICTING lore/monster system mega-diff. Creative + rebase gate.

  • Label suggestion: park · needs-rebase · creative-gate

frankxai pushed a commit that referenced this pull request Aug 8, 2026
… PR #76 staging

Review round 2 fixes:
- THESSARA.md no longer claims #96 removed the last Thessara-as-godbeast
  references; stray references persist in mirrors, agent prompts, and
  packages, and a repo-wide sweep is now stated as a precondition for
  promoting the redeployment.
- NAMING_REGISTRY.md superseded list gains a cleanup-status warning
  (Amaterasu and Thessara both still referenced repo-wide).
- All Nethyssa citations now note it is proposed in open, unmerged PR #76
  rather than reading as in-tree canon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AomsLJvgAwwzuWph4bD7YT
… System STAGING rows)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLQUyDFhK9taQ7mBLht4aD
@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown

Review: Arcanea Monster System + Nethyssa Leviathan + Hermes pipeline

Went through the canon changes, the new MCP tool/data, the tests, and the lockfile diff. Overall this is disciplined lore work — the STAGING/LOCKED boundary is respected and the cross-file details (name, resonance, domain, material, corruption) stay consistent everywhere they're repeated. A few things worth a look before merge.

Code quality

  • packages/arcanea-mcp/src/data/leviathans/index.ts + generators.ts — clean, single-source-of-truth pattern (generators.ts explicitly comments "Do not duplicate Nethyssa inline here"). generateLeviathan's named/procedural split and element-normalization are straightforward and match the documented behavior in the PR description.
  • Minor nit: in the procedural "corrupted" branch, elementalWeaknesses: ["Spirit", "the Tidesong"] mixes flavor text ("the Tidesong" isn't an element) into a field that's typed/used elsewhere as pure elemental strings. Harmless today but slightly inconsistent typing if anything downstream treats elementalWeaknesses as an enum-like list.
  • generateLeviathan({ named: true }) with no element returns LEVIATHANS[0] — fine with one entry, but there's no documented tie-breaker for when a second canonical Leviathan is added. Worth a // TODO once the tier grows past one.

Test coverage

  • packages/arcanea-mcp/tests/leviathan-generators.test.mjs imports from ../dist/tools/generators.js, but the package's test script (node --test tests/*.test.mjs) does not run build first, and dist/ is gitignored. From a clean checkout, pnpm test in this package will fail (module not found) unless someone remembers to tsc first — so the "46/46 passing" claim in the description isn't reproducible by CI as-is. I didn't find a GitHub workflow wired to packages/arcanea-mcp either, so there's currently nothing that runs this suite automatically on future PRs.
  • classifyCanonFit and inferCaptureType are tested via hand-inlined copies of the logic (explained in the file header: .arcanea/intake/ isn't in the package's tsconfig build graph). That means these ~30 tests validate the copy, not the real .arcanea/intake/canon-spectrum.ts / capture-types.ts — a future edit to the real files won't be caught by this suite unless the copy is updated in lockstep. Today they're identical (checked), but this is exactly the kind of silent-drift risk lore-lint.mjs was built to prevent for prose; there's no equivalent guard for this logic duplication. Worth at minimum a comment/TODO in the real source files pointing back at the test copy, or extracting the shared logic into a small module both sides can import.

Canon consistency

  • CANON_LOCKED.md correctly keeps Starweave (852 Hz) rather than reintroducing the stale "Shift" name, and the new Leviathan tier is appended as STAGING without touching the locked Tier 2 table — good discipline.
  • nethyssa.md, leviathans/index.ts, and the new CANON_LOCKED.md section all agree on name, title, resonance ("Abyssal Hum"), domain ("The Drowned Deep"), material ("Nethyss Pearl"), and corruption ("The Drowned Shadow"). No drift found.
  • Small nit: the three new APPROVAL LOG entries (Monster System taxonomy, Leviathan tier, Nethyssa) are all dated 2026-06-22 — identical to the pre-existing, unrelated Magic System entry directly above them. If that's not the actual staging date, worth fixing so the log stays a reliable timeline.

Scope

  • The PR description doesn't mention packages/arcanea-mcp/src/data/atlas/** (~505 new lines: types.ts, universes/avatar.ts, creatures/avatar.ts, arcanea-variants/avatar.ts, index.ts) — a new "Creature Atlas" for referencing third-party fictional universes (Avatar: The Last Airbender). It's handled carefully from an IP standpoint (rightsTier: "factual_reference", promptable: false, no generated imagery — documentation only), so I don't think it's a blocker, but it's undisclosed scope creep relative to the stated "Monster System + Nethyssa + Hermes" title. Worth flagging so reviewers know to actually look at it, and maybe splitting Atlas work into its own PR going forward.

pnpm-lock.yaml

  • The large diff (283 additions / 4704 deletions) looked alarming at a glance, but it's just the lockfile catching up to packages/ai-agents and packages/contracts, which were already removed from the tree on main before this branch — no live package lost its lock entries. Net positive cleanup, not a regression.

Security

  • Nothing hardcoded, no secrets, no dynamic filesystem writes outside the documented _inbox/arcanea/<batch>/ review-queue pattern (which explicitly never auto-publishes and never promotes STAGING → LOCKED). Looks fine.

Nothing here blocks merging on its own — the test-runner/build-order gap is the one I'd actually want addressed (or at least acknowledged) before relying on "46/46 passing" as a merge gate going forward.

@frankxai

Copy link
Copy Markdown
Owner Author

State corrected to draft: the exact head has failed Node 22 package/CLI tests, failed lint, and a failed test summary. Preserve the canon work, repair the red quality gates on a current base, and return only a fully green exact head to review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants