642 Storage SL5b allholonNodes fix - #657
Open
nphias wants to merge 3 commits into
Open
Conversation
11 tasks
nphias
marked this pull request as ready for review
August 24, 2026 15:17
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Whole-space discovery becomes ordinary graph traversal. Publishing a lineage writes
Holon ─OwnedBy→ HolonSpaceand itsHolonSpace ─Owns→ Holoninverse;get_all_holons_internalreads that
Ownscollection instead of theAllHolonNodespath index. This is thecoordinator-owned replacement #631 §3 and §9 named as SL5b's precondition.
GetAllHolons, the legacy dance, the wire binding, and the TS SDK keep their contracts.One semantic change: the
LocalHolonSpaceanchor is no longer inGetAllHolonsresults — it isnot a member of its own
Ownscollection (#642 §1, §9), so every DB count drops by one. Callersthat want the anchor use
TransactionContext::get_space_holon().AllHolonNodesis still written and all of its tests still pass. Nothing reads it, which makes thisa reversible checkpoint rather than a cutover.
Changes
commit_functions.rs— newpersist_space_membership, called fromcommit_holon'sForCreatearm immediately after
persist_holonreturns. It writes theOwnedByandOwnsSmartLinksdirectly through the existing
persist_smartlink, in the node pass.Membership is deliberately not staged for the relationship pass. See the first note below — this
is the load-bearing decision in the PR.
staged.rs—relationship_collections_for_commitexcludesOwnedByfrom every scope. Storageauthors membership; the coordinator never replays it. A staged
OwnedBycan still arrive (cloning asaved holon carries its persisted membership into the staged map) and is dropped rather than
re-anchored to the clone or to a new version.
guest_holon_service.rs—get_all_holons_internalresolves the space and delegates to theexisting
fetch_related_holons_internalwithOwns; no new traversal code.delete_holon_internalgains
retract_space_membership, deleting theOwnslink on the space and theOwnedBylink on theholon before the node delete.
create_local_space_holongains a comment recording that its stagingbypass is now load-bearing.
Reading through
fetch_related_holons_internalis a small win: the old code built its collectionwith
add_referencesand no keys, whereas theOwnslink's canonical key is the owned holon'skey — so the collection is now keyed from tag bytes with no hydration, which is what every
get_by_keyconsumer behind this call already wanted.Contract docs —
holon_service_api.rs,holon_dance_adapter.rs,get_all_holons_dance.rs,transaction_command.rs,transaction_wire.rs,map-sdk/.../transaction.ts, andschema-src/commands/schema.tdl(with the generated JSON regenerated — a one-line diff). All said"all persisted holons".
Fixtures —
count_saved()drops the+ 1for the anchor, carrying eight fixtures at once;load_holons_internal_fixture's five hardcodedMapInteger(1)baselines becomeMapInteger(0);ENSURE_DB_EMPTYand the inline "+1 for the space holon" comments corrected. Newpartial_commit_membership_fixture— see Testing.Notable decisions
Membership is authored in the node pass, beside
persist_holon. This is where the designlanded after two earlier attempts, and the reason matters. Membership is what whole-space
discovery reads, so a holon that publishes without it is persisted and permanently invisible —
something
AllHolonNodescould not produce, because it indexed insidePublishRoot.Staging the edge for the relationship pass reintroduces exactly that split. The relationship pass
is skipped outright whenever any staged holon fails to publish, so a commit of two holons where
one is rejected leaves the other published and unowned, with nothing to repair it later. Ordering
OwnedByfirst within the pass — which an earlier revision of this PR did — narrows the windowbut does not close it. Writing membership beside the node write restores the coupling
unconditionally.
The coordinator never replays membership. Since storage authors it, replaying a staged copy
can only do harm: a version-producing commit replays the full scope against the new version's
id, which would make the space own the lineage root and every version after it separately (Storage SL5b — Replace AllHolonNodes with HolonSpace ownership #642
§8 rejects re-anchoring per version). Excluding
OwnedByfrom every scope is one filter andcovers the version case, the clone case, and any future path that stages it.
-
resolve_inverse_relationship_namenever seesOwnedBy, so the descriptorshort-circuit added for it is gone and the descriptor system is back to having no exceptions.
Deletion retracts membership both ways.
delete_holon_nodedeletes only the path link and theentry; SmartLinks survive it, which would leave deleted holons discoverable (Storage SL5b — Replace AllHolonNodes with HolonSpace ownership #642 §9). The
retraction lives in
delete_holon_internal, not the persistence layer, which is deliberatelydescriptor-unaware.
A latent harness bug surfaced.
FixtureHolons::counts()charged a deleted holonsaved -= 1in addition to its head no longer counting as
Saved. The old anchor allowance cancelled itexactly, which is why it went unnoticed and why
delete_holon_fixture's post-delete assertion wascommented out with a TODO.
Deletednow contributes nothing and that assertion is enabled — it ishow the retraction above is tested.
The saved-content comparator ignores
OwnedBy.assert_saved_content_eqrefused to compare anundescribed actual holon carrying any relationship content, and every saved holon now carries
membership. Infrastructure-supplied edges never appear in fixture snapshots — the same reasoning
that already tolerates commit-materialized inverse SmartLinks there.
all_holon_nodes.rsand itsmod.rswiring;index_under_all_holon_nodesand itsPublishRootcall;
ALL_HOLON_NODES_PATH;LinkTypes::AllHolonNodesand its four dispatch arms; both validators,the
AllHolonNodesDeleterejection, and their re-exports; theall_holon_nodes_delete_for_testprobe and the
RootIndexLinkType::AllHolonNodesvariant; bothcoordinator-surface.toml[[export]]blocks; and theAllHolonNodestests and table legs.mock_conductor.rs's livenessprobe calls the
get_all_holon_nodesextern purely as a callability check and needs repointing.validate_root_index_createand the shared rejection variants stay —LocalHolonSpacestill usesthem (Storage SL5 — Retire Obsolete Persistence Indexes #631 §9). Removing the enum variant renumbers
LocalHolonSpace1→0 andSmartLink2→1 and sochanges the DNA hash: the same accepted consequence as SL5a, inert by construction since the enum is
the single source of truth for both the write and the read of every link.
Testing
The write and retract paths are both directly exercised, which matters because discovery now
depends entirely on them:
ensure_database_countstep pins membership exactly, now that the anchorallowance is gone: pre-commit steps assert the anchor is excluded, post-commit steps assert new and
cloned lineages appear, and
stage_new_version_fixtureasserts a version adds no member.partial_commit_membership_fixture(new) stages two holons, one carrying moreproperties than PVL permits, so its
PublishRootis rejected and the commit reportsIncompletebefore the relationship pass runs. It then asserts the other holon is still an owned member. This
is the case the earlier staged-membership design got wrong; the fixture was confirmed to fail
against that design and pass against this one.
delete_holon_fixture's post-delete count assertion, re-enabled here, asserts thedeleted holon is gone from
GetAllHolons— the retraction's only test, and the reason the harnesscounting bug above had to be fixed.
Not measured: the
Ownsfan-in cost. Every inverseOwnslink is based on the space holon, andput_smartlinkscans all live links on the base for conflict detection — so a 191-holon core-schemaload is ~O(n²) tag decodes against one base. Numbers on this branch:
dance_tests144.9s,holon_storage_tests68.8s,pvl_validation_tests47.9s,smartlink_tests19.1s. A before/aftercomparison still needs a baseline run on the parent commit. This is the open SL3 candidate-scan cost,
now on a hot path; conflict-detection semantics were deliberately left alone.
Environment note. The three workspaces share one
target/; runningcargooutsidenix developmixes rustc versions and the next nix-side build fails withE0514plus misleadingcannot find type Xerrors in untouched files. Also,npm run sweet:testskips the WASM rebuild andthe audit.
Scope
Unchanged:
QueryExpression,QueryDance, query planning (#642 §5); any new public whole-spacequery API; key or type indexes; the core-schema JSON inputs beyond one regenerated command
description;
persistence_layer::smartlinkand the Tag v1 codec;LocalHolonSpacebootstrapbehavior; exact-version reads and Storage SL2 record behavior.
OwnedByandOwnsstill set neitherIsDefinitionalnorAllowsDuplicatesin the TDL. Nothingreads them — storage authors membership directly and the coordinator never resolves it through
descriptor policy — so it is not blocking, but it is a trap for anyone who later routes ownership
through that policy.
Still open, unaffected: the
put_smartlinkcandidate-scan cost (SL3), now more load-bearing thanbefore, and the reference-layer/storage disagreement over what "the same link" means (SL4).