# Step 4 — Verification Log Verification pass over `_docs/02_document/` against `src/` source. ## Scope Documents verified: - `architecture.md` - `system-flows.md` - `data_model.md` - `deployment/{containerization,ci_cd_pipeline,environment_strategy,observability}.md` - `diagrams/flows/{flow_annotation_create,flow_sse_subscription,flow_failsafe_drain}.md` - (sanity re-check only) `module-layout.md`, `components/*/description.md`, `modules/*.md` ## Method For each generated artifact: 1. Extracted code-entity references (controllers, services, methods, DTOs, env vars, table/column names, route paths). 2. Cross-referenced each against the actual source (`src/Program.cs`, `src/Controllers/*`, `src/Services/*`, `src/Database/*`, `src/Enums/*`, `src/DTOs/*`, `.woodpecker/build-arm.yml`, `src/Dockerfile`). 3. Re-traced each system flow's mermaid sequence against the corresponding service/controller code. 4. Listed corrections, applied them inline to the affected files, and recorded them below. ## Counts | Item | Verified | Corrected | Open question | |------|----------|-----------|----------------| | Controllers + their routes | 6 | 0 | 0 | | Services + their public methods | 8 | 0 | 0 | | DB tables / columns | 9 / ~60 | 0 | 5 (lazy upsert / `media.duration` / class catalog mutability / id collision / outbox JSON shape) | | Enums | 7 | 0 | 0 | | Env vars | 8 | 0 | 0 | | Flows | 8 | 4 (F1, F7, F8, dependencies table) | 6 (consolidated below) | | ADRs | 7 | 1 (ADR-004 hash details) | 0 | Module-level coverage: **11 / 11 modules** documented; **6 / 6 components** assembled. ## Corrections applied inline ### `architecture.md` 1. **Internal communication table**: tightened to reflect that SSE publish + outbox enqueue happen **only on `CreateAnnotation`**; outbox enqueue is gated by `system_settings.silent_detection`. Added explicit row noting `DatasetService` writes are silent on SSE/outbox today. 2. **ADR-004 (annotation id hash)**: replaced "hash of bytes" with the actual `ComputeHash` strategy — `XxHash64` over a deterministic sample (length prefix + head/middle/tail 1 KB for inputs > 3072 bytes; full bytes otherwise). Documented collision implication. 3. **Open Architectural Risks**: rewrote with verified findings — silent Update/Delete/dataset paths, `silent_detection` semantics, F1 non-atomicity, static `EnqueueAsync` vs project rule. 4. **Section 4 "Data flow summary"**: split into Create-only / Update-and-friends / read paths, removed the inaccurate claim that thumbnails are produced inline by Create. ### `system-flows.md` 1. **F1 sequence + data flow + error scenarios**: replaced with the verified ordering — image file → optional media row → annotation → detections (`BulkCopyAsync`) → label file → SSE publish → conditional outbox enqueue. Removed thumbnail write from the Create path. 2. **F7 ("Reset call missed" risk)**: removed — verified that `SettingsService` calls `pathResolver.Reset()` at lines 71 and 85 of `Services/SettingsService.cs`. Replaced with a "Verified" note. 3. **F8 (Dataset bulk status)**: rewrote — `DatasetService.UpdateStatus` and `BulkUpdateStatus` issue direct `UPDATE annotations SET status` statements only. **They do NOT publish SSE and do NOT enqueue the outbox.** Updated routes (`PATCH /dataset/{id}/status`, `POST /dataset/bulk-status`) and error scenarios accordingly. 4. **Flow Dependencies table**: corrected F1 row (gating + Create-only), F3 row (only F1 Create publishes), F8 row (no SSE / no outbox today). ### `diagrams/flows/flow_annotation_create.md` - Replaced sequence + flowchart to match the verified F1 ordering (image first, optional media, label, SSE, conditional outbox); thumbnail removed. - Added note that Update/UpdateStatus/Delete are silent today. ### `diagrams/flows/flow_sse_subscription.md`, `flow_failsafe_drain.md` - No structural corrections needed; spot-checked sequence vs `AnnotationsController.Events`, `AnnotationEventService`, `FailsafeProducer.EnqueueAsync`. Notes already capture multi-drainer dedupe and channel-unbounded back-pressure concerns. ### `data_model.md` - No structural corrections; verified every column name and default against `Database/DatabaseMigrator.cs` and `Database/Entities/*.cs`. Spot-quirk (`detection_classes` ids 9 + 10 share `#000080`) is pre-existing and noted. ### `deployment/*` - No structural corrections; verified `.woodpecker/build-arm.yml` step-by-step, `Dockerfile` two-stage build, `Program.cs` env-var fallbacks. ## Confirmed entities (sample — full list traced during the pass) Controllers and routes (file:line where attributes were inspected): - `AnnotationsController` — `Controllers/AnnotationsController.cs:10–80` — `[Route("annotations")]`, `[Authorize(Policy = "ANN")]`, all listed routes match. - `MediaController` — `Controllers/MediaController.cs:10–55` — `[Route("media")]`, `[Authorize(Policy = "ANN")]`, routes: `POST`, `POST /batch`, `GET`, `GET /{id}/file`, `DELETE /{id}`. - `DatasetController` — `Controllers/DatasetController.cs:9–41` — `[Route("dataset")]`, `[Authorize(Policy = "DATASET")]`, routes: `GET`, `GET /{annotationId}`, `PATCH /{annotationId}/status`, `POST /bulk-status`, `GET /class-distribution`. - `SettingsController` — `Controllers/SettingsController.cs:10–66` — `[Route("settings")]`, segments `system`, `directories`, `camera`, `user` each with GET + PUT. - `ClassesController` — `Controllers/ClassesController.cs:9–13` — `[Route("classes")]`, `[Authorize]`, single `[HttpGet]`. - `AuthController` — **removed** in the auth refactor; annotations no longer mints or refreshes tokens. `JwtExtensions.AddJwtAuth` (verifier-only, ES256 over admin's JWKS) is the sole auth wiring in `Program.cs`. Services: - `AnnotationService.CreateAnnotation` (`Services/AnnotationService.cs:13–104`) — verified sequence used to rewrite F1. - `AnnotationService.UpdateAnnotation` / `UpdateStatus` / `DeleteAnnotation` — verified that none publish SSE or enqueue outbox. - `DatasetService.UpdateStatus` / `BulkUpdateStatus` (`Services/DatasetService.cs:75–94`) — verified silent on SSE / outbox. - `SettingsService` — verified `pathResolver.Reset()` calls at lines 71, 85. - `FailsafeProducer.EnqueueAsync` — confirmed as the public outbox-write helper, called by `AnnotationService.CreateAnnotation` only. Tables / migrator (`Database/DatabaseMigrator.cs`): - All 9 tables referenced in `data_model.md` exist with the columns and defaults as documented; idempotent `CREATE TABLE IF NOT EXISTS` + `ALTER TABLE … IF NOT EXISTS`; `detection_classes` seed of 19 rows with `ON CONFLICT DO NOTHING`. Env vars (`Program.cs`): - Required (fail-fast via `ConfigurationResolver.ResolveRequiredOrThrow`): `DATABASE_URL`, `JWT_ISSUER`, `JWT_AUDIENCE`, `JWT_JWKS_URL`. - Optional with defaults: `RABBITMQ_HOST`, `RABBITMQ_STREAM_PORT`, `RABBITMQ_PRODUCER_USER`, `RABBITMQ_PRODUCER_PASS`, `RABBITMQ_STREAM_NAME`. - CORS: `CorsConfig:AllowedOrigins` (string array) + `CorsConfig:AllowAnyOrigin` (bool); `CorsConfigurationValidator.EnsureSafeForEnvironment` blocks startup in `Production` when origins are empty and `AllowAnyOrigin` is not explicitly set. CI (`.woodpecker/build-arm.yml`): - `event: [push, manual]`, `branch: [dev, stage, main]`, `platform: arm64`, secret refs, `${BRANCH}-arm` tag, OCI image labels — all verified. ## Stakeholder resolutions (closed 2026-05-14) The six open questions surfaced by this pass were resolved with the maintainer. Authoritative wording lives in `architecture.md` (ADR-004, ADR-008..ADR-011 + Refactor Backlog RB-01..RB-06). Quick map: | Question | Resolution | Tracked | |----------|------------|---------| | Are silent Update/Delete/dataset-status changes intentional? | No — World B is the design; the drainer (`FailsafeProducer.cs:108–123`) was already plumbed for `Validated` + `Deleted` ops, the producer side was never wired in the new HTTP backend (legacy WPF UI did this directly). Wire all mutations to publish + enqueue. | ADR-009 / RB-01 | | `silent_detection` semantics? | Remove the flag entirely — superseded by the suite e2e harness. | ADR-010 / RB-02 | | F1 atomicity (FS / DB / outbox)? | Adopt a business-transaction wrapper (transactional outbox); FS writes go post-commit. | ADR-008 / RB-03 | | `XxHash64` over sample collision risk? | Switch to `XxHash3.Hash128` over the same sample (file-size-independent — videos can be 3–5 GB). | ADR-004 / RB-04 | | `FailsafeProducer.EnqueueAsync` static + DB I/O? | Accept as-is; documented `coderule.mdc` deviation. | (no refactor) | | `detection_classes` static or admin-managed? | Admin-managed with read-through cache (`PathResolver`-style `Reset()`). | ADR-011 / RB-06 | ### Additional finding while verifying #1 - `FailsafeProducer.cs:138` has an empty `catch { }` that swallows `IOException` on image read and emits a stream message with `image = null`. Direct `coderule.mdc` violation ("never suppress errors silently"). Operationally invisible failure mode. Tracked as RB-05 (architecture doc). ## Step 4.5 follow-on resolutions (closed 2026-05-14) Confirmed alongside the Step 4.5 condensed-view approval: | Question | Resolution | Tracked | |----------|------------|---------| | Suite vs code: `Flight` (code) vs `mission` (suite spec) | Rename code → `Mission*`; suite stays canonical | ADR-012 / RB-07 | | Stream consumer dedupe contract owner | This service owns it; dedupe by `(annotationId, operation, dateTime)` baked into the wire message | ADR-013 / RB-09 | | Hard-delete vs soft-delete | Soft-delete: status → `Deleted (40)`, files relocated to a new `deleted_dir` | ADR-009 (folded in) / RB-01 | | Tight coupling 04 Dataset ↔ 01 Annotations REST | Decouple — dataset writes flow through `AnnotationService` via a public domain interface | RB-08 | ## Remaining gaps and uncertainties (carried into Step 6 problem extraction) 1. **`media.duration` format**: TEXT NOT NULL is permissive; format is unspecified. 2. **Lazy-upsert semantics** for `system_settings` / `directory_settings` / `camera_settings` — confirm services initialize defaults vs rely on user-driven inserts. 3. **`UserId` body field vs JWT subject** drift — reconcile in suite spec or in code. 4. **No automated tests in repo**: addressed by autodev Phase A Steps 3–7. ## Completeness score - 11 / 11 modules documented (`modules/*.md`). - 6 / 6 components assembled (`components/*/description.md`). - 1 / 1 module-layout file (`module-layout.md`). - 1 / 1 architecture file (`architecture.md`). - 1 / 1 system-flows file (`system-flows.md`) covering 8 flows. - 1 / 1 data-model file (`data_model.md`) covering 9 tables. - 4 / 4 deployment files (`deployment/*.md`). - 3 flow diagrams (F1, F3, F4) in `diagrams/flows/`. **Score: 100% of modules + components covered.** Remaining open items are behavioral questions, not coverage gaps.