Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions research/pr626-omitted-digest-audit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# What the three omitted #626 digests measure

**Verifier audit; no new kappa.** The [Linux reconciliation](../selected626-linux-reconciliation/README.md)
compares the preserved run `38317651752` with the committed #626 assembly after
removing three digest paths. It already says those differences are not known to
be gzip metadata, because the template, owner audit and invoice were not
uploaded. This note classifies the digests from the producers that wrote them.

```sh
python3 -B research/pr626-omitted-digest-audit/verify.py
```

Expected:

```text
PASS_PR626_OMITTED_DIGEST_AUDIT template=decompressed-payload owner_audit=indented-json-file fresh_invoice=indented-json-file differences=3
```

Python 3.10+ and Git. The sprint base `66849e1475c930fd7732b1a0067ffee7e1e95f60`
must be present; a shallow clone has to fetch it first. No supplier replay.

## Classification

At that base, `research/complex-k492-owner-repair/code/invoice_allcold.py`
defines `filehash` as SHA-256 of the file bytes and `contenthash` as SHA-256
of the decompressed payload when the file ends in `.gz`.

| Omitted assembly path | Producer | What a difference means |
|---|---|---|
| `template_sha256` | `contenthash` of `GLOBAL-TEMPLATE.json.gz`, checked again by decompressing that file in `audit_allcold.py` | The template **payload** differs. Two gzip containers of one payload have the same `contenthash`, so this recorded mismatch is not a compression-header artifact. |
| `input_sha256.owner_audit` | SHA-256 of `OWNERS-AND-PROFILE.json`, written with `json.dumps(..., indent=2)` | The indented owner file differs. That file includes `source_artifacts`, and the invoice binds `CANONICAL-WORD.json.gz` with `filehash`, so the file can change because a nested container changed or because a mathematical field changed. |
| `input_sha256.fresh_invoice` | SHA-256 of `PREFIX-AND-INVOICE.json`, also `indent=2`, via `sha(invoice_path)` in `assemble.py` | Same kind of mixed file receipt for the invoice. |

The recorded Linux and committed values differ on all three paths. They are
copied from the predecessor's `EXPECTED.json`; this package does not recompute
them from missing artifacts.

The fixture shows the distinction directly. One payload compressed twice, at
different level and `mtime`, yields two containers and one payload hash.
Replacing a nested container hash inside an indented owner file changes
`owner_audit` while a projection that keeps only `paid_profile` stays equal.
Changing `paid_profile` still changes that projection. So dropping the file
hash can hide a container-only change, and keeping the reduced assembly
comparison still sees a profile change.

`template_sha256` is the case that cannot be explained that way. Because it is
a payload hash, the recorded disagreement is a real template-content
disagreement. The omitted owner and invoice hashes stay unresolved: without
those files, a container change and a mathematical change are not separable.

## Open next questions

Can the omitted owner and invoice files be recovered from the retained stage
logs, or are they absent from run `38317651752`? That decides whether the two
file-hash disagreements are nested container receipts or changed audit fields.
The first step is to search the preserved logs for `OWNERS-AND-PROFILE.json`
and `PREFIX-AND-INVOICE.json` bytes or their payload hashes. It is blocked by
the reconciliation's own scope: the workflow did not upload those files, and
this audit does not invent them.

## Credit and limits

The reconciliation is Josh Snider's (`jsnider3`), with the selected #626
composition. The gzip-container convention is the existing reproducibility
note. This package only classifies these three digests and adds the fixture
that separates container bytes from payload bytes.

No selected certificate, supplier or kappa changes. The audit does not explain
the numerical template difference and does not rerun `make frontier626-verify`.
All-size, hosting, precision and analytic assumptions of the selected
construction remain open.

Prepared by Riquelme Soares (@hevlyo) with substantial Cursor agent assistance.
Apache-2.0. No independent human peer review is asserted.
30 changes: 30 additions & 0 deletions research/pr626-omitted-digest-audit/SOURCES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"schema_version": 1,
"base_commit": "b5ea56e0a5ac4552166253b1fa4be0753bb38c30",
"files": [
{
"path": "certificates/selected-result.json",
"usage": "provenance",
"git_blob": "ae6a99f28aa468649eb66a361e764d769363bf11",
"sha256": "10b9e340fd8e354a37a0c4813c90e5de1dd42044b5f6aca4a3fc5a42b685fa32"
},
{
"path": "docs/research/agent-task-catalog.json",
"usage": "provenance",
"git_blob": "889b95d936746df124872133ae1e7cc339adcb92",
"sha256": "7d185cd8c2af35988d638d969748ad7b670f902606444dbfc748e4402c340df6"
},
{
"path": "research/selected626-linux-reconciliation/EXPECTED.json",
"usage": "input",
"git_blob": "d3c578df6beee2e1f48a70149190a1aea3b01676",
"sha256": "33e4ee3a4927ff56b4526e451d5fbdff7316c7f25cdc6f262bb8a27b7e83161c"
},
{
"path": "research/selected626-linux-reconciliation/verify.py",
"usage": "input",
"git_blob": "89f5d1bee94a1f63ee6081a05c293f63ec1b78ee",
"sha256": "d2e3999f1b0284d14070c148b15c8716b32521f9ffa3143f5dae2bea6f31ece8"
}
]
}
60 changes: 60 additions & 0 deletions research/pr626-omitted-digest-audit/TASK.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"schema_version": 1,
"task_id": "portable-replay",
"priority_revision": 5,
"base_commit": "b5ea56e0a5ac4552166253b1fa4be0753bb38c30",
"original_card": {
"id": "portable-replay",
"question": "Can one reproducible environment or artifact-comparison failure be repaired?",
"starting_points": [
"research/gzip-container-reproducibility/README.md",
"docs/ci-verification.md"
],
"methods": [
"Reproduce one portability failure and add a minimal cross-environment fix.",
"Audit one canonical-payload comparison for omitted proof-relevant fields."
],
"acceptance_gate": "Preserve immutable source hashes and proof-relevant content; a different compressed byte stream alone is not a mathematical mismatch.",
"first_step": {
"command": [
"python3",
"-B",
"scripts/research_baseline.py"
],
"expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY",
"inspect": "Read the gzip-container reproducibility examples and distinguish frozen source bytes from regenerated payloads.",
"scope": "Positive control / source-bound orientation only; this is not a new construction or full cold replay."
},
"first_experiment": "Reproduce one specific mismatch or audit one canonical comparison for omitted proof-relevant fields.",
"bottleneck_link": "Portable exact checks prevent environment failures from blocking valid contributions.",
"allowed_variations": [
"One interpreter/version mismatch or one payload field comparison."
],
"scope_change_triggers": [
"Removing immutable-source pins because generated compressed bytes differ."
],
"overlap_terms": [
"gzip container",
"reproducibility",
"canonical payload"
]
},
"scope_status": "refined",
"actual_scope": "Do the three digest paths omitted by the #626 Linux assembly comparison measure gzip containers, decompressed payloads, or raw indented JSON files?",
"scope_change_reason": "The card allows one payload-field comparison. The object is the three recorded differences in research/selected626-linux-reconciliation, classified from the hash helpers at sprint base 66849e1475c930fd7732b1a0067ffee7e1e95f60. It does not repair a supplier or drop an immutable pin.",
"bottleneck_link": "A portable check has to tell a compression difference from a changed proof object. Misreading these three digests would either block a valid replay or hide a real template change.",
"budget_minutes": 60,
"stop_rule": "Stop after classifying the three recorded paths and the container-versus-payload fixture. Do not rerun frontier626 or reconstruct the missing owner and invoice files.",
"outcome": "completed",
"evidence": "verify.py pins three producer blobs, shows one payload has two container hashes and one content hash, shows an indented owner file hash can change while paid_profile does not, and classifies the recorded Linux/committed pairs: template_sha256 is a decompressed payload, the other two are indented JSON files.",
"author": "Riquelme Soares (@hevlyo)",
"ai_assistance": "Substantial Cursor agent assistance in reading the pinned producers, the fixture, and the write-up. No proof assistant was used.",
"open_questions": [
{
"question": "Can the omitted owner and invoice files be recovered from the retained stage logs of run 38317651752, or are their bytes absent?",
"why_it_matters": "Those two file hashes mix nested container receipts with mathematical fields, so only the files separate those causes.",
"first_step": "Search the preserved logs for OWNERS-AND-PROFILE.json and PREFIX-AND-INVOICE.json bytes or payload hashes.",
"blocked_by": "The frontier626 workflow did not upload those files. This audit does not invent them."
}
]
}
21 changes: 21 additions & 0 deletions research/pr626-omitted-digest-audit/VERIFY.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"schema_version": 1,
"status": "ready",
"python": "3.12",
"requirements": [],
"inputs": [
"research/selected626-linux-reconciliation/EXPECTED.json",
"research/selected626-linux-reconciliation/verify.py"
],
"commands": [
{
"argv": [
"{python}",
"-B",
"{package}/verify.py"
],
"timeout_seconds": 60,
"checks": "Pinned producer blobs, payload-versus-container fixture, owner-file fixture, and the three recorded digest classes."
}
]
}
178 changes: 178 additions & 0 deletions research/pr626-omitted-digest-audit/verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#!/usr/bin/env python3
"""Classify the three assembly digests omitted by the #626 Linux reconciliation.

The reconciliation already refuses to call those differences gzip metadata.
This check says what each digest measures, using the producers at the pinned
sprint base. It does not rerun the supplier. Python 3.10+.
"""

from hashlib import sha256
import gzip
import json
from pathlib import Path
import subprocess

ROOT = Path(__file__).resolve().parents[2]
PREDECESSOR = ROOT / "research/selected626-linux-reconciliation"
BASE = "66849e1475c930fd7732b1a0067ffee7e1e95f60"
BLOBS = {
"research/complex-k492-owner-repair/code/invoice_allcold.py":
"b244cd59940659465c07b013ebe3225077496f45",
"research/complex-k492-owner-repair/code/assemble.py":
"d1674e657ff046f9d55c917723002f270e05c42a",
"research/complex-k492-owner-repair/code/audit_allcold.py":
"e29d90e89af9d7535c96de6ebf7c4d03616f7c48",
}
SNIPPETS = {
"research/complex-k492-owner-repair/code/invoice_allcold.py": (
"def filehash(path): return hashlib.sha256(path.read_bytes()).hexdigest()",
"return hashlib.sha256(gzip.decompress(data) if path.suffix=='.gz' else data).hexdigest()",
"assert replay['canonical_word_sha256']==filehash(cp)==registry['canonical_word_sha256']",
"template_hash=contenthash(template_path)",
"owner_audit_sha256=filehash(audit/'OWNERS-AND-PROFILE.json')",
"(out/'PREFIX-AND-INVOICE.json').write_text(json.dumps(receipt,indent=2)+'\\n')",
),
"research/complex-k492-owner-repair/code/assemble.py": (
"sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()",
"input_hashes={'owner_audit':owner_sha,'fresh_invoice':invoice_sha,'inputs/PR543-EXPECTED.json':sha(bit_path)}",
"template_sha256=owner['template_sha256']",
"owner_path=C/'audit/OWNERS-AND-PROFILE.json';invoice_path=C/'audit/PREFIX-AND-INVOICE.json'",
"r=assemble(owner,invoice,sha(owner_path),sha(invoice_path))",
),
"research/complex-k492-owner-repair/code/audit_allcold.py": (
"raw=gzip.decompress((OUT/'GLOBAL-TEMPLATE.json.gz').read_bytes());assert hashlib.sha256(raw).hexdigest()==summary['template_sha256']",
"source_artifacts=summary['source_artifacts']",
"(REPORT.parent/'OWNERS-AND-PROFILE.json').write_text(json.dumps(owners,indent=2)+'\\n')",
),
}


def need(condition, message):
if not condition:
raise ValueError(message)


def git_show(spec):
return subprocess.check_output(["git", "-C", str(ROOT), "show", spec])


def git_rev_parse(spec):
return subprocess.check_output(
["git", "-C", str(ROOT), "rev-parse", spec], text=True).strip()


def filehash(data):
return sha256(data).hexdigest()


def contenthash(data, gzip_wrapped):
payload = gzip.decompress(data) if gzip_wrapped else data
return sha256(payload).hexdigest()


def check_producers():
sources = {}
for path, blob in BLOBS.items():
need(git_rev_parse(BASE + ":" + path) == blob, "producer blob changed: " + path)
text = git_show(BASE + ":" + path).decode()
for snippet in SNIPPETS[path]:
need(snippet in text, "producer no longer defines: " + snippet)
sources[path] = text
return sources


def check_container_is_not_payload():
payload = b'{"frames":[1,2,3],"paid":{"1":4}}\n'
first = gzip.compress(payload, compresslevel=9, mtime=0)
second = gzip.compress(payload, compresslevel=1, mtime=5)
need(first != second, "fixture failed to build two containers")
need(contenthash(first, True) == contenthash(second, True) == sha256(payload).hexdigest(),
"decompressed payload hash changed with the container")
need(filehash(first) != filehash(second), "container file hash ignored a real container change")
changed = payload.replace(b"[1,2,3]", b"[1,2,4]")
recompressed = gzip.compress(changed, compresslevel=9, mtime=0)
need(contenthash(recompressed, True) != contenthash(first, True),
"payload hash missed a template change")
return {"same_payload_containers_differ": True,
"payload_hash": contenthash(first, True),
"container_hashes_differ": True}


def owner_text(artifact_hash):
owner = {
"status": "PASS_INDEPENDENT_ASYNC_OWNERS_AND_PROFILE",
"template_sha256": "ab" * 32,
"paid_profile": {"1": 4},
"source_artifacts": {"providers/p/CANONICAL-WORD.json.gz": artifact_hash},
}
return (json.dumps(owner, indent=2) + "\n").encode()


def projected_assembly(owner_text_bytes):
"""Fields the #626 assembly keeps after the three omitted digests are removed."""
owner = json.loads(owner_text_bytes)
return {"complex_profile": owner["paid_profile"]}


def check_omitted_file_can_hide_container_change(container):
payload = b'{"frames":[1,2,3],"paid":{"1":4}}\n'
other = gzip.compress(payload, compresslevel=1, mtime=5)
left = owner_text(filehash(container))
right = owner_text(filehash(other))
need(filehash(left) != filehash(right), "owner file hash missed a nested container change")
need(projected_assembly(left) == projected_assembly(right),
"reduced assembly changed when only a container hash changed")
# A real profile change is still visible without the omitted file hash.
mutated = json.loads(left)
mutated["paid_profile"] = {"1": 5}
mutated_text = (json.dumps(mutated, indent=2) + "\n").encode()
need(projected_assembly(mutated_text) != projected_assembly(left),
"reduced assembly missed a profile change")


def classify_recorded_differences():
expected = json.loads((PREDECESSOR / "EXPECTED.json").read_text())
rows = expected["assembly_digest_differences"]
need([row["path"] for row in rows] == [
["template_sha256"],
["input_sha256", "owner_audit"],
["input_sha256", "fresh_invoice"],
], "recorded difference paths changed")
classes = {
("template_sha256",): "decompressed-payload",
("input_sha256", "owner_audit"): "indented-json-file",
("input_sha256", "fresh_invoice"): "indented-json-file",
}
classified = []
for row in rows:
path = tuple(row["path"])
need(row["linux"] != row["committed"], "recorded digests unexpectedly agree: " + str(path))
need(len(row["linux"]) == len(row["committed"]) == 64, "digest length")
classified.append({"path": list(path), "measures": classes[path],
"linux": row["linux"], "committed": row["committed"]})
predecessor = (PREDECESSOR / "verify.py").read_text()
need("DIFFERING_PATHS = [('template_sha256',), ('input_sha256', 'owner_audit'),\n"
" ('input_sha256', 'fresh_invoice')]" in predecessor,
"reconciliation no longer omits these three paths")
need("require(left == right, 'Mathematical assembly fields differ')" in predecessor,
"reconciliation equality check moved")
return classified


def main():
check_producers()
containers = check_container_is_not_payload()
sample = gzip.compress(b'{"frames":[1,2,3],"paid":{"1":4}}\n', compresslevel=9, mtime=0)
check_omitted_file_can_hide_container_change(sample)
classified = classify_recorded_differences()
need([row["measures"] for row in classified] == [
"decompressed-payload", "indented-json-file", "indented-json-file"],
"classification changed")
need(containers["same_payload_containers_differ"], "container fixture lost")
print("PASS_PR626_OMITTED_DIGEST_AUDIT "
"template=decompressed-payload owner_audit=indented-json-file "
"fresh_invoice=indented-json-file differences=3")


if __name__ == "__main__":
main()