Skip to content

Telemetry: service skeleton + slice 1 (TelemetryPolicyConfigured config-as-events)#42

Merged
erikshafer merged 5 commits into
mainfrom
telemetry/skeleton-and-slice-1-config
Jul 10, 2026
Merged

Telemetry: service skeleton + slice 1 (TelemetryPolicyConfigured config-as-events)#42
erikshafer merged 5 commits into
mainfrom
telemetry/skeleton-and-slice-1-config

Conversation

@erikshafer

Copy link
Copy Markdown
Owner

Opening PR of the W006 Telemetry transport chain. Stands up CritterCab.Telemetry as the repo's second service and realizes W006 slice 1 (TelemetryPolicyConfigured config-as-events) in one PR, per the named skeleton-plus-first-slice cadence exception (mirrors the Dispatch skeleton + slice-5.1 precedent).

Config-as-events is the dependency-correct first slice: slice 2's gRPC ingest reads this slice's TelemetryPolicy view (throttlePolicyVersion, h3Resolution, intervals). So this opening PR of the "first real transport build" deliberately lands no transport — that's the correct dependency order, not an omission.

What landed

Skeleton — new Microsoft.NET.Sdk.Web service mirroring Dispatch (own crittercab_telemetry DB, ports 5315/5316), paired Alba test project, apphost + slnx wiring. Kafka deliberately not wired into apphost.cs — transport lands with the slice that needs it (slice 3), the same deferral the Dispatch skeleton made.

Slice 1 (TelemetryPolicy/ feature folder)

  • TelemetryPolicyConfigured singleton event stream (full-replacement, ADR-011)
  • ConfigureTelemetryPolicy command + nested AbstractValidator (boundary validation: intervals positive, heartbeat ≥ minPublish, h3Resolution in 0–15)
  • TelemetryPolicy self-aggregating live-stream view exposing throttlePolicyVersion as a long (the Marten stream version, matching the proto int64)
  • IInitialData migration-time idempotent bootstrap seed (defaults 9/30/5, operatorId=system-bootstrap) per ADR-011 Option A
  • 3 Alba GWTs (bootstrap / reconfigure / reject) + a DB-less smoke test

Two firsts in code

  • First config-as-events instance — ADR-011's third instance overall; Dispatch and Onboarding were design-only. Triggers ADR-011's deferred migration-template skill → registered as a DEBT.md row (not authored here).
  • First FluentValidation boundary validation — introduces WolverineFx.Http.FluentValidation.

Verification

  • Four jasperfx-source-verifier gates ran before wiring: IInitialData seed vehicle, long stream-version property, self-aggregating LiveStreamAggregation registration + Marten-9 partial scope, Wolverine.HTTP FluentValidation middleware. All confirmed at build.
  • The gRPC client-streaming re-verification found the local JasperFx checkout stale at V5.37.2 (no 6.1x/6.17 source) — hand-wire verdict holds through 5.37.2, 6.17 unverified. This is a slice-2 (PR B) concern and gated nothing here.
  • dotnet build green; smoke test green locally. Local Docker's container-start path was wedged all session (the handoff-flagged condition), so the 3 slice-1 Testcontainers tests are gated on CI here rather than run locally.

No ADR fires — W006 §11's three candidates are all later-arc (Kafka topic-naming → slice 3; stream-processing-4th-shape → slices 2–4; windowed client-streaming → slice 2). No narrative anchor (PR #40: the narrative layer does not apply to Telemetry).

Prompt + retro: docs/{prompts,retrospectives}/implementations/006-telemetry-skeleton-and-slice-1-config.md.

Follow-on PRs

  • B — slice 4 (LastKnownPosition + eviction) + slice 2 (gRPC ingest, hand-wired against IMessageBus)
  • C — slice 3 (DriverLocationUpdated → Kafka; wires Kafka into apphost)
  • D — slice 5 (Dispatch consumer, Kafka half — replaces NearbyAvailableDriversStub)

…ig-as-events)

Stands up CritterCab.Telemetry as the repo's second service and realizes W006
slice 1 in one PR (skeleton + first-slice cadence exception; mirrors the Dispatch
skeleton + slice-5.1 precedent). Opening PR of the W006 transport chain — config-
as-events is the dependency-correct first slice (slice 2's gRPC ingest reads this
slice's TelemetryPolicy view), and it lands no transport.

- TelemetryPolicyConfigured singleton event stream (full-replacement, ADR-011)
- ConfigureTelemetryPolicy command + nested AbstractValidator (boundary validation)
- TelemetryPolicy self-aggregating live-stream view; throttlePolicyVersion as long
  (the Marten stream version, matching the proto int64)
- IInitialData migration-time idempotent bootstrap seed (defaults 9/30/5,
  operatorId=system-bootstrap) per ADR-011 Option A
- 3 Alba GWTs (bootstrap / reconfigure / reject) + DB-less smoke test
- apphost: Telemetry + crittercab_telemetry DB (5315/5316); Kafka deliberately
  deferred to slice 3

First config-as-events instance in code (ADR-011's third instance; Dispatch and
Onboarding were design-only) and first FluentValidation use. Four jasperfx-source-
verifier gates ran before wiring. No ADR fired (W006 §11 candidates are later-arc).

Prompt + retro under docs/{prompts,retrospectives}/implementations/006-*. DEBT row
registered for the config-as-events bootstrap-seed skill (ADR-011's deferred follow-up).
…liation

The critter-skill-auditor Phase 2 pass caught a design-vs-implementation gap:
W006 §6.1 locks the seed as "ADR-011 Option A (migration-time seed)", but the
Marten IInitialData realization runs at host startup — structurally Option B, the
option ADR-011 rejected over a multi-instance seed race. IInitialData does serve
Option A's intent under the JasperFx `resources setup` deploy step, but also self-
seeds at host start; the race is benign here (idempotent guard + full-replacement)
and irrelevant at single-instance MVP scale.

Corrects artifacts to stop bare-claiming "Option A" and registers the reconciliation
(plus the write-path optimistic-concurrency question — Append with no expected version,
because [WriteAggregate] has no binding to a constant singleton id) as questions the
future canonical-pattern skill must resolve. The ADR-011 amendment itself is a
user-owned decision, not made here.

- TelemetryPolicyBootstrap.cs / Program.cs: accurate seed comments + reconciliation note
- W006 Document History: open-reconciliation note
- DEBT.md: two design questions the reference impl must resolve before enshrining
- retro: "design meets code" section capturing the finding
…est)

CI caught it: the reject GWT got 200 instead of 400 — the boundary validator
never ran. UseFluentValidationProblemDetailMiddleware resolves IValidator<T> from
DI, but nothing registered the validators. Add opts.UseFluentValidation()
(WolverineFx.FluentValidation) which discovers and registers all IValidator<>
in the app assembly — the idiomatic pairing the Wolverine HTTP sample uses, and it
auto-discovers future validators rather than needing a per-validator registration.

Local build green; the reject test's 400 is verified on CI (local Docker wedged).
…wiring DEBT

Records the confirmed CI outcome (run 29119259592: Telemetry 4/4 + Dispatch 11/11)
and the CI-caught validator-registration bug as a methodology lesson (a Docker-wedged
session's DB-less smoke test gives false confidence about paths it never touches).
Registers the Wolverine.HTTP FluentValidation two-call wiring as a wolverine-http-handlers
skill gap.
@erikshafer erikshafer self-assigned this Jul 10, 2026
… LWW

Resolves the Option-A/B-for-Marten gap the slice-1 Phase-2 audit surfaced (user-
directed, in-PR). Amends ADR-011 with a 2026-07-10 section recording that IInitialData
(via .InitializeWith<T>()) is the canonical Marten realization of Option A — it seeds at
the deploy-time apply step and idempotently at host start as a self-healing safety net;
the multi-instance race is mitigated by the idempotent guard + full-replacement and
avoided by the deploy step / single-instance MVP. Also records last-writer-wins as the
accepted config-singleton write semantic (full-replacement has no invariant to defend;
optimistic concurrency not required; manual constant-id append since [WriteAggregate]
has no id field to bind).

Cascades the "resolved" status through DEBT (skill now unblocked), W006 history, the
retro (design-meets-code + spec-delta + outstanding items, documenting the mid-flight
user-directed expansion), both README indexes, and the two seed code comments. Prompt
left as authored (historical record), not retroactively re-scoped.
@erikshafer
erikshafer merged commit 4d288bd into main Jul 10, 2026
1 check passed
@erikshafer
erikshafer deleted the telemetry/skeleton-and-slice-1-config branch July 10, 2026 20:32
erikshafer added a commit that referenced this pull request Jul 10, 2026
…nsport build

Introduces three handoff notes tracking Telemetry chain progression from PR #39 (protos) through PR #42 (skeleton + slice 1). Post-PR-42 handoff orients next session on PR B: slices 4+2 paired (LastKnownPosition store + ReportLocations gRPC ingest), CritterCab's first gRPC surface in code and first Kafka testcontainer. Documents verified WolverineFx.Grpc client-streaming gap (must hand-wire against IMessageBus through 5.37.2; 6.17 unconfirmed), local Docker wedged state (lean on CI), and ADR-011 amendment locked (IInitialData bootstrap + last-writer-wins singletons). Supersedes three earlier untracked handoffs after PR #40 (narrative decision—does not apply, recorded) and PR #41 (package refresh to 6.17.0) both merged. Carries forward design-locked guardrails, three W006 §11 ADR candidates, and open skill-debt items. Includes narrative-decision and post-PR-41 intermediate handoffs for session lineage; all disposable once next session orients.
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.

1 participant