Skip to content

Phase 3a — Governed LINE-group assistant (engineering spec)

  • Date: 2026-06-23
  • Status: Design (implements ADR-0143 §D1–D5; gated on the DPIAdocs/compliance/dpia-line-group-memory.md)
  • Scope (3a): opt-in full-thread storage + on-join consent + KB-grounded Q&A (@mention/command) + on-demand summarization + retention cron. Tasks (3b), slip verification (3c, buy), infographics (deferred) are later.

Hard gate: the message-store tables + the ingest write path must not deploy to prod until the DPIA is signed (ADR-0143 §D1). Everything else (schema scaffolding behind a feature flag, the consent flow, the mention-gated mode) can land earlier.

1. Data model (new — first migration of this work)

Section titled “1. Data model (new — first migration of this work)”
line_groups
id TEXT pk grp_{nanoid}
tenantId TEXT (scoped; P0 invariant)
workspaceId TEXT NULL
lineGroupId TEXT LINE group/room id (unique per tenant)
consentStatus TEXT 'pending' | 'opted_in' | 'opted_out' (storage OFF unless opted_in)
consentBy TEXT NULL LINE userId of the admin who opted in
consentAt INTEGER NULL
mode TEXT 'full_thread' | 'mention_only' (per-group privacy mode, ADR-0143 D1)
retentionDays INTEGER conservative default (DPIA-set)
features TEXT JSON: { qa, summarize, tasks }
enabled INTEGER
createdAt/updatedAt INTEGER
group_messages -- DPIA-GATED; only written when consentStatus='opted_in' AND mode='full_thread'
id TEXT pk
tenantId, groupId TEXT
lineMessageId TEXT
senderId TEXT LINE userId
senderName TEXT NULL cached display name
text TEXT REDACTED on ingest (national id/phone/card)
attachmentsJson TEXT NULL reuse ADR-0043 shape (slip/doc images)
createdAt INTEGER (retention cron deletes by this)
group_summaries
id, tenantId, groupId TEXT
summary TEXT
fromMessageId/toMessageId TEXT
requestedBy TEXT
createdAt INTEGER
  • Indexes: (tenantId, lineGroupId) unique on line_groups; (groupId, createdAt) on group_messages (window reads + retention scan).
  • Enum widening already done (conversations.source/TicketChannel include channel keys); group entities are separate tables, not conversations (ADR-0143 §D4 — multi-participant, not 1:1).
  • LINE join event (bot added) → upsert line_groups (consentStatus=‘pending’) → bot posts the on-join transparency notice (TH/EN: what it stores, retention, opt-out, privacy link).
  • Admin command จดจำ เปิด (opt in) → consentStatus=‘opted_in’, record consentBy/At. Any member จดจำ ปิด/ออก → ‘opted_out’ (storage stops; existing data subject to retention/erasure).
  • leave/memberLeft handled; admin can set mode + retentionDays via command or the admin UI.

3. Webhook branch (reuse the existing LINE handler)

Section titled “3. Webhook branch (reuse the existing LINE handler)”
  • api/channels/line/+server.ts POST: after signature verify, branch on event.source.type:
    • 'user' → existing 1:1 runChannelTurn (unchanged).
    • 'group' | 'room' → new runGroupEvent(event) (new lib/channels/group-turn.ts).
  • runGroupEvent dispatches: join/leave/memberJoined → consent/lifecycle; message → (a) if mention_only or not addressed → store only if addressed; (b) if full_thread + opted_in → redact + store; then if the message addresses the bot (@mention/command) → handle Q&A / summarize.

4. Ingest + redaction (DPIA-gated write path)

Section titled “4. Ingest + redaction (DPIA-gated write path)”
  • On a stored message: run the PII redactor (Workers AI / regex for Thai national id \d{13}, phone, card) → store redacted text. Raw bytes never persisted for slips beyond the ADR-0043 pipeline (vendor verifies in 3c).
  • Write gated: consentStatus==='opted_in' && mode==='full_thread'. Ack-fast + waitUntil (same hot-path discipline as ADR-0133 §D2).

5. Q&A + summarization (reuse shared libs)

Section titled “5. Q&A + summarization (reuse shared libs)”
  • Q&A: addressed message → hybridSearch (tenant KB, persona-scoped) + LLM, reply to the group. Reuse selectChatModel, buildSystemPrompt, the answer-marker parsing.
  • Summarize: จดจำ สรุป [N] → read last N group_messages (or transient fetch in mention-only mode) → LLM summary → store in group_summaries → reply. Bounded N.
  • Extend the existing cron (or new /api/cron/group-retention): delete group_messages where createdAt < now - retentionDays*86400, per group. Redact stale summaries. Idempotent, batched (mind D1 throughput — the overload discipline from the backup/retention crons).
  • Per-group: admin command / admin UI → delete group_messages + summaries + line_groups (or tombstone). Per-member (§33): delete that senderId’s messages + redact their spans in summaries; audit-logged.

8. Build sequence (each validated + deployed like the channels)

Section titled “8. Build sequence (each validated + deployed like the channels)”
  1. Schema + consent flow + mention-gated Q&A (NO full-thread storage) — low DPIA exposure; a narrow pilot is possible if counsel agrees (DPIA §5).
  2. Full-thread storage + redaction + retention cronDPIA sign-off required before this deploys.
  3. Summarization over the stored thread.
  4. Tasks (3b), then slip verification (3c, buy).
  • Audience (internal vs customer-facing) → fixes the default mode + lawful basis.
  • Retention default, redaction coverage, member-notification wording — DPIA.
  • Pricing/quota for a group product (storage + AI credits + group count) — distinct from per-seat support plans.