Implementation Plan: PDEV-610 Cross-User Staleness Signal
Implementation Plan: PDEV-610 Cross-User Staleness Signal
Section titled “Implementation Plan: PDEV-610 Cross-User Staleness Signal”Overview
Section titled “Overview”This plan executes the architecture in design.md for PDEV-610. Scope is PDEV-610 only; PDEV-613 is tracked separately. The work is frontend-only — the operations repository is not touched.
Execution model: single agent walking phases serially.
PR strategy: one PR for everything from the integration branch jmpicnic-ag/acceptance-test-failures-pdev-610 in arda-frontend-app. Post-completion documentation lands in a separate PR.
Rollout: default-on. Dev deploy + verification before merge to main triggers prod release. NEXT_PUBLIC_ITEM_CARDS_POLL_MS is the operational kill switch (set to a huge value to effectively disable pollTimer).
File-size guideline: every file touched is kept under 600 lines; mechanical splits are applied when a touched file exceeds that, with no behavioural redesign. The current ItemCardsContext.tsx (~730 lines plus the new wiring) is the only file that requires planned splitting — see § File-split plan.
Sequencing
Section titled “Sequencing”Topologically ordered to keep main green at every phase boundary. Earlier phases produce the dependencies later phases consume.
| Phase | Theme | Depends on |
|---|---|---|
| 1 | Bus foundation (itemStaleBus, test polyfill, bus tests) | — |
| 2 | Provider integration (ItemCardsProvider, registry, pollTimer) | 1 |
| 3 | Test-utility extensions (render-with-providers, visibility helpers, two-provider harness, stateful MSW handler) | 1, 2 |
| 4 | Producer wiring (mutations, card-state events, RefreshButton, useRefreshOnFocus) | 2, 3 |
| 5 | Integration and robustness tests | 3, 4 |
| 6 | E2E (staleness-mock fixture + Playwright specs) | 5 |
| 7 | Pre-PR validation | 6 |
| 8 | Deploy: dev → verify → prod | 7 |
Phase 1 — Bus foundation
Section titled “Phase 1 — Bus foundation”Build the itemStaleBus module in isolation and a deterministic BroadcastChannel test polyfill so the next phase’s tests can rely on it.
| # | File | Action | Construct | Behavior coverage |
|---|---|---|---|---|
| 1.1 | src/app/items/itemStaleBus.ts | create | ItemStaleBus type, createItemStaleBus, markItemStale, dispose, capability fallback | BV-1-01 … BV-1-06 |
| 1.2 | src/test-utils/broadcastChannelTestEnv.ts | create | installBroadcastChannelTestEnv() + teardown helper | (used by tests below) |
| 1.3 | src/app/items/__tests__/itemStaleBus.test.ts | create | unit tests | BV-1-01 … BV-1-06 |
Phase gate
Section titled “Phase gate”npx jest src/app/items/__tests__/itemStaleBus.test.ts --no-coverage --watchAll=falsegreen.npx tsc --noEmitclean for the new files.
Phase 2 — Provider integration
Section titled “Phase 2 — Provider integration”Wire the bus into ItemCardsProvider, add mountedEids and channelSubscriber, install visibilityListener and pollTimer. Adjust useFreshRead and useStaleCheck to register / unregister. Apply the file-split plan below to keep ItemCardsContext.tsx under 600 lines.
| # | File | Action | Construct | Behavior coverage |
|---|---|---|---|---|
| 2.1 | src/app/items/ItemCardsContext.tsx | modify + split | Apply File-split plan before touching behaviour. | (enables 2.2 – 2.6) |
| 2.2 | src/app/items/ItemCardsContext.tsx | modify | Construct itemStaleBus on provider mount, dispose on unmount. | BV-4-04 |
| 2.3 | src/app/items/ItemCardsContext.tsx | modify | mountedEids: Set<string> + register(eid) / unregister(eid) callbacks on internal context. | BV-3-01 … BV-3-03 |
| 2.4 | src/app/items/ItemCardsContext.tsx | modify | channelSubscriber effect → enqueueStaleRefresh(eids). | BV-4-01 |
| 2.5 | src/app/items/ItemCardsContext.tsx | modify | pollTimer effect (interval default 120 s, env-tunable, gated on visibility + non-empty registry). | BV-2-06, BV-5-01, BV-5-02, BV-5-04 |
| 2.6 | src/app/items/ItemCardsContext.tsx | modify | Expose markItemStale on public context value (delegates to owned bus). | BV-4-02, BV-4-04 |
| 2.7 | src/app/items/useFreshRead.ts (post-split) | modify | Call register(eid) / unregister(eid) from the effect. Freshness logic unchanged. | BV-3-01, BV-3-02 |
| 2.8 | src/app/items/useStaleCheck.ts (post-split) | modify | Same registration discipline as useFreshRead. | BV-3-01 |
| 2.9 | src/app/items/ItemCardsContext.test.tsx | modify + extend | New cases for the new context surface; ensure existing freshness / staleCheck cases stay green. | BV-3-, BV-4-, BV-5-02, BV-5-04 |
Phase gate
Section titled “Phase gate”- All unit tests under
src/app/items/__tests__/andsrc/app/items/ItemCardsContext.*.test.tsxgreen. npx tsc --noEmitclean.- No file under
src/app/items/exceeds 600 lines.
Phase 3 — Test-utility extensions
Section titled “Phase 3 — Test-utility extensions”Stand up the helpers Phases 4 – 6 consume.
| # | File | Action | Construct | Behavior coverage |
|---|---|---|---|---|
| 3.1 | src/test-utils/render-with-providers.tsx | modify | Extend RenderWithProvidersOptions with itemCardsPollIntervalMs?: number and itemStaleBusConsumer?: (eids: string[]) => void. Thread both into the ItemCardsProvider instance created by every helper. | (enables most tests) |
| 3.2 | src/test-utils/visibilityTestEvents.ts | create | setVisibility(state), dispatchVisibilityChange(), dispatchWindowFocus(). | (enables BV-2-05, BV-5-*) |
| 3.3 | src/test-utils/itemCardsTwoProviderHarness.tsx | create | renderTwoProviderHarness(...) mounts two ItemCardsProvider instances sharing one polyfilled BroadcastChannel. | (enables BV-6-01) |
| 3.4 | src/mocks/handlers/kanban-queries.ts | modify | New exported stateful handler factory: returns rId set A on first call for a fixture eid, set B on subsequent calls. | (enables BV-6-02, BV-6-03, BV-7-01, BV-6-04, BV-6-05) |
| 3.5 | (no file) | — | Pick concrete fixture data: FIXTURE_ITEM_EID = "pdev610-fixture-eid", RID_SET_A = ["card-S0-1"], RID_SET_B = ["card-S1-1"]. | — |
Phase gate
Section titled “Phase gate”- Building blocks compile and have at least smoke unit coverage where applicable (e.g.,
renderTwoProviderHarnessrenders two distinct providers; stateful MSW handler returns A then B on consecutive reads). npx tsc --noEmitclean.
Phase 4 — Producer wiring
Section titled “Phase 4 — Producer wiring”Add markItemStale calls at every real producer site identified in the design’s DQ-005 — Item mutations, Card-state events (11 surfaces), RefreshButton, and useRefreshOnFocus. The frozen surface count is 13 producer sites + the RefreshButton host; Task 4.14 is the re-grep checkpoint that confirms no surface was missed during implementation.
| # | File | Action | Construct | Behavior coverage |
|---|---|---|---|---|
| 4.1 | src/app/items/hooks/useRefreshOnFocus.ts | modify | Replace direct refreshCardsForItems(eids) call with markItemStale(eids); eid set unchanged (displayed-grid-items ∪ open-panel-item). | BV-2-05, BV-5-03 |
| 4.2 | src/app/items/hooks/useRefreshOnFocus.test.tsx | modify | Update assertions from refreshCardsForItems to markItemStale. | BV-2-05, BV-5-03 |
| 4.3 | src/components/items/ItemFormPanel.tsx | modify | In post-createItem / updateItem success path, call markItemStale(resultItem.entityId) before invoking the caller’s onSuccess. Apply file split if file grows past 600 lines. | BV-2-01 |
| 4.4 | src/app/items/hooks/useDeleteItems.ts | modify | In success branch, call markItemStale(deletedEntityIds) after the existing toast / dismiss logic. | BV-2-02 |
| 4.5 | src/components/common/CardStateDropdown.tsx | modify | On successful card-state event, call markItemStale(itemEntityId). | BV-2-03 (1 of 11) |
| 4.6 | src/components/table/columnPresets.tsx | modify | On successful card-state event from grid quick actions, call markItemStale(itemEntityId). Apply file split if file grows past 600 lines. | BV-2-03 (2 of 11) |
| 4.7 | src/components/scan/ScanModal.tsx | modify | On event success, call markItemStale(itemEntityId). | BV-2-03 (3 of 11) |
| 4.8 | src/components/scan/CardPreviewModal.tsx | modify | On event success, call markItemStale(itemEntityId). | BV-2-03 (4 of 11) |
| 4.9 | src/components/scan/MobileScanView.tsx | modify | On event success, call markItemStale(itemEntityId). | BV-2-03 (5 of 11) |
| 4.10 | src/components/scan/DesktopScanView.tsx | modify | On event success, call markItemStale(itemEntityId). | BV-2-03 (6 of 11) |
| 4.11 | src/app/order-queue/page.tsx | modify | On event success from order-queue actions, call markItemStale(itemEntityId). | BV-2-03 (7 of 11) |
| 4.12 | src/app/kanban/cards/[cardId]/page.tsx | modify | On event success from the direct kanban-card page, call markItemStale(itemEntityId). | BV-2-03 (8 of 11) |
| 4.13 | src/components/orders/OrderSidebar.tsx | modify | On event success, call markItemStale(itemEntityId). | BV-2-03 (9 of 11) |
| 4.14 | (verify after 4.5 – 4.13) | — | Re-grep producer set; capture the remaining two card-state surfaces if any are uncovered during implementation. Update this row with file paths. | BV-2-03 (10 – 11 of 11) |
| 4.15 | src/components/items/ItemDetailsPanel.tsx | modify | Add RefreshButton to the existing <StaleDataBanner> block (around line 926). On click, call markItemStale(item.eid). Apply file split if file grows past 600 lines. | BV-2-04 |
| 4.16 | src/app/items/hooks/useDeleteItems.test.tsx | modify | Add success-path assertion that markItemStale(deletedEntityIds) is called. | BV-2-02 |
| 4.17 | each producer file’s existing test | modify | One additional case per file asserting the new markItemStale call. Some files will need a new test file alongside. | BV-2-01, BV-2-03, BV-2-04 |
Phase gate
Section titled “Phase gate”- All BV-2-* unit tests green.
npx jest --no-coverage --watchAll=falsegreen for the touched files plus their reverse dependencies.npx tsc --noEmitclean.- No touched file exceeds 600 lines.
Phase 5 — Integration and robustness tests
Section titled “Phase 5 — Integration and robustness tests”Add the BV-6-01 / BV-6-02 / BV-6-03 integration tests and BV-7-01 … BV-7-04 robustness / coexistence tests.
Test-naming convention. Every new BV-* test (Phases 1, 2, 5, 6, plus the producer tests under Phase 4) names its it(...) or test(...) description with the BV-<group>-<seq> prefix — e.g., it("BV-2-01: ItemFormPanel save calls markItemStale(entityId) on success", ...). This lets npx jest -t "BV-" enumerate the full PDEV-610 suite for the exit-criteria check.
| # | File | Action | Construct | Behavior coverage |
|---|---|---|---|---|
| 5.1 | src/components/items/ItemDetailsPanel.crossTab.test.tsx | create | Two-provider harness; mutate via the first; assert sibling’s banner appears. | BV-6-01 |
| 5.2 | src/components/items/ItemDetailsPanel.polling.test.tsx | create | Stateful MSW handler + short poll override + fake timers; assert banner appears within one poll cycle without user action. | BV-6-02 |
| 5.3 | src/components/items/ItemDetailsPanel.writerFeedback.test.tsx | create | Single provider; trigger a mutation whose follow-up refresh returns a different rId set; assert banner appears. | BV-6-03 |
| 5.4 | src/components/items/ItemDetailsPanel.refreshFailure.test.tsx | create | Mock refreshCardsForItems to resolve to the fetch-failed sentinel; assert isStale stays false. | BV-7-01 |
| 5.5 | src/app/items/ItemCardsContext.cycleSafety.test.tsx | create | Spy markItemStale; deliver an inbound channel message; assert no further markItemStale is invoked after the resulting refresh resolves. | BV-7-02 |
| 5.6 | src/app/items/hooks/useBulkSelectionStaleGuard.test.tsx | modify | Add one case where markItemStale is invoked concurrently; assert the guard’s existing behaviour is preserved. | BV-7-03 |
| 5.7 | src/components/items/ItemDetailsPanel.windowEventCoexistence.test.tsx | create | Dispatch refreshItemCards window event AND call markItemStale in one tick; spy refreshCardsForItems; assert exactly one batched call. | BV-7-04 |
Phase gate
Section titled “Phase gate”- All BV-6-01 … BV-6-03 and BV-7-* tests green.
npx jest --no-coverage --watchAll=falseruns end-to-end with no failures.- Coverage thresholds in
jest.config.jsnot regressed againstmain.
Phase 6 — E2E
Section titled “Phase 6 — E2E”Add the Playwright fixture and the two E2E specs that exercise the cross-browser polling path and the RefreshButton end-to-end.
| # | File | Action | Construct | Behavior coverage |
|---|---|---|---|---|
| 6.1 | e2e/fixtures/staleness-mock.ts | create | Playwright fixture extending e2e/fixtures/base.ts; registers the stateful MSW handler before navigation; overrides NEXT_PUBLIC_ITEM_CARDS_POLL_MS to a short test value. | (enables 6.2, 6.3) |
| 6.2 | e2e/specs/pdev-610.polling.spec.ts | create | Use staleness-mock; navigate to ItemDetailsPanel for the fixture eid; assert banner appears within the test’s poll window without user action. | BV-6-04 |
| 6.3 | e2e/specs/pdev-610.refresh-button.spec.ts | create | Use staleness-mock; navigate; wait for banner; click RefreshButton; assert banner clears (useFreshRead.refresh() re-snapshots and adopts the new server state). | BV-6-05 |
Phase gate
Section titled “Phase gate”make test-e2e-safari(or the equivalentnpx playwright testinvocation in mock mode) green for the new specs.- Existing Playwright suite green.
Phase 7 — Pre-PR validation
Section titled “Phase 7 — Pre-PR validation”Local equivalent of the CI gate.
| # | Action | Why |
|---|---|---|
| 7.1 | make ci-replicate clean from the worktree root | Full CI parity (T1 fast gate + T2 queue gate + T3 quality gate). Catches anything the per-phase gates missed. |
| 7.2 | npx jest --no-coverage --watchAll=false rerun | Belt-and-braces; surface flakes early. |
| 7.3 | npx tsc --noEmit | Type checker clean. |
| 7.4 | npm run lint | ESLint clean. |
| 7.5 | Confirm coverage thresholds in jest.config.js are not regressed against origin/main | Required by the repo’s coverage gate. |
| 7.6 | Open PR from jmpicnic-ag/acceptance-test-failures-pdev-610 to main | PR body includes ## CHANGELOG section (PR-body changelog convention), ## Closes referencing PDEV-610, the Authored-by attribution block (see the frontend PR process authoring guidance), and links to design. |
Phase gate
Section titled “Phase gate”- PR is green on CI; reviewer-ready.
Phase 8 — Deploy
Section titled “Phase 8 — Deploy”| # | Action |
|---|---|
| 8.1 | Merge to main is not done in this phase; instead, deploy the PR branch to dev. |
| 8.2 | Manual verification on dev — repro the PDEV-610 scenario across two browsers; observe banner appearing in the second browser within ≤ 120 s. |
| 8.3 | If dev verification is green, merge to main. Post-merge changelog assembly + tag are automatic (PR-body changelog model). |
| 8.4 | Prod deploy follows the standard cadence; no special handling required (the change is default-on; kill switch is NEXT_PUBLIC_ITEM_CARDS_POLL_MS). |
Phase gate
Section titled “Phase gate”- Dev observation matches the design’s BV-6-04 expectation.
- Prod release tag created by the assembly workflow.
- PDEV-610 closed in Linear (post-completion documentation lands in a follow-up PR per user direction).
File-split plan
Section titled “File-split plan”The 600-line ceiling forces one preplanned split. Apply the split before behaviour changes so the test suite migration is mechanical and reviewable in isolation.
src/app/items/ItemCardsContext.tsx (~730 lines today; target: < 600 after additions)
Section titled “src/app/items/ItemCardsContext.tsx (~730 lines today; target: < 600 after additions)”Mechanical extraction — no behaviour changes in the split commit:
| New file | Construct(s) extracted | Re-export contract |
|---|---|---|
src/app/items/useFreshRead.ts | useFreshRead, FreshReadResult, FreshReadOpts, the rIdSet and setsEqual helpers it uses | ItemCardsContext.tsx re-exports useFreshRead, FreshReadResult, FreshReadOpts for backwards-compatible import paths. |
src/app/items/useStaleCheck.ts | useStaleCheck | ItemCardsContext.tsx re-exports useStaleCheck for backwards-compatible import paths. |
After the split, ItemCardsContext.tsx retains: the ItemCardsEntry / ItemCardsContextType / ItemCardsInternalContextType types, the ITEM_CARDS_TTL_MS constant, the ItemCardsProvider component, the useItemCards / useItemCardsInternal hooks. Estimate post-split: ~430 lines, leaving headroom for the new wiring (estimated +120 lines) under the ceiling.
If any other file modified during Phase 4 grows past 600 lines (the most likely candidates are ItemFormPanel.tsx, columnPresets.tsx, ItemDetailsPanel.tsx), apply the same approach: extract a colocated *.helpers.ts or per-section sub-component. Document each ad-hoc split inline in the PR description.
Risk register
Section titled “Risk register”| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
useRefreshOnFocus refactor breaks the existing useRefreshOnFocus.test.tsx. | High | Low | Refactor + test update in the same commit (Task 4.1 + 4.2). Pre-flight: read every assertion in the existing test before the refactor and migrate each. |
useBulkSelectionStaleGuard and the new bus race when both fire in the same tick (e.g., guard preflight just before a sibling tab’s mutation arrives via channel). | Medium | Medium | BV-7-03 covers the concurrent-traffic case. If a flake appears, fall back to scheduling markItemStale on the next microtask so the existing preflight completes deterministically. |
BroadcastChannel polyfill behaviour differs from production browsers. | Medium | Low | Polyfill exercises only the postMessage → onmessage synchronous path. Production behaviour adds asynchrony only, never different semantics. Confirmed by the E2E specs running against a real browser. |
| Producer set has more than 11 card-state event surfaces (recon may have missed some). | Medium | Low | Task 4.14 is a re-grep checkpoint at the end of Phase 4. Add any missed surfaces with the same one-row pattern. |
ItemCardsContext.tsx split changes some import paths used by tests. | Low | Low | Re-export from the original module preserves the public import surface. The split commit only moves code. |
| Coverage threshold regression on the new files. | Low | Low | Each new file ships with focused unit tests; Phase 5 / 6 add integration coverage. Task 7.5 verifies before PR. |
| Polling traffic spike at default 120 s makes the BFF unhappy in dev. | Low | Low | SMB customers, low concurrency. If a spike appears, raise NEXT_PUBLIC_ITEM_CARDS_POLL_MS to 300 s as the immediate response and reassess. |
refreshItemCards window event and the bus produce a double-refresh. | Low | Low | BV-7-04 asserts coalescing. If it ever drifts, the cleanup task (remove the window event listener) is a clear follow-up. |
600-line ceiling forces an ad-hoc split during Phase 4 (e.g., ItemDetailsPanel.tsx already large). | Medium | Low | Plan section File-split plan describes the approach; PR description must call out any ad-hoc split. |
Inbound channel message arrives before any useFreshRead is mounted on the destination eid. | Low | None | channelSubscriber enqueues a refresh; enqueueStaleRefresh refreshes the cache; no isStale flips because no subscriber is mounted. When the panel later mounts, useFreshRead snapshots a cache that is already current — banner stays hidden, which is correct. No code fix needed; documenting the behaviour. |
Several Phase 4 files (ItemFormPanel.tsx 2469 lines, columnPresets.tsx 1740, ItemDetailsPanel.tsx 1321, ScanModal.tsx 1012, CardPreviewModal.tsx 637, MobileScanView.tsx 1834, DesktopScanView.tsx 2476, order-queue/page.tsx 2645) are already over the 600-line ceiling before any change. | High | High | See dedicated § Pre-existing file-size violations below. Default treatment is documented there. |
Pre-existing file-size violations
Section titled “Pre-existing file-size violations”Verified line counts as of the implementation-plan date:
| File | Lines | Over by |
|---|---|---|
src/app/order-queue/page.tsx | 2645 | 4.4× |
src/components/scan/DesktopScanView.tsx | 2476 | 4.1× |
src/components/items/ItemFormPanel.tsx | 2469 | 4.1× |
src/components/scan/MobileScanView.tsx | 1834 | 3.1× |
src/components/table/columnPresets.tsx | 1740 | 2.9× |
src/components/items/ItemDetailsPanel.tsx | 1321 | 2.2× |
src/components/scan/ScanModal.tsx | 1012 | 1.7× |
src/app/items/ItemCardsContext.tsx | 733 | 1.2× |
src/components/scan/CardPreviewModal.tsx | 637 | 1.06× |
The user constraint is: “whenever possible keep any file you touch to under 600 lines, even if it means mechanical splitting of pre-existing file contents, but don’t engage in re-design for this goal.”
For ItemCardsContext.tsx (the File-split plan target), a clean mechanical split exists: useFreshRead, useStaleCheck, and their colocated helpers come out into sibling modules with re-exports for backwards compatibility. No redesign required.
For the other eight files, no purely mechanical split is feasible:
ItemFormPanel.tsx,ItemDetailsPanel.tsx,DesktopScanView.tsx,MobileScanView.tsx,ScanModal.tsx,CardPreviewModal.tsx— each is a single large React component with deeply intermingled state, effects, handlers, and JSX. Splitting requires deciding cohesion boundaries (sub-components, hook extraction), which is design work.columnPresets.tsx— a single module exporting many ag-grid column-renderer constants that share helpers. Mechanical extraction by column family is possible but each family carries different dependencies; “no redesign” is unclear.order-queue/page.tsx— large page component with inline data fetching, state, JSX. Same shape as the form/scan files.
Resolution (per user direction): split where mechanical, leave where design-required. Concretely:
- Will split mechanically:
ItemCardsContext.tsx— extractuseFreshReadanduseStaleCheckper File-split plan. - Marginal — assessed during execution:
CardPreviewModal.tsx(only 37 lines over). If a colocated helper / local hook block of 50+ lines can be extracted without cohesion judgment, do it; otherwise leave with the touch-and-go treatment below. - Touch-and-go (no split):
ItemFormPanel.tsx,ItemDetailsPanel.tsx,ScanModal.tsx,MobileScanView.tsx,DesktopScanView.tsx,order-queue/page.tsx,columnPresets.tsx. Each is large in ways that require cohesion judgment (single React components with intermingled state / effects / JSX, or a 600-line single function insidecolumnPresets). Mechanical-only splits are infeasible. The PR description will inventory these pre-existing violations and call them out as follow-up cleanup work.
Effort sizing
Section titled “Effort sizing”T-shirt per phase, assuming agentic execution with periodic human review.
| Phase | Size |
|---|---|
| 1 | S |
| 2 | M |
| 3 | S |
| 4 | M |
| 5 | M |
| 6 | S |
| 7 | XS |
| 8 | XS (human-driven verification) |
Exit criteria
Section titled “Exit criteria”PDEV-610 is closed when all of the following hold:
- All 29 BV-* tests across the seven Behavioral Design groups are green in CI: BV-1-01 … BV-1-06, BV-2-01 … BV-2-06 (BV-2-03 includes 11 concrete surface tests), BV-3-01 … BV-3-03, BV-4-01 … BV-4-04, BV-5-01 … BV-5-04, BV-6-01 … BV-6-05, BV-7-01 … BV-7-04.
- No regressions on any pre-existing test suite in
arda-frontend-appversusmain. - Coverage thresholds in
jest.config.jsare not regressed. - Dev deploy is verified (Task 8.2) and prod deploy succeeds.
- PDEV-610 is moved to Done in Linear; PR is merged.
Out of scope
Section titled “Out of scope”- Any change to the
operationsrepository (per DQ-010). Theoperations-pdev-610worktree is removed as part of this plan’s prep. - ETag / conditional GET (per DQ-004).
- Server-sent events, WebSocket, pub-sub fabric (per DQ-001 rejected alternatives).
- Reworking
useBulkSelectionStaleGuardor the page-level bulk-selection banner. - Removing the existing
refreshItemCardswindow-event listener (left as follow-up). - Post-completion documentation (separate PR per user direction).
- Resolution of PDEV-613 (tracked separately).
References
Section titled “References”Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved