# Praxis Table Performance Hardening V2 Implementation Plan

## Objective

Execute the first two cuts of the `praxis-table` performance hardening plan without changing workspace configuration and without introducing public compatibility knobs too early.

The immediate goal is to make the runtime safe for enterprise grids by:

1. making the render path side-effect free;
2. creating instrumentation that proves the current and improved behavior;
3. preparing the table for on-demand contextual discovery without visual instability;
4. keeping the correction in the canonical table runtime, not in host apps.

This plan covers only:

- `Cut 0`: observability and performance budgets
- `Cut 1`: removing discovery side effects from the render path

It intentionally does **not** yet implement:

- a new public performance API;
- scheduler/concurrency orchestration;
- backend contract changes;
- broad UX redesign of row actions.

For clarity, this is an implementation-ready internal plan for the canonical table runtime.
It is not yet a public consumer contract change.

---

## Executive Decision

The canonical default direction for enterprise is:

- collection-level capabilities remain eager;
- item-level capabilities/actions move toward on-demand;
- row/render code must not trigger network discovery;
- layout stability and accessibility are first-class acceptance criteria, not follow-up polish.

---

## Current Runtime Problems

The current table still mixes rendering and discovery:

- `fetchData()` triggers `prefetchRowDiscovery(page.content)` in [praxis-table.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.ts)
- `prefetchRowDiscovery()` loops through visible rows and calls `ensureRowDiscovery(row)`
- `ensureRowDiscovery()` triggers item-level `capabilities` and `actions`
- `getResolvedRowActions(row)` also calls `ensureRowDiscovery(row)` in the render path

That creates three categories of cost:

1. network fan-out for each visible row;
2. change-detection churn because each completion calls `markForCheck()`;
3. unstable runtime semantics because template reads can mutate runtime state.

In enterprise scenarios this gets worse under:

- pagination, filtering and sorting;
- multiple grids on the same screen;
- remote multi-tenant hosts;
- slower networks and session-scoped permissions.

---

## Guiding Principles

- Render-time getters must be pure.
- Discovery must be event-driven or policy-driven, never template-driven.
- Instrumentation comes before optimization guesses.
- Layout must stay stable while contextual affordances are unresolved.
- Accessibility states must distinguish `loading`, `blocked`, `error`, and `ready`.
- Session cache and visual runtime state must be modeled separately.
- No premature public flags. Stabilize canonical internal behavior first.

---

## Scope and Impact Map

### Canonical subproject affected

- [praxis-table](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table)

### Consumers impacted

- [praxis-crud](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-crud)
- the remote host lab in [quickstart-remote-metadata-lab.page.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/src/app/features/quickstart-remote-metadata-lab/quickstart-remote-metadata-lab.page.ts)

### Public docs potentially affected

- [README.md](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/README.md)
- [praxis-table.json-api.md](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.json-api.md)

For Cuts 0 and 1, documentation updates are expected to stay in internal planning/test notes unless implementation changes observable runtime behavior.

### Tests and validations minimally required

- focused specs in `praxis-table`
- focused regression in `praxis-crud` only if row action integration changes
- `npm run build:praxis-table`
- remote/browser proof only after the code cuts, not during planning

### Breaking-change risk

- low if changes remain internal and preserve current row actions behavior
- medium if a hidden reliance on render-triggered discovery exists in current templates/integration tests

---

## Cut 0: Observability and Budgets

## Goal

Add instrumentation that makes current row discovery cost measurable before behavior is changed.

## Required outcomes

1. We can count item-level discovery requests per table session.
2. We can distinguish:
   - discovery requested
   - discovery served from cache
   - discovery completed
   - discovery failed
3. We can inspect row contextual runtime state without relying on network logs.
4. We can define initial budgets and assert against them in tests.

## Canonical budgets for Cut 0

These are initial engineering budgets, not final SLAs:

- no more than `1` discovery trigger per row/rel while an identical request is in flight
- no more than `1` item-level discovery request per rel/href inside the freshness window
- no implicit discovery from render-time getters
- remote load of a page with 10 rows must not enqueue more discovery work than the current explicit policy allows

These cuts must also define initial perception budgets for enterprise validation:

- contextual row affordance must expose a visible `loading-context` state immediately when user-triggered discovery starts
- row action area must remain layout-stable while contextual discovery is pending
- row expansion/detail entry must distinguish `loading-context`, `blocked-semantic`, and `error-transient`
- no focusable control may disappear from under keyboard focus during contextual resolution

Budgets to measure and expose internally:

- `rowDiscoveryRequestedCount`
- `rowDiscoveryNetworkCount`
- `rowDiscoveryCacheHitCount`
- `rowDiscoveryFailureCount`
- `rowDiscoveryInFlightCount`
- `rowDiscoveryLastResolvedAt`
- `rowDiscoveryPendingRowCount`
- `rowDiscoveryUserTriggeredCount`
- `rowDiscoveryPrefetchCount`

Initial measurable thresholds to freeze for Cuts 0 and 1:

- repeated evaluation of row-action getters for the same row must add `0` new network requests
- a single explicit row interaction must start at most `1` capability request and `1` action-catalog request for that row/interaction cycle

## Planned write set

Primary files:

- [praxis-table.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.ts)
- [praxis-table.html](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.html) if instrumentation is surfaced through loading-state affordances used in focused tests
- [praxis-table.remote-regression.spec.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.remote-regression.spec.ts)
- potentially one new focused spec file near the table runtime if it improves isolation

Optional if needed for test support:

- [README.md](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/README.md)

## Implementation tasks

### 0.1 Introduce internal row discovery metrics state

Add an internal structure in `PraxisTable` dedicated to diagnostics/observability for row discovery.

It should capture at least:

- total requested by rel
- total served from cache by rel
- total completed by rel
- total failed by rel
- current in-flight href count

This must remain internal runtime state for now.

### 0.2 Add explicit instrumentation points

Instrument the following events:

- prefetch scheduled
- on-demand discovery scheduled
- cache hit
- request started
- request resolved
- request failed

Instrumentation should attach enough context to debug:

- `tableId`
- `resourcePath`
- `rel`
- `href`
- source:
  - `prefetch`
  - `row-action`
  - `expansion`
  - future sources can be added later

### 0.3 Add a debug snapshot accessor

Expose an internal/debug-friendly snapshot method or readonly getter on the component that tests can inspect.

This is not a new public API for consumers. It is a runtime diagnostic surface for focused specs.

Recommended contents:

- metrics counters
- current policy decisions
- number of cached hrefs
- number of pending hrefs

Implementation constraint:

- prefer a clearly internal debug accessor/helper used only by focused specs
- do not export a new public symbol from `public-api.ts`
- do not document this accessor as a supported consumer contract

### 0.4 Encode the initial budgets in tests

Focused specs should prove:

- dedupe works while requests are in flight
- repeated reads inside TTL count as cache hits rather than network hits
- current explicit prefetch policy is visible in diagnostics
- user-triggered discovery transitions the runtime into a distinct `loading-context` state
- loading/blocked/error states are distinguishable in runtime diagnostics

## Focused tests for Cut 0

Primary:

- add/update a focused spec around row discovery metrics in `praxis-table`
- keep or extend [praxis-table.remote-regression.spec.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.remote-regression.spec.ts)

Suggested test cases:

1. `records a single network discovery for repeated same-href capability requests`
2. `records cache hit for repeated row capability read inside TTL`
3. `distinguishes prefetch and on-demand discovery sources`
4. `does not increment network count when href is already in flight`
5. `records pending contextual rows and loading-context transitions for user-triggered discovery`

## Acceptance criteria for Cut 0

- row discovery metrics are available in focused tests
- dedupe and cache hit behavior are asserted by tests
- runtime diagnostics distinguish `idle`, `loading-context`, `ready`, `blocked-semantic`, and `error-transient`
- no workspace config changes
- no public API contract is introduced yet

---

## Cut 1: Remove Side Effects From the Render Path

## Goal

Make render-time reads pure by removing discovery-triggering side effects from row action resolution.

## Required outcomes

1. Template-driven calls must no longer trigger network work.
2. Row discovery must be triggered only from explicit runtime entrypoints.
3. Existing visual affordances should continue to work, using already-resolved state.
4. This cut should not yet force the final on-demand UX policy; it only removes the structural flaw.

## Planned write set

Primary files:

- [praxis-table.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.ts)
- [praxis-table.html](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.html)
- [praxis-table.scss](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.scss) when needed to keep row affordance layout stable during unresolved contextual discovery
- [praxis-table.remote-regression.spec.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.remote-regression.spec.ts)
- [praxis-crud.component.spec.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-crud/src/lib/praxis-crud.component.spec.ts) only if host interaction assumptions change

Potential supporting docs after implementation:

- [README.md](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/README.md)

## Implementation tasks

### 1.1 Remove `ensureRowDiscovery(row)` from render-time action resolution

Target the current call path rooted in `getResolvedRowActions(row)`.

After this cut:

- `getResolvedRowActions(row)` must only derive from current row runtime state
- `getVisibleRowActions(row)`, `getInlineRowActions(row)`, `getOverflowRowActions(row)` must stay pure

### 1.2 Introduce explicit runtime trigger points

Create explicit discovery trigger methods with named intent, for example:

- trigger discovery for row overflow-menu open intent
- trigger discovery for explicit row workflow interaction only when the action is already surfaced by resolved runtime state
- keep expansion-triggered discovery in expansion flow only

These methods should become the only legal entrypoints for row item discovery until the scheduler cut.

For avoidance of ambiguity, the expected concrete UI/runtime anchor points are:

- row expansion trigger from the expander control rendered in [praxis-table.html](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.html) around the `_expander` column and its `aria-expanded` toggle
- row overflow contextual actions from the overflow menu trigger rendered in the row actions cell in [praxis-table.html](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.html), currently using `matMenuTriggerFor` and the `more_vert` icon
- runtime handlers must live in [praxis-table.ts](/D:/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.ts) rather than in template-side incidental calls

For Cut 1, freeze the trigger policy explicitly:

- allowed:
  - expansion toggle in `hypermedia` mode
  - row overflow menu open
  - explicit workflow interaction when the row runtime already exposed that affordance
- not allowed:
  - plain row render
  - repeated getter evaluation
  - hover-only discovery
  - passive focus traversal that is not an explicit user intent to inspect contextual actions

These entrypoints must also set explicit runtime states that future UX can rely on:

- `loading-context`
- `ready`
- `blocked-semantic`
- `error-transient`

### 1.3 Preserve current behavior using already-known state

If row discovery has not run yet:

- configured CRUD actions should still render according to existing fallback behavior
- discovered workflow actions should only appear after explicit resolution
- unresolved contextual affordances must keep a stable reserved area in the row action layout
- unresolved contextual affordances must not masquerade as `no actions available`

This prevents the cut from accidentally breaking baseline CRUD affordances while removing the side effect.

### 1.4 Add temporary guardrails for accidental regressions

Add a diagnostic assertion pattern in tests that proves repeated rendering/getter access does not change discovery counters.

This is the key regression guard for the cut.

Also add guardrails for UX/accessibility regressions:

- keyboard focus remains on a stable trigger while discovery is pending
- loading state is represented distinctly from semantic unavailability
- row action area does not collapse/reflow when contextual state changes

## Focused tests for Cut 1

Suggested cases:

1. `render-time row action getters do not trigger discovery`
2. `opening contextual row affordance triggers discovery exactly once`
3. `expansion hypermedia still resolves capabilities on demand when expanded`
4. `configured CRUD row actions remain visible without implicit discovery`
5. `discovered workflow row actions appear only after explicit contextual resolution`
6. `row action area stays layout-stable while contextual discovery is pending`
7. `contextual loading state is distinguishable from blocked or transient error states`

If needed, split tests into:

- pure table runtime spec
- remote regression spec

## Acceptance criteria for Cut 1

- template/read getters are side-effect free
- no discovery is triggered during plain render
- explicit interaction still triggers discovery
- expansion hypermedia behavior remains on-demand
- focused tests prove the difference between render and interaction paths
- row affordance area stays visually stable while contextual discovery is unresolved
- contextual interaction exposes an explicit `loading-context` state
- contextual failure does not silently degrade into `no actions`
- no focusable affordance disappears from under keyboard focus during contextual resolution
- render/getter re-evaluation alone adds `0` new discovery requests for the same row
- a single overflow-menu open or expansion toggle does not fan out into repeated duplicate requests for the same row/rel while one is already in flight

---

## Design Constraints for Cuts 0 and 1

These constraints must be respected now even if some of them are fully exercised only in later cuts.

### Layout stability

- do not make row action width depend on late-arriving discovery in a way that causes visible reflow
- prefer existing configured actions plus later enrichment over inserting/removing controls unpredictably

### Accessibility

- do not make focusable controls disappear from under focus
- if interaction-triggered loading is introduced in templates, it must be announced in the same implementation slice
- do not conflate `loading` with `blocked`
- degraded discovery states must remain perceivable for keyboard and assistive-tech users

### Degraded UX contract

- `loading-context`: user-triggered contextual resolution is pending
- `blocked-semantic`: backend resolved the context and denied the affordance semantically
- `error-transient`: contextual discovery failed or timed out and can be retried
- `ready`: contextual discovery completed and affordances are stable

These states must be explicit in runtime/test diagnostics even if the final UX chrome evolves later.

### Cache semantics

- do not redesign the full cache in Cut 1
- but do not add new logic that further mixes:
  - session cache
  - current-page visual state
  - in-flight requests

### Future compatibility

- Cut 1 must make it easier, not harder, to add:
  - row runtime state materialization
  - concurrency scheduler
  - context-aware cache keys
  - viewport-aware discovery

---

## Validation Strategy

Minimal validation for implementation of these cuts:

1. focused specs in `praxis-table`
2. `npm run build:praxis-table`
3. only if host integration changes are affected, focused specs in `praxis-crud`

Not required for these cuts by default:

- full workspace suite
- remote browser proof
- backend changes

If validation is partial, the implementation handoff must state exactly which focused specs were run.

Recommended escalation only if a focused regression becomes ambiguous:

- one browser-level proof in the remote lab after Cut 1 to confirm row action/menu behavior did not drift visually

---

## Not In Scope Yet

- scheduler with concurrency limit
- visual skeleton redesign for row menus
- context-aware cache key including tenant/principal/locale
- backend aggregation of row affordances
- new public config flags for prefetch policies

These belong to later cuts after Cuts 0 and 1 are stable.

---

## Exit Condition

This plan is ready to execute when:

1. focused implementation starts in `praxis-table`;
2. Cut 0 metrics are in place;
3. Cut 1 removes render-path side effects without breaking baseline CRUD row affordances;
4. the Cut 1 write set and tests explicitly cover runtime states, layout stability, and non-silent degraded UX.
