The implemented combat foundation currently owns exact arithmetic, deterministic random streams, temperament/runtime identities and structural species/lane/team/deck record decoding. It does not yet claim a content catalog or either battle engine.
NewIdentity / identity admit the six Temperaments paired with exactly container, vm,
bare_metal, serverless, edge or mainframe. Unknown, empty, case-adjusted, aliased or
whitespace-adjusted values refuse instead of being repaired. TypeScript also refuses non-string
axes and returns fresh frozen identity storage; Go returns a value and no partial identity on
refusal. These are internal pairs, not a new serialized envelope, species catalog or ownership rule.
ChartIdentities / chartIdentities revalidate both caller-built pairs and delegate to the
existing temperament chart. Runtime is a future catalog tag carrier, not a second Phase-0 type
chart: all 1,296 directed matchups across the 36 identities match the existing shared literal
chart rows. Every identity has 12 advantaged, 12 disadvantaged and 12 neutral opponents. Both
runtimes consume testdata/combat/identity-vectors.json plus the existing arithmetic vectors.
No stats, tags, sprites, effects, Trust/Soul tables, live engine, replay or HTTP consumer is added.
As with the reference library, authoritative integration must register combat's runtime paths
and perform the genuine kernel version change before activation.
DecodeTeamReferences / decodeTeamReferences and the corresponding deck functions implement
Combat C2's exact {v: 1, ids: [...]} envelope. Teams have three ordered species references;
decks have eight ordered card references. Callers must supply declarations from their validated,
pinned catalog. Unknown references, wrong slot counts, missing/extra/duplicate keys, noninteger
version tokens and non-string entries refuse. Escaped keys/references, whitespace and valid Unicode
remain valid; malformed surrogate escapes cannot become a declared replacement-character ID.
Slots are preserved, not sorted or deduplicated. This is structural reference validation, not
authorization, pet-instance selection, duplicate-selection policy or battle/cycle legality.
Those checks belong to the complete catalog/engine/integration contracts. The result owns its
storage (a Go array; a frozen TypeScript tuple), with no mutation of source or declarations and
no partial references returned on refusal. Both runtimes consume
testdata/combat/reference-vectors.json; the tests use declarations, not a fabricated combat
catalog. No live/replay/HTTP consumer imports this new layer yet. Combat's authoritative
integration must register its runtime paths with the kernel identity guard and perform the
genuine semantic version change; this standalone library is not a replay activation or mint.
DecodeSpeciesRecord / decodeSpeciesRecord implement C2's exact species row:
{id, temperament, runtime, stats: {hp, atk, def, spd, stamina_max}, moves, tags}.
Both identity axes use the existing C1 authority; all five stat fields require integer tokens
in signed-int32 range. Every field is present/non-null, object keys are exact and unique, and
the four ordered moves must occur in the caller's declared move set. Tags are an ordered string
array. Duplicates in moves/tags are preserved: this reader does not invent a selection rule or
synergy vocabulary. Valid escaping/Unicode and empty tag arrays survive; missing/extra/case-
aliased/duplicate keys, malformed surrogate text, wrong types and undeclared moves refuse.
Go additionally rejects malformed UTF-8 before its JSON decoder can replace bytes.
This is structural row decoding, not the complete catalog loader. Its signed-int32 representation does not decide battle-legal stat minima, ID namespace, effect compatibility, species ownership, runtime tag inheritance or balance values. Those remain the complete catalog/engine contracts. The decoder returns no partial Go record on refusal and does not rewrite source bytes. Go results own their four slots and tag slice; TypeScript freezes the record, stats, moves and tags.
Both runtimes consume testdata/combat/species-vectors.json, with eight accepted row forms and
67 refusals, plus every existing temperament/runtime pair and storage/declaration controls.
The ordinary row values are synthetic test data, not a production species fixture. No live
engine, save/replay or HTTP consumer imports this layer. Its eventual authoritative integration
must register combat's runtime prefixes and make the genuine kernel version change before
activation. Full C2's versioned catalog, effect union, cross-row policies and Phase-0 population,
C5's Trust/Soul tables and both battle engines remain unimplemented.
DecodeLaneRecord / decodeLaneRecord decode C2's common lane unit/building/spell
row: {id, temperament, cost, hp, atk, spd, range, targets, tags} with an optional
boolean bypass marker. The existing temperament authority is reused; targets are
exactly units, buildings or any. All five numeric fields require signed-int32
integer tokens. Every required field is non-null; duplicate, extra, case-aliased,
missing or wrong-type fields refuse. Ordered string tags and valid Unicode survive;
malformed surrogate text refuses, and Go also rejects malformed UTF-8 bytes.
Absence, explicit false and true bypass markers retain their distinct serialized
forms. Go returns no partial record and owns its tag slice/marker; TypeScript
returns fresh frozen records and tags. Source bytes remain untouched.
This is structural representation, not battle-legal stat/cost minima, a cheap-cycle
threshold, effect execution, a cross-row bypass-coverage check or a complete catalog.
The shared testdata/combat/lane-record-vectors.json supplies eight accepted forms
and92 refusals; tests also cover all54 temperament/target/marker combinations and
independent storage. These synthetic values are not production balance or content.
No live/replay/HTTP consumer imports the decoder. Full catalog construction still
needs the effect/table contracts and complete Phase-0 population; authoritative
integration must register Combat's prefixes and make the genuine kernel version change.
CompareLaneOrder / compareLaneOrder compare C4's (tick, player_slot, placement_seq) keys lexicographically, returning exactly −1, 0 or 1. Earlier
fields always outrank later ones; equal coordinates compare equal. Comparisons
do not subtract signed-int32 components, so representation-edge differences cannot wrap.
Go keys have int32 fields; TypeScript validates all three signed-int32 components
in both keys before comparing, refusing missing/non-numeric/coercible inputs.
The helper does not serialize a command, mutate keys or inspect unrelated payload fields.
This is an ordering projection, not battle-input admission: legal ticks, slots,
sequences, ownership and duplicate-placement handling remain the engine's contract.
Signed edge fixtures exercise representation, not playable slot/tick values.
Both runtimes consume testdata/combat/lane-order-vectors.json; independently
enumerated ranks and reversed rotations also exercise the actual standard sort consumers.
There is no live engine/replay consumer, complete battle log or C4 acceptance claim.
The six Temperaments are ordered lazy, playful, curious, sassy, shy, chaotic. An attacker has advantage over the next two entries, disadvantage against the previous two, and a neutral result against itself and the opposite entry. The chart result is an enum, never a floating multiplier.
Damage is calculated identically in Go and TypeScript using integer stages:
- base power multiplied by attacker ATK and floor-divided by 64;
- advantage multiplied by 13/10 or disadvantage by 10/13, multiply before floor-divide;
- a critical result multiplied by 3/2 and floor-divided;
- positive base power receives minimum damage 1;
- storage saturates to signed int32.
Every intermediate is int64/BigInt. HP and stamina use explicit integer clamp operations.
TypeScript integer-store inputs must be primitive BigInts, and the damage critical flag must be a primitive boolean, even for zero base power. Runtime numbers (including fractions/NaN), numeric strings, boxed primitives and caller coercion cannot pass those boundaries. Damage's base power/ATK and clamp bounds remain signed-int32 numbers; valid results are unchanged. These are standalone library checks, not a new wire grammar or a battle consumer.
Native division is absent from server/combat and client/src/combat; the shared Go
integer.DivideFloor and TypeScript idiv helpers live outside those directories. Both accept
nonnegative dividends and positive divisors, with invalid inputs rejected. The Go helper leaves
the existing staged damage calculation and int32 saturation unchanged.
make verify-combat-go-boundary recursively parses all Go combat sources, including tests and
future engine subdirectories, with Go's AST. It rejects / and /=, malformed sources,
symlinks, missing trees and empty source populations. Its tests exercise those refusals alongside
safe comments, strings and helper calls. It runs in both server verification targets, including
the server CI lane and local verify-push.
make verify-combat-boundary recursively parses every .js, .jsx, .ts, .tsx, .mts, and .cts combat module
with TypeScript's AST. On every verification run it proves direct /, /=, division inside or
after a template interpolation, and TSX division are rejected; strings, comments, and regular
expressions cannot create blind spots or false positives; and a seeded violation in a nested future
engine directory is discovered.
SplitMix64 and unbiased rejection sampling have one Go authority in server/determinism; the
balance harness aliases it rather than maintaining a copy. A battle seed is the first SplitMix64
draw from the match seed. Independent consumers initialize from battle_seed XOR fnv1a64(label),
so adding a random consumer cannot shift crit, obedience, or bot-policy draws. TypeScript implements
the same uint64 wrap semantics with BigInt. Shared vectors cover damage ordering, minimum damage,
saturation, non-identity ATK scaling, an advantage half-point that distinguishes floor from round,
the order-sensitive disadvantage-plus-
critical path, battle seed, all three registered Phase-0 substream labels, and bounded draws.
The bounded corpus records the number of draws consumed and includes a case that rejects four
successive draws before accepting, so a modulo-biased implementation cannot pass accidentally.
testdata/combat/substream-sequences.json additionally pins eight draws for each of the
three fixture labels at match seeds 0, 42 and uint64 maximum. The retained Python reference
prints these values using direct nth-state arithmetic without importing production code;
its seed-42 first draws agree with the original vectors. Go/TS keep all stream instances
alive together and compare complete sequences under round-robin, reversed and chunked
consumption, including a new consumer making bounded draws. A controlled shared/resettable
factory passes the earlier first-draw/recreation pattern but fails the sequence comparison.
This tests factory/state ownership, not a demonstrated production alias, threaded battle,
full catalog label population or golden battle log. Production RNG code is unchanged.
The same shared fixture also pins all 36 directed temperament pairs, nine signed-int32 saturation boundaries, thirteen clamp results and invalid clamp/damage domains in both runtimes. Its high-ATK disadvantage case exceeds int32 between stages but finishes below it, so saturating before the final store produces the wrong result. These are pure-kernel checks, not evidence of an integrated battle, populated combat catalog or live HP/stamina consumer.
The strict combat catalog and Trust/Soul input tables remain unimplemented because their active RFC does not yet enumerate the promised closed effect union or literal piecewise table points. Those are recorded as DESIGN-GAPs in the active planning log rather than represented here as shipped behavior.