Skip to content

Findings — separating the capability from the badge

1. §2 PROVENANCE — answered, and the adapter is not in the chain

The chain, established from bytes:

step artefact evidence
1 tga_pdfs/CHCPRT025_R1.pdf — 188,499 B, 9 Mar 2026 20:33 untracked, ignored by .gitignore:69
2 tga_data/CHCPRT025.json — 5,522 B, 9 Mar 2026 20:58, 25 min later untracked, ignored by .gitignore:67; carries source_url: https://training.gov.au/Training/Details/CHCPRT025, version: R1, 3 elements, element_number typed integer
3 generator/generate_complete_course.py:123 _tga_data_to_triumvirate() maps codesource_code, titletitle, applicationapplication, elements[]outcomes[], element_numberoutcome_number (:143, integer passed straight through), performance_criteriacriteria verbatim, keys number/description unchanged
4 submission a human, by hand — nothing in version control submits (runnable-state §0.2)

Machine-generated, not hand-authored. The deciding evidence is that the payloads' criteria are the TGA JSON's performance_criteria including their key names{number, description} — with identical values, and the outcome titles match the element titles character for character. A hand-author would not reproduce the source's internal key names.

The integer outcome_number mystery is closed. :143 passes element_number through unchanged, and element_number is an integer in the extracted JSON. The integers were never a decision; they are a passthrough.

THE TGA ADAPTER DID NOT PRODUCE THIS INPUT. adapters/tga/adapter.py::translate() emits an envelope whose required fields are triumvirate_id, adapter_id, client_id, instruments (:278), with outcomes carrying outcome_id_local and criterion_id_local. No stored payload carries instruments or any *_id_local field. The producer is in generator/, not in adapters/tga/. The adapter is not on the path that produced the engine's entire empirical record.

NOT ESTABLISHED: who ran the extraction on 9 March, under what instruction, and whether the JSON was edited by hand afterwards. The mechanism is proven; the human act is not.

2. §2.3 — four inputs, not sixty-four

Over the 45 forward payloads carrying a triumvirate, canonicalised and hashed:

triumvirate jobs outcomes outcome_number type
6083750fbd9e 31 1 str
74e0989d95ce 9 3 int
441a346fff12 3 2 str
9b59da3ff9bf 2 0 (the two X placeholder rows)

Four distinct inputs. The dominant one — 31 of 45 forward jobs — carries a single outcome, i.e. one element of a three-element unit. Only 9 jobs ever ran the complete unit as extracted.

This narrows the runnable-state document's §0.1 further than it currently states. It says one unit, 64 times. It is more exactly: four truncations of one unit, the most-used of which is one third of it. Whatever the 69-job record evidences about throughput, failure and cost, it evidences it mostly about a one-outcome input.

3. §3 SEPARATION — capability, badge, fossil

module bucket justification disposition
pdf_processor.py SPLIT extract_unit_data() is PDF → LLM → structured obligation. That mechanism is domain-neutral and is the magic smoke. But download_pdf() (:58, curl to TGA) is pure badge, and the extraction prompt at :222 hardcodes the TGA output schema including source_url: https://training.gov.au/... assimilate the extractor; leave the downloader and the prompt. The domain-neutral version takes a PDF and a target schema — not a tga_code
adapter.py CAPABILITY-shaped, BADGE-bound the corpus→Triumvirate translation contract (identify/ingest/translate/validate) is the engine's adapter interface. Its body is TGA field names throughout keep the interface, engine-side; the TGA body leaves. Note it produced none of the record
packages.py BADGE "all 54 Australian Training Packages" leaves
domain_map.py BADGE TGA package/unit codes → UCCA domains leaves
__init__.py BADGE docstring is the VET adapter's identity leaves
scripts/bootstrap_from_tga_unit.py BADGE fetches training.gov.au HTML directly. Also the open no-TGA-requests breach — read as source, never executed leaves
scripts/sync_training_packages.py, sync_training_package_domain_map.py, reconcile_training_packages_v0.py BADGE training-package registry sync/reconcile leaves
reconcile/runs/** — 11 tracked JSON/MD FOSSIL dated 2026-01-02 run artefacts discard
registry_sync/runs/2026-01-04T0531Z/…xlsx FOSSIL / client-domain data 17,493,772 B — 17 MB of the 18 MB is this ONE untracked spreadsheet census standing rule: discards by default

Directory totals: 22 tracked files, 35 on disk; registry_sync 17 M, scripts 216 K, reconcile 164 K, __pycache__ 96 K, all source files together under 100 K.

The warning the brief asked for. The most likely mis-assimilation is pdf_processor.extract_unit_data(). Its prompt names TGA fields and emits a TGA-shaped object. Lifting it as-is produces "a generic extractor that still expects a unit of competency" — badge wearing a capability label. The assimilated version must take the target schema as a parameter, not carry one.

4. §4 BLAST RADIUS — near zero on the live path

4.1 The four importers

importer in deployed image? TGA refs verdict
backend/app/utils/ucca_domains.py no 5 not shipped; backend is not on the live path
backend/scripts/ingest_course.py no 1 not shipped
generator/generate_complete_course.py YES 2 shipped but already unimportable — top-level from adapters.tga.pdf_processor import TGAPDFProcessor at :44, and adapters/ is absent from the image
generator/ucca_config.py YES 1 its only TGA reference is a comment (:44)

4.2 The live-path gap, closed

  • adapters/ is not present in the deployed container image (/app holds engine, generator, reasoner, requirements.txt only).
  • find_spec in the image: adapters, adapters.tga, adapters.tga.pdf_processor all NOT FOUND; generator.course_generator FOUND.
  • Importing generator.course_generator inside the image succeeds and sys.modules contains no adapters* module at all.
  • The deployed ucca-gate bundle has zero matches for TGA / training.gov / competency vocabulary.

Nothing on the live path reaches TGA. Removal breaks no deployed component.

4.3 / 4.4 answered above: tga_data and tga_pdfs are the origin of the record (§1); the 18 MB is one 17 MB untracked spreadsheet.

5. What removal would actually cost

Not the live path — the input path. Nothing deployed breaks. What leaves with adapters/tga/ is the only mechanism that turns a source document into tga_data/*.json, which is the only input _tga_data_to_triumvirate() knows how to read.

The converter itself survives removal — it is in generator/, not adapters/tga/. Its feedstock does not.

Consequence for the qualification run (recon brief §1): the run does not depend on adapters/tga if it reuses an existing tga_data/*.json. It does depend on it for any unit not already extracted. Extraction is the piece to assimilate before removal, and it is one function.

6. Owed, and not decided here

  1. generate_complete_course.py ships broken in the container — a top-level import of a package that is not in the image. It cannot have run there. Worth its own look: it is the module carrying the converter.
  2. scripts/bootstrap_from_tga_unit.py makes live training.gov.au requests and remains an open breach of the standing rule. Read, never executed, on this pass.
  3. The census narrowing at §2 should reach UCCA-ENGINE-RUNNABLE-STATE §0.1 — that document is filed and awaiting carry, and its "one unit, 64 times" is true but less exact than "four truncations, mostly one outcome". Amending a filed pre-carry document is Tim's call, not this seat's.
  4. Who performed the 9 March extraction is unestablished and may not be recoverable.

RECEIPT-CHECK: echo this line's end before acting.