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
6 changes: 3 additions & 3 deletions .github/workflows/verdict.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ jobs:
- name: Validate the witness registry against its schema
run: python3 tools/check-registry.py
# §6.2/§6.3: every committed observation and verdict conforms to the
# 1.0.0 schemas, and every committed hash is self-consistent.
- name: Validate the committed corpus against the 1.0.0 schemas
# 1.1.0 schemas, and every committed hash is self-consistent.
- name: Validate the committed corpus against the 1.1.0 schemas
run: python3 tools/validate-corpus.py

verdict:
Expand Down Expand Up @@ -204,7 +204,7 @@ jobs:

# §6.2: the freshly produced observations must conform to the schema,
# not merely hash to the committed value.
- name: Validate fresh observations against the 1.0.0 schema
- name: Validate fresh observations against the 1.1.0 schema
run: |
python3 tools/validate-corpus.py \
--extra-observations "_work/${{ matrix.artifact }}/observations"
Expand Down
8 changes: 6 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ Your entry must declare:
And, in the pull request body:

- **Coverage declaration.** Which fields of the controlled vocabulary you
claim. Today that vocabulary is `entity_counts`; `layer_topology`,
claim. Today that vocabulary is `entity_counts`, `relations` and `storeys`
(the last two added in 1.1.0, SPEC.md §9.4, §20); `layer_topology`,
`linework`, `bounding_boxes`, `xdata_fields` and `text_content` are
specified but not yet exercised. Declaring a field you cannot actually
parse is a registration violation and grounds for removal.
parse is a registration violation and grounds for removal — and the gate
enforces the declaration for the set-valued classes: a witness that does
not declare `relations` or `storeys` makes the verdict `MANIFEST_ERROR`
rather than being quietly skipped.
- **A version pin.** An exact git SHA or release tag, or the exact PyPI /
crates.io / NuGet specifier the CI gate will install. Floating ranges are
forbidden. A witness update is a separate pull request that includes the
Expand Down
196 changes: 123 additions & 73 deletions README.md

Large diffs are not rendered by default.

151 changes: 147 additions & 4 deletions SPEC.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions corpus/MANIFEST_INDEX.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@
"artifact_id": "g-2026-0001",
"bridge_sha256": "d07c7462aee22640661faed5262cf802ce0fcbc663f312961a39be92bf857050",
"manifest_path": "corpus/artifacts/g-2026-0001/manifest.json",
"manifest_sha256": "6620d925a7a89688b991d4b1a8e10ead6191e73b4242502b8b014cdfbc0c3971",
"manifest_sha256": "ab33a0f4918587504d083551f3eefef8ec33065268355f9d5f9b487a6436d203",
"prev_hash": null,
"source_sha256": "c805df445d613b408e37337765572021265e3f5dfdc7d1fa53b22ba1600b8014",
"verdict_hash_sha256": "94d723919686963121c05c7475c3ea126e9f8d97fa4b9136c6e5656421c8b40a",
"verdict_hash_sha256": "c615cc6c4606c3486be2a82af7566b4e6a5002623ffd39227f591b9df5182efa",
"verdict_status": "PASS"
},
{
"alias": "magnetar-2024-core-interior-slim",
"artifact_id": "g-2026-0002",
"bridge_sha256": "bfdf36ffb0bb768f3409d818403990e64d4c262c6780603be87f8077387ad86d",
"manifest_path": "corpus/artifacts/g-2026-0002/manifest.json",
"manifest_sha256": "f22783cd973aa9fb4968d72c4b0b47849f4da97574e0c71dcca36de37c39893a",
"prev_hash": "6e2a42634621ed26b655dbf1c3bc7cd1488650779e65cf02223a33c66450279f",
"manifest_sha256": "76e218f0f74ea1d2172727240d28422238856dc38b70a20d1f62a7624915b2f8",
"prev_hash": "65ee5d9750b2225244ea4e2ae873fc5f6a95ae97b000c9ed82feef1412a9499e",
"source_sha256": "c805df445d613b408e37337765572021265e3f5dfdc7d1fa53b22ba1600b8014",
"verdict_hash_sha256": "48ef575a11505ee1fcbb02b37b3551e0e8d5618a1ec39aeb6d6cb250e085648c",
"verdict_hash_sha256": "cd171b3167c602d0882dd8a0d38c99791012a9fe693043e43e2d43b028554325",
"verdict_status": "PASS"
}
],
"chain_algorithm": "sha256 over JCS-lite canonical entries (sorted keys, no whitespace, UTF-8)",
"chain_head": "50fb3adb759b3d2ddb45a9b4e7d33a4c486cba749901777e7f58df1ec3aa7a58",
"chain_head": "5e6a64228083a30b9c3ef667d0d6e02ad96b370954b34599c1da18850fcdad23",
"schema_version": "1.0.0",
"signature": null,
"signing_status": "not implemented — SPEC.md §12.2 requires an Ed25519 signature over the chain head; no maintainer key is published yet, so this chain is tamper-evident but not cryptographically anchored"
Expand Down
45 changes: 26 additions & 19 deletions corpus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ corpus/
MANIFEST_INDEX.json hash chain over every manifest
artifacts/
g-2026-0001/
manifest.json SPEC.md §6.1 — hashes, origins, counts, surface
manifest.json SPEC.md §6.1 — hashes, origins, counts,
relations, storeys, surface
PROVENANCE.md what is recorded and what is not
observations/ SPEC.md §6.2 — one per witness, committed
verdict.json SPEC.md §6.3 — the recorded decision
Expand Down Expand Up @@ -68,35 +69,41 @@ driven by the umbrella's different job:
|---|---|
| `artifact_id` + `alias` instead of `id` | the umbrella assigns `g-YYYY-NNNN`; `alias` keeps the id the decoder repository uses, so observations from either side resolve. See the artifact's PROVENANCE.md. |
| `source` / `bridge` blocks carry `origin`, `license` and `bytes` | the corpus fetches by hash instead of committing bytes; without an origin an artifact is not replayable |
| the `decoder_*` fields are dropped | they are the decoder repository's own regression baseline, which the gate explicitly ignores (§6.1). A category that exists *only* as a decoder baseline goes with them — `levels` in g-2026-0001's upstream, which carries no `source_ifc_type` and so no bridge witness can see it. Where the same category does carry a `source_ifc_type`, as `levels` does in g-2026-0002, it stays: it is a real cross-witness observation (15 exported storeys against 12 recovered) and is excluded first-class rather than dropped. Dropping it would hide a measured disagreement behind a formatting rule |
| `octetproof_version` names the protocol document each manifest was authored against | g-2026-0001 says `1.0.0`, g-2026-0002 says `1.0.1`. 1.0.1 is a patch release with no schema, diff-function or provenance change (SPEC.md §19a), so the two conform identically; the field records history rather than a difference in obligations, and an immutable manifest is not rewritten to chase a patch number |

Everything the gate reads is unchanged: the normative `counts` block, with
`source_ifc_type`, `status` (`known` / `known_gap` / `unsupported`),
`tolerance`, `tracking_issue` and `unsupported_feature` carrying exactly the
§6.1 meanings.
| the `decoder_*` fields are dropped | they are the decoder repository's own regression baseline, which the gate explicitly ignores (§6.1). A category that exists *only* as a decoder baseline goes with them — one that carries no `source_ifc_type` (or, in the 1.1.0 blocks, no `relation_ifc_type` / `storey_ifc_type`) is invisible to every bridge witness and is not a cross-witness category at all. A category that *does* carry its type key stays even when its status excludes it: `relations.IFCRELFILLSELEMENT` on g-2026-0001 is `decoder_baseline` and is kept, because dropping it would hide behind a formatting rule the fact that the two sides of that edge cannot be compared on it |
| `octetproof_version` names the protocol document each manifest was authored against | both say `1.1.0`, because both now carry the `relations` and `storeys` blocks that 1.1.0 introduced (SPEC.md §20). A manifest using a 1.1.0 block while declaring 1.0.0 would be misdescribing itself. The field records which document the manifest was written against, not a difference in obligations — 1.1.0 is additive and a 1.0.0 document is still conformant input (§16.2) |

Everything the gate reads is unchanged in kind: the normative `counts` block,
with `source_ifc_type`, `status` (`known` / `known_gap` / `unsupported` /
`decoder_baseline`), `tolerance`, `tracking_issue` and `unsupported_feature`
carrying exactly the §6.1 meanings — joined since 1.1.0 by two blocks that
work the same way. `relations` categories carry `relation_ifc_type` and
`expected_pairs`; `storeys` categories carry `storey_ifc_type` and
`expected_storeys`. Neither has a `tolerance`: their field class is exact set
equality and §7.2 gives it no tolerance concept.

The manifest additionally declares `semantic_surface` and `excluded` — the
result that `counts` implies. `tools/verdict.py` derives both from `counts`
and raises `MANIFEST_ERROR` if the declaration disagrees, so flipping a
category's status without noticing is loud rather than a silent change to
what is being claimed.
result those three blocks imply. `tools/verdict.py` derives both and raises
`MANIFEST_ERROR` if the declaration disagrees, so flipping a category's
status without noticing is loud rather than a silent change to what is being
claimed.

## What is in the corpus today

Two artifacts, one edge, three witnesses.
Two artifacts, one edge, three witnesses, three agreement classes.

| Artifact | Alias | Bridge file | Verdict |
|---|---|---|---|
| `g-2026-0001` | magnetar-2024-core-interior | `2024_Core_Interior.ifc`, 20,392 bytes — an element-export fixture | PASS, 8 surface fields, 4 excluded |
| `g-2026-0002` | magnetar-2024-core-interior-slim | `2024_Core_Interior_slim.ifc`, 1,665,968 bytes — the full project export | PASS, 4 surface fields, 9 excluded |
| `g-2026-0001` | magnetar-2024-core-interior | `2024_Core_Interior.ifc`, 20,392 bytes — an element-export fixture | PASS, 6 surface fields, 10 excluded |
| `g-2026-0002` | magnetar-2024-core-interior-slim | `2024_Core_Interior_slim.ifc`, 1,665,968 bytes — the full project export | PASS, 13 surface fields, 3 excluded |

**They share the same `.rvt` and the same committed rvt-rs observation.** Only
the Revit-authored bridge file differs, which makes the pair a direct measure
of how much of the first artifact's agreement was real: the second has a
narrower surface and five times the exclusions, because the full export shows
360 walls, 132 doors, 256 columns, 116 spaces and 80 slabs where rvt-rs
recovers 0, 0, 0, 18 and 64.
of what a thin reference can support. The full export scores 360 walls, 132
doors, 256 columns, 80 slabs, 20 shading devices and 15 storeys as exact
matches, plus 138 `IfcRelFillsElement` host/filling pairs and the 15
`[name, elevation]` storeys; the 20 KB fixture can score none of the element
counts and no relation, but carries the same complete fifteen-storey
hierarchy and so supports `storeys.IFCBUILDINGSTOREY` exactly as strongly.

See the top-level [README](../README.md#what-exists-today) for what that is
worth, and each artifact's PROVENANCE.md
Expand Down
Loading