Task: Bring ores.telemetry to the clean standard

Table of Contents

This page documents a task in the Clean ores.telemetry to the component clean standard story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

ores.telemetry passes the Component Clean Standard for a component of kind Protocol.

2. Status

Field Value
State DONE
Parent story Clean ores.telemetry to the component clean standard
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-09-23

3. Acceptance

  • Every checklist item that applies to a component of kind Protocol passes, with its evidence recorded on the task.
  • Every item that does not apply is recorded as not applicable, with the reason.
  • Every generator defect found is fixed at its source, with a regression test.
  • ores.telemetry is listed in COMPONENTS_UNDER_TEST, and check_component_drift.py --all passes.

4. Plan

Work the Component Clean Standard in its section order, one entity at a time, and record each item's result in the table below.

The standard's table holds 49 items, though its own prose still says 47. This component is of kind Protocol: it serves and sends NATS messages, and it has no entity, junction or operation model at its baseline. The All items are the work, and the Protocol items are too. The Entity items are recorded as not applicable with their reason, and so are the Shell items, which follow from them.

  1. Baseline and inventory (B01 to B06). Record the baseline before any edit.
  2. Models (M01 to M10).
  3. Protocol (P01 to P06).
  4. Generation (G01 to G08).
  5. Wiring and data (W01 to W04).
  6. Shell (S01, S02).
  7. Structure and hygiene (H01 to H05).
  8. Verification (V01 to V08), ending with the registry entry.
Item Result Evidence
B01 Pass Baseline before any edit. check_component_drift.py --component telemetry reports a component the codegen cannot name, because the component has no catalogue row. check_model_drift.py reports only the one known reporting exception. The three parts' component_files.cmake lists are all stale. Evidence in evidence/b01_baseline.txt.
B02 Pass 0 entity, junction, operation, message, field-group and module models. The three part overviews are component models, and core/modeling/ores.telemetry.protocol.org declares no #+type: at all, so the loader does not read it as a model. The component has no root modeling/ directory: every other composite keeps its models and its root overview in one. Evidence in evidence/b02_models.txt.
B03 Pass 89 hand-written C++ files, none generated, classified: 42 generatable, 36 infrastructure, 11 dead. Per part, core is 33/20/9, database 6/2/1 and service 3/14/1. The survey's name-based dead set was checked twice: `application_exception.hpp` is used by the component's own service and is kept, and the ten files that are dead are the file-JSONL exporter sub-tree, `log/macros.hpp`, the database `database_sink_utils` duplicate and the unbuilt `core/examples/` directory. Evidence in `evidence/b03_handwritten_inventory.tsv`, with the corrections in `evidence/b03_b06_summary.txt`.
B04 Pass Six subjects, each declared once in one of three hand-written protocol headers, and all six served by service/src/messaging/registrar.cpp. No raw subject literal sits outside a protocol header. One subject is sent by ores.compute's wrapper and one by ores.service's heartbeat publisher. Evidence in evidence/b04_subjects.txt.
B05 Pass No branch in flight on origin touches the component. Four captures in the backlog name it, and none asks for a change that collides with this task. The instrument_components_with_telemetry_context capture names the intended consumer of the tracing API that B06 finds test-only. Evidence in evidence/b05_captures_and_branches.txt.
B06 Pass Symbol census over every public header: 138 symbols, of which 71 have a production consumer, 30 are reached only from a test and 37 from nothing. The test-only set is the distributed-tracing API, whose intended consumer the capture at B05 names. The unreached set is the file-JSONL exporter sub-tree, `log/macros.hpp` and the unbuilt example; `application_exception.hpp`, which the first count put there, is used by the component's own service. Evidence in `evidence/b06_symbol_census.tsv`.
M01 to M05 N/A Entity items, and the component has no entity or junction model: there is no model format to migrate, no variability profile to bind, no feature namespace to check, and no table shape to review.
M06 Pass Every wire type the component exchanges is modelled. Three operation models declare the six messages and the payloads they carry, and the protocol headers they generate replace the hand-written ones. The three hand-written protocol headers and the six payload domain headers are deleted, and every caller reads the generated types: the repository, the mapper, the sink backend, the handler, the NATS poller and the tests inside the component, plus `ores.compute`'s wrapper and `ores.testing`'s logging listener outside it.
M07 N/A Entity item: no junction model, so there is no singular to name, no key column to type and no row-level security to scope.
M08 N/A Entity item: no entity model, so no custom column type is bound. The one custom type this task binds is a message field's enum, and it is bound through the same registry the item describes.
M09 Pass No legacy fragment remains in any model. The three models this task writes carry no Qt or Wt drawer, no property naming a retired message type and no dead history property. The log entry's `source_name` comment named the retired `ores.qt` as its example; it names a live component now.
M10 Pass The component model is current. The component had no overview of its own, and now has one in the group-index shape with its diagram; the three part overviews are refreshed and no longer describe the exporters this task deletes, and the messaging reference documents all six subjects rather than one.
P01 N/A Protocol item, and the component has no entity: there are no canonical entity verbs to speak. Its operations are its own, and P04 covers them.
P02 Pass Every subject the component owns is declared once, in a model: six subjects across three operation models. No raw subject literal sits outside a generated header. The environment prefix is applied by `ores.nats`, not here, which is this component's one layering exception and is recorded in Notes.
P03 Pass No duplicate subject, no unregistered derived message and no declared message that nothing serves: six subjects, each served by `service/src/messaging/registrar.cpp`. The two NATS sample handlers answered every request with an empty sample list, so the two repository reads behind them were unreached; they call them now.
P04 Pass The operations that are not entity verbs are operation models: the log list and the log publish, the service heartbeat and the service list, and the two NATS sample lists. Their messages generate from the models.
P05 Pass Every consumer follows the change in the same piece of work: the component's own callers, `ores.compute`'s wrapper and `ores.testing`'s logging listener all read the generated header, and the whole tree builds.
P06 N/A Protocol item, and the component has no entity and no event cache: there is no canonical list and no event action for a cache to warm through.
G01 Pass Regeneration is byte-identical and idempotent: `check_component_drift.py –component telemetry-cpp –dry-run` reports no drift, and the component is listed in `COMPONENTS_UNDER_TEST`, so the `–all` run covers it too.
G02 Pass Every hand-written file the generation supersedes is deleted: the three protocol headers and the six payload domain headers. The dead files go with them: the file-JSONL exporter sub-tree, `log/macros.hpp`, the database `database_sink_utils` duplicate and the unbuilt examples directory.
G03 Pass Every generated file carries the marker and no hand-written file does: the three protocol headers, their three TypeScript twins and the six CMake source lists. The component's root `CMakeLists.txt` is generated by the group model too, and a hand edit of it had to be regenerated out.
G04 N/A Entity item: no generated entity code to read against the component's best hand-written code.
G05 Pass No defect was found in the code generation this component's models drive: the three models emitted the headers and TypeScript twins they claim, and the one thing the work needed from the framework was a row in the custom-type registry rather than a template change. The diagram pass did find three defects, in `build/scripts/generate_component_puml.py`, which drops a wrapped base clause, a `static` data member and a wrapped member initialiser. They are not fixed here: the measurement in the capture `diagram_parser_drops_wrapped_declarations` shows each fix reaches components outside this one, 23 of them for the `static` member alone, and a component clean-up must not rewrite other environments' committed diagrams. The manual diagram pass draws the three shapes, and the fix belongs to the story that capture asks for. The three models generated the headers and the TypeScript twins they claim, the enum field needed a registry row rather than a template change, and the one tooling gap — the `ores.cmake` address regenerates the root CMakeLists but not the per-part source lists — is a writer gap the clean-compute task already captured.
G06 Pass The CMake source lists match the tree: all three parts report their `component_files.cmake` up to date, after the database list was regenerated to drop the deleted duplicate header.
G07 Pass The four diagrams were refreshed in this task and read back. The code they draw changed: the exporter sub-tree is gone from the core diagram, the generated messaging types are in it, and the two-pass sentinel held.
G08 Pass Three modules, three namespace documentation headers, and three claims corrected in place: the core header was named for the component and promised exporters this task deletes, the database module had none, and the repository facet declared a namespace no code is in. Evidence in `evidence/g08_namespace_headers.txt`.
W01 to W04 N/A Entity items: no registrar family, no handler permission, no populate script and no SQL script is generated for this component. Its five tables and their RLS policies stay hand-written, which is what the Protocol scope of this story records.
S01, S02 N/A Entity items: no entity and no junction, so there is no shell command unit and no recipe to generate.
H01 Pass The component passes the Component Architecture Audit. `validate_docs.sh` reports no violation for it; its four diagrams were refreshed in two passes and the rendered images read back; and the audit's registry check found the component missing from its System Model layer page, which now carries it with its three parts, their roles and their dependencies.
H02 Pass No legacy code remains. The file-JSONL exporter sub-tree, `log/macros.hpp`, the database `database_sink_utils` duplicate and the unbuilt examples directory are deleted; the two NATS sample handlers no longer answer with nothing; the distributed-tracing API and the four stats reads are kept with the capture that names the consumer that should reach them. No commented-out code and no hand-written duplicate of a generated artefact remain.
H03 Pass Comments follow the project rules. Six violations found and fixed, all end-of-line comments or edit-history narration. The `} // namespace` closers stay: they are the repo convention.
H04 Pass No hand-rolled per-platform shim. The survey searched the component for the macros the three supported compilers define and found none: no `_WIN32`, no `_MSC_VER`, no `__APPLE__`, no `__GNUC__`, no `unreachable()` stand-in and no attribute macro. Evidence in `evidence/h04_platform_shims.txt`.
H05 Pass The component's documentation is complete rather than a skeleton. Its own overview and the three part overviews each carry their sections and a diagram with its `.puml` source and rendered image beside it; the messaging reference documents all six subjects and who serves them; and the prose that described the deleted exporters is gone from both overviews.
V01 Pass The whole tree builds, with the component's tests and every consumer: `./compass.sh build` reaches 100 percent. Evidence in `evidence/v01_v02_build_and_tests.txt`.
V02 Pass The codegen suite passes, and the component's three suites pass: core 2175 assertions in 50 cases, database 70 in 12 and service 32 in 10. The fleet-dependent half of V02 belongs to the components that serve; this one owns no service of its own. Evidence in `evidence/v01_v02_build_and_tests.txt`.
V03 N/A Entity item: the component owns no generated schema, so there is nothing for the regeneration to create and no service of its own to start.
V04 N/A Entity item: the component generates no shell command, so there is nothing to run against the fleet.
V05 Pass Every codegen gate is green with the component added: component drift, model drift, protocol twin coverage, handler permissions, populate references, the shell recipe inventory and the component's CMake source lists. The physical-space inventory check and the CMake check's repo-wide remainder are pre-existing and recorded. Evidence in `evidence/v05_gates.txt`.
V06 Pass The component joins `COMPONENTS_UNDER_TEST` as `telemetry-cpp`, and `check_component_drift.py –all` passes with it listed. Evidence in `projects/ores.codegen/scripts/component_registry.py`.
V07 Pass No vacuous test remains. Of the 29 cases the survey found, 22 now assert a literal expected value and 7 are deleted: the two that asserted one production run against another for a time-sortable id the generator cannot make deterministic, and the five that asserted a valid logging configuration is accepted by a `validate()` that returns nothing. Evidence in `evidence/v07_vacuous_tests.txt` and the suites.
V08 Pass Every source file has a test that exercises it, or is recorded with the reason it does not: 23 sources, 9 covered, 7 partial and 7 that no test touches, which is the coverage work item. The nine never-included headers are the namespace banners nothing should include. Evidence in `evidence/v08_source_files.txt`.

5. Notes

5.1. The wire payloads are messages, not entities

The generator decides a message field's TypeScript type from its C++ type. A primitive, a message declared in the same model, and a type bound in the custom-type registry all project. A `domain::` entity type projects to an import from a module the domain facet writes, and this component has no entity model, so that import would name a file nothing writes.

That is why the payload a message carries is declared as a message beside it, and why the repository, the mapper and the sink backend read `messaging::telemetry_log_entry` rather than `domain::telemetry_log_entry`. The alternative reading of M06, entity models for the four stored shapes, would put this story in the Entity items it was not scoped for.

5.2. What the generation supersedes, and what it does not

Deleted because the generation supersedes them: the three hand-written protocol headers, and the six payload domain headers whose types the models now declare.

Deleted because nothing reaches them: the file-JSONL exporter sub-tree (`log_exporter`, `file_log_exporter`, `hybrid_log_exporter`, `upload_position_tracker`) and `log/macros.hpp`. Their last mention was a doxygen example in `log/lifecycle_manager.hpp`, which is rewritten.

Kept, with the reason recorded: `application_exception.hpp`, which the census called dead and which the component's own service uses. The distributed-tracing API, test-only, whose intended consumer the `instrument_components_with_telemetry_context` capture names. The four stats reads, test-only, whose intended consumer the `expose_the_telemetry_stats_surface` capture names.

Kept because the story's Protocol scope leaves them hand-written: the five tables, their RLS policies and the stats SQL functions.

5.3. What the item table records as a limit rather than a pass

Two things this component cannot assert, both named on the item that owns them, so that neither reads as coverage it does not have:

  • The id ordering property. `span_ids_are_time_sortable` and its trace counterpart asserted that a later id sorts later, which is one production run against another; the generator takes the system clock and accepts no other, so the cases are deleted rather than made deterministic.
  • Accepted logging options. `validate()` returns nothing, so a case that a valid configuration is accepted cannot tell real validation from an empty body. The five accept cases are deleted and their inputs folded into the reject cases, whose literal exception is the contract.

One observation the task did not act on: `ores.telemetry.core` links `ores.nats.lib` PUBLIC, and no core source includes a nats header. The link is a leftover from the exporter sub-tree this task deletes. Removing a PUBLIC transitive link would change what every consumer sees, so it is recorded here rather than changed inside a clean-up.

5.4. The NATS sample handlers answered with nothing

Both `nats_server_samples_list` and `nats_stream_samples_list` decoded their request, discarded the filter and replied `{success = true}` with an empty sample list, while `query_server_samples` and `query_stream_samples` sat unreached in the repository. A subject that answers nothing is worse than a missing subject, so the handlers call the repository and the two reads are reached again.

6. Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

7. PRs

PR Title
#2178 [telemetry] Bring ores.telemetry to the component clean standard

8. Review

# Comment summary File Decision Notes
1 The two fire-and-forget messages are generated with `requires_session = true`, but their handlers never call `make_request_context`, so the constant claims a session nothing checks. `service_samples_protocol.hpp`, `logs_protocol.hpp` Accept Both messages are `:auth: none` in their models now, which is what `ores.compute`'s telemetry model does for the message a wrapper publishes with no session. The four request/response messages keep `true`, and their handlers do validate.
2 `build_where_clause()` escapes SQL by hand rather than binding, and the `component` and `message_contains` filters interpolate caller input into `LIKE` patterns. `telemetry_repository.cpp` Accept as a capture Pre-existing logic this branch only renamed types in, so it is not fixed inside a component clean-up. Recorded as `parameterise_the_telemetry_log_query`, with the reviewer's point that the story already had the file open.

9. Result

ores.telemetry is at the Component Clean Standard for a component of kind Protocol, the first through it. All 49 items are recorded: 31 pass and 18 are not applicable, each with its reason. The Entity items do not apply because the component models no entity, and the Shell items follow from them.

The work came to five things. The component had no model: three operation models now declare the six subjects it serves and sends, with the payloads they carry, and the protocol headers they generate replace the hand-written ones. The three protocol headers and the six payload domain headers are deleted, and the repository, the mapper, the sink backend, the handler, the NATS poller and the component's tests read the generated types, as do ores.compute's wrapper and ores.testing's logging listener in other components. The component had no model of its own: it gained its root modelling directory, a group-index overview and a two-pass diagram, and its three part overviews were refreshed. The dead code is gone: the file-JSONL exporter sub-tree, log/macros.hpp, a duplicate database sink helper and an unbuilt examples directory. The two NATS sample handlers answered every request with nothing, and now read the repository the subjects promised. The four diagrams were refreshed in two passes and read back.

Every gate is green for this component: the drift check reports no drift with the component listed in COMPONENTS_UNDER_TEST; the model, protocol twin, handler permission, populate, recipe and CMake gates pass; and the three suites pass at 2175, 70 and 32 assertions with the codegen suite at 626. Two failures on main are recorded rather than fixed, because neither is this component's and both belong to other environments: ores.shell.workflow has no overview and no diagram, which fails the documentation check, and the physical-space inventory check is stale on ores.doc.knowledge.org. The third failure this branch met on main — the same shell part's test directory without a main.cpp, which stopped the whole build — was fixed on main while the branch was open, so its capture is withdrawn and the build is green.

What stays open is recorded rather than claimed. The distributed-tracing API and the four stats reads are kept, each with the capture that names the consumer that should reach them. The diagram parser drops a wrapped base clause, a static data member and a wrapped initialiser, which the manual diagram pass draws by hand and a capture now owns, because fixing the parser would rewrite 72 components' committed diagrams. Seven of the 23 sources have no test, which is the coverage work item.

Emacs 29.3 (Org mode 9.6.15)