Repository navigation
Conversation
LamaSu
added a commit
that referenced
this pull request
Sep 24, 2026
…head) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PGXt1epJimheWaESMGBbXc
LamaSu
added a commit
that referenced
this pull request
Sep 24, 2026
…nt head) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PGXt1epJimheWaESMGBbXc
LamaSu
added a commit
that referenced
this pull request
Sep 24, 2026
…spections as failures, pins versions by intersection Evidence's review of #363 (bus #2777) probed three real holes; all three are closed and its probe file now fails on each of them. - Duplicates inflated minSamples: the same signed bundle presented twice, or one event listed twice inside a bundle, counted twice. Events are now counted once by their verified hash; binding has already proved hash == hashEvent(e). - A failed inspection admitted: cv_inspection_result {passed: false} counted as a sample at inspected_output (the photo adapter sets passed from its anti-spoof score). An inspection whose passed field is present and not true is now a failure: with a completion it is contradictory-evidence, alone it is device-failure, and it never counts toward minSamples. No passed field means no verdict was claimed, so it still counts. - Version pins used the union of the two lists, so neither was enforced. The evidence carries one version field, so an observation now counts only if its version is in both lists; a profile whose lists share no string can never be satisfied and fails closed as unverifiable-term. Doc: the caller contract for bundles (every bundle the accepting kernel stored, never a presenter's selection, since omission hides a failure), and why interpretation.evidenceTypeIds and outcome.objectIdentity are descriptive in v1. Tests: 8 new (40 total). Evidence's probe file run against this: P1, P1b, P2, P3a, P3b now fail (holes closed), P4 passes (a caller contract, now documented). Each of the new rules is mutation-checked. Spec 954/954, tsc clean, test files type-check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PGXt1epJimheWaESMGBbXc
LamaSu
added a commit
that referenced
this pull request
Sep 24, 2026
…he oracle signs rejects on (J4)
The oracle signed reject verdicts on a producer-asserted contradiction flag
it never derived: the same class as signing an underived claim. Ruling
(#3240): it signs reject only for a contradiction it derives from
authenticated events, and an underived one is refused unsigned. The derivable
rule must be public, so the oracle and profile admission read one predicate.
- deriveContradictions(events) returns, in a fixed order:
- "completion-and-failure": a device-reported completion and an
execution_failed in the same set;
- "completion-and-failed-inspection": a completion and an inspection
reporting its own negative verdict.
- inspectionFailed(event): an INSPECTION_EVENT_TYPES event whose
payload.passed is present and not true. This is the rule sensors adopted
in profile admission (#363); it now lives next to the inspection types.
- A failure without a completion is a device failure, not a contradiction.
- Fabricated events prove no contradiction, just as they prove no level.
Tests: evidence-level 26/26 (6 new); spec 823/823; tsc clean. 5 mutants
killed: fabricated counted, completion gate, absent verdict, non-inspection
verdict, failure kind.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117ows6894R3n6YQXBCRahS
LamaSu
added a commit
that referenced
this pull request
Sep 28, 2026
…head) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu
added a commit
that referenced
this pull request
Sep 28, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu
added a commit
that referenced
this pull request
Sep 28, 2026
…nt head) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu
added a commit
that referenced
this pull request
Sep 28, 2026
…vidence's failure and contradiction rules Coord-watch's cross-cutting rule (#2961), carried to #363 by evidence (#3079): evaluate only what you hashed. LO-EV-9 (#341 @799cea1d) now returns the verified canonical snapshots of each bundle's events. Admission reads those snapshots for every check (dedupe, simulation, levels, device, version, window, the inspection verdict), never the caller's objects, whose getters or non-enumerable fields could answer differently from the hashed bytes. inspectionFailed and the contradiction rule now come from evidence-level.ts (#345 @cb81284f): inspectionFailed replaces my private copy (same rule), and deriveContradictions is the one public contradiction rule the oracle signs rejects on, so admission and the oracle cannot drift. A failure with no completion is still a device failure under onDeviceFailure. Stack refreshed: #338 @92b4302b (R20 round 2), #341 @799cea1d, #345 @cb81284f, #336 @06a5a49b. Tests: a payload.passed getter that answers false while binding hashes it and true afterwards is rejected (with the old live-object read put back, that test fails); contradictions are named by kind. 42 admission tests; spec 979/979; tsc clean; test files type-check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu
added a commit
that referenced
this pull request
Sep 28, 2026
LamaSu
added a commit
that referenced
this pull request
Sep 29, 2026
LamaSu
added a commit
that referenced
this pull request
Sep 29, 2026
…rable; new fail-closed terms and unknown keys are refused #363 @48a69252 checks each version pin against its own evidence field (source.adapterVersion, source.firmwareVersion), so disjoint adapter and firmware lists are now registrable. The "pins can never both be met" test is replaced by a positive control. Registration refuses what admission would fail closed on: a pattern pin ("*"), a device kind no evidence source can carry, an inactive primitive id; and an unknown profile term is profile-invalid, so it can never be committed. Tests: registration 15 passed; spec 47 files, 1054 passed; tsc clean; the test files type-check.
LamaSu
added a commit
that referenced
this pull request
Sep 29, 2026
…te numbers and untyped list entries Ported from #363 @48a69252, which found these while answering the cross-family DO-NOT-SHIP on N24 (pack 04 side findings). They belong in the PR that introduces validateMeasurementProfile: - a v1 profile has exactly the v1 terms: an unknown key at any level is a violation, because it would be committed by the digest and evaluated by nothing; - maxIntervalMs and validityWindowSeconds must be finite (Infinity passed); - version lists, evidenceTypeIds and requiredRoles entries must be non-empty strings; - calibration.required must be a boolean. The field docs now say what device.kind, the version pins and the unit mean to evidence (source.deviceType, adapterVersion and firmwareVersion, a decimal-string value). The validation code and its tests are byte-identical to 48a6925, where each rule was mutation-checked. Tests: spec 42 files, 887 passed; tsc clean; the test file type-checks. The pinned print-pilot digest is unchanged.
LamaSu
added a commit
that referenced
this pull request
Sep 29, 2026
LamaSu
added a commit
that referenced
this pull request
Sep 29, 2026
Owner
Author
Evidence lane re-review of #363 @9d29bde6 (range 29630a4..9d29bde): APPROVED, one nitVerified on the DGX Spark: spec 1039 passed; My #3419 rulings, as implemented:
Nit: Context (#3543, #3426): at 🤖 Generated with Claude Code |
LamaSu
added a commit
that referenced
this pull request
Sep 29, 2026
…; profile checks run on a one-pass copy Round 2 answer to astra's DO-NOT-SHIP on 2df436b (pack 40; triage bus #3687). #336 is now stacked on #341 (LO-EV-9, 799cea1) and #338 round 3 (6cea24d). #52 (40-1..40-3): an entry's signature covers {capturedAt, rawContent, source}, not its predecessor, position or run, so entries alone could not show a chain was the kernel's. makeExecutionLogVerifier now takes the kernel-signed BUNDLE as its instance and the subject from ctx.subject (the job record). It: - verifies the bundle signature: the registered signer (deps.expectedSigner), ed25519, a 64-byte value, and deps.verifyBundleSignature; - runs LO-EV-9 subject binding; - extracts exactly one linear chain from GENESIS from the bound events (one payload.entries carrier, or one log_hash_chain_entry per entry, linked); - pins every entry signature to the same signer and algorithm; - requires capturedAt to be non-decreasing (evidence ruling, bus #3553); - runs verifyLogChain. Truncation, reordering with rewritten links, cross-run splicing, forks, gaps and a second log are rejected. The factory throws on an invalid minEntries or a missing expectedSigner. The entry-hash formula is unchanged. The oracle has been told of the contract change (bus #3691). Profile (40-5..40-8): calibration procedureId and validityWindowSeconds are invalid unless calibration.required; sparse arrays fail; profileGoverns never throws and returns the deep-frozen snapshot it validated and digested (plus a code). Every check runs on util/plain-data.ts plainDataCopy, a one-pass copy that reads each property exactly once and refuses anything JSON cannot carry (canonicalize reads each property twice). There is a real key-reordering test. Vector (40-9, 40-10): the emitter exports buildLose3Envelope, and a test regenerates the fixture byte-for-byte. negatives.failureBearingBundle is the positive bundle plus execution_failed, hashed and signed; its integrity verifies, so a consumer refuses it by outcome policy (#363). The positive bundle is unchanged (b80f569c...). Tests: spec 43 files, 968 passed; tsc clean; the test files type-check. Mutation check: 23 single-rule mutations of the new code, all caught.
LamaSu
added a commit
that referenced
this pull request
Sep 29, 2026
…, #336 round 2) # Conflicts: # packages/spec/src/evidence/measurement-profile.ts
LamaSu
added a commit
that referenced
this pull request
Sep 29, 2026
…titles and descriptions, and a new branch's history (N44 r4) Astra r3 on 976c8c3, weakest link: the pre-merge scan checked the pull request's head and history, but the commit that lands on master (a squash or merge commit, whose message is written at merge time and can carry "[skip ci]") was never scanned before master moved. - secret-scan.yml runs on merge_group: it checks out the queue's base, fetches the queue's commit as objects and scans its tree and everything it adds, message included. This holds once the operator enables the merge queue and requires the check (a repository setting). - pull_request_target also runs on edited, and scans the title and description, passed only through the environment. - A push that creates the branch scans its whole history (--history), in secret-scan.yml and ci.yml; before, only its final tree. Scanner: --history <rev>; --text <label> scans stdin; a range or history that ends at an annotated tag scans the tag object and its message (the header claimed tags, but endpoints were peeled to commits); an unknown option, stray argument, repeated option or missing value exits 2 instead of falling back to a clean index scan. Tests: secret-scan-workflow.test.mjs (6 structure checks) and 5 scanner tests; 10 of these fail on 976c8c3. CI runs all three test files. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…; no child diagnostics or control bytes reach the terminal (N44 r4) Astra r3 on 976c8c3, finding 3 (blocking): - Verify passed on an empty body, two JSON documents or trailing bytes, because only the exact string "invalid" was rejected. It now needs HTTP 200 and exactly one JSON object whose result.verified is a boolean; true and false both pass. - The E2E flow ignored HTTP status, took the string "true" for valid, accepted a body followed by a transport error, and never failed on the type, kernel or setup-status steps. Every counted step now needs transport success, the route's success status (provision 201, the rest 200) and one JSON object of the expected shape; validate needs the boolean valid: true. Integration status is informational only. - mktemp and rm could print a key-bearing TMPDIR path. Requests no longer use a temporary file (http_request reads the status from -w), and curl's messages are discarded. say() strips control bytes, so an escape sequence or carriage return in a response never reaches the terminal. Tests: smoke-digital-verifier.test.mjs runs the script hermetically with a stub curl and gh: 21 tests, of which 16 fail on 976c8c3. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…says it did not (N59, ADK item 8) The setup test job claimed to verify the operator's pipeline, but on master it fell back to `kernel_dev_001`, ran on the gateway's in-process mock regardless of kernelId, and for a deviceless kernel wrote a self-attested bundle it reported as "completed". So an onboarding agent got a green test-job that never touched its device. Now the route is owner-gated and honest: - kernelId is required (no kernel_dev_001 fallback); an unregistered kernel is 404. - The caller must be the kernel's recorded operator (apiGate's principal); an unowned placeholder address never matches. 403 otherwise. - A deviceId on that kernel is required; a device on another kernel is 404. - The gateway runs the job only if a real adapter for that device is loaded in its runtime (it tries a DB refresh first); otherwise 409 device_not_runnable_here, telling the operator to run it on their own node. There is no self-attest fallback, so a deviceless kernel is never a pass. - The reply carries ran/passed: `ran` is true only when an adapter executed here, `passed` only when that run completed. Verifying evidence against the kernel's registered key (D4a, #428) will tighten `passed` when it lands. KernelService gains `kernelId` and `hasRunner(deviceId)` accessors. Tests: setup.test.ts test-job cases rewritten — kernelId required, owner gate (stranger + anonymous 403), device-on-kernel, the not-runnable refusal (no self-attest, submitJob not called), a passing run and a non-completing run. Gateway setup suite 41/41, tsc clean. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The consumers acked this on the bus: readmodels (#3623), adk (#3785) and refvertical (#3571, #3792). - A2: OpportunityDTO.capabilityContractDigest pins the exact CSD revision. It is REQUIRED on a funded funded_offer, because the accepted plan pins it; optional on kit_build_request; forbidden on demand_aggregate. - A3: evidence.requiredEventClasses: string[] becomes evidence.requiredPrimitives: CsdEvidencePrimitiveRef[], the same grammar as a CSD's evidence refs and setup's EmitterDecl emits[]. Each id must be active in EVIDENCE_PRIMITIVES, so the ADK's provenance planner compares supply against demand with no mapping table. - A4: the manifest header states two conventions. The provenance-recipe mediaType is application/vnd.pcc.provenance-recipe+json;v=1, and compatibility.interfaces uses the kernel's AdapterType names, never "mock". There is no shape change. - A5: a demand_aggregate carries releasePeriod (YYYY-MM), required there and forbidden elsewhere. asOf stays the READ time for every kind (readmodels' counter), so it never says when an intent happened (#365 F4). - A6: artifact names must be safe relative POSIX paths. Before, the schema accepted '../../etc/passwd', a path traversal for any installer that writes artifacts by name. Refvertical's real R46 kit already complies, so its kitDigest is unchanged. - One CSD_CAPABILITY_URL_PATTERN is now shared by the kit manifest and OpportunityDTO. Golden kit digests are unchanged (A6 only validates). kits-contracts passes 26/26 (5 new). With the source changes reverted, 8 fail. The full spec suite passes 823/823 and tsc is clean. A1, the OperatorBindingDTO part, follows separately: operator-ux, its last acker, is paused (steward #4029). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
OperatorBindingDTO capability types become CSD urls (adk's ask (a) in
#3152, with A1b acked in #3785).
- bindings[].capabilityType and executionAuthority.canClaimCapabilityTypes
must match CSD_CAPABILITY_URL_PATTERN, the same pattern as OpportunityDTO
and the kit manifest. A Work Inbox or the ADK's match compares them
directly, so a legacy '3d-printing' can no longer silently match nothing.
- NEW unmappedCapacity: {kind, id, legacyType}[] (required, may be empty)
lists capacity whose legacy type resolves to no CSD. It is never
claimable, and it shows the operator why that capacity matches nothing.
operator-ux acked A1 (#3997) and asked that claim rights stay derived only
from mapped bindings. The superRefine already does that, and the test
"unmapped capacity can never become claimable" pins it. Steward #4111:
land it normally with A2-A6.
Tests: kits-contracts passes 30/30 (4 new). Against the old
operator-binding.ts, 2 of the 4 fail (the CSD-url binding and the required
unmappedCapacity). The other two are defense in depth: the old schema
already refused them, by claim binding and by strictness. The spec suite
passes 827/827 and tsc is clean.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…fact roles) KIT_ARTIFACT_ROLES gains "intake-schema" (the filled human-intake record schema, ADK-track item 6) and "safety-envelope" (sensors' R8 operational envelope). A device kit can then ship the onboarding artifacts the ADK produces. The change is additive: every existing manifest and golden digest is unchanged, and an unknown role is still refused. adk acked as a consumer (#4099). refvertical, the other manifest producer, is asked on the bus before this is pushed. Tests: kits-contracts passes 32/32 (2 new), the spec suite 829/829, and tsc is clean. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…, astra packs 73/74 fixes)
…es a __proto__-carrying profile (astra pack 74) Both findings were reproduced at e4478ed before the fix. - HIGH: a JSON-parsed profile whose measurement.sampling held only {"__proto__": {"minSamples": 1}} carried the same digest as one committing minSamples 2. Admission then ADMITTED one qualifying sample against the commitment that asked for two. The root cause was plainDataCopy, fixed in #336 (73ac0bb, merged here in e6dd0a8). The admission-level regression test now rejects it as profile-invalid, and computeMeasurementProfileDigest refuses the profile outright. - MEDIUM: the top-level reads of the input (the pin, callbacks, subject, bundles, digest and profile) sat outside any catch. A getter throwing there rejected the promise instead of returning a decision. Every read of input now happens once inside one guard, and a throw resolves to reject with the new code input-unreadable. Tests: 2 new; spec 1102/1102. Removing the guard fails its test.
…ir read models (N68) Astra r1 on #448 (610607c), CRITICAL; reproduced first. POST /api/query answered its kernel_health, network_status, operator_stats and find_capability intents with the stored shop_kernels and capabilities rows. Any self-provisioned key read every kernel's exact coordinates and street address. The four new tests failed at 610607c with the canary's exact latitude in each answer. Those intents now read through the kernel and capability facades, whose populators project a site's location: coarse unless its operator opted in, and no street address. The answer text is filled from the same rows. The job intents are unchanged here; they belong to the job-read gate (F3, #403). agent: pcc-readmodels (c255d7dc)
…ry included Astra r1 on #448 asked for /api/query in the real-gateway privacy sweep. The sweep that reproduced N68 (uncommitted until now) boots createGateway. It registers a canary kernel and capability at an exact point and street address, then calls every registered GET route (anonymously and with a stranger's key) plus the POST reads that answer with kernels or capabilities: /ask, pcc-discover and the four /api/query intents. At this head: 0 leaking responses in 1,475 calls per pass. With the previous nl-query.ts: 4 keyed leaks, one per /api/query intent, and the test fails. agent: pcc-readmodels (c255d7dc)
fix(spec): economics refuses, never throws, on a number the canonical form cannot write (D5, #359)
ci(pcc-node): run the whole pcc-node suite on Python 3.9 and 3.12, failing on skips (N117)
…ne rule (N24, steward #6478) #345 replaced the device-id independence rule that admission called (evidenceLevelOf, evidenceLevelOfBundle, executingDeviceIds) with one rule over AUTHENTICATED bundles: independence between trust domains, the rule the oracle signs on. Admission now uses it. It does not keep a private copy of the old rule. - New input executorTrustDomains: the operators the deal assigned, from the accepted deal (as subject comes from the job record). It must be a list of operator principal ids; absent or malformed rejects as input-unreadable. - The signature leg answers { trustDomain }: the verified signer's operator principal, or null when the registry names none. false, a throw, a bare true, a malformed principal or an accessor fails it (unauthenticated-bundle). - Levels come from evidenceLevelsOfEvents and contradictions from deriveContradictions, both over the authenticated bundles. An event present in several bundles counts at the LOWEST level any copy gets (steward #6478), and reached is the highest of those. - An inspection counts only with a pass under evidence's pinned verdict field (inspectionVerdict), not payload.passed: instrument_result is `pass`. Tests: the pilot world has two operators, the assigned executor (A) and an independent inspector (B). New cases cover the lowest level across copies, the leg's answer, the executor-domain checks, assignment of the inspector's own operator, and contradiction across domains. 116 of 116. Mutations: 10 of 11 killed; the survivor ("null is lowest") is equivalent here, because a null copy needs a fabricated bundle, which admission refuses first (simulationProhibited is always true). Spec suite: 2529 of 2529.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 05:27 — with
GitHub Actions
Failure
LamaSu
added a commit
that referenced
this pull request
Oct 4, 2026
…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.
LamaSu
added a commit
that referenced
this pull request
Oct 4, 2026
LamaSu
added a commit
that referenced
this pull request
Oct 4, 2026
…e leg names the verified signer (astra pack 271) Pack 271's HIGH, reproduced at cdff055. The signature leg named a trust domain bundle by bundle, with nothing binding it to the verified signer or to one registry snapshot. One key (SIGNER_A) signs the printer's bundle and the camera's; the leg genuinely verifies both, then names A first and B second. Admission admitted at inspected_output; with honest answers it rejects (device_reported). The fix combines astra's two options: - signerTrustDomains, a new required input, is ONE pinned registry snapshot as data: each registered signer's id (as kernel bundles declare it, kernelSignature.signer, 0x + 40 lowercase hex) and the operator principal that owns its key, or null. Admission looks each bundle's domain up there itself, so a signer has exactly one domain. A malformed row, or a signer listed twice (even with one domain), is input-unreadable. - verifyBundleSignature now answers the verified signer's id (or false). It must equal the bundle's declared signer, and the snapshot must hold that signer; otherwise unauthenticated-bundle. - A null domain still authenticates, and still gives no independence. - The caller contract now also persists the executor domains and the signer snapshot with the decision. Pack 271's LOW, reproduced: the test helper admit() listed executorTrustDomains twice. Removed. Tests: - the leg's contract: true, a domain record, a malformed id, or a signer other than the declared one fails it; - one key signing both bundles gets the executor's one domain; - a flipping leg is refused; - a null domain; - a signer missing from the snapshot; - every malformed or duplicate snapshot row; - the LO-SE-3 fixture's signer maps to no operator. Admission tests 119/119; spec 2548/2548; tsc clean. Mutations: 11 of 12 killed. The survivor, the leg answer's format check, is equivalent: a malformed answer either differs from the declared signer or is missing from the snapshot, which holds only checked ids.
…a pack 271 LOW) The tests are not type-checked (tsconfig excludes them) and there is no linter, so TypeScript's duplicate-property error never ran on them; pack 271 found a repeated executorTrustDomains in admission's test helper. This test walks every test file's syntax tree with the TypeScript compiler and lists each repeated key. A self-test covers quoted and bare keys, numeric keys, spreads, accessor pairs and computed keys. Run on cdff055's admission test, it reports exactly the duplicate pack 271 found (line 233).
LamaSu
added a commit
that referenced
this pull request
Oct 4, 2026
…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
added a commit
that referenced
this pull request
Oct 4, 2026
LamaSu
added a commit
that referenced
this pull request
Oct 4, 2026
…urrences, as #345 does (steward #6623, evidence #6559) The duplicates ruling is option (a): admission keeps no policy of its own, and takes #345's per-occurrence semantics. Each occurrence is levelled by its own bundle, and an event counts at the highest. This reverses the "lowest copy" rule of steward #6478. With one domain per signer, only a copy in a truly independent signer's bundle can lift an event. - reached is now #345's own evidenceLevelOfBundles over the authenticated bundles (the maximum over every occurrence). - The per-event level the observation loop counts is the max of evidenceLevelsOfEvents over the event's occurrences (higherLevel; null is the lowest). Tests: - The duplicate case flips: the camera's inspection in both the executor's and the independent inspector's bundle now admits at inspected_output. With only the executor's copy it is device_reported. - A parity test holds admission's reached equal to evidenceLevelOfBundles, and the counted level equal to the max of evidenceLevelsOfEvents, on the duplicate case.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 07:04 — with
GitHub Actions
Failure
… against ONE pinned key registry (astra pack 275) Pack 275's HIGH, reproduced at 2ab6e41. The signer snapshot bound 20-byte signer LABELS, not keys, and the callback leg verified against its own registry. In the reproduction, one Ed25519 key signs both bundles, declaring label A on the printer's and label B on the camera's. A leg that verified that key for both and returned each label was handed a snapshot of {A -> OPERATOR_A, B -> OPERATOR_B}, and admission admitted at inspected_output. The fix takes the reviewer's structural option: verification against the pinned rows. - registryKeys (new, required) is ONE pinned registry snapshot as data. Each row is an Ed25519 public key, the raw 32 bytes as 0x + 64 LOWERCASE hex, one spelling per key, with the operator that owns it, or null. - Rows are unique by key and by signer id (0x + the key's first 40 hex, as kernels declare it). A key listed twice, two keys whose signer ids collide, or a malformed row is input-unreadable. - pinnedRegistryDigest (new, required) is computeRegistryDigest over the rows: a tagged sha256 of {REGISTRY_SNAPSHOT_DOMAIN, rows sorted by key}. A malformed pin is registry-pin-invalid. Rows that do not digest to it, a key rotated or reassigned after the pin, are registry-mismatch. Both are new codes. - SIGNATURE runs here, through Web Crypto Ed25519. The bundle's declared signer must name one key of the registry, the algorithm must be ed25519, and the signature over signingPreimage(bundleHash) must verify under THAT key. The trust domain is that key's row. - verifyBundleSignature, BundleSignatureAnswer and signerTrustDomains are gone. Verification and domain come from the same pinned row, so no callback registry can differ. - The caller contract now pins the registry with the evidence set. Tests, under a world whose signer ids derive from the real keys: - the reproduction: A's key declaring B's id does not verify under B's key; - a bundle signed by another registered key; - an unregistered id, another algorithm, an unparseable signature, and a signature over another digest; - one key with one id has one domain; - null domains; - a rotation after the pin, a malformed pin, and row order not committed; - every malformed registry, including a duplicate key, an uppercase spelling, colliding signer ids and an accessor row; - the registry walked for code; - computeRegistryDigest; - the LO-SE-3 fixture under its own key; - the set-mismatch test without its callback counter. Admission 123/123; spec 2551/2551; tsc clean. Mutations 19/19, with the max-over-occurrences flip and the lint check included.
…accessor kinds (astra pack 275 LOW)
Pack 275's LOW, reproduced: { executorTrustDomains: [],
["executorTrustDomains"]: [...] } in a test file passed the check.
- A computed key whose value is a literal (a string, a number, or a
template without substitutions) is now that value.
- A getter and a setter of one name may share it, once each. A second get
or set, or an accessor beside a data member, is a duplicate.
- Only a non-literal computed key is skipped.
- The self-test covers each case: the reviewer's computed duplicate, a
computed template against a bare key, a computed number against a quoted
one, two getters, get beside data, set beside data, a third accessor,
and the skipped non-literal keys.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 07:08 — with
GitHub Actions
Failure
…mes (astra pack 281) Pack 281's HIGH, reproduced at eb142ab: any key in the pinned registry could authenticate evidence for any kernel or job. B, registered, signed the printer's execution events for KERNEL, declared B, and admission returned admit at device_reported. The steward completed the property (DECISIONS 00:26); this implements it: - a registered row names its key, its operator and its GRANTS: { role: executor | witness, kernelId, jobId? }. Registry membership alone authorizes nothing; - after signature and subject binding, the verifying key must hold a grant naming the subject. An executor signs any event of it; a witness, inspections only. Otherwise unauthorized-signer; - a session key counts only through a delegation rooted in a registered row of the same snapshot ({ publicKey, delegatedBy, authorization }), checked when the registry is read: the LO-EV-1 wire form and preimage, this row's key, the root's signature, evidence_submit, a named job, issuedAt <= expiresAt. It holds its root's domain and grants, only for the jobs its scope names, and only for events whose own timestamps fall inside its window; - the digest moves to PCC:evidence-registry-snapshot:v2 and commits the grants (sorted) and each delegation as received. The header names what callers build the rows from: shop_kernels' signing_key_public_key and operator_address. No record assigns a witness yet, and wall-clock expiry, revocation and maxSignatures stay with the submission path and the pinning party. Tests: astra's reproduction refused, an unauthorized registered key refused, the authorized independent inspector admitted, the executor's and witness's subject and role, session keys (scope, root subject, window, delegation refusals, commitment), and the digest's definition. Admission 132/132, spec 2561/2561, tsc clean; 38 of 38 mutations killed.
…s them (astra pack 281 LOW) Pack 281's LOW, reproduced at eb142ab: { [1n]: 1, "1": 2 } was not reported, because a BigInt computed key was skipped. Keys are now compared after ToPropertyKey: a BigInt is parsed (TypeScript leaves 0x10n unnormalized), signs are applied ([-0] is 0; [+1n] throws at runtime and is skipped), true, false and null are keys, and parentheses and as / satisfies / <T> / ! are unwrapped. Only a computed key whose value is not a constant primitive is skipped. The self-test covers each kind.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 07:50 — with
GitHub Actions
Failure
…ng witness grant is named (steward #6694) Two additions to round 13's grants: - A key that signs as the subject's executor is the executor's own, never an independent inspector. #345 builds its executor set from the deal's executorTrustDomains plus the operators of bundles holding execution events. So the kernel's own key, whose operator the deal left out, could sign an inspections-only bundle that counted as independent: #345 on the deal's executors alone says inspected_output (now a test). Admission now passes #345 the executor set completed by roles: every executor-role bundle's operator joins it, and an executor key whose operator the registry does not name leaves no inspection independent. A deal that names no executor still shows no independence, as before. - When the profile requires inspected_output and no pinned row grants a witness for the subject, the shortfall is no-witness-authorized (under onMissingData), not level-not-reached. No registry record assigns witnesses yet (N132), so inspected_output through a witness waits for it visibly (steward #6694). Admission 134/134, spec 2563/2563, tsc clean; 8 of 8 mutations for this change killed, and round 13's set re-run.
LamaSu
had a problem deploying
to
trusted-checks
October 4, 2026 07:58 — with
GitHub Actions
Failure
LamaSu
added a commit
that referenced
this pull request
Oct 4, 2026
…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.
LamaSu
added a commit
that referenced
this pull request
Oct 4, 2026
…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
added a commit
that referenced
this pull request
Oct 4, 2026
LamaSu
added a commit
that referenced
this pull request
Oct 4, 2026
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.
Stacked PR. Base is #336 (
feat/sensors-measurement-profile). This branch also merges pcc-evidence's #341 (LO-EV-9 subject binding) and #345 (evidence levels), so until those land the diff below includes their commits. Merge order: #338 → #341, #345 → #336 → this → #384.Sensors lane 7a438686, goal
pcc-reconciliation. Ledger row 21 (LO-SE-2), board row N24.What it adds
profileAdmitsBundle()— does the evidence for a job satisfy its committedMeasurementProfileV1? It returns admit / reject / hold with a closed set of reason codes. Every profile term is either evaluated, bound by the digest, checked at registration, or failing closed; the header's table goes field by field.Round 2 (
48a69252) answers the cross-family DO-NOT-SHIP on29630a48(review-routern24-363-profileadmit-astra). All four findings were open at39dfd450; triage is on bus #3418.F1, terms were not evaluated. A qualifying observation now needs:
passed === true) on inspection types;source.deviceType == device.kindandsource.adapterType == device.adapterType;payload.profileObservationrecord matching the profile (committed digest, a listed primitive id, object, method, quantity, unit, a decimal-string value iff the unit isn't "none", a sampleId);verifyPrimitiveInstance, which the oracle composes from its verifier registry. It has no default, it receives deep-frozen snapshots, and stubs fail closed.More terms fail closed:
maxIntervalMsminFractionother than 1*in a pinF2, completeness was a caller promise. There is now a required
pinnedBundleSetDigest, and the presented bundles must open to it.computeBundleSetDigestis domain-separated, job-, kernel- and unit-bound, with sorted, unique, tagged entries.Callers and the pin (evidence #3419, oracle #3426):
/settle, on the evidence the committed package names. The gateway may pre-check.F2 is partially closed: the gate refuses a subset of the pin, but an unforgeable pin is (b).
F3, a reissued sample counted twice. Samples are now counted by a distinct
sampleId(asha256:commitment to the raw capture). A sampleId stops reissue; it doesn't prove physical distinctness, which is the primitive verifier's question.F4, one version field met both pins. There are now additive
source.adapterTypeandsource.adapterVersion(TS type and zod mirror). Each pin is checked against its own field, and the intersection rule is gone.Side findings: finite checks, typed list entries, and a boolean
calibration.required. There is also a Python-recomputed event-hash golden for the new fields. That golden is whyvalueis a decimal string: JS writes1e-7, Python writes1e-07.Verified (DGX Spark)
48a69252:@pcc/spec46 files, 1039 tests passed.tsc --noEmitis clean, and these test files type-check too (spec's tsconfig excludes tests).1e-7, or01.5profile-observation-hash-parity.test.ts), with pcc-node's canonicalizer run in vitest: the event hash with the new fields, and the bundle-set digest.review-packs-for-chatgpt-20260924/39-n24-363-profileadmit-r2-astra-48a69252.md.Not claimed
Draft until pcc-evidence reviews and the cross-family round 2 returns. Operator merges.
🤖 Generated with Claude Code