The rules
Each of these was paid for. They are stated as they were learned.
A borrowed method is a symptom; the state behind it is the cause
Extracting a function's logic into the kernel never moved the borrowed count. What moved it was making the state the function needed a capability the host constructs and hands to the kernel. Port the state, and the method follows.
A port on host data is smaller than a port on host behaviour
needsEncryption(eventType) handed in by the host is behaviour; reverseTableMap handed in is data, and the kernel applies the rule. Prefer the data port every time. Trading four behaviour ports for four data ports leaves the count unchanged and moves the rules into the kernel, which is the point.
Repackaging is forbidden
Collapsing six ports into one object reads as −5 and removes nothing. The count is of dependencies, not of properties.
Every declared port is travelled
A port the kernel declares and never uses is surface a new host implements for nothing — and gets wrong without finding out. peerEnclave was a tested, green capability reading a key nothing wrote; it survived because no app path travelled it. The contract gate refuses an untravelled port.
A missing port is undefined until the first branch that needs it
Ports typed any are not type errors. dmEpochSet was destructured by the ingest path and supplied by nobody for weeks; every branch that populated an epoch ladder threw inside a catch, and the warning sat in the browser log through every green run. Gate declared-vs-supplied mechanically.
A Cmd the host cannot route is reported, never dropped
performNow returns { ok: false, error: 'no pollNow port' }, and the dispatcher says so. The log gate then refuses the run.
A decision belongs on the Cmd, not behind a second round trip
A kernel that cannot hold a cipher has two ways to encrypt: ask the host and fold the answer into a second Cmd, or say which cipher, for whom as data on the first one and let the performer seal and send in one step. They are not equivalent. The app that this framework came from had shipped the bug the two-step re-opens: a group message sealed against the enclave the model named when the answer came back, and posted to the enclave the model named when the send went out — unreadable by every member of the group.
So a plan rides on the Cmd:
{ kind: 'submit',
target: { to: 'enclave', id },
encrypt: { scheme: 'sent', to: peer, text: 'hello' } }The family decides the scheme and the recipient — pure, testable without a key. The host holds the key material. The performer resolves the target once and hands it to the cipher, so the ciphertext and its destination cannot come apart.
A capability gate is the performer's refusal, taken away
if (!adapter?.submit) continue. if (sdk?.submit_to_peer) { … }. Each asks a
port whether a write would work — while the write is already a Cmd, and
performNow refuses an unroutable one by name. The guard's only effect is
to make sure that named refusal never happens.
Ten of these were found in one application. Six resolved an adapter to ask,
which is not free. The worst did not even skip: it fell through to another
path and wrote a Personal-bound event into whatever enclave happened to be
bound — and the test asserting that fall-through was asserting the misroute.
A gate refuses the whole class now: no family reads .submit off a port, and
none resolves an adapter by enclave id.
A gate with a floor on a number the work reduces will fail on success
Three guards in one codebase asserted > 0 raw adapter writes, > 40 declared
ports, and a minimum corpus size. Every one of them went red on a commit that
made things better, and a guard that fails on success is a guard somebody
deletes — taking its true half with it. Assert the property: that the parse
found names it must find, that a new raw write is refused. Never the count of
what you are removing.
A measure that reads only the entry point stops seeing what moves out of it
A port survey read the ports a family destructured inside its run function,
on the reasoning that a helper taking a ctx takes something else. True when
written; false the moment a carve started lifting work out of the runs into
helper(ctx: FamilyCtx, …). It then reported a port no family reads while
two families were calling it. Measure by the type, not by the shape of the
call site — or the next refactor hides its own cost.
A fix that only makes a symptom rarer is refuted
A longer timeout, a bounded retry on a refusal, a re-run until green — each hides the same defect a little better. If a spec has to be edited to pass, stop and report it.
Never claim a pass you did not run — and read the artifact before running again
A "starved" verdict is not evidence about the machine. Two pages at 97 % of their main threads were a read-receipt ping-pong the DOM never showed; a CPU profile of the page named it in one run after five diagnoses by re-running had not.
Batch the gate, mechanically
Iterate on the fast tier; pay the full node + browser pass once per coherent group. As prose this slipped six times in a day; as a certificate the pre-commit hook checks, it cannot. See Testing cadence.