Skip to content

Commit 19a51eb

Browse files
committed
docs(status): document 2026-04-16 PROD-00 round-2 hardening wave
Adds a delivery section for PRs #884-#891 covering SEC-28 SECURITY.md, DOC-06 CONTRIBUTING.md, DOC-07 CONFIGURATION_REFERENCE.md, PERF-09 response compression (with BREACH-aware level downgrade), PERF-10 composite DB indexes, OPS-29 container hardening (non-root end-to-end, IPv6/IPv4 healthcheck fix, setpriv entrypoint), FE-14 error boundary (null-throw sentinel fix), and FE-15 HTTP retry with backoff (skipRetry opt-out). Updates backend/frontend snapshots to reflect the new hardening surfaces.
1 parent 03c92fb commit 19a51eb

1 file changed

Lines changed: 16 additions & 3 deletions

File tree

docs/STATUS.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Taskdeck Status (Source of Truth)
22

3-
Last Updated: 2026-04-15
3+
Last Updated: 2026-04-16
44
<br>
55
Status Owner: Repository maintainers
66
Authoritative Scope: Current implementation, verified test execution, and active phase progress
@@ -135,6 +135,16 @@ Current constraints are mostly hardening and consistency:
135135
- **INT-06 integrations registry foundation** (`#340`/`#841`): full-stack integrations registry with domain entities (`IntegrationConnector`, `ConnectorEvent` with `ConnectorType`/`ConnectorDirection`/`ConnectorStatus` enums), application service (`IntegrationRegistryService` with `IIntegrationConnectorRepository`/`IConnectorEventRepository`), EF Core infrastructure (repositories + `AddIntegrationConnectorsAndEvents` migration), API (`IntegrationsController` with 7 endpoints — CRUD + enable/disable, all `[Authorize]`), frontend (`IntegrationsView.vue` at `/workspace/integrations` with `integrationStore` and `integrationsApi` with enum normalization), connector taxonomy (inbound/context/outbound per GP-06 — inbound connectors route through capture), architecture documentation at `docs/architecture/INTEGRATIONS_REGISTRY.md`; 60 new tests (24 domain + 12 application + 15 API + 9 frontend)
136136
- Tracker closures: 6 completed trackers closed — `#721` (TST-54 rigorous test expansion wave), `#647` (LLM-05 tool-calling), `#648` (MCP-00 server), `#329` (MVP-03 secondary follow-through), `#242` (UI-00 premium UI wave), `#235` (SEC-15 managed-key threat model)
137137

138+
- PROD-00 production-readiness round-2 hardening wave (2026-04-16, PRs `#884``#891`, 8 issues from PROD-00 tracker `#881`, each PR received two rounds of adversarial review with bot-findings triage):
139+
- **SEC-28 SECURITY.md vulnerability disclosure policy** (`#853`/`#884`): repo-root `SECURITY.md` with responsible-disclosure contact (GitHub private vulnerability reporting + fallback email), 48h acknowledgement target, supported-versions scope (`main` guaranteed; latest pre-1.0 tag best-effort), in-scope/out-of-scope breakdown, and safe-harbor language; README cross-link; round-2 enabled the private vulnerability reporting feature and reconciled supported-versions prose with the table
140+
- **DOC-06 CONTRIBUTING.md onramp** (`#873`/`#885`): repo-root `CONTRIBUTING.md` covering prerequisites, cross-platform local setup (Windows/macOS/Linux), Windows-specific notes (`check-git-env.sh`, Cygwin-git pitfall, PowerShell chaining, stale `index.lock`), default URLs, test commands (backend xUnit, frontend typecheck/build/vitest/lint, Playwright E2E), commit conventions, and PR process; README cross-link; AGENTS.md remains the authoritative contributor protocol
141+
- **DOC-07 CONFIGURATION_REFERENCE appsettings schema** (`#874`/`#887`): `docs/platform/CONFIGURATION_REFERENCE.md` documents every configuration section in `backend/src/Taskdeck.Api/appsettings.json` and every typed `*Settings.cs` binding (JWT, GitHub OAuth, OIDC, MFA, LLM provider/tool-calling/quota/kill-switch/abuse-detection, Workers, Outbound webhook security, CORS, Forwarded headers, Rate limiting, Cache, SignalR backplane, Security headers, Observability/Sentry/Telemetry/Analytics, Connection strings, Export/import, First run, Development sandbox, Logging, MCP server) with key/type/default/description/required flags and environment-variable override conventions; cross-linked from `CLAUDE.md` Key Docs and `docs/platform/README.md`; `deploy/.env.example` aligned
142+
- **PERF-09 response compression** (`#845`/`#886`): `AddTaskdeckResponseCompression()` registers Brotli + Gzip providers with `EnableForHttps = true`; `UseResponseCompression()` wired in `PipelineConfiguration` after forwarded headers and before CORS/static/routing so controllers, SPA assets, and `index.html` fallback all emit compressed bodies when the client opts in via `Accept-Encoding`; compressible MIME set extended to include `application/problem+json`; 3 integration tests in `ResponseCompressionApiTests` (gzip encoding, brotli encoding, no-compression baseline); **round-2 correction**: BREACH threat-model analysis caught that JWTs are in fact returned in `/api/auth/login` and `/api/auth/register` response bodies (contradicting the inline comment assuming JWTs only appeared in `Authorization` headers); compression level downgraded from `CompressionLevel.Optimal` to `CompressionLevel.Fastest` to reduce BREACH/CRIME oracle surface while preserving bandwidth wins
143+
- **PERF-10 composite DB indexes** (`#846`/`#888`): EF Core migration `20260416161303_AddPerfIndexes` adds three composite indexes — `IX_Cards_BoardId_ColumnId` replaces the single-column `IX_Cards_BoardId` (SQLite satisfies leftmost-prefix via the composite; `FK_Cards_Boards_BoardId` remains enforced by the FOREIGN KEY schema; `IX_Cards_ColumnId` retained), `IX_AuditLogs_UserId_Timestamp` accelerates `AuditLogRepository.GetByUserAsync`, and `IX_AuditLogs_EntityId_Timestamp` accelerates `AuditLogRepository.GetByBoardAsync`; **explicit deviations from issue AC documented**: `AuditLog.EntityId` is used instead of the aspirational `BoardId` (AuditLog uses polymorphic `EntityId` targeting, not a dedicated `BoardId`), and `IX_LlmRequests_UserId_Status` was confirmed pre-existing so no new migration was required
144+
- **OPS-29 Docker container hardening** (`#866`/`#889`): backend and frontend containers now run non-root end-to-end; backend Dockerfile uses `setpriv` entrypoint to chown `/app/data` and drop to `appuser` (UID 10001) for upgrade-safe volume ownership on existing root-owned `taskdeck-db` volumes; backend HEALTHCHECK against `/health/ready`; frontend runs on `nginxinc/nginx-unprivileged` with HEALTHCHECK against `/healthz` (dedicated nginx location); docker-compose gains `mem_limit`/`cpus` (classic-compose compatible), `logging` driver with `max-size: 10m`/`max-file: 3`, `init: true`, `no-new-privileges`, and unprivileged proxy image; **round-2 shipbreaker caught**: initial frontend HEALTHCHECK used `localhost`, which BusyBox's resolver preferred as `::1` (IPv6) while nginx bound IPv4 only, so the healthcheck never transitioned to healthy; fixed by pinning the probe to `127.0.0.1`
145+
- **FE-15 HTTP retry with exponential backoff** (`#854`/`#890`): Axios retry interceptor in `httpRetry.ts` with max 3 attempts, exponential backoff (1s/2s/4s ± 25% jitter), idempotent methods only (GET, HEAD, OPTIONS, PUT, DELETE — POST/PATCH never retried), 5xx + network + 408/429/503 retryable (501/505 excluded), 429 honours `Retry-After` (seconds or HTTP-date) capped at a sane upper bound, no retry on 401/403/404/409/422, respects `axios.isCancel` for navigation aborts, backoff races against `AbortSignal`, logging gated behind `import.meta.env.DEV`; **round-2 fixes**: `parseRetryAfter('-5')` was returning 0 because the HTTP-date branch required only letter presence as a guard — fixed with a proper letter-guard to return `null`; `skipRetry` opt-out introduced on `RetryableRequestConfig` so pre-existing PR `#725` baseline tests (which assert retry-less 5xx semantics) and two Playwright specs with first-request-fails mocks can opt out without being silently retried; case-insensitive `Retry-After` header parsing via `AxiosHeaders`
146+
- **FE-14 Vue error boundary for crash prevention** (`#852`/`#891`): `ErrorBoundary.vue` uses `onErrorCaptured` to catch descendant render/lifecycle errors and render a recoverable fallback (Reload + Go-to-Home actions); `App.vue` wraps `<RouterView />` in an outer `ErrorBoundary` backstop, and `AppShell.vue` wraps its inner `<router-view />` in a nested `ErrorBoundary` for per-view crash containment so a crashed view does not take down the shell; `main.ts` installs `app.config.errorHandler` plus `window` listeners for `error` and `unhandledrejection`; optional Sentry passthrough via `window.Sentry?.captureException` (no hard dependency); dev-only stack trace display, prod-safe messaging; **round-2 fix**: the `crashedError === null` sentinel would collide if a descendant threw a literal `null` — replaced with an explicit `hasCrashed: boolean` flag; null-throw and Sentry-info-context coverage added to tests
147+
138148
Target experience metrics for the capture direction:
139149
- capture action to saved artifact should feel under 10 seconds in normal use
140150
- capture artifact to reviewed/applicable proposal should be achievable inside a ~60-second loop
@@ -149,7 +159,7 @@ Direction guardrails (explicit):
149159
### Backend
150160

151161
- Architecture: Clean Architecture (`Domain`, `Application`, `Infrastructure`, `Api`)
152-
- Persistence: EF Core 8.0.14 + SQLite (aligned to net8.0 TFM as of `#760`/`#767`)
162+
- Persistence: EF Core 8.0.14 + SQLite (aligned to net8.0 TFM as of `#760`/`#767`); composite indexes delivered via migration `20260416161303_AddPerfIndexes` (`#846`/`#888`) — `IX_Cards_BoardId_ColumnId` replaces the single-column `IX_Cards_BoardId`, `IX_AuditLogs_UserId_Timestamp` accelerates `GetByUserAsync`, and `IX_AuditLogs_EntityId_Timestamp` accelerates `GetByBoardAsync` (AuditLog uses polymorphic `EntityId`, not a dedicated `BoardId`)
153163
- Core controllers: boards, columns, cards, labels
154164
- Extended controllers: auth, users, board-access, audit, export/import, external-imports, llm-queue, automation proposals, archive, chat, notifications, ops-cli, logs, health, starter-packs, search, metrics, data-portability, note-import, telemetry, api-keys, forecast, mfa, oidc, integrations
155165
- Worker runtime:
@@ -163,6 +173,7 @@ Direction guardrails (explicit):
163173
- `AuthenticatedControllerBase` for claim extraction and authenticated-user guardrails
164174
- request correlation middleware (`X-Request-Id`) with response echo and log scope propagation
165175
- development CORS origin policy keeps localhost defaults (`http://localhost:5173`, `http://localhost:5174`), adds fallback localhost dev ports (`http://localhost:4173`, `http://localhost:5001`), and supports additive `Cors:DevelopmentAllowedOrigins` config overrides
176+
- response compression (`#845`/`#886`): `AddTaskdeckResponseCompression()` registers Brotli + Gzip providers at `CompressionLevel.Fastest` (BREACH-aware downgrade from `Optimal`) with `EnableForHttps = true`; `UseResponseCompression()` wired in `PipelineConfiguration` after forwarded headers and before CORS/static/routing; compressible MIME set includes `application/problem+json` alongside JSON defaults
166177
- Implemented automation stack:
167178
- `AutomationProposalService`, `AutomationPlannerService`, `AutomationPolicyEngine`, `AutomationExecutorService` (decomposed into `OperationParameterParser`, `ExecutionAuditRecorder`, `OperationHandlerRegistry`)
168179
- `ArchiveRecoveryService` (decomposed into `ArchiveConflictDetector`, `RestorePlanner`, `RestoreExecutor`)
@@ -232,6 +243,8 @@ Direction guardrails (explicit):
232243
- Cross-cutting UI infrastructure:
233244
- command palette with global search (Ctrl+K): live cross-board search for boards and cards via `/api/search`, with 200ms debounced queries, abort-on-supersede, and keyboard-first grouped results navigation
234245
- feature flags, correlation IDs, toasts, keyboard shortcuts
246+
- HTTP retry interceptor (`#854`/`#890`): `httpRetry.ts` Axios interceptor retries 5xx/network/408/429/503 on idempotent methods only (GET/HEAD/OPTIONS/PUT/DELETE — POST/PATCH never retried), max 3 attempts with exponential backoff (1s/2s/4s ± 25% jitter), honours `Retry-After` (seconds + HTTP-date, case-insensitive) with a sane upper bound, races backoff against `AbortSignal`, respects `axios.isCancel`; `skipRetry` opt-out on `RetryableRequestConfig` for test baselines and error-state E2E mocks
247+
- Vue error boundary for crash prevention (`#852`/`#891`): `ErrorBoundary.vue` uses `onErrorCaptured` with an explicit `hasCrashed` boolean sentinel (null-throw safe) to render a recoverable fallback (Reload + Go-to-Home); `App.vue` wraps `<RouterView />` as outer backstop while `AppShell.vue` wraps its inner `<router-view />` as per-view crash containment; `main.ts` installs `app.config.errorHandler` plus `window` listeners for `error` and `unhandledrejection`; optional Sentry passthrough via `window.Sentry?.captureException`
235248
- shared UI primitives foundation (UI-02): 15 TdButton/TdInput/TdDialog/TdDropdown/TdTooltip/TdBadge/etc. primitives built on Reka UI via shadcn-vue ownership model with WAI-ARIA keyboard foundation; stack decision documented in `docs/analysis/ui-primitive-stack-decision-spike.md`
236249
- appshell premium reskin: shell sidebar, topbar, command palette, and keyboard help components now use `--td-*` design token system with focus-visible accessibility rings and glass morphism effects
237250
- board/card surface polish: board canvas, toolbar, action rail, column lanes, and card components now use design-token-based styling with standardized interactive states and accessibility focus rings
@@ -1027,7 +1040,7 @@ Automation and data:
10271040

10281041
Observability and scalability:
10291042
- frontend/CI baseline is now Node 24.13.1 (LTS) to align with Vite 7 engine requirements and longer support runway
1030-
- containerized deployment baseline is now shipped (`#69`): backend/frontend Dockerfiles, compose profile, reverse proxy compression/security headers posture, and CI image artifacts
1043+
- containerized deployment baseline is now shipped (`#69`): backend/frontend Dockerfiles, compose profile, reverse proxy compression/security headers posture, and CI image artifacts; **container hardening delivered** (`#866`/`#889`): backend and frontend containers now run non-root end-to-end — backend uses a `setpriv` entrypoint that chowns `/app/data` and drops to `appuser` (UID 10001) for upgrade-safe volume ownership on existing root-owned `taskdeck-db` volumes with HEALTHCHECK on `/health/ready`; frontend runs on `nginxinc/nginx-unprivileged` with HEALTHCHECK on `/healthz` (dedicated nginx location; probe pinned to `127.0.0.1` because BusyBox's `localhost` resolver preferred IPv6 `::1` while nginx bound IPv4 only); docker-compose adds `mem_limit`/`cpus`, `logging` rotation (`max-size: 10m`/`max-file: 3`), `init: true`, `no-new-privileges`, and unprivileged proxy image
10311044
- Terraform IaC baseline is now shipped (`#102`): reusable AWS single-node environment templates (`dev`/`staging`/`prod`), host bootstrap for the existing Docker workload layer, JWT secret retrieval from a pre-created SecureString SSM parameter instead of raw EC2 user-data injection, a dedicated persistent EBS data volume for `/var/lib/taskdeck`, instance replacement on bootstrap changes without discarding the SQLite path, stop-before-detach protection for planned data-volume attachment changes, protected data-volume destroy defaults for `staging`/`prod`, backup-bucket noncurrent-version expiry with explicit versioning dependency, and an operator drift-check workflow
10321045
- multi-tenancy strategy ADR is now documented (`#71`) with shared-schema + `TenantId` as the default rollout target; tenant isolation implementation slices remain pending
10331046
- local developer MCP posture now includes a Docker Marketplace server bundle with a stable default gateway set (`docker,docker-docs,openapi,time,jetbrains,filesystem,SQLite,terraform`) and optional integrations staged behind credentials/config (`postman`, `dockerhub`, `kubernetes`, `semgrep`)

0 commit comments

Comments
 (0)