feat(kernel-utils): add sheaf programming module#870
Open
Conversation
Contributor
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
4184513 to
03f6113
Compare
487dd20 to
282a277
Compare
4123110 to
2519237
Compare
0a7b40c to
f4bb458
Compare
f4bb458 to
f59d51b
Compare
342233d to
0de9c94
Compare
0de9c94 to
4f47c89
Compare
rekmarks
reviewed
May 5, 2026
Member
rekmarks
left a comment
There was a problem hiding this comment.
I think this is a promising direction for composing object capabilities that we should continue to experiment with. Some notes:
- The terminology is abstruse for the algebraic topologically challenged. I propose an alternative here
- Sheaves should be in their own package
@metamask/sheaves. We'll have to cut a release once this is merged.
2bd354e to
62f7a94
Compare
Contributor
Author
Suggestions applied, except any renaming suggestion that included 'tags' in the new name. |
5c536ca to
eed028e
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tocol violation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…se only Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lic exports Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ve.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"Metadata" is one compound word; the mid-word capital was inconsistent with the surrounding identifiers and prose docs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The alias added a second public name for PresheafSection<M>[] with no external consumers. Callers write the array type directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The guard is passed dynamically at call time so TypeScript cannot propagate the method signatures through Sheaf<M>. The comment prevents future contributors from chasing a phantom improvement. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ndler failure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LIFT.md: fix exhaustion description to match actual error shape - README.md: remove stale "registry" and "tracks" claims post-revocation-removal - types.ts: remove "revocable" from Sheaf method docs; clarify when to use global section variants vs explicit-guard variants - USAGE.md: use makeSection (public API) in single-provider example; clarify proxyLift vs yield* for lift composition Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dataKey conflation bugs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gen.next(errors) was passing the same live mutable array reference on every resume. A lift that stores the received value from one yield and inspects it after a later yield would see mutations from subsequent failures. Pass [...errors] snapshots so each yield receives an independent copy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… conflation
JSON.stringify maps undefined, NaN, Infinity, and -Infinity all to null,
so sections with e.g. { cost: Infinity } and { cost: null } produced
identical keys and were incorrectly collapsed into one germ. Replace the
plain JSON.stringify(entries) with encodeMetadataEntry, which includes a
typeof tag in each tuple so all of these distinct values produce distinct
keys. BigInt metadata values no longer throw at serialization time either.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sheaf is a large, self-contained subsystem. Keeping it under its own subpath import reduces coupling on consumers who don't need it, and keeps the main index focused on general utilities. - Add @metamask/kernel-utils/sheaf entry point (src/sheaf/index.ts) - Remove sheaf re-exports from the main index - Add ./sheaf export to package.json alongside the other subpaths - Remove sheaf overview from README (belongs in sheaf/README.md) - Update CHANGELOG: use subpath import, drop internal exports (collectSheafGuard, getStalk, guardCoversPoint), add makeSection and noopLift, fix MetadataSpec capitalisation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…decomposeMetadata Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
=== fails for NaN (NaN !== NaN), so a NaN value shared by all germs was never promoted to a constraint — it remained in each germ's distinguishing metadata instead. Object.is correctly treats NaN === NaN and is consistent with the type-tagged encoding already used in collapseEquivalent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…aKey Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
JSON.stringify(-0) produces "0", so -0 and +0 were serialised to the same metadataKey and incorrectly collapsed into one germ by collapseEquivalent. Object.is(0, -0) is false, so decomposeMetadata already treated them as distinct — making the two functions inconsistent. Add -0 as an explicit special case alongside NaN, +Infinity, -Infinity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
eed028e to
cc25c7f
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit cc25c7f. Configure here.
…eMetadata
Two bugs in decomposeMetadata:
1. `key in constraints` matches prototype-inherited names (e.g. 'constructor')
on an empty {} object, causing distinguishing metadata keys to be silently
dropped from stripped candidates.
2. `key in meta` matches prototype-inherited names, and when the inherited
value happens to equal Object.is the candidate value, the key is wrongly
treated as shared and placed in constraints.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pe chain
Replace `key in obj` with `Object.hasOwn(obj, key)` in two places inside
decomposeMetadata:
- Sharing check (line 127): `key in meta` would pass for prototype-inherited
names (e.g. 'constructor'), causing a key to be treated as present in a
candidate that does not own it. If the inherited value also happens to match
via Object.is, the key is wrongly promoted to a shared constraint.
- Stripping step (line 137): `key in constraints` is true for prototype
property names on an empty {} object, so any metadata key that shadows a
prototype name gets silently dropped from stripped candidates even when it
was never added to constraints.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

Introduce operational presheaf + sheafify for guard-based dispatch:
Note
Medium Risk
Mostly additive new package, but it introduces new dispatch/routing logic (guard matching, metadata evaluation, policy-driven retries) that will affect callers adopting it and could surface subtle runtime edge cases.
Overview
Adds a new
@metamask/sheavespackage that implements guard-based capability routing viasheafify, producing dispatch sections that select among matching providers using evaluated metadata and a caller-supplied async-generatorPolicy(including retry with accumulated errors and constraint/option metadata decomposition).Includes helpers for metadata specs (
constant/callable/sourcewith optional compartment compilation), policy composition utilities (noopPolicy,withFilter,withRanking,fallthrough,proxyPolicy), remote-provider wrapping viamakeRemoteSection, plus extensive unit/e2e tests and documentation. Wires the package into the monorepo build/test setup (newtsconfigrefs,vitestconfig, lockfile entry) and adds standard package metadata/licensing/changelog.Reviewed by Cursor Bugbot for commit ba12dbe. Bugbot is set up for automated code reviews on this repo. Configure here.