# SCRATCHPAD: Complete OpenAPI Documentation Coverage

## 2026-04-24 — Plan Created

### Branch / Worktree

- Worktree: `/Users/roberisaacs/alga-psa.worktrees/docs/api-docs-input-output`
- Branch: `docs/api-docs-input-output`
- Existing uncommitted local file to avoid: `.env.localtest`

### Current Progress Baseline

- Completed batches: 1–16 (80 operations).
- Latest pushed commit at plan creation: `e733631bf docs(openapi): document asset history and automation execution routes`.
- Remaining CE placeholder count: `488`.
- Progress tracker: `/tmp/alga-openapi-doc-progress.md`.

### Required Commands

Regenerate CE/legacy specs:

```bash
npm --prefix sdk run openapi:generate
```

Regenerate EE specs:

```bash
npm --prefix sdk run openapi:generate -- --edition ee
```

Placeholder scan pattern:

```bash
python3 - <<'PY'
import json
spec=json.load(open('sdk/docs/openapi/alga-openapi.ce.json'))
items=[]
for path, methods in spec['paths'].items():
  for method, op in methods.items():
    if isinstance(op,dict) and ('generated automatically' in op.get('description','') or (op.get('extensions') or {}).get('x-placeholder')):
      items.append((path,method.upper(),op.get('summary')))
print('remaining', len(items))
for row in items[:50]:
  print(row)
PY
```

### Batch Workflow Decisions

- Continue in batches of 5 operations.
- Use subagents for investigation where possible.
- Preferred model: `deepseek/deepseek-v4-pro`.
- Investigators are read-only and should report source evidence, auth/security, tenant behavior, inputs, outputs, statuses, and ID provenance.
- Main agent writes registrars and generated specs.
- Commit/push periodically.
- Do not stage `.env.localtest`.

### Registrar Rules

- Put domain registrars under `server/src/lib/api/openapi/routes/`.
- Import/register new registrars in `server/src/lib/api/openapi/index.ts` before `registerInventoryBackfillRoutes`.
- Manual route registration key is method+path. If manual route is registered before inventory backfill, placeholder is skipped.
- Prefer actual source behavior over intended behavior when they differ.
- When source has implementation bugs, document them in descriptions/extensions rather than fixing runtime code.

### Current Remaining Placeholder Domains (CE)

Total: `488` placeholders.

Top route families at plan creation:

- 37 `/api/v1/integrations`
- 37 `/api/v1/quickbooks`
- 37 `/api/v1/webhooks`
- 34 `/api/v1/workflows`
- 31 `/api/v1/invoices`
- 29 `/api/v1/financial`
- 26 `/api/v1/users`
- 24 `/api/v1/contract-lines`
- 22 `/api/v1/teams`
- 21 `/api/v1/quotes`
- 19 `/api/v1/tags`
- 18 `/api/v1/projects`
- 17 `/api/v1/time-entries`
- 17 `/api/v1/time-sheets`
- 14 `/api/v1/automation`
- 14 `/api/v1/categories`
- 12 `/api/v1/tickets`
- 10 `/api/v1/roles`
- 9 `/api/v1/clients`
- 8 `/api/v1/contacts`
- 8 `/api/v1/meta`
- 8 `/api/v1/time-periods`
- 7 `/api/v1/permissions`
- 7 `/api/v1/schedules`
- 4 `/api/v1/contracts`
- 4 `/api/v1/user`
- 3 `/api/v1/client-contract-lines`
- 2 `/api/v1/feature-flags`
- 2 `/api/v1/contract-line-templates`
- 2 `/api/v1/rbac`
- 1 `/api/v1/billing-analytics`
- 1 `/api/v1/feature-access`
- 1 `/api/v1/permission-checks`
- 1 `/api/v1/test-auth`
- 1 `/api/v1/user-roles`

### Next Batch at Plan Creation

Batch 17:

1. `GET /api/v1/automation/performance`
2. `GET /api/v1/automation/rules`
3. `POST /api/v1/automation/rules`
4. `POST /api/v1/automation/rules/bulk-execute`
5. `POST /api/v1/automation/rules/bulk-status`

### Known Gotchas Discovered So Far

- Some v1 asset routes have edge middleware that checks only `x-api-key` presence, but route files lack a wrapper that sets `req.context`; these routes can fail with `Request context not available`. Document actual behavior.
- Several controllers return v2 HATEOAS links from v1 routes. Document where observed.
- Some service methods throw generic `Error` for not-found/invalid state, producing 500 rather than expected 404/400. Document current behavior.
- Some inventory routes point to missing route files. Document as inventory-only/missing-handler rather than inventing behavior.
- `deepseek/deepseek-v4` failed earlier; `deepseek/deepseek-v4-pro` is the working provider-qualified model when provider balance is available.

### Commit Discipline

Before every commit:

```bash
git status --short
```

Expected persistent unstaged file:

```text
 M .env.localtest
```

Do not `git add .`; stage exact registrar/spec/plan files only.

## 2026-04-24 — Batch 17 Completed (Automation Rules/Performance + Remaining Automation Family)

### Scope completed

- Documented all remaining placeholder automation endpoints in `server/src/lib/api/openapi/routes/automation.ts`:
  - `GET /api/v1/automation/performance`
  - `GET /api/v1/automation/rules`
  - `POST /api/v1/automation/rules`
  - `POST /api/v1/automation/rules/bulk-execute`
  - `POST /api/v1/automation/rules/bulk-status`
  - `DELETE /api/v1/automation/rules/{id}`
  - `GET /api/v1/automation/rules/{id}`
  - `PUT /api/v1/automation/rules/{id}`
  - `POST /api/v1/automation/rules/{id}/execute`
  - `GET /api/v1/automation/statistics`
  - `GET /api/v1/automation/templates`
  - `POST /api/v1/automation/templates`
  - `GET /api/v1/automation/templates/{id}`
  - `POST /api/v1/automation/templates/{id}/use`

### Key source-grounded decisions (with rationale)

- Documented service/controller mismatch where `manualExecutionSchema` requires `automation_rule_id` in request body but execution uses path `/rules/{id}`. Kept this explicit in schema/description to avoid hiding runtime behavior.
- Documented known 500-vs-404/400 gaps where `AutomationService` throws generic `Error` for not-found or invalid-state conditions.
- Documented query-array parsing gap on performance route because controller query parsing (`validateQueryParams`) maps URL params to string values while schema expects arrays for `rule_ids` and `metrics`.
- Preserved tenant/RBAC metadata using base-controller auth flow (`x-api-key` + optional `x-tenant-id`, `hasPermission` against resource `automation`).

### Commands / runbook executed

```bash
npm --prefix sdk run openapi:generate
npm --prefix sdk run openapi:generate -- --edition ee
python3 <placeholder scan script for CE+EE targeted automation ops + remaining counts>
```

### Validation results

- CE targeted automation placeholder check: all 14 automation operations now non-placeholder.
- EE targeted automation placeholder check: all 14 automation operations now non-placeholder.
- Remaining placeholder counts after batch:
  - CE: `474` (down from 488)
  - EE: `482`

### Updated next cursor

- Next unresolved CE operations:
  1. `GET /api/v1/billing-analytics/overview`
  2. `GET /api/v1/contract-lines`
  3. `POST /api/v1/contract-lines`
  4. `DELETE /api/v1/contract-lines/bulk`
  5. `POST /api/v1/contract-lines/bulk`

### Gotchas reinforced

- OpenAPI registry request bodies must be registered as `request: { body: { schema: ... } }`; passing the schema directly (`body: Schema`) causes `zod-to-openapi` generation failure.

## 2026-04-24 — Contract Line/Template Family Completed (F002)

### Scope completed

- Added new registrar: `server/src/lib/api/openapi/routes/contractLines.ts`.
- Imported and registered it before inventory backfill in `server/src/lib/api/openapi/index.ts`.
- Documented all 26 previously-placeholder operations across:
  - `/api/v1/contract-lines`
  - `/api/v1/contract-lines/{id}` and subresources (`activation`, `analytics`, `copy`, `fixed-config`, `services`, `usage-metrics`)
  - `/api/v1/contract-lines/bulk` + explicit bulk subpaths
  - `/api/v1/contract-line-templates`
  - `/api/v1/contract-line-templates/{id}/create-contract-line`

### Key decisions / discovered gaps

- These controllers depend on `requireRequestContext(req)` but the global middleware only checks x-api-key presence for `/api/*` and does not populate request context. Documented as current auth wiring gap (`x-request-context-wiring-gap`).
- `POST /api/v1/contract-lines/{id}/copy` currently uses `source_contract_line_id` from body and does not consume the path id.
- `POST /api/v1/contract-line-templates/{id}/create-contract-line` currently validates/uses body `template_id` and does not consume the path id.
- `GET /api/v1/contract-lines/{id}/services/{serviceId}` currently delegates to `getContractLineServices()` and returns full service list for `{id}`; `serviceId` is effectively ignored in GET behavior.

### Validation results

- CE contract-line/template placeholder check: all 26 operations are non-placeholder.
- EE contract-line/template placeholder check: all 26 operations are non-placeholder.
- Remaining placeholders after this pass:
  - CE: `448`
  - EE: `456`

### Commands executed

```bash
npm --prefix sdk run openapi:generate
npm --prefix sdk run openapi:generate -- --edition ee
python3 <targeted CE/EE contract-line placeholder checks + global counts>
```

### Next cursor (CE)

1. `GET /api/v1/billing-analytics/overview`
2. `GET /api/v1/categories/analytics`
3. `POST /api/v1/categories/bulk/delete`
4. `GET /api/v1/categories/search`
5. `DELETE /api/v1/categories/service/{id}`

## 2026-04-24 — Category Family Completed (F003)

### Scope completed

- Extended `server/src/lib/api/openapi/routes/serviceCategories.ts` to document all previously-placeholder category operations:
  - `GET /api/v1/categories/analytics`
  - `POST /api/v1/categories/bulk/delete`
  - `GET /api/v1/categories/search`
  - `DELETE|GET|PUT /api/v1/categories/service/{id}`
  - `GET|POST /api/v1/categories/ticket`
  - `POST /api/v1/categories/ticket/move`
  - `GET /api/v1/categories/ticket/tree`
  - `GET /api/v1/categories/ticket/tree/{boardId}`
  - `DELETE|GET|PUT /api/v1/categories/ticket/{id}`

### Key source-grounded notes

- Permission resource is dynamic for search/analytics/bulk delete endpoints (`billing_settings` for service categories, `ticket_settings` otherwise).
- `GET /api/v1/categories/ticket/tree` currently derives board id from URL last segment; on this route it passes literal `tree` to service (implementation quirk documented).
- Tree-by-board route currently works because the final path segment is the board id, even though controller still parses from URL rather than params.

### Validation

- CE category placeholder set: all targeted operations are non-placeholder.
- EE category placeholder set: all targeted operations are non-placeholder.
- Remaining placeholder counts after this pass:
  - CE: `434`
  - EE: `442`

### Next cursor (CE)

1. `GET /api/v1/billing-analytics/overview`
2. `GET /api/v1/clients`
3. `POST /api/v1/clients`
4. `GET /api/v1/clients/stats`
5. `DELETE /api/v1/clients/{id}`

## 2026-04-24 — Client/Contact Family Completed (F004)

### Scope completed

- Added registrar: `server/src/lib/api/openapi/routes/clientsContacts.ts`.
- Registered in `server/src/lib/api/openapi/index.ts` before inventory backfill.
- Documented all 20 previously-placeholder operations under:
  - `/api/v1/clients*`
  - `/api/v1/contacts*`
  - `/api/v1/client-contract-lines*`

### Key source-grounded notes

- Client/contact custom routes (`stats`, `search`, `export`, nested client subresources) perform explicit API key validation in-controller and set request context directly.
- Client-contract-line routes reuse `ApiContractLineController` request-context-dependent methods; documented current request-context wiring gap and TODO-stub behavior for list endpoint.
- Contact export can return CSV file response when `format=csv`; documented this response-mode distinction.

### Validation

- CE placeholder check for the full client/contact/client-contract-line set: all operations are non-placeholder.
- EE placeholder check for the same set: all operations are non-placeholder.
- Remaining placeholder counts after this pass:
  - CE: `414`
  - EE: `422`

### Next cursor (CE)

1. `GET /api/v1/billing-analytics/overview`
2. `POST /api/v1/feature-access`
3. `GET /api/v1/feature-flags`
4. `POST /api/v1/feature-flags`
5. `POST /api/v1/financial/billing/calculate`

## 2026-04-24 — Financial/Invoice/Billing-Analytics Family Completed (F005)

### Scope completed

- Added registrar: `server/src/lib/api/openapi/routes/financialInvoices.ts`.
- Registered in `server/src/lib/api/openapi/index.ts` before inventory backfill.
- Documented all previously-placeholder operations under:
  - `/api/v1/billing-analytics/overview`
  - `/api/v1/financial/*`
  - `/api/v1/invoices/*`

### Key source-grounded decisions / gaps documented

- `GET /api/v1/billing-analytics/overview` maps to `ApiContractLineController.getBillingOverviewAnalytics()` and currently requires `req.context` without route-level auth wiring; documented `x-request-context-wiring-gap`.
- Multiple `/api/v1/financial/*` routes are currently wired to generic `ApiFinancialController.list/create/update/getById/delete` methods (transaction-oriented) rather than route-name-specific resources (`payment-methods`, `tax/rates`, `financial/invoices/{id}/*`); documented as route-to-controller mismatches.
- `POST /api/v1/financial/bulk/transactions` and `POST /api/v1/financial/bulk/credits` currently return `501 Not implemented`; documented as implementation gaps.
- Recurring invoice template routes include TODO stubs (`list` returns empty array, `update` synthetic response, `delete` direct 204); documented explicitly.
- `GET /api/v1/invoices/{id}/pdf` returns redirect behavior (`307`) when `download_url` exists; documented as redirect endpoint.

### Validation / tests completed in this pass

- `T001` completed: CE placeholder scan for the F005 route family (`ce_f005_remaining = 0`).
- `T002` completed: EE placeholder scan for the same family (`ee_f005_remaining = 0`).
- `T003` completed: `npm --prefix sdk run openapi:generate` succeeded.
- `T004` completed: `npm --prefix sdk run openapi:generate -- --edition ee` succeeded.
- `T005` completed: verified `registerFinancialInvoiceRoutes()` is imported and called in `openapi/index.ts` before `registerInventoryBackfillRoutes()`.

### Remaining placeholder counts after this pass

- CE: `353`
- EE: `361`

### Next cursor (CE)

1. `POST /api/v1/feature-access`
2. `GET /api/v1/feature-flags`
3. `POST /api/v1/feature-flags`
4. `GET /api/v1/integrations/quickbooks/accounts`
5. `GET /api/v1/integrations/quickbooks/accounts/mappings`

### Commands / runbook executed

```bash
npm --prefix sdk run openapi:generate
npm --prefix sdk run openapi:generate -- --edition ee
python3 <CE and EE placeholder scans for F005 family + global counts>
```

## 2026-04-24 — QuickBooks Duplicate Families Completed (F006)

### Scope completed

- Added registrar: `server/src/lib/api/openapi/routes/quickbooksV1.ts`.
- Registered in `server/src/lib/api/openapi/index.ts` before inventory backfill.
- Documented all previously-placeholder routes in both v1 QuickBooks families:
  - `/api/v1/integrations/quickbooks/*`
  - `/api/v1/quickbooks/*`

### Alias verification (T008)

- Verified both families map method-for-method to the same `ApiQuickBooksController` handler methods.
- Distinction documented in operation descriptions/extensions:
  - `/api/v1/integrations/quickbooks/*`: route files generally wrap controller calls in explicit `try/catch` with `handleApiError`.
  - `/api/v1/quickbooks/*`: route files generally bind controller handlers directly (`export const GET = controller.method()`).
- Since controller methods already return `handleApiError` on failure, current runtime behavior is functionally aliased despite route-wrapper style differences.

### Key source-grounded implementation notes captured

- Controller authenticates via `x-api-key` + optional `x-tenant-id`, then RBAC checks against resource `quickbooks` with actions `read|write|admin`.
- Multiple handler methods intentionally return temporary/stub payloads or TODO behavior (for example connection refresh, mapping deletes, sync cancel, account/tax mapping reads); these gaps are called out in descriptions.

### Validation / tests completed in this pass

- `T003` and `T004` rerun successfully:
  - `npm --prefix sdk run openapi:generate`
  - `npm --prefix sdk run openapi:generate -- --edition ee`
- `T008` completed:
  - CE QuickBooks placeholder check: `ce_quickbooks_remaining = 0`
  - EE QuickBooks placeholder check: `ee_quickbooks_remaining = 0`

### Remaining placeholder counts after this pass

- CE: `279`
- EE: `287`

### Next cursor (CE)

1. `POST /api/v1/feature-access`
2. `GET /api/v1/feature-flags`
3. `POST /api/v1/feature-flags`
4. `GET /api/v1/quotes`
5. `POST /api/v1/quotes`

## 2026-04-24 — Webhook Family Completed (F007)

### Scope completed

- Added registrar: `server/src/lib/api/openapi/routes/webhooks.ts`.
- Registered in `server/src/lib/api/openapi/index.ts` before inventory backfill.
- Documented all previously-placeholder webhook routes under `/api/v1/webhooks*` (37 operations).

### Key source-grounded wiring notes documented

- `/api/v1/webhooks/search` route currently calls `ApiWebhookController.list()` (not `search()`); documented as route-to-controller mismatch.
- `/api/v1/webhooks/templates/{id}` `GET|PUT|DELETE` routes currently call generic webhook `getById|update|delete` methods; documented as template-route wiring mismatch.
- Global subscription routes (`/api/v1/webhooks/subscriptions` `GET|POST`) call methods that require webhook `{id}` path extraction; current behavior yields UUID validation failure for literal `subscriptions` path segment.
- Several controller paths intentionally return stub/TODO responses (health checks, validation, secret rotation, template/subscription helpers, delivery detail helpers); these are explicitly called out in descriptions/extensions.

### Validation results

- CE webhook placeholder check: `ce_webhooks_remaining = 0`.
- EE webhook placeholder check: `ee_webhooks_remaining = 0`.
- Regeneration commands succeeded:
  - `npm --prefix sdk run openapi:generate`
  - `npm --prefix sdk run openapi:generate -- --edition ee`

### Remaining placeholder counts after this pass

- CE: `242`
- EE: `250`

### Next cursor (CE)

1. `POST /api/v1/feature-access`
2. `GET /api/v1/feature-flags`
3. `POST /api/v1/feature-flags`
4. `GET /api/v1/quotes`
5. `POST /api/v1/quotes`

## 2026-04-24 — Workflow Family Completed (F008)

### Scope completed

- Added registrar: `server/src/lib/api/openapi/routes/workflowsV1.ts`.
- Registered in `server/src/lib/api/openapi/index.ts` before inventory backfill.
- Documented all previously-placeholder `/api/v1/workflows*` operations (34 routes).

### Source-grounded findings and decisions

- There are no `server/src/app/api/v1/workflows/**/route.ts` handlers in this worktree.
- The inventory placeholders for `/api/v1/workflows*` were documented explicitly as inventory-only/missing-handler routes rather than inventing behavior.
- Descriptions now point to existing workflow APIs under `/api/workflow-definitions`, `/api/workflow-runs`, and `/api/workflow/events` to avoid ambiguity.

### Validation / tests completed in this pass

- CE workflow placeholder check: `ce_workflows_remaining = 0`.
- EE workflow placeholder check: `ee_workflows_remaining = 0`.
- Regeneration commands succeeded:
  - `npm --prefix sdk run openapi:generate`
  - `npm --prefix sdk run openapi:generate -- --edition ee`
- `T009` completed by documenting webhook family security/payload behavior and workflow family missing-handler reality with security/missing-route behavior.

### Remaining placeholder counts after this pass

- CE: `208`
- EE: `216`

### Next cursor (CE)

1. `POST /api/v1/feature-access`
2. `GET /api/v1/feature-flags`
3. `POST /api/v1/feature-flags`
4. `GET /api/v1/quotes`
5. `POST /api/v1/quotes`

## 2026-04-24 — Access Control/User/Role/Permission/Team Families Completed (F009)

### Scope completed

- Added registrar: `server/src/lib/api/openapi/routes/accessControlUsers.ts`.
- Registered it in `server/src/lib/api/openapi/index.ts` before inventory backfill.
- Documented all previously-placeholder operations across:
  - `/api/v1/users*`
  - `/api/v1/user-roles`
  - `/api/v1/roles*`
  - `/api/v1/permissions*`
  - `/api/v1/permission-checks`
  - `/api/v1/rbac/*`
  - `/api/v1/teams*`

### Key source-grounded notes / gaps documented

- `GET /api/v1/rbac/audit` is currently a hardcoded 501 stub route and does not call an RBAC controller.
- `POST /api/v1/users/bulk/create` currently delegates to `ApiUserController.create()` (single-user schema/behavior), not bulk-create logic.
- `PUT /api/v1/users/bulk/deactivate` currently delegates to `ApiUserController.update()` and fails UUID path extraction because the route segment `bulk` is treated as `{id}`.
- `POST /api/v1/users/{id}/teams` currently delegates to `ApiUserController.create()` (create-user behavior), not team membership creation.
- `DELETE /api/v1/users/{id}/teams/{teamId}` currently delegates to `ApiUserController.delete()`; `teamId` is ignored and operation acts on user id.
- `GET /api/v1/teams/hierarchy` currently parses team id from URL tail and uses literal `hierarchy` as id on this route.
- `DELETE /api/v1/teams/{id}/permissions/{permissionId}` revokes by permission id and does not use team id in service call.

### Validation

- CE F009 placeholder check: `ce_f009_remaining = 0`.
- EE F009 placeholder check: `ee_f009_remaining = 0`.
- Regeneration commands succeeded:
  - `npm --prefix sdk run openapi:generate`
  - `npm --prefix sdk run openapi:generate -- --edition ee`

### Remaining placeholder counts after this pass

- CE: `139`
- EE: `147`

### Next cursor (CE)

1. `POST /api/v1/feature-access`
2. `GET /api/v1/feature-flags`
3. `POST /api/v1/feature-flags`
4. `GET /api/v1/quotes`
5. `POST /api/v1/quotes`

## 2026-04-24 — Project/Ticket/Tag/Schedule/Time Families Completed (F010)

### Scope completed

- Added registrar: `server/src/lib/api/openapi/routes/workManagementV1.ts`.
- Registered it in `server/src/lib/api/openapi/index.ts` before inventory backfill.
- Documented all previously-placeholder operations across:
  - `/api/v1/projects*`
  - `/api/v1/tickets*`
  - `/api/v1/tags*`
  - `/api/v1/schedules*`
  - `/api/v1/time-entries*`
  - `/api/v1/time-sheets*`
  - `/api/v1/time-periods*`

### Key source-grounded notes / wiring gaps documented

- `GET /api/v1/schedules/search` and `GET /api/v1/schedules/{id}/conflicts` currently delegate to `ApiTimeSheetController.list()`.
- `GET /api/v1/schedules/{id}` currently delegates to `ApiTimeSheetController.getById()` (time-sheet read path) instead of schedule-specific getter.
- `GET /api/v1/time-periods/current` currently delegates to `ApiTimeSheetController.list()`.
- `POST /api/v1/time-periods/{id}/close` and `/reopen` currently delegate to `ApiTimeSheetController.update()` (time-sheet update path).
- Several time-sheet subroutes are currently wired to generic handlers rather than route-intent-specific methods:
  - `POST /api/v1/time-sheets/bulk` -> `list()`
  - `GET /api/v1/time-sheets/search` -> `list()`
  - `POST /api/v1/time-sheets/{id}/add-entry` -> `create()`
  - `GET /api/v1/time-sheets/{id}/entries` -> `list()`
  - `POST /api/v1/time-sheets/{id}/reject` -> `update()`
  - `DELETE /api/v1/time-sheets/{id}/remove-entry` -> `delete()`
  - `GET /api/v1/time-sheets/{id}/summary` -> `list()`

### Validation

- CE F010 placeholder check: `ce_f010_remaining = 0`.
- EE F010 placeholder check: `ee_f010_remaining = 0`.
- Regeneration commands succeeded:
  - `npm --prefix sdk run openapi:generate`
  - `npm --prefix sdk run openapi:generate -- --edition ee`

### Remaining placeholder counts after this pass

- CE: `41`
- EE: `49`

### Next cursor (CE)

1. `POST /api/v1/feature-access`
2. `GET /api/v1/feature-flags`
3. `POST /api/v1/feature-flags`
4. `GET /api/v1/quotes`
5. `POST /api/v1/quotes`

## 2026-04-24 — Quotes/Contracts + Meta/Utility Families Completed (F011, F012)

### Scope completed

- Added registrar: `server/src/lib/api/openapi/routes/quotesContractsV1.ts`.
- Added registrar: `server/src/lib/api/openapi/routes/metaUtilityV1.ts`.
- Added registrar: `server/src/lib/api/openapi/routes/eeInventoryOnly.ts` (EE-only inventory placeholders).
- Registered all three in `server/src/lib/api/openapi/index.ts` before inventory backfill.
- Documented all previously-placeholder operations under:
  - `/api/v1/quotes*`
  - `/api/v1/contracts*`
  - `/api/v1/feature-access`
  - `/api/v1/feature-flags`
  - `/api/v1/meta/*`
  - `/api/v1/test-auth`
  - `/api/v1/user/telemetry-*`
  - EE-only inventory placeholders:
    - `/api/ext-bundles/*`
    - `/api/extensions/install-info`
    - `/api/extensions/registry-db-check`
    - `/api/extensions/reprovision`
    - `/api/provisioning/tenants`
    - `/api/v1/auth/verify`

### Key source-grounded notes / gaps documented

- Quote read authorization uses billing-resource authorization context in `ApiQuoteController.assertQuoteReadAllowed()` while controller CRUD permissions are checked on `quote` resource; documented this distinction.
- Contract v1 routes currently mount `ApiContractLineController` (v2 controller) and call `requireRequestContext(req)`; documented request-context wiring dependency/gap on those paths.
- Feature flag and telemetry routes are session-oriented (`getSession`/`getCurrentUser`) while global `/api/*` middleware can still require API key presence unless path is explicitly skipped; documented as mixed auth behavior.
- EE-only placeholder paths that have no handler files in this worktree were converted to explicit inventory-only route docs instead of leaving generated placeholders.

## 2026-04-24 — Final Validation and Closeout (F013–F024, T006–T012)

### Final placeholder results

- CE full scan: `0` placeholders.
- EE full scan: `0` placeholders.

### Representative auth/RBAC verification (T006)

Reviewed representative read/mutation flows across major API-key v1 domains and confirmed docs align with source behavior:

- User domain:
  - Read: `ApiUserController.stats()` / `getPermissions()` enforce `hasPermission(..., 'user', 'read', ...)`.
  - Mutation: `assignRoles()` / `changePassword()` enforce update checks and contextual constraints.
- Team domain:
  - Read: `ApiTeamController.getMembers()` enforces `team:read`.
  - Mutation: `addMember()` enforces `team:update`.
- Project domain:
  - Read: `ApiProjectController.list()/search()/stats()` enforce read checks and authorization filtering.
  - Mutation: `bulkUpdate()/createPhaseTask()` enforce update checks.
- Ticket domain:
  - Read: `ApiTicketController.list()/stats()` enforce read checks.
  - Mutation: `updateStatus()/updateAssignment()` enforce update checks.

### Representative billing/financial/invoice verification (T007)

Re-validated representative endpoints in `server/src/lib/api/openapi/routes/financialInvoices.ts` against source-backed request/response behavior:

- `POST /api/v1/financial/billing/calculate`: request body schema registered and required under `request.body.schema`; response envelope documented as API success/error.
- Invoice lifecycle request schemas (`InvoiceSendBody`, `InvoiceFinalizeBody`, `InvoicePaymentBody`, bulk schemas) are registered and attached to route request bodies.
- Financial/invoice responses retain documented envelope patterns (`ApiSuccess` / `ApiPaginated` / error structure) and known implementation-gap notes from earlier F005 work.

### Commit/staging discipline verification (T010)

- Before each commit in this pass, `git status --short` confirmed `.env.localtest` remained unstaged while intended plan/registrar/spec files were staged explicitly.

### Plan consistency review (T012)

Reviewed final alignment among:

- `PRD.md`
- `features.json`
- `tests.json`
- `SCRATCHPAD.md`
- `/tmp/alga-openapi-doc-progress.md`

Result: all tracked workstreams and acceptance checkpoints are synchronized with final generated OpenAPI state (CE/EE placeholder scans at zero).

### Final summary of discovered implementation gaps (F024)

Representative deferred runtime issues captured in OpenAPI docs instead of code fixes:

- Route-to-controller mismatches on several v1 legacy paths (notably some user/team/time-period/time-sheet/schedule routes).
- Session-vs-API-key mixed auth behavior on feature flag and telemetry endpoints.
- Contract v1 routes using v2 controller assumptions (`requireRequestContext`).
- Inventory-only EE paths with missing handlers documented as such.

No runtime behavior changes were made as part of this documentation pass.
