PMU Visa of Competence — Cross-Lattice Interlock Protocol

PRO-M iter-2 spec. The contract for how two problem-space lattices compose at the coordinate level — turning verified competence into a portable passport for marketplace matching. Answers the Goldilocks threshold question explicitly.
GIST. Two lattices share canonical axes ⇒ same coordinate space ⇒ no translation matrix. A lit-cell pattern from lattice A is already comparable to a needed-cell pattern from lattice B cell-by-cell. The match function is mass-weighted hamming distance with a per-job tolerance radius. Constant-time per (worker, job) pair. No LLM in the match path.

1. The compatibility check — when two lattices CAN compose

Two lattices A and B are compatible iff:

  1. Same canonical axes at every depth. The 12 length-1/2 axes (A,B,C,A1,A2,A3,B1,B2,B3,C1,C2,C3) are non-negotiable — they're the patent v20 §16 ShortLex backbone. Any deeper expansion (depth 3+) uses the position-parity rule: length-N axes match by canonical ID, not by per-problem-space label.
  2. Same address-shape branching factor at every depth. Both lattices use the demo-default 3 letters / 3 digits, OR both use the same alternative branching factor (architecture supports arbitrary).
  3. Optional but desirable: each lattice publishes its per-axis mass (gzip-compressed bytes of axis tile dump). The match score can be mass-weighted; without mass, fall back to uniform-weight hamming.
Compatibility is checked once at publication time, not per match. The bridge stores a canonical-fingerprint per lattice (hash of axes at each depth + branching factor). Two lattices with identical canonical-fingerprints are interlock-ready by construction.

2. Lit-cell pattern — what a worker publishes

A worker (agent or human) runs the decomposition pipeline on their own corpus (resume, portfolio, work history). The output is a 144-cell binary pattern: which cells are COMPETENT (lit) given their intent (what they offer) vs reality (what they've demonstrably done).

{
  "lattice_fingerprint": "sha256 of canonical axes + branching factor",
  "publisher": "did:agent:0xA1B2…",
  "issued_at": "2026-05-24T22:00:00Z",
  "lit_cells": {
    "A1:A1": 1, "A1:B2": 1, "B2:B2": 1, "B2:C3": 1, …
  },
  "per_cell_mass": {
    "A1:A1": 8242,  // gzip-bytes of the cell's intent text
    …
  },
  "signature": "ECDSA signature over lit_cells + per_cell_mass"
}

3. Needed-cell pattern — what a job publishes

A job posting (or any task) decomposes the same way. The output is a needed-cell pattern with an explicit threshold per cell: "the worker MUST have this cell lit" (hard) vs "it would help" (soft), and a global tolerance radius.

{
  "lattice_fingerprint": "must match worker's fingerprint",
  "issuer": "did:org:0xC3D4…",
  "issued_at": "2026-05-24T22:00:00Z",
  "needed_cells": {
    "A1:A1": "hard",    // must be lit
    "A1:B2": "hard",
    "B2:B2": "soft",    // adds match score but not gating
    "B2:C3": "soft",
    "C3:C3": "soft"
  },
  "tolerance_radius": 0.20,  // see Goldilocks section below
  "signature": "ECDSA signature over needed_cells + tolerance"
}

4. The Goldilocks question — answered explicitly

The user flagged this as the "one hidden trap": "If a job requires 15 specific cells to be lit, does the worker need a 100% bitwise match, or is an 80% overlap sufficient for a Visa?"

The hybrid answer (this spec's choice): hard-required cells are 100% bitwise; soft cells contribute to a mass-weighted score that must clear the issuer's tolerance radius. Two dials, both per-job.

4.1 Hard cells — exact lock, no tolerance

Every cell marked "hard" in needed_cells MUST be lit on the worker's pattern. Zero tolerance. If any hard cell is unlit, the match returns { visa: false, reason: "hard cell unlit", missing: ["A1:A1"] } regardless of how high the soft score is.

4.2 Soft cells — mass-weighted overlap, tolerance radius

For cells marked "soft", the match score is the mass-weighted fraction of soft cells that are lit on the worker's pattern:

soft_score = Σ (worker_lit[c] · max(worker_mass[c], 1))  for c in soft cells
              ─────────────────────────────────────────
             Σ max(needed_mass[c], 1)                    for c in soft cells

The worker passes iff (1 - soft_score) ≤ tolerance_radius. A tolerance_radius of 0.00 demands 100% overlap; 0.20 allows up to 20% missing weight; 0.50 allows half.

4.3 Why mass-weighted, not bit-counted

A cell with 8 KiB of gzip-compressed mass carries more semantic payload than one with 800 bytes. A worker missing the 800-byte cell is a lighter miss than missing the 8 KiB one. Mass-weighted overlap prices the semantic loss proportionally — same principle as risk-weighted assets in the underwriter spec.

4.4 Issuer-set, not protocol-set

Both the hard/soft labels AND the tolerance_radius are set by the issuer, not by the protocol. The bridge does not enforce a fixed threshold; different jobs in different markets pick different tolerances. The Goldilocks zone is the issuer's calibration, not the spec's prescription.

5. The match function — one constant-time call

function matchVisa(worker_pattern, job_pattern):
  if worker_pattern.lattice_fingerprint != job_pattern.lattice_fingerprint:
    return { visa: false, reason: "incompatible lattice" }

  for c in job_pattern.needed_cells where label == "hard":
    if not worker_pattern.lit_cells[c]:
      return { visa: false, reason: "hard cell unlit", missing: [c] }

  soft_cells   = [c for c in job_pattern.needed_cells where label == "soft"]
  weighted_hit = Σ (lit_cells[c] · max(per_cell_mass[c], 1)) for c in soft_cells
  weighted_max = Σ max(per_cell_mass[c], 1)                   for c in soft_cells
  soft_score   = weighted_hit / weighted_max  (or 1 if soft_cells empty)

  if (1 - soft_score) > job_pattern.tolerance_radius:
    return { visa: false, reason: "soft tolerance exceeded",
             soft_score, tolerance: job_pattern.tolerance_radius }

  return { visa: true, soft_score, hard_passed: true }

Constant-time per (worker, job) pair: O(|needed_cells|), bounded by 144 at depth 2, by 9N² at recursive depth N. AC⁰ on the verifier because there is no model, no nearest-neighbor search, no embedding distance — just bit lookup + multiply-accumulate.

6. Cloud bridge state ≡ local lattice state

The user's load-bearing question: "how to compare the cloud map state to the local map state?" Answer: they ARE the same state.

This is why position IS meaning: the cell coordinate is the universal address. Cloud and local share the address; therefore they share the meaning. No translation layer, no consensus protocol on "what does cell A1:B2 mean" — the cell IS what it means.

7. Iteration risk assessment

The user asked: "is this going to work… without many iterations?" Honest scorecard:

ComponentIteration riskWhy
Compatibility check (§1)Low Pure hash comparison. Either both lattices use canonical axes or they don't. No subjective judgment.
Lit-cell publishing (§2)Low The decompose.mjs pipeline already produces this exact shape. Add signature + fingerprint, ship.
Needed-cell publishing (§3)Medium Hard vs soft labeling is a per-issuer judgment. Will need iteration on what's "hard" in a real job posting — expect 2-3 rounds with first design-partner.
Goldilocks threshold (§4)Medium The 0.20 default is a guess. Will need empirical calibration per market (federal-AI vs healthcare vs voice-agents). Expect per-issuer tuning, not a universal default.
Match function (§5)Low ~30 lines, constant-time, no LLM. Implementation is mechanical.
Cloud=local equivalence (§6)Low (by design) The architecture forces them to be the same state. No state- synchronization protocol needed.
Total iteration estimate: 1-2 iterations on the protocol itself (§§1-3, 5, 6), 3-5 iterations on per-market threshold calibration (§4). The protocol is decidable; the threshold is empirical.

8. What this spec doesn't cover (yet)

PRO-M iter-2 · drafted 2026-05-24 · sibling to PRO-L iter-3.x decomposition pipeline. Next: PRO-M iter-3 wires this into bridge-receive.mjs as a real cross-lattice runner; iter-4 ships a marketplace demo page.