Files
annotations/_docs/00_problem/acceptance_criteria.md
T
Oleksandr Bezdieniezhnykh 03f879206e docs+src: complete Steps 1-3 outcomes + auth re-sync baseline
This commit captures everything produced during autodev existing-code
Steps 1 (Document), 2 (Architecture Baseline Scan), and 3 (Test Spec),
together with the targeted auth + CORS re-sync triggered on 2026-05-14
when codebase drift was detected at Step 4 entry. None of this work was
previously committed.

Step 1 (Document) — 50+ _docs/02_document/ files: problem, solution,
architecture, system flows, glossary, module-layout, per-component
specs (01..06), modules, deployment, diagrams, data model, FINAL
report, verification log, discovery.

Step 2 (Architecture Baseline) — architecture_compliance_baseline.md.
Verdict PASS_WITH_WARNINGS (0 Critical, 0 High, 1 Medium, 2 Low). No
High/Critical findings; auto-chained to Step 3 per existing-code flow.

Step 3 (Test Spec) — _docs/02_document/tests/* (67 scenarios across
blackbox, security, resilience, resource-limit, performance), plus
e2e/docker-compose.test.yml, e2e/seed/run.sh, scripts/run-tests.sh,
scripts/run-performance-tests.sh. Coverage 88% over the active scope
(40 of 45 items covered, 6 RB-deferred, 5 documented-as-uncovered).

Targeted auth + CORS re-sync — replaces the deleted in-house token
issuer with a JWKS-verifier model. AuthController and TokenService
removed; JwtExtensions switched from HS256 symmetric to ES256 over
admin's JWKS. ConfigurationResolver and CorsConfigurationValidator
added under src/Infrastructure/. ADR-002 and ADR-006 retired; SEC-01,
SEC-02, SEC-03 marked Closed. One new testability risk recorded in
architecture.md Open Risks Section 6 (JWKS HTTPS gating).

Source changes:
- src/Auth/JwtExtensions.cs (modified) — ES256, JWKS, alg pinning
- src/Program.cs (modified) — DI wiring for ConfigurationResolver
  and CorsConfigurationValidator
- src/Controllers/AuthController.cs (deleted) — no in-service issuance
- src/Services/TokenService.cs (deleted) — same
- src/Infrastructure/ConfigurationResolver.cs (new)
- src/Infrastructure/CorsConfigurationValidator.cs (new)
- .env.example (new) — required env var documentation
- .gitignore (updated)

Cross-repo coordination: _docs/cross-repo/flights_h1_h2_h3_change_spec
captures the change-spec for downstream services that consumed the now
deleted /auth endpoints.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 20:19:05 +03:00

9.1 KiB
Raw Blame History

Azaion.Annotations — Acceptance criteria (retrospective)

Every criterion has a measurable value and a code/config evidence pointer. No automated test suite exists in the repo today (_docs/02_document/00_discovery.md), so the criteria below are derived from validation rules, configuration limits, and explicit code branches — they are the contract a future test suite (autodev existing-code Step 3 + Step 6) must encode. Criteria that depend on a Refactor Backlog item landing first are flagged with [after RB-XX].

Functional — annotation lifecycle (component 01 annotations-rest)

ID Criterion Measurable value Evidence
AC-F-01 POST /annotations with image_bytes + detections for the same payload returns the same id on every call. id is XxHash3.Hash128 (32 hex chars) of the sampled image_bytes window — byte-stable. Services/AnnotationService.cs GenerateAnnotationId(...) (post RB-04 — currently XxHash64).
AC-F-02 A repeat POST /annotations for an existing id is a no-op write (no duplicate row, no duplicate file). DB reads return existing row before insert; file write is WriteAllBytesAsync overwriting same bytes. Services/AnnotationService.cs.
AC-F-03 POST /annotations writes a YOLO-format label file at images_dir/<id>.txt containing one line per detection: <class_id> <cx> <cy> <w> <h>. Exact format with space-separated floats, normalised 0..1, line-per-detection. Services/AnnotationService.cs (label-file write site).
AC-F-04 POST /annotations returns HTTP 200 with the persisted entity (id, status, detections). Response shape mirrors AnnotationDto. Controllers/AnnotationsController.cs.
AC-F-05 [after RB-01] Every successful POST/PUT/PATCH/DELETE /annotations/* emits exactly one SSE event AND inserts exactly one annotations_queue_records row, with the correct QueueOperation enum. Created=10, Updated=20, Deleted=40. _docs/02_document/architecture.md ADR-009; Services/QueueOperation.cs.
AC-F-06 [after RB-01] DELETE /annotations/{id} flips the row to Status=Deleted (40), relocates images_dir/<id>.{jpg,txt} to deleted_dir/, and emits the Deleted lifecycle event. Row count unchanged; files moved; status transitions per AnnotationStatus. _docs/02_document/architecture.md ADR-009 + glossary "Soft-delete".
AC-F-07 [after RB-01 + RB-08] Soft-deleted rows do not appear in GET /annotations or GET /dataset results. Filter WHERE Status <> 40 enforced at every read path. RB-01, RB-08 in _docs/02_document/architecture.md.
AC-F-08 [after RB-02] There is no silent_detection column, field, DTO property, or branch in code. Schema diff + grep produces zero matches. RB-02.

Functional — realtime + sync (component 02 annotations-realtime-sync)

ID Criterion Measurable value Evidence
AC-F-10 A connected SSE client receives the lifecycle event for a successful POST /annotations within 1 second of the response. <1s P99 in single-instance, single-pod local run. Services/AnnotationEventService.cs.
AC-F-11 A subscriber that joins after the event has been published does not receive it (channel is fire-and-forget). No backfill replay in Channel<>. ADR-001.
AC-F-12 FailsafeProducer consumes a row from annotations_queue_records and publishes a MessagePack-gzip frame to the azaion-annotations stream within the configured drain interval. Drain loop interval is the configured cadence; row deletion happens after stream confirm. Services/FailsafeProducer.cs.
AC-F-13 [after RB-09] Every wire message carries (annotation_id, operation, date_time) so a downstream consumer can dedupe re-deliveries. Three fields present on the wire schema. _docs/02_document/architecture.md ADR-013.

Functional — media (component 03 media)

ID Criterion Measurable value Evidence
AC-F-20 POST /media accepts a multipart upload, persists the file to the configured directory, and returns the persisted MediaDto. HTTP 200 + JSON body. Controllers/MediaController.cs, Services/MediaService.cs.
AC-F-21 POST /media/batch accepts N files in one request, writes N rows + N files, and returns N persisted DTOs. N inputs → N outputs, atomic per file. same.

Functional — dataset (component 04 dataset)

ID Criterion Measurable value Evidence
AC-F-30 GET /dataset honors filter parameters (mission id, status, class). Returned rows match filter conditions. Controllers/DatasetController.cs, Services/DatasetService.cs.
AC-F-31 POST /dataset/status/bulk flips status on N rows in a single SQL statement. One UPDATE WHERE id IN (…). Services/DatasetService.cs.

Functional — settings & metadata (component 05 settings-metadata)

ID Criterion Measurable value Evidence
AC-F-40 PUT /settings/directories persists changes and triggers pathResolver.Reset() so subsequent path lookups reflect the new values. Verified — Services/SettingsService.cs:71, 85. Services/SettingsService.cs.
AC-F-41 GET /classes returns the 19 seeded detection classes (ids 018: ArmorVehicle, Truck, Vehicle, Artillery, Shadow, Trenches, MilitaryMan, TyreTracks, AdditionArmoredTank, Smoke, Plane, Moto, CamouflageNet, CamouflageBranches, Roof, Building, Caponier, Ammo, Protect.Struct). 19 rows; ids stable from DatabaseMigrator. Database/DatabaseMigrator.cs:101-121.
AC-F-42 [after RB-06] [ADM] write endpoints exist for /classes; the in-memory cache invalidates on write via Reset(). Cache hit ratio observable; cache miss on each write. RB-06.

Functional — auth & platform (component 06 platform)

ID Criterion Measurable value Evidence
AC-F-50 A request bearing an ES256 access token issued by admin (iss = JWT_ISSUER, aud = JWT_AUDIENCE, signature verifies against the JWKS at JWT_JWKS_URL, exp in the future) reaches the controller. Tokens that fail issuer / audience / signature / lifetime validation, or whose alg is not ES256, return HTTP 401. JwtBearerHandler defaults + AddJwtAuth parameters. Auth/JwtExtensions.cs.
AC-F-51 Annotations does not host any token-issuance or token-refresh endpoint. Long-running callers refresh against admin's POST /token/refresh and pass the resulting access token to annotations. No [AllowAnonymous] route except /health; AuthController removed. Program.cs, suite admin docs.
AC-F-52 Endpoints under policy ANN reject callers without that role with HTTP 403. Endpoints under DATASET reject non-DATASET callers with HTTP 403. Endpoints under ADM reject non-ADM with HTTP 403. Authorization middleware. Auth/JwtExtensions.cs.
AC-F-53 All errors are returned in the { error: { code, message, …details } } envelope. Single envelope shape across all controllers. Middleware/ErrorHandlingMiddleware.cs, _docs/02_document/common-helpers/01_http-error-envelope.md.
AC-F-54 GET /health returns HTTP 200 within 5 seconds of process start (Dockerfile HEALTHCHECK). 200 OK on /health. Dockerfile, Program.cs.

Non-functional

ID Criterion Measurable value Evidence
AC-N-01 Container boot to /health 200 ≤ Docker HEALTHCHECK interval/timeout configured in the suite-level orchestrator. Per Dockerfile HEALTHCHECK directive (consult orchestrator config for actual values). Dockerfile.
AC-N-02 DatabaseMigrator.MigrateAsync() is idempotent — second boot against the same DB makes no schema changes. IF NOT EXISTS / ON CONFLICT DO NOTHING everywhere. Database/DatabaseMigrator.cs.
AC-N-03 FailsafeProducer keeps annotations_queue_records depth bounded under steady-state lifecycle traffic. Queue depth metric (to be exposed during Step 14 Observability work). Services/FailsafeProducer.cs.
AC-N-04 The service emits zero unhandled exceptions to clients — every uncaught exception is mapped via ErrorHandlingMiddleware into the error envelope. Middleware terminal handler. Middleware/ErrorHandlingMiddleware.cs.
AC-N-05 Single SSE connection survives ≥ 30 minutes idle with bounded memory (channel is unbounded; growth must come from real traffic, not heartbeats). Heap stable across 30-minute idle window. Services/AnnotationEventService.cs.

Gaps acknowledged

  • No measurable latency / throughput targets (P50, P95, P99) are stated anywhere in code. Need to be set during Step 15 (Performance Test).
  • No security audit findings yet (Step 14). Items like JWT issuer validation, CORS tightening, and Swagger gating are planned, not yet acceptance criteria.
  • No backup / RPO / RTO contract for images_dir and deleted_dir — the storage layer is treated as durable by assumption.