docs: repair the code block that swallowed half of How-to Load an Anvil world
The sed range that lifted the bootstrap example out of Anvil-Chunk-Loader
stopped at `instance.setChunkLoader(loader);` — three lines and the closing
fence short. An unterminated fence swallows everything after it, so the page
rendered as one code block from step 1 to the bottom.
The same range cut the constructor signature table, which left
"The relevant Falco signatures are:" pointing at nothing.
Both restored verbatim from 78cde20. Checked every other page for the same
damage: this was the only unbalanced fence, and no other cut landed inside a
code block.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
04f9ace
docs: restructure the wiki along Diátaxis
Seven pages carried all four modes at once. Anvil-Chunk-Loader opened with
status, ran through usage, architecture, a comparison, measured tables and
error handling in one file; Light-Engine, Benchmarking, Installation,
Instances-And-Chunks and World-Migration had the same shape. Every paragraph
was correct and no reader was ever in all four states at once.
The four quadrants are now separate pages, encoded in the title because that
is the only structure a wiki preserves: 1 tutorial, 10 how-tos, 9 reference
pages, 15 explanation pages. Project-Status and the Research pages stay
outside the four on purpose — they are the operational record, not
documentation of the software — and so does the build documentation.
Three things the split fixed rather than moved:
- There was no tutorial anywhere. Tutorial-Load-your-first-world-with-Falco
is the one guided path: empty Gradle project to a served world with light,
one path, no alternatives.
- Reference existed only as prose embedded in explanation. The nine pages now
carry generator banners naming the source in the code repository, so the
next step is a generator and a CI drift gate rather than proofreading.
Reference-Exceptions-and-faults says outright that it is incomplete: the
two Reason enums declare eleven constants and the wiki ever recorded two.
- The measured tables were published twice. The four charts lived only in
Benchmarking, their tables only in Measured-Results, and _Footer.md carried
a rule about which source wins — the symptom, not the cure. The charts now
sit with their tables on Reference-Measured-results and the duplicate block
is gone; every figure in it was verified present first. Same for the
reproduction command lines, which stood in two pages.
Prose was moved, not rewritten. What is not carried over is the intros and
tables of contents of the dissolved pages, and the duplicated headline
block. All 195 cross-page links, their anchors and their link texts were
rewritten to the new titles; nothing dangles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
c2f1397
docs: the migration module, and the counts that had drifted
falco-migration arrived with #48 and #50 and had no page at all — it did not
appear anywhere in this wiki. World Migration is that page: the three modes,
the backup that cannot be switched off, why migration runs before the version
guard, and where the rules' version numbers come from.
Project Status: item 1 of Open resolved by #49, items 2 and 3 renumbered. The
entry is kept in full down to "What was built", the way the #34 entry is,
because its reproduction is what made the defect actionable — and because the
first attempt at fixing it answered too broadly and broke two tests that
assert versionPolicy(null) means the check does not run. That is recorded
rather than tidied away.
The counts in that page had drifted further than the migration module alone.
"What is in the branch" said 14 types for falco-anvil where the tree has 26,
3 for falco-instance where it has 16, and carried no row for falco-migration
or falco-archunit; "Where things live" listed types that predate several
merged pull requests. Every column is now re-derived at cbd74ccb from one
tree and one test run, which the previous table could not claim: its
executed-test counts came from a build whose commit was not recorded, and its
falco-demo cell carried no number because nothing reproduced it.
Architecture Rules said 39 rules in five classes; there are 48 in seven.
ForeignWritePathTest and MigrationBoundaryTest were missing entirely.
Both pages now state the same thing plainly: falco-migration sits outside
ModuleBoundaryTest's PUBLISHED set because it legitimately depends on
falco-anvil, and the other rules of that class therefore do not reach it. A
gap, not a decision.
Installation and Publishing carried three modules at 1.0.0; there are four at
2.1.0, and five artefacts with the BOM.
Anvil Chunk Loader: ChunkMigrator as the third service of that shape, the
truncated-chunk refusal from #49 under the version floor, and one sentence
that #50 made false — "nothing is converted or migrated" — corrected rather
than left standing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jtJ4GUtmyCSHkiGY1CvgR
78cde20
docs(anvil): correct three claims this branch's policies made stale
The extension-points branch moved the version guard into
DefaultChunkVersionPolicy and the unknown-entry fallback into
UnknownEntryPolicy, but this page still pointed at the pre-branch
shape in three places:
- The version-floor section named FalcoAnvilLoader#requireReadableVersion
as the living guard, with a source link. That method is gone; the
body moved to DefaultChunkVersionPolicy#check, now the loader's
default ChunkVersionPolicy and cross-referenced to the
already-accurate "Replaceable policies" section.
- The lazy-registry section and the comparison table described a
private Registries record published by a single volatile write,
plus members BiomePaletteResolver.resolved and #registries. The
record was removed outright; the field is resolvedRegistry and
holds only the registry, the accessor is registry(). The fallback
id it used to pair with the registry is gone too -- an unknown
biome now goes through UnknownEntryPolicy instead.
- The testing section claimed there was no unit test for
BlockPaletteResolver and BiomePaletteResolver and that a
server-free one was "currently missing". UnknownEntryPolicyTest
is exactly that, exercising both resolvers against
Biome.createDefaultRegistry() without a running server.
Also corrected, pre-existing and not caused by this branch: the
Status section undercounted the package's public types at thirteen;
there are twenty-two, all now linked. The nearby "ten of the
thirteen classes import nothing from net.minestom" turned out to
already be correct on its own terms -- it counts the thirteen rows
of the Testing table (ten of which need no Minestom server), not
the public-type count above it, verified by counting the table's
"Needs a Minestom server" column directly.
Every corrected line was checked against the current source before
being rewritten.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jtJ4GUtmyCSHkiGY1CvgR
9f93ae9
docs(anvil): document the two replaceable loader policies and their resolution rules
94d8e40
docs(status): the half of the air hole that #45 did not close
#45 named three decisions that combined to load a pre-21w43a world as air. It closed the first two
and left the third: NbtReads.optionalList still returns an empty list for a missing key, and
decodeSections still reads sections through it. A chunk with Status full and neither sections nor
Level therefore still reaches the caller as air - reproduced during the review, not argued, with
chunksLoaded at 1 and errors at 0.
Entered as item 1 because the list is ordered by consequence: it is a different defect from the one
#45 fixed, but it shares the failure mode that makes both consequential - the caller is handed a
chunk that looks generated. Two smaller items from the same review follow under Smaller items: the
unverified span between DataVersion 2844 and 2860, and the "-1" label a mistyped DataVersion gets in
the diagnostics breakdown.
The cross-reference "item 2 of this list records" was pointing at an entry that had already been
resolved and removed, so renumbering could not fix it. Replaced with the page that actually carries
the two rows.
21fdad4
docs(anvil): make the air-decode-then-overwrite claim conditional
The line said flatly that a save "then wrote" the air chunk over the
real data. The design spec is explicit that whether a later save
actually does that has not been measured and is not claimed. The page
already phrased this correctly, conditionally, further down -- this
brings the first mention in line with the second instead of
contradicting it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jtJ4GUtmyCSHkiGY1CvgR
7803054
docs(anvil): document the version floor guard
a3fcb96
The quick start has five steps now, not four
Three places said four. The readme gained a step showing all three modules
together, and the class that compiles its snippets was renamed to
DocumentationSnippets because it now covers the readme as well as the wiki.
f5e4b7f
Say that Minestom's chunk events still fire, and when to take which
The usage page introduced ChunkLifecycleListener without mentioning that
InstanceChunkLoadEvent, InstanceChunkUnloadEvent and PlayerBlockBreakEvent are
dispatched by FalcoInstance exactly as they are by a container. Read as written,
it suggested the listener is how you observe a chunk here, which would push
application code onto an interface that runs inside the chunk lock and whose
throws fail a load. The events are the right default; the listener is for acting
before anybody else sees the chunk, and for per-block notification.
128b44c
Add a usage page for falco-instance, which had none
The wiki had a usage page for each of the other two modules -- Anvil Chunk
Loader and Light Engine -- and for the instance it pointed at Rationale:
Instances and Chunks instead. That page answers why the module exists and what
it refuses to do; it never showed anybody how to use it. The sidebar entry said
as much by naming the rationale page "the third module".
Instances and Chunks is that missing page: when it is worth taking, the builder,
the chunk loader form, the combination with the light engine that only became
possible in 1.0.0, both lifecycle routes, shared worlds, and the accessors that
read a lazy chunk without materialising it -- getSections() allocates all 24
sections, which is the one thing on this module that is easy to trip over.
Every code block is compiled rather than typed into a page. WikiSnippetCheck in
falco-demo carries all of them, and writing it found an error before a reader
could: the light engine block said new ChunkLightScheduler(instance), and the
constructor takes a ChunkLightService.
Navigation updated in the three places a new page needs it -- the sidebar, the
Using Falco list on Home, and the rationale page, which now points forward to
the usage page as well as being pointed at.
069384b
Take the coordinates to 1.0.0, which is the release that carries the BOM
1.0.0 shipped on 2026-08-03 and is the first release with the chunk, instance
and shared instance work in it. Installation told readers to depend on 0.3.0.
Two statements were not just stale but wrong once 1.0.0 existed. The BOM snippet
carried a snapshot coordinate and a comment explaining that the release endpoint
did not serve falco-bom yet; it does now, verified against
repo.onelitefeather.dev, so the snippet is an ordinary coordinate and the
workaround is gone. The snapshot section said 0.3.0 is the latest release and
0.3.1-SNAPSHOT is what the endpoint serves; it is 1.0.0 and 1.0.1-SNAPSHOT.
Build Setup and Versioning and Releases quote files rather than describe them --
apiBaselineVersion, the version assignment, the release-please manifest -- and
every quoted value was checked against the file it comes from rather than
adjusted by pattern.
Statements dated to a version were left as they are: "carried out on 2026-08-01
against Falco 0.3.0", "falco-instance shipped in 0.3.0", and the tag list naming
v0.2.0, v0.2.1 and v0.3.0 are all true of when they were written, and rewriting
them would turn a record into a claim about today.
aa7aa7a
Bring the instance and chunk pages level with what shipped on 2026-08-03
falco-instance gained its own chunk storage, a facade split, a shared instance
and a lighting chunk that finally sits on the same class as the rest. The wiki
described the state before all four, and in three places it now said the
opposite of the code.
Corrected, each read against the source first:
- Project Status listed "FalcoInstance and FalcoLightingChunk are mutually
exclusive" under things that cannot be used together. That was the limitation
the whole rewrite existed to remove; FalcoLightingChunk extends FalcoChunk now
and the two run together. The section says what it was, what closed it, and
what it cost -- the class is final, which is a deliberate break of binary
compatibility recorded in gradle/api-breaks.properties.
- Rationale: Instances and Chunks explained "why the superclass is DynamicChunk"
and stated that FalcoChunk adds no storage because Minestom's was never the
part that needed replacing. Both were true of the first version. The section
now says what changed the answer, which was a measurement rather than an
opinion.
- Research: Shared Instances asserted FalcoChunk extends DynamicChunk in the
present tense and named two ways out of the batch light gap. It closed by
neither of them, and the paragraph says so.
- Four places across three pages said there is no instance benchmark in the
repository at all, one of them quoting the build file as proof. The build file
declares jmhImplementation(project(":falco-instance")) since 2026-08-03 and
four JMH classes exist. None has been run as a baseline, so the conclusion
those passages drew is unchanged and each now says which half is missing: the
instrument is there, the run is not.
New: a "Counted" section on Measured Results, kept apart from the timings
deliberately. Object counts have no spread and are unaffected by what else the
machine was doing, so they are the only figures on that page that survive being
taken on a busy machine -- 25 objects and 840 bytes against 192 and 6 848, the
62.24 % empty-section census, the materialisation counts per operation, and the
palette break-even. No timing figure was added anywhere, because the instance
suite has still never run.
Two open items added to Project Status: the unlocked tick map, which is
inherited from DynamicChunk rather than introduced, and the macOS hang that
leaves the counted figures proven on two platforms out of three.
Both page headers that claimed their page carries no measurement were corrected
too -- adding a number and leaving that sentence standing is how a page starts
lying about itself.
a61b6fb
docs: split the three changes apart, and name the case that got slower
A third run separates them. The first attempt at it was discarded rather
than quoted: it degraded partway through, and what caught that was the
same job measuring minestomFull, a class no commit here touches, at
+64.8 %. The re-run holds that control inside 1 %.
The reordering and the arrival-direction skip carry the section
propagator on their own. The flat column adds nothing there, which is the
expected answer for a change that lives in the other class, and it is
worth recording as a control whose correct result was zero.
On the chunk propagator the flat column adds a further -8 to -15 % on top
of the two free changes, so its buffer is paid for. Except on the sky
pass at four sections, where it costs +11 to +18 %. The earlier pair made
that look like noise; on a clean run both configurations agree and the
tighter one reads +11.4 %.
It is kept for a reason that is arithmetic, not tolerance: +22 us at four
sections against -733 us at twenty-four, and a real overworld chunk is
twenty-four. The entry says so, and says what the targeted fix would be,
so the decision can be revisited by someone who has four-section columns
rather than rediscovered.
c80a857
docs: delete the Before ratio column instead of repairing it
Not one of its six cells could be recovered from the columns beside it —
74.2 / 49.4 is 1.50 where the cell said 1.42, and at 1 source and 30 % solid
61.8 against 62.0 puts Falco marginally ahead where the cell said 1.03 %
behind, so even the direction did not follow. Five of six missed by more than
a rounding step.
The marker offered two ways out. The first, publishing the Minestom column of
the pre-69381af run, is not available: that run was never recorded and cannot
be re-created, because the pair compares two Falco versions on one machine and
would need both commits measured in one process on that machine. Two CI runs
land on different CPUs and answer a different question. So the second: the
column is gone.
Nothing measured was removed. Both Falco columns and the Minestom column stay,
and they still carry what the pair was published to show — Falco's own time
falls on every row between the two commits, 74.2 to 44.5 at one source in open
sky. That comparison never needed the Minestom column. What is gone is the
arithmetic nobody could check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
e9b72b0
docs: measure the eight-state row, where Falco loses and it resolves
The marker asked for the eight-state rows to be published or the claim
dropped. Both, as it turns out.
A full-suite run puts Falco at 566.87 ± 12.61 against Minestom 538.70 ± 4.51
at distinctStates = 8 on one thread. The intervals are disjoint, so unlike the
published 200-state row this one does resolve: Falco is 5.2 % slower, bounds
2.0 % to 8.5 %. The withdrawn "20 % at 8" falls well outside that and stays
withdrawn.
What fewer distinct states change is that the single-thread disadvantage
becomes measurable, not that it grows — the 200-state row of the same run
overlaps exactly as the published one does. That is the same conclusion the
bullet already drew, now with a row that resolves rather than one that does
not.
This is a row where Falco loses and it is published as such, with its bounds,
next to the rows where it wins.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
c29eea1
docs: the first rebuild figures this suite has actually measured
Three of the entries under Smaller items came from the standalone rebuild
and said so: directions rather than factors, no intervals, none of them
measured here. #36 built all three and they are now measured, with
intervals, on every configuration of two benchmark classes.
The rebuild said -26 % on searching a column. Measured: -27.3 % to
-39.7 % on the chunk propagator and -7.3 % to -22.0 % on the section one.
The direction held and the magnitude was understated, which is worth
recording as plainly as a miss would have been.
Two things are left open rather than rounded off. The split between the
two commits is not established: the run meant to separate them degraded
partway through, and the only reason that is known is that the same job
measured minestomFull, which neither commit touches and which read
+64.8 % there. And propagateSky at four sections shows no clear win in
either direction, which is exactly where the flat column should be
weakest, so it is named instead of averaged into the range.
94ba7fd
docs: measure the uniform-section fast paths, and keep the old figures anyway
The marker asked for both methods to be re-run with their intervals and the
block replaced. They have been run — in the full-suite run at the class
annotations — and the block is not replaced, for a reason the marker could not
have known.
The opacity figure could never have been reproduced. The design-time table
names no resolveCost, and that parameter decides an order of magnitude: the
same 200-against-1 comparison measures 15.8x at resolveCost 0 and 99.9x at 50.
Its 40.8 µs for 200 states falls between the two measured values and matches
neither, so there is no configuration under which the published pair is right.
The palette figure is 51x against a measured 87.7x.
The measured rows are published beside the old ones with their bounds and their
provenance, and the old ones stay where they are, still marked as design-time
measurements. Replacing them with numbers from a different machine would be the
substitution this wiki refuses everywhere else.
What the block was written to establish holds, and more strongly than the
figures claimed: detecting a uniform section up front beats walking 4 096
blocks by one to two orders of magnitude, depending on what a state lookup
costs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
d37835e
docs: one cell of the light table has been repeated, and fix a stale four
The marker asked for the figures of a shorter confirmation run, or for the
claim of a repeat to be dropped. Neither was needed in that form: the two-fork
re-run of 2026-08-02 repeats the 64 / 0 % cell and reproduces it — 109.88 ±
1.47 against the published 109.2 ± 1.6, intervals overlapping. That is the
project's fifth cross-run repeat and it is registered.
The rest of the table still has no repeat, and the shorter confirmation run
this page once implied was never recorded anywhere. Both now stated instead of
left as a question.
Two counts were left at four when the register moved to five, three lines
apart from a sentence that already said five. Light Engine and Rationale:
Lighting corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
c602ab9
docs: settle the save-stage percentages, which no denominator produces
Two pages carried the same question: were the withdrawn 97 % and 63 % computed
against `full` rather than against the sum of the stages? They were not, and
they cannot have been. One denominator cannot yield both — 97 % needs 4 182 µs
and 63 % needs 4 287 µs.
A CI run of ChunkSaveStageBenchmark at two forks settles the half that
arithmetic could not. `full` comes out 1.3 % to 1.5 % above the sum of its
stages at both parameter levels, so the decomposition is complete: there is no
unaccounted time in the save path for a different denominator to hide in, and
the gap between `full` and the sum is an order of magnitude too small to
explain 63 % against 65.3 %.
Both markers close on that. The shares that follow from the published rows are
98.0 % and 65.3 %, which Rationale: Chunk Loading already states; the withdrawn
figures stay withdrawn because nothing reproduces them.
The run itself is not published here. Its absolute figures are from a shared
runner and belong to no table on this page — what carries across machines is
the ratio between full and its own stages, measured in one process.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
b9bb345
docs: follow the javadoc corrections through five pages
#37 fixed two javadoc claims. The wiki had documented both as defects, so
fixing them made five sentences here wrong — the fifth time in this round that
a correct change invalidated prose standing beside it, and the first time the
change was one of ours.
Project Status counted three overrides where the table itself went on to name
four; it says five now, and what the last two actually do.
Two TODOs are resolved and removed. The one on Measured Results is replaced
rather than deleted, because the correction surfaced something nobody has
measured: within a trial the chunks are not rebuilt and both methods write
light into them, so every iteration after the first re-lights already lit
chunks. Level.Iteration would settle it and would leave the configuration that
table was measured under, which makes it a decision rather than a fix.
Benchmarking and Rationale: Measurement described the javadoc as claiming a
rebuild that does not exist. It no longer claims it. Both keep the substance —
the conclusion survives because a fourfold to sixfold gap is far outside what
repeated lighting could produce — and now say the third digit is untested
instead of implying the mechanism was the only problem.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
662e4db
docs: record that benchmarks can be run from CI
The workflow shipped in #29 and grew through #31, #33 and #35, and the wiki
mentioned it nowhere — not on Benchmarking, whose whole purpose is to say how
these are run. The fourth time this session that a correct change left the
prose beside it untouched.
Benchmarking gains the two profiles, what the workflow uploads, and the two
limits that matter: its numbers are provenance rather than precision, and a
shard is a whole class because splitting a comparison across runners measures
the hardware — 2.13× against the 1.13× one machine gives.
Contributing gains one paragraph, because someone about to run a benchmark
locally should know that a local run records none of its own conditions unless
they write them down.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
7288ac1
docs: record that stage 4 gave up the premise this section researched
The recommendation of section 1 was a delegating Falco-owned view, and it was
not taken. FalcoSharedInstance extends SharedInstance over a plain container
whose chunk supplier is FalcoChunk::new, so areLinked answers true and the
viewer union is unnecessary. The price the section did not name is that the
container stays the block owner and keeps its instance monitor on every write.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
abedd9e
docs: record what #34 built, and a summed row whose assumption expired
The first entry of Open is resolved. It is the one that was closed by
building what it proposed, and it departed from the proposal in one way
worth keeping: the invalidation is per chunk, not per section. Rebuilding
a single section still means reading that section's block states, and the
reading is what a table costs, so a per-section bound would have bought a
fraction of a rebuild for the price of a second index.
The entry keeps its measurement. That table is still exactly what the
recalculating path pays, because compute() keeps nothing by contract and
was not touched. What is added is the count — a steady 3x3 tick built 50
table sets and now builds 1 — and the workflow run behind the timing,
with the note that its two halves ran on different CPU models and that
full() is the control which makes them readable at all.
Two published tables carried figures the change moved. Both now name the
commit they belong to rather than being overwritten: the numbers are
right for the run they describe, and the runs measured after the change
were taken on shared runners and replace no absolute figure here.
One of them is worth more than a correction. The "one tick pays for both
kinds" row is a sum, and the page had written down the assumption behind
it: that such a pass shares no work between the two kinds. #34 made that
false, because an opacity table is not a kind of light and the sky pass
now reuses what the block pass built. The row is an upper bound now, and
it could only be found to have expired because the assumption was stated
instead of left implicit.
Items 2 and 3 are renumbered to 1 and 2, and the two cross-references
into them are pulled along.
7b0d4d6
docs: record what #28 moved, and repeat the opacity measurement
Four pages carried 31.41 µs for the propagate stage as if it were current.
It is the after-value of one commit, 69381af, and #28 has since taken the
same stage under the same parameters to 27.3 µs — measured, but written
down nowhere. Each of the four now says which commit its figures belong to.
The opacity share in Project-Status was a single three-fork run. A second
one under the same configuration gives 21.8 / 20.2 / 17.0 % against
19.4 / 20.4 / 15.7 %. The range it establishes is the same, and the repeat
lands on the caveat the page already carried: area 16 moved again, and it
is again the widest interval. Neither run was taken on an idle machine and
the page now says so, which is the part the first provenance line omitted.
One sentence in Project-Status is also corrected rather than extended: the
gap between propagate and the per-section table narrowed, from 3.9x to
3.4x. It does not change the ranking that entry exists to correct.
3636c19
docs: a ratio is no more portable than an absolute figure
Every one of the five cross-run repeats behind "ratios reproduce, third digits
do not" was run on the same machine. The first CI run measures the UNIFORM
64 / 0 % pair at 1.35× on a four-core EPYC where the sixteen-core machine
behind the published tables gives 1.13× and 1.16×. Both are tight, both are
disjoint, and they disagree by more than either interval admits.
So the ratio is not a property of the two algorithms. It is a property of
those algorithms on a given cache hierarchy and memory system, and it moves
when that changes. Nothing here holds on hardware other than what its
provenance line names.
The second half is the part an interval cannot warn anyone about: the CI
figures are narrower than the published ones — Minestom's half-width is 0.68
against 5.60 — while being further from the truth about any other machine.
Precision and generality are separate properties and the ± measures only the
first.
The figures are quoted for the comparison between machines and not as a claim
about either implementation, and they are kept out of every published table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
961f829
docs: measure the compression level, and find that neither half was one number
Four TODOs across four pages waited on one run of compressFalcoLevel against
compressMinestomLevel. It has been done at two forks over all five
distinctStates levels, and the answer is that the question was wrong: the
trade has no single value on either side.
Time runs from unresolvable at one distinct state — the intervals overlap
there — to 2.96× at 1024. Size runs from 0.08 % to 40.66 % over the same
sweep. They move together, so the cheap size and the flattering factor are
never the same measurement, and "1.83× faster for about 3 % more bytes" was
never one.
Two of the three competing factors survive. 2.4× at 256 distinct states falls
inside the measured bounds 2.07×-2.53×, and 3.1× at 1024 inside 2.74×-3.20×:
they were readings that had lost their conditions, not contradictions. 1.83×,
the one quoted with no distinctStates attached, falls inside no row's bounds
and has been dropped.
The size figures need no interval and get none. Deflate output length at a
fixed level over fixed input is deterministic, so the table states byte counts
rather than estimates. It runs the benchmark's own setup to compress the same
payload, rather than rebuilding a chunk that would silently be a different
measurement.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
888136f
docs: re-derive the benchmark families against the sources
The TODO asked whether "ten library benchmarks, eleven in a bare JVM fork" had
survived #26. Both did: AreaPassStageBenchmark is neither. The count that #26
actually broke was three lines above it — "five must start a server" is six.
Every one of the seventeen classes was assigned from its source rather than by
pattern: the ten library ones plug FakePaletteEntryResolver or
FakeBlockLightSource, and neither fake imports Minestom at all;
RegionFileComparisonBenchmark is the one comparison that still needs no
server, because Minestom's RegionFile reaches no registry.
Three things the re-derivation turned up that the TODO did not ask about.
The families no longer summed: ten plus four plus two is sixteen against
seventeen classes. AreaPassStageBenchmark joins the decision family, and the
page says why it does not fit its shape — the second method is a step inside
the first, not an alternative to it.
The design claim needed a boundary. "Both libraries were designed so that a
bare fork is possible" holds at the section level and cannot hold above it:
ChunkLightArea#compute takes an Instance, so there is no seam, and three of
the six server-starting classes are now Falco-against-Falco light benchmarks
rather than comparisons against Minestom.
And the heap gap lost its justification while keeping its point. The two
classes with no heap flags were said to have the largest live set in the
harness; AreaPassStageBenchmark loads a 4×4 field plus its ring — 36 real
chunks — and pins -Xms2g -Xmx2g. The gap is unchanged; the precedent for
closing it now sits in the same package.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
da6c80d
docs: retire the 112.7 anchor and put the regression on a run that exists
Four pages carried a TODO because "mixed sources cost Falco about a third"
rested on a UNIFORM value of 112.7 µs that appeared in no table. It was never
traceable and is now dropped rather than reconciled. Nothing was substituted
for it: 109.2 is a real number from a different run at different iteration
counts, and putting it in that slot would have invented a measurement.
The claim itself holds and now has a source. The two-fork re-run of 2026-08-02
measures both cells together — 109.88 ± 1.47 against 155.73 ± 8.44 — for a
rise of 41.7 % with conservative bounds of 32.3 % to 51.4 %. The old "about a
third" sat at the bottom of that range rather than at its centre.
What makes the replacement better is not the extra fork. It is that both cells
come from one run at one setting, which is precisely what the old pair could
not offer: its two halves were measured at -wi 5 -i 10 and -wi 3 -i 5, so the
percentage between them was never derivable in the first place.
Light Engine loses its TODO as well, for the same reason: its "roughly 1.3× to
1.06×" had the same untraceable base. The margin does fall, but the MIXED
intervals overlap and no factor may be quoted for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
520ad03
docs: bind the remaining class counts to a commit or mark them open
Three more counts said sixteen. Two are mechanically checkable and now name
the commit they were read at: fifteen of seventeen pin their heap at a09c71f
(it was fourteen of sixteen, and the two exceptions are unchanged), and seven
of seventeen run at two forks.
The third is not checkable by grep — neither "library benchmark" nor "runs in
a bare JVM fork" is a property a pattern can decide — so it loses the totals
it cannot support and carries a TODO to re-derive both against the sources.
Guessing which side AreaPassStageBenchmark falls on would have been inventing
a fact.
One count keeps sixteen and is correct: it names ca79507.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QzEdy5fN5JKGxJo8gwtNeu
d97f50b