Repository navigation
fix(spec): profile registration holds under post-load realm mutation (#384 follow-up, steward #5186) - #520
Draft
LamaSu wants to merge 42 commits into
Draft
fix(spec): profile registration holds under post-load realm mutation (#384 follow-up, steward #5186)#520LamaSu wants to merge 42 commits into
LamaSu wants to merge 42 commits into
Conversation
…load realm mutation (steward #5186) AGENT: implementer-admission-r9 #363 round 9: the realm-mutation class of astra packs 162-171, across admission's trust path. Reproduced first at 45f2c1f with a patch harness: 51 of 88 scenarios changed an acceptance, a digest, a term list or a level (e.g. Array.prototype.push replaced: every reject admitted; Object.prototype .passed/.value/.profileObservation written: excluded observations counted; DECIMAL_VALUE_PATTERN and TAGGED_DIGEST_PATTERN recompiled: a non-decimal value and a non-digest sampleId admitted; "value" in descriptor with Object.prototype.value written: an accessor pin and an accessor signature leg read as data, a left-out failure bundle admitted; Promise.prototype.then forging computeBundleSetDigest's and binding's returned promises; an Object.prototype.then taking over admission's result; constructor+then forging a leg's false). After: 0 of 88. - profile-admission.ts and evidence-level.ts call only util/primordials.ts captures, plain loops and operators (source scans enforce it). Exported lists are frozen where defined; sets are stringSet records built at load, the vocabulary's active primitive ids included (EVIDENCE_PRIMITIVES defs are exported, mutable objects; getPrimitive answers from a Map). - DECIMAL_VALUE_PATTERN becomes isDecimalValue, a code-unit predicate equal to the old pattern on edge cases and 25,000 generated strings; isTaggedSha256 (primordials) replaces signing-preimage's RegExp here. - codeInData and the input boundary use own-property descriptor reads (hasOwn), captured Reflect.ownKeys and an ancestor list: no in, for...of or Set. Everything evaluated is a null-prototype plain-data copy. - computeBundleSetDigest hashes with node:crypto's SHA-256, methods captured at load (as measurement-profile does); the goldens are unchanged. - The binding leg (subject-binding.ts, the evidence lane's) still hashes through ambient methods, so its answer is re-verified with the captured hash and own reads before anything is evaluated; the re-check only refuses more. - Promises: awaited and returned promises get their own constructor and then (ownPromise); a leg's promise is followed through the then captured at load (fulfillsWithTrue); the result has a null prototype. - evidence-level reads own data only; isFabricated is asked about a null- prototype view of the event's own source and payload. Harness: profile-admission-intrinsics.test.ts runs every realm mutation in a child realm (harness/admission-realm.ts via node --import tsx). Held in vitest's own realm, a replaced Array.prototype[Symbol.iterator] stalls vitest itself (a 300 ms timer test with no @pcc/spec code never completes), which is what hung the first run; it was never our code.
…e and the delivered result, and serves an array hole AGENT: implementer-admission-r9 Rows that exercise every line of the round-9 fix, so the mutation check can kill a regression in each. All reproduce at 45f2c1f (53 of 91 scenarios violate there; 0 here): - Object.prototype.then turning binding's failure into ok with the presented events: evidence from another job, kernel, payload kernel, settlement unit, challenge or output admitted at 45f2c1f; the re-check refuses each now. - Promise.prototype.constructor and then together: a sync leg's false, an async leg's false, the set digest, and the result read through .then. - Array.prototype[1] holding a digest: computeBundleSetDigest read a hole. - Object.prototype.value = true, and evidence-level accessors that must never run (a verdict, a simulated flag). - The codeInData recipe must be identical, not merely a refusal: plainDataCopy would still refuse the accessor, so only the reason shows codeInData read the descriptor's own value. - A cycle anywhere in the input resolves to a reject, never a rejected promise.
…ta hold, and legs that answer truthy but not true AGENT: implementer-admission-r9 Two cases, so that the mutation check can kill a widened pin check (it would turn the reject into a hold) and a leg answer read as truthy instead of exactly true, synchronously or from a promise.
…istration-hardening
…(steward #5186) AGENT: implementer-admission-r9 #384 round 9, after merging #363's hardening (feat/sensors-admission- hardening). Reproduced first at 4a25a42 with a child-realm patch harness: with Array.prototype.push replaced after load, every refused request (device or capability mismatch, an unverifiable term, a client digest that is not the profile's) became registrable; with the array iterator or %ArrayIteratorPrototype%.next replaced, every unverifiable term was dropped and the profile registered. Array.prototype.map and JSON.stringify changed only the problems of a refusal. checkProfileRegistration now calls only util/primordials.ts captures (append, mapList, newList, JSONStringify), plain loops and operators; a source scan enforces it. It reads its null-prototype plainDataCopy only, as before. The header states the post-load property and the pre-load boundary. Harness: profile-registration-intrinsics.test.ts and harness/registration-realm.ts, one child realm per scenario (38 scenarios, 23 requests): every result identical, or a refusal (ok: false).
…w starts and between two window ends AGENT: implementer-admission-r9 The mutation check found that swapping the capture window's bounds (opening at the latest start, closing at the earliest end) survived: every window case placed the observation outside both candidate bounds. These two admit cases place it between them, so earliest-start and latest-end decide the outcome.
…istration-hardening
…prototype for an ordinary profile's AGENT: implementer-admission-r9 The #384 mutation check found that an inherited read of measurement.tolerance in unverifiableProfileTerms survived: registration and admission pass null-prototype copies, and an inherited term only adds a refusal. The function is exported and its header claims own-data reads, so this recipe holds it to that claim: with Object.prototype.tolerance and maxIntervalMs written, the terms of every ordinary-object profile are identical, not merely more.
…istration-hardening
…th a species pinned at load (astra pack 187) Native then (and catch and finally, which go through it) builds the promise it returns with SpeciesConstructor(promise), that is promise.constructor[Symbol.species]. ownPromise pinned `constructor` to the global Promise, whose species is a configurable accessor: code running after load could point it at a constructor whose "promise" is a forged thenable, and `await profileAdmitsBundle(signatureFails).then((x) => x)` received an admit. Reproduced at 6d401f3 for profileAdmitsBundle, computeBundleSetDigest (a rejected digest became a value too) and fulfillsWithTrue, through then, then.then, then(undefined, r), catch and finally; and two siblings: a derived promise awaited with Promise.prototype.then and .constructor replaced, and a chain's second link with then replaced. - ownPromise's own `constructor` is PINNED_CONSTRUCTOR: a frozen record with no prototype whose one property, [Symbol.species], is PinnedSpecies, a frozen constructor that makes a native promise with the Promise captured at load and passes it through ownPromise. Every derived promise is pinned too, at any depth. The record is shared and reachable as promise.constructor, so it is frozen: no caller can re-point it. - awaitedHere keeps admission's await of the binding leg's answer on the engine's internal path (own constructor = %Promise%). An ownPromise now resolves `await` through its `then`, which would look `then` up on binding's ordinary-object answer. - Tests: astra's recipe verbatim; every consumption shape; the shared constructor; the then-and-constructor sibling; a guard that admission's await adds no lookup on binding's answer. Realm harness: a Promise[Symbol.species] patch row, astra's recipe, and delivery rows in every scenario that use and await the promise then, catch and finally return (viaThen discarded it).
…ed at load (astra pack 187) A caller's `promise.catch(...)` or `promise.finally(...)` looked the method up on Promise.prototype, where code running after load can replace it with one that answers anything. Checked with pack 187's species finding and reproduced, at 6d401f3 and still after the species pin (296b0da): admission's rejection delivered as an admit, the set digest as another digest, and a rejected digest (the empty set) as a value, through either method. ownPromise now also gives the promise own, fixed `catch` and `finally`: Promise.prototype's, as they were at load. The native catch calls the own `then`, and the native finally builds its promises with the pinned species, so both deliver what the promise settled with. Tests: catch and finally replaced after load, in process; the realm harness's Promise.prototype.catch and Promise.prototype.finally patch rows, whose forgers run the original method and forge what its promise delivers.
The record's species was fixed twice: a non-writable, non-configurable property, and the record frozen. Either alone holds it, so removing either could not be caught by any test. Now the species is a plain own property of the null-prototype record and the freeze is what fixes it; a test fails without it (a caller holding any returned promise re-points the species).
…a caller's own callback result is outside the boundary
…which follows #363 profile-admission.test.ts auto-merged with #363's probe adaptation to #341's binding. One more adaptation to #341: in 'admission's own await of binding's answer looks nothing up on it', the binding now answers with a frozen null-prototype object, so no inherited then is looked up at all (0, was 1 when its own promise resolved). The test asserts that, and pins why: the answer has no prototype and is frozen. A polluted Object.prototype.then cannot reach admission through it. spec green, tsc clean.
's final file) Evidence #6440 asked sensors to own this port: #519's 550b212 pattern, applied to #345's final evidence-level.ts. On master, code that runs after @pcc/spec loads could change its answers. With Array.prototype.indexOf replaced, meetsEvidenceLevel("submitted", "inspected_output") was true. The new harness, run against master's file, changes answers in 31 of its 68 checks, escalations included. - It calls only intrinsics captured at load (util/primordials.ts, which is #519's reviewed version, verbatim), index loops and operators. No method looked up at call time, no iterator protocol, spread, in or RegExp, at load or at call time. - The sets and the pinned-verdict table are frozen null-prototype records, and the exported lists are frozen. - The principal-id RegExp is a code-unit predicate, held equal to the pattern. - The pinned verdict field must hold an own value. - Rule 7 still reads each field once, but only OWN properties count. An own accessor still runs once. payload.mock keeps isFabricated's read, which E5's trap test pins; written on Object.prototype it can only refuse. Tests: E5's evidence-level.test.ts is unchanged, 128 of 128. The new evidence-level-intrinsics.test.ts runs 61 child-realm scenarios over 141 answers, plus source scans, frozen exports, RegExp reachability and the predicate's equivalence. Mutations: 26 of 26 killed. Spec: 2476 of 2476.
…l CI (steward #6515)
…own fields of objects only (pack 267) Pack 267 (DO-NOT-SHIP) found 2 HIGH in #577. Both were reproduced at e13a395 by new realm scenarios; 6 scenarios changed answers. - An inherited payload.mock was still read, through isFabricated, BEFORE source.deviceId. An Object.prototype.mock getter could write the event's deviceId and answer false, and an unattributed completion then proved device_reported (an up-tier). A throwing getter turned a classification into a refusal. - A primitive source or payload was passed to isFabricated as it was. Its read boxed the primitive and consulted Number.prototype, String.prototype and Object.prototype: a written `simulated` made a bundle fabricated and erased its completion-and-failure contradiction. isFabricated now receives null-prototype snapshots of source.simulated and payload.mock. Each is read once as an OWN property, and is undefined for a non-object. E5's trap test now expects the own-only read of mock (getOwnPropertyDescriptor:mock, no get:mock). Its inherited-mock case is now an explicit assertion: canonicalize hashes own fields only, so verified evidence never carries an inherited marker. The harness gains primitive-source and primitive-payload items and four scenarios: a mock getter that writes deviceId, a throwing mock getter, Number.prototype.simulated and String.prototype.mock. The test's mock exception is gone, so every scenario must be identical. Mutations: 28 of 28 killed. E5 128 of 128; realm tests 73.
The fullwidth and Arabic-Indic digits in the principal-id cases were literal characters in the source. As escapes, a reviewer sees them for what they are. No behaviour changes.
…to the realm-hardened admission #363 moved profile admission to #345's one rule (steward #6478). The deal's executorTrustDomains are an input. The signature leg answers { trustDomain }. Levels and contradictions run over authenticated bundles, each event counts at its LOWEST level, and inspections pass by the pinned verdict field (inspectionVerdict). #519 had made admission hold under post-load realm mutation. This merge carries the migration into #519's hardened file, so both hold. Conflicts and how each was resolved: - evidence-level.ts: #577's file (426be44), the realm port of #345's final file. It already freezes EVIDENCE_LEVELS, #363's only change to that file, and master has not touched the file since #577 forked. E5's evidence-level.test.ts is #577's too, for the two expectations #577 changed. - profile-admission.ts: #519's hardened file with the migration applied in its style: - executorTrustDomains is read as data with the other fields (INPUT_FIELDS, DATA_FIELDS, plainDataCopy) and checked by isOperatorPrincipalId. That is a code-unit predicate equal to principal-id.ts parseOperatorPrincipalId, with no RegExp; it is exported, and a test holds the two equal on edge cases and 20,000 near-misses. - The signature leg's answer is read once from its own data (signerOf) into a frozen null-prototype record. A proxy, an own then, an accessor or a malformed domain fails it. - A native promise is followed by the then captured at load (followedPromise, new in util/primordials.ts), and its value is read inside the handler. - A promised answer must have no prototype (signedBy, exported). Resolving an ordinary object looks then up on Object.prototype, where code running after load could substitute the answer. A synchronous plain answer is still accepted, since nothing resolves it. - Levels and contradictions run over null-prototype AuthenticatedBundles. The lowest level per event hash is kept in a null-prototype record. reached is the highest of those. - Tests: - #363's two-operator world, with the auto-merged helper's duplicated executorTrustDomains removed. - #519's pack-187 inputs now carry executorTrustDomains, and the binding-lookup test uses the two-bundle pilot. - New cases cover the signature leg under a then getter planted on Object.prototype, signedBy, a promised ordinary record, a thenable or proxy answer, and the executors refused at the input boundary (no leg runs). - The realm harness: - It moves to the two-operator world (the printer in A's bundle, every other device in B's) and the new leg contract. - It gains cases for the new leg (a signedBy async leg, a promised ordinary record, a bare true, a null domain, the executor's own camera, no or malformed executors, a thenable). - Its evidence-level items are dropped: #577's harness (evidence-level-realm.ts) holds the levels under realm mutation. Spec 2676 of 2676, admission 249 of 249, tsc clean. Mutations: 19 of 20 killed. The survivor, "null copy not lowest", is equivalent, as in pack 271: copies of one event differ in level only when a bundle is fabricated, and admission refuses fabricated events first.
…519 #519 follows #577 for evidence-level.ts: the previous merge already took #577's file and E5's two changed expectations. This merge brings #577's history and its realm tests: evidence-level-intrinsics.test.ts and harness/evidence-level-realm.ts. One conflict, util/primordials.ts. #577's copy is #519's file verbatim (3b27e8a's blob), so #519's side is the resolution: that file plus followedPromise. Spec 2749 of 2749 (81 files); tsc clean.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 06:16 — with
GitHub Actions
Failure
…ardened admission #363 now takes trust domains from ONE pinned signer snapshot (signerTrustDomains, signer to operator, or null), and its leg answers the VERIFIED signer's id. That id must equal the bundle's declared kernelSignature.signer, so one key has one domain (astra pack 271). This merge carries that into #519's hardened file. profile-admission.ts was the one conflict, resolved by porting the fix in #519's style: - signerTrustDomains is read as data with the other fields (INPUT_FIELDS, DATA_FIELDS, the code walk, plainDataCopy). Its rows go into a frozen null-prototype record, checked by isSignerId (new: a code-unit predicate equal to ^0x[0-9a-f]{40}$, exported and tested on edge cases and 20,000 near-misses) and isOperatorPrincipalId. - A signer listed twice is refused. - The leg's answer is a signer id, a string. A native promise is followed through the then captured at load (followedPromise), and a string resolves with no then lookup, so signedBy, signerOf and the null-prototype rule for promised records are gone. - The answer must equal the declared signer, read from admission's null-prototype copy of the bundle, and the snapshot must hold it. Tests: - #363's new cases arrive through the merge. - #519's signer describe now covers: a promised signer id followed; a thenable refused; signerTrustDomains refused at the input boundary before any leg runs; signerTrustDomains walked for code. - The two pack-187 inputs get the snapshot. - The realm harness takes the snapshot and the signer-id leg, with cases for: an async signer id; true; a domain record; another signer than declared; a null-domain snapshot (device_reported admits, inspected rejects); one key for both bundles; a missing signer; a duplicate row; a malformed row; a thenable. - The intrinsics test adds isSignerId's agreement. Spec 2755/2755 (82 files), admission 255/255, tsc clean. Mutations: 17 of 19 killed. Both survivors are equivalent: - the leg answer's format check: a malformed answer either differs from the declared signer or is not in the snapshot, which holds only checked ids; - "null copy not lowest", as in pack 271.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 07:04 — with
GitHub Actions
Failure
…ned admission #363's pinned key registry (astra pack 275) and the steward's max-over-occurrences ruling (#6623), ported into #519's admission: - registryKeys and pinnedRegistryDigest are read as data at the input boundary and walked for code; rows go into null-prototype records, checked by isRegistryKey (a code-unit predicate equal to ^0x[0-9a-f]{64}$), unique by key and by signer id; - the registry digest is the captured SHA-256 over canonicalize({domain, keys}); - each bundle's Ed25519 signature is checked here, synchronously, through node:crypto's verify as captured at load, with its key options in a null-prototype record. No promise is on the signature path: Web Crypto resolves importKey with a CryptoKey object, and that resolution looks `then` up on Object.prototype (the realm harness reproduced a forged admit through an awaited helper first); - the signer leg, its snapshot, isSignerId and followedPromise are gone; - the per-event level is the max over occurrences, and reached is #345's. The realm harness moves to the registry (seeded keys, registry cases, node:crypto and options-pollution scenarios); the intrinsics test checks isRegistryKey.
…ned admission #363's signer grants (astra packs 281, 285, 287; steward DECISIONS 00:26 and #6694), ported into #519's admission: - registry rows carry grants { role: executor | witness, kernelId, jobId? }, and session-key rows { publicKey, delegatedBy, authorization } are rooted in a registered row. They are read through own descriptors and checked with Reflect.ownKeys as captured at load, and become frozen null-prototype records. Grants are sorted by a JSON-quoted identity that orders as #363's does, so the v2 digest is byte-identical; - the delegation's root signature is checked synchronously through node:crypto's verify, captured at load, over the LO-EV-1 delegation preimage rebuilt here: - a fixed key order; - strings quoted by JSON.stringify as captured at load; - safe-integer numbers; - lists sorted by code unit; - lowercase key hex; - UTF-8 encoded code unit by code unit; - authorization after binding: a grant must name the subject, a witness signs inspections only, and a session key counts within its scope and window. The window uses Date.parse as captured at load and floors whole seconds by arithmetic; - the executor set for #345's levels is completed by executor-role operators, and no-witness-authorized names an inspected_output shortfall with no witness granted; - computeRegistryDigest reads a plain-data copy. The realm harness gets the round-13 cases with seeded session keys and new scenarios: grant-field pollution, plus JSON.stringify and Date.parse (IDENTICAL-strict), Math.floor and toLowerCase. New tests show the rebuilt delegation bytes equal LO-EV-1's: unicode, a lone surrogate, unsorted lists, either-case hex, a derivation path. Pack 287's LOW (a test comment that did not match its assertion) is reworded here as the tracked follow-up.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 08:19 — with
GitHub Actions
Failure
…rimordial, checked by type (astra pack 289) Pack 289's HIGH, reproduced at dfb73b2. lowerHex() read bytes.length, the accessor %TypedArray%.prototype inherits, so code that replaced it after load could empty the publicKey in the LO-EV-1 delegation preimage. A root's signature over a keyless delegation then authorized the row's full session key: a clean realm rejected the input, and with the getter replaced it was admitted at inspected_output. - lowerHex iterates the caller's count (32 bytes for a key) and reads no length. - A TYPE-AWARE closed allowlist (steward DECISIONS 01:30; the TypeScript checker): on a value whose static type is a built-in (typed arrays, ArrayBuffer, DataView, Map, Set, Promise, RegExp, Date, Error, functions, arrays, strings, numbers), only a numeric element access, or the own length of an array or a string, is allowed. Every other read or call fails. It covers profile-admission.ts and the functions admission calls (evidence-level, subject-binding, measurement-profile, plain-data, canonicalize, isFabricated). A self-test plants 14 violations. A diagnostics control rules out silent `any`, and the check reports the original bytes.length. - The realm harness gets astra's attack as a case, a delegation whose root signed a keyless preimage, and a %TypedArray%.prototype length / byteLength / byteOffset scenario. That scenario and every other scenario added with the signature and registry rounds are now IDENTICAL-strict. An admission test is the clean-realm control: the keyless signature is refused.
…ver, which could be any built-in A closed allowlist cannot pass what it cannot classify: a receiver whose type the checker reports as `any` is refused like a built-in. The trusted path has none; the self-test plants one.
…lement read is proven own (astra pack 291) Astra pack 291 (SHIP-WITH-FIXES, two MEDIUMs on the checker) and steward #6792. The check moves to builtin-reads-check.ts and walks EVERY node that runs after load on admission's trusted path. A node passes only as one of the forms the file names, under that form's condition; any other syntax is reported. MEDIUM 1, numeric indexes: - A numeric index passes unconditionally only on a typed array. - An array, string or record element must be proven own, with nothing running between the check and the read: `hasOwn(x, k) ? x[k]`, `hasOwn(x, k) && x[k]`, or `if (hasOwn(x, k))` whose branch begins with the read. Other options are a string index inside a canonical `for (i < s.length; i++)` loop, or a key of a trusted `ObjectCreate(null)` record. - An element key must be a string, number or symbol. - The fixture refuses a hole, an out-of-range string index, an unbounded index, a guard with a call in between, and a guard on another receiver. - A runtime control shows that `new Array(1)[0]` and `"ab"[5]` do read a written prototype, while listAt and charAt do not. - A new identical-strict realm scenario writes Array.prototype[0..63] and String.prototype[0..255]. MEDIUM 2, closure: - Default-deny covers object destructuring, `in`, a namespace import read by any spelling, and built-ins reached through a subclass, an intersection or a type-parameter constraint. - hasOwn and ObjectCreate count only when declared in a primordials file, so a look-alike is refused. - `x.length = n` is allowed only on an array, with a primitive value. Production changes (mechanical; behavior unchanged for data): - Array and string element reads go through listAt and charAt. - readRegistry builds fresh null-prototype rows instead of writing into earlier ones. - The error classes define `name` rather than assigning it. - canonicalize reads an own key through hasOwn. - This touches subject-binding.ts, measurement-profile.ts, evidence-level.ts, canonical.ts and plain-data.ts on admission's path. Evidence: - The checker reports nothing on the trusted modules. - Six mutations of the new rules are each caught by their fixture line. - spec: 83 files and 2788 tests pass; tsc is clean.
…d is no proof at the read (astra pack 293) Astra pack 293 (SHIP-WITH-FIXES): MEDIUM 2 closed, MEDIUM 1 open on one allowance. The canonical string-loop rule, provenInRange, compared identifiers by spelling. It also allowed statements between the loop's bound and the read. Reproduced at 96d4ce3, where each of these was unreported: - astra's shadowedRange (`const s = ""` inside the loop); - a shadowed index; - a string reassigned through a call inside the loop. At runtime, with String.prototype[0] written, the shadowed read returns the written value. The fix is astra's first option: the allowance is removed. A string element is read only through the captured charAt, and admission's path already does so (the check still reports nothing there). The self-test: - its allowed string loop uses charAt; - canonicalLoop, shadowedRange, shadowedIndex and reassignedByCall are refused lines; - it now lists every misclassified line in one assertion. Run against the 96d4ce3 check, it names all four new negatives. spec: 83 files and 2788 tests pass; tsc is clean.
…file admission captures at load build-dashboard has failed on #519 since admission began taking node:crypto's one-shot verify at load (astra pack 275): "verify" is not exported by "src/lib/node-crypto-shim.ts", imported by "../../packages/spec/dist/evidence/profile-admission.js" Reproduced locally at 7f3067e with vite 5.4.21, using the dashboard's own aliases: spec's built profile-admission.js was bundled through node-crypto-shim.ts and node-util-shim.ts and failed with the same error. The shim now exports `verify`, which throws if anything calls it: - a browser has no synchronous Ed25519 check; - the dashboard bundles admission but never admits a bundle; - admission only captures the binding at load and never calls it there; - so a signature is never taken as verified in a browser. With the shim, the same vite build of all of spec's dist/index.js passes, with no missing export.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 11:03 — with
GitHub Actions
Failure
…tive closure (DECISIONS 04:06) Steward DECISIONS 04:06: the check's scope is the trusted entry points' transitive in-repo call closure, computed from the TypeScript program, with no hand list. #519 adopts it before it merges; #578 uses the same check, kept identical below the header. - builtin-reads-check.ts: - trustedClosure computes every in-repo declaration the seed files reach. It follows ANY reference (a call, or a function passed, stored or captured as a value) through imports and re-exports, but not an import alone or a name used only in a type. - closureReads checks each reached declaration in place: what runs at load stays exempt. - A collaborator named with its reason is not entered, and the list is closed: an unused entry fails. - programOfFiles builds the multi-file self-test. - The test seeds profile-admission.ts. The closure reaches 165 declarations: primordials 56, evidence-level 45, plain-data 25, measurement-profile 20, subject-binding 13, evidence 2, primitives, is-fabricated, canonical and admission 1 each. It reports none. - The reach is asserted, so it is not vacuous. - A two-file self-test shows the closure follows a call and a stored function, skips an import named only in a type, and stops at a named collaborator. - primordials.ts: listAt and charAt move beside hasOwn, and every helper reads elements through listAt. These shared-region lines are byte-identical to #578's (75f1c81), so the branches merge cleanly. Mutations: - the closure following nothing: 2 tests fail; - a live JSON lookup planted in ownPromise, which only the closure reaches: reported. spec: 83 files and 2788 tests pass; tsc is clean.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 11:24 — with
GitHub Actions
Failure
…ptures, and its trusted path is default-deny checked (astra pack 188) Astra pack 188's tracked MEDIUM, reproduced at a3ee228: the registration harness's 38 scenarios lacked Number.isInteger, Object.create, Object.defineProperty, Object.is, Object.prototype.hasOwnProperty and Reflect.apply. Registration reaches all of them through validation and plainDataCopy. - 19 recipes added (57 scenarios). Besides the six above: Date.parse, String.prototype.charAt, Set.prototype.add, Function.prototype.bind, the Promise prototype methods and Promise[Symbol.species], TypeError, Error, Proxy, structuredClone and Uint8Array. - A fixed inventory assertion: a scenario exists for every callable intrinsic captured by the files the registration closure reaches. Against the old harness it fails and names the missing recipes. - Registration's trusted path is checked by the computed closure (DECISIONS 04:06). It reaches plainDataCopy, validateMeasurementProfile, unverifiableProfileTerms, computeMeasurementProfileDigest and canonicalize. - The closure found one read: terms[i] in checkProfileRegistration, now listAt. Put back, it is reported. spec: 85 files and 2867 tests pass; tsc is clean.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 11:27 — with
GitHub Actions
Failure
…nce field runs after load, collaborators are exact (astra pack 303) Astra pack 303 (DO-NOT-SHIP) on the computed closure. All three findings were reproduced at dede88d. HIGH 1: the closure dropped function values supplied at run time. An entry point's `cb()` was neither reached nor reported, and admission's caller-supplied verifyPrimitiveInstance ran with zero collaborators reported. A call (and a `new`) now passes only when its TARGET is fixed, seen code: - a function, method or class declared in the repository; - a function written in place, or a const bound to one; - a const computed at load, unless it was taken from third-party code; - a parameter whose function is not an entry point (exported from a seed file, or a public member of an exported class), never escapes as a value, and gets seen code from every call in the scope; - the resolve and reject of an executor passed to the Promise captured at load. Anything else fails closed unless that exact call site is a named collaborator with its reason: a callback, a private field set at run time, a const read from data, a let, an unresolved import or third-party code. Admission names one: its PRIMITIVE leg, the caller's verifier, called only through legAnswer. evidence-level's verdict table now holds a tag rather than a reader function, so verdictOfPayload calls readBooleanVerdict or readPassFailVerdict by name (behavior unchanged). HIGH 2: an instance field's initializer in a class evaluated at load was exempt. It now runs as code after load; a computed name and a static initializer stay load-time. MEDIUM: collaborator keys used base names, and reasons were never checked. - Keys are now paths relative to CheckOptions.root, which is required. - Every entry needs a non-empty reason and must match exactly one declaration or call site. - When one declaration makes several calls of the same unseen callee, the call sites are numbered #n. - Unexplained, stale and ambiguous entries are reported. Tests: - The fixture refuses six new forms (a caller's callback, a const from data, a declared value, a let, a private field, an instance field) and allows a higher-order helper, a static field and a named collaborator. - A two-file self-test covers each finding. - Mutations of each rule are caught: targets always fixed (3 tests fail), no field deferral (2), reasons unchecked (1), base-name keys (3). spec: 83 files and 2789 tests pass; tsc is clean.
…hich follows #519 (a plain merge)
…rogram, and covers Array, Map, Promise, Set and Set.prototype.size (astra pack 305) Astra pack 305 (SHIP-WITH-FIXES), its MEDIUM reproduced at d5c19b8. The hand-written inventory missed captured callables: adding Array, Promise, Set and Set.prototype.size to it fails, naming all four. The inventory is no longer written by hand. capturedIntrinsics() computes it: - It covers the files the registration closure reaches. - Every outermost property chain rooted at a global (default library or @types/node) in a top-level const initializer, whose value can be called or constructed, is a capture. - So is every accessor captured through getOwnPropertyDescriptor(chain, "name"). - Roots resolve through top-level const aliases (FunctionPrototype is Function.prototype), and globalThis.X is X. - A new capture therefore joins the inventory without anyone listing it. What it found missing gets a recipe: Array, Map, Promise and Set (the globals replaced) and the Set.prototype.size getter. 62 scenarios in all, each run identical or refused. The test asserts the inventory holds pack 188's six and pack 305's four. Removing a recipe fails it, naming the recipe. Also: the registration closure check takes root (required since #519 @b61262ae) and names files from the repo root. spec: 85 files and 2873 tests pass; tsc is clean.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 12:00 — with
GitHub Actions
Failure
This branch had an error being deployed
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.
Follow-up to #384 (frozen at 64be17e), which it stacks on. It merges the #363 follow-up (feat/sensors-admission-hardening @6d401f3e, reviewed in its own PR). Its own change is one commit, ccdbc6f, for steward #5186: profile registration holds under post-load realm mutation.
Reproduced at 4a25a42 (#384, registration unmodified)
repro-384-at-4a25a424.txt: 4 failed / 36 passed; 30 violations.pushmade 13 refused requests registrable.%ArrayIteratorPrototype%.next, dropped every unverifiable term (8 each).mapandJSON.stringifychanged only the problems listed in a refusal.The fix (ccdbc6f)
profile-registration.tsuses the intrinsics captured at load and index loops, the same discipline as admission. The child-process harness (__tests__/harness/registration-realm.ts) runs 38 scenarios, with 0 violations.Checks (2453141)
tsc --noEmitclean.🤖 Generated with Claude Code