# @staratlas/dev-sage

## Fleet settlement with career XP

`TheOneFleetStateHandler` and `StopSubwarp` both settle subwarp Pilot XP through
the same Character-level budget. Use the generic handler to advance state (it
continues an unfinished subwarp); use `StopSubwarp` to stop at the settled
position. Once a trip has arrived, either resolves it to Idle without a second
award. Warp XP remains paid at departure, not arrival.

On XP-v1 Games (`Game.version == 2`), supply the controlling `character` and:

- `xpBudget`: writable `CareerXpBudget` PDA, seeds
  `["CareerXpBudget", game, character]`.
- `xpBudgetConfig`: readonly `XpBudgetConfig` PDA, seeds
  `["XpBudgetConfig", game]`.

The generic handler's account order is `funder, fleet, game, system?, asteroid?,
character?, systemProgram?, xpBudget?, xpBudgetConfig?`. The first six accounts
and optional trailing rent System Program preserve existing legacy callers.
Keep program-ID placeholders for absent optionals before a supplied account.
`StopSubwarp`'s account list is unchanged. An updated binding exposes the new
handler tail; older bindings must explicitly append System Program (or a
program-ID placeholder), then the two XP accounts. This requires the updated
program to be deployed; a binding update alone cannot fix the old handler.

Legacy Games (`version == 0`) must omit XP accounts. Sealed Games (`version == 1`)
still reject XP-writing settlement. On XP-v1, omitting the budget/config fails
closed; an exhausted budget permits movement to settle with zero XP, never a
legacy fallback. The generic handler also forwards the same XP context when
resolving mining; existing asteroid/Character requirements still apply.

## Bundled Game decoder correction

This release line includes the upstream prefixed-array decoder allocation fix
through a publisher-side patch to the locked codec dependency. The rebuilt ESM
and CommonJS artifacts execute the correction; consumers need no local patch,
Buffer workaround or Kit 8 migration. The declared Kit peer range remains
**`^6.1.0`**, and isolated packed-package checks install **Kit 6.10.0**.

Main is the **0.53 account/instruction contract**, not a drop-in 0.52 backport.
The separate 0.52.1 compatibility release preserves the published 0.52 source/IDL.
Do not select a binding solely by this performance correction: match the deployed
program's account/instruction contract. No release is published by opening a PR.

From `clients`, run `pnpm install --frozen-lockfile`, `pnpm build`,
`pnpm typecheck`, `pnpm lint`, and `pnpm test`. Tests include an isolated packed
SDK installation, both public conditional exports, full decoded-value digests,
truncated-input parity and a copied-byte allocation budget. `prepublishOnly`
also runs the packed check. These are Node checks, not browser memory claims or
an exhaustive malformed-input proof; consumer/browser integration remains a
separate validation boundary.

## Publisher-owned ship-model catalog, schema v1

The opt-in JSON subpath is:

```text
@staratlas/dev-sage/ship-models/zink-ptr-15615.json
```

It is **not imported by the main SDK entry point** and contains no duplicated
ship stats. Consumers may load it lazily and index references to their existing
immutable definitions. No additional RPC request is performed by this artifact.

This is an explicit, publisher-owned membership/default mapping for one validated
catalog, **not a universal hundred-ID allocation rule or a `latest` alias**.
Its reference capture is pre-reset; its presence in a new SDK does not establish
that it matches a current deployment.

### Identity and validity

- `scope` identifies the chain genesis hash, program ID and Game address. All
  three must match the consumer's deployment context.
- `modelId` is the publisher's model ID (`ShipMintId` in authoring), not the
  configuration ID called `ShipId` in the on-chain schema. `modelKey` is the
  canonical publisher registry key; `displayName` is cosmetic.
- `configurationIds` explicitly identifies active members. A member's live
  `Ship.mint` must equal its model's deployment-specific `mint`.
- `defaultConfigurationId` is an explicit member ID, or `null` if no default is
  specified. Never infer it from order, minimum ID, name or ID arithmetic.
- `reference` records the capture slot, ship-definition sequence, account hash
  and publisher/program source revisions. The sequence is provenance, **not an
  assertion that this is the current live Game**.
- `identityDigest` is SHA-256 of UTF-8 `JSON.stringify` of every active
  `[configurationId, mint]` tuple, sorted ascending by numeric configuration ID,
  with no whitespace. It covers the complete active catalog, not `mintToId`
  history. Reject duplicate active IDs before hashing.

Before enabling grouped lookup, require schema version 1, matching scope and
identity digest, unique model IDs/keys, exactly one membership for every active
configuration, matching live mints, and defaults belonging to their model.
Unknown schema versions, missing/new/reassigned configurations or incompatible
scope must make this mapping unavailable. Individual config lookup, full listing
and Fleet decoding must remain available independently.

Rebuild the lazy index when the Game/definitions identity or sequence changes,
and when the catalog revision changes. A stats-only change may retain a valid
identity digest, but references in the rebuilt index must point to the new
definition objects. A reset requires revalidation even if the Game address is
unchanged. Checking only a remembered sequence number is insufficient.

### Publisher updates

The publisher registry policy preserves existing model/config IDs and never
reuses retired IDs. Additions get explicit registry assignments, **not** IDs
computed by consumers. New/removed configurations, model mint/default changes or
membership changes require a new catalog revision; publish a new scoped JSON
artifact rather than silently changing an already published catalog ID. Removed
configurations disappear from active membership but remain reserved in the
authoring registry. Do not repurpose an existing model key for another model.

The program's admin APIs are not a permanence guarantee: removal exists, mint
history is retained, and a deleted ID is not protected by a permanent tombstone.
Existing configuration updates reject changed mints. Consumers must still apply
the catalog validity checks instead of assuming every deployment/upgraded
program enforces the publisher policy.

### Provenance and maintenance

The initial catalog joins publisher `id_registries/ship.json` and
`id_registries/ship_config.json` with `_c4_ship_meta`/`c4_config_id` authoring
records, the publisher's generated `default_config_ids.json`, and the captured
Game's active mints. It was cross-checked against the authored zink configuration.
All 61 models, 2,928 members and 61 defaults agree. Source revisions are pinned
in the JSON. No name matching or ID arithmetic was used for this join.

For a new catalog, use those explicit publisher records, resolve the target
deployment mints, record a new scope/reference/digest, and validate complete
active membership before adding its package export. `pnpm test:ship-models`
verifies the committed catalog against the public Game fixture and exercises
incompatible deployment, membership, default, schema and mint controls. The
publisher-only verifier is not an exported Node-dependent runtime SDK helper.
