Skip to content

3.0 cyberspace!#12

Merged
facerix merged 55 commits into
mainfrom
3.0-cyberspace
Jun 26, 2026
Merged

3.0 cyberspace!#12
facerix merged 55 commits into
mainfrom
3.0-cyberspace

Conversation

@facerix

@facerix facerix commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Whew, this turned out to be pretty chunky

@facerix facerix force-pushed the 3.0-cyberspace branch 2 times, most recently from 3f6a530 to e497c47 Compare June 9, 2026 21:02
facerix and others added 16 commits June 10, 2026 12:11
DATA_NODE_SLICE objective kind with cross-validated requiresCyberspace/count
params, cyber-data-spike recipe gated on Act 2+ with a living Decker
(availableWhen filter across all three generation paths), Campaign
hasLivingDecker getter + loud deploy gate, and NEEDS DECKER row gating in
the briefing crew list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JackInPoint interactable (Ω) placed for data-node-slice contracts: Decker-only
capability sniff, linked latch with logged (never thrown) redundant input,
one-shot EVENT.JACK_IN emit. Run latches a dormant cyberspace state machine at
briefing; snapshot/restore enforce the block ⇔ cyber-contract invariant and
throw on unknown phases, dormant payload smuggling, or missing linked state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records the four scope decisions, S1/S2 implementation notes (shipped),
approved architecture for S3-S7, and verified research notes so the
Cyberspace effort can resume cold at S3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
facerix and others added 11 commits June 10, 2026 13:39
S3 of the cyberspace slice plan. The digital layer exists and round-trips:

- cyberMapBuild: deterministic node-lattice generator (spanning random walk
  over a 4×2 cell lattice, rooms + L-corridors, FLOOR/WALL only), seeded
  from the contract via Rng.fork('cyberspace') so layout is independent of
  jack-in turn. Connectivity flood-validated; unreachable port/node throws.
- CyberAvatar (Entity subclass): maxHp = Decker RAM, damageReduction = ICE
  resistance, carries intrusionStrength + callsign, baseHitChance 0.8,
  isCyberAvatar capability flag.
- EntryPort interactable: avatar-only interact emits JACK_OUT (resolve
  latch lives on Run.cyberspace, not the port).
- CyberspaceLayer: own bus + World, build/restore ctors, onTurnEnded AP
  refresh + alarm tick driven by the single meat TurnQueue, mapSeen.
- Run: CyberspaceState active/resolved phases, cyberActive/activeWorld/
  activeActor accessors, jackIn/jackOut with throw-on-illegal-phase,
  cyber ENTITY_DAMAGED listener (avatar death → RESULT/DEATH flatline),
  snapshot block for all three phases.
- persistence: restoreCyberspace with full adversarial throw matrix
  (allowed-keys per phase, grid/tile validation, exactly-one avatar/port,
  bounds checks against the cyber grid); Decker cyber stats persist via
  both crew paths (campaign snapshot + run-entity extra), absent → base
  defaults, partial → throw.
- Decker: ram/intrusionStrength/iceResistance fields with ctor validation
  and DECKER_BASE_* constants (scope decision #4 — named stats, real
  effects).

49 new tests under tests/unit/game/cyber/ (failing-first verified).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
S4 of the cyberspace slice plan. The layer now has a reason to exist:

- DataNode interactable (glyph ◈): interact adds the actor's
  intrusionStrength to sliceProgress; sliced at sliceDifficulty (2/3/4 by
  contract difficulty). Avatar-only via the isCyberAvatar sniff — the
  Decker body also carries intrusionStrength, so the stat is not the
  capability. Slicing is silent; ICE detection (S6) owns the cyber alarm.
  Progress is raw (never clamped) so persisted state loses nothing.
- CyberspaceLayer.build gains required nodeCount (the contract objective's
  count — required, not defaulted, so a divergence can't silently corrupt
  the objective) and spawns nodes on the farthest node anchors from entry,
  Chebyshev-sorted stable over the deterministic generator order.
- Run: ObjectiveState.cyber tally threaded per phase (live count while
  active, the resolved latch after jack-out, zero while dormant);
  DATA_NODE_SLICE satisfaction counts sliced >= required; jackOut latches
  objectiveComplete from the real tally — jacking out early leaves the
  objective permanently unsatisfiable and extraction gated through the
  existing abort-confirm flow (zero new extraction code).
- objectiveProgress: dataNodeProgress helper + NODES HUD chip; the chip
  pipeline is kind-generic so no shell changes needed.
- persistence: data-node entity codec (label/sliceDifficulty/sliceProgress,
  malformed throws); active cyberspace blocks must carry exactly the
  contract's node count (nodes never despawn — any drift is corruption).

25 new tests (dataNode.test.ts, dataNodeObjective.test.ts; failing-first
verified), suite at 1629 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The P3.M4.6 pull-forward (scope decision #2), completing what S3/S4
started (resolve latch, teardown, autosave, objective tally):

- JackInPoint gains the `burned` latch: `Run.jackOut()` burns the meat-side
  port when the avatar routes out, so re-jack-in is refused with dedicated
  flavor (`link-burned` / "LINK BURNED — the connection is dead") instead
  of the redundant-input 'already-linked'. `burn()` on an unlinked port
  throws (corrupt state), as does constructing one.
- Snapshot extra carries `burned`; pre-S5 records restore unburned (legacy
  normalization), non-boolean or burned-without-linked throws.
- Coverage audit of the planned S5 matrix: jackOut from dormant / on a
  non-cyber contract throws (new); resolved latching both ways, post-jack-
  out round-trip, and resolved-block smuggling were already locked by
  S3/S4 tests.

8 new tests (jackOut.test.ts; failing-first verified), suite at 1637 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
S6 of the cyberspace slice plan:

- ProbeIce (glyph ¶, displayName Probe) extends PatrolHostile: explicit
  small stats (hp 3, melee dmg 1, sight 6 — ICE scaling is its own axis,
  resolveEnemyStats tiers deliberately unused). EngageSteps = trace flare
  then Guard-style close-and-strike: raiseAlarm({repPenalty: false}) on
  the *cyber* world self-gates while ALERT, so the 'trace-alarm' step
  fires once per alarm window; probes listen for alarms (PatrolHostile
  default, unlike the Lookout) so one flare converges the pack.
- CyberspaceLayer.build spawns one probe per patrol ring at a seed-picked
  ring tile (pure function of the contract seed) and binds it to the
  layer bus; the restore path was already re-binding PatrolHostiles.
- Registry plumbing: 'probe-ice' joins PATROL_ARCHETYPE_IDS (shares the
  generic patrol snapshot/restore machinery wholesale), archetypeOf,
  SNAPSHOT_EXTRACTORS, ARCHETYPE_FACTORY, kindFromId; 'trace-alarm' step
  type + corp-turn status copy line.
- Death paths proven through the real combat pipeline (no synthetic
  emits): probe melee on a 1-hp avatar → RESULT/DEATH with cause; avatar
  killing a probe counts in run telemetry. ICE resistance mitigation
  locked to the min-1 rule (dmg 1 vs resist 1 → 1).
- Cyber alarm verified decoupled from Meatspace (no ALARM on the meat
  bus, meat latch untouched) and ticking on meat round advances.

9 new tests (ProbeIce.test.ts; failing-first verified), suite 1646 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cyber tileset axis in palette (glyphForTile tileset param, throws on
unknown), buildFrame/AsciiRenderer threading, RAM-labeled avatar HUD,
// THE GRID // location line, active-view seam (run.activeWorld /
activeActor) through applyIntent + look/describe + vision, and the
chained dual-phase corp turn (meat hostiles, then ICE) locked by
dualPhaseTurn.test.ts shared-rng order assertions.

kaizen.md: index.ts complexity findings recorded for post-M3 cleanup
(ShellScene casts, statusLine extraction, listener rewire dedupe,
bus listener-order coupling, active-view seam enforcement).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
buildScoreContract now emits kind DATA_NODE_SLICE with
{requiresCyberspace: true, count: 1} (Rylee's call: the Score always
has a Cyberspace component, for now). P3.M1.7 tests deploy the
Decker through the living-Decker gate and lock the objective shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An incomplete jack-out is irreversible (LINK BURNED + objective latched
unsatisfiable) but resolved with zero warning. Run.jackOut now defers to
onJackOutRequested when the objective is incomplete (mirror of the
onAbortRequested seam; no callback → resolve immediately, same harness
posture); run.confirmJackOut() finalizes, throwing on illegal states —
unlike confirmAbort there is no legal request-voiding race, so a stale
confirm is a wiring bug. A complete jack-out never asks.

Shell: confirmation modal context 'jack-out-early' with link-burn copy;
JACK_OUT bus handler skips the Meatspace swap while the layer is still
live (deferred); completeJackOutShellSwap shared by both resolve paths.
wireRunConfirmations extracted and now also called on campaign resume —
restored runs previously lost onAbortRequested entirely, silently
skipping the abort-extraction confirm after any mid-run reload.

EntryPort success copy neutralized ("routing out…") since the port no
longer knows whether the link actually dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@facerix facerix marked this pull request as ready for review June 26, 2026 00:28
@facerix facerix merged commit 529d3ce into main Jun 26, 2026
2 checks passed
@facerix facerix deleted the 3.0-cyberspace branch June 26, 2026 00:48
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.

1 participant