Hide registered tools without scanning revisions or allocating keys
Hiding a model-visible tool scanned every registered revision to find its logical name, then allocated a replacement string key for an existing BTreeMap entry. Borrow the existing logical-name selection mutably and set it to Hidden. Retain every revision and the exact missing-tool error.
Lower bound
One lookup in the existing ordered name index, followed by replacing the selection. Free the previous selected revision string when needed. No allocation, key/payload copy, hash, encoding, new index, syscall or lock is required. This API has no accounting observer and records no charge; hiding a funded native copy must leave its accounting budget unchanged.
Before / after
| Observation | Tools | First name, ns before → after | Middle name, ns before → after | Last name, ns before → after |
|---|---|---|---|---|
| No subscriber | 1 | 50 → 40 | 50 → 31 | 50 → 40 |
| No subscriber | 64 | 50 → 40 | 160 → 60 | 270 → 80 |
| No subscriber | 4,096 | 60 → 40 | 6,830 → 80 | 23,906 → 100 |
| Tracing registry | 1 | 50 → 40 | 50 → 40 | 50 → 40 |
| Tracing registry | 64 | 50 → 40 | 161 → 60 | 281 → 60 |
| Tracing registry | 4,096 | 60 → 40 | 9,594 → 80 | 19,390 → 100 |
Wall medians characterize repeated hides. Allocation assertions include the initial transition and every repeated call. Every baseline call has 1 allocation / 11 allocated bytes / 11 peak live bytes; every candidate call has 0 / 0 / 0. Key copying falls from 11 bytes to zero. The previous selected revision is still dropped. Registry metadata and retained tool definitions are unchanged.
Whole-fixture syscall traces: 3,905 → 3,878 calls, with 3,275 mprotect calls in both. The difference includes test output and the baseline's intentional failure; no per-operation syscall reduction is claimed. Candidate futex calls are test completion and thread joining, not application lock contention. A successful hide itself makes no syscall and takes no lock.
Process RSS measurements include fixture construction and are not comparable as isolated operation storage: baseline cost test 27,712 KiB; candidate registry suite with the stronger property 28,348 KiB. The deterministic operation-level peak heap assertion is 11 → 0 bytes; no process RSS improvement is claimed.
Baseline measured ELF SHA256 e6dc9a36c231dd33911d3867e831a274bb3e72ac2befebffe574aeba25b0551e; initial candidate 96d2be940ad54f7a6feac36c547ca23b050ad66d4289ff23465853518e8f5bc3. A subsequent focused build strengthens clone comparisons and returns property failures without unwrap/expect; the final allocation and timing table above comes from that check. The production method is unchanged.
Identical release fixtures: 1, 64 and 4,096 registered tools; first, middle and last names; 64 samples each, with no subscriber and with a tracing registry. Construct the fixture outside the measured closure. Restore the original scan-and-insert method in the remote source for the baseline; the new allocation regression must fail after recording all eighteen profiles.
Verification
-
Final registry suite: 2 passed, 0.430144 s process wall. This includes the shrinking mutation/extension/clone property, funded native copies and the zero-allocation regression.
-
Existing explicit model-selection suite: 1 passed, 0.040980 s.
-
Restored original method: property passes in 0.093555 s; allocation regression deterministically fails after all eighteen rows in 0.076109 s with
hiding allocates. -
Release builds: baseline 4m31s, initial candidate 2m05s, final strengthened test harness 1m17s, excluded from suite timings. No unchanged build was repeated; all runs use the built binaries directly.
-
cargo update --workspace --locked, rustfmt andgit diff --checkpass. #592 is merged; the unpublished branch was rebased onto main with signed commits and the temporary duplicate fix was dropped. All three changed registry source files are byte-identical to the validated version after the rebase, so no redundant rebuild was added. -
Shrinking property: after every successful or rejected registration, explicit selection, hiding, cloning and disjoint/overlapping extension, the selected-map key set equals the set of logical names in all registered versions. Valid names and revisions vary independently. This is the invariant that permits the name index to answer existence without a version scan.
-
The property also exercises the real funded native clone, checks the same key-set invariant, hides its entries and verifies its remaining work budget is unchanged.
-
The existing revision-selection test checks ambiguity, explicit selection, hiding, pinned retrieval, registering a new hidden revision and missing-name errors.
-
Deterministic allocation regression covers eighteen profiles, not a timing threshold.
Decisions
- Keep the existing ordered name index, which also provides canonical definition order; add no cache or metadata.
- Restrict the production change to hiding. Registration's separate scan and pinned lookup allocations remain separate topics.
- No synchronization or parser code changes; the unbounded registry-state invariant is covered by a shrinking property.
- Portable validation runs only through bs.ps1. The separate #592 portable cancellation fixture fix is now on main.
Summary by cubic
Hides a model-visible tool without scanning every registered revision or allocating a replacement key: remove_from_model now mutates the existing selection entry in place instead of checking self.versions and inserting a new String key.
- Cuts hiding from 1 allocation / 11 bytes to 0 / 0, across 1, 64, and 4,096 registered tools; timing improves accordingly.
- Adds a shrinking property that verifies the logical-name index stays in sync across registration, selection, hiding, cloning, and extension, plus a deterministic allocation regression covering 18 profiles.