Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions docs/AGENT_QUICKSTART.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Agent Quickstart

Short reference for starting any PR. Copy-paste to the agent when launching a new session.

---

## Standard command (use for every new PR)

```
Read docs/ROADMAP.md and docs/TZ.md.

Verify git log --oneline -20 on main matches the "Completed" section of ROADMAP.md.

Start the PR listed under "In progress" in ROADMAP.md.

Rules:
- Do not redo closed work (check status markers in TZ.md).
- Do not skip ahead to later PRs.
- Do not propose scope outside the current PR.
- Decisions listed in ROADMAP.md "Decisions locked" section are final — do not re-discuss.
- If ROADMAP and codebase disagree, stop and ask in chat — do not guess.
- .mcp.json is gitignored, do not commit it.

Workflow per PR:
1. Create a feature branch from main
2. Implement scope incrementally, commit each logical step with a clear message
3. Run `dotnet test` + `npx tsc --noEmit` before each commit
4. Open PR with description linking to ROADMAP item and affected TZ points
5. Fix CI failures as needed
6. Squash-merge to main
7. Update ROADMAP.md: move completed item to "Completed", promote next from "Upcoming" to "In progress"
8. Update TZ.md status markers for affected points
9. Commit the ROADMAP/TZ update as first commit of next PR OR as a final chore commit

Begin.
```

---

## When things go wrong

**Agent proposes already-shipped work:**
> Check ROADMAP.md "Completed" section again. Your proposed work is in PR #X. Re-read main git log and restart planning.

**Agent proposes scope creep:**
> That is out of the current PR scope. Current PR is defined in ROADMAP.md "In progress" section only. If you think the extra scope is critical, stop and ask in chat.

**Agent is unsure about a design decision:**
> Check ROADMAP.md "Decisions locked" section. If your question is answered there, proceed. If not, ask in chat before proceeding.

**Agent wants to defer tests:**
> Tests are not deferrable for security-critical or data-integrity code. If the test infrastructure is missing, extend it in this PR, not "later". See PR #610 and #611 — test debt was created and had to be immediately closed in a follow-up, that's a pattern to avoid.

**CI fails repeatedly on same issue:**
> Stop iterating. Report the failure in chat with the exact error. Three failed iterations on the same issue means the approach is wrong, not that one more tweak will fix it.

---

## When a new TZ point emerges mid-roadmap

During roadmap execution, new bugs or requirements will come up. Protocol:

1. Do not interrupt the current PR.
2. Add the new item to TZ.md as a new point with `[NOT YET SCHEDULED]` marker.
3. Add to ROADMAP.md under "Technical debt" or a new "Emerging scope" section.
4. At the next PR boundary, discuss priority in chat before deciding if it slots into upcoming PRs or waits.

---

## Keeping ROADMAP accurate

After each merge the roadmap MUST reflect reality. Common drift patterns to avoid:

- Agent marks a PR "complete" in ROADMAP while leaving part of scope as "deferred". Either the scope is shipped or it's not. Deferred scope stays in "In progress" as a follow-up or moves to "Upcoming" as a new PR.
- Agent updates "Completed" but forgets to promote next item into "In progress". Always do both.
- Agent edits plan inline in chat instead of the file. File is source of truth.
17 changes: 12 additions & 5 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,23 @@
- [x] **PR #609** — tenant feature flags, `[RequireFeatureFlag]` middleware, `FeatureFlagGate`, Budget hidden by flag *(TZ 4, TZ 5)*
- [x] **PR #610** — super-admin foundation: `IsSuperAdmin`, JWT claims (`is_super_admin`, `mfa_verified`), TOTP MFA, `AdminController` with `IgnoreQueryFilters()`, `/admin/tenants` + `/admin/tenants/:id` pages, audit log table *(TZ 14 foundation)*
- [x] **PR #611** — super-admin integration tests (8 scenarios), `TestAuthHandler` extended with opt-in headers *(test debt from #610 closed)*
- [x] **PR #612** — full Season model (Seasons table with StartDate/EndDate/IsCurrent, tenant-admin `/api/seasons/*` CRUD, super-admin `/api/admin/tenants/{id}/seasons/*` CRUD, partial unique index for single current season, 3 default seasons seeded per existing tenant, Dashboard consumes real season list via arrow navigation) *(TZ 2 closed)*

---

## In progress

- [ ] **PR #612 — Full Season model** *(TZ 2 remainder)*
- Replace hardcoded year-list with `Seasons` table (Id, TenantId, Code, Name, StartDate, EndDate, IsCurrent)
- Data migration: seed 3 default seasons per existing tenant (2023/24, 2024/25, 2025/26)
- CRUD for super-admin (`/api/admin/tenants/{id}/seasons/*`) and tenant-admin (`/api/seasons/*`)
- Dashboard: season arrows iterate real Season list, label uses `StartDate/EndDate`
- Unique constraint: only one `IsCurrent=true` per tenant (partial index)
- Breaking change to `/api/seasons` response shape — update all frontend consumers in same PR

---

## Upcoming (in order — do not reorder without approval)

- [ ] **PR #613 — Currency system** *(TZ 8.2)*
- `UserPreferences.PreferredCurrency` (UAH/USD/EUR, default UAH)
- `ExchangeRate` table (Code, Date, RateToUah), PK (Code, Date)
Expand All @@ -31,10 +42,6 @@
- Fallback: last stored rate on NBU failure; weekend/holiday → previous business day
- Exports: currency header with NBU rate on export date

---

## Upcoming (in order — do not reorder without approval)

- [ ] **PR #614 — Super-admin advanced + impersonation** *(TZ 14 remainder)*
- Impersonation: 60min TTL, mandatory reason, red banner in UI, email to target user, rate limit 3/day per (admin, target) pair
- Forbidden actions in impersonation: password/email change, API keys write scope, billing ops, tenant export
Expand Down
4 changes: 2 additions & 2 deletions docs/TZ.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@

---

## ПУНКТ 2 — Перемикач періоду на дашборді показує конкретні дати `[CLOSED in PR #605, #607, #612]`
## ПУНКТ 2 — Перемикач періоду на дашборді показує конкретні дати `[PARTIALLY CLOSED in PR #605, #607; full Season model in PR #612]`

**Problem:** Dashboard buttons `День / Тиждень / Місяць / Сезон` didn't show which period was active.

**Shipped in #605:** `?period=` URL query param on `/dashboard`; monospace label under segmented control shows resolved date range.
**Shipped in #607:** `‹ ›` anchor-date stepping with keyboard shortcuts (`←`/`→`), boundary-aware disable via `/api/tenant/data-boundaries`.
**Shipped in #612:** Full `Seasons` table (Id, TenantId, Code, Name, StartDate, EndDate, IsCurrent). Dashboard season arrows iterate the real season list per tenant, label uses the stored Season name, and arrow boundaries are driven by `StartDate`/`EndDate`. Legacy `/api/seasons` year-list endpoint replaced with tenant-admin CRUD; super-admin CRUD under `/api/admin/tenants/{tenantId}/seasons`. Data migration seeded 3 default seasons (2023/2024, 2024/2025, 2025/2026) per existing tenant.
**Remaining for #612:** Full Season model with real StartDate/EndDate per tenant (currently uses hardcoded year-list for season boundaries — silent bug for non-standard crop cycles).

---

Expand Down
58 changes: 54 additions & 4 deletions docs/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,57 @@ Living document. Each PR collapses a slice of ТЗ into a shippable unit.
## Completed

- PR #609 (merged as d30972e): tenant optional feature flags + budget gating (ТЗ #13).
- PR #610 (merged as d208689): super-admin foundation — TOTP MFA, audit log, tenant admin UI (ТЗ #14, first half).
- PR #611 (merged as d358a24): integration tests for super-admin auth + MFA + audit.

## In flight — PR #610: super-admin foundation (ТЗ #14, first half)
## In flight — PR #612: Season model (ТЗ #2, completes dashboard seasonal scope)

Branch: `pr612-season-model`.

### Why now

Dashboard seasonal UI (PR #605/#607) currently uses a year-list derived from operation/cost/sale timestamps. This is semantically wrong for farms with non-calendar crop cycles (Ukraine convention: Aug 1 → Jul 31). Users see "Season 2025" rather than "Сезон 2025/2026: 1 серп. 2025 — 31 лип. 2026", and `‹ ›` navigation steps by integer year, not by real season. This PR promotes Season to a first-class, tenant-configurable entity.

### Backend

1. Domain entity `Season : AuditableEntity` with Code(16), Name(100), StartDate, EndDate, IsCurrent.
2. Migration `AddSeasons`:
- Table + unique `(TenantId, Code)` + CHECK `EndDate > StartDate` + partial unique for `IsCurrent = true` per tenant.
- Idempotent data seed: for each existing tenant, create `Сезон 2023/2024`, `Сезон 2024/2025`, `Сезон 2025/2026` (current) if no seasons exist.
3. `/api/seasons` response shape changes from `int[]` → `SeasonDto[]` (breaking; one frontend consumer).
4. Tenant-scoped CRUD at `/api/seasons` (GET list, GET current, POST, PUT, DELETE, POST set-current). Admin-only for mutations.
5. Super-admin CRUD at `/api/admin/tenants/{id}/seasons` — `[SuperAdminRequired]`, audited via `ISuperAdminAuditService`.
6. `SetCurrent` is transactional (flips IsCurrent for the tenant).
7. `Delete` safety net: rejects when cost / sale / operation rows fall inside the season's date window.

### Frontend

1. Types + `useTenantSeasons` hook return `SeasonDto[]`.
2. Dashboard arrow navigation iterates through `SeasonDto[]` by StartDate, labels use real dates (`"Сезон 2025/2026: 1 серп. 2025 — 31 лип. 2026"`).
3. New `/settings/seasons` for tenant admin (table + modals).
4. New `/admin/tenants/:id/seasons` mirror for super-admin.
5. i18n keys added to `uk.ts` + `en.ts`.

### Tests (mandatory)

1. Migration idempotency (run twice, no duplicates).
2. SetCurrent transactional (exactly one IsCurrent after flip).
3. Delete safety — blocked with linked data.
4. `GET /api/seasons` as tenant-admin returns only own tenant's rows.
5. Super-admin `POST /set-current` → 200 + exactly one new `SuperAdminAuditLog`.
6. Same endpoint without super-admin claim → 403.

### Out of scope

- Tying AgroOperation/CostRecord/Sale rows to a `SeasonId` FK (separate PR — backfill strategy).
- Per-crop cycle overrides.
- E2E dashboard scroll test (deferred — no Playwright pipeline in CI today).

### Deployment note

Data migration auto-seeds 3 default seasons (Aug–Jul boundaries) for existing tenants. Review and adjust per tenant before PR #613 (planned: currency + historical FX), which will rely on accurate season boundaries for annual reports.

## Archived — PR #610 scope (super-admin foundation, first half)

Branch: `pr610-super-admin-foundation`. Single PR as agreed (no split).

Expand Down Expand Up @@ -87,6 +136,7 @@ Coexistence with the existing role-based super-admin:

## Upcoming

- PR #611 — multi-tenant DNS / org-level routing.
- PR #612 — **Season model (ТЗ #2)**: replace the legacy year-list `/api/seasons` endpoint with a real tenant-scoped `Season` entity (Code, Name, StartDate, EndDate, IsCurrent) so the dashboard navigates through actual crop cycles (Aug 1 → Jul 31) instead of calendar years. Adds tenant-admin CRUD at `/api/seasons/*` and super-admin CRUD at `/api/admin/tenants/{id}/seasons/*` (audited). Migration seeds three default seasons (2023/2024, 2024/2025, 2025/2026-current) per existing tenant, idempotent. Breaking API change for the frontend: response shape `int[]` → `SeasonDto[]`. Billing/plan enforcement is deferred (feature flags already differentiate plans — see PR #609).
- PR #613 — super-admin phase 2 (users, audit UI, billing, impersonation).
- PR #613 — currency + historical FX (depends on Season boundaries from #612).
- PR #614 — super-admin phase 2 (users, audit UI, impersonation).
- PR #615 — tie `SeasonId` FK onto AgroOperation / CostRecord / Sale (depends on #612). Backfill by date-range.
- Post-roadmap (no firm need yet) — multi-tenant DNS / org-level routing; billing + plan enforcement. Feature flags already demonstrate plan differentiation; billing deferred until monetization is a concrete goal.
Loading