EchoSpire: Portals — Game Assets (Cards, Mechanics, Artifacts, Balance)¶
Status: Point-in-time assessment, 2026-08-28. Not canon and not a plan. Provenance: Grok 4.6. Reassessment requested via
grok-eval-instructions.md. Scope: live cards, class/House mechanics, artifacts, abilities, enemies, art coverage, and the simulation harness as a balancing tool. The card list isdata/content/cards.json.ProductionRiftContentCatalog.cswas not used as the pool.docs/proposals/card-pack-gpt54/is designed, not built. Verification: Parsed all 584cards.jsonrows (class/House/rarity/effect counts, empty payloads, samples). Readhero-classes.json,factions.json,artifacts.json(21),enemies.json(164),consumables.json(35),random-events.json(0 rows), Core handlers and 22IEffectimplementations,ArtifactHandler,HeuristicPolicy,BatchRunner,SimulationController. Countedassets/cards/art(580 files), enemy art, artifact art, crests. Did not visually score every still. Did not run a live combat. Known errors: none at publication.
Date: 2026-08-28
Companion to: 2026-08-28-overall-concept.md
Note on the 2026-08-27 class/faction evaluation: its identity critique (cover test, one verb per owner) still stands as design advice. Its inventory (“5 class cards + 1 shared faction card”) was read off the bootstrap seed and is wrong. This file replaces that inventory, not that advice.
You said there has been no balance pass, and that part of the app is a test harness for that work. Both are visible. The harness can grind seeded runs. It cannot yet tell you whether Anchor is stronger than Drifter, because too many identity cards do not execute and the policy does not understand the class verbs.
Bottom line¶
The catalog size is real. 584 published cards, 80 per class, ~32–40 per House, 476 with effect payloads, 164 enemies, 21 artifacts, 35 consumables. That is not a five-card prototype.
The identity layer is mostly in description. Two verbs — DEAL_DAMAGE (220) and GAIN_BLOCK (148) — are the majority of payloads. The cards that read like EchoSpire (Vent Density, store Overload, deploy a turret, spawn an Anomaly, Scry 3) are over-represented in the 108 empty-payload rows. Playing those cards does nothing.
Mechanics in code are better than mechanics on the first starter hand. Density retains Block. Echo-Lock executes at 5. Overload debts energy and burns you. Constructs auto-fire. Mutation replaces a card from an Anomaly pool that currently has zero cardType: anomaly rows, so mutate no-ops.
Until payloads, keywords, and pools match the text, you are balancing a generic STS-like game that happens to be named EchoSpire. The harness will confirm that, loudly and wrongly.
| Grade | |
|---|---|
| Class fantasy (as designed) | A- |
| Class fantasy (live starters + payloads) | C |
| House as price-of-the-turn (as designed) | B+ |
| House passives (JSON vs handlers) | C (Valerii and Salvari disagree with their own data) |
| Card pool as inventory | B (size is real) |
| Card pool as identity | C- |
| Artifacts | B- (good texts; mixed implementation; small pool) |
| Enemies | C (names and HP; almost no unique engines) |
| Abilities (innate/ultimate) | F (null on every class) |
| Balance harness | C (pipes exist; the instrument cannot see the designed game) |
| Art coverage | C+ (cards mostly present; enemies/artifacts/banners thin; AI-batch look) |
1. The live card pool¶
Source: data/content/cards.json. All 584 rows status: published, isActive: true.
Shape¶
| Slice | Count |
|---|---|
| Total | 584 |
With effects |
476 |
Empty effects: [] |
108 |
| Per class | 80 × 5 |
| House-tagged | 178 (Valerii 36, Syntacta 32, Aethari 36, Annalis 40, Salvari 34) |
| Neither class nor House | 6 (Ghostwalk Feint, Emergency Shunt, Rescue Compound and + twins) |
| Starter / common / uncommon / rare | 64 / 154 / 214 / 152 |
| Attack / skill / power | 214 / 288 / 82 |
cardType: anomaly |
0 |
| Keywords set | 4 rows (exhaust ×2, overload ×2). 580 rows have keywords: []. |
Upgrades (upgradeId present) |
292 of 584 |
artAssetKey |
584 |
assetFilePath empty |
12 |
eligibleClassIds / eligibleFactionIds are present and empty. Ownership is classId XOR factionId.
Effect vocabulary¶
22 IEffect classes in Core. Uses in JSON:
| Effect ID | Uses |
|---|---|
DEAL_DAMAGE |
220 |
GAIN_BLOCK |
148 |
DRAW_CARDS |
60 |
GAIN_ENERGY |
40 |
APPLY_ECHO_LOCK |
18 |
GAIN_DENSITY |
16 |
APPLY_STASIS |
16 |
HEAL |
16 |
MUTATE_CARD |
6 |
DEPLOY_CONSTRUCT |
4 |
APPLY_STATUS |
4 |
| House-unique / conditional | 2 each (ECHO_BURN, ECHO_RECALL, LOGIC_STREAM, SELF_DAMAGE, …) |
OVERCLOCK_CONSTRUCT |
0 in JSON (code exists) |
Roughly 1.2 effects per payload card. ~292 of 476 payload cards are damage and/or block only (this pass’s classifier). Description text mentions Echo-Lock 52 times, Overload 52, Construct 52, Mutate 38, Density 36, Scry 23 — far more often than those verbs exist as effects.
The empty 108 are the identity cards¶
Not blank names. Examples:
- Anchor: Relief Vent — “Vent up to 1 Density. Gain that much Energy.”
- Anchor: Density Retainer — persist Density between combats
- Drifter: Hollow Corridor — prevent the next 7 damage
- Conduit: Debt Bank — store Overload as Energy
- Conduit: Redline Prism — +3 damage if Overloaded
- Machinist: Hot Swap, Spare Frame, Toolbox Search
- Catalyst: Specimen Debt — lose 2 HP, add a temporary Anomaly
- Axiom: Clean Branch — Scry 3
- Syndicate: Lux Harvest — lose 2 HP, gain 2 Gold
The later-wave authoring invented verbs the registry cannot speak, then shipped the row with empty effects. A player who is offered Relief Vent is offered flavor. That is worse than not having the card: it teaches that text is a lie.
Worse: some payload cards lie in the other direction. Latch Turret and Drone Swarm say they deploy; they DEAL_DAMAGE 1–2. Machinist identity is a 6 HP Repair Drone and a Salvari decoy. Everyone else is Rivet Burst.
Starters (what a first fight actually is)¶
Official deck rule: 20 cards = 15 class + 5 House, one authored upgrade each. Live BuildStartingDeck is not that: each class’s starterCardIds is 5 class cards plus IDs 601–605 — one starter from every House — then sparse signatureCardIds (Valerii 2 extra, Salvari Ablative Drone, others empty). The first hand is a mixed-House sampler, not a House-priced class.
Starter cores, with payloads:
Anchor. Foundry Bulwark (Block 6 + Density 1), Dense Impact (8 damage, no Counterweight keyword even though DealDamageEffect supports it), Holdfast Array (Block 8), Gravemass Advance (12 + Density), Sinkline Guard (Block 4 + draw). Density exists. Vent does not. Counterweight does not fire.
Drifter. Phase Strike (7 + Echo-Lock 1), Flicker Jab (4×2), Dimensional Slip (Block 6 + draw), Echo Mark (Echo-Lock 2), Tempo Burst (12). Execute is in the engine. The starter can mark; it cannot evade in the fantasy sense. It Blocks like everyone else.
Conduit. Arc Discharge (9), Volt Siphon (6 + energy), Insulation Field (Block 7), Plasma Lash (5 to all), Flux Tap (Block 4 + energy). No Overload keyword on the class starters. Emergency Shunt (shared, heal 8, keyword overload) is the nearby card that actually debts. The class that is supposed to borrow against tomorrow plays as “damage and energy refund.”
Machinist. Repair Drone Bay (real deploy), four generic damage/block/draw cards. The grid is a single repair pet.
Catalyst. Unstable Slash (7 + mutate), Wildcard Compound (Block 5 + mutate), three generics. Mutate is real if AnomalyPool is filled. Quick Play fills it with cardType == Anomaly. JSON has none. Mutation no-ops unless something else injects the pool.
Cover test: hide the class name on Dense Impact, Tempo Burst, Arc Discharge, Rivet Burst, Mutagenic Strike. They are the same card in five fonts. Those are the cards making everything feel like “something” instead of EchoSpire.
Flavor text is often better than rules text (“Draw the threat. Hold the line.” / “If it reaches them, it has already reached you.”). Keep the flavor. Make the rules match.
2. Class mechanics (engine vs cards)¶
The 2026-08-27 identity lock is still the right table. One verb per owner; everyone else may touch it. This pass grades fantasy + live handler + live cards.
Anchor — Density — engine B, cards C¶
Handler: retain Block = min(current Block, Density), cap 10, Grav-Lock at 10 (hand size −1). Distinct. Punishes greed. Counterweight (add Block to damage, do not spend Block) is implemented on DealDamageEffect via keyword and is almost unused in JSON.
Missing: Vent (the class sink), Density from taking hits, persist between combats. Grav-Lock has no Vent to escape. The interesting decision — “how much mass can I carry before Grav-Lock?” — needs a spend. Right now you only stack.
To make it stronger: ship Vent as an effect (VENT_DENSITY → energy or damage). Put Counterweight on Dense Impact. One card that gains Density when Block absorbs HP. Grav-Lock should hurt and be a resource you choose to risk.
Drifter — Echo-Lock — engine A-, cards C+¶
Handler: attack vs ≥5 stacks → ×3, ignore Block, reset. Cap 5. The “exact 5” miss-trap in the open-design list is already a threshold, not a waste-a-stack trap. Good.
Only 18 APPLY_ECHO_LOCK uses in 584 cards. Starters can mark; they cannot need the execute the way Silent needed poison. Evade fantasy is Block.
To make it stronger: more apply-lock cards, a 0-cost “look at the shot” (Scry is Axiom’s verb — Drifter may glance, not own Scry), a card that is bad unless the target is at 3+. Do not give other classes 3× execute.
Conduit — Overload — engine A-, cards D+¶
Handler is registered universally so House cards can Overload. Debt at turn start; excess = unblockable Burnout. That is a real question: “can I pay this back?”
Starters do not ask it. Many Conduit uncommons describe Overload conditions with empty payloads. RandomPolicy will never play into deficit, so the harness cannot see this class.
To make it stronger: Overload keyword on 2–3 starters. One card that is only good if you are already in debt. Burnout-to-Block as a later card (Brownout Halo is text-only). Walk back Overload-as-universal-keyword on class cards that are not Conduit or a House price; the factory comment that faction cards may Overload is fine.
Machinist — Constructs — engine B, cards D¶
Handler: 3-slot grid, turn-start auto-effects, destroy events. AllEnemies auto-target currently resolves to first enemy only — a real bug if you ever ship an all-target turret.
JSON deploys: four (Repair Drone Bay ±, Ablative Drone ±). Overclock unused. “Turret/swarm” cards deal 1–2 damage.
To make it stronger: a 3-construct starter roster (blocker, chip, utility). Overclock as a 1-cost skill. Enemy intents that actually shoot constructs (targeting policies exist). Until the board is a board, this is Defect with one orb.
Catalyst — Mutate — engine C, cards D+¶
MutationHandler only counts CardMutated for a future ultimate. MutateCardEffect picks from AnomalyPool, forces Exhaust, sets rarity Legendary. Pool is empty in content. Six mutate payloads.
Self-harm-to-gamble is the right fantasy and currently collides with Syndicate (self-harm to get paid). Keep the distinction: Catalyst pays HP to roll; Syndicate pays HP to invoice.
To make it stronger: author 8–12 Anomaly cards as cardType: anomaly (one-use, loud, Exhaust). Specimen Debt must have a payload. “Mutated cards do not Exhaust” (Stable Strain) as a rare power, once mutate works.
Innate and ultimate abilities are null on every class in hero-classes.json. Handlers still increment execution/mutation counters “for ultimate charge.” Policies still call UseAbility(). There is no ability. Do not advertise ultimates.
3. Houses as the price of the turn¶
Designed correctly: a House is not a second class. Live data uses pre-July names; that is propagation debt, not a redesign.
Passives: JSON vs handlers¶
| House | factions.json says |
Handler does | Match? |
|---|---|---|---|
| Valerii | Stasis: skip enemy execution, freeze Block | StasisLockPassive: +3 Block if no HP damage last turn |
No. Skip-turn Stasis is StasisHandler + APPLY_STASIS cards |
| Syntacta (Axiom) | Logic Stream: look at top X, discard/reorder | Sets SCRY_AVAILABLE / SCRY_COUNT=2; headless does not reorder |
Partial |
| Aethari (Syndicate) | Pay HP, next Attack +Y% | Every turn 2 unblockable HP, +20% all damage | Stronger, always-on tax |
| Annalis (Censors) | Pick 1 from discard | Auto-pick random copy at +1 cost | Partial |
| Salvari | Construct death or Block broken → energy or draw | +1 energy on card Exhaust | No |
Valerii and Salvari are the dangerous ones. Design docs, House data, and combat disagree. A player reading “Scrap Protocol” in a tooltip and exhausting nothing will think the game is broken. Exhaust is also nearly untagged in JSON (2 cards), so the live Salvari passive barely has fuel.
Always-on Syndicate 2 HP / turn is a sharp identity and will dominate balance: every other plan is “don’t die to your own House.” That can be great if the payoff is visible. It is currently a quiet leak.
House cards that actually fire identity: Valerii Taunt/Intercept/Stasis, Axiom Logic Stream, Syndicate Echo Burn + Crystal Veil, Censors discard conditionals, Salvari Ablative Drone. That is a thin but real layer. Lux Harvest (the most Syndicate sentence in the pool — lose HP, gain gold) has no payload.
Cover test for Houses: a House card should change the price of the class turn (sacrifice, steal, refuse), not add a second engine. Keep Stasis on Valerii. Do not let Conduit Scry. Do not let Catalyst become “Syndicate but random.”
Legality matrix in gameplay-reference.md matches restrictedClassIds. Keep the holes.
4. Artifacts¶
21 published rows in artifacts.json. Bands: foundation 6, engine 7, volatile 5, capstone 3. Texts are among the best rules writing in the project — they talk in run language, not just combat:
- Anchor Shard Lattice — Block at combat start; Anchor nodes reveal a safer branch
- Contraband Ledger — first market purchase each realm costs 25 less
- Null Chapel Core — first sanctuary reinforce free; next combat Overloaded
- Riftwake Diadem — extra card reward; elite/boss gold
- Aegis Confluence — elite/boss: energy, Block, copy first Attack
ArtifactHandler implements a solid combat subset (Ferrum Shard, Bulwark Seal, Execution Spike, Echo Diode, Gravity Gyro, Overcharge Baffle, Mutagenic Vial, Scrap Magnet, etc.). QuickPlayRunService hard-codes some map/economy IDs (Ledger, Compass, Halo, Diadem, Chapel). Several texts (SCRIBE'S ECHO, parts of route artifacts) are still authoring.
The pool is small and combat-passive-heavy, as the todo says. It is also the right shape for run pivots. One deliberately loud artifact per class verb and per House price would do more than growing to 80 relics that all say “at turn start, gain 4 Block.”
Art: JSON points at assets/artifacts/art/artifact-*.jpg. Disk has ~16 images vs 21 rows. Coverage is incomplete.
5. Enemies, events, consumables¶
Enemies: 164 published (98 minion, 43 elite, 23 boss). HP 12–150. Almost every intent is a short attack/defend/debuff cycle with effectId: null. passiveEffects on 12; scriptedPhases on 21. Variety is names + biomes + HP, not engines. Boss names are strong (The Null Arbiter, The Temporal Parasite, Father of Tides, The Unfiled Page). Fights will feel same-y until intents call real effects and a few bosses have phases that change the question (destroy constructs; punish Overload; require Echo-Lock execute).
Enemy art: ~37 files vs 164 rows. Many will share placeholders (B_1.png style). Fine for internal play; not fine for a trailer close-up of every elite.
Random events: random-events.json has rows: []. The run loop has an Event node type and a WPF view. The pool is empty. That is a content hole the size of STS’s events, which are half the personality of a run.
Consumables: 35 published, many interesting (Null-Seal, Breach Flare, Dimensional Anchor, Patchwork Repair). Official direction defers this layer until artifacts prove stable. Do not build a second item system on Kickstarter. One or two tutorial consumables is enough.
Quests: 6 templates — the opening + five House tutorials. No campaign quests. Hero progression (levels 2–10, binary specializations) is design-only.
6. Art¶
| Set | On disk | vs content |
|---|---|---|
| Card art | 287 jpg + 3 png + 290 json sidecars | Most cards keyed; 12 empty paths |
| Card frames | 20 (4 types × 5 Houses) | Present; old House filenames |
| Crests | 5 png | Present; Syntacta / Aethari / Annalis |
| Banners / leader portraits | empty folders | Still true |
| Artifact art | ~16 | Incomplete |
| Enemy art | ~37 | Thin |
| Hero / class portraits | present (class-anchor.png etc.) |
Fine for setup |
| UI stills, opening plates, fonts | relatively rich | Opening plates exist; copy is wrong |
This is a Leonardo / Lucid Origin batch look. For a first-game Kickstarter that can work if: (a) the stills are consistent, (b) you do not pretend they are a hand-painted AAA, (c) combat uses the art you have instead of ART PENDING, (d) trailer and demo share House crests under the canon names. AI-art comment threads are inevitable. A fun 25-minute loop is the only rebuttal that matters. A second 400-card art pass is not.
ACTIVE-ASSET-REGISTRY.md is a useful map and is only partially populated. It does not inventory the 584-card set.
7. The balancing harness — what it needs to succeed¶
You built the right kind of tool: headless Core, seeded batches, policies, telemetry, analytics endpoints (win rate, card dominance, enemy lethality). That is more than most solo deckbuilders ever get.
It will not succeed until it is measuring the designed game.
Must-haves:
- Promoted
cards.jsononly. If the batch ran on bootstrap 49 or on fallback Strikes, throw the batch away. Tag every run with snapshot hash. - Unimplemented filter. Any card with empty
effectsis excluded from offers and from dominance charts, or is charted separately as “inert.” Otherwise Relief Vent pollutes “dead cards.” - Identity policies, not just greedy numbers.
- Conduit: allowed to play into energy deficit; scores Burnout vs lethal.
- Drifter: values Echo-Lock stacking and holds the execute.
- Anchor: values Density and Vent; understands Grav-Lock as a cap.
- Machinist: values empty construct slots and Overclock.
- Catalyst: only meaningful once Anomalies exist.
- RandomPolicy must not be the headline. Keep it as a baseline idiot. Publish heuristic (and later, scripted “try to execute”) as the balance brain.
- Stats by card id, class×House pair, and enemy id. Names collide (
Foundry Bulwarkexists twice). - Starter decks that match the official 15+5, or you are balancing a mixed-House sampler nobody will ship.
- Human goldens. Ten scripted fights per class (the “did Vent work?” tests) sitting next to the 1,000-run batches. Simulation without a feel pass will flatten identity into DPS.
Nice-to-haves after that: balance dashboard, outlier bosses, realm-passive toggles, protection-rift survival rates. Do not build the dashboard first.
Admin card-effect editor is still missing. JSON-in-repo is acceptable for a solo developer. It is not acceptable as the only way to fill 108 payloads if you want non-you help. A form that cannot save List<CardEffect> is why empty rows survive.
How to make the game stronger (assets only)¶
In order. Not a second backlog — a filter.
- Fill or cut the 108. Every empty payload is either an effect you implement this month or a card you unpublish. No third state.
- Make starters teach the verb. Two identity cards in the first five, minimum, per class. Overload keyword on Conduit. Counterweight on an Anchor attack. A second construct. Anomalies in the pool.
- Fix the two House-passive lies (Valerii, Salvari) so tooltip, JSON, and handler say the same sentence.
- Stop shipping text/payload mismatches (Latch Turret deals 1).
- One event pool of 12–20 real choices before another 80 cards.
- Enemies that ask the class question — one elite that shoots constructs, one that must be Echo-Locked, one that punishes Overload. 164 same-cycle minions will not do this.
- Then run the harness with identity policies. Balance numbers last. You cannot tune what does not execute.
The designed classes are strong enough. The live pack is a generic deckbuilder wearing their names. Closing that gap is the entire mechanical job between here and a demo a stranger remembers.