Skip to content

Deliver the scene format and fixture scenes (1.2) - #9

Draft
leynos wants to merge 3 commits into
mainfrom
1-2-deliver-the-scene-format-and-fixture-scenes
Draft

Deliver the scene format and fixture scenes (1.2)#9
leynos wants to merge 3 commits into
mainfrom
1-2-deliver-the-scene-format-and-fixture-scenes

Conversation

@leynos

@leynos leynos commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

This branch carries the pre-implementation execution plan for roadmap step
1.2, which fills the currently empty thysalion-world crate with the voxel type
registry, the scene document model, load-time validation, and the fixture scenes
every later spike consumes. No production code lands here: the branch is one
document, and its purpose is to fix the format's hard-to-reverse decisions before
anyone writes against them.

Roadmap task: (1.2) — docs/roadmap.md

Execplan: docs/execplans/1-2-deliver-the-scene-format-and-fixture-scenes.md

The plan authorizes four commits' worth of future work — one per roadmap task
plus a documentation stage — and it resolves the open question
ADR 005 forwarded to this step:
the scene format stays inside thysalion-world, with bevy behind an optional
feature as the mechanism that keeps the format engine-free. A new ADR 006 will
capture that and the format's other structural choices when the work lands.

The plan went through two adversarial design reviews before any code was
written, the second of which verified its serde, Clippy, and schemars claims
empirically against the pinned toolchain rather than from memory. It changed
substantially both times.

Review walkthrough

Reviewers with limited time should read the two revision notes, then the
decision log.

  • Start with Purpose / big picture
    for what a contributor can do afterwards and how to observe it. It separates
    the two compatibility promises — Rust-source and wire — that the first draft
    conflated.
  • Then the first
    and second
    revision notes, which are the shortest route to what review overturned and to
    the two claims the plan corrected about its own earlier reasoning.
  • Then the decision log,
    which carries the positions worth arguing about, including two reversals of
    earlier decisions in the same document.
  • Then Risks,
    which now reaches past this step: format evolution against phase 8's content
    hashes, the mutability question against step 2.1.3, semantic validity as
    distinct from structural validity, and a palette reorder silently
    reinterpreting every stored index.
  • Then Constraints,
    where the canonical-encoding rules, the trust boundary, and the clippy.toml
    complexity thresholds live. Those are the parts that cannot be relaxed later
    without invalidating saves.
  • Then Stage B
    for the behavioural specification and the fully worked minimal scene document,
    which is the reference every later section assumes.
  • Finish with Interfaces and dependencies,
    the contract the implementer works from, and
    Signposts,
    which lists the documentation and agent skills to load, in order.

Validation

Documentation only, so the Markdown gates apply.

$ make markdownlint
markdownlint-cli2 '**/*.md'
Finding: **/*.md !**/.venv/** !.node_modules/** !**/node_modules/** !**/target/**
Linting: 21 file(s)
Summary: 0 error(s)
make spelling
uv run scripts/generate_typos_config.py
find . -type f -name '*.md' -not -path './target/*' -print0 | \
	xargs -0 uv tool run typos@1.48.0 --config typos.toml --force-exclude

The plan itself is not executable. The gates it commits future work to are in
its Validation and acceptance section.

Notes

Three departures from the design document, all amended back into
thysalion-design.md in the plan's documentation stage, and each worth a
reviewer's disagreement:

  • §7.3's "dense Z-major layers, run-length encoded" becomes a chunk-keyed
    payload with uniform-chunk elision.
    A single global run stream fragments on
    every raster row of a spatially localized fixture — roughly 165,000 runs and
    2 MB of JSON for a populated fragment inside the wilderness extent — and a
    one-building edit rewrites the whole stream.
  • §7.2's slope_dir: IVec2 becomes a closed SlopeDirection enum, and its
    passable: [bool; 6] becomes a six-field struct.
  • §7.3 credits lille's map format as proven prior art, but that format was
    never implemented in lille
    — it ships a Tiled pipeline instead. The plan
    names the three lessons actually taken and improves on each.

The plan also pins a fixed-point scale (Q8.8) that design §10.5 leaves
unspecified, having mandated a width and no scale; §10.5 is amended to match.

The behavioural suite does not reuse the existing Bevy rstest-bdd adapter,
because thysalion-world has no Bevy dependency and ADR 005 stages one in
later. A structurally identical adapter over a plain loader context is added
instead, to be merged with the Bevy one at step 1.3.1 — the promotion point the
developers' guide already names.

Two open questions are deliberately left for ADR 006 rather than settled here:
whether chunk_size is a real degree of freedom or should be a schema constant,
and whether the decoded VoxelGrid is a transient buffer that step 2.1.1 copies
into bevy_voxel_world's storage and discards.

Of the six review lenses, scaling and long-term viability were completed by the
author rather than by an independent reviewer after those reviewers were
interrupted. Their conclusions are folded into Risks, Tolerances, the
module-size budgets, and the test-proportionality note, but they have had less
scrutiny than the rest.

Draft the execution plan for roadmap step 1.2, which fills the empty
`thysalion-world` crate with the voxel type registry, the scene document
model, load-time validation, and three fixture scenes.

The plan resolves the open question ADR 005 forwarded to this step: the
scene format stays inside `thysalion-world` rather than becoming a leaf
`thysalion-scene` crate, with a recorded reversal trigger. A new ADR 006
will record that and the format's other hard-to-reverse choices.

Substantive positions taken, each with its rationale in the decision log:

- The document is all-integer (centi-degrees, millimetres, fixed-point),
  so JSON/MessagePack parity holds by construction and the world crate
  keeps the float discipline ADR 005 requires of it.
- The voxel payload is keyed by chunk with uniform-chunk elision, rather
  than one global Z-major run stream. A global stream fragments on every
  raster row of a localized fixture; chunk-keying makes encoding,
  decoding, hashing, and diffing scale with populated volume instead of
  declared extent.
- MessagePack is written with `to_vec_named`, pinned by a wire-shape
  assertion rather than by a round-trip test alone.
- Loading is two-phase: fail-fast deserialization located by
  `serde_path_to_error`, then accumulate-everything validation reported
  through `miette`'s related-diagnostic collection.
- `slope_dir` becomes a closed `SlopeDirection` enum, and palette index
  zero is reserved for air.

Research recorded in Surprises: design section 7.3 credits lille's map
format as proven prior art, but that format was never implemented in
lille. The design document is corrected as part of the plan, and the
three lessons actually taken from it are named.

The plan signposts the documentation and agent skills a reader needs, and
passes `make markdownlint`.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f79d4cae-b6ae-4e82-a27d-e76212abbb39

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 1-2-deliver-the-scene-format-and-fixture-scenes

Comment @coderabbitai help to get the list of available commands.

codescene-access[bot]

This comment was marked as outdated.

@sourcery-ai

sourcery-ai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a comprehensive execution plan document for roadmap step 1.2 that defines the scene format, voxel/world data model, validation strategy, fixtures, and implementation stages for the thysalion-world crate, without introducing any production Rust code.

Sequence diagram for two-phase scene loading and validation

sequenceDiagram
    actor Operator
    participant SceneLoader
    participant SceneSource

    Operator->>SceneLoader: load
    SceneLoader->>SceneSource: read
    SceneSource-->>SceneLoader: bytes
    SceneLoader->>SceneSource: has
    SceneSource-->>SceneLoader: bool

    SceneLoader-->>Operator: Scene
    alt validation_failed
        SceneLoader-->>Operator: SceneLoadError
    end
Loading

File-Level Changes

Change Details Files
Introduce a detailed execplan for roadmap step 1.2 that specifies the scene document model, validation rules, fixture scenes, and phased implementation/acceptance criteria for the thysalion-world crate.
  • Define constraints, tolerances, risks, progress milestones, and acceptance criteria for implementing the scene format and loader in thysalion-world
  • Specify the voxel scene document structure (palette, chunked voxel storage, entities, lighting, knowledge) including all-integer fields, chunk-keyed RLE encoding, and reserved air palette index
  • Describe load-time validation approach with two-phase deserialize/validate pipeline, miette/thiserror-based diagnostics, and exhaustive error reporting semantics
  • Lay out behavioural specs and test strategy using rstest, rstest-bdd, proptest, insta, trybuild, and Python-based fixture generators for three canonical scenes
  • Resolve architectural decisions such as keeping the scene format inside thysalion-world, avoiding Bevy/knowledge dependencies, and using SceneSource ports and capability-based IO
docs/execplans/1-2-deliver-the-scene-format-and-fixture-scenes.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Stress-test the scene-format plan through structural, alternatives,
contracts, and failure-mode reviews before any code exists, and fold the
confirmed findings back into the plan. Six changes are load-bearing.

`VoxelRun` becomes a named struct. `rmp_serde::serialize_tuple_struct`
writes a positional array unconditionally, ignoring the struct-map
configuration, so the plan's headline mitigation for positional encoding
never reached its most numerous wire type. Spike A2 must now measure the
named form against the 1 MiB tolerance.

The document gains a compatibility policy. The draft cited
`#[non_exhaustive]` as enforcing wire additivity, which it cannot do:
it is a Rust-source guarantee with no bearing on the encoding, and it is
inert inside the defining crate. Source and wire compatibility are now
separated, the version rule is a major/minor pair with an accepted
range, the serde attribute policy is a constraint, and checked-in golden
bytes are the enforcing test.

Content hashing is designed in. Design section 12.3 requires save
archives to hash scene assets, and the naive fix breaks on this step's
own two-encoding premise: a scene's JSON and MessagePack forms hash
differently. The hash is defined over the canonical MessagePack encoding
of the validated document, which four canonicality rules make well
defined.

`grid/` becomes a peer of `scene/`. The runtime voxel structure is
state-plane data, and four later phases would otherwise import it
through a path announcing itself as the file format.

The document-to-domain split becomes real, and `SceneSource` loses its
`has` method. A derived `Deserialize` on `Palette` was a public
constructor reaching every invariant the plan declares enforced, and a
boolean existence check cannot distinguish an absent file from an
unreadable one.

Validation becomes phased and bounded, with a stated trust boundary,
enforced resource caps, and iterative prototype resolution. "Report
every problem" is not implementable against untrusted input.

Two decisions are reversed and two rationales corrected. `miette` is
dropped: its spans would point at generated JSON rather than at the
authoring layer file, and it would couple every snapshot to an upstream
renderer version. The all-integer document is justified by `Eq` across
the document tree, the NaN gap in JSON, and design section 10.5 — not by
a JSON/MessagePack float divergence, which does not exist.
codescene-access[bot]

This comment was marked as outdated.

A second adversarial review verified the plan's serde, Clippy, schemars,
and miette claims against the pinned toolchain rather than from memory.
Four defects had survived the first revision.

The worked minimal JSON did not deserialize. `ChunkPayload` is an
externally-tagged enum, and the example wrote a run array directly beside
`at` while never declaring the entry type joining them. The payload is
now a newtype-variant enum behind an explicit `payload` field,
`ChunkEntryDocument` is declared, and the uniform case is shown.

`clippy.toml` had never been read. It sets a cognitive-complexity
threshold of 9 against a denied lint, 70 lines per function, four
arguments, and four levels of nesting — and the plan mandated a fused
per-chunk traversal checking four properties, which breaches the
complexity ceiling by construction. The traversal becomes one pass
feeding several single-purpose accumulators.

The operator tool sat on an unresolvable trilemma: `make lint` lints
examples, `clippy::print_stdout` is denied, and the plan forbade new
suppressions. Writing through `writeln!` on a `Stdout` handle resolves it
without a suppression, and the coverage half is resolved by amending the
plan's own promise with one ignore entry.

Two writers existed for one format and only one was tested. The staleness
check compared Python output against Python output, while Rust's
serializer re-emits defaults the generator would omit — different bytes
for the same scene from the first commit, invisible to a canonical-bytes
test. Fixtures are now committed fully explicit.

Also folded in: a palette-reorder hazard guarded by a committed palette
manifest; the layered-text authoring format specified with a worked
example, having previously been named but not defined; the fixed-point
scale for `SimProperties` pinned at Q8.8, since design section 10.5
mandates a width and no scale; a scope tolerance that would otherwise
fire on Stage C3 by construction; reference data moved ahead of the rules
that validate against it; `Scenario Outline` in place of a `Scenario`
carrying an `Examples:` table, which would not parse; port path-safety
rules; and `hyperfine` pointed at a built binary rather than `cargo run`.

One severity claim is corrected. The previous revision note called the
tuple-struct `VoxelRun` a defect; it is not, encoding identically under
both `to_vec` and `to_vec_named`. The objection is purely about
evolution, and the named struct stands on that ground alone.
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