# Raw Memories

Merged stage-1 raw memories (stable ascending thread-id order):

## Thread `019f5b4d-c79f-7172-96a4-80e10b3959e2`
updated_at: 2026-07-14T13:08:28+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/13/rollout-2026-07-13T14-47-31-019f5b4d-c79f-7172-96a4-80e10b3959e2.jsonl
rollout_summary_file: 2026-07-13T11-47-31-K7uO-purchase_plan_and_cross_store_refresh_audit.md

---
description: approved browser-persistent purchase-plan feature plus a follow-up cross-store refresh audit/fix that eliminated availability-text false positives and was committed
task: spec-and-implement-pirkumu-plans-with-cross-store-refresh-audit
task_group: car-parts-comparator-poc
task_outcome: success
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: purchase-plan, localStorage, refresh banner, directRefresh, availability normalization, Handler.lv, IC24, noVNC, regression tests, commit b28f1f8
---

### Task 1: Purchase-plan spec, implementation, and commit

task: plan-and-implement browser-persistent Pirkumu plāns feature
task_group: frontend/backend feature work
task_outcome: success

Preference signals:
- user said: "The plan is to write up a functional spec first and then implement it after it has been verified and finalized." -> keep spec-first workflow for similar feature additions
- user said: "Name: Pirkumu plans is good." -> default to the Latvian `Pirkumu plāns` terminology
- user clarified: "they are alternatives for consideration ... maybe one store will have other, cheaper products and the overall order will be larger which is preferrable" -> model one chosen offer plus alternatives, not a flat cart
- user asked: "show a banner that the offers may be outdated with a button to refresh? What do you think?" -> explicit stale-data banner + manual refresh is the preferred UX pattern
- user asked: "selectable out of all options" -> store ordering should be user-selectable/persisted rather than fixed

Reusable knowledge:
- purchase plan is stored in browser local storage under `car-parts-comparator.purchase-plan` as versioned JSON
- primary view groups chosen offers by store/orders; secondary view groups by part requirement and shows alternatives
- only chosen offers contribute to store subtotals and grand total; alternatives do not
- refresh requests must bypass the ordinary 15-minute search-result cache and claim freshness only on a newly successful store response
- deterministic part markers should stay stable across reloads

Failures and how to do differently:
- ambiguous "keep both" semantics required multiple spec revisions; future work should settle whether alternatives are purchases or merely candidates before coding
- first pass drifted toward a standard cart model; correct model is part requirements + one chosen offer + alternatives
- no implementation should begin until the spec is approved

References:
- `PURCHASE-PLAN-FUNCTIONAL-SPEC.md` (approved)
- `PURCHASE-PLAN-IMPLEMENTATION-PLAN.md`
- commit `b28f1f8283c833b28e01c619bbe0632a92acbc71` / `Add persistent purchase planning and offer refresh`
- validation: `npm run test`, `npm run test:web`, `npm run typecheck`, `npm run build:web`, `npm run lint`, live probes

### Task 2: Cross-store refresh false-positive audit and fix

task: audit all enabled stores for refresh false positives and normalize availability handling
task_group: backend refresh/parsing + regression tests
outcome: success

Preference signals:
- user corrected scope: "You need to test all the stores for such a case and see how it behaves and fix if needed" -> always audit all enabled stores for this class of bug, not just the first failing one
- user wanted the issue fixed, not just explained -> require regression tests and a live matrix before closing similar parsing/refresh bugs

Reusable knowledge:
- `availability` is display-only; refresh comparison should rely on structured stock state (`inStock`) and ignore wording/punctuation/delivery-note churn
- Handler.lv search pages are a marketplace of supplier rows; exact-refresh must normalize the chosen row, keep shipping date separate, and avoid treating delivery-note-only changes as offer changes
- unknown schema.org availability values should remain unknown rather than being forced to out-of-stock
- IC24 exact-refresh can require the warm resident browser/noVNC human challenge path

Failures and how to do differently:
- the first fix was Handler-only; the user requested a store-wide audit, and the final fix became a cross-store rule with store-specific normalization only where necessary
- comparing raw availability text caused false change warnings across multiple stores; compare structured stock changes instead
- the initial handling of unknown schema availability was too aggressive; unknown must stay unknown
- combined browser probes can skip or challenge some stores, so fall back to per-store probes and recorded fixtures where needed

References:
- live audit touched: `partsale`, `xparts`, `eoltas`, `carparts`, `handler`, `trodo`, `rd24`, `euautodalas`, `autodoc`, `ic24`
- files involved in the fix: `src/core/directRefresh.ts`, `web/src/app/core/purchase-plan.domain.ts`, `tests/directRefresh.test.ts`, `tests/handler.test.ts`, `web/src/app/core/purchase-plan.domain.spec.ts`
- final commit includes the cross-store fix and the purchase-plan feature: `b28f1f8283c833b28e01c619bbe0632a92acbc71`
- live verification included IC24’s noVNC challenge flow and then confirmed search-to-refresh parity

## Thread `019f64a9-abaa-7262-8beb-13203e59d651`
updated_at: 2026-07-16T07:19:43+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/15/rollout-2026-07-15T10-24-28-019f64a9-abaa-7262-8beb-13203e59d651.jsonl
rollout_summary_file: 2026-07-15T07-24-28-NdSN-phase_g_android_showcase_review_hardening.md

---
description: Phase G Android app work in car-parts-comparator-poc: challenge-view showcase build, native UI polish, review fixes, versionCode hardening, single-point URL decoration, and final commit/cleanup.
task: build challenge browser view showcase; fix Phase G review findings; commit final Android app changes
task_group: car-parts-comparator-poc / Android app and release workflow
 task_outcome: success
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: android, capacitor, WebViewFetcherPlugin, solve view, showcase APK, dark mode, vector icon, animations, versionCode, github.run_number, carparts parser, outbound decoration, search.gateway, aapt, gradle, review-fixes, commit
---

### Task 1: Challenge-view showcase build

task: create a demo build that opens the challenge browser UI on a random store page, then refine its appearance

task_group: Android UI / showcase APK
 task_outcome: success

Preference signals:
- when the user asked for "a build that opens the browser view for a challenge" with "a random store page", they wanted a visual showcase build rather than a hidden test harness
- when the user said the close button should be "black/dark grey", they wanted native UI chrome to match the design, not the default framework look
- when the user said "Keep in mind the dark mode", they wanted theme-aware styling by default
- when the user said the icon was "big, grey (not even dark grey) and blurry", they wanted a crisp vector icon, not a tinted framework drawable
- when the user asked for "nice modern animations", they wanted restrained open/close motion polish if it felt native
- when the user said "remove the force open", they wanted showcase-only auto-open behavior removed from the normal build

Reusable knowledge:
- the Android solve UI is implemented in `android/app/src/main/java/lv/carp/partscomparator/WebViewFetcherPlugin.java`
- solve dialog animations live in `android/app/src/main/res/anim/solve_view_enter.xml` and `solve_view_exit.xml`
- the crisp close button should use a vector asset (`android/app/src/main/res/drawable/ic_close.xml`), not `android.R.drawable.ic_menu_close_clear_cancel`
- normal APK build path used here was `npm run package:android:debug`

Failures and how to do differently:
- tinting the framework close drawable was not enough; the icon itself was low-quality, so replace the asset instead of just recoloring it
- temporary showcase/force-open plumbing should be removed once the user says the demo-only behavior is no longer needed

References:
- `android/app/src/main/java/lv/carp/partscomparator/WebViewFetcherPlugin.java`
- `android/app/src/main/res/drawable/ic_close.xml`
- `android/app/src/main/res/anim/solve_view_enter.xml`
- `android/app/src/main/res/anim/solve_view_exit.xml`
- `android/app/build/outputs/apk/debug/app-debug.apk`

### Task 2: Phase G review fixes and Android release hardening

task: address code-review findings in Phase G, harden Android versioning, and remove stray artifacts

task_group: Android review / release workflow
 task_outcome: success

Preference signals:
- when the user provided a review file path, they wanted the findings checked against the actual code and fixed rather than assumed
- when the user asked to "Commit", they wanted the review fixes and implementation captured as one coherent commit
- when the user said "You can remove those" about the bug-report ZIPs, they wanted the workspace cleaned up after completion

Reusable knowledge:
- `src/adapters/carparts.ts` now preserves offers whose rendered heading ends with the brand, using the canonical query as fallback when the displayed part number is missing
- `web/src/app/core/search.gateway.ts` is the right place to keep native clickout decoration single-sourced; avoid double-decoration across layers
- `android/app/build.gradle` now validates version strings and uses a monotonic CI override (`ANDROID_VERSION_CODE=${{ github.run_number }}` in the release workflow)
- `aapt dump badging android/app/build/outputs/apk/debug/app-debug.apk` was used to confirm `versionCode` / `versionName`

Failures and how to do differently:
- the original Android versionCode formula was too brittle; future release code should fail fast on unsupported semver shapes instead of trying to coerce them
- URL decoration should happen in one layer only; having both the Angular gateway and the native host decorate can reserialize URLs or duplicate logic
- delete generated bug-report ZIPs after they are no longer needed so the worktree stays clean

References:
- review file: `/tmp/claude-1000/-home-Messier82-projects/car-parts-comparator-poc/f0672c66-7fab-4cec-af61-9887283b584a/scratchpad/phase-g-review.md`
- commit: `64f8e84 feat: complete Phase G Android app`
- APK verification: `aapt dump badging android/app/build/outputs/apk/debug/app-debug.apk` reported `versionCode='100399' versionName='0.1.3'`
- cleanup: removed `bugreport-stallion-CP2A.260605.012-2026-07-16-07-12-51.zip` and `bugreport-stallion-CP2A.260605.012-2026-07-16-07-50-47.zip`

## Thread `019f6a2c-b130-7a80-b133-70910579ae76`
updated_at: 2026-07-16T11:35:42+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/16/rollout-2026-07-16T12-05-41-019f6a2c-b130-7a80-b133-70910579ae76.jsonl
rollout_summary_file: 2026-07-16T09-05-41-dr8y-windows_challenge_flow_trodo_session_fix.md

---
description: Fixed a Windows Electron challenge-window loop affecting Trodo and other walled stores; added selector-based readiness checks for HTML stores, tightened Trodo JSON-envelope handling, verified with desktop build/tests and packaged a portable Windows exe, then committed the result.
task: diagnose-and-fix-windows-desktop-challenge-loop-and-trodo-zero-result-behavior
task_group: car-parts-comparator-poc
_task_outcome: success
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: Windows, Electron, challenge_required, Trodo, rd24, euautodalas, Autodoc, BrowserWindow, backgroundThrottling, waitForSelector, portable-exe, commit
---

### Task 1: Fix Windows desktop challenge-loop and Trodo timing

task: diagnose-and-fix-windows-desktop-challenge-loop-and-trodo-zero-result-behavior
task_group: car-parts-comparator-poc
task_outcome: success

Preference signals:
- when platform evidence is ambiguous, the user corrected: "It wasnt android. It was windows. Why did you assume that it was Android? You should've asked" -> ask which platform before making platform-specific edits.
- when asked for the exact repro, the user supplied `34118831822` and `51137497285` -> request the exact query/part numbers before concluding the issue is store-specific vs session/timing-related.
- when the fix was verified, the user said "Commit this" -> after verification, commit the working tree instead of stopping at a verbal summary.

Reusable knowledge:
- Electron desktop challenge handling for this repo is in `desktop/electronBrowserHub.ts`; the working approach was to trust rendered challenge markers and store-specific readiness, not a 403 alone.
- For RD24 and EUAutodalas, adding `waitForSelector` to `fetchPageHtml()` prevented accepting a rendered 403 page before the result cards actually appeared.
- Trodo’s valid no-result shape is still `[{"products":[]}]`; malformed or transitional payloads should not be treated as a clean empty result.
- Hidden Electron windows needed `backgroundThrottling: false` so Cloudflare/session scripts could continue while the window was hidden.
- Validation that mattered: `npm run build:desktop`, `npm test`, portable Windows packaging, then live probe in the packaged app.

Failures and how to do differently:
- The initial Android-specific change was a wrong-platform assumption; it was reverted. For similar future reports, ask clarifying platform questions before editing.
- The first desktop fix still looped because the browser re-navigated too early after clearance; the durable fix was to avoid the second immediate navigation and require stable readiness.
- Trodo needed separate handling from the HTML stores; do not collapse all walled-store failures into one timing rule.

References:
- `desktop/electronBrowserHub.ts` — desktop challenge state machine and readiness checks
- `src/adapters/rd24.ts` / `src/adapters/euautodalas.ts` — `fetchPageHtml(..., { waitForSelector })`
- `src/adapters/trodo.ts` — JSON-envelope parsing guard
- `tests/rd24.test.ts`, `tests/euautodalas.test.ts`, `tests/trodo.test.ts` — regression coverage
- `release/Car-Parts-Comparator-0.1.3-portable-x64.exe` — verified Windows portable artifact
- `396be13 Fix desktop challenge session handling` — final commit

## Thread `019f6ac6-58d0-75f0-a7b9-d9e0a7889bd8`
updated_at: 2026-07-17T06:56:25+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/16/rollout-2026-07-16T14-53-31-019f6ac6-58d0-75f0-a7b9-d9e0a7889bd8.jsonl
rollout_summary_file: 2026-07-16T11-53-31-dBUY-search_history_spec_implementation_commit.md

---
description: spec-first implementation of persistent search history, including removing search analytics/UTM plumbing, cross-platform persistence, focus-driven history UI, packaging, verification, and final commit
task: persistent search history feature + measurement removal + commit
task_group: car-parts-comparator-poc
task_outcome: success
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: search history, functional spec, focus-driven history, canonical deduplication, browser fallback, SQLite, Android Preferences, Electron JSON storage, measurement removal, UTM removal, package:win:portable, package:android:debug, commit
---

### Task 1: functional specification for search history

task: draft and approve search history functional spec before implementation
task_group: product_specification
task_outcome: success

Preference signals:
- when adding a significant feature, the user said: "The plan is to write up a functional spec first and then implement it after it has been verified and finalized." -> keep feature work spec-first until the spec is approved.
- when proposing a separate history control, the user corrected: "Beside the search field? That how?" and later "I think separate button to view history is unnecessary. In the industry, the history is shown on input focus" -> default to focus-driven history UI, not a dedicated history button/icon.
- when keyboard navigation was being defined, the user said: "if keyboard was used to select the item, pressing tab will just prefill it" -> preserve Enter/click vs Tab behavior in history selection.
- when measurement was discussed, the user asked "what measurement?" and then "I think we can safely remove all of it. We don't really need it" -> do not preserve separate search/click analytics as a product requirement.

Reusable knowledge:
- The approved search-history spec is `SEARCH-HISTORY-FUNCTIONAL-SPEC.md`.
- It defines recent searches as a user-visible history list, not saved searches/alerts.
- History is capped at 20 distinct canonical part numbers, ordered most-recent-first, and deduplicated by canonicalized query.
- The final UI spec is focus-driven: focusing an empty search field opens history; typing closes it; clearing while focused reopens it; no separate history button is shown.
- The product docs were updated so search history, not analytics, is the retained search-related concept.

Failures and how to do differently:
- The first draft exposed a separate history icon/button; the user rejected that. Future drafts should ask early whether history should follow conventional input-focus autocomplete behavior.
- The first pass left measurement ambiguous. Future specs should explicitly answer whether analytics/telemetry survives or is removed.
- Keyboard behavior needed explicit Enter vs Tab semantics; future similar specs should state this up front.

References:
- User wording: "The plan is to write up a functional spec first and then implement it after it has been verified and finalized."
- User wording: "I think separate button to view history is unnecessary. In the industry, the history is shown on input focus"
- User wording: "if keyboard was used to select the item, pressing tab will just prefill it"
- User wording: "I think we can safely remove all of it. We don't really need it"
- Final spec file: `SEARCH-HISTORY-FUNCTIONAL-SPEC.md`

### Task 2: implement, verify, package, and commit search history

task: implement persistent search history across web/desktop/android, remove measurement plumbing, package builds, and commit
task_group: car-parts-comparator-poc
task_outcome: success

Preference signals:
- when the spec was approved, the user said: "Go ahead with the implementation" -> proceed directly to implementation once the spec is finalized.
- when the feature was done, the user said: "Commit" (repeated) -> commit the completed work as a single feature commit.

Reusable knowledge:
- Hosted web uses SQLite-backed `/api/search-history`; desktop uses an atomic JSON file under `app.getPath("userData")`; Android uses Capacitor Preferences.
- Search history has a browser fallback and a durable pending-change marker so offline deletions/additions are retried after restart instead of being overwritten by stale backend state.
- The final interaction is focus-driven rather than button-driven, and the input is not auto-focused on page load.
- The measurement pipeline was removed entirely: `measurement.ts`, `statsPage.ts`, `/api/searches`, `/stats`, search IDs, and UTM/link-decoration plumbing were deleted.
- Search/history persistence is covered by new unit tests in both backend and web layers.

Failures and how to do differently:
- The first implementation built a separate history button; after the user rejected it, the UI had to be revised to focus-open history. Future artifact/build requests should wait until the interaction model is settled.
- Persistence needed a retry-safe pending marker; otherwise a stale backend could restore deleted history after restart. Future syncable local-state features should explicitly handle unsynchronized removals, not just saves.
- The first packaged binaries were produced before the final UX change, so they had to be rebuilt; future release requests should be issued only after final UX approval.

References:
- Commit: `522250c feat: add persistent search history`
- Windows portable artifact: `release/Car-Parts-Comparator-0.1.3-portable-x64.exe`
- Android debug APK: `android/app/build/outputs/apk/debug/app-debug.apk`
- Rebuilt artifact SHA-256:
  - `3af9409537f619cdec96570b209fc549d0a354af22c26f2aeff78ea77a5b29e4` (portable EXE)
  - `b7f960e5de8e77c97508918aca2e16d1b3d66b6b8707772843994de13508b141` (APK)
- Verification commands that passed: `npm test`, `npm run test:web`, `npm run typecheck`, `npm run lint`, `npm run build:web`, `npm run build:desktop`, `npm run build:android`, `npm run check:engine-browser`

## Thread `019f6edc-be38-74c0-87b9-6c7692d64e71`
updated_at: 2026-07-17T12:52:17+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/17/rollout-2026-07-17T09-56-28-019f6edc-be38-74c0-87b9-6c7692d64e71.jsonl
rollout_summary_file: 2026-07-17T06-56-28-IFEt-car_parts_comparator_shell_glass_header_scrollbar_fix.md

---
description: Fixed desktop/Android shell scrollbar overlap by moving scrolling into a content region below the header, then made the header genuinely glass-like (72% alpha + blur) after the user said it still looked opaque; final portable Windows .exe and Android .apk were rebuilt and the changes were committed.
task: fix shell scrollbar and add glass header, rebuild portable exe and android apk, commit
task_group: car-parts-comparator-poc
task_outcome: success
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: scrollbar, header, glass header, backdrop-filter, opacity, desktop smoke, electron, android apk, portable exe, package:win:portable, package:android:debug, app-content-scroll, app-shell, commit
---

### Task 1: Fix shell scrollbar overlap

task: fix desktop main scrollbar going under header; inspect other host variants too
task_group: UI layout / shell behavior
task_outcome: success

Preference signals:
- when the user reported: "main scrollbar goes under the header when the handler is up" -> default to checking shell/header scroll ownership before touching unrelated UI
- when the user said it was desktop but asked it was "worth checking others aswell" -> inspect shared host variants (desktop/android) when the same shell pattern exists

Reusable knowledge:
- In this repo, the scroll bug was fixed by making `app-shell` a full-height relative container and moving scroll into `.app-content-scroll`; leaving the whole page scrollable while the header stayed sticky still let the scrollbar travel behind the header.
- `document.body.style.overflow` still needs to be toggled for overlays; the final implementation keeps that lock logic but uses a computed `scrollLocked` signal.
- Desktop smoke can validate shell geometry directly from Electron (`headerBottom`, `contentTop`, `contentBottom`, `contentOverflowY`).

Failures and how to do differently:
- The first sticky-header layout was not enough because the scrollbar still belonged to the page. Use a dedicated content scroll region instead.
- Don’t rely on CSS intuition alone for this bug; verify with runtime geometry in the desktop smoke check.

References:
- `web/src/styles.css`: `.app-shell`, `.app-content-scroll`, `app-root.app-scroll-locked .app-content-scroll`
- `web/src/app/app.html`: added `<div class="app-shell">` and wrapped `<main>` in `.app-content-scroll`
- `web/src/app/app.ts`: `[class.app-scroll-locked]`, `scrollLocked = computed(...)`
- `desktop/main.ts`: smoke checks for content/header bounds and `overflowY`

### Task 2: Add glass header, then correct ineffective transparency
task: add opacity/blur header, then make it actually look transparent
task_group: UI styling / header
task_outcome: success

Preference signals:
- when the user asked: "Let's add a opacity to header and a blur filter for a more modern look" -> default to translucent header styling with blur rather than an opaque color tweak
- when the user corrected: "still non transparent" -> treat visual validation as mandatory; do not assume a build means the effect is visible

Reusable knowledge:
- A header can be technically translucent but still look opaque if the content behind it is a flat white layer; the fix was to let content scroll behind the header and inset the scrollbar track below it.
- The final header styling used `background-color: color-mix(in oklch, var(--card) 72%, transparent)` with `backdrop-filter: blur(16px) saturate(1.3)`.
- Desktop smoke can read computed style and confirm the header alpha/filter (`headerBackground`, `headerBackdropFilter`) instead of trusting the CSS source alone.

Failures and how to do differently:
- The first rebuild was visually ineffective; the CSS existed but the effect was hidden. Fix the layering/scrolling first, then the glass effect becomes visible.
- A screenshot / computed-style check is better than just saying the artifact was rebuilt.

References:
- `web/src/styles.css`: absolute header, translucent background, blur/saturate filters, scrollbar track inset
- `desktop/main.ts`: checks for `headerBackground.includes(" / 0.72")` and `headerBackdropFilter !== "none"`

### Task 3: Rebuild installables and commit
task: rebuild portable Windows exe and Android apk, then commit the final UI fix
task_group: packaging / release
task_outcome: success

Preference signals:
- when the user said simply "commit" after the fix was ready -> finalize cleanly rather than leaving the patch uncommitted
- earlier clarification that the main targets were portable `.exe` and Android `.apk` -> rebuild those deliverables after UI changes

Reusable knowledge:
- Successful packaging commands: `npm run package:win:portable` and `npm run package:android:debug`.
- Final artifact locations: `release/Car-Parts-Comparator-0.1.3-portable-x64.exe` and `android/app/build/outputs/apk/debug/app-debug.apk`.
- Final rebuilds should follow the corrected implementation, not the first visually ineffective build.

Failures and how to do differently:
- The first packaging pass happened before the transparency fix was truly visible. After the user correction, package again so the artifacts match the final UI.

References:
- Final hashes after corrected rebuild: `880fafae6e9e0cd0a3fa3c67eed7d840892d5ca25b7c10acc5307ed6e62a9c0b` (exe), `cb5e62f826bbfbc7d97c67bf79730b47053dbdf1f018c15e003a22987bb323eb` (apk)
- Commit: `362a007 fix shell scrollbar and add glass header`

## Thread `019f751c-9dee-7e52-a4d2-6dddb1d7d6c9`
updated_at: 2026-07-18T18:01:59+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/18/rollout-2026-07-18T15-03-57-019f751c-9dee-7e52-a4d2-6dddb1d7d6c9.jsonl
rollout_summary_file: 2026-07-18T12-03-57-VlUt-autodoc_diagnostics_ui_hidden_and_committed.md

---
description: User refined the diagnostics feature to be less visible; implementation was changed to contextual icon-only triggers, rebuilt for Android/Windows, and committed.
task: hide diagnostics UI and commit the refinement
task_group: car-parts-comparator-poc
task_outcome: success
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: diagnostics, hidden UI, icon-only trigger, rebuild, commit, android apk, windows portable, car-parts-comparator-poc
---

### Task 1: Hide diagnostics UI and commit the refinement

task: refine diagnostics feature visibility; make it more hidden; commit the result
task_group: frontend_ui + release workflow
task_outcome: success

Preference signals:
- when the diagnostics feature was too obvious, the user said: "Right now its too apparent. It should be more hidden" -> default to subtle, contextual controls instead of obvious labels/links for this kind of feature.
- when the refinement was ready, the user said: "commit" -> finalize the verified changes with a Git commit instead of stopping at a summary.

Reusable knowledge:
- The final implementation used an icon-only diagnostic affordance shown only on stores with failure/check-required history; the global `Diagnostika` link was removed.
- Diagnostic evidence remains available after later successful retries, so a hidden UI does not lose retrievability.
- The repo’s verified packaging commands were rerun after the UI refinement before committing.

Failures and how to do differently:
- The first diagnostic UI was too apparent; the user wanted it less visible. Future similar work should start with low-prominence, contextual entry points.
- When the user asks to commit, avoid leaving the worktree dirty; verify first, then stage and commit.

References:
- `web/src/app/app.html` and `web/src/app/app.ts`: removed global diagnostics link; per-store info icon only.
- `web/src/app/core/search.service.ts`: diagnostic-session tracking and retrieval stayed in place.
- `web/src/app/core/search-diagnostics.ts`: helper functions for bounded report generation / store-scoped checks.
- `cdcf21e fix: harden store challenge handling and diagnostics`
- Rebuilt artifacts: `android/app/build/outputs/apk/debug/app-debug.apk`, `release/Car-Parts-Comparator-0.1.3-portable-x64.exe`

## Thread `019f7664-888c-7102-9567-ebc90973ca44`
updated_at: 2026-07-21T12:12:05+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/18/rollout-2026-07-18T21-02-07-019f7664-888c-7102-9567-ebc90973ca44.jsonl
rollout_summary_file: 2026-07-18T18-02-07-JoMc-frontend_architecture_refactor_feature_components.md

---
description: user requested a behavior-preserving frontend architecture refactor in car-parts-comparator-poc; root Angular shell/template were split into feature components, docs/tests were updated, build/test/smoke/package checks passed, and the refactor was committed
task: analyze and refactor the whole frontend architecture
task_group: car-parts-comparator frontend architecture refactor
task_outcome: success
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: Angular, app.html, app.ts, feature components, refactor, build:web, typecheck, smoke:desktop, package:android:debug, frontend architecture, search-input, store-progress, search-results, purchase-plan, diagnostics-dialog, solve-overlay, aapt, adb, vitest, eslint, commit
---

### Task 1: Audit frontend complexity and choose refactor boundaries

task: analyze frontend structure and identify refactor hotspots before editing

task_group: car-parts-comparator frontend architecture refactor
task_outcome: success

Preference signals:
- when the user said "I feel like it could to a lot of refactoring. Like, for example, app.html is large. So I want you to analyze the whole frontend and determine the places that need refactor and do it" -> prefer a real architecture audit and boundary analysis before making edits, not a superficial split
- when the user later said "commit" -> finish verified refactor work with a Git commit instead of stopping at a summary

Reusable knowledge:
- `web/src/app/app.html` and `web/src/app/app.ts` were the main presentation bottlenecks; core services/domain modules were already comparatively cohesive and test-covered
- The most useful refactor boundary was presentation ownership: search/history, store progress, search results, purchase plan, diagnostics dialog, solve overlay
- `FRONTEND-ARCHITECTURE.md` is the right place to record and preserve those ownership boundaries for future frontend work

Failures and how to do differently:
- initial direct work from the monolithic shell/template was less effective than first measuring size and confirming which layers were already cohesive
- later test fallout showed that template ownership assumptions had to be updated after extraction; tests should follow the new feature owners instead of the old root file

References:
- `web/src/app/app.html` was 913 lines before extraction; `web/src/app/app.ts` was 589 lines
- new architecture doc: `FRONTEND-ARCHITECTURE.md`
- check command that established the surface area: `wc -l web/src/app/app.html web/src/app/app.ts web/src/styles.css web/src/app/core/*.ts`

### Task 2: Extract feature components and preserve behavior

task: refactor the frontend into smaller feature-owned Angular components without changing behavior
task_group: car-parts-comparator frontend architecture refactor
task_outcome: success

Preference signals:
- when the user called out that `app.html` was large and asked to refactor the whole frontend -> optimize for reducing root-component ownership and template size
- when the user asked to commit after verification -> treat the refactor as something to finish through validation and commit, not an exploratory prototype

Reusable knowledge:
- feature-owned components were introduced for `SearchInputComponent`, `StoreProgressComponent`, `SearchResultsComponent`, `PurchasePlanComponent`, `DiagnosticsDialogComponent`, and `SolveOverlayComponent`
- the root `App` now acts as shell/coordinator while the extracted components own their own transient UI state and templates
- feature-specific animation CSS for store progress was moved out of `web/src/styles.css` into `web/src/app/features/store-progress/store-progress.css`
- a small pure helper (`web/src/app/features/search-input/search-history.presentation.ts`) was added for history date formatting, with deterministic tests in `search-history.presentation.spec.ts`
- the real-anchor regression test had to move from `web/src/app/app.html` to the new owners in `web/src/app/features/search-results/search-results.html` and `web/src/app/features/purchase-plan/purchase-plan.html`

Failures and how to do differently:
- one root test failed because it still assumed the old monolithic template owned outbound anchors; after extraction, tests must target the new feature templates
- Android live-device validation could not be completed because no usable ADB daemon/device was exposed; APK build success is not the same as device execution

References:
- new feature files:
  - `web/src/app/features/search-input/search-input.ts`
  - `web/src/app/features/search-input/search-input.html`
  - `web/src/app/features/search-input/search-history.presentation.ts`
  - `web/src/app/features/search-input/search-history.presentation.spec.ts`
  - `web/src/app/features/store-progress/store-progress.ts`
  - `web/src/app/features/store-progress/store-progress.html`
  - `web/src/app/features/store-progress/store-progress.css`
  - `web/src/app/features/search-results/search-results.ts`
  - `web/src/app/features/search-results/search-results.html`
  - `web/src/app/features/purchase-plan/purchase-plan.ts`
  - `web/src/app/features/purchase-plan/purchase-plan.html`
  - `web/src/app/features/diagnostics-dialog/diagnostics-dialog.ts`
  - `web/src/app/features/diagnostics-dialog/diagnostics-dialog.html`
  - `web/src/app/features/solve-overlay/solve-overlay.ts`
  - `web/src/app/features/solve-overlay/solve-overlay.html`
- updated root shell:
  - `web/src/app/app.html`
  - `web/src/app/app.ts`
- updated regression test: `tests/engine.test.ts`

### Task 3: Verification and commit

task: run frontend/runtime/android verification and commit the refactor
task_group: car-parts-comparator frontend architecture refactor
task_outcome: success

Preference signals:
- when the user said "commit" -> stage and commit the finished change after the verification gates pass

Reusable knowledge:
- the verification path that succeeded was `npm run lint`, `npm test`, `npm run build:web`, `npm run typecheck`, `npm run smoke:desktop`, and `npm run package:android:debug`
- the desktop smoke is still a meaningful regression check after frontend extraction because it exercises the Electron shell geometry and persistence bridges, not just template compilation
- Android packaging succeeded and the APK metadata was confirmed with `/home/Messier82/Android/Sdk/build-tools/36.0.0/aapt dump badging android/app/build/outputs/apk/debug/app-debug.apk`
- final commit: `48207af Refactor frontend into feature components`

Failures and how to do differently:
- `adb devices`/ADB daemon startup failed in this environment, so live-device Android validation was unavailable even though the APK build succeeded

References:
- final APK path: `android/app/build/outputs/apk/debug/app-debug.apk`
- final APK SHA-256 observed during verification: `cd6c466b487a1833a85a51a0868df2fc6c6f1d48281a25125ad2344e4889431`
- final commit: `48207af Refactor frontend into feature components`
- package/build commands: `npm run smoke:desktop`, `npm run package:android:debug`, `npm test`, `npm run lint`, `npm run build:web`, `npm run typecheck`

## Thread `019f8497-2b34-7312-a662-f3fb5ebf2419`
updated_at: 2026-07-22T08:55:32+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/21/rollout-2026-07-21T15-12-07-019f8497-2b34-7312-a662-f3fb5ebf2419.jsonl
rollout_summary_file: 2026-07-21T12-12-07-zJEt-localization_settings_cross_platform_ui_fixes.md

---
description: Implemented approved Latvian/Russian/English localization with device detection and persistence, then fixed cross-platform Purchase Plan and Android UI regressions; verified and committed.
task: localization-and-cross-platform-ui-polish
 task_group: car-parts-comparator-poc
 task_outcome: success
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: localization, Russian, English, Latvian, device-locale, Capacitor Preferences, Spartan Select, Purchase Plan, Android, Electron smoke, untracked, layout, 186d422
---

### Task 1: Localization and Settings

task: implement approved app localization and language selector
task_group: car-parts-comparator-poc
task_outcome: success

Preference signals:
- The user said "Spec approved, proceed" after reviewing the functional spec -> preserve the spec-first workflow and implement only after explicit approval.
- The approved behavior is Latvian/Russian/English, saved explicit choice before device locale, Latvian fallback, device-local persistence, and immediate switching without losing active state.

Reusable knowledge:
- Spartan Select's closed trigger displays raw values unless `[itemToString]` is supplied. Mapping language codes through `itemToString` fixed `en/lv/ru` displaying as `English/Latviešu/Русский`.
- Android locale detection and preference persistence were implemented through native locale data and Capacitor Preferences; the preference is local to each device/profile.
- App-owned UI is localized while store names, product text, availability text, and machine-readable support fields remain source-faithful/stable.

Failures and how to do differently:
- Electron runtime smoke initially failed in the restricted container with `content/browser/sandbox_host_linux.cc:41`; rerun with host access to validate renderer behavior.

References:
- `LOCALIZATION-FUNCTIONAL-SPEC.md` status approved.
- `SETTINGS-FUNCTIONAL-SPEC.md` status approved.
- Runtime smoke confirmed `languageLabel: "English"`, `htmlLang: "en"`, and `searchLabel: "Search"`.

### Task 2: Responsive UI corrections and packaging

task: fix Android/desktop search, store-status, and Purchase Plan layouts
task_group: cross-platform UI verification
task_outcome: success

Preference signals:
- The user clarified "same row as price and store action" and later "this should apply to desktop aswell" -> apply the exact accepted grouping consistently across narrow and desktop layouts.
- The user expects rebuilt APK/desktop artifacts after visual fixes.

Reusable knowledge:
- `web/src/app/features/store-progress/store-progress.ts`: when an effect closes a panel on search changes, call `untracked(() => this.close(true))`; otherwise `close()` reads `panelOpen` and opening the panel can retrigger the effect and immediately close it.
- Purchase Plan Orders layout now uses item details on row one and quantity/price/store action on row two for all widths.
- Android Purchase Plan trigger uses `width: auto; min-width: 48px` so count badges can expand the button.
- Parts/alternatives candidate rows keep price left and a compact grouped action cluster right.

Failures and how to do differently:
- The first responsive layout attempt placed quantity beside item details, contrary to the user's intended grouping. Confirm exact row ownership before editing responsive grids.
- Search-results spacing regression came from sibling template content without component-owned vertical layout; adding `host: { class: 'flex flex-col gap-5' }` restored spacing.

References:
- Key files: `web/src/app/features/purchase-plan/purchase-plan.html`, `web/src/app/features/store-progress/store-progress.ts`, `web/src/app/features/search-results/search-results.ts`, `web/src/styles.css`.
- Verification passed: `npm --prefix web test` (61 tests), `npm run typecheck`, `npm run lint`, desktop smoke with host access, `npm run build:android`, Gradle `assembleDebug`, and Windows portable packaging.
- Commit `186d422 Add localized settings and diagnostics`; final worktree clean.

## Thread `019f8909-d1b9-73f3-8a0e-db6fcba0156a`
updated_at: 2026-07-22T09:28:20+00:00
cwd: /home/Messier82/projects/car-parts-comparator-poc
rollout_path: /home/Messier82/.codex/sessions/2026/07/22/rollout-2026-07-22T11-55-49-019f8909-d1b9-73f3-8a0e-db6fcba0156a.jsonl
rollout_summary_file: 2026-07-22T08-55-49-ciRY-hosted_open_stores_native_coverage_retailer_partnership_funn.md

---
description: User endorsed a staged distribution strategy: conservative hosted web with open stores plus broader desktop/Android apps, privacy-disclosed disableable click measurement, then use measured demand to secure authorised feeds/CPA partnerships.
task: staged-hosted-native-distribution-and-retailer-conversion
task_group: car-parts-comparator-poc strategy
 task_outcome: uncertain
cwd: /home/Messier82/projects/car-parts-comparator-poc
keywords: hosted, desktop, Android, clickout, telemetry, CPA, affiliate, retailer-partnership, Browserless, BrowserHub, privacy
---

### Task 1: Staged hosted/native distribution strategy

task: staged-hosted-native-distribution-and-retailer-conversion
task_group: product strategy and monetization
task_outcome: uncertain

Preference signals:
- The user proposed hosting only open stores, adding broader desktop/Android editions, and tracking clicks from both surfaces: “track clicks from them (disablable, notify user about that)” -> future planning should treat narrow, disclosed, user-disableable click measurement as a potential product requirement, but only after a new privacy spec is approved.
- The user wants measured traffic used as leverage with closed stores and later existing open stores: “do you want to be in?” and “now we want to work with them, or they’re out” -> future commercial planning should proactively include a retailer-conversion phase and a consistently published inclusion/removal policy.

Reusable knowledge:
- Hosted web should remain limited to plain-reachable, unauthenticated, query-scoped adapters. Native editions can provide extended coverage from the user’s device, reducing central extraction and browser-farm requirements, but they are not a complete legal shield.
- Click telemetry should report only minimal retailer-click facts (store, surface, coarse result position, timestamp, short-lived event ID), never part numbers, destination query URLs, advertising IDs, stable installation IDs, account identity, precise location, or fingerprints. Telemetry failure must never prevent the retailer link from opening.
- Click counts prove qualified outbound visits, not sales. Sales claims require affiliate tracking, postbacks, coupons, or retailer conversion reporting.
- Commercial model should prefer CPA/affiliate or authorised feed/API agreements over CPC. Keep organic ranking price-neutral and never sell ranking.
- Existing measurement plumbing was deliberately removed on 2026-07-16 (`measurement.ts`, `statsPage.ts`, `/api/searches`, `/stats`, `sid`, UTM decoration). Reintroducing measurement requires a separate approved functional/privacy design.

Failures and how to do differently:
- Do not describe the strategy as eliminating legal risk. It reduces centralized circumvention and infrastructure exposure but does not remove operator, database, contract, copyright, competition, privacy, or consumer-law issues.
- Do not threaten stores with removal unless the project is prepared to lose them. Publish a validation-period and post-validation participation policy and apply it consistently.
- Browserless or similar browser infrastructure changes who runs the browser, not who operates the comparator; obtain vendor permission and measure cost before production.

References:
- User proposal: “host a version with open stores, track their clicks… dedicated options (Anroid, desktop)… This will give us the leverage… approach closed stores… do you want to be in?”
- `DISTRIBUTION.md`, `MONETIZATION.md`, `TECHNICAL-SPEC.md`
- Implementation seams: `src/core/browserHub.ts`, `desktop/electronBrowserHub.ts`, `src/core/types.ts`

## Thread `019fb225-3913-7691-b37c-e477eaa3f79c`
updated_at: 2026-07-31T13:42:28+00:00
cwd: /home/Messier82/projects/pellet-price-tracker
rollout_path: /home/Messier82/.codex/sessions/2026/07/30/rollout-2026-07-30T11-30-11-019fb225-3913-7691-b37c-e477eaa3f79c.jsonl
rollout_summary_file: 2026-07-30T08-30-11-pDd2-pellet_price_tracker_source_coverage_and_encrypted_drive_bac.md

description: Pellet tracker evolved from incomplete planning into a deployed FastAPI/SQLite monitoring service with broad Latvian source coverage, Engure delivery modeling, Telegram alerts, and encrypted Google Drive backups; all 152 tests passed and containers were healthy.
task: expand pellet source coverage and implement/verify encrypted off-host backups
task_group: pellet-price-tracker
 task_outcome: success
cwd: /home/Messier82/projects/pellet-price-tracker
keywords: Engure, Latvia, pellet monitoring, graanul.lv, GRAANUL INVEST, ENplus registry, source coverage, FastAPI, SQLite, Telegram, rclone, Google Drive, drive.file, backup, podman, pytest

### Task 1: Source discovery and monitoring architecture

task: expand Latvian pellet source coverage and define reliable Engure orderability
 task_group: pellet market research and tracker architecture
 task_outcome: success

Preference signals:
- When the first source list omitted `graanul.lv`, the user corrected: “Those are not all the options. There are something like graanul.lv aswell” -> future source discovery should start broad and maintain explicit known/candidate/manual/blocked coverage states rather than implying completeness.
- The user wants a tool that checks “all the pellet sources that can do delivery to Engure” and shows status “at a glance” -> dashboard and alerts should expose source health, freshness, delivery evidence, and coverage gaps.

Reusable knowledge:
- Model stock/order acceptance separately from delivery eligibility. Smarto had explicit stock but unverified Engure delivery; Scandbio exposed delivery calculation while stating new orders were paused; Graanul homepage stated new orders were temporarily unavailable.
- Use the ENplus Latvian registry as an authoritative discovery seed. Observed active producers include AKZ, Kurzemes Granulas, GRAANUL INVEST, New Fuels, Scandbio, Staļi, LATGRANULA, and Bitus.
- Normalize delivered EUR/tonne using actual weight, VAT, quantity, and freight. Keep raw evidence and never turn parser/network failure into “out of stock.”
- Planning artifacts: `docs/FUNCTIONAL-SPEC.md`, `docs/ARCHITECTURE.md`, `docs/SOURCE-COVERAGE.md`.

Failures and how to do differently:
- Initial coverage research missed sources and was challenged by the user. Future passes should consult official producer registries, specialist sellers, construction retailers, aggregators, and classifieds before presenting a coverage register.
- Direct curl checks failed with `curl: (6) Could not resolve host` in the restricted environment; use browser/search tooling for research and treat direct network failure as environment-specific.

References:
- `https://enplus-pellets.eu/language/lv/producer-lv/`
- `https://www.graanul.lv/`
- `/home/Messier82/projects/pellet-price-tracker/docs/SOURCE-COVERAGE.md`

### Task 2: Encrypted Google Drive backup implementation

task: add encrypted off-host backup, setup UI, retention, and health notifications
 task_group: backup and operations
 task_outcome: success

Preference signals:
- The rollout added dashboard/setup visibility for backup state and failure/recovery actions -> similar operational features should be user-visible and testable, not silent background jobs.

Reusable knowledge:
- Use `rclone authorize drive --drive-scope drive.file`; OAuth requires a browser-equipped machine because a private LAN callback is unsuitable.
- `src/pellet_tracker/drive_backup.py` stores rclone config/state outside SQLite with mode `0600`; archive contents, filenames, and directory names are encrypted before upload.
- Uploads are append-only, local retention is 14 archives, remote retention is 90 days, and retry attempts are throttled. The encryption password must be kept separately for recovery.
- Telegram sends one backup warning after three consecutive failures and one recovery message after the next successful upload.

Failures and how to do differently:
- First targeted pytest invocation failed because global `pytest` was unavailable (`pytest: command not found`); use `.venv/bin/pytest`.
- Web tests hung in the restricted sandbox but passed when run with local networking available; if FastAPI TestClient stalls, rerun with the repository virtualenv and appropriate local-network permissions.

References:
- `src/pellet_tracker/drive_backup.py`
- `/backup-setup`
- `Dockerfile` installs `rclone`.
- `.venv/bin/pytest -q` -> `152 passed`.

### Task 3: Deployment verification

task: rebuild and verify the running web/worker deployment
 task_group: podman deployment
 task_outcome: success

Reusable knowledge:
- `podman compose build` succeeded; packaged rclone reported `v1.60.1-DEV` and supports `drive.file`.
- Final checks: `/health/live` `{"status":"ok"}`, `/health/ready` `{"status":"ready","database":"ok"}`, `/health/worker` healthy; both containers eventually reported `healthy`.
- Service URL verified at `http://10.82.82.1:8000/backup-setup`. Dashboard is LAN/private only because login protection is not yet implemented.

Failures and how to do differently:
- Immediately after recreation, `podman compose ps` showed `starting`; run configured health checks and wait before judging deployment failure.

References:
- `podman compose up -d --force-recreate`
- `podman compose ps`
- `podman healthcheck run pellet-price-tracker_web_1`
- `podman healthcheck run pellet-price-tracker_worker_1`
- `git diff --check` -> exit 0

## Thread `019fbceb-abdb-7f40-9c7f-6529d8480a03`
updated_at: 2026-08-01T18:03:37+00:00
cwd: /home/Messier82/projects/pellet-price-tracker
rollout_path: /home/Messier82/.codex/sessions/2026/08/01/rollout-2026-08-01T13-43-09-019fbceb-abdb-7f40-9c7f-6529d8480a03.jsonl
rollout_summary_file: 2026-08-01T10-43-09-zezp-add_rootless_podman_quadlet_deployment.md

description: Added rootless Podman Quadlet deployment for separate web/worker services; verified generator, image build, disposable runtime health, and identified prior-data migration risk
 task: add Quadlet host service for pellet-price-tracker
 task_group: pellet-price-tracker deployment
 task_outcome: partial
 cwd: /home/Messier82/projects/pellet-price-tracker
 keywords: podman, quadlet, systemd, rootless, pellet-data, volume, persistence, enable-linger, healthcheck, compose-migration

### Task 1: Add Quadlet host service

task: Create persistent rootless Podman Quadlet services for the web app and worker
 task_group: container deployment
 task_outcome: partial

Preference signals:
- The user requested: “We need to set up the container so that it runs as a service on host. Using Quadlet.” -> future deployment work should provide boot-persistent service units, not only direct `podman run` instructions.
- The user later explained that the previous direct Podman container lost persistence after PC restart and Quadlet started empty -> future agents should inspect and migrate existing container/volume data before creating a new named volume.

Reusable knowledge:
- Added `deploy/quadlet/pellet-price-tracker-data.volume` with `VolumeName=pellet-data`.
- Added separate `pellet-price-tracker-web.container` and `pellet-price-tracker-worker.container`, both mounting `pellet-data:/data`; worker has `Wants=` and `After=` on the web service.
- Both services use `Restart=always`, health checks, `HealthOnFailure=kill`, `NoNewPrivileges=true`, and `[Install] WantedBy=default.target`.
- `deploy/install-quadlet.sh` installs units under the user Quadlet directory, creates `%E/pellet-price-tracker/environment` with mode 0600, builds `localhost/pellet-price-tracker:latest`, reloads, and restarts services.
- `loginctl enable-linger <user>` is required for rootless user services to survive logout and start at boot.
- Quadlet generator validation succeeded; generated commands included the shared `pellet-data` volume and both expected `podman run` commands.
- `podman build --tag localhost/pellet-price-tracker:latest .` succeeded.
- Disposable runtime validation succeeded for web readiness (`{"status":"ready","database":"ok"}`) and worker health; temporary containers and volume were cleaned up.

Failures and how to do differently:
- The environment had no `systemctl`, so actual host installation was not verified.
- The full test suite was not completed: it reached 143/152 tests and hung in `test_dashboard_and_delivery_cost_flow`; it was interrupted. Rerun remaining tests before claiming full green status.
- The initial Quadlet volume label contained spaces and generated invalid split labels. Use labels without spaces or quote/escape them and rerun the generator.
- The exact previous storage was never confirmed because the volume inspection was aborted. Before migration, inspect `podman ps -a`, `podman inspect <old-container>`, `podman volume ls`, and Compose-resolved volume names. Do not assume old data is in `pellet-data`.
- Direct container data may be in the container writable layer or another anonymous/differently named volume. Recreating a container does not recover it automatically.

References:
- `deploy/quadlet/pellet-price-tracker-data.volume`
- `deploy/quadlet/pellet-price-tracker-web.container`
- `deploy/quadlet/pellet-price-tracker-worker.container`
- `deploy/install-quadlet.sh`
- `docs/OPERATIONS.md` section `Quadlet host service`
- Generator command: `QUADLET_UNIT_DIRS="$PWD/deploy/quadlet" /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun`
- Volume inspection: `podman volume inspect pellet-data --format '{{.Mountpoint}}'`
- Destructive operation to avoid without backup: `podman volume rm pellet-data`

