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 code→source_code, title→title, application→application, elements[]→outcomes[], element_number→outcome_number (:143, integer passed straight through), performance_criteria→criteria 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 (/appholdsengine,generator,reasoner,requirements.txtonly).find_specin the image:adapters,adapters.tga,adapters.tga.pdf_processorall NOT FOUND;generator.course_generatorFOUND.- Importing
generator.course_generatorinside the image succeeds andsys.modulescontains noadapters*module at all. - The deployed
ucca-gatebundle 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¶
generate_complete_course.pyships 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.scripts/bootstrap_from_tga_unit.pymakes livetraining.gov.aurequests and remains an open breach of the standing rule. Read, never executed, on this pass.- 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. - Who performed the 9 March extraction is unestablished and may not be recoverable.
RECEIPT-CHECK: echo this line's end before acting.