Files
moreminimore-chat/HANDOFF.md
Moreminimore 02ae690a87
Some checks failed
Lock Threads / action (push) Has been cancelled
Frontend Lint & Test / test (push) Has been cancelled
Run Chatwoot CE spec / security-scan (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Sync GHSA advisories to Linear / sync (push) Has been cancelled
docs: handoff state for M1 (openai custom URL) + M2 analytics foundation
Record current session state: rebrand + M1 (per-account openai custom base URL) + M2 analytics foundation
committed and pushed to Gitea. Includes the approved M2 design decisions (daily immutable snapshots,
live re-tag, admin-only report, product catalog, weekly persona eval + approval flow via Line/Telegram/webhook)
and the remaining M2 parts 2b-ii/iii + phases 2-3.
2026-08-19 13:57:27 +07:00

14 KiB
Raw Blame History

HANDOFF

═══════ SESSION 2026-08-19 — Moreminimore Chat: rebrand + M1 + M2 work ═══════

Current state (2026-08-19)

Repo /Users/kunthawat/Gitea/Chatwoot, branch develop, pushed to Gitea. Remote origin = ssh://git@moreminimoreapps-gitea.ahkhwd.easypanel.host:2222/kunthawat/moreminimore-chat (NOT github). SSH push via port 2222 (the HTTPS path git.moreminimore.com gets nginx 413; SSH port 22 is not Gitea SSH). HEAD = ccff2dfca. Worktree clean except untracked .hermes/plans/ (never commit).

Completed this session (all through independent five-key reviews, deployed by user)

  • Rebrand → Moreminimore Chat complete (phases 1-3 + Dockerfile + brand DB fix): f17ce89, d925ab1, 18e6fc28a, bee28c32e, d77d60443, 4b35130ae.
  • Docker/build: root Dockerfile added (4b35130ae) so EasyPanel 'Dockerfile' builder works instead of Nixpacks rbenv stack. nixpacks.toml added then removed (wrong approach).
  • M1 — per-account OpenAI custom base URL (the /app/accounts/{id}/settings/integrations/openai page): commit f9628db0e. Adds optional custom OpenAI-compatible base_url per account (apps.yml schema+form, llm_base_service api_base priority hook.base_url→CAPTAIN_OPEN_AI_ENDPOINT→api.openai.com, key_validator base_url kwarg, hook https validation). NOTE: an earlier wrong-scope Captain-level provider switch (46fe70398) was reverted (961ecd388) — the Captain CAPTAIN_OPEN_AI_ENDPOINT already supports custom URL as original behavior, so no change needed there.
  • M2 — analytics foundation (self-improving chatbot, admin-only):
    • 9fbb0da9b conversation_daily_metrics (per account/day; tags, deal outcomes, peak hours, agent/team/channel/inbox breakdown)
    • 0fad9ce6b customer_daily_metrics (per customer/day; agent_ids)
    • 161e4210b product_catalog_entries (per-account hierarchical product reference: group>subgroup>product+aliases)
    • ccff2dfca lib/llm/resolver.rb (LLM cascade: per-account openai hook → Captain → nil; https-only api_base)
  • DB brand fix done by user on prod (login no longer shows Chatwoot / logo loads).

M2 design decisions (approved by user — IMPORTANT for remaining work)

  • Analyze EVERY chat of EVERY account daily (high LLM cost accepted — selling point is self-improving chatbot).
  • LLM cascade: per-account openai → fallback Captain config → if neither, feature disabled (LLM integration effectively always on unless Captain unset).
  • Use Chatwoot's ORIGINAL conversation tags (ActsAsTaggableOn tag_list) — do NOT add many tag fields. Product/sale tag goes into the same tag list, prefixed (e.g. lines like group>subgroup>product joined, or สินค้า:.../กลุ่ม:...). Daily metric top_tags aggregates from these.
  • No tagged_at (user decided to skip time-tracking to avoid DB complexity).
  • Per-day stats are IMMUTABLE snapshots; conversation live tags get re-tagged on later analysis (e.g. session now about product B → product A tag removed). The daily snapshot preserves the earlier state.
  • Report page admin-role ONLY (agent must NOT see deep analytics). Filter by agent/team/inbox/channel/tag/sale-tag/time + cross-analysis. Break down per CUSTOMER and per AGENT.
  • Weekly (Mon 10:00) LLM summarizes 7 days → persona improvement recommendation (summary only, NOT full prompt). Delivered via preferred channel: Line (has quick-replies) / Telegram (inline keyboard) / webhook (3rd party). Admin chooses approve / view-full-prompt / reject; viewing full prompt → re-approve; on approve → webhook with prompt (admin sets URL+secret).
  • Product list: per-account, admin-only menu (like report), import via copy/paste or CSV/XLSX. Multi-level hierarchy (big group>subgroup>product>display) — LLM matches from lowest level first, tags ancestors; supports multiple products/groups per chat.

NEXT TO DO (M2 remaining — biggest remaining work)

  1. part2b-ii: LLM classifier service — read conversation (latest message + history for context), classify → tags (topic + product from catalog using '>' join) + deal (won/lost/undecided). Cascade via Llm::Resolver.
  2. part2b-iii: daily batch job (sidekiq-cron) at ~02:00-03:00 in the SUPER ADMIN timezone (single TZ, no hourly-check) → auto-write tags to conversation, aggregate into conversation_daily_metrics + customer_daily_metrics (immutable counts; re-tag live).
  3. phase 2: monthly/yearly rollup (sum daily) + admin-only report dashboard (filters agent/team/inbox/channel/tag/sale/time, cross-analysis) + deep per-customer/per-agent views.
  4. phase 3: product-list import UI (admin-only, copy/paste + CSV + XLSX), weekly persona eval + approval flow (Telegram inline button, Line quick-reply, webhook) + docs.

Verification notes / blockers

  • Ruby env active is 2.6.10; target 3.4.4. Rails/RSpec/RuboCop cannot be reliably claimed passed. Ruby ruby -c syntax + YAML validity + isolated smoke tests used instead.
  • Full Vitest baseline is clean under TZ=UTC: 414 files / 4176 tests, 0 failed.
  • Enterprise (EE) files still read CAPTAIN_OPEN_AI_ENDPOINT directly and won't honor per-account base_url gating — intentional (out of OSS scope), note as follow-up.
  • No secrets committed. Temporary test helpers under /tmp only.

───────── historical (2026-08-16, before this session) ─────────

Latest handoff — 2026-08-16 (updated after rebrand + push)

  • Rebrand to Moreminimore Chat complete across all phases (committed): f17ce89 (phase 1 configurable brand core: logo/config/mailer defaults/views), d925ab1 (phase 2a cleanup + 512px thumbnail), 18e6fc28a (phase 2b color theme: black #333333 primary + Moreminimore yellow #FED400 accent), bee28c32e (phase 3 visible surfaces: app.json + mailers + 56 locales + prompts). All phases passed independent five-key reviews (deleg_a733cdf4, deleg_37455fbd, deleg_9b054593, deleg_892d115f).
  • PUSHED to Gitea via SSH port 2222: branch develop (HEAD bee28c32e) now on kunthawat/moreminimore-chat. Remote origin = ssh://git@moreminimoreapps-gitea.ahkhwd.easypanel.host:2222/kunthawat/moreminimore-chat. HTTPS push to git.moreminimore.com is blocked by nginx 413 (client_max_body_size < ~237MB pack); SSH port 22 is NOT Gitea SSH — use 2222. upstream (github.com/chatwoot) removed; Gitea rejects shallow push, so unshallow first. SSH key moreminimore-local-ed25519 added via Gitea API.
  • Earlier checkpoints: 19dc449 SM-07, 2ef6fa5 SM-07 remediation (approved deleg_087e4a3f), 364e72f SM-08, 8101395 residual Hub removal (approved deleg_49d6ee2e + deleg_58a576e1).

Verified checkpoints

  • 8ebb320 — privacy/branding audit harness.
  • 832a7fd — Hub sync/registration/telemetry removal.
  • 1a3697f — restored only the compatibility APIs required by direct push/billing callers.
  • 8b1a033 — removed the Chatwoot Hub push relay; exact post-commit independent review passed.
  • 3458272 — removed cwctl telemetry from the installer; exact post-commit independent review passed.
  • d9bf4c4 — removed community signup/onboarding website-branding enrichment; split exact post-commit reviews deleg_5e2578c5 (backend) and deleg_ee3e217f (frontend) passed with empty blocking arrays.
  • ced77af — SM-06 Product Analytics removal and dashboard config allowlist remediation; pre-commit review deleg_f9644984 and exact post-commit review deleg_9b0a2f63 passed with empty security/logic arrays.

Current work / uncheckpointed work

  • SM-05 community signup enrichment removal is checkpointed at d9bf4c4; pre-commit review, commit hooks, and split exact post-commit reviews passed. The enterprise service/spec remain unchanged and fail with NameError: uninitialized constant WebsiteBrandingService. This remains an explicit architecture/legal blocker; do not add a dummy service or restore remote enrichment.
  • SM-06 is verified at ced77af. Its follow-up fix applies .slice(*GLOBAL_CONFIG_KEYS) before merging app_config, preventing stale/unrequested values such as CLOUD_ANALYTICS_TOKEN from reaching serialized window.globalConfig. SM-07 local-only observability and the remaining SM-08 dashboard/config script-injection work are still uncheckpointed. The community Help Center SM-08 correction is committed as 5619cc3: remove GTM remote-container execution, reject/remove legacy GTM config, and retain only fixed allow-listed provider snippets for explicit admin configuration. Exact post-commit review deleg_dd3db5b7 passed with empty blocking arrays. Enterprise marketing conversion tracking remains outside this community scope pending legal/compatibility review.

Evidence and blockers

  • SM-03 exact post-commit review passed for 1a3697f..8b1a033; reviewer suggestions are to add explicit missing/blank/partial Firebase negative coverage and clarify legacy FCM configuration names in .env.example.
  • SM-04 exact post-commit review passed for 1a3697f..3458272; Bash 3.2 compatibility and fail-closed privacy-test behavior were verified.
  • SM-05 exact committed range 3458272..d9bf4c4 passed split independent reviews: backend deleg_5e2578c5 and frontend deleg_ee3e217f; both returned complete five-key verdicts with empty security and logic arrays. Frontend targeted run passed 9 suites/8 tests. Backend RSpec rerun is environment-blocked because Ruby 3.4.4 is required but only 3.4.10 is installed and the bundle has no usable rspec executable.
  • SM-06 exact committed range a96b977..ced77af changed one controller line and passed fresh pre-commit/post-commit independent review. Raw merge leaked a stubbed analytics token; the allowlist slice now removes unrequested keys before layout serialization. Committed Ruby syntax, diff check, and security scan passed. Rails RSpec/RuboCop remain blocked because the active Ruby is 2.6.10 and Bundler 2.5.16 is unavailable.
  • SM-08 exact post-commit review deleg_dd3db5b7 passed for d9bf4c4..5619cc3; 57 portal model/controller examples, GTM regression 3/3, Ruby/ERB syntax, RuboCop, ESLint/Prettier, Vite build and privacy-audit harness passed. Full Vitest with TZ=UTC passes 414 files / 4176 tests, 0 failed.
  • No push or deploy has occurred.
  • Approved product name, domains, logos/icons and sender/legal values are still missing; stop before SM-09/10.
  • Full Rails/frontend suites, production-like DB checks, HTTP/DNS egress capture, git fetch --unshallow, and upstream merge rehearsal remain outstanding. The full Vitest frontend baseline is clean under TZ=UTC: 414 files / 4176 tests passed, 0 failed (the earlier "407/15 failed" record was a non-UTC run artifact; timezone-sensitive tests all pass under UTC).
  • Temporary test services/configuration stay under /tmp; never commit them or secrets.

Repository baseline

  • Path: /Users/kunthawat/Gitea/Chatwoot
  • Origin: https://github.com/chatwoot/chatwoot.git
  • Branch: develop
  • Initial baseline commit: 9a73c1473ffa0ae6a9c7725046b8ca17922dcc83
  • Latest checkpoint: ced77af (SM-06 Product Analytics plus dashboard config allowlist remediation; exact post-commit review deleg_9b0a2f63 passed). SM-08 Help Center remains separately verified at 5619cc3.
  • Clone is shallow; fetch full upstream history before merge/rebase rehearsal.

Primary deliverable

  • .hermes/plans/2026-08-15_092534-chatwoot-private-rebrand.md
  • Small-model execution index: .hermes/plans/chatwoot-private/README.md
  • The execution package contains 00-execution-contract.md plus runbooks 0112 with 58 unique SM-* tasks. Send only one task at a time and review its exact diff/test evidence before advancing.

Most important finding

DISABLE_TELEMETRY=true is not a complete opt-out. lib/chatwoot_hub.rb still posts instance_config to the Hub; it only omits instance_metrics. The corresponding behavior is asserted in spec/lib/chatwoot_hub_spec.rb.

Other unsolicited/vendor-controlled egress to remove

  • Chatwoot Hub daily sync and onboarding registration
  • Chatwoot Hub /send_push relay fallback
  • Hub-hosted changelog fetch
  • silent cwctl command reporting to the Hub
  • automatic signup email-domain website/DNS branding enrichment
  • Amplitude browser analytics
  • Sentry/frontend/backend remote error reporting
  • optional remote APM agents
  • arbitrary dashboard scripts and remote GTM container execution
  • enterprise marketing conversion tracking (proprietary/cloud-only; pending separate legal/compatibility decision)

Implementation order

  1. Follow dependency order in .hermes/plans/chatwoot-private/README.md.
  2. Establish the baseline and privacy audit harness before behavior changes.
  3. Remove Hub sync/registration/manual refresh, push relay, cwctl telemetry, signup-domain enrichment and remote changelog.
  4. Neutralize/remove analytics, remote error reporting/APM and script injection.
  5. Stop at the Runbook 09 input gate until approved brand values/assets are supplied.
  6. Add central brand defaults and replace visible assets/copy without renaming compatibility APIs.
  7. Run static and isolated dynamic egress gates, positive feature-traffic tests, full review and visual QA.
  8. Complete upstream-history and merge-rehearsal workflow; never mix upstream merge with customization commits.

User decisions required before implementation

  • Product name, canonical/support/docs domains, logos/icons and email sender name
  • Private Gitea repository name
  • Whether to keep deprecated widget API aliases (recommended: yes)
  • Whether local-only logs are sufficient or a self-hosted observability endpoint is required
  • Push notification plan and own FCM/VAPID credentials

Safety/legal notes

  • Root is MIT, but enterprise/ is proprietary; do not ship enterprise features without a separate license.
  • No remote repository was created, no files were pushed and no deployment was performed.