|
|
@@ -0,0 +1,155 @@ |
|
|
|
|
|
--- |
|
|
|
|
|
epic: 1 |
|
|
|
|
|
title: Foundation, Auth, Municipality Profiles & Admin Seed |
|
|
|
|
|
date: 2026-05-07 |
|
|
|
|
|
facilitator: Bob (Scrum Master) |
|
|
|
|
|
participant: Daniel (Project Lead, solo dev with rotating AI agents) |
|
|
|
|
|
status: completed |
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
|
|
# Epic 1 Retrospective |
|
|
|
|
|
|
|
|
|
|
|
## Epic Summary |
|
|
|
|
|
|
|
|
|
|
|
| | | |
|
|
|
|
|
|---|---| |
|
|
|
|
|
| Stories delivered | 13 of 13 (100%) | |
|
|
|
|
|
| Test growth | Backend 4 → 155, Frontend 18 → 45, no regressions | |
|
|
|
|
|
| Course corrections absorbed mid-epic | 2 (Logout SCP, Spreadsheet-Source SCP) | |
|
|
|
|
|
| Stories that landed clean (≤1 review patch) | 1-1, 1-2, 1-12, 1-13 | |
|
|
|
|
|
| Stories with heavy review cycles (6+ patches) | 1-3, 1-7, 1-8, 1-9, 1-10, 1-11 (14 patches) | |
|
|
|
|
|
| Reusable patterns hardened | `IAuditService` (append-only), `ILegacyLinkedRecord(Provider)`, `AuthIntegrationTestFactory` | |
|
|
|
|
|
| New CI gate | `.gitea/workflows/release-gates.yml` | |
|
|
|
|
|
|
|
|
|
|
|
## What Went Well |
|
|
|
|
|
|
|
|
|
|
|
- **Reusable infrastructure paid off fast.** `ILegacyLinkedRecord` from Story 1.8 became the integration seam every later municipality story (1-10, 1-12) plugged into automatically. |
|
|
|
|
|
- **Review gate caught real defects.** Two SCPs (logout flow, late spreadsheet source) landed mid-epic with zero rework — review surfaced gaps before `done`. |
|
|
|
|
|
- **Append-only audit contract was the right call.** The 1-3 → 1-5 cascade where the OIDC review motivated `IAuditService` having no update/delete methods is now the de-facto pattern. |
|
|
|
|
|
- **TDD-first discipline correlated cleanly with quality.** Stories 1-12 and 1-13, which explicitly noted "added failing tests before implementation," combined for 1 review patch. |
|
|
|
|
|
|
|
|
|
|
|
## What Struggled |
|
|
|
|
|
|
|
|
|
|
|
- **"Contract-shape over behavior" pattern.** Stories 1-7, 1-8, 1-9, and 1-11 each shipped non-functional production wiring on first submission while passing happy-path tests. Examples: 1-7 compared schema baseline to itself, 1-9's `SeedAsync()` was a placeholder and not DI-registered, 1-11 wouldn't compile. |
|
|
|
|
|
- **"Manual endpoint shipped, scheduler/pipeline integration deferred" pattern.** Release gate (1-7), nightly integrity check (1-8), and seed (1-9) all shipped admin/manual triggers without the pipeline integration that was the actual deliverable. |
|
|
|
|
|
- **Validation gaps on inputs that look valid.** OIDC state never validated (1-3), `ReadOnlyCommandGuard` rejecting valid SELECTs (1-6), zero/negative IDs accepted (1-8), raw-string rules with no scope metadata (1-9). |
|
|
|
|
|
- **Test-isolation rediscovery.** `AuthIntegrationTestFactory` had to override `IAuditService` (1-5) and again `ILegacyDataAccess` (1-10). Pattern emerged twice — now a guardrail. |
|
|
|
|
|
|
|
|
|
|
|
## Key Insight (Driving Action Items) |
|
|
|
|
|
|
|
|
|
|
|
**Routing the work to the right model matters.** Local-hosted AI handled bounded CRUD/UI-binding work (1-1, 1-2, 1-12, 1-13) cleanly. The same model struggled where the AC was satisfied by a stub but the actual deliverable was system-level wiring — DI registration, scheduler hookup, pipeline integration, real implementation behind an interface. That's exactly the heavy-rework set: 1-7, 1-8, 1-9, 1-11. |
|
|
|
|
|
|
|
|
|
|
|
Story 1-11 (confirmed local AI, 14 review patches, didn't compile on first pass) is the canonical signature of the mismatch. |
|
|
|
|
|
|
|
|
|
|
|
## Action Items |
|
|
|
|
|
|
|
|
|
|
|
### 1. Tag stories with `ai_routing` field at creation time |
|
|
|
|
|
|
|
|
|
|
|
Add to story frontmatter: |
|
|
|
|
|
|
|
|
|
|
|
```yaml |
|
|
|
|
|
ai_routing: local-ok # CRUD shape, bounded surface, contract == behavior |
|
|
|
|
|
ai_routing: cloud-only # infrastructure, wiring, multi-component integration |
|
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
**Heuristics:** |
|
|
|
|
|
|
|
|
|
|
|
- `local-ok`: single-table CRUD, UI binding to existing API, read-only views, isolated test additions. |
|
|
|
|
|
- `cloud-only`: introduces a new cross-cutting contract, requires DI registration in `Program.cs`, requires scheduler/pipeline hookup, or has a "real implementation behind interface" requirement. |
|
|
|
|
|
|
|
|
|
|
|
**Routing applied to Epic 2:** |
|
|
|
|
|
|
|
|
|
|
|
| Story | Routing | |
|
|
|
|
|
|---|---| |
|
|
|
|
|
| 2.1 Municipality-to-Cycle Kanban Entry Point | cloud-only | |
|
|
|
|
|
| 2.2 Create Election-Cycle Job | local-ok | |
|
|
|
|
|
| 2.3 Election-Cycle Key Dates | local-ok | |
|
|
|
|
|
| 2.4 Prior-Cycle Defaults Application | cloud-only | |
|
|
|
|
|
| 2.5 Readiness Status & SafeCommitRail | cloud-only | |
|
|
|
|
|
| 2.6 Spreadsheet Import & Column Mapping | cloud-only | |
|
|
|
|
|
|
|
|
|
|
|
**Owner:** Daniel |
|
|
|
|
|
**Trigger:** Apply during `bmad-create-story` for next Epic 2 story |
|
|
|
|
|
**Success criteria:** Every Epic 2 story has `ai_routing` set before development begins |
|
|
|
|
|
|
|
|
|
|
|
### 2. "Wiring Proven" acceptance criterion for cloud-only stories |
|
|
|
|
|
|
|
|
|
|
|
Add a standard AC to any cloud-only story: |
|
|
|
|
|
|
|
|
|
|
|
> "The production caller invokes this on the real path — verified by an integration test that fails if DI registration, scheduler hookup, or pipeline gate is missing." |
|
|
|
|
|
|
|
|
|
|
|
**Why:** Would have caught 1-7, 1-8, 1-9, 1-11 on first pass. Closes the "contract satisfies AC but production wiring is absent" gap. |
|
|
|
|
|
**Owner:** Daniel (apply at story creation) |
|
|
|
|
|
**Success criteria:** No cloud-only story merged without this AC verified |
|
|
|
|
|
|
|
|
|
|
|
### 3. Tests-first requirement on cloud-only stories |
|
|
|
|
|
|
|
|
|
|
|
Make TDD non-negotiable on cloud-only stories: failing test added and committed before implementation. Optional on local-ok stories. |
|
|
|
|
|
|
|
|
|
|
|
**Why:** 1-12 and 1-13 used this discipline and combined for 1 review patch. 1-11 (no tests-first) took 14. |
|
|
|
|
|
**Owner:** Daniel (enforce via dev-story workflow) |
|
|
|
|
|
**Success criteria:** Cloud-only story records explicitly note "tests added before implementation" |
|
|
|
|
|
|
|
|
|
|
|
## Carried-Forward Items (Deferred from Epic 1) |
|
|
|
|
|
|
|
|
|
|
|
Daniel chose to carry these forward rather than schedule a hardening pass. Track and address opportunistically inside Epic 2 stories where they intersect. |
|
|
|
|
|
|
|
|
|
|
|
**HIGH severity** |
|
|
|
|
|
|
|
|
|
|
|
- `AuthorizationProbeController` ships canned operational routes in production controllers (from 1-4) |
|
|
|
|
|
- Audit log JSONL files committed to repo with actor identities, trace IDs (from 1-7) |
|
|
|
|
|
- OIDC `workspacePath` not validated as relative path → latent open-redirect (from 1-3) |
|
|
|
|
|
|
|
|
|
|
|
**MEDIUM severity** |
|
|
|
|
|
|
|
|
|
|
|
- JCode embedded-whitespace mismatch between `GetAllJurisdictionsAsync` and `GetByJCode` (from 1-10) — relevant to Story 2.2 join paths |
|
|
|
|
|
- Hardcoded audit `Outcome="updated display name"` becomes misleading once profile gains fields (from 1-10) |
|
|
|
|
|
- `pendingCallbackSequence` not scoped per callback invocation; double-mount of `useOidcSession` would skip CSRF (from 1-3) |
|
|
|
|
|
|
|
|
|
|
|
**LOW severity** |
|
|
|
|
|
|
|
|
|
|
|
- `ProfileId` uses `Guid.ToString("N")` (no hyphens) — cross-system UUID format risk |
|
|
|
|
|
- `CreatedAt` stored but absent from API DTO (1-10) |
|
|
|
|
|
- `MunicipalityAddress.State` is free-text with no validation (1-11) |
|
|
|
|
|
- `MunicipalityId` existence not validated pre-insert; surfaces as `DbUpdateException` (1-11) |
|
|
|
|
|
- Post-create `refresh()` doesn't reload jurisdiction list (1-10) |
|
|
|
|
|
|
|
|
|
|
|
## Readiness Assessment |
|
|
|
|
|
|
|
|
|
|
|
| Dimension | Status | |
|
|
|
|
|
|---|---| |
|
|
|
|
|
| Testing & Quality | Solid — 155 backend / 45 frontend, monotonic growth | |
|
|
|
|
|
| Deployment | Local-only (no production deploy planned for Epic 1 alone) | |
|
|
|
|
|
| Stakeholder Acceptance | Deferred — no external review until Epic 2+ adds visible value | |
|
|
|
|
|
| Technical Health | Feels solid (Daniel's gut-check) | |
|
|
|
|
|
| Unresolved Blockers | None — deferred items carried forward, not blocking | |
|
|
|
|
|
|
|
|
|
|
|
## Next Epic Preview — Epic 2: Election-Cycle Job Creation & Planning |
|
|
|
|
|
|
|
|
|
|
|
6 stories, all already at `ready-for-dev`. Direct dependencies on Epic 1 work are intact: |
|
|
|
|
|
|
|
|
|
|
|
- Legacy join keys (1-6, 1-8) → every Epic 2 story |
|
|
|
|
|
- Seed required-field rules (1-9) → Story 2.5 readiness/SafeCommitRail |
|
|
|
|
|
- Audit service (1-5) → all Epic 2 commits |
|
|
|
|
|
- Workspace shell + auth (1-2, 1-3, 1-4) → Story 2.1 kanban entry point |
|
|
|
|
|
- Prior-cycle defaults view (1-13) → Story 2.4 *applies* those defaults |
|
|
|
|
|
|
|
|
|
|
|
**No epic update required.** Nothing from Epic 1 invalidates Epic 2's current plan. Story 2.6 (spreadsheet import) was added via SCP and is already accounted for in the story list. |
|
|
|
|
|
|
|
|
|
|
|
## Critical Path Before Epic 2 Kickoff |
|
|
|
|
|
|
|
|
|
|
|
None. Daniel can begin `bmad-create-story` for Story 2.1 in a fresh context window. |
|
|
|
|
|
|
|
|
|
|
|
## Significant Discoveries |
|
|
|
|
|
|
|
|
|
|
|
None requiring epic plan updates. |
|
|
|
|
|
|
|
|
|
|
|
## Commitments Recap |
|
|
|
|
|
|
|
|
|
|
|
- 3 action items (story routing tagging, wiring-proven AC, tests-first on cloud-only) |
|
|
|
|
|
- 0 critical-path items |
|
|
|
|
|
- 11 deferred items carried forward into Epic 2 work |
|
|
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
|
|
*Retrospective facilitated 2026-05-07.* |