Skip to content

synapse-cdm 3.0.1

Latest

Choose a tag to compare

@github-actions github-actions released this 20 Sep 14:44
· 6 commits to main since this release

synapse-cdm 3.0.1

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.0.1
CDM version 3.0.0
SC-OES version 0.1.0
Adapter API version 3.0.0
manifest schema version 2.1.0

Major changes

The corrective of the tagged-never-published 3.0.0, and the first published release of the audit
remediation arc.
v3.0.0 was tagged on 2026-09-20 and its own release run (35506445471) refused
it in the build job: the gate job had run the suite green on the same commit, and the build job's
second run of it — in an interpreter that job had first loaded with twine and cyclonedx-bom,
whose dependency closure made every spawned parser worker import a Lark grammar and seven format
libraries before its first byte of input — crossed a wall-clock budget in one isolation test and
stopped, recording a count and no name. Nothing reached PyPI. What moved between 3.0.0 and 3.0.1
is the release workflow (the tooling now lives in a venv of its own, and condition 4 names the
tests it fails on), MIGRATIONS.md and version.py; the distribution is otherwise byte-for-byte
the tree v3.0.0 named, so everything below describes this release. MIGRATIONS.md's 3.0.1
section is the record, and no test budget moved to get here.

The audit remediation release. Between the 2.2.0 release of 2026-09-17 and this one, the
independent audit of 2026-09-19 raised nine findings against this package, F01 to F09, and every
one of them is answered in the tree at this tag: version compatibility is directional and rests
on frozen evidence rather than on major-number arithmetic; the lossless check's empty result is
no longer treated as proof, and a path-bound preservation ledger replaces it wherever an adapter
declares its field mappings; the conformance suite's parser deadline is a real, process-isolated
one; the published JSON Schema now states every constraint the Python models enforce; the STANAG
4676 adapter's wire binding is declared provisional in its manifest, its CLI row and its page;
resource limits are enforced where the platform can enforce them and refused where it cannot,
never claimed; evidence records carry five categories with the three external ones honestly
ABSENT; governance is an auditable derivation with a runbook; and the documentation is derived
from the tree and drift-checked. The register of all nine, with reproductions, tests, evidence
paths and what stays external, is docs/audit-remediation-report.md. Two things are removed or
narrowed, which is why this is a MAJOR on both of the first two axes — read the next two sections.

If you are upgrading from 2.2.0 — which is what the index serves — read this as the MAJOR it
is on both numbers.
If you are upgrading from 2.1.2 or earlier, read the 2.2.0 notes first: they
are the body of the v2.2.0 Release, and everything they describe is still here.

Why the number is 3.0.1

The number is the derived floor twice over: a PATCH from v3.0.0 for the corrective — an arc of
the release workflow, MIGRATIONS.md and version.py, which gates/bump_derivation.py derives
as PATCH with nothing unruled — on top of the MAJOR the 3.0.0 release commit typed over v2.2.0,
the first MAJOR since 2.0.0.
For that arc, gates/bump_derivation.py reads from v2.2.0 and
derives MAJOR from one shape signal —
lossless.unrepresented, an importable name removed with no alias, because the old name was a
claim of proof the function could not make — and from the units whose meaning changed and were
ruled MAJOR in MIGRATIONS.md's 3.0.0 section: version.compatible and version.parse (F01),
harness.run and evidence.badges (F02), suite.check_malformed and
suite.check_parser_robustness (F03), conformance.assess_a, conformance._validator and
models.Timestamp (F04), manifest.AdapterMetadata, Stanag4676Adapter and parse_document
(F05), harness.load_raw (F06) and evidence.EvidenceRecord (F07). The units the table cannot
classify on its own are every one ruled in that section, and the gate reads those rulings and
reports nothing unruled.

Package version 3.0.1 · CDM schema_version 3.0.0 · Adapter API 3.0.0 · manifest schema 2.1.0
· evidence schema 2.0.0.
The first two were level at the 3.0.0 release commit and are one PATCH
apart at this one, and the equality was a coincidence of two separately argued majors and not a
derivation
: the package moved on version.py's table for a
removed name and ruled meaning changes; the schema moved on MIGRATIONS.md's table because
finding F04 NARROWED the published contract (next section). ADAPTER_API_VERSION moved
2.1.0 -> 3.0.0 on VERSIONING.md §3's own row because AdapterMetadata.binding is required with
no default; the manifest schema moved 1.2.0 -> 2.0.0 for that required field and 2.0.0 -> 2.1.0 for
one added enum member; the evidence schema moved 1.0.0 -> 2.0.0 for three required fields. 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,
and tests/test_cdm_packaging.py sweeps the package for an assignment that would derive one number
from another.

What changed on the wire, and what a 2.2.0 consumer must do

The published schema is narrower, and that is the MAJOR. The four object schemas under
schemas/ carry pattern on every schema_version and adapter_version, pattern on
Entity.symbol and uniqueItems on Entity.ontology_types. Every one of those constraints
restates what the Python models enforced already, so no document this package ever emitted becomes
invalid — but a consumer validating with the published schema alone accepted
"adapter_version": "banana" under 2.1.0 and is refused it under 3.0.0, and the bump table in
MIGRATIONS.md puts a narrowed type on the MAJOR row for exactly that reader. No path was removed, no
required list grew, no enum member went. The 2.1.0 contract stays frozen under
tests/frozen/cdm/2.1.0/ beside the 3.0.0 one under tests/frozen/cdm/3.0.0/, and
tests/test_cdm_version_matrix.py shows the narrowing on the frozen bytes.

Every golden moved by one stamp. The 538 golden files under the package's fixtures/*/golden/
and the eight reference-position goldens under fixtures/adsb/local/ carry
schema_version: "3.0.0" and differ from 2.2.0's by that line alone; the harness reads the same
538 fixture verdicts, 0 failed, on every adapter. The fourteen examples under examples/ declare
3.0.0 too (they declared 2.0.0 through two minors): synapse conformance reads a document's
schema_version against this package's, and a document of another major is refused in its
structural dimension rather than read on trust.

What to change in a 2.2.0 consumer, in one place:

  • Rename lossless.unrepresented to lossless.value_presence_heuristic, and read its {} as
    "nothing seen", not as proof.
  • Expect version.compatible(written_with, read_by) to answer False for any pair a major apart
    (("2.1.0", "3.0.0") in either direction), False for a writer newer than the reader within a
    major, False for a minor nobody has frozen, and to raise ValueError on a malformed string
    (a trailing newline, a leading zero, a prefix, a sign). Use version.assess() for the verdict,
    the direction and the evidence it rests on. KNOWN_CONTRACTS reads ("2.0.0", "2.1.0", "3.0.0").
  • Declare binding= on every adapter's metadata: "standard-encoding" where the wire form is the
    cited document's own encoding, "provisional-internal-profile" with a limitation containing
    "provisional" where the element names or namespace were chosen locally. A provisional binding
    claims PROVISIONAL (new) and not VERIFIED.
  • Expect the harness's lossless column to FAIL on a ledger loss for an adapter that declares
    MAPPINGS, and the lossless-verified badge to read heuristic for one that does not.
  • Expect the suite's H and N checks to carry outcome codes in details, and N's
    over_time_bound strings to name a code rather than a duration; expect --startup-timeout,
    --diagnostics, --memory-limit-bytes and --cpu-limit-seconds on the suite's CLI, the last
    two refused with an explicit reason on a platform that cannot enforce them.
  • Expect the conformance tool to refuse a string boolean, a trailing newline in a version, a
    malformed UUID and a timestamp outside the wire form on the JSON path.
  • Expect --list-adapters to print a binding column; expect an evidence record to carry
    snapshot, evidence_categories and maturity_support, and a manifest to carry binding.
  • Nothing is migrated in place: manifests, schemas, evidence and goldens are regenerated from
    their sources.

Two maintainer rulings, applied in this release

Preservation maturity is declared to what the evidence proves. Eleven manifests declared L4
ROUNDTRIP VERIFIED on a basis sentence that cited the lossless check as evidence — and since
F02 that check, for an adapter with no declared field mappings, rests on the value-presence
heuristic: a source value present somewhere in the output is not a value that reached its path.
"Applicable information survives source -> CDM -> source" is precisely the claim the heuristic
cannot prove, so adsb, ais, cat021, cat023, cat034, cat048, cat062, gmti,
stanag4609, stanag4676 and tak declare L3 from this release, each basis sentence says why,
and tests/test_cdm_manifests.py holds a bidirectional adapter to L3 wherever the report's
preservation.basis reads heuristic. Their roundtrip columns still read PASS and the suite's
maturity_eligible still computes L5 from the verdicts; what moved is the declaration, and an
adapter regains L4 when its field mappings are declared and the ledger reports no loss. pntmap,
the one adapter with a ledger, is ingest-only and stays at L3 for the reason it always gave.

A provisional binding claims PROVISIONAL. manifests/stanag4676.json declared
claim_status: VERIFIED beside binding: provisional-internal-profile. A gate that passes against
element names chosen in this repository verifies nothing about the standard, so ClaimStatus gains
a seventh member, PROVISIONAL (the manifest schema's 2.1.0), the adapter claims it, and
manifest.AdapterMetadata refuses the two fields apart in both directions. VERIFIED returns
with standard-encoding or normative-verified — the road to which is the register's §5 item 1.

The nine findings, and what each one left open

  • F01 — version compatibility is directional and evidence-based: assess() returns a verdict,
    a direction and the frozen evidence; the 2.0.0 and 2.1.0 contracts are frozen from their tags
    and 3.0.0 from this commit. Nothing external.
  • F02 — preservation rests on a path-bound ledger where MAPPINGS are declared (one shipped
    adapter today) and on the heuristic, said so, everywhere else. Declaring mappings for the other
    thirteen is queued work, one adapter per session.
  • F03 — parser deadlines are process-isolated (multiprocessing spawn over os.pipe()), with
    a six-code outcome vocabulary and one refusal among them.
  • F04 — schema and model alignment: the published schema states what the models enforce, a
    semantic-rules document and a 44-case corpus cover what a schema cannot say, and the wire path
    refuses a string boolean, a trailing newline and a malformed UUID.
  • F05 — STANAG 4676: binding is a required field on every manifest, the 4676 adapter reads
    provisional-internal-profile, an explicit normative mode exists and refuses with a five-step
    procedure until an authorised XSD pair and a validator are supplied. The normative verification
    itself is external and has not happened.
  • F06 — resource limits: input bounds on depth and bytes, memory and CPU limits enforced on
    Linux and refused with a reason elsewhere; the Linux enforcement branch is first observed by
    the remote CI of the pushed branch.
  • F07 — evidence: five categories per record, the three external ones ABSENT on every adapter,
    a runner and a verifier for exercise reports. No partner, exercise or date is invented.
  • F08 — governance: one workflow defect closed, workflow properties tested, an audit gate that
    says UNVERIFIED rather than guessing, two staged ruleset proposals and a runbook. Applying a
    ruleset needs an administrator and has not been done.
  • F09 — documentation: a current-contracts page generated from the constants, a support matrix
    generated from the declarations, symbol citations instead of line numbers, and a widened lint
    gate with an explicit, shrink-only baseline.

What else moved

  • No runtime dependency changed. pydantic and jsonschema, as before; jsonschema was
    already a runtime dependency before F04 used it on the Python path.
  • No adapter was added or removed, and no adapter's translation changed: every golden moved by
    its schema_version stamp and by nothing else.
  • The release rehearsal gate (gates/release_ref_rehearsal.py) replays every ref-dependent
    release step against the local tag before it is pushed, as it has since 2.1.2; it is refused
    without a tag, which is the reading the release commit records before the tag exists.
  • The release workflow moved, and it is the reason this is 3.0.1 and not 3.0.0. The build
    job's twine and cyclonedx-py live in a venv of their own, so the interpreter that runs
    condition 4's suite receives the documented install and nothing else, and condition 4 writes the
    suite's output to a file and prints the failing tests before it stops. Nothing about the ref was
    involved, which is why the rehearsal gate passed v3.0.0 and was right to.

Fourteen adapters, all harness-verified

python -m synapse_cdm.harness --adapter <name> --update-golden, run over the roster with no
--fixtures at this commit, 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. The roster did not move this arc: discover() and roster() each return
fourteen, the same fourteen names in the same two directions as 2.2.0.

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

538 fixture verdicts, 0 failed across the fourteen adapters, against the published 3.0.0
schemas — the same 538 as 2.2.0. The roundtrip column reads PASS for the eleven emitters and a
declared SKIP for the three that emit nothing; the lossless column reads PASS on every one, on
the ledger for pntmap and on the heuristic, said so, for the other thirteen.

Published by CI over OIDC, as 1.1.0 through 2.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 20 records the 2.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.0.1
python -m synapse_cdm.harness --list-adapters

Adapter status

14 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
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
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
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 2 analysis file(s) of 89d2c70
  • pip-audit, this release's run: No known vulnerabilities found; strict, over 43 third-party distribution(s), ignore-vuln flags in force: []
  • Security exceptions in force: none. No finding is excepted; every gate in the pipeline passed on its own terms.

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

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

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

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
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
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
tak PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS PASS SKIP PASS PASS CONFORMANT