Skip to content

Adopt the CLI-scaffold-parity component baseline (#11)#12

Merged
mbreissi merged 7 commits into
mainfrom
feat/baseline-adoption
Jul 20, 2026
Merged

Adopt the CLI-scaffold-parity component baseline (#11)#12
mbreissi merged 7 commits into
mainfrom
feat/baseline-adoption

Conversation

@mbreissi

Copy link
Copy Markdown
Contributor

Summary

Adopts the CLI-scaffold-parity component baseline for telemetry-processor. Per issue #11, this repo
is a processor, not a southbound adapter, so only the cross-cutting baseline applies (no sb/*
verbs, no southbound_health, no device seam, no required panels). Full P0+P1+P2 per the resolved
scope decisions in the master remediation plan (SD-A: full scope; SD-B: commit the lockfile; SD-D:
fix the license mismatch inline).

Checklist (mapped to issue #11)

P0 — correctness of the reproducible build

P1 — baseline adoption

  • 2. CI coverage job. cargo llvm-cov --fail-under-lines 90 added to ci.yml, same feature
    set as the build job (streaming,streaming-file-parquet,streaming-file-avro,scripting-lua). Getting
    there honestly required splitting src/app.rs's untestable EdgeCommons composition root
    (ProcessorApp::start/build_route/run — none of Config/MessagingService/EventsFacade/
    CommandInbox/StreamService has a public test constructor) from the testable fan-out/command
    logic, moved to a new src/dispatch.rs. Added a downstream MessagingService fake
    (src/test_support.rs) and a test-only recording EvtEmitter (mirroring file-replicator's
    Events::recording_eventsEventsFacade has the same no-public-constructor constraint). The
    gate excludes only main.rs, app.rs (the genuine composition-root seam), and test_support.rs
    (test-only support code, same treatment file-replicator gives testutil.rs).
  • 3. deploy-docs.yml. Added verbatim (modbus-adapter shape) so a docs-only push still
    triggers a site rebuild.
  • 4. config.schema.json. Models component.global.defaults (what edgecommons component validate currently checks) plus $defs.route/$defs.stage describing every
    component.instances[] shape (filter/sample/aggregate/project/script, incl. the multi-signal
    inputs/output form) for documentation and forward compatibility. Validated against both shipped
    test-configs/ samples with a real JSON Schema validator (ajv, draft 2020-12) — both pass; a
    typo'd key in either section is rejected.
  • 5. Governance files. AGENTS.md, CLAUDE.md (@AGENTS.md import + local-dev note),
    DESIGN.md (decision register D-TP-1..8 seeded from the shipped v2 design + this remediation's own
    decisions).

P2 — enhancements

  • 6. Edge-console panels (optional, not required for processors). Implemented rather than
    deferred — straightforward via the library's existing commands.register_panel. Two
    scope: "component" panels: overview (fleet totals + flush/pause/resume) and routes (per-route
    counters via get-stats).
  • 7. License reconciliation. Cargo.toml's license = "Apache-2.0" (stale) → "BUSL-1.1"
    (matches the actual LICENSE file and org policy). Fixes #9.
  • 10. Stale-comment sweep. The Cargo.toml comment pointing at a ../core-monorepo/libs/rust
    sibling path (the current convention is a gitignored .cargo/config.toml [patch] override at
    ../core/libs/rust) fixed while touching the file.

Validation

  • cargo test --locked --features scripting-lua: 96 passed, 0 failed (native Windows and
    WSL/Linux — identical results).
  • cargo clippy --all-targets --locked --features scripting-lua -- -D warnings: clean on both
    platforms.
  • cargo llvm-cov --locked --features streaming,streaming-file-parquet,streaming-file-avro,scripting-lua --ignore-filename-regex '(app|main|test_support)\.rs' --fail-under-lines 90:
    92.48% line coverage (gate passes), identical on native Windows and WSL/Linux — no undercount
    discrepancy this run.
  • Fresh-clone build confirmed: built the committed Cargo.lock in an isolated copy with no ancestor
    .cargo/config.toml override in scope (so it resolves the pinned git rev, exactly as CI/a real
    fresh clone would) — cargo build --locked --all-targets --features scripting-lua succeeds.
  • config.schema.json validated against both test-configs/*.json samples with ajv (draft 2020-12).

Deferred validation (per the org matrix, flagged rather than silently skipped): HOST/dual-MQTT,
GG-lab, and Kubernetes live-infra regression were not re-run. This changeset is hygiene/CI-shaped
(lockfile, coverage gate, schema, governance docs, a license fix, a testability-only source split,
and two additive console panels) — it changes no wire contract, config-consuming behavior, or
deployed runtime path, so no live-infra gate applies per the validation matrix.

Deviations from the plan

None. The app.rs/dispatch.rs split was not explicitly called out in the issue, but is a direct,
disciplined consequence of implementing item #2 (the coverage gate) honestly rather than papering
over an untestable composition root with a broad exclusion — recorded as D-TP-7 in DESIGN.md.

Fixes #11
Fixes #9

🤖 Generated with Claude Code

https://claude.ai/code/session_01DxV5wFi7LCratBERVT6aiL

mbreissi and others added 7 commits July 19, 2026 22:19
Cargo.lock was gitignored with a rationale ("excluded until the edgecommons pin
is bumped to the UNS-core rev") that no longer held -- the pin moved to the
D-U28 rev in 49d11fd, so the gate on committing it had already been cleared.

Per the org-level lockfile-commit policy (SD-B of the component-baseline
remediation plan): regenerated with the local .cargo/config.toml [patch]
override inactive, so the lock records the git dependency
(edgecommons?rev=36a70c48...) rather than a local sibling path -- resolvable on
a fresh clone and in CI. Verified: cargo build/test/clippy all pass against
this lock with the override removed from the resolution path (isolated build,
no ancestor .cargo/config.toml), on both Windows and WSL/Linux.

This also gives issue #10's Docker-build report a lockfile CI can actually see.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxV5wFi7LCratBERVT6aiL
Cargo.toml declared license = "Apache-2.0", a stale leftover that disagreed
with the actual LICENSE file (BSL 1.1 / SPDX BUSL-1.1) per org policy: all
EdgeCommons repos are licensed BUSL-1.1 throughout.

Also sweeps a stale comment above the edgecommons git dependency that still
pointed at a `../core-monorepo/libs/rust` local-dev override path -- the
current sibling layout (and the org convention) is a gitignored
.cargo/config.toml [patch] override at ../core/libs/rust, not a hand-edited
Cargo.toml path dependency.

Fixes #9

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxV5wFi7LCratBERVT6aiL
…mposition root out of app.rs

Adds a `coverage` job to ci.yml (cargo llvm-cov --fail-under-lines 90, the same
feature set the build job uses) -- the repo's ~94%/91% coverage figures were
previously validated locally only; CI never gated them.

Getting there honestly required a real testability fix, not just an exclusion
list: `ProcessorApp::start`/`build_route`/`run` (src/app.rs) obtain a live
Config/Arc<dyn MessagingService>/EventsFacade/Arc<CommandInbox>/Arc<dyn
StreamService> from a real &EdgeCommons, and none of those types has a public
or test constructor outside the edgecommons crate -- so this file cannot be
unit-tested without a live runtime (mirrors why the protocol-adapter templates
exclude their `supervisor.rs` seam, for the analogous reason of a live device
driver). Everything that does NOT need &EdgeCommons directly is moved to a new
src/dispatch.rs, which IS fully unit-tested:

- the self-echo guard + fan-out handler (self_subscribe)
- the get-stats/flush/pause/resume command verbs + panel registration
- the two edge-console panels (`overview`, `routes`) -- issue #11's optional
  P2-6 enhancement, straightforward via the existing register_panel, so
  implemented rather than deferred; scope: "component" (no console instance
  dimension for a processor)
- script-output-topic validation

This crate's own src/test_support.rs adds a downstream MessagingService fake
(FakeMessaging), since the library's own testutil::RecordingMessaging is
pub(crate) and unreachable here. src/observe.rs's EvtEmitter gains a test-only
`recording()` constructor for the same reason -- EventsFacade has no public
constructor -- mirroring file-replicator's Events::recording_events precedent
for the identical constraint. src/proc/route.rs's Dispatcher (already
EdgeCommons-free) gains full local/northbound/stream-target/restamp/
failure-evt test coverage using the new fakes.

The coverage job excludes exactly main.rs, app.rs, and test_support.rs (the
last is test-only support code, not product logic -- the same treatment
file-replicator gives its own testutil.rs). Measured at 92.48% lines
(96 tests, cargo test/clippy -D warnings/llvm-cov all green, identical results
on native Windows and WSL/Linux -- no undercount discrepancy this time).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxV5wFi7LCratBERVT6aiL
ci.yml's paths-ignore (**.md, docs/**) means a docs-only push triggers no
workflow at all today, so the docs site never refreshes until an unrelated
code push happens to land alongside it. Adds the hook-guarded docs-rebuild
trigger (verbatim the modbus-adapter shape): hits the Cloudflare Workers
Builds deploy hook for edgecommons-docs on a docs-only push to main, a no-op
until the repo's CLOUDFLARE_DEPLOY_HOOK secret is set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxV5wFi7LCratBERVT6aiL
…es[]

`edgecommons component validate` had nothing to check this component's config
against: no schema file existed anywhere in the repo, so a typo'd pipeline key
was caught by no tool at any stage.

Models component.global.defaults (the block edgecommons component validate
currently checks) plus $defs.route/$defs.stage describing each
component.instances[] entry -- the full route/pipeline-stage surface (filter,
sample, aggregate, project, script incl. the multi-signal inputs/output form)
-- for documentation and forward compatibility (the CLI does not yet validate
instances[] against a component schema; see ec-validate's schema.rs).
Deliberately does not redeclare the library envelope (logging, messaging,
metricEmission, heartbeat, streaming, tags, component.token, ...), which the
canonical schema owns.

Validated against both shipped test-configs/ samples (component.global +
every instances[] route) with a real JSON Schema validator (ajv, draft
2020-12): both pass; a typo'd key in either section is rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxV5wFi7LCratBERVT6aiL
The design substance already existed (rich module-level doc headers, the
monorepo's processor design docs) but the repo carried no agent-facing
shape/conventions file and no in-repo design register.

AGENTS.md: the processor's shape (the Processor stage seam, the
app.rs/dispatch.rs testability split, config location, validation
expectations) plus the org conventions it inherits. CLAUDE.md: a short header
+ `@AGENTS.md` import + the local-dev .cargo override note, per the org
scaffold shape. DESIGN.md: a decision register (D-TP-1..8) seeded from the
shipped v2 design (payload-agnostic core, external script files + the
multi-signal script stage, tags-as-JSON-column) plus this remediation's own
decisions (the committed lockfile, the license fix, the coverage-seam split,
the console panels), the command/metrics surface, and validation evidence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxV5wFi7LCratBERVT6aiL
…mmons seam

Adversarial review on PR #12 correctly flagged the app.rs coverage exclusion
as broader than a thin live-runtime seam: build_route still contained
testable decision branches (target/filter/publish-topic resolution,
script-output-topic validation, the restamp policy) that need only a plain
Config, not a live EdgeCommons.

Moved every such decision into a new src/route_build.rs:
resolve_global_wiring (cross-route defaulting + the processor's own identity
strings -- previously inlined in ProcessorApp::start), resolve_target,
resolve_filters, resolve_publish_topic, resolve_script_output_topics, and
compute_restamp. All of it is unit-tested directly against
Config::from_value-built configs -- no fake needed, since Config itself has a
public test constructor (11 new tests, ~99% line coverage on the new file).

What remains in src/app.rs is now only the code that must obtain a live
Arc<dyn MessagingService>/EventsFacade/Arc<CommandInbox>/Arc<dyn
MetricService>/Arc<dyn StreamService> (gg.streams(), stream targets only --
build_route's one remaining live dependency) or gg.shutdown_signal() from a
real &EdgeCommons -- none of those types has a public or test constructor
outside the edgecommons crate, so this residual is a genuine, irreducible
seam, not an over-broad exclusion.

ci.yml's exclusion list is unchanged ((app|main|test_support)\.rs) --
route_build.rs is deliberately NOT excluded, since it is now fully tested and
belongs in the denominator. Updated AGENTS.md/DESIGN.md (D-TP-7) to describe
the narrower, corrected boundary.

Re-validated: cargo test 107/107 (was 96), cargo clippy -D warnings clean,
cargo llvm-cov --fail-under-lines 90 -> 92.95% lines (was 92.48%), identical
on native Windows and WSL/Linux. Cargo.lock untouched (verified via git diff
before and after -- still git-sourced at the pinned rev; all cargo
invocations for this change ran in isolated copies outside the worktree, so
the ancestor .cargo/config.toml [patch] override never touched the committed
lock).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxV5wFi7LCratBERVT6aiL
@mbreissi

Copy link
Copy Markdown
Contributor Author

Update: addressed an adversarial-review MINOR.

The app.rs coverage exclusion was flagged as broader than a thin live-runtime seam — build_route still contained testable decision branches (target/filter/publish-topic resolution, script-output-topic validation, the restamp policy) that need only a plain Config, not a live EdgeCommons.

Fixed by extracting all of that into a new src/route_build.rs (resolve_global_wiring, resolve_target, resolve_filters, resolve_publish_topic, resolve_script_output_topics, compute_restamp), unit-tested directly against Config::from_value-built configs (11 new tests, ~99% coverage on the new file — no fake needed, since Config has a public test constructor). app.rs now contains only code that must obtain a live Arc<dyn MessagingService>/EventsFacade/Arc<CommandInbox>/Arc<dyn MetricService>/Arc<dyn StreamService> (stream targets only) or gg.shutdown_signal() — none of which has a public/test constructor outside the edgecommons crate, so this residual is a genuine, irreducible seam.

ci.yml's exclusion list is unchanged ((app|main|test_support)\.rs) — route_build.rs is deliberately included, not excluded.

Re-validated: cargo test 107/107 (was 96), cargo clippy -D warnings clean, cargo llvm-cov --fail-under-lines 9092.95% lines (was 92.48%), identical on native Windows and WSL/Linux. Cargo.lock confirmed untouched throughout (still git-sourced at the pinned rev — all cargo invocations for this fix ran in isolated copies outside the worktree, never in-place, to avoid the ancestor .cargo/config.toml [patch] override rewriting it).

@mbreissi
mbreissi marked this pull request as ready for review July 20, 2026 15:31
@mbreissi
mbreissi merged commit 9a6e817 into main Jul 20, 2026
4 checks passed
@mbreissi
mbreissi deleted the feat/baseline-adoption branch July 20, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adopt the CLI-scaffold-parity component baseline License: reconcile license declarations to BSL 1.1 (BUSL-1.1)

1 participant