# Scratchpad — Premium ABAC Follow-up Remediation

- Plan slug: `premium-abac-remediation`
- Created: `2026-04-22`

## What This Is

Keep a lightweight, continuously-updated log of discoveries and decisions made while implementing this plan.

Prefer short bullets. Append new entries as you learn things, and also *update earlier notes* when a decision changes or an open question is resolved.

## Decisions

- (2026-04-22) This is a **follow-up remediation plan** for `ee/docs/plans/2026-04-21-premium-abac-authorization-kernel/`, not a replacement for it.
- (2026-04-22) Scope is limited to defects identified during review of the first implementation session.
- (2026-04-22) Remediation should be **surgical** where possible and should preserve the architecture and product decisions from the original plan.
- (2026-04-22) The most urgent fixes are draft/publish integrity, selected-rule fidelity, starter-bundle publish behavior, time resource-key alignment, simulator fidelity, and API pagination semantics.
- (2026-04-22) `F001` implemented by adding explicit remediation-to-original feature/test mapping to this plan PRD; this follow-up now has auditable traceability to original plan IDs.

## Discoveries / Constraints

- (2026-04-22) `getAuthorizationBundleDraftEditorAction` currently requires only read permission but calls `ensureDraftBundleRevision(...)`, which creates draft state as a side effect.
- (2026-04-22) `upsertBundleRule(...)` and `deleteBundleRule(...)` are currently not scoped tightly enough to the active draft revision, which weakens draft/publish isolation.
- (2026-04-22) Runtime relationship evaluation for `selected_clients` and `selected_boards` depends on evaluation input fields, but the stored rule config is not fully propagated into bundle-rule evaluation objects.
- (2026-04-22) Starter bundle seeding currently creates bundle rules without publishing the initial revision, leaving seeded bundles inert.
- (2026-04-22) Migrated Time UI/catalog uses `time_entry` while selected time/delegation kernel calls currently use `timesheet`.
- (2026-04-22) API list controllers for tickets, projects, and quotes paginate in the service layer first, then narrow in memory, then report filtered-page counts as totals.
- (2026-04-22) Simulator fidelity is weakened by billing-record mismatch and by simulator kernel construction that does not mirror all builtin resource-specific invariants used in real runtime paths.
- (2026-04-22) `server` package tests run directly via `cd server && npx vitest run ...`; root `npm run test:local -- ...` currently fails because its `dotenv` invocation expects a different CLI syntax.
- (2026-04-22) Control-plane schema allows `authorization_bundle_rules.bundle_id` to drift from the referenced revision’s `bundle_id` because rule rows currently only foreign-key revision by `(tenant, revision_id)`.
- (2026-04-22) Runtime rule mapping loaded `constraints`/`redactedFields` from rule config but did not load `selectedClientIds` or `selectedBoardIds`, so selected template rules could evaluate against missing IDs.
- (2026-04-22) `seedStarterAuthorizationBundlesAction` created starter bundle draft revisions/rules but never published them, so seeded bundles were assignable yet inert.
- (2026-04-22) Time delegation/approval kernelization used resource type `timesheet` for bundle evaluation while the bundle editor/catalog and starter rules target `time_entry`.
- (2026-04-22) Simulator billing record lookup/listing used `invoices`, but migrated billing API authorization evaluates `billing` over quote records (`quotes`).
- (2026-04-22) Simulator previously evaluated all actions via `authorizeResource`, so approve-path mutation guards (for billing/time self-approval) were not represented.
- (2026-04-22) Simulator previously allowed non-read action modes (`create/update/delete`) without mutation context, which could imply unsupported fidelity.
- (2026-04-22) API authorization subject shaping only mapped `client_id` and ignored camelCase client ID payloads and portfolio client ID arrays.
- (2026-04-22) Ticket/project/quote API list controllers paginated service results before authorization narrowing, which stranded authorized records and reported filtered-page counts as totals.

## Commands / Runbooks

- (2026-04-22) Inspect original plan scratchpad and feature/test references:
  `read ee/docs/plans/2026-04-21-premium-abac-authorization-kernel/SCRATCHPAD.md`
- (2026-04-22) Review committed implementation history on this branch:
  `git log --oneline --decorate --reverse --ancestry-path $(git merge-base HEAD origin/main)..HEAD`
- (2026-04-22) Review changed files in the premium-ABAC rollout:
  `git diff --stat $(git merge-base HEAD origin/main)..HEAD`
- (2026-04-22) Spot-check known hot files:
  - `server/src/lib/authorization/bundles/service.ts`
  - `ee/server/src/lib/actions/auth/authorizationBundleActions.ts`
  - `server/src/lib/authorization/kernel/providers/bundleProvider.ts`
  - `packages/scheduling/src/actions/timeEntryDelegationAuth.ts`
  - `server/src/lib/api/controllers/Api{Ticket,Project,Quote}Controller.ts`

## Links / References

- Original plan:
  - `ee/docs/plans/2026-04-21-premium-abac-authorization-kernel/PRD.md`
  - `ee/docs/plans/2026-04-21-premium-abac-authorization-kernel/features.json`
  - `ee/docs/plans/2026-04-21-premium-abac-authorization-kernel/tests.json`
  - `ee/docs/plans/2026-04-21-premium-abac-authorization-kernel/SCRATCHPAD.md`
- Key implementation files:
  - `server/src/lib/authorization/bundles/service.ts`
  - `server/src/lib/authorization/kernel/providers/bundleProvider.ts`
  - `server/src/lib/authorization/kernel/relationships.ts`
  - `ee/server/src/lib/actions/auth/authorizationBundleActions.ts`
  - `packages/scheduling/src/actions/timeEntryDelegationAuth.ts`
  - `server/src/lib/api/controllers/authorizationKernel.ts`
  - `server/src/lib/api/controllers/ApiTicketController.ts`
  - `server/src/lib/api/controllers/ApiProjectController.ts`
  - `server/src/lib/api/controllers/ApiQuoteController.ts`
  - `server/migrations/20260421190000_create_authorization_bundle_control_plane.cjs`

## Open Questions

- Should this follow-up fully solve post-authorization pagination totals, or only make pagination semantics honest and fail-safe for now?
- Should simulator fidelity in this follow-up be broadened by modeling builtin resource rules per resource family, or narrowed by explicitly refusing unsupported scenario types?

## Progress Log

- (2026-04-22) Completed `F001`:
  - Added a new `Traceability Mapping` section in `PRD.md` that maps each remediation feature cluster to original-plan feature/test IDs.
  - Purpose: satisfy follow-up-plan auditability and make remediation-to-original linkage explicit in the source-of-truth artifact.
- (2026-04-22) Completed `F002`:
  - Updated `getAuthorizationBundleDraftEditorAction` to check `system_settings:write` capability.
  - Write-capable users keep existing behavior (`ensureDraftBundleRevision`), while read-only users load an existing draft or published revision without creating draft state.
  - Added explicit summary text for read-only fallback when no active draft exists.
- (2026-04-22) Completed `F003`:
  - Hardened `upsertBundleRule` to require a draft revision scoped by `tenant + bundle + revision`.
  - Rule updates now also require `tenant + bundle + revision + rule` match and fail closed when no matching draft rule exists.
- (2026-04-22) Completed `F004`:
  - Hardened `deleteBundleRule` to require `tenant + bundle + revision + rule` match.
  - Delete now fails closed when the rule is not in the expected draft revision scope.
- (2026-04-22) Verification for `F002-F004`:
  - Command: `cd server && npx vitest run src/test/unit/authorization/bundleManagementPermissions.test.ts`
  - Result: pass.
- (2026-04-22) Completed `F005`:
  - Added migration `server/migrations/20260422113000_enforce_authorization_rule_revision_bundle_integrity.cjs`.
  - Enforced unique key on `authorization_bundle_revisions (tenant, bundle_id, revision_id)` and moved rules FK to `(tenant, bundle_id, revision_id)`.
  - Result: a rule can no longer reference a revision from a different bundle.
- (2026-04-22) Verification for `F005`:
  - Command: `cd server && npx vitest run src/test/unit/migrations/authorizationBundleControlPlaneMigration.test.ts`
  - Result: pass.
- (2026-04-22) Completed `F006`:
  - Added selected-client ID normalization/loading from rule config in runtime rule resolution and simulator rule normalization.
  - Supports both camelCase (`selectedClientIds`) and snake_case (`selected_client_ids`) config keys.
- (2026-04-22) Completed `F007`:
  - Added selected-board ID normalization/loading from rule config in runtime rule resolution and simulator rule normalization.
  - Supports both camelCase (`selectedBoardIds`) and snake_case (`selected_board_ids`) config keys.
- (2026-04-22) Completed `F008`:
  - Extended bundle-provider rule type with `selectedClientIds`/`selectedBoardIds`.
  - Updated template evaluation so `selected_clients` and `selected_boards` use rule-scoped configured IDs during decisioning instead of relying on ad hoc caller input.
- (2026-04-22) Verification for `F006-F008`:
  - Command: `cd server && npx vitest run src/test/unit/authorization/bundle.provider.test.ts`
  - Result: pass.
- (2026-04-22) Completed `F009`:
  - Starter seeding now publishes each created starter bundle revision immediately after inserting starter rules.
  - Kept publish inside the existing transaction flow by widening `publishBundleRevision` to accept `Knex | Knex.Transaction`.
- (2026-04-22) Verification for `F009`:
  - Command: `cd server && npx vitest run src/test/unit/authorization/starterBundles.test.ts`
  - Result: pass.
  - Command: `cd server && npx vitest run src/test/unit/authorization/bundleManagementPermissions.test.ts`
  - Result: test pass but process ended with coverage cleanup error (`ENOENT .../coverage/.tmp`).
- (2026-04-22) Completed `F010`:
  - Updated time delegation/approval kernel evaluations in `timeEntryDelegationAuth.ts` to use resource type `time_entry` for bundle decisions.
  - Updated scheduling authorization regression test bundle-rule fixtures to match `time_entry`.
- (2026-04-22) Verification for `F010`:
  - Command: `cd server && npx vitest run ../packages/scheduling/tests/timeEntryDelegationAuth.authorization.test.ts`
  - Result: pass.
- (2026-04-22) Completed `F011`:
  - Updated simulator billing record reference data and persisted-record loading from `invoices` to `quotes`.
  - Updated authorization-record normalization to include `quote_id`.
- (2026-04-22) Completed `F012`:
  - Added simulator parity for mutation-guard invariants on `approve` actions by using `authorizeMutation` with not-self-approver guards for `billing` and `time_entry`.
  - Added simulator-side document client-visibility narrowing for client principals to mirror runtime deny behavior.
  - Added managed-user lookup to simulation subject shaping for closer runtime parity.
- (2026-04-22) Completed `F013`:
  - Added explicit simulator support gating: only `read/approve` actions are allowed in this follow-up; unsupported modes fail closed with clear errors.
  - Added explicit refusal for ticket simulation with client principals until board-visibility builtin invariants are modeled.
- (2026-04-22) Verification for `F011-F013` and `F023`:
  - Command: `cd server && npx vitest run src/test/unit/authorization/bundleSimulatorAction.test.ts`
  - Result: pass.
- (2026-04-22) Completed `T009`, `T010` and `F023`:
  - Extended `bundleSimulatorAction.test.ts` to verify quote-backed billing simulation records and approve self-approval guard behavior.
  - Added regression coverage for supported-mode fidelity and explicit unsupported-mode rejection semantics.
- (2026-04-22) Completed `F014`:
  - Extended API authorization subject normalization to support `client_id`/`clientId` and `portfolio_client_ids`/`portfolioClientIds`.
  - Subject shaping now preserves client/portfolio scope IDs across API user payload variants.
- (2026-04-22) Completed `F015`, `F016`, `F017`, `F018`:
  - Added `authorizationAwarePagination` helper that scans source pages, applies authorization first, and paginates authorized rows afterward.
  - Updated `ApiTicketController`, `ApiProjectController`, and `ApiQuoteController` list paths to use authorization-aware pagination totals.
  - Remediation remains fail-closed: only authorized rows contribute to response data/totals.
- (2026-04-22) Completed `T011`:
  - Added API authorization subject unit coverage for snake_case and camelCase client/portfolio identifiers.
- (2026-04-22) Completed `T012`, `T013`, `T014` and `F024`:
  - Added `authorizationAwarePagination.test.ts` with ticket/project/quote scenarios proving no authorized rows are stranded behind filtered source pages.
  - Coverage asserts coherent authorized totals after narrowing.
- (2026-04-22) Verification for `F014-F018`, `T011-T014`:
  - Command: `cd server && npx vitest run src/test/unit/authorization/bundleSimulatorAction.test.ts src/test/unit/api/authorizationKernel.test.ts src/test/unit/api/authorizationAwarePagination.test.ts`
  - Result: pass.
- (2026-04-22) Completed `T001`, `T002`, `T003` and `F019`:
  - Verified bundle-management permission and draft-revision scoping regressions remain covered in `bundleManagementPermissions.test.ts`.
- (2026-04-22) Completed `T004`:
  - Verified migration-level revision/bundle integrity constraints in `authorizationBundleControlPlaneMigration.test.ts`.
- (2026-04-22) Completed `T005`, `T006` and `F020`:
  - Verified runtime selected-client and selected-board evaluation coverage in `bundle.provider.test.ts`.
- (2026-04-22) Completed `T007` and `F021`:
  - Verified starter-bundle publish-backed enforceability in `starterBundles.test.ts`.
- (2026-04-22) Completed `T008` and `F022`:
  - Verified time delegation authorization path alignment to `time_entry` in `timeEntryDelegationAuth.authorization.test.ts`.
- (2026-04-22) Verification for `T001-T008`, `F019-F022`:
  - Command: `cd server && npx vitest run src/test/unit/authorization/bundleManagementPermissions.test.ts src/test/unit/migrations/authorizationBundleControlPlaneMigration.test.ts src/test/unit/authorization/bundle.provider.test.ts src/test/unit/authorization/starterBundles.test.ts ../packages/scheduling/tests/timeEntryDelegationAuth.authorization.test.ts`
  - Result: pass.
