mirror of
https://github.com/azaion/annotations.git
synced 2026-06-21 14:11:07 +00:00
03f879206e
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>
9.1 KiB
9.1 KiB
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 0–18: 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_diranddeleted_dir— the storage layer is treated as durable by assumption.