Skip to content

feat(asteria-player): spawn-ship + harden publish-images + auto-public ghcr packages#67

Open
paolino wants to merge 4 commits intomainfrom
asteria-spawn-v2
Open

feat(asteria-player): spawn-ship + harden publish-images + auto-public ghcr packages#67
paolino wants to merge 4 commits intomainfrom
asteria-spawn-v2

Conversation

@paolino
Copy link
Copy Markdown
Collaborator

@paolino paolino commented Apr 26, 2026

What this PR does

Three coupled changes that together make it impossible to land asteria spawn (or any future component) work without the published image actually reaching ghcr in a state Antithesis can pull.

1. fix(ci): publish-images fails loudly when an entry can't be built

scripts/push-cardano_node_master_images.sh

Previously the script logged Error: Tag '<tag>' not found in repo. Skipping <name>. and exited 0, so silent skips left CI green while the testnet ran against stale registry images — exactly what happened to my prior asteria PR.

Now:

  • Already-published <name>:<tag> images take a fast path and skip cleanly. This handles legacy components whose original commit has been rebased away (sidecar:1362c5b, tracer-sidecar:ff408d6) — their image is still served from ghcr, the testnet keeps working, and we don't try to rebuild from a missing commit.
  • Anything not in the registry must resolve its tag against the repo. If it can't (e.g. the previous :dev tag), the entry is recorded and the job fails with exit 1 and a clear summary.

2. ci: make published packages public on ghcr

.github/workflows/publish-images.yaml

Antithesis pulls images anonymously; ghcr packages default to private on first push. Adds a post-push step that PATCHes each component package referenced in the testnet's docker-compose to public visibility. Idempotent.

3. feat(asteria-player): spawn-ship works end-to-end against the cluster

Re-applies the previously-reverted asteria spawn work, now with a resolvable image tag (asteria-spawn-v2, the branch name) instead of :dev. The hardened publish script will resolve that tag against this branch's tip, build, and push.

  • Game.hs no longer emits an explicit payTo for the new PILOT NFT — the upstream lambdasistemi/cardano-node-clients PR #77 balanceTx residual-MA folding drops it into the player's ADA change output.
  • PlayerMain.hs calls buildWith with a 1.2x ExUnits margin (mirrors cardano-cli's submit-time overshoot) to absorb the cardano-ledger version drift between client and cluster.
  • configurator.sh writes Conway-mainnet maxTxExUnits (16.5 M mem, 10 B steps) into alonzo-genesis.json.
  • apply-params.sh strips traces via --trace-level silent.
  • cabal.project pins cardano-node-clients to the merged main SHA f578d6cf....

Test plan

  • Local cluster: asteria_player_ship_spawn_attempted_1ship_spawn_built_1ship_spawned_1 on a single attempt with no failures.
  • publish-images job for this PR pushes a real asteria-player:asteria-spawn-v2 to ghcr (gating, no silent skips).
  • Asteria-player package on ghcr is public so Antithesis can pull anonymously.
  • 1-hour Antithesis dispatch on this branch ref (workflow_dispatch with duration=1, ref=asteria-spawn-v2) reports green.
  • Only after the antithesis result is green: ask for merge approval.

paolino added 3 commits April 26, 2026 16:54
Previously the script logged `Error: Tag '<tag>' not found in repo.
Skipping <name>.` and exited 0, so silent skips left CI green
while the antithesis testnet ran against stale registry images.

Now:
- Already-published `<name>:<tag>` images take a fast path and
  skip the build/git-resolve cleanly. This handles legacy
  components whose original commit has since been rebased away
  (e.g. sidecar:1362c5b, tracer-sidecar:ff408d6) — their image is
  still served from ghcr, so the testnet keeps working.
- Anything that is NOT in the registry MUST resolve its tag
  against the repo. If it can't, the entry is recorded and the
  job fails with a non-zero exit at the end with a clear summary
  of which images couldn't be built and why.

This makes it impossible to merge a PR that bumps a docker-compose
image tag without the corresponding git ref and component
directory landing in the same commit.
…#56)

Off-chain wiring that takes the asteria spawn flow from build-only
to accepted-on-chain in the antithesis local cluster, on top of
lambdasistemi/cardano-node-clients PR #77.

- `Game.hs` no longer emits an explicit `payTo` for the new
  PILOT NFT — the upstream `balanceTx` residual-MA folding drops
  it into the player's ADA change output, matching mainnet
  asteria's 3-output spawn shape.
- `PlayerMain.hs` calls the new `buildWith` with a 1.2x
  ExUnits margin (mirrors cardano-cli's submit-time overshoot)
  to absorb cardano-ledger version drift between the client and
  the cluster's cardano-node 10.7.1.
- `configurator.sh` writes Conway-mainnet `maxTxExUnits`
  (16.5 M mem, 10 B steps; block 62 M / 40 B) into
  `alonzo-genesis.json`.
- `docker-compose.yaml` mounts the configurator script for
  local iteration without rebuilding the configurator image, and
  bumps the asteria-player image tag from `:dev` (a local-only
  placeholder that the publish-images job can't resolve to a
  git ref) to `:asteria-spawn-v2` — the branch name. The
  publish-images script clones origin and resolves
  `asteria-spawn-v2` to the branch tip, builds and publishes
  the image at that tag.
- `apply-params.sh` strips traces via `--trace-level silent`
  to keep validator eval cost down.
- `cabal.project` pins cardano-node-clients to the merged main
  SHA `f578d6cf...`.

Verified locally: asteria_player_ship_spawn_attempted_1 →
ship_spawn_built_1 → ship_spawned_1 on a single attempt with no
failures, on the local docker-compose cluster.
Antithesis pulls images anonymously; ghcr packages default to
private on first push. Adds a post-push step that PATCHes each
component package referenced in the testnet's docker-compose to
public visibility. Idempotent for already-public packages and a
no-op for packages that haven't been published yet (they'll be
flipped on the next run).
@paolino paolino added the enhancement New feature or request label Apr 26, 2026
@paolino paolino self-assigned this Apr 26, 2026
@paolino paolino added the enhancement New feature or request label Apr 26, 2026
Fresh clones don't expose feature branches as bare refs — only
as @origin/<branch>@ remote-tracking refs. The publish script's
@git rev-list -n 1 <tag>@ failed to find @asteria-spawn-v2@
even though the branch existed on origin. Falling through to
@origin/<tag>@ when the bare lookup fails covers the
feature-branch case while still working for git tags and
explicit commit SHAs.
paolino added a commit that referenced this pull request Apr 27, 2026
Unblocks scheduled runs. asteria-player:dev never published —
publish-images skips it ("Tag 'dev' not found in repo"), so
Antithesis fails to pull and every scheduled run since 2026-04-25
came back Incomplete with zero container stdout.

Asteria work continues under #56 / PR #67. Re-add to compose once a
real image tag is published.

Ogmios already absent from main (removed in 7e0f8f5 pending #49).
paolino added a commit that referenced this pull request Apr 28, 2026
New `components/asteria-stub/` ships a tiny Debian image with
cardano-cli (+ liblmdb0 runtime), jq, and bash. Its only job is to
host composer scripts at `/opt/antithesis/test/v1/stub/`:

  - parallel_driver_heartbeat.sh — short tick, emits Sometimes
  - eventually_alive.sh         — post-fault: pings p1/p2/p3, Always
  - finally_alive.sh            — post-workload: pings p1/p2/p3, Always

The container itself just `sleep infinity`s. This is intentional:
the stub is the experimentation harness for evolving property checks
before touching the real asteria-player. Real game logic stays in
PR #67's `components/asteria-player/`.

Conventions adopted from the working sidecar template:
  - cardano-cli ping uses `-j --magic 42 --host X --port Y --tip
    --quiet -c1`. Without `-j --tip` the cli has no query target and
    exits non-zero.
  - SDK output goes to /tmp/sdk.jsonl (default path) on a tmpfs
    mount; Antithesis introspects that path. A named-volume
    convention (e.g. ANTITHESIS_OUTPUT_DIR=/sdk) is not picked up.
  - liblmdb0 must be apt-installed; cardano-cli loads liblmdb.so.0
    at start regardless of subcommand.

On failure, eventually_alive / finally_alive emit a self-classifying
JSON detail with the failing host, exit code, and the cardano-cli
stderr — so the next failure self-diagnoses without another round
trip.

Image referenced by content digest in
testnets/cardano_node_master/docker-compose.yaml; the publish-images
workflow builds and pushes both `:asteria-stub-v3` and
`:<full-commit>` on PR. Bootstrap relied on a paired git tag so the
script could resolve the friendly name back to a commit; once
merged, the digest pin is the only contract.
paolino added a commit that referenced this pull request Apr 29, 2026
New `components/asteria-stub/` ships a tiny Debian image with
cardano-cli (+ liblmdb0 runtime), jq, and bash. Its only job is to
host composer scripts at `/opt/antithesis/test/v1/stub/`:

  - parallel_driver_heartbeat.sh — short tick, emits Sometimes
  - eventually_alive.sh         — post-fault: pings p1/p2/p3, Always
  - finally_alive.sh            — post-workload: pings p1/p2/p3, Always

The container itself just `sleep infinity`s. This is intentional:
the stub is the experimentation harness for evolving property checks
before touching the real asteria-player. Real game logic stays in
PR #67's `components/asteria-player/`.

Conventions adopted from the working sidecar template:
  - cardano-cli ping uses `-j --magic 42 --host X --port Y --tip
    --quiet -c1`. Without `-j --tip` the cli has no query target and
    exits non-zero.
  - SDK output goes to /tmp/sdk.jsonl (default path) on a tmpfs
    mount; Antithesis introspects that path. A named-volume
    convention (e.g. ANTITHESIS_OUTPUT_DIR=/sdk) is not picked up.
  - liblmdb0 must be apt-installed; cardano-cli loads liblmdb.so.0
    at start regardless of subcommand.

On failure, eventually_alive / finally_alive emit a self-classifying
JSON detail with the failing host, exit code, and the cardano-cli
stderr — so the next failure self-diagnoses without another round
trip.

Image referenced by content digest in
testnets/cardano_node_master/docker-compose.yaml; the publish-images
workflow builds and pushes both `:asteria-stub-v3` and
`:<full-commit>` on PR. Bootstrap relied on a paired git tag so the
script could resolve the friendly name back to a commit; once
merged, the digest pin is the only contract.
paolino added a commit that referenced this pull request Apr 29, 2026
New `components/asteria-stub/` ships a tiny Debian image with
cardano-cli (+ liblmdb0 runtime), jq, and bash. Its only job is to
host composer scripts at `/opt/antithesis/test/v1/stub/`:

  - parallel_driver_heartbeat.sh — short tick, emits Sometimes
  - eventually_alive.sh         — post-fault: pings p1/p2/p3, Always
  - finally_alive.sh            — post-workload: pings p1/p2/p3, Always

The container itself just `sleep infinity`s. This is intentional:
the stub is the experimentation harness for evolving property checks
before touching the real asteria-player. Real game logic stays in
PR #67's `components/asteria-player/`.

Conventions adopted from the working sidecar template:
  - cardano-cli ping uses `-j --magic 42 --host X --port Y --tip
    --quiet -c1`. Without `-j --tip` the cli has no query target and
    exits non-zero.
  - SDK output goes to /tmp/sdk.jsonl (default path) on a tmpfs
    mount; Antithesis introspects that path. A named-volume
    convention (e.g. ANTITHESIS_OUTPUT_DIR=/sdk) is not picked up.
  - liblmdb0 must be apt-installed; cardano-cli loads liblmdb.so.0
    at start regardless of subcommand.

On failure, eventually_alive / finally_alive emit a self-classifying
JSON detail with the failing host, exit code, and the cardano-cli
stderr — so the next failure self-diagnoses without another round
trip.

Image referenced by content digest in
testnets/cardano_node_master/docker-compose.yaml; the publish-images
workflow builds and pushes both `:asteria-stub-v3` and
`:<full-commit>` on PR. Bootstrap relied on a paired git tag so the
script could resolve the friendly name back to a commit; once
merged, the digest pin is the only contract.
paolino added a commit that referenced this pull request Apr 29, 2026
New `components/asteria-stub/` ships a tiny Debian image with
cardano-cli (+ liblmdb0 runtime), jq, and bash. Its only job is to
host composer scripts at `/opt/antithesis/test/v1/stub/`:

  - parallel_driver_heartbeat.sh — short tick, emits Sometimes
  - eventually_alive.sh         — post-fault: pings p1/p2/p3, Always
  - finally_alive.sh            — post-workload: pings p1/p2/p3, Always

The container itself just `sleep infinity`s. This is intentional:
the stub is the experimentation harness for evolving property checks
before touching the real asteria-player. Real game logic stays in
PR #67's `components/asteria-player/`.

Conventions adopted from the working sidecar template:
  - cardano-cli ping uses `-j --magic 42 --host X --port Y --tip
    --quiet -c1`. Without `-j --tip` the cli has no query target and
    exits non-zero.
  - SDK output goes to /tmp/sdk.jsonl (default path) on a tmpfs
    mount; Antithesis introspects that path. A named-volume
    convention (e.g. ANTITHESIS_OUTPUT_DIR=/sdk) is not picked up.
  - liblmdb0 must be apt-installed; cardano-cli loads liblmdb.so.0
    at start regardless of subcommand.

On failure, eventually_alive / finally_alive emit a self-classifying
JSON detail with the failing host, exit code, and the cardano-cli
stderr — so the next failure self-diagnoses without another round
trip.

Image referenced by content digest in
testnets/cardano_node_master/docker-compose.yaml; the publish-images
workflow builds and pushes both `:asteria-stub-v3` and
`:<full-commit>` on PR. Bootstrap relied on a paired git tag so the
script could resolve the friendly name back to a commit; once
merged, the digest pin is the only contract.
paolino added a commit that referenced this pull request Apr 29, 2026
Drop the asteria-stub harness — it served its purpose as the green
baseline (PR #74 iter 5 was the proof). Replace it with the actual
asteria-player workload from #67 (asteria-spawn-v2 HEAD ab1a462):

  - components/asteria-player/ — Haskell binaries (asteria-bootstrap
    + asteria-player), Aiken validators, composer scripts at
    composer/asteria/{eventually_asteria_alive, helper_sdk_lib,
    parallel_driver_asteria_bootstrap, parallel_driver_asteria_player}.sh

Compose adds three services (asteria-bootstrap one-shot + 2 long-
running players) and the asteria-sdk volume that connects them. The
image is pinned by content digest to the build of #67's HEAD —
ghcr.io/.../asteria-player@sha256:2cf2b33f… — so we run exactly the
bytes #67 produced. The branch tag (:asteria-spawn-v2) which #67's
own compose still uses is a tag-mutability footgun that #71's
discipline eliminates here.

Future bumps: change the source under components/asteria-player/,
let publish-images build a new image at the new commit, then update
this digest. publish-images already supports digest-only entries via
PR #71's script change (skipped, image already exists).
paolino added a commit that referenced this pull request Apr 30, 2026
PR 1 of 2 toward the real asteria workload. Scope-limited to the
rename + Haskell source lift + nix scaffolding so the binaries
build alongside the existing utxo-indexer. Bootstrap is iteration
5b — *not safe for repeat invocation* — that gap is explicitly the
subject of PR 2.

Rename:
  components/asteria-stub/ → components/asteria-game/
  service asteria-stub      → asteria-game
  volume  asteria-stub-db   → asteria-game-db

Lift from #67 (asteria-spawn-v2):
  components/asteria-game/aiken/         (validators + apply-params)
  components/asteria-game/src/Asteria/   (game state, datums, validators, wallet, providers, RNG, SDK)
  components/asteria-game/app/{BootstrapMain.hs, PlayerMain.hs}
  components/asteria-game/asteria-game.cabal      (renamed package)
  components/asteria-game/cabal.project           (cardano-node-clients SRP bumped to PR #98 head 5707836b)

Build infra:
  flake.nix — extends prior asteria-stub flake with haskell.nix /
    iohk-nix overlays so local Haskell packages compile, while
    keeping the upstream cardano-node-clients flake input that
    supplies the prebuilt utxo-indexer (PR #98 supervisor).
  nix/project.nix — lifted from PR #67, package renamed.
  nix/docker-image.nix — bundles utxo-indexer + asteria-bootstrap
    + asteria-game (player) execs + composer scripts + bash/jq/
    socat; entrypoint stays utxo-indexer.

KNOWN GAP — admin_mint validator is the always-true placeholder
PR #67 ships. Without an on-chain one-shot guarantee, every
container restart could mint another admin NFT. Bootstrap is *not*
wired into compose in this PR for that reason. PR 2 patches the
Aiken admin_mint to take an OutputReference parameter, runs
apply-params at bootstrap-time, and adds defence-in-depth detection
in Bootstrap.hs.

Composer scripts (composer/stub/{parallel_driver_heartbeat,
eventually_alive, finally_alive, helper_sdk}.sh) unchanged from
the green stub baseline so this PR stays bisect-safe and in
property terms identical to commit 3b6fb0e (PR #74's merged head).
paolino added a commit that referenced this pull request Apr 30, 2026
PR 1 of 2 toward the real asteria workload. Scope-limited to the
rename + Haskell source lift + nix scaffolding so the binaries
build alongside the existing utxo-indexer. Bootstrap is iteration
5b — *not safe for repeat invocation* — that gap is explicitly the
subject of PR 2.

Rename:
  components/asteria-stub/ → components/asteria-game/
  service asteria-stub      → asteria-game
  volume  asteria-stub-db   → asteria-game-db

Lift from #67 (asteria-spawn-v2):
  components/asteria-game/aiken/         (validators + apply-params)
  components/asteria-game/src/Asteria/   (game state, datums, validators, wallet, providers, RNG, SDK)
  components/asteria-game/app/{BootstrapMain.hs, PlayerMain.hs}
  components/asteria-game/asteria-game.cabal      (renamed package)
  components/asteria-game/cabal.project           (cardano-node-clients SRP bumped to PR #98 head 5707836b)

Build infra:
  flake.nix — extends prior asteria-stub flake with haskell.nix /
    iohk-nix overlays so local Haskell packages compile, while
    keeping the upstream cardano-node-clients flake input that
    supplies the prebuilt utxo-indexer (PR #98 supervisor).
  nix/project.nix — lifted from PR #67, package renamed.
  nix/docker-image.nix — bundles utxo-indexer + asteria-bootstrap
    + asteria-game (player) execs + composer scripts + bash/jq/
    socat; entrypoint stays utxo-indexer.

KNOWN GAP — admin_mint validator is the always-true placeholder
PR #67 ships. Without an on-chain one-shot guarantee, every
container restart could mint another admin NFT. Bootstrap is *not*
wired into compose in this PR for that reason. PR 2 patches the
Aiken admin_mint to take an OutputReference parameter, runs
apply-params at bootstrap-time, and adds defence-in-depth detection
in Bootstrap.hs.

Composer scripts (composer/stub/{parallel_driver_heartbeat,
eventually_alive, finally_alive, helper_sdk}.sh) unchanged from
the green stub baseline so this PR stays bisect-safe and in
property terms identical to commit 3b6fb0e (PR #74's merged head).
paolino added a commit that referenced this pull request Apr 30, 2026
PR 1 of 2 toward the real asteria workload. Scope-limited to the
rename + Haskell source lift + nix scaffolding so the binaries
build alongside the existing utxo-indexer. Bootstrap is iteration
5b — *not safe for repeat invocation* — that gap is explicitly the
subject of PR 2.

Rename:
  components/asteria-stub/ → components/asteria-game/
  service asteria-stub      → asteria-game
  volume  asteria-stub-db   → asteria-game-db

Lift from #67 (asteria-spawn-v2):
  components/asteria-game/aiken/         (validators + apply-params)
  components/asteria-game/src/Asteria/   (game state, datums, validators, wallet, providers, RNG, SDK)
  components/asteria-game/app/{BootstrapMain.hs, PlayerMain.hs}
  components/asteria-game/asteria-game.cabal      (renamed package)
  components/asteria-game/cabal.project           (cardano-node-clients SRP bumped to PR #98 head 5707836b)

Build infra:
  flake.nix — extends prior asteria-stub flake with haskell.nix /
    iohk-nix overlays so local Haskell packages compile, while
    keeping the upstream cardano-node-clients flake input that
    supplies the prebuilt utxo-indexer (PR #98 supervisor).
  nix/project.nix — lifted from PR #67, package renamed.
  nix/docker-image.nix — bundles utxo-indexer + asteria-bootstrap
    + asteria-game (player) execs + composer scripts + bash/jq/
    socat; entrypoint stays utxo-indexer.

KNOWN GAP — admin_mint validator is the always-true placeholder
PR #67 ships. Without an on-chain one-shot guarantee, every
container restart could mint another admin NFT. Bootstrap is *not*
wired into compose in this PR for that reason. PR 2 patches the
Aiken admin_mint to take an OutputReference parameter, runs
apply-params at bootstrap-time, and adds defence-in-depth detection
in Bootstrap.hs.

Composer scripts (composer/stub/{parallel_driver_heartbeat,
eventually_alive, finally_alive, helper_sdk}.sh) unchanged from
the green stub baseline so this PR stays bisect-safe and in
property terms identical to commit 3b6fb0e (PR #74's merged head).
paolino added a commit that referenced this pull request May 1, 2026
PR 1 of 2 toward the real asteria workload. Scope-limited to the
rename + Haskell source lift + nix scaffolding so the binaries
build alongside the existing utxo-indexer. Bootstrap is iteration
5b — *not safe for repeat invocation* — that gap is explicitly the
subject of PR 2.

Rename:
  components/asteria-stub/ → components/asteria-game/
  service asteria-stub      → asteria-game
  volume  asteria-stub-db   → asteria-game-db

Lift from #67 (asteria-spawn-v2):
  components/asteria-game/aiken/         (validators + apply-params)
  components/asteria-game/src/Asteria/   (game state, datums, validators, wallet, providers, RNG, SDK)
  components/asteria-game/app/{BootstrapMain.hs, PlayerMain.hs}
  components/asteria-game/asteria-game.cabal      (renamed package)
  components/asteria-game/cabal.project           (cardano-node-clients SRP bumped to PR #98 head 5707836b)

Build infra:
  flake.nix — extends prior asteria-stub flake with haskell.nix /
    iohk-nix overlays so local Haskell packages compile, while
    keeping the upstream cardano-node-clients flake input that
    supplies the prebuilt utxo-indexer (PR #98 supervisor).
  nix/project.nix — lifted from PR #67, package renamed.
  nix/docker-image.nix — bundles utxo-indexer + asteria-bootstrap
    + asteria-game (player) execs + composer scripts + bash/jq/
    socat; entrypoint stays utxo-indexer.

KNOWN GAP — admin_mint validator is the always-true placeholder
PR #67 ships. Without an on-chain one-shot guarantee, every
container restart could mint another admin NFT. Bootstrap is *not*
wired into compose in this PR for that reason. PR 2 patches the
Aiken admin_mint to take an OutputReference parameter, runs
apply-params at bootstrap-time, and adds defence-in-depth detection
in Bootstrap.hs.

Composer scripts (composer/stub/{parallel_driver_heartbeat,
eventually_alive, finally_alive, helper_sdk}.sh) unchanged from
the green stub baseline so this PR stays bisect-safe and in
property terms identical to commit 3b6fb0e (PR #74's merged head).
paolino added a commit that referenced this pull request May 1, 2026
PR 2: make rerunning bootstrap safe. Antithesis can restart the
asteria-game container at any time; serial_driver_asteria_bootstrap
will re-fire on each restart. The Haskell binary now skips the
mint+lock if the asteria spend address already carries a UTxO with
the asteriaAdmin token.

Defence layers in place after this PR:
  1. Haskell-side detection (this PR): Provider.queryUTxOs at the
     asteria spend address; presence of (admin_mint_hash,
     "asteriaAdmin") = already deployed → exit 0 with
     sdk_sometimes("asteria_bootstrap_already_deployed").
  2. Antithesis serial_driver scheduling: exclusive access while
     bootstrap runs → no concurrent invocations on the same timeline.

KNOWN GAP — defence layer 3 (Plutus one-shot) is still on the
todo list. PR #67's admin_mint validator is the always-true
placeholder, so there is no chain-level uniqueness guarantee yet.
A future PR replaces admin_mint with a parameterised one-shot
that consumes a seed OutputReference; until then the indexer
race window between detection and submission, while small under
serial_driver scheduling, is non-zero. See spec FR-010 / User
Story 4.

New: composer/stub/serial_driver_asteria_bootstrap.sh — exec
/bin/asteria-bootstrap. Antithesis discovers it under
/opt/antithesis/test/v1/stub/ (already mounted via the same
docker-image build).
paolino added a commit that referenced this pull request May 1, 2026
Renames components/asteria-player/ → components/asteria-game/ and
upgrades the lifted PR #67 sources so the bootstrap is safe to
re-run on container restart.

  - cabal package + executable renamed asteria-player → asteria-game.
  - cabal.project SRP pinned to cardano-node-clients PR #98 head
    5707836b (utxo-indexer supervisor + N2C reconnect).
  - flake.nix pulls cardano-node-clients utxo-indexer as a flake
    input so dockerTools bundles the prebuilt binary.
  - nix/docker-image.nix bundles utxo-indexer + asteria-bootstrap +
    asteria-game (player) execs + composer/stub scripts; entrypoint
    is the indexer, the bootstrap runs as a serial driver.
  - composer/stub/ shape replaces composer/asteria/: green-baseline
    heartbeat / eventually_alive / finally_alive plus a new
    serial_driver_asteria_bootstrap that execs /bin/asteria-bootstrap.
  - app/BootstrapMain.hs gains Asteria.Bootstrap.isAlreadyDeployed:
    queries Provider for UTxOs at the asteria spend address and
    short-circuits if any UTxO carries the @"asteriaAdmin"@ token.
    Antithesis can restart the asteria-game container at will and
    bootstrap exits 0 quickly on subsequent invocations.

The asteria_game testnet that wires this image into Antithesis is
added in the next commit.

KNOWN GAP — admin_mint validator is the always-true placeholder
PR #67 ships. The Haskell-side detection plus Antithesis's
@serial_driver_@ scheduling are the contract until a follow-up PR
replaces admin_mint with a one-shot policy parameterised on a seed
@OutputReference@.

Tracks: #67 (asteria-spawn-v2), #98 (utxo-indexer supervisor).
Closes companion: #108 (idempotent bootstrap, content folded here).
paolino added a commit that referenced this pull request May 1, 2026
Adds the player workload to the asteria_game testnet's composer
harness. PlayerMain.hs is reshaped from a forever-loop to a
single-pass binary so the Antithesis composer can re-fire it on
its own schedule (a forever loop blocks exclusive scheduling for
serial drivers).

components/asteria-game/composer/stub/parallel_driver_asteria_player.sh:
  - Picks ASTERIA_PLAYER_ID in {1,2,3} based on the wallclock so
    different timelines exercise different players.
  - Player 1 attempts the spawn (PlayerMain gates on id == "1");
    players 2 and 3 observe the asteria UTxO without acting,
    exercising the read path.

components/asteria-game/app/PlayerMain.hs:
  - main: drop the forever loop and the in-process IORef Bool
    "have I spawned yet" guard. Spawn idempotence comes from chain
    state — once the asteria UTxO has been consumed-and-replaced
    with a higher ship_counter, the next attempt's tx is rejected
    by the validator as expected, and reported via the existing
    asteria_player_ship_spawn_failed_<id> sdkUnreachable assertion.
  - Adds asteria_player_pass_errored_<id> and
    asteria_player_pass_completed_<id> SDK assertions so the report
    can score one pass per fire even when the inner observation
    bombs out.

Locally validated on testnets/asteria_game/ compose (3-pass run,
one per player_id):
  - bootstrap idempotence still holds (cold deploy + 2 short-circuit)
  - player 1 attempts spawn end-to-end (observe → build → sign →
    submit → reject); players 2, 3 observe-only
  - sdk.jsonl shows ship_counter and move_planned per pass

KNOWN ISSUE — submission is currently rejected by the spacetime
validator with "PlutusV3 script failed: overspending the budget"
(CekError, ~600k cpu over). This is a pre-existing issue in the
lifted PR #67 Aiken validators — the off-chain wiring works
correctly, but the on-chain validator over-spends its execution
units. Tracked as a follow-up; does not block landing this driver
since the wiring + report assertions are independent of validator
correctness.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant