feat(MeshActionRegistry): Apple-parity mesh-wide action discovery, without ambient trust - #216
Merged
Merged
Conversation
…-wide actions
Apple's AppIntents surface is system-wide by default: any app can register
intents any other app can invoke, all queryable via one AppIntentSchemas.sqlite.
This spec gives the estate the equivalent SHAPE without the ambient trust:
- Each participant's catalogue is referenced by a signed manifest
(SourceOSRepoManifest, hardened this session to reject empty policyClasses).
- Each participant's action catalogue conforms to Noetica's
ActionCatalogEntry (PR #572) — typed with the ValueType algebra we
shipped, carrying consentRequired and capabilityRef.
- The mesh registry ITSELF is signed and carries a registryDigest so
consumers detect a tampered snapshot.
- Each participant's catalog is pinned by catalogDigest so a swap of the
remote catalogue file is visible as a digest mismatch, not silent trust.
- Verb ontology reference is optional per-participant (see Noetica's
verb-ontology.json, PR #573); absent means the participant does not
classify by verb, a weaker claim Apple ships.
Execution stays governed by the origin app's own capability-ledger and
consent gates — this is a discovery pattern, not a shared executor.
15/15 validator cases pass:
3 positive: full example, unsigned variant, single-participant
12 negative: empty participants, bare-string digests, missing manifestRef,
missing catalogDigest, missing signature.algorithm, unknown
algorithm, wrong repo shape, non-date-time generatedAt, wrong
type discriminator, wrong URN namespace, undeclared field
Date-time is enforced with a pattern because jsonschema treats format as
annotation-only by default — same lesson as the semantic-proof fix earlier
this session.
mdheller
force-pushed
the
rescue/mesh-action-registry
branch
from
July 30, 2026 01:32
cc547fd to
66d5ebb
Compare
mdheller
added a commit
that referenced
this pull request
Jul 30, 2026
…wide contract
The estate's governance thesis is that a dispatch is true only when both a Law
factor and an Evidence factor are established. It was stated in one app's
docstring and enforced nowhere: Noetica's dispatch ledger declared it while all
five call sites passed the literal 'POS', and 34 of 53 real entries recorded
`grounded: false` alongside `verdict: 'POS'` in the same JSON object.
An implementation can always drift back into that. A schema cannot. Put the
algebra in the spec so any app on the mesh — Noetica, prophet-workspace,
prophet-mesh agents, agora, ops-fabric — emits the same shape and a validator in
any language enforces it without trusting the emitter.
Give `×` its precise meaning: the MEET in the chain NEG < ZERO < POS (strong-
Kleene conjunction, i.e. min). A three-element chain is a category, so that meet
IS the categorical product — the equation is an identity, not a slogan. It is
NOT sign multiplication in {-1,0,+1}, which gives NEG × NEG = POS: a refused
gate and a refuted outcome certifying as true.
Two invariants, both enforced by shape:
1. verdict == law.factor × evidence.factor. The 9 branches are GENERATED from
min() rather than hand-typed, so the schema cannot disagree with the algebra
by transcription error.
2. evidenceTier T1 is unavailable when either factor is 'declared'. T1 means
instrumented; claiming it for a value no instrument produced is the same
defect one level up. Under-claiming is always permitted.
Ships conformance/lawful-verdict-vectors.json — the cross-language anti-drift
device. Two implementations that each pass their own unit tests can still
disagree with each other; only a shared vector set makes that detectable. Both
Noetica (TypeScript) and prophet-platform libs/python/lawful-verdict test against
this file, and the sealed example receipt is verified byte-identically by both
canonicalisers.
The validator asserts by REJECTION, 64 checks: all 9 cells accepted, all 18 wrong
verdicts rejected, NEG × NEG = POS rejected by name, T1 rejected whenever a
factor is declared, the example's seal recomputed, the vectors checked against
min(), and 15 shape controls. Proven able to fail: admitting NEG × NEG = POS in
the schema makes it exit 1.
Two defects found and fixed while building it. The regex classes were
over-escaped, so schemaVersion '0.1.0' failed its own pattern. Worse, the
validator printed "all 9 cells accepted" as an unconditional print() and duly
printed it while 9 of 9 cells were failing — a gate narrating success
independently of its own result, the exact defect this contract exists to
prevent, reproduced inside the checker for it. Every section line now derives
OK/FAIL from the failure list.
Stacked on rescue/mesh-action-registry (#216) because the Makefile target sits
beside validate-mesh-action-registry; retargets cleanly to main once that lands.
mdheller
added a commit
that referenced
this pull request
Jul 30, 2026
…wide contract
The estate's governance thesis is that a dispatch is true only when both a Law
factor and an Evidence factor are established. It was stated in one app's
docstring and enforced nowhere: Noetica's dispatch ledger declared it while all
five call sites passed the literal 'POS', and 34 of 53 real entries recorded
`grounded: false` alongside `verdict: 'POS'` in the same JSON object.
An implementation can always drift back into that. A schema cannot. Put the
algebra in the spec so any app on the mesh — Noetica, prophet-workspace,
prophet-mesh agents, agora, ops-fabric — emits the same shape and a validator in
any language enforces it without trusting the emitter.
Give `×` its precise meaning: the MEET in the chain NEG < ZERO < POS (strong-
Kleene conjunction, i.e. min). A three-element chain is a category, so that meet
IS the categorical product — the equation is an identity, not a slogan. It is
NOT sign multiplication in {-1,0,+1}, which gives NEG × NEG = POS: a refused
gate and a refuted outcome certifying as true.
Two invariants, both enforced by shape:
1. verdict == law.factor × evidence.factor. The 9 branches are GENERATED from
min() rather than hand-typed, so the schema cannot disagree with the algebra
by transcription error.
2. evidenceTier T1 is unavailable when either factor is 'declared'. T1 means
instrumented; claiming it for a value no instrument produced is the same
defect one level up. Under-claiming is always permitted.
Ships conformance/lawful-verdict-vectors.json — the cross-language anti-drift
device. Two implementations that each pass their own unit tests can still
disagree with each other; only a shared vector set makes that detectable. Both
Noetica (TypeScript) and prophet-platform libs/python/lawful-verdict test against
this file, and the sealed example receipt is verified byte-identically by both
canonicalisers.
The validator asserts by REJECTION, 64 checks: all 9 cells accepted, all 18 wrong
verdicts rejected, NEG × NEG = POS rejected by name, T1 rejected whenever a
factor is declared, the example's seal recomputed, the vectors checked against
min(), and 15 shape controls. Proven able to fail: admitting NEG × NEG = POS in
the schema makes it exit 1.
Two defects found and fixed while building it. The regex classes were
over-escaped, so schemaVersion '0.1.0' failed its own pattern. Worse, the
validator printed "all 9 cells accepted" as an unconditional print() and duly
printed it while 9 of 9 cells were failing — a gate narrating success
independently of its own result, the exact defect this contract exists to
prevent, reproduced inside the checker for it. Every section line now derives
OK/FAIL from the failure list.
Stacked on rescue/mesh-action-registry (#216) because the Makefile target sits
beside validate-mesh-action-registry; retargets cleanly to main once that lands.
mdheller
added a commit
that referenced
this pull request
Jul 30, 2026
…wide contract (#218) * schemas: LawfulDispatchReceipt — Truth = Law × Evidence as an estate-wide contract The estate's governance thesis is that a dispatch is true only when both a Law factor and an Evidence factor are established. It was stated in one app's docstring and enforced nowhere: Noetica's dispatch ledger declared it while all five call sites passed the literal 'POS', and 34 of 53 real entries recorded `grounded: false` alongside `verdict: 'POS'` in the same JSON object. An implementation can always drift back into that. A schema cannot. Put the algebra in the spec so any app on the mesh — Noetica, prophet-workspace, prophet-mesh agents, agora, ops-fabric — emits the same shape and a validator in any language enforces it without trusting the emitter. Give `×` its precise meaning: the MEET in the chain NEG < ZERO < POS (strong- Kleene conjunction, i.e. min). A three-element chain is a category, so that meet IS the categorical product — the equation is an identity, not a slogan. It is NOT sign multiplication in {-1,0,+1}, which gives NEG × NEG = POS: a refused gate and a refuted outcome certifying as true. Two invariants, both enforced by shape: 1. verdict == law.factor × evidence.factor. The 9 branches are GENERATED from min() rather than hand-typed, so the schema cannot disagree with the algebra by transcription error. 2. evidenceTier T1 is unavailable when either factor is 'declared'. T1 means instrumented; claiming it for a value no instrument produced is the same defect one level up. Under-claiming is always permitted. Ships conformance/lawful-verdict-vectors.json — the cross-language anti-drift device. Two implementations that each pass their own unit tests can still disagree with each other; only a shared vector set makes that detectable. Both Noetica (TypeScript) and prophet-platform libs/python/lawful-verdict test against this file, and the sealed example receipt is verified byte-identically by both canonicalisers. The validator asserts by REJECTION, 64 checks: all 9 cells accepted, all 18 wrong verdicts rejected, NEG × NEG = POS rejected by name, T1 rejected whenever a factor is declared, the example's seal recomputed, the vectors checked against min(), and 15 shape controls. Proven able to fail: admitting NEG × NEG = POS in the schema makes it exit 1. Two defects found and fixed while building it. The regex classes were over-escaped, so schemaVersion '0.1.0' failed its own pattern. Worse, the validator printed "all 9 cells accepted" as an unconditional print() and duly printed it while 9 of 9 cells were failing — a gate narrating success independently of its own result, the exact defect this contract exists to prevent, reproduced inside the checker for it. Every section line now derives OK/FAIL from the failure list. Stacked on rescue/mesh-action-registry (#216) because the Makefile target sits beside validate-mesh-action-registry; retargets cleanly to main once that lands. * schemas: bind each FACTOR to its record (invariants 3 & 4); pin the seal functions Adversarial review of the first revision found a hole that mattered more than either invariant it shipped with. The schema enforced verdict = law × evidence but NOTHING bound the factors to their own evidence, so all six of these were ACCEPTED: law.factor: POS alongside barCleared: false (the gate REFUSED) law.factor: POS alongside residual: [...] (undischarged) evidence.factor: POS alongside refuted: true evidence.factor: POS with no digests at all evidence.factor: POS alongside grounded: false evidence.factor: POS with grounded absent The product then faithfully multiplied the lie. Enforcing an invariant one level too high is indistinguishable from not enforcing it. INVARIANT 3: law.factor is a function of (barCleared, residual). INVARIANT 4: evidence.factor is a function of (digests, grounded, refuted). Both enforced BOTH ways, so a factor can be neither over- nor under-claimed: POS requires both well-formed digests AND grounded AND not refuted; NEG requires an actual refutation; everything else is ZERO. Verified exhaustively — 12 law and 24 evidence combinations, accepting only the derivable factor — plus the six attacks asserted by name so a future edit that reopens one fails loudly instead of quietly dropping a count. Also pins the SEAL FUNCTIONS, after review on prophet-platform#1065 found two cross-language divergences that every receipt-shaped test had missed: canonicalJson Python's default ensure_ascii=True escapes "café" to "café" while JavaScript emits it raw, so every seal over non-ASCII content diverged. The cross-language seal test passed anyway because the example receipt is pure ASCII. contentHash Python hashed raw bytes; TypeScript hashes canonicalJson(s), the QUOTED JSON encoding. They disagreed on EVERY input. Nothing caught it because the schema only checks that a digest is well-formed, and a wrong digest is still well-formed. New `canonicalJson` (6 cases: sorted keys, recursive, accented text, CJK, astral-plane emoji, bare string) and `contentHash` (5 cases incl. empty string, non-ASCII, embedded newlines and JSON metacharacters) vector sections make the functions themselves conformance-checked rather than merely their output shape. The validator asserts the vectors against its own canonicaliser AND requires at least one non-ASCII case, so the trap cannot be silently removed. TypeScript is canonical by incumbency: it sealed the production ledger. Two fixes to the validator itself. with_factors now produces DERIVABLE factor bodies — without that, invariants 3/4 reject every product case and all 27 pass vacuously, the same trap as comparing two empty result sets and calling them identical. And its own canonical_json needed ensure_ascii=False, or it could not check the very cases it ships. 64 checks → 118. make validate green.
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.
Apple's
AppIntentssurface is system-wide by default: any app can register intents any other app can invoke, all queryable via oneAppIntentSchemas.sqlite. This spec gives the estate the equivalent SHAPE — without the ambient trust.What lands
schemas/MeshActionRegistry.json— signed, digest-pinned registry of participantsexamples/mesh-action-registry.example.json— 2-participant referencetools/validate_mesh_action_registry.py— validator, 3 positive + 12 negativeMakefilewired viavalidate-mesh-action-registry.github/workflows/mesh-action-registry.yml— dedicated CIDiscovery, not shared execution
The mesh registry is a catalogue of pointers, not an executor:
.sourceos/manifest.json(SourceOSRepoManifest, hardened this session to reject emptypolicyClasses).ActionCatalogEntryschema (PR #572), typed with theValueTypealgebra and carryingconsentRequired+capabilityRef.catalogRefis pinned bycatalogDigest— a swap of the remote catalogue is detectable, not silently trusted.registryDigestfor tampering detection.verbOntologyRefis optional per-participant (see Noetica'sverb-ontology.json, PR #573); absent means the participant doesn't classify by verb, a weaker claim.Execution stays governed by the origin app's own capability-ledger and consent gates. A consumer walking the mesh sees what actions exist and their type shape, but each invocation still passes through the origin's local gates.
Where this beats Apple
consentRequiredper-action from ActionCatalogEntrypolicyClasseson referenced manifestVerification
15/15 validator cases pass:
manifestRef, missingcatalogDigest, missingsignature.algorithm, unknown algorithm, wrong repo shape, non-date-timegeneratedAt, wrong type discriminator, wrong URN namespace, undeclared field on a participantDate-time is enforced with a pattern because jsonschema treats
formatas annotation-only by default — same lesson as the semantic-proof fix earlier this session.Follow-up (not this PR)