Commit graph

254 commits

Author SHA1 Message Date
acamilo
acf7c2ebb8 flysim: the feed server takes a FeedState, so another process can run it
feed::router needed the whole AppState for three things: the watch slot, the
feed counters and the idle cadence. It now takes exactly those, and Shared's
Metrics sits behind an Arc so the counters can be handed over. Nothing about
what the feed writes changes.
2026-09-23 11:41:28 +00:00
acamilo
3eb82d7144 a warp's tear deals no pad
wCurMap names the new map thirty-two frames before the header, the
coordinates and the warp table follow it, while the screen fades, and
nothing sets the joypad bits until the fade ends. The seam read
"map 54 at (16, 17)" -- Pewter City's doormat under the gym's id -- as
an overworld, dealt a pad, and a walk started there planned over the
wrong map; what it aimed at and the tile it left went into the ledgers
under the new map's id. Live, GO OUT started and finished in 0.05 s.

The driver reads a tear as the map byte having changed while the fly
still stands on a warp of the loaded table that leads to the map the
byte names (a doormat's LAST_MAP under a town's id included), deals it
as Unknown with an empty pad, and records no ground from it. Teleport
pads do not change the map byte, so they are never a tear; a tear is
bounded at TEAR_FRAMES all the same.
2026-09-23 11:26:49 +00:00
acamilo
5fd16536db a battle decided is the cartridge's
Between a trainer's challenge closing and the battle screen the
transition runs 219 frames with every joypad and script bit clear, so
the scene read overworld and a pad was dealt: a walk toward the leader
pressed into the animation, gave up after three refused steps and put
him in the blocked ledger, and the trainer's conversation read as over.

wCurOpponent is set when a battle is decided and cleared by EndOfBattle
with wIsInBattle. It is not in the generated table; it is the byte
between wIsInBattle's flag byte and wBattleType, both neighbours
checked against the table, and controllable() reads it.
2026-09-23 11:26:49 +00:00
acamilo
484cc075cc the rung's people are in the room when the screen does not show them
CheckSpriteAvailability writes $ff into the image index of a sprite
outside its window, and state::npcs reports what is drawn. From the
Pewter Gym's doormat that is the guide alone, already talked to, so the
rung's list was empty: GO OBJECTIVE had nothing to aim at and GO OUT,
withheld only while the rung's person is in the room, was the pad.
Outside, GO OBJECTIVE walked back in. BROCK was twelve rows up.

state::offscreen_npcs reports the sprites the cartridge hides only for
being outside the window, read from bytes the seam already has, and
objective_targets reads them for a person. Nothing else does: a sprite
out of the window may be a toggleable object switched off, and GO NPC,
TALK and objects keep what is drawn.

Facing any of the rung's people is the arrival: with three in a gym,
leaving out only the one ahead walked GO OBJECTIVE between the leader
and the trainer.
2026-09-23 11:26:49 +00:00
acamilo
552428a7be survey: the route probe reads the room on the fly's Nth arrival on a map
FLY_PROBE_CATCH_MAP and FLY_PROBE_CATCH_ENTRIES stop the route survey
forty frames after the fly's Nth arrival on a map (the first frames on a
new map byte still carry the old map's warps), and the dump lists every
person the macros can see with its talked, blocked and reached entries.
Row 58's pad was one door in and one door out, and what it needed read
was the room on the far side of the door.
2026-09-23 11:26:49 +00:00
acamilo
174dc7eabd docs: v0.5.4 status, and the unstick rule
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-23 11:26:32 +00:00
acamilo
6d5827716f Merge fix/loop-row57: an escort walls the tile it reached, and a refusal that teaches nothing is not dealt again 2026-09-23 11:26:29 +00:00
flybrain
6c7ee5476c docs: macros.md 12.21 and the row 57 audit 2026-09-23 10:10:03 +00:00
acamilo
bbb25e1afc docs(design): legacy-gameboy-v1 16, where the rung slot sits is the whole archive difference 2026-09-23 09:33:10 +00:00
acamilo
4b6624757b Merge docs/port-contracts: PROF-02a and RT-01a, the legacy Game Boy profile and environment boundary, per the operator's 2026-09-23 decisions 2026-09-23 09:32:09 +00:00
acamilo
3c4bb30bed docs: review round 1 on the port contracts
B1: a slot save due at a boundary completes before any State.Capture or FLYSIM01 export there;
the ported milestone archive holds the post-capture ratchet and slot where legacy holds the
pre-capture ones, declared in legacy-gameboy-v1 sections 4 and 16 and step-v1 sections 3 and 6.
N1 the exact restore transient (blocked window from 0 ms, restored winner reported blocked on the
first decode); N2 the decoder-config form; N3 worker status and lost-reply resolution for the
extension methods; N4 boundary actions and captures in the step-v1 section 8 trace; N5 a warm-up
override is another profile; N6 sugar refused until the first commit after a restore.
2026-09-23 09:00:12 +00:00
acamilo
2f8ad4914a session types: shared decoderConfigDigest vectors from both decoder presets
Review round 1, N2. The gameboy-decoder-config-v1 form (channels as ordered arrays), vectors for
raw mode and the Pokemon Red macro group computed by flysim's legacy_profile_identity test from
gameboy_decoder_config_with_macros and reproduced by @flybrain/session-types from the oracle's
gameboyDecoderConfig. The example composition now carries the real macros-mode digest and
channel set.
2026-09-23 09:00:12 +00:00
acamilo
a6e1623698 session types: boundary actions and captures in the step trace, a capture never precedes a slot save
Review round 1, B1 and N4. TraceBehaviour.boundaryActions records the slot saves and the rollback
at the reached boundary in application order; TraceOperational.captures records each capture with
the number of boundary actions before it; TransitionTrace refuses a capture taken before the
boundary's slot saves. Both languages, fixtures for the rule, synthetic coordinator records both
lists empty. contractDigest moves to a56e25e6.
2026-09-23 09:00:11 +00:00
acamilo
24ba933500 docs: PROF-02a and RT-01a, the legacy Game Boy composition on the session framework
The operator decided on 2026-09-23 to port the live fly onto the session framework in full.
New contract legacy-gameboy-v1: the profile gameboy-legacy-fafb-v783-v1, the proof that the
legacy f64 frame clock equals the rational one, the step-by-step placement of step_frame in
lockstep-v1, the readout context (location allowed and declared), the channels decision, the
memory-image inspection and ROM AssetRef, the environment (one no-button setup frame, u8->f32
audio, DC blocker at the edge, gameboy-slots-v1), the pokered-macros-v1 executor as one object
with its task, the legacy-ratchet-rollback-v1 policy, the composition digest carrying decoder
and macro-channel configuration, legacy-transient-reset restore semantics, sugar admission with
a one-commit lag, and FLYSIM01 as format of record until RETIRE-01. PROF-02b is a stub.

Dated amendments, each citing the decision, where earlier text kept the legacy loop outside
lockstep or had no place for it: workers-v1 (telemetry, Initialize, executor, episode request
kind, admission, new section 7 extension methods), step-v1 (rollback edge, Phase B/C, clock,
episode policy, sugar lag), state-media-v1 (audio, memory-image retention, restore semantics,
format of record, section 7 ratchet), README section 4, implementation.md (AGENT-01 and ENV-01
unblocked), the MaleCNS backlog (FOUNDATION-02 split, RUNTIME-01 contract) and analysis (5.2,
5.4), and readout.md (where the location comes from).
2026-09-23 08:32:05 +00:00
acamilo
bbf71bfead session types: the legacy Game Boy profile and the RT-01a extension methods
PROF-02a and RT-01a, machine-readable half, per the operator's port decisions of 2026-09-23.

Generic (in the session schema set, so contractDigest moves):
- AgentTelemetry.stimulusRemainingMs (number|null): sugar admission reads the pulse from the
  last commit.
- EpisodeRequest.kind is terminal | rollback.
- Environment.SaveSlot / Environment.RestoreSlot (capability gameboy-slots-v1) and
  Agent.Rollback (capability legacy-ratchet-rollback-v1) payloads, with their scope checks;
  maxSlots 4.

Legacy Game Boy (module gameboy, digested apart from contractDigest):
- registered payload schemas gameboy-readout-context-v1, gameboy-channels-v1,
  gameboy-joypad-v1, gameboy-memory-inspection-v1, legacy-ratchet-rollback-v1, each SchemaRef
  digest over its canonical declaration;
- the one legacy profile gameboy-legacy-fafb-v783-v1 embedding today's schema-1 fingerprint,
  lif-1ms-f64-v2 and fly-kc-mbon-rstdp-v2, with its AssetRef digest;
- the composition declaration carrying the decoder and macro-channel configuration, the
  executor pokered-macros-v1, gameboy-slots-v1, legacy-ratchet-rollback-v1,
  legacy-transient-reset and FLYSIM01 as format of record, cross-checked against the FLYSIM01
  compatibility string.

Fixtures regenerated by update_fixtures (new derived gameboy-legacy.json); valid/invalid cases
for every new type in both languages; the frame clock proven identical to the legacy f64
accumulator. flysim gains only a test (and a dev-dependency) that recomputes the pinned
fingerprint, versions, frame size, warm-up, clock and button order. The synthetic fly-session
agent reports stimulusRemainingMs null and its task names kind terminal; no runtime change.
2026-09-23 08:31:58 +00:00
flybrain
73b160c2f8 clippy 2026-09-23 07:27:34 +00:00
flybrain
cc6f06ed2a a refusal that teaches the ledger something is not held against the tile
Measured with the real brain on the seeded pocket: it pressed GO ROUTE
first, while the gym's door was still the second tier's answer. That
refusal wrote the door to the blocked ledger and also withheld GO ROUTE
on the tile, so the next deal -- the last resort, whose walk now goes
where it can -- was never made, and the fly waited out the window.

Only a last resort deals goals the ledger is already resting, so the
refusal worth remembering where the fly stands is the one that taught
the ledger nothing new. A refusal that writes a new exclusion changes
the next deal by itself.
2026-09-23 07:25:19 +00:00
flybrain
ef98b04547 a last resort that cannot reach the objective's door takes a way out it can
Row 57's pocket had a way out: the road east, three tiles from the fly
and resting in the blocked window. The last resort ignores that window
but narrows to the ways toward the objective, and the only one was the
gym's door beyond the fence, so the route search refused and the road
was never tried. With the refusal withheld the pad went empty and the
fly waited out the road's window, ten brain minutes.

The narrowing is a preference and the dealer cannot search. When start's
route search cannot reach the preferred ways, it tries the rest of the
last resort, nearest reachable first, exactly as every walk chooses. The
pad is unchanged; only where the pressed macro walks.

The ROM proof now asserts the fly leaves the pocket inside a brain
minute: frame 517 (0.14 minutes), against 36,325 (10.1) on the base.
2026-09-23 07:00:07 +00:00
flybrain
2d04abaf52 survey: the hunt can start inside a live session's ledgers, and counts refusals
The seeding moves from scene_probe into examples/support/ledgers.rs and
trap_hunt reads it as FLY_TRAP_SEED_*, so both arms of a hunt can start
inside the state the live session was in: row 57's pad was dealt by
ledgers a restore starts empty, and a hunt from the bare checkpoint
never meets it. The report gains refusals by macro and the longest run
of one macro refused with the fly on one tile.

scene_probe's own choice seed is FLY_PROBE_RNG now, so it does not read
as one of the FLY_PROBE_SEED_* ledgers.
2026-09-23 06:23:13 +00:00
flybrain
b8a5c8fc52 watchdog: check 10 counts outcomes, not only starts
Row 57's pad was one button refused every hold for two hours: one start
and one name in ten brain minutes, so neither the sequence rule nor the
dominance rule could fire, and the coordinator's own watcher saw it
first. Check 10 now reads every macro outcome in the window. The fly's
decisions are its starts and its refusals; `stalled` flags 90% of 20+
decisions ending refused, blocked or timed out, and `zero-progress`
flags decisions with no `done` among them on two probes in a row. Both
sit behind the same no-new-ground gate as the old rules.

fly_loop_refused, fly_loop_blocked and fly_loop_done are exported, and
loop.json carries window.decisions and window.outcomes. It still never
acts: the lint fixture asserts no unit is restarted across six cases,
two of them new (the row-57 log, and a zero-progress log).
2026-09-23 05:48:01 +00:00
flybrain
6f05b96334 tests: the ROM proof from the Pewter checkpoint
the_pewter_east_pad_is_never_one_dead_button_from_the_rung_ten_checkpoint,
gated on FLY_ROM and FLY_PEWTER_EAST_CHECKPOINT. Part one, from the
ratchet's rollback snapshot: GO ROUTE walks east, meets the youngster,
and the pushed ledger walls a tile he fires on, not the south entrance
the walk set out from. Part two, from the live frame with the pocket's
session ledgers rebuilt: twelve brain minutes, no button refused twice
running on one tile, and the fly leaves the pocket. On the base the same
run refuses GO ROUTE 746 holds running on one tile.
2026-09-23 05:47:43 +00:00
flybrain
acbc655a23 a button refused from here is not dealt again from here
Row 57, live on rung 10 for two hours: the Pewter City pad was GO ROUTE
and nothing else, refused `no route` every 800 brain ms, no button
pressed. The dealer asks the cheap question and start asks the real
one; the blocked ledger closes the gap for every list except a last
resort, which ignores that ledger by design. So GO ROUTE's refusal,
which wrote the gym door to the ledger, could not take the button off
the pad, and re-stamped the door's window every hold, which kept GO
OBJECTIVE's only goal excluded for ever.

A `no route` or `precondition` refusal is now recorded with the tile
the fly stood on, and the dealer does not deal that button from that
tile for the blocked window. It is dealt again the moment the fly
stands anywhere else or the window closes. Nothing presses; a pad with
nothing runnable is empty and the fly waits.
2026-09-23 05:47:38 +00:00
flybrain
ab3d4cb6fb an escorted walk walls the tile it reached, not the one it set out from
Row 57. The pushed ledger (row 37, no window) recorded the tile a macro
set out from when the cartridge moved the fly. For a TALK that is the
tile the script fired on; for a walk it can be the far side of the map.
Pewter City's youngster takes the joypad on four tiles by the road east,
GO ROUTE aims east at Route 3 every time, and one walk from the town's
south entrance walled the south entrance, twenty-six tiles from the
script. Walks start wherever the last one ended, so the walls fenced the
fly into a pocket no route could leave.

A walk now records the tile it last stood the fly on, which is where the
cartridge took over. Every other macro keeps the tile it started on.
2026-09-23 05:47:32 +00:00
flybrain
c9212e5498 survey: the route probe, and a seam to rebuild a session's ledgers
FLY_PROBE_CATCH=route drives the real PokemonPalette from a checkpoint,
one uniform choice per hold, prints every pad it deals and every refusal
with its reason, and reads the frame the pad comes down to one refusing
button on: which list emptied why, the no-window ledgers, and whether the
route search reaches each way out. FLY_PROBE_SEED_* rebuild the session
ledgers a restore starts empty; FLY_PROBE_RATCHET starts from the
ratchet's rollback snapshot; FLY_PROBE_HOLD presses raw directions first.

PokemonPalette gains inspect(), fences() and a doc(hidden) ledgers_mut()
for the survey. The loop never calls them.
2026-09-23 05:46:56 +00:00
acamilo
d5d9249ea9 docs: v0.5.3 status
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-23 01:37:28 +00:00
acamilo
b903e738d5 Merge fix/loop-row56: a two-option box is found around the cursor the game parked in it 2026-09-23 01:37:26 +00:00
flybrain
9f47e7fd9c tests: the ROM proof from the gym checkpoint, and answers counted per person
Row 41 counted `YES` starts per map, which cannot tell one conversation from
another; the Pewter Gym stall was 64 `YES` and 62 `NO` in ten brain minutes at
one person. So the harness counts an answer to a box it can read as a choice,
charged to whatever the fly is facing -- `palette::facing_target`, which is
`TALK`'s own precondition and the same reading its ledger entry uses.

`the_fly_leaves_the_pewter_gym_guides_ring_from_the_rung_ten_checkpoint`: the
fly leaves map 54 on frame 1,337 against never in twenty brain minutes, spends
27 macros in the gym (one lap of the ring), answers the guide's prompt **once**
against a bound of four, re-opens the ring **never**, and deals `NEXT` on no pad
while a readable prompt is open.

The rung is printed and not asserted: neither this run nor either arm of the
trap hunt reaches rung 11 from this checkpoint, and why the fly does not walk
back through the gym door is row 12.5's ground and row 54's rather than this
row's. Named in the residuals.
2026-09-23 01:22:01 +00:00
flybrain
a22edacbc8 docs: macros.md 12.20, macros-wram.md section 11, the row 56 audit
The survey whole, both arms of the trap hunt, the ROM run, and the residuals --
including the two this row does not work: neither arm reaches rung 11 from this
checkpoint, and the watchdog's check 10 cannot see a loop of exactly four
distinct macros.
2026-09-23 01:17:33 +00:00
flybrain
886509b3b5 a NO inside a conversation declines nothing, so it does not un-arm the talk
The other half of row 56. The Pewter Gym guide's conversation is fifty-two boxes
long and a third of the presses that walk it are `NO`, whose B advances a plain
box exactly as `NEXT`'s A does. Every one of them cleared the pending `TALK`, so
the talked ledger never learned the conversation had happened, `TALK` stayed on
the overworld pad, and an A press at him reopened the whole ring: thirty brain
minutes of scene `dialog` with no walk macro dealt.

12.4's rule -- "the fly said no, so the thing is still on offer" -- is about a
declined *offer*, and which of the two a `NO` was is decided where it can be
seen: by whether the box closes on it. So the decision moves to the frame the
text goes away, which is where the talked entry is written anyway, and the
reading is `pending_answer`: armed only by an answer to a prompt this crate can
read, alive for one hold. A declining `NO` still standing there is a `NO` the
box closed on; anything else is a conversation walked through to its end.
2026-09-23 00:37:44 +00:00
flybrain
5f9237efcd a two-option box is the one the cartridge drew, not the one row 41 pinned
Row 41 read the border at (11, 6)-(19, 11) because that is where the Pokemon
Center's script puts it, and named the limit in its own residual: "Red places a
two-option menu where the script asking for it says, so a prompt drawn elsewhere
reads false and keeps the pad it had". The Pewter Gym guide draws the same menu
at (14, 7)-(19, 11) with the cursor at column 15. Surveyed over 260 presses from
the live checkpoint: the box is drawn on 10 frames, `yes_no_prompt` answered
false on all 260, and `wTextBoxID` read `TWO_OPTION_MENU` on exactly the 10.

So the pad was `NEXT, YES, NO` on a box that was a choice -- two channels for
one A press, which is 12.10's forbidden pair -- and the reopened-prompt
exclusion never armed, because it only judges an answer to a prompt this crate
can read.

The screen half is now the border drawn **around the cursor the game parked in
it**. One fact about `DisplayTwoOptionMenu` rather than about any script: the
cursor goes in the box's first interior column, so the left edge is one column
to its left, in both boxes surveyed. The top is not fixed -- the nurse's box
begins two rows above the first item and the guide's one -- so the top is found
and the figure is then read whole, as `waiting` and the move list are.
2026-09-23 00:37:44 +00:00
flybrain
a6a8f797ff survey: the conversation probe, and every rectangle the cartridge drew
Row 56 is a gym's dialog for thirty brain minutes with YES, NO and NEXT dealt
in equal thirds, so the question is which box each press is answering and
whether the seam can see it at all. `state::drawn_boxes` asks the screen
instead of a pinned rectangle: every complete `TextBoxBorder` on the frame.
`FLY_PROBE_CATCH=dialog` walks the conversation one raw pulse at a time and
prints both readings of every frame side by side, with the pad the palette
deals for it, and tallies how the candidate readings separate the frames.

`FLY_PROBE_ANSWER` picks the button a frame with a box on it is answered with,
so the two arms of a choice can be walked separately.
2026-09-23 00:30:35 +00:00
acamilo
2de2dced0a tests: the service integration test expects the v6 adapter
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-22 23:00:14 +00:00
acamilo
cfb3c1c506 docs: v0.5.2 status
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-22 22:49:33 +00:00
acamilo
c24cbe0171 Merge fix/loop-row55: the shop pad is read from the drawn screen and offers only what can run 2026-09-22 22:49:30 +00:00
acamilo
6342adba39 docs: row 55 gates, as run on the merged tree 2026-09-22 22:48:11 +00:00
acamilo
e5fe6c46ac Merge main into fix/loop-row55: row 50's battle seam beside row 55's counter
Three conflicts, all of them two reviews appending in the same place, all
resolved by keeping both sides: `macros.md` (row 50 keeps section 12.18, row 55
becomes 12.19), `macros-traps.md` (both trap rows, both residual pairs, both arm
sections) and `scene_probe.rs` (both survey modes, `accept` and `shop`). Every
source file auto-merged.

The two rows compose on the cartridge: from the mart checkpoint the fly leaves
the counter, walks the town, and no `MOVE n` blocks any more, so the longest
chain of one macro blocked on an unchanged frame is 1 in any scene.
2026-09-22 22:38:10 +00:00
acamilo
549df6c323 docs: row 55, and the two claims about a mart the survey narrowed
`macros.md` section 12.18 is the review; `macros-wram.md` section 7.1 corrects
the address table, where `wListMenuID` was said to be cleared by every text
display and an item's position in the stock was said to be its cursor index;
`macros-traps.md` carries row 55, its two arms and its ROM run, and two new
residuals -- `wListScrollOffset` is not a pinned address, and a mart that has
never drawn a buy list reads `Unknown` rather than `Shop`.
2026-09-22 22:29:07 +00:00
acamilo
01c199fe18 probe: clippy's three readings of the counter survey 2026-09-22 22:29:07 +00:00
acamilo
351338038a docs: v0.5.1 status
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-22 22:13:09 +00:00
acamilo
349a1fd28c Merge fix/loop-row50: a battle turn is the fly's only while the move list is drawn 2026-09-22 22:13:08 +00:00
dev
e307b7dde4 docs: row 50's survey, its two trap hunts and its ROM run
The press survey, twenty brain minutes before and after on both checkpoints,
and the ROM-gated forest run.

The ethos check's 'fewer flagged windows, more distinct tiles' holds on the
tiles on both arms (296 to 430, 163 to 184) and does NOT hold on the windows
(33/73 to 70/73, 68/73 to 73/73). After the fix the fly spends three quarters
of each run inside battles it is actually fighting, and the hunt's tile rule
flags a fighting fly exactly as hard as a stuck one. Reported rather than
smoothed.
2026-09-22 22:11:01 +00:00
acamilo
01494a104e docs: the session framework slices that landed, and the two that are blocked
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-22 21:11:55 +00:00
acamilo
954b9f4db5 Merge fix/sf-resolution-bound-flake: a short acknowledgement is the contract, and two timing bets become claims 2026-09-22 21:11:49 +00:00
acamilo
b2afce1fce Merge feat/sf-publish-01: the publication boundary, committed snapshots and observer isolation over the same bus 2026-09-22 21:04:40 +00:00
dev
5e61c50728 session: an unreadable event batch is not the end of the stream
take_events kept returning Option<EventBatchView> and defaulting through the
question-mark operator, so a batch missing a field read as end of stream and the
ConsumerEvents enum added in the previous round described nothing. It returns
Batch or Unreadable now, and a test publishes a batch with no droppedBefore,
asserts it is reported as unreadable naming the field, and asserts the next real
batch still reads.

The checkpoint-envelope-v1 amendment cites the rule that lets a required
manifest field land with envelopeVersion still 1 while no production file
exists.
2026-09-22 21:03:59 +00:00
acamilo
50ab3d47ba session: hold an Acknowledge to the request it answered
Dropping the length check left nothing checking the reply against the
request at all: AcknowledgeResult::validate_against existed with no
caller, so a worker could acknowledge ids this session never asked
about. That is the other half of the rule the README states. A short
list is the worker reporting what it released and is accepted; an id
from outside the request is the worker reporting about someone else's
cache and is refused, named, before any mutation.

The bootstrap-path regression is now covered too. The earlier test calls
acknowledge_replies directly, which guards the check where it lives but
not where it lived, so a length check put back into acknowledge_lifecycle
left it green. The duplicate_lifecycle_acknowledge injection releases the
ids first, out of sight, so the call that method makes and checks is
already the second one -- the shape the section 6 resolution produces.
Verified by putting the old check back: three tests fail with it, none
without.

Also: last_resolution_attempts is cleared with last_resolution, so a
resolution ending before its first attempt no longer reports the
previous count; the guard half of the bound test asserts the fence like
the budget half; the attempts assertion checks a real bound rather than
u32::MAX; and two dead Instant bindings are gone.
2026-09-22 21:03:09 +00:00
dev
305a50d8df session: a graph identity does not cross a recovery
The index an agent attested to at Agent.Initialize joins its compatibility
identity and its checkpoint manifest row, so a replacement fly that built
another graph -- the same dataset, the same neuron count, another index --
cannot install a checkpoint taken under the first one. It was accepted before,
because agent_compatibility digested a dataset digest recomputed from a free
function rather than what the worker attested to, and the restored composition
was then published under its predecessor's indexDigest.

The refusal names what the worker is, not just that two digests differ. Dated
amendment to checkpoint-envelope-v1's agents row; no wire type and no schema
text change, so the contract digest is unchanged.
2026-09-22 20:36:51 +00:00
dev
6bf5687aca session: a snapshot publishes the telemetry of the transition that just ended
AgentSlot.telemetry was written only by the Agent.Initialize handler, so every
CommittedSnapshot carried warm-up telemetry labelled as boundary k while each
AgentCommitResult.telemetry was validated and dropped. The commit result is now
stored on the slot beside the committed step, and the media test asserts that
published telemetry advances across boundaries instead of merely being nonzero.

A refused snapshot is recorded and sequenced like a refused descriptor revision,
because the repair path exists for the consumer that did not receive it; the
sequence advances with the value rather than with the delivery, so two snapshots
can never share one. The query service counts an answer it could not deliver
rather than discarding the result, and an unreadable event batch is distinct
from the end of the stream.
2026-09-22 20:18:49 +00:00
acamilo
0cec73704c docs: v0.5.0 deployed, the run restarted from rung 8
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-22 20:00:43 +00:00
acamilo
270d4cb435 tests: the fly leaves the mart counter, and a blocked chain is measured
From the checkpoint the stream looped in: the fly is out of the shop scene and
off the map inside twenty brain minutes, no macro reports `blocked` more than
three times in a row in the shop with the map, the tile, the wallet and the scene
unchanged, and the shop never deals an empty pad.

The chain is recorded per scene as well as overall, because the battle scene has
one of its own and it belongs to another review.
2026-09-22 19:56:38 +00:00