Skip to content

synapse-cdm 3.3.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 07:54
· 11 commits to main since this release

synapse-cdm 3.3.0

Rendered by synapse release-notes from the tree at this release's tag. Every
section below except major changes is a derivation; that one is quoted from
RELEASE_NOTES.md, which is written by a person. MIGRATIONS.md condition 4 is
unchanged by this file: it is still a person's, and this only makes the nine
mechanical fields impossible to mistype.

Versions

axis version
package version 3.3.0
CDM version 3.0.0
SC-OES version 0.1.0
Adapter API version 3.1.0
manifest schema version 2.1.0

Major changes

The TacticalAPI blue-force read side release. Between the 3.2.0 release of 2026-10-06 and this
one, the tree gained one adapter module, tacticalapi, and the registry moved from twenty to
twenty-one. It reads one response of the TacticalAPI blue-force tracking service
(rheinmetall.tactical_api.v0, upstream commit 58661c9) — a GetBlueForcesResponse snapshot or
a SubscribeBlueForceEventsResponse stream update, as a serialized google.protobuf.Any or as its
parsed twin — into one Entity and one Event per blue force, in list order. It ships with its
wire reader tacticalapi_codec, pinned synthetic fixtures with their goldens and protoc's own
readings of every payload, a field-level preservation ledger (MAPPINGS), a structured residual on
every object, declared and enforced input bounds, a manifest, and a harness and conformance
reading of CONFORMANT. Nothing on the wire moved: SCHEMA_VERSION stays 3.0.0, no model, enum or
published schema changed, and no golden of the twenty that predate the arc moved by a byte. The
design record is docs/tacticalapi-implementation.md, which ships in nothing; MIGRATIONS.md's
3.3.0 section is the record of what the distribution carries.

If you are upgrading from 3.2.0, nothing you read or write changes. A 3.2.0 reader reads a
3.3.0 object unchanged, and version.compatible("3.0.0", "3.0.0") is the same answer it was. No
member of the Adapter contract moved, so an adapter subclass written against 3.2.0 keeps working.
Of the package's own dependency declarations only the [lint] extra's pin moved, ruff 0.16.7 →
0.16.9, a development tool nothing in the package imports.

Why the number is 3.3.0

The number is the derived floor: a MINOR from v3.2.0. gates/bump_derivation.py --json,
run before any number was typed, reported the arc as {"kind": "MINOR", "number": "3.3.0", "unruled": []}. The floor comes from the public names the arc added and from nothing removed —
the TacticalapiAdapter subclass with its wire reader tacticalapi_codec and the tacticalapi
fixture set — and the one unit the table cannot decide on its own,
pyproject.toml:optional-dependencies (the [lint] extra's ruff pin, moved in place by the
dependency unit that reached main on 2026-10-06), is ruled PATCH in MIGRATIONS.md's 3.3.0
section, which the gate reads and reports nothing unruled. Over the arc the gate's table decides
186 signals, 102 MINOR and 84 PATCH, and the one ruling decides the rest (--json at the tag
lists 187).

Package version 3.3.0 · CDM schema_version 3.0.0 · Adapter API 3.1.0 · manifest schema 2.1.0
· evidence schema 2.0.0.
The first two are three MINORs apart, the third for the ordinary
reason: the surface grew and the contract did not. python -m synapse_cdm.schemas --check --out schemas reads CURRENT: schemas vs models at 3.0.0 and python -m synapse_cdm.manifests --check
reads CURRENT: manifests vs 21 shipped adapters at manifest schema 2.1.0.
ADAPTER_API_VERSION, MANIFEST_SCHEMA_VERSION and EVIDENCE_SCHEMA_VERSION did not move in this
arc. SC-OES, the Operational Ontology and the profile versions did not move: SC_OES_VERSION is
still 0.1.0 and still a Draft. synapse_cdm/version.py states the nine version axes and their
independence.

The tacticalapi adapter, and what it is measured to do

In FORMAT_COVERAGE.md's ordinal table tacticalapi is #22.
It is the twenty-first registered adapter and carries ordinal 22 because ordinal 9 was issued to a
specification that has no adapter: the ordinal is not the roster count.

Adapter Specification / profile Direction What ships
tacticalapi TacticalAPI (rheinmetall.tactical_api.v0, upstream commit 58661c9 of 2026-09-01, which has no tag or release), the blue-force read side only: GetBlueForcesResponse and SubscribeBlueForceEventsResponse ingest one response → one Entity and one Event per blue force; the wire reader tacticalapi_codec with the contract's field table embedded; the tacticalapi fixture set

What it reads. One already-received response per call, as octets or as the dict a
.parsed.json fixture holds; both forms go through one twin and give the same objects. gRPC
framing is not read, and the adapter holds no session, reads no network and keeps nothing between
two calls, so a stream is a sequence of calls. Each blue force becomes an Entity — position in
WGS 84 decimal degrees, speed, a course in [0, 360) as course_deg with the north reference
assumed true and said so, a symbol only from a MIL-STD-2525D numeric code, and the message and
entry exactly as stated in the typed block tacticalapi-blueforce/1 — and an Event,
TRACK_UPDATE, or STATUS_CHANGE for a deleted entry, which is a status and never an invented
valid_to. Affiliation reads UNKNOWN, because the message carries no affiliation field, unless
the caller supplies one keyword-only as TacticalapiAdapter(affiliation=...). Every field the
contract does not name is carried in the structured residual that every Entity and every Event
carries, and never dropped.

What it refuses, by name. Every refusal the codec and the adapter raise is a
TacticalapiRefused, a ValueError whose code is one of the codec's 31 reason codes: every other message of the contract and every
other type; a response whose header does not report success; a blue force without an identity;
a coordinate out of range; encodings a lenient protobuf parser accepts and repairs — a singular
field twice, two members of one oneof, a named field with another wire type, an out-of-range or
overlong varint; a truncated input; and more unknown fields, objects or carried text than the
declared bounds. A payload above the declared 4 195 328-octet bound is refused by the base class
before the decoder runs, and nothing is repaired. The adapter's tests assert the refusals by
name, on the eight committed payloads under fixtures/tacticalapi/malformed/ and on inputs the
tests generate.

Where its behaviour comes from. The contract's ten interface definition files, pinned at
upstream commit 58661c9 outside every repository and found through
SYNAPSE_CDM_TACTICALAPI_PROTO_DIR; the maintainer ruled that the Eclipse Public License 2.0
governs them. The adapter embeds only their field names, field numbers and enum values, and both
copies of NOTICE state that derivation; no other text of the files is carried, and the files are
neither in this repository nor in the wheel. Every payload is synthetic and starts as protoc's own
encoding of a text source against the pinned files. The adapter declares the licence class OPEN.
This repository is not affiliated with, endorsed by or reviewed by the interface's publisher, and
the name says which published contract the adapter reads: it is kept, on the maintainer's ruling,
without seeking the publisher's written consent.

External interoperability is a separate conclusion, and it is NOT claimed

Local implementation completion and external interoperability completion are two conclusions.
The first is reached for tacticalapi and this release claims it. The second is not claimed
anywhere in the tree: no TacticalAPI server, client or captured message has been exercised, and no
exchange with any system is asserted.

  • The five evidence categories, as generated by python -m synapse_cdm.evidence generate --adapter tacticalapi on this tree — the records themselves are generated, gitignored, and
    attached by a Release rather than committed: internal_fixture PRESENT; self_round_trip
    NOT_APPLICABLE, since the adapter is ingest only; independent_expected ABSENT;
    normative_schema ABSENT; independent_endpoint ABSENT.
  • The comparison with the pinned contract is a test that needs the pinned files and protoc,
    and it does not run in CI.
    The two tests that rebuild every generated fixture and compare the
    embedded field table with the pinned descriptor read the directory
    SYNAPSE_CDM_TACTICALAPI_PROTO_DIR names; no workflow sets it, so in CI they record
    BLOCKED_EXTERNAL_EVIDENCE rather than passing.
  • Declared maturity is L3 and claim status VERIFIED, read from manifests/tacticalapi.json.
    L4 is not declared: there is no egress direction for information to be lost in.
  • evidence.available is true on tacticalapi from this release. The adapter landed
    declaring false — "it becomes true at the first release that attaches them" — and
    tests/test_cdm_evidence.py holds the field to the newest release tag's tree. The release
    commit flipped it, with a limitation sentence that names no version, the form dis7's took at
    3.2.0: .github/workflows/publish.yml generates the records for every shipped adapter and
    attaches them to the Release of the version it publishes. What the field does NOT say: nothing
    about the wheel's contents, and nothing about the three external categories.

What changed for a 3.2.0 consumer

  • Nothing on the wire. No field, enum, schema or golden of the twenty that predate the arc
    moved; git diff v3.2.0 -- schemas/ is empty.
  • A new adapter, tacticalapi, with the keyword-only affiliation= argument, and a new
    fixture directory
    , fixtures/tacticalapi/; --list-adapters reports twenty-one and the
    harness replays tacticalapi with --adapter tacticalapi and no --fixtures. No other
    adapter's verdict moved.
  • NOTICE gains one paragraph, in both copies, stating that the embedded field table derives
    from interface definition files the Eclipse Public License 2.0 governs. The wheel's licence
    metadata does not move: license stays Apache-2.0 and license-files stays LICENSE and
    NOTICE.
  • The [lint] extra pins ruff 0.16.9. The rule set the workflows read from it was measured
    unchanged under the new version (MIGRATIONS.md's 3.3.0 section, the paragraph that begins THE
    RUFF RECORD).

What else moved

  • The wheel gate knows the new modules. gates/wheel_install.py decides each of the five new
    test modules on one of its two lists, and its licences check asserts the derivation statement
    in the installed wheel's NOTICE; tests/test_cdm_packaging.py asserts it offline in both copies.
    Three gate modules of the arc's own, gates/tacticalapi_field_table.py,
    gates/tacticalapi_contract_comments.py and gates/protoc_text.py, regenerate the embedded
    field table and check that no text of the pinned files reached the arc's files.
  • The dependency unit of 2026-10-06 landed by hand, as one signed-off commit: the ruff pin
    above; react, react-dom and @types/react at ^19.3.0 and three overrides floors in
    docs/package.json (serialize-javascript, fast-uri and shell-quote), which now carries
    fifteen overrides entries; and anchore/sbom-action at v0.24.3 at its four sites in
    publish.yml and rc-build.yml. security/exceptions/GHSA-vfj7-8cjw-p6xm.json, the one
    exception, still excepts braces, which has no fixed release, until 2026-12-04, with the
    maintainer as owner. All of the documentation site's dependencies are build-time and ship in
    nothing.
  • Five open items found on the way are recorded and not fixed here, in the implementation
    record's "Open items": adapter.wire_size raises UnicodeEncodeError for a str holding a
    lone surrogate before any adapter can refuse it; adapter.wire_size and
    adapter.container_depth take a value's type from isinstance, so an in-process object whose
    __class__ claims octets, text, a dict or a list raises there; the adapter's
    validate_source writes a refusal's text whole; no test raises a plain ValueError on the path
    of validate_source that handles one; and a released memoryview leaves the codec's entry
    points, and the host's wire_size, with Python's own ValueError and no reason code. None
    touches a claim of this release: four need an input only an in-process caller can build, and
    the fifth is a test the arc did not write.

Twenty-one adapters at 3.3.0, all harness-verified

python -m synapse_cdm.harness --adapter <name> --schemas schemas --json, run over the roster
with no --fixtures on this tree, and every verdict read from the run. The table is the live registry, and tests/test_cdm_release.py::test_the_release_notes_roster_table_is_the_registry requires both directions to agree. python -m synapse_cdm.harness --list-adapters prints 21 adapters registered. Declared maturity and claim status are read from manifests/<name>.json.

Adapter Direction Fixture verdicts Declared maturity Claim
adsb bidirectional 32 L3 VERIFIED
ais bidirectional 22 L3 VERIFIED
cat021 bidirectional 40 L3 VERIFIED
cat023 bidirectional 34 L3 VERIFIED
cat034 bidirectional 34 L3 VERIFIED
cat048 bidirectional 82 L3 VERIFIED
cat062 bidirectional 56 L3 VERIFIED
gmti bidirectional 32 L3 VERIFIED
legion ingest 6 L3 VERIFIED
pntmap ingest 4 L3 VERIFIED
stanag4586 ingest 24 L3 VERIFIED
stanag4609 bidirectional 126 L3 VERIFIED
stanag4676 bidirectional 34 L3 PROVISIONAL
tak bidirectional 12 L3 VERIFIED
geojson bidirectional 4 L4 VERIFIED
geopackage ingest 8 L3 VERIFIED
c2sim bidirectional 10 L4 VERIFIED
aixm511 ingest 10 L3 VERIFIED
aixm52 ingest 10 L3 VERIFIED
dis7 bidirectional 6 L4 VERIFIED
tacticalapi ingest 10 L3 VERIFIED (new in the 3.3.0 arc)

596 fixture verdicts, 0 failed across the twenty-one, against the published 3.0.0 schemas —
the 586 that 3.2.0 shipped, unchanged, plus 10 from the new set. The two readings the wheel gate
takes, its resources and harness checks, run on this tree from a directory outside the
repository, read 21 adapters, 617 fixture files and 21 adapters x 2 schema modes, 1192 fixture verdicts, 0 failed;
gates/wheel_install.py takes the same readings off the installed wheel in the release workflow.
The lossless column reads PASS on every JSON fixture and a stated SKIP on every non-JSON one,
and FAIL on none, on a declared ledger for pntmap, the five of the 3.1.0 arc, dis7 and
tacticalapi, and on the value-presence heuristic, said so, for the other thirteen. The three L4
declarations (geojson, c2sim, dis7) rest on a ledger that reports no loss over a self round
trip under the adapter's declared tolerance; every other declaration is L3.

Nine schema files, regenerated from the models by python -m synapse_cdm.schemas and
byte-identical to the committed ones: entity, event, track, plan_object,
payload_gnss_interference and cdm_object under schemas/, manifests/adapter-manifest and
evidence/evidence and evidence/exercise beside them. None of the nine moved in this arc.

Published by CI over OIDC, as 1.1.0 through 3.2.0 were

No API token. .github/workflows/publish.yml builds on the tagged tree, gates that build with
gates/wheel_install.py --mutation-check, runs twine check --strict, checks that the tag names
the tree's PACKAGE_VERSION, and uploads those same files through PyPI Trusted Publishing after a
required reviewer approves the pypi environment. PUBLICATION.md ledger entry 6 records the
configuration, and entry 24 records the 3.2.0 upload.

Artefacts

An sdist and a wheel, built once by the workflow, gated as that build, and uploaded as those same
files. Their SHA-256 digests are recorded in PUBLICATION.md's ledger together with the
workflow run that produced them. The GitHub Release additionally carries both SBOMs, the evidence
set, the conformance report, the witness record and these notes — so the evidence a claim rests
on is retrievable with the release rather than only as a workflow artefact with a retention
window.

They are deliberately not committed here, for the reason this file has given since 1.1.0. A digest
is a property of one build rather than of the tree: two builds of one tree have identical payloads
but differ in their generated metadata, so a digest written here before the tag would not be the
digest of the file PyPI serves, and one written after the tag could never be inside the tree the tag
names. 1.3.0 measured that the hard way — a local build and the published wheel came out at the
same byte count and were different files — so the digests to compare a download against are the
workflow's, never a rebuild's. Everything else in this document is readable off that tree, which is
what condition 4 of the release procedure asks for.

pip install synapse-cdm==3.3.0
python -m synapse_cdm.harness --list-adapters

Adapter status

21 adapters ship in this distribution. declared is the rung the
adapter's manifest claims; eligible is the rung this release's conformance
sweep allows it. A declared rung above an eligible one is a defect, not a note.

adapter version direction declared eligible claim result
adsb 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
ais 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
aixm511 1.0.0 ingest L3 L5 VERIFIED CONFORMANT
aixm52 1.0.0 ingest L3 L5 VERIFIED CONFORMANT
c2sim 1.0.0 bidirectional L4 L5 VERIFIED CONFORMANT
cat021 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
cat023 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
cat034 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
cat048 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
cat062 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
dis7 1.0.0 bidirectional L4 L5 VERIFIED CONFORMANT
geojson 1.0.0 bidirectional L4 L5 VERIFIED CONFORMANT
geopackage 1.0.0 ingest L3 L5 VERIFIED CONFORMANT
gmti 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
legion 1.0.0 ingest L3 L5 VERIFIED CONFORMANT
pntmap 1.0.0 ingest L3 L5 VERIFIED CONFORMANT
stanag4586 1.0.0 ingest L3 L5 VERIFIED CONFORMANT
stanag4609 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT
stanag4676 1.0.0 bidirectional L3 L5 PROVISIONAL CONFORMANT
tacticalapi 1.0.0 ingest L3 L5 VERIFIED CONFORMANT
tak 1.0.0 bidirectional L3 L5 VERIFIED CONFORMANT

Security status

control state
GitHub secret scanning active
Secret-scanning push protection active
Private vulnerability reporting active
Secret scanning in CI (gitleaks) active
No secret in the repository's history active
Input bounds on every adapter active
Parser-safety policy active
Resource isolation of the conformance worker active
Streaming input not provided
Verification of the integrity block not provided
Signed commits active
Dependabot alerts and the dependency graph active
Dependabot security updates active
Dependabot version updates active
Dependency review on pull requests active
Python dependency audit active
npm dependency audit over docs/ active
Pinned floors on transitive npm dependencies of docs/ active
CodeQL, with a gate that fails the build active
SBOM, two formats active
Build attestation, no long-lived key active
Documented, time-bounded security exceptions active
Secret-scanning non-provider patterns not enabled in this round
Secret-scanning validity checks not enabled in this round
Cryptographic signing of CDM objects not provided
Defence against a producer asserting false semantics not provided
  • CodeQL, this release's run: 0 result(s), 0 blocking at or above 7.0; gate exit 0 over 4 analysis file(s) of e3acf78
  • pip-audit, this release's run: No known vulnerabilities found; strict, over 43 third-party distribution(s), ignore-vuln flags in force: [--ignore-vuln GHSA-vfj7-8cjw-p6xm]
  • Security exceptions in force: 1 — GHSA-vfj7-8cjw-p6xm.json

Known limitations

Stated by the adapters themselves, in their manifests. This is not a survey of what
the package cannot do; it is what each adapter says it does not do.

adsb

  • the edition this adapter is written against is NOT STATED by any document in this repository — no pin record exists for RTCA DO-260x or ICAO Annex 10, both of which fixtures/adsb/spec/adsb_terms.json records as priced, so format.version is null rather than guessed
  • DF17 and DF18 only; every other downlink format is out of scope
  • a single frame carries no unambiguous position — the 17-bit CPR fields need a second frame of the opposite parity or a reference position, and no frame carries a time at all
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

ais

  • the edition this adapter is written against is NOT STATED by any document in this repository — the format has two publishers, ITU-R (M.1371) and NMEA (0183), and fixtures/ais/spec/ais_terms.json records both without naming the edition this implementation targets, so format.version is null rather than guessed
  • message types 1, 2, 3, 4, 5, 18, 19 and 21 are in scope; every other type is named in FORMAT_COVERAGE.md with the reason it is out
  • type 24 static data is out of scope, so a vessel's name and call sign arrive in no message this adapter reads
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

aixm511

  • {'id': 'pinned-families', 'notes': None, 'severity': None, 'summary': 'the five families are Airspace, AirportHeliport, Runway, RunwayDirection, Navaid and VerticalStructure, plus the Digital NOTAM event:Event (its own reading, limitation digital-notam-profiles); a feature of any other class (Unit, OrganisationAuthority, Route, Procedure …) becomes an Entity of type UNKNOWN carrying the GENERIC reading only — identity, interpretation, sequence and correction numbers, validTime, featureLifetime, the pinned simple properties the slice happens to state, and the ledger-bound complex elements (CARRIED_ELEMENTS: location, ARP, a repeated availability — or a single one on the eleven NavaidEquipment classes NAV.UNS binds, whose time-slice types list it — activation, part, navaidEquipment, geometryComponent) carried source-verbatim at their block field — with every other property in the residual and no position or status, and validate_source names it; a member that is not a feature at all (no aixm:timeSlice) is carried whole in the residual. Within a family only the pinned properties are typed (the implementation record lists them); the rest is residual', 'unsupported_paths': []}
  • {'id': 'digital-notam-profiles', 'notes': None, 'severity': None, 'summary': "Digital NOTAM is read on the AIXM 5.1.1 + Event Schema 2.0.m combination only (Event 2.0.k and 2.0.l accept the same documents; 1.0 / 2.0.f / 2.0.j do not; no Event schema targets AIXM 5.2). The pinned scenario profiles are exactly RWY.CLS, ATSA.ACT, SAA.ACT and NAV.UNS at scenario version 2.0 of the Digital NOTAM Specification 2.0, which is a DRAFT ('in preparation', validated by implementation) and is never described as an approved final standard; an Event stating any other scenario is typed and carried with profile_finding and no scenario rule is checked. The rule layer holds a bound slice to the rules it can be held to on its own — the binding rule (TEMPDELTA on a feature class the profile names) and the status rule (RWY.CLS.03, ER-01/ER-02, NAV.UNS.04) on the change's own structures; a structure carrying the REMARK 'Baseline data copy. Not included in the NOTAM text generation' (ATSA.ACT ER-04, SAA.ACT ER-06, NAV.UNS.08) restates the baseline and is kept, not judged — as findings, never repairs; the schema layer is normative_validation. NOTHING is resolved by this adapter: correction, cancellation, replacement and expiry are typed as the slice's own source assertion, and the resolved view at an instant is synapse_cdm.aixm_resolve, which takes explicit prior state. The event extension on a feature slice is read and not consumed (it stays in the residual at its position)", 'unsupported_paths': []}
  • {'id': 'unsupported-geometry-refused', 'notes': None, 'severity': None, 'summary': "gml:ArcByCenterPoint, gml:CircleByCenterPoint, gml:Arc, gml:ArcString, gml:Circle, gml:CompositeCurve, gml:OrientableCurve, a CRS other than urn:ogc:def:crs:EPSG::4326 and urn:ogc:def:crs:OGC:1.3:CRS84, and a third dimension refuse the document by default with the element form and the source text named; under Aixm511Adapter(unsupported_geometry='report') the slice is an Entity with the form named in attributes.aixm and no PlanObject. No arc is replaced by a chord and no circle by a polygon", 'unsupported_paths': []}
  • {'id': 'composite-volumes-not-drawn', 'notes': None, 'severity': None, 'summary': 'an Airspace whose geometry components use UNION, INTERS or SUBTR, or whose BASE components state different vertical limits, is typed component by component and produces no PlanObject: the CDM Area is one volume and composing one would be a derivation the source did not state. volume_projection on the block says why; validate_source names it', 'unsupported_paths': []}
  • {'id': 'vertical-projection-by-member', 'notes': None, 'severity': None, 'summary': 'a vertical limit projects onto the CDM only where a VerticalUnit and a VerticalReference exist for it (FT/M against SFC/MSL/W84/STD, FL against STD); SM, a flight level against another reference, an unstated uom and the tokens UNL/GND/FLOOR/CEILING are carried typed with the reason they are not projected. Feet are never converted to metres and no datum is converted', 'unsupported_paths': []}
  • {'id': 'cancelled-slice-needs-as-of', 'notes': None, 'severity': None, 'summary': "a time slice with no validTime instant of its own (§4.4.8's cancellation with nilReason='inapplicable', an absent validTime, an indeterminate begin) can be an Entity only under Aixm511Adapter(as_of=AsOf(instant, basis)), whose instant becomes valid_from with the basis in source.transformations; without it the document is refused naming the slice. Nothing is stamped with the clock or the epoch", 'unsupported_paths': []}
  • {'id': 'identifier-required', 'notes': None, 'severity': None, 'summary': 'a feature of a pinned family without a gml:identifier is refused: identity is derived from the UUID the feature carries across documents (Feature Identification and Reference 1.0 §2.1), and a document-local gml:id would make the same feature two entities in two documents', 'unsupported_paths': []}
  • {'id': 'references-one-hop', 'notes': None, 'severity': None, 'summary': "xlink:href is resolved against the document and the caller's references table, one hop, never over a network; a URL is classified and not fetched; unresolved references are kept with their property, href and form under attributes.aixm.references and listed in unresolved_references", 'unsupported_paths': []}
  • {'id': 'schedules-not-resolved', 'notes': None, 'severity': None, 'summary': 'Timesheets, activation and availability structures are carried as typed validity, never resolved to instants; status on an Entity is set only when the slice states exactly one such structure and it carries no timeInterval, so an unconditional status is never asserted from a scheduled one', 'unsupported_paths': []}
  • positions carry position_source: MANUAL — a surveyed, published coordinate is none of GNSS, INERTIAL or ESTIMATED — with the MSL elevation of an ElevatedPoint in vertical (its uom kept, reference MSL by the model's own definition of elevation) and alt_m None because MSL is not height above the ellipsoid
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged. evidence.available is true because 3.1.0 is the first release carrying this adapter and its pipeline generates the records for every shipped adapter and attaches them to the v3.1.0 Release as evidence-3.1.0.tar.gz, retrievable by a third party; it says nothing about what the wheel contains, and a tag that released nothing carries this sentence to nobody
  • of §3.5's five resource limits this adapter enforces THREE — max_input_bytes by the base class before decode and again by secure_xml before a parser exists, max_depth and max_objects in this module before any object is built — plus an element count the manifest schema has no field for. The other two are absent with their reasons in capabilities.limits.absent_because
  • XML is parsed with the standard library's expat through secure_xml and NOT with defusedxml, which is not a dependency of this package: a DOCTYPE is refused at its declaration before any entity is read, every entity reference other than the five predefined ones is refused, no external resource is fetched and XInclude is refused where it starts (tests/test_cdm_secure_xml.py). The tree is xml.etree.ElementTree's own, built through its TreeBuilder
  • this adapter reads AIXM 5.1.1 only: a document in the 5.1 or 5.2 namespace is refused at the root with its namespace named; AIXM 5.2 is a sibling adapter

aixm52

  • {'id': 'pinned-families', 'notes': None, 'severity': None, 'summary': "the five families are Airspace, AirportHeliport, Runway, RunwayDirection, Navaid and VerticalStructure, with the AIXM 5.2 property groups pinned (the implementation record lists the 5.2 paths per family and the 5.1.1 → 5.2 differences); a feature of any other class (Unit, OrganisationAuthority, Route, Procedure …) becomes an Entity of type UNKNOWN carrying the GENERIC reading only — identity, interpretation, sequence and correction numbers, validTime, featureLifetime, the pinned simple properties the slice happens to state, and the ledger-bound complex elements (CARRIED_ELEMENTS: location, ARP, a repeated availability, activation, part, navaidEquipment, geometryComponent) carried source-verbatim at their block field — with every other property in the residual and no position or status, and validate_source names it; a member that is not a feature at all (no aixm:timeSlice) is carried whole in the residual. Within a family only the pinned properties are typed; the rest is residual — including the 5.2 AirspaceVolume name and location, which ride in the component's verbatim source and are not typed", 'unsupported_paths': []}
  • {'id': 'digital-notam-not-available', 'notes': None, 'severity': None, 'summary': "No Digital NOTAM Event schema exists for AIXM 5.2: every published Event Schema revision (5.1-e, 5.1.1-f/-j/-k/-l/-m) targets http://www.aixm.aero/schema/5.1.1/event or …/5.1/event, and the publisher states 'The current specifications for … Digital NOTAM are based on AIXM 5.1.1' (https://aixm.aero/page/aixm-52, read 2026-09-20). This adapter therefore reads NO Digital NOTAM: no Event feature, no scenario profile (profiles is empty), no attributes.dnotam block and no scenario rule; a member in the AIXM 5.1.1 Event namespace inside a 5.2 document is named by validate_source, carried whole in the residual and never typed. Digital NOTAM is the AIXM 5.1.1 adapter's (aixm511, limitation digital-notam-profiles), on the AIXM 5.1.1 + Event Schema 2.0.m combination only; nothing here claims that the same extension works with 5.2", 'unsupported_paths': []}
  • {'id': 'unsupported-geometry-refused', 'notes': None, 'severity': None, 'summary': "gml:ArcByCenterPoint, gml:CircleByCenterPoint, gml:Arc, gml:ArcString, gml:Circle, gml:CompositeCurve, gml:OrientableCurve, a CRS other than urn:ogc:def:crs:EPSG::4326 and urn:ogc:def:crs:OGC:1.3:CRS84, and a third dimension refuse the document by default with the element form and the source text named; under Aixm52Adapter(unsupported_geometry='report') the slice is an Entity with the form named in attributes.aixm and no PlanObject. No arc is replaced by a chord and no circle by a polygon", 'unsupported_paths': []}
  • {'id': 'composite-volumes-not-drawn', 'notes': None, 'severity': None, 'summary': 'an Airspace whose geometry components use UNION, INTERS or SUBTR, or whose BASE components state different vertical limits, is typed component by component and produces no PlanObject: the CDM Area is one volume and composing one would be a derivation the source did not state. volume_projection on the block says why; validate_source names it', 'unsupported_paths': []}
  • {'id': 'vertical-projection-by-member', 'notes': None, 'severity': None, 'summary': "a vertical limit projects onto the CDM only where a VerticalUnit and a VerticalReference exist for it (FT/M against SFC/MSL/W84/STD, FL against STD); SM, a flight level against another reference, an unstated uom and the tokens UNL/GND/FLOOR/CEILING are carried typed with the reason they are not projected. Feet are never converted to metres and no datum is converted. An ElevatedPoint's verticalDatum is free text in 5.2 (TextNameType) and is carried as the text stated", 'unsupported_paths': []}
  • {'id': 'cancelled-slice-needs-as-of', 'notes': None, 'severity': None, 'summary': "a time slice with no validTime instant of its own (§4.4.8's cancellation with nilReason='inapplicable', an absent validTime, an indeterminate begin) can be an Entity only under Aixm52Adapter(as_of=AsOf(instant, basis)), whose instant becomes valid_from with the basis in source.transformations; without it the document is refused naming the slice. Nothing is stamped with the clock or the epoch", 'unsupported_paths': []}
  • {'id': 'identifier-required', 'notes': None, 'severity': None, 'summary': 'a feature of a pinned family without a gml:identifier is refused: identity is derived from the UUID the feature carries across documents (Feature Identification and Reference 1.0 §2.1), and a document-local gml:id would make the same feature two entities in two documents. The gml:identifier a 5.2 «object» may carry is a property of that object, never an identity', 'unsupported_paths': []}
  • {'id': 'references-one-hop', 'notes': None, 'severity': None, 'summary': "xlink:href is resolved against the document and the caller's references table, one hop, never over a network; a URL is classified and not fetched; unresolved references are kept with their property, href and form under attributes.aixm.references and listed in unresolved_references", 'unsupported_paths': []}
  • {'id': 'schedules-not-resolved', 'notes': None, 'severity': None, 'summary': 'Timesheets, activation and availability structures are carried as typed validity, never resolved to instants; status on an Entity is set only when the slice states exactly one such structure and it carries no timeInterval, so an unconditional status is never asserted from a scheduled one', 'unsupported_paths': []}
  • positions carry position_source: MANUAL — a surveyed, published coordinate is none of GNSS, INERTIAL or ESTIMATED — with the MSL elevation of an ElevatedPoint in vertical (its uom kept, reference MSL by the model's own definition of elevation) and alt_m None because MSL is not height above the ellipsoid
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged. evidence.available is true because 3.1.0 is the first release carrying this adapter and its pipeline generates the records for every shipped adapter and attaches them to the v3.1.0 Release as evidence-3.1.0.tar.gz, retrievable by a third party; it says nothing about what the wheel contains, and a tag that released nothing carries this sentence to nobody
  • of §3.5's five resource limits this adapter enforces THREE — max_input_bytes by the base class before decode and again by secure_xml before a parser exists, max_depth and max_objects in the shared reader before any object is built — plus an element count the manifest schema has no field for. The other two are absent with their reasons in capabilities.limits.absent_because
  • XML is parsed with the standard library's expat through secure_xml and NOT with defusedxml, which is not a dependency of this package: a DOCTYPE is refused at its declaration before any entity is read, every entity reference other than the five predefined ones is refused, no external resource is fetched and XInclude is refused where it starts (tests/test_cdm_secure_xml.py). The tree is xml.etree.ElementTree's own, built through its TreeBuilder
  • this adapter reads AIXM 5.2 only: a document in the 5.1 or 5.1.1 namespace is refused at the root with its namespace named; AIXM 5.1.1 is the sibling adapter aixm511, and the two share one reader under two profiles (aixm511.Profile)

c2sim

  • {'id': 'untyped-object-class', 'notes': None, 'severity': None, 'summary': "an initialisation's Person, NonMilitaryOrganization, CommunicationNetwork, Overlay, CulturalFeature, EnvironmentalObject, GeographicFeature, non-Route map graphic, Action and PlanPhaseReference produce no canonical object: each is carried whole in every object's residual at its own path under residual.data.MessageBody and named by validate_source. The typed classes are ForceSide, Unit, the four Platform kinds and the Route map graphic", 'unsupported_paths': []}
  • {'id': 'unsupported-message-body', 'notes': None, 'severity': None, 'summary': 'AcknowledgementBody, PlanBody, RequestBody, ObjectInitializationBody, SystemAcknowledgementBody and SystemCommandBody are refused by name: session and protocol state live in the exercise client (examples/c2sim/exercise_client.py), never in the adapter', 'unsupported_paths': []}
  • {'id': 'pinned-task-forms', 'notes': None, 'severity': None, 'summary': 'a ManeuverWarfareTask whose TaskActionCode is not MoveToLocation or HoldInPlace (decision D1) is refused with a diagnostic naming the two supported forms, on ingest and on egress; no other task is ever substituted', 'unsupported_paths': []}
  • {'id': 'untyped-observations', 'notes': None, 'severity': None, 'summary': 'ActivityObservation, ResourceObservation and SubjectTypeObservation are carried whole under payload.c2sim.observations[].raw (typed: false); Health, Location and Name observations are typed', 'unsupported_paths': []}
  • {'id': 'simulation-time-needs-a-clock', 'notes': None, 'severity': None, 'summary': "a SimulationTime instant is scenario seconds since the scenario epoch and resolves only against C2simAdapter(exercise=ExerciseClock(epoch, basis)); a RelativeTime never resolves here. An Event whose observed_at would need an unresolved instant is refused, naming the form; a task's start and end times are carried typed either way", 'unsupported_paths': []}
  • {'id': 'affiliation-needs-own-side', 'notes': None, 'severity': None, 'summary': "affiliation is read from the own side's stated ForceSideRelation only when C2simAdapter(own_side=<uuid>) names it (FR, HO, NEUTRL; every other hostility code reads UNKNOWN with the code kept); without it every entity is UNKNOWN. Nothing is inferred from a name or a SIDC", 'unsupported_paths': []}
  • {'id': 'extension-not-reemitted', 'notes': None, 'severity': None, 'summary': 'elements and attributes outside the pinned vocabulary are preserved and listed on ingest but are not written back on egress, so an emitted document stays inside the schema the validator judges; a re-ingested round trip therefore lacks them, and the harness fixtures carry none', 'unsupported_paths': []}
  • the APP-6 SIDC string a source states is carried verbatim as a classification and never becomes Entity.symbol, which is a 20-digit MIL-STD-2525D code; positions carry position_source: ESTIMATED, the understating reading legion applies to a simulation's fixes, and their MSL or AGL height is kept in vertical unconverted with alt_m None
  • egress of an Entity with no attributes.c2sim block is refused: the schema needs a UUID, at least one EntityType and, for a Unit, an EchelonCode, and none of these is invented from the CDM's own fields
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged. evidence.available is true because 3.1.0 is the first release carrying this adapter and its pipeline generates the records for every shipped adapter and attaches them to the v3.1.0 Release as evidence-3.1.0.tar.gz, retrievable by a third party; it says nothing about what the wheel contains, and a tag that released nothing carries this sentence to nobody
  • of §3.5's five resource limits this adapter enforces THREE — max_input_bytes by the base class before decode and again by secure_xml before a parser exists, max_depth and max_objects in this module before any object is built — plus an element count the manifest schema has no field for. The other two are absent with their reasons in capabilities.limits.absent_because
  • XML is parsed with the standard library's expat through secure_xml and NOT with defusedxml, which is not a dependency of this package: a DOCTYPE is refused at its declaration before any entity is read, every entity reference other than the five predefined ones is refused, no external resource is fetched and XInclude is refused where it starts (tests/test_cdm_secure_xml.py). The tree is xml.etree.ElementTree's own, built through its TreeBuilder

cat021

  • ADS-B MOPS versions 0, 1 and 2 in full; version 3 (ED-102B / DO-260C) only partially, and mostly in the Reserved Expansion Field
  • Edition 2.6 states on its own cover that it is not backwards compatible to Edition 2.1 or earlier, and this adapter does not read those editions
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

cat023

  • the smallest specification pinned in this repository: nine data items, and nothing outside them is decoded
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

cat034

  • the pinned edition is 1.29 and it is NOT the latest: fixtures/cat034/spec/cat034_pin.json records Edition 1.30 as available, and this adapter is written against 1.29
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

cat048

  • the Reserved Expansion field is carried verbatim and never interpreted: no edition of this category defines its contents
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

cat062

  • the input is already the output of a fusion process, so the CDM objects this adapter emits carry judgements a tracker made and this adapter neither repeats nor re-decides
  • items the specification marks implementation-dependent are parked verbatim and not interpreted
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

dis7

  • Entity State subset only: protocol version 7, PDU type 1, protocol family 1. Every other PDU type, protocol family and DIS version is refused, and no network transport, capture file or multi-PDU datagram is read
  • no IEEE certification is claimed, and acceptance under this subset is not validation of every reserved bit of the standard
  • the IEEE 1278.1-2012 text was not consulted: the layout authority is open-dis-python (https://github.com/open-dis/open-dis-python, BSD-2-Clause) at commit 732b6655bb47e34ccc73722eefe0f4706fd0032f. A comparison with it exists as an opt-in test and an exercise report; no report ships in the distribution
  • the state instant is supplied by the caller with its basis; the DIS timestamp is preserved and never converted. Affiliation is UNKNOWN for every force ID, and velocity is projected only for dead reckoning algorithms 2 to 5
  • egress replays the original octets of an unchanged Entity only; a fresh or edited Entity is refused
  • fixture_instance supplies the packaged fixtures' context (session unnamed, synthetic, the vectors' state instant and basis) and refuses synthetic=False. The harness and the conformance suite construct through it, so their verdict is defined for the packaged fixtures only; PDUs from another exercise need a caller-built adapter
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run, and the release pipeline generates the records for every shipped adapter and attaches them to the GitHub Release of the version it publishes. evidence.available is true because every published distribution that carries this adapter has that Release, so its records are retrievable by a third party; it says nothing about what the wheel contains

geojson

  • {'id': 'geometry-collection', 'notes': None, 'severity': None, 'summary': "GeometryCollection is refused, not translated: geo.py keeps it out of the CDM union deliberately (a heterogeneous geometry has no single semantics for a consumer), so a feature carrying one refuses the whole document. The RFC's other six geometry types are all carried", 'unsupported_paths': []}
  • {'id': 'bare-geometry', 'notes': None, 'severity': None, 'summary': 'a GeoJSON text whose top-level object is a geometry (RFC 7946 §3 permits it) is refused: it has no id and no properties to carry, so nothing identifies it. Wrap it in a Feature', 'unsupported_paths': []}
  • {'id': 'empty-coordinates', 'notes': None, 'severity': None, 'summary': 'a geometry whose coordinates array is empty is refused. RFC 7946 §3.1 lets a processor read it as a null geometry; this adapter does not turn a stated geometry into an absent one silently', 'unsupported_paths': []}
  • {'id': 'legacy-crs', 'notes': None, 'severity': None, 'summary': 'a crs member at any level is refused. RFC 7946 §4 removed it; a document carrying one follows the 2008 specification, and no legacy profile is implemented here. Projected coordinates are never relabelled as WGS84', 'unsupported_paths': []}
  • {'id': 'empty-collection', 'notes': None, 'severity': None, 'summary': 'an empty FeatureCollection translates to no object, which is what it carries; its collection-level members then have no object to ride in and the preservation ledger reports them LOST for that document. Stated here so the empty result is never read as a filtered one', 'unsupported_paths': []}
  • {'id': 'no-source-time', 'notes': None, 'severity': None, 'summary': "RFC 7946 states no instant for a Feature or a FeatureCollection, so no object carries a timestamp unless the caller supplies an as-of context (GeojsonAdapter(as_of=AsOf(instant, basis))), which lands at validity.observed_at (PlanObject) or valid_from (Entity) with its basis in source.transformations. The conformance suite's check J reads this id as the declared reason it has no timestamp to judge", 'unsupported_paths': []}
  • {'id': 'bbox-not-projected', 'notes': None, 'severity': None, 'summary': 'bbox at every level is kept verbatim in the residual and is NOT projected to a canonical BoundingBox: PlanObject carries bounds only inside an Area, and geo.BoundingBox refuses an antimeridian-crossing box (west > east) by construction. Every bbox is noted in source.transformations, the antimeridian case as such', 'unsupported_paths': []}
  • egress accepts PlanObject and Entity (an entity's position becomes a Point, or a null geometry when it has none); an Event or a Track is not compatible with the geo-object profile and egress refuses it rather than choosing a geometry for it
  • egress under the exchange profile refuses a source property already named sc:... rather than overwriting it; the sc: keys of a re-ingested exchange document are ordinary source properties on ingest (this adapter never reads its own export keys back as canonical meaning), so such a document exports under the generic profile only
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged. evidence.available is true because 3.1.0 is the first release carrying this adapter and its pipeline generates the records for every shipped adapter and attaches them to the v3.1.0 Release as evidence-3.1.0.tar.gz, retrievable by a third party; it says nothing about what the wheel contains, and a tag that released nothing carries this sentence to nobody
  • of §3.5's five resource limits this adapter enforces THREE — max_input_bytes and max_depth by the base class before decode, and max_objects (the feature count) in this module before any object is built — plus a position count the manifest schema has no field for. The other two are absent with their reasons in capabilities.limits.absent_because

geopackage

  • {'id': 'version-1-4-0-only', 'notes': None, 'severity': None, 'summary': 'only GeoPackage 1.4.0 (user_version 10400, application_id GPKG) is read; any other user_version, and the GP10/GP11 application ids of 1.0 and 1.1, are refused as unsupported with the value reported. Earlier versions are not implemented and are not silently accepted', 'unsupported_paths': []}
  • {'id': 'wgs84-only', 'notes': None, 'severity': None, 'summary': "a selected layer's SRS must be WGS 84 by DEFINITION — EPSG:4326, OGC:CRS84 or EPSG:4979, judged on the WKT's root, datum, ellipsoid and authority; an 'undefined' definition (srs_id 0 or -1), a projected CRS or any other datum refuses the package with the declaration reported. Nothing is reprojected and no other CRS is relabelled", 'unsupported_paths': []}
  • {'id': 'measured-geometry', 'notes': None, 'severity': None, 'summary': 'M ordinates are refused, never dropped and never read as height: a layer declaring m = 1 or m = 2 is inventoried as unsupported content (refused if selected by name), and a row carrying an XYM or XYZM geometry, or an envelope with an M range, refuses the package', 'unsupported_paths': []}
  • {'id': 'geometry-collection-and-extended-types', 'notes': None, 'severity': None, 'summary': 'GeometryCollection, the non-linear extension types (CircularString, CompoundCurve, CurvePolygon, MultiCurve, MultiSurface, Curve, Surface) and ExtendedGeoPackageBinary (flag X = 1) are outside the profile: a layer declared with such a type is inventoried as unsupported; a row of a GEOMETRY column carrying one refuses the package', 'unsupported_paths': []}
  • {'id': 'non-vector-content', 'notes': None, 'severity': None, 'summary': "raster tiles, gridded coverages, attributes-only tables and views are inventoried as unsupported content in every object's residual.data.package.inventory and are never read; a mixed package translates its selected vector layers only and every object says so", 'unsupported_paths': []}
  • {'id': 'wal-dependent', 'notes': None, 'severity': None, 'summary': "a container whose SQLite header says WAL mode (write/read version 2) is refused as WAL-dependent, because its committed content may be in a -wal file the octets do not include; wal_checkpointed=True is the caller's assertion that the file was checkpointed, recorded on every object", 'unsupported_paths': []}
  • {'id': 'no-source-time', 'notes': None, 'severity': None, 'summary': "a GeoPackage states no observation time; gpkg_contents.last_change is the table's last-edit stamp and is carried as package metadata, never as a row's time. No object carries a timestamp unless the caller supplies an as-of context (GeopackageAdapter(as_of=AsOf(instant, basis))), which lands at validity.observed_at (PlanObject) or valid_from (Entity) with its basis in source.transformations. The conformance suite's check J reads this id as the declared reason it has no timestamp to judge", 'unsupported_paths': []}
  • {'id': 'z-meaning-as-declared', 'notes': None, 'severity': None, 'summary': "a Z ordinate is carried as the third coordinate element with the meaning the package declares and no other: under EPSG:4979 the SRS declares ellipsoidal height in metres; under EPSG:4326 or CRS84 the SRS declares no vertical axis and the value's datum and unit are undeclared — carried as sent, not asserted as height above the ellipsoid", 'unsupported_paths': []}
  • ingest only: from_cdm is the base class's refusal (NotImplementedError), the manifest advertises no egress, and no GeoPackage is ever written. The cross-format path is the GeoJSON adapter's export (examples/geopackage_to_geojson)
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged. evidence.available is true because 3.1.0 is the first release carrying this adapter and its pipeline generates the records for every shipped adapter and attaches them to the v3.1.0 Release as evidence-3.1.0.tar.gz, retrievable by a third party; it says nothing about what the wheel contains, and a tag that released nothing carries this sentence to nobody
  • of §3.5's five resource limits this adapter enforces THREE — max_input_bytes and max_depth by the base class before decode, and max_objects (the row count) in this module before any row is fetched — plus a per-value size, a coordinate count and an output estimate the manifest schema has no field for. The other two are absent with their reasons in capabilities.limits.absent_because

gmti

  • the Controlled Extension FIELD DEFINITIONS are not implemented and cannot be: AEDP-4607's §L.4, which is where their field tables belong, reads "(TO BE PROVIDED)" in the promulgated Edition A Version 1
  • the standard itself declines to specify error handling (§2.2), so a malformed packet's treatment is this adapter's decision and is documented at the site rather than cited
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

legion

  • a VENDOR API rather than a ratified standard: fixtures/legion/spec/openapi_pin.json records that it "can change between deploys, and its info.version demonstrably does not move when it does", so v3 names the endpoint family and not a frozen edition
  • ingest only — this adapter does not emit Legion documents and never acquires one: no HTTP client, no token, no cursor, no base URL
  • only the resources the pinned OpenAPI document names are in scope
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

pntmap

  • no document in this repository DEFINES the PNTMAP alert payload — FORMAT_COVERAGE.md carries no PNTMAP section — so there is no edition to name and format.version is null; the licence class rests on the payload being first-party rather than on a licensing sentence about the format
  • ingest only: this adapter does not emit PNTMAP alerts
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

stanag4586

  • Edition 4 is current (promulgated as AEP-84 Edition A) and is NOT implemented: FORMAT_COVERAGE.md records that nso.nato.int answers HTTP 403 on every route tried, that the mirror carrying this family lists exactly Editions 2 and 3, and that the commercial distributors holding Edition 4 serve it paywalled and DRM-wrapped. No sentence here claims an Edition 3 decoder reads an Edition 4 feed
  • ingest only: this adapter does not emit DLI
  • whether the 5-octet millisecond timestamp steps at a leap second is not stated by the document, and every object carries attributes.time_basis saying so
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

stanag4609

  • ST 0601.14 is the authoritative tag table; ST 0601.19 is pinned as CONTEXT ONLY and is never a source of tag semantics here
  • a length-divergent item's original octets are carried verbatim so that egress is byte exact, which means this adapter reproduces a defect rather than correcting it
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces ONE — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5). The other four are still absent, each with its own reason in capabilities.limits.absent_because; a depth, object-count, decompression or wall-clock bound is not enforced here today

stanag4676

  • Edition A (the STANAG 4676 Edition 1 generation) is read for the edition delta and is never a basis: Edition B §2.1.1.1 declares the two incompatible
  • the binary encoding of AEDP-12.1 Annex F is not implemented
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces TWO — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5), and max_depth, declared the same way and refused by parse_document immediately after the XML parse and before either reader recurses into the tree (2026-09-16). The other three are still absent, each with its own reason in capabilities.limits.absent_because; an object-count, decompression or wall-clock bound is not enforced here today
  • XML is parsed with the standard library's expat (pyexpat into an xml.etree.ElementTree tree, parse_document's _parse_xml) and NOT with defusedxml, which is not a dependency of this package (M's F5.5 ruling, round P5). Since F05 (2026-09-20) an EXTERNAL entity reference FAILS the parse at the reference and parameter-entity parsing is off, so an external DTD subset is never requested — both set on the parser rather than read off its defaults — but an INTERNAL entity IS expanded, and what stops an entity bomb is libexpat's own input-amplification limit rather than anything in this package. That protection belongs to the RUNTIME's expat build (2.4.0 and later, on by default), so a deployment on an older expat loses it without this package changing; max_input_bytes bounds the document either way. tests/test_cdm_parser_safety.py reads the linked version and takes every one of these readings rather than asserting them
  • the XML element names this adapter reads and writes are a PROVISIONAL internal profile (binding: provisional-internal-profile): AEDP-12's UML attribute names bound through one table (ELEMENT_NAMES) in no namespace, because the normative XSD is distributed through NATO national representatives (Ed B §B.5) and is not held here. Reader and writer agreeing through that table proves the profile is self-consistent, not that it is the standard's binding; FORMAT_COVERAGE.md marks every NITS row · provisional for the same reason. The verified normative binding is a separate, explicit mode (SYNAPSE_CDM_NITS_BINDING=normative) that validates every document read or emitted against the authorised schema through the local-resource hook SYNAPSE_CDM_NITS_XSD_DIR and FAILS BLOCKED_EXTERNAL_EVIDENCE without it — it never falls back to the profile, and no shipped fixture, golden or evidence record was produced under it

tacticalapi

  • {'id': 'blue-force-read-side-only', 'notes': None, 'severity': None, 'summary': 'reads the two read-side responses of the BlueForceTracking service, GetBlueForcesResponse and SubscribeBlueForceEventsResponse, and nothing else: every other message of the contract (own pose, situation objects, every request, the write-side blue-force messages) and every type outside it is refused unsupported-message-type, naming the two. Ingest only; no encoder exists', 'unsupported_paths': []}
  • {'id': 'capture-envelope', 'notes': None, 'severity': None, 'summary': 'the unit of ingest is one response wrapped in google.protobuf.Any (bytes) or its twin (dict), because a serialized message does not name its own type. gRPC framing, calls, sessions, stream bookkeeping and keep-alive timing are not read and not held: the caller receives each message and hands it over, and a stream is a sequence of calls', 'unsupported_paths': []}
  • {'id': 'pinned-commit-v0', 'notes': None, 'severity': None, 'summary': "written against rheinmetall.tactical_api.v0 at upstream commit 58661c9 (2026-09-01), which has no tag or release; the embedded field table is generated from protoc's descriptor of those pinned files. A field a later revision adds is carried as an unknown field; a new upstream commit is a new pin and a new adapter version, never a silent update", 'unsupported_paths': []}
  • {'id': 'coordinate-unit-not-stated', 'notes': None, 'severity': None, 'summary': 'the contract places the point on the WGS 84 ellipsoid and states no angular unit for latitude_coordinate and longitude_coordinate; they are read as decimal degrees, inferred from every other angle in the contract, not stated by it', 'unsupported_paths': []}
  • {'id': 'course-reference-not-stated', 'notes': None, 'severity': None, 'summary': 'point_location.course is in degrees with no stated north reference, and Kinematics.course_deg means degrees true: a course in [0, 360) is mapped to course_deg with the north reference ASSUMED true, an assumption course_basis states on every Entity it is applied to (R5, ruled 2026-10-06). A course outside that range, 360 included, is not mapped and not normalised; it stays in the typed block and validate_source names it', 'unsupported_paths': []}
  • {'id': 'symbol-2525d-only', 'notes': None, 'severity': None, 'summary': 'Entity.symbol is set only from a MIL-STD-2525D numeric code (catalog SYMBOL_CATALOG_MIL2525_D, first set 1 000 000 000 to 9 999 999 999, second set 0 to 9 999 999 999), unrewritten. Every other symbol — another catalog, the string form, a set out of range — is carried in the typed block unconverted and named by validate_source; no symbol is derived from the affiliation (R4, ruled 2026-10-06)', 'unsupported_paths': []}
  • {'id': 'proto3-zero-indistinguishable', 'notes': None, 'severity': None, 'summary': 'proto3 writes no scalar holding its default, so an unset coordinate pair and 0°N 0°E are the same bytes and a false bool reads as an unset one. A geo_point with neither coordinate on the wire yields no position, one with a single coordinate on the wire reads the other as 0.0 and position_basis says so, and blue_force_type flags that are not true give entity_type UNKNOWN, never a person or a unit. Likewise a Timestamp holding seconds 0 and nanos 0 (1970-01-01T00:00:00Z) is what a default-constructed one serialises to, so neither point_location.location_time nor last_contact_time holding it is read as a source time: the next choice stands, the basis says the zero Timestamp was passed over, validate_source names it, and its text stays in the typed block', 'unsupported_paths': []}
  • {'id': 'non-canonical-encoding-refused', 'notes': None, 'severity': None, 'summary': 'encodings a lenient protobuf parser accepts and repairs are refused by name: a singular field twice, two members of one oneof, a named field with another wire type, an int32 or enum varint outside 32 bits, a bool other than 0 or 1, a varint whose tenth octet sets bits past the 64th, a tag written in more than five octets, and a field the well-known types Any, Timestamp, StringValue and DoubleValue do not define. Nothing is repaired', 'unsupported_paths': []}
  • {'id': 'source-time-optional', 'notes': None, 'severity': None, 'summary': 'the read side marks no time field mandatory: valid_from and observed_at come from point_location.location_time, else last_contact_time, else the receipt instant from the injected clock, a zero Timestamp being passed over (proto3-zero-indistinguishable), and valid_from_basis and observed_at_basis say which. The CDM renders instants to the millisecond; the source text stays whole in the typed block. A deleted element gets no valid_to: the contract says only that the blue force is no longer present (R6, ruled 2026-10-06)', 'unsupported_paths': []}
  • {'id': 'no-endpoint-exercised', 'notes': None, 'severity': None, 'summary': "no TacticalAPI server, client or captured message has been exercised. Every payload is synthetic, and every one starts as protoc's own encoding of a text source against the pinned files; none was written from scratch by hand. Fourteen are protoc's bytes unchanged. Seven were changed afterwards by the fixture builder: three have hand-encoded fields appended, and four refusal payloads are protoc's bytes after one byte surgery each. protoc's own readings of the payloads are the only independent oracle", 'unsupported_paths': []}
  • {'id': 'evidence-availability', 'notes': None, 'severity': None, 'summary': 'the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run, and the release pipeline generates the records for every shipped adapter and attaches them to the GitHub Release of the version it publishes. evidence.available is true because every published distribution that carries this adapter has that Release, so its records are retrievable by a third party; it says nothing about what the wheel contains', 'unsupported_paths': []}
  • {'id': 'resource-limits', 'notes': None, 'severity': None, 'summary': 'of the five resource limits this adapter enforces three — max_input_bytes, max_depth and max_objects, each an implementation cap declared with its basis and refused before any object is built — plus two the manifest has no field for, declared beside max_objects: MAX_UNKNOWN_FIELDS (65 536 carried unknown fields per input) and MAX_CARRIED_COPY_CHARS (16 777 216 characters of compact JSON text, with ASCII escaping, of the message-level data carried across all the Entities and Events of one message, each copy counted). max_decompressed_bytes and max_parse_seconds are absent, each with its reason in capabilities.limits.absent_because', 'unsupported_paths': []}

tak

  • the CoT schema edition this adapter is written against is NOT STATED by any document in this repository — fixtures/tak/spec/tak_terms.json records the Base-Event Schema's own version as its publisher states it, which is a reading of that page and not a statement that this implementation targets it, so format.version is null rather than guessed
  • contact atoms only on ingest; CoT types outside a-.-... are out of scope
  • leftovers are parked in Entity.attributes / Event.payload under source_extras (lossless.residual()), not in the origin-identifying container ARCHITECTURE.md §5 gives to P3 — the Part 1 stance that section rules for the adapters already shipped
  • the evidence RECORD for this adapter is not IN the distribution: evidence/ is untracked and unpackaged, CI generates the set on every run and the release pipeline attaches it to the GitHub Release. evidence.available is true because the records for 2.1.2 are attached to the v2.1.2 Release and retrievable by a third party, and it says nothing about what the wheel contains
  • of §3.5's five resource limits this adapter enforces TWO — max_input_bytes, declared in capabilities.limits with its basis beside it and refused before decode by the base class (round P5), and max_depth, declared the same way and refused by this module immediately after the XML parse and before anything recurses into the tree (2026-09-16). The other three are still absent, each with its own reason in capabilities.limits.absent_because; an object-count, decompression or wall-clock bound is not enforced here today
  • XML is parsed with the standard library's xml.etree.ElementTree and NOT with defusedxml, which is not a dependency of this package (M's F5.5 ruling, round P5). An EXTERNAL entity is not resolved and an external DTD is not fetched — the parser never reads the external subset — but an INTERNAL entity IS expanded, and what stops an entity bomb is libexpat's own input-amplification limit rather than anything in this package. That protection belongs to the RUNTIME's expat build (2.4.0 and later, on by default), so a deployment on an older expat loses it without this package changing; max_input_bytes bounds the document either way. tests/test_cdm_parser_safety.py reads the linked version and takes every one of these readings rather than asserting them

Conformance summary

Checks A–O of the Synapse Conformance Suite over every
shipped adapter, from this release's own sweep.

adapter A B C D E F G H I J K L M N O result
adsb PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT
ais PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT
aixm511 PASS PASS PASS PASS SKIP PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT
aixm52 PASS PASS PASS PASS SKIP PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT
c2sim PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT
cat021 PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS CONFORMANT
cat023 PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS CONFORMANT
cat034 PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS CONFORMANT
cat048 PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS CONFORMANT
cat062 PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS CONFORMANT
dis7 PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS CONFORMANT
geojson PASS PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS SKIP SKIP PASS CONFORMANT
geopackage PASS PASS PASS PASS SKIP PASS PASS PASS PASS SKIP PASS PASS SKIP PASS PASS CONFORMANT
gmti PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT
legion PASS PASS PASS PASS SKIP PASS PASS PASS PASS PASS PASS PASS SKIP SKIP PASS CONFORMANT
pntmap PASS PASS PASS PASS SKIP PASS PASS PASS PASS PASS PASS PASS SKIP SKIP PASS CONFORMANT
stanag4586 PASS PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS CONFORMANT
stanag4609 PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS PASS SKIP PASS PASS CONFORMANT
stanag4676 PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT
tacticalapi PASS PASS PASS PASS SKIP PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT
tak PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT