RFC Review: AI Agent Knowledge — Conversation History (Phase 1)
Companion review for
conversation-history.md, produced by therfc-reviewerskill. Lives beside the RFC; valid only for the RFC revision inreviewed_rfc_last_updated(2026-06-20).
Executive Summary
- Overall Score:
7.5/10 - Rating:
Strong - RFC Type:
full-stack - Sub-Type:
frontend: new-feature · backend: enhancement - Assessment Confidence:
Medium-High - Applied Caps/Gates: No caps triggered. The R1 cross-layer cap (6.5) is lifted — the directory endpoint is no longer an "unverified No" row; it is verified-absent and re-specified as a net-new endpoint with a proposed contract (§2.4 row 6, §2.G row 4), so there is no contract mismatch. Lowest scored category is ACV = 7.0 (≥ 5.0). Deploy order specified → ROL not capped.
- Implementation Readiness Verdict:
HOLD — 4 cross-team/sign-off blockers gate full execution (webhook contract, masking SLA, design frames, scoping sign-off); BE chunks 1–6 + 2b buildable now - Report Path:
chatbot/ai-agent-knowledge/rfcs/conversation-history-review.md - RFC Author: BOT squad (Hadiningbot) | Reviewed: 2026-06-20 (cycle R2)
This is a delta re-review (R2). Although the RFC's last_updated stamp is unchanged (2026-06-20), the body was materially revised in response to the R1 review — the §6 Comment log and §5/§7 explicitly record the changes. All of those changes were re-grounded against the live repos (chatbot fa6dd8b79, chatbot-fe f5b80d5a) and they hold: §2.4 now carries grounded request/response JSON whose field names match the real Grape entities (incl. the qouta typo, verified at usage.rb:11); the divisions+agents directory is confirmed-absent in frontend_service (only internal PATCH /v1/chat/divisions exists, role qontak_chat) and re-specified as a net-new endpoint built from the real Division/AgentsDivision models; and §2.3 now gives concrete partial-unique DDL matching the verified repo pattern (unique_knowledge_store.rb where: 'deleted_at is null'). The biggest strength remains anti-hallucination grounding — every load-bearing claim re-verified TRUE this cycle. The remaining gaps are all cross-team or sign-off items the author has honestly gated §7 to no on: the training-status webhook contract (Data & AI), the masking SLA (Data & AI), the config-form Figma frames (Design), and PM+eng sign-off on division-scoping. The one thing still blocking full agentic execution is the FE design frames + the external Data & AI contracts — the BE half (chunks 1–6, plus the now-specified directory endpoint 2b) is buildable today.
Quick Verdict
Why this RFC can be implemented agentically (the BE half, now incl. directory):
- Backend anchors re-verified against real code with file:line evidence (§2.0 Source Verification): the one-per-company guard at
create_omnichannel_agent_conversation_knowledge_source.rbL29–31 (still nodivision_id/cap),post_knowledge_sources.rb{id,type:'conversation',participant_ids},update_status.rbpoll,schedule.yml0 1 * * * Asia/Jakarta, the un-wired tile atAddKnowledgeDrawer.vue:440. - The §2.4 endpoint contracts now include grounded JSON examples (create-202, list-200, usages-200) whose field shapes match the real entities (
OmnichannelAgentConversationKnowledgeSource={id,agent_id,agent_name,ai_knowledge_source_id};KnowledgeSource={id,name,type:{code,name},status,updated_at,url};Usage.qouta). An agent can now generate FE types and assert responses. - The DDL change is concrete and bounded (one nullable
division_id+ a realadd_index ... where: 'deleted_at IS NULL AND division_id IS NOT NULL'), with deploy order, rollback, and a migrate/rollback test command.
Why this RFC still gates to HOLD (the FE-new-feature + cross-team half):
- The config-form UI (
ConversationHistoryForm.vue, confirmed absent in chatbot-fe) has no Figma frame —n/a — design pending(§2.A, §5 blocker 3). An agent would invent the division-first multiselect, the 15-cap interaction, and the training-status badge states. - The training-status webhook contract is undefined (§5 blocker 1, Decision 6) — chunk 10 correctly marked unbuildable; poll baseline ships.
- Division-scoping (Decision 1) is technically specified now, but the scoping choice still awaits PM+eng sign-off (§5 blocker 5), and the live create use case still only guards one-per-company (re-confirmed: schema has no
division_id).
Findings Ledger (carry-forward)
| ID | Severity | Finding (one line) | RFC location | Status | First seen | Resolved in | Evidence / fix |
|---|---|---|---|---|---|---|---|
REV-1 | blocker | Divisions+agents directory endpoint was an "unverified No" cross-layer row | §2.4 (row 6), §2.G (row 4), §5 blocker 4 | fixed | R1 | R2 (RFC rev) | RFC now states it verified no GET directory exists in frontend_service (re-confirmed: only internal PATCH /v1/chat/divisions at internal_service/v1/chat/division.rb, role qontak_chat); re-specified as net-new GET /v1/divisions(/:id/agents) built from Division/AgentsDivision (both models exist) with a proposed contract + chunk 2b. No longer a mismatch — net-new with a contract. Frame remains design-pending → see REV-2. |
REV-2 | blocker | Config-form Figma frame missing (n/a — design pending); prototype diverges (toggle + fixed 9-agent list, no 15-cap/tooltip/division-first) | §1 Design References, §2.A, §5 blocker 3 | open | R1 | — | Designer (Wulan) to deliver frames for division-first multiselect, 15-cap validation, expertise tooltip, and Training-in-Progress/Error list states. Re-confirmed: qontak-designer prototype files present at cb601ea; ConversationHistoryForm.vue absent in chatbot-fe. |
REV-3 | blocker | Training-status webhook contract undefined (states/payload/retry/auth) | §2.4 inbound table, Decision 6, §5 blocker 1 | open | R1 | — | Data & AI to publish contract (PRD OQ#4, due 2026-07-15); poll baseline ships meanwhile (re-confirmed update_status.rb get_vector_status poll path live) — acceptable interim. |
REV-4 | blocker | PII masking spec/SLA owned by Data & AI; 100%-mask Alpha gate not yet contracted | §3.D, Decision 8, §5 blocker 2 | open | R1 | — | Data & AI to publish masking SLA + entity coverage (PRD OQ#1, due 2026-07-15). External gate; does not block chatbot chunks 1–6/2b. |
REV-5 | major | Source-scoping decision (extend one-per-company → division-scoped) not yet confirmed with PM+eng | Decision 1, §5 blocker 5 | open | R1 | — | Re-confirmed: live code still guards one-per-company only (create_…rb L29–31; schema ai_knowledge_sources L173 has no division_id). Technical spec now concrete (DDL, guard); only PM+eng sign-off on the choice remains before chunk 2/3. |
REV-6 | major | No request/response payload examples for any endpoint; deferred to "chunk 1" | §2.4 (was "Exact … payload examples to be finalized … during chunk 1") | fixed | R1 | R2 (RFC rev) | §2.4 now embeds grounded JSON for create (202), list (200), usages (200). Re-verified against real entities: CreateOmnichannelAgentConversationKnowledgeSource→OmnichannelAgentConversationKnowledgeSource {id,agent_id,agent_name,ai_knowledge_source_id}; KnowledgeSource {id,name,type:{code,name},status,updated_at,url}; Usage.qouta typo confirmed. |
REV-7 | major | Create-endpoint cross-layer match was "partial" — division_id echo + 422 code alignment unconfirmed | §2.G (row 1), §3.A/§3.B | fixed | R1 | R2 (RFC rev + grounding) | Re-grounded: the create entity exposes no division_id — so the answer is "BE does NOT echo division_id", and the RFC's grounded 202 example correctly omits it; §2.G now reads "yes". 422 codes (agent_limit_exceeded/already_exist) are pinned in §3.B and consumed by string in §3.A FE catalog. Match is now consistent, not partial. |
REV-8 | minor | One-per-division uniqueness enforced only in prose, not as concrete DDL | §2.3 | fixed | R1 | R2 (RFC rev) | §2.3 now gives the real migration: add_index :ai_knowledge_sources, %i[company_id division_id ai_knowledge_source_type_id], unique: true, where: 'deleted_at IS NULL AND division_id IS NOT NULL', matching the verified repo pattern (db/migrate/20240724144048_unique_knowledge_store.rb:4). Canonical table clarified as ai_knowledge_sources. |
REV-9 | minor | No quantified FE performance budget (bundle delta, LCP/INP/CLS) | §3 Performance Requirement ("no measurable bundle delta") | open | R1 | — | State a concrete bundle-size budget and CWV targets for the new form component. Unchanged this cycle. |
REV-10 | minor | No distributed trace connecting FE request → API → Data & AI; observability is event+alert only | §3 Monitoring, §12 (PRD) | open | R1 | — | Add a correlation/trace id across create → AI-Service post → status poll for 3am debugging. Unchanged this cycle. |
REV-11 | minor | Create commits DB rows then posts to AI-Service outside the transaction; a crash between COMMIT and post leaves a source in IN_PROGRESS until the next 01:00 sync (≤24h) | §2.D row 1, create use case L33–41 | open | R2 | — | Re-grounded: PostKnowledgeSources is called inside the ChatbotGptRecord.transaction block but its outbound HTTP effect is non-transactional; on rescue the rows roll back but a partial post is possible. State whether create enqueues an immediate post-retry rather than waiting for the daily sync. (Was Open-Question #7 in R1; promoted to a tracked finding.) |
Ledger summary: 6 open (3 blocker / 1 major / 2 minor), 4 fixed this cycle (REV-1, REV-6, REV-7, REV-8), 0 accepted-risk, 1 newly found (REV-11). The three open blockers (REV-2 design, REV-3 webhook, REV-4 masking) are all cross-team; REV-5 is a PM+eng sign-off. Still-open material findings are promoted to Open Questions by id below.
PRD → RFC Traceability Matrix
Forward: every PRD element maps to an RFC section. Reverse: every RFC decision traces to a PRD requirement.
Standard format (PRD exists — v1.2)
| PRD Element | RFC Section | Coverage |
|---|---|---|
| CONVHIST-S01 Source configuration | §2.4 create (extended, +grounded JSON) · §2.A form · Detail 1.C | Full (build-pending design, REV-2) |
| CONVHIST-S02 Remove from agent (unlink) | §2.4 unlink ai_agent_knowledges · Detail 1.C | Full |
| CONVHIST-S03 Delete source (in-use guard) | §2.4 DELETE (extended) · §3.B in_use 422 · Detail 1.C | Full |
| CONVHIST-S04 15-agent cap | §2.A counter · §2.4 contract ≤15 · §3.B agent_limit_exceeded | Full |
| CONVHIST-S05 Select whole division | §2.A division-first multiselect · §2.3 division_id · §2.4 directory (now net-new spec) | Partial — directory frame design-pending (REV-2); endpoint now specified, not unverified |
| CONVHIST-S06 PII masking | §2.D / §2.F.1 step 4 · §3.D · status Error surfacing | Partial — masking owned by Data & AI; SLA open (REV-4) |
| CONVHIST-S07 Quality gate (≥5 msgs) | Detail 1.C (Cross-squad) · §2.F.1 step 3 | Full (Data & AI owns) |
| CONVHIST-S08 Exclude whispers | Detail 1.C · §2.F.1 step 3 · §3.A.1 | Full (Data & AI scope) |
| CONVHIST-S09 Recency prioritization | Detail 1.C · §2.F.1 step 3 | Full (Data & AI scope) |
| CONVHIST-S10 Daily maintenance sync | §2.F worker · §2.2 sync sequence · §4.D ch.5 | Full |
| CONVHIST-S11 Reference room-id link | §3 authz · §2.4 · §4.D ch.9 (Could-Have, may slip) | Partial — Could-Have that may slip; room-permission mechanism described but not file-grounded |
| CONVHIST-S12 Agent deactivation | §2.F · Detail 1.C · §3.A.1 | Full |
| CONVHIST-S13 Text-only | Detail 1.C · §2.F.1 step 3 | Full (Data & AI scope) |
| NEG-1 Hide tile when not Qontak360 | §2.1 branch flow · §3 plan flag · §4.B | Full |
| NEG-2 Agent with 0 qualifying rooms ignored | §3.A.1 Branch & Skip Catalog | Full (Data & AI scope) |
| NEG-3 Block 2nd source per division | §2.3 guard (now concrete DDL) · §2.E collision map · §3.B already_exist | Full (DDL now concrete, REV-8 fixed) |
| NEG-4 Whisper-with-PII excluded | §3.A.1 · §2.F.1 step 4 | Full (Data & AI scope) |
| PRD §12 Observability events | §3 Monitoring & Alerting | Full |
| PRD §13 Success metrics (≥30% adoption) | §1 Success Criteria | Full |
| PRD §6 Quota (15/seat) | §5 Open Questions (OQ#3 — usages quota=1 today, re-confirmed Usage.qouta default) | Partial — flagged unresolved |
Summary: 14 of 19 PRD elements Full (NEG-3 moved Full→Full with concrete DDL), 5 Partial (S05 design, S06 masking, S11 Could-Have, quota), 0 Missing. No RFC decision lacks a PRD driver — Detail 1.B reverse-maps all 10 decisions. Reverse traceability remains exemplary. No scope creep.
Scorecard
Full-Stack Scorecard (18 categories)
| # | Category | Source | Score | Evidence-Based Rationale |
|---|---|---|---|---|
| 1 | PRT — PRD Traceability | Merged | 8.5 | FE: §2.A maps form→AC, Detail 1.A forward/reverse cite composite AC ids · BE: PRD-to-Schema Derivation maps every entity→table.column→endpoint→PRD §; §-coverage table (all 17 §). Bidirectional, no silent drops. Held below 9 by S05/S11 partials. |
| 2 | TDC — Technical Decisions | Merged | 7.5 | 10 decisions in Detail 1.B with named alternatives + rejection reasons. Up from 7.0: Decision 1 now has concrete DDL + guard spec (only PM sign-off pending, not a technical gap); Decision 4 poll-baseline is sound. Weak layer: webhook (Decision 6) is explicitly dangling but deferred-by-design (gates only chunk 10). |
| 3 | CNT — Contract Specificity | FE | 6.5 | TS interfaces present (ConversationHistoryFormProps/State, §2.A); data-fetching strategy stated (§2.B). Up from 6.0: the directory read shape the multiselect consumes is now a concrete proposed contract, and create/list responses are exemplified. Still held below 7 by the missing config-form design frame (REV-2) — the agent has the data contract but not the visual/interaction spec. |
| 4 | SCB — Scope Boundaries | FE | 8.5 | §2.I names exact files in scope and explicit NOT-touched (PDF/URL/text flows, Inbox Copilot render, auto_train_agent_conversation). Re-verified auto_train still rails_admin-only (rails_admin.rb:189). |
| 5 | DEP — Dependencies | FE | 7.5 | §1 Dependencies table: owner/deliverable/blocking/status per dep. Up from 7.0: the directory endpoint moved from "to confirm" to a specified BOT-owned net-new endpoint (chunk 2b) grounded in real models, removing the unvalidated-consumed-dependency drag. |
| 6 | NFS — Non-Functional Specificity | FE | 6.5 | §3.E a11y concrete (keyboard nav, aria-live counter, aria-describedby, focus trap, color-independent badge). Performance still qualitative ("< 100ms drawer", "no measurable bundle delta") — no quantified budget (REV-9). |
| 7 | TPS — Test Plan Specificity | FE | 7.5 | §4.C names exact commands (pnpm test vitest, pnpm test:e2e playwright); §4.D per-chunk acceptance criteria verifiable. Could name more E2E scenarios but the spine is there. |
| 8 | DMS — Data Model & Schema | BE | 8.0 | §2.3 gives real DDL for the additive change incl. the concrete partial-unique index (REV-8 fixed), matching the verified unique_knowledge_store.rb pattern; cardinality, PII classification per column, retention. Canonical table (ai_knowledge_sources) now disambiguated. Up from 7.5. |
| 9 | ACV — API Contract & Versioning | BE | 7.0 | §2.4 tables give method/path/auth/status-codes/idempotency/versioning plus grounded request/response JSON verified against real entities (REV-6 fixed); directory endpoint now specified (REV-1 fixed). Up from 5.5. Held at 7.0 (not higher) because the directory endpoint contract is proposed (net-new, not yet built) and per-422 example bodies are catalogued in §3.B rather than shown as full JSON per code. |
| 10 | DIC — Data Integrity & Consistency | BE | 7.5 | §2.D matrix strong: transaction scope (ChatbotGptRecord.transaction), partial-failure behavior, idempotency keys, consistency (strong DB / eventual vectors), duplicate handling. §2.E collision map covers two-admin and sync-vs-delete races. Held at 7.5 by the non-atomic COMMIT→AI-post window (REV-11). |
| 11 | FMC — Failure Mode Coverage | Merged | 7.5 | FE: §3.C error message catalog with exact strings + surface; §2.C UI-state matrix per surface. BE: §3.B error response catalog (code/HTTP/message/when) + §3.A merged catalog confirms FE↔BE codes consistent. Cross-layer check passes (FE handles the specific 422 codes BE returns). Async retry/DLQ in §2.F (sidekiq retry:3, dead set). |
| 12 | CSS — Concurrency & Scaling | BE | 6.5 | §2.E collision map + §2.F concurrency (queue application_maintenance, retry policy, per-source isolation). No QPS/pool numbers, but correctly not performance-scoped (heavy work in Data & AI service); bounded load (≤1 source/division). |
| 13 | SAS — Security & Authorization | BE | 7.5 | §3 Role×Endpoint matrix, ownership check (company_id == current_user['company_id']), per-field validation, parameterized AR, fixed AI-Service URL (no SSRF), secrets via env/Vault, PaperTrail. Re-verified all endpoints carry set_role(%w[owner supervisor admin]). |
| 14 | ROL — Rollout & Rollback | Merged | 7.5 | FE: single SystemPreference flag (default OFF), staged Alpha→Beta→GA gates. BE: nullable-column migration, rollback recipe (§4.E). Cross: deploy order specified (BE first, rationale) + full §4.A matrix incl. the one "no" (frontend-first) explicitly avoided. Up from 7.0: webhook coordination clearer (poll is the committed baseline; webhook strictly additive). |
| 15 | OBS — Observability | Merged | 7.5 | FE: analytics events with exact names+properties (§2.A, PRD §12). BE: Rollbar on exceptions, reused Sidekiq queue-latency metric, named alert thresholds (3× fail → Slack), dashboard owner. No distributed trace across FE→API→Data&AI (REV-10) keeps it off 8.5+. |
| 16 | SBC — Service Boundary & Coupling | BE | 8.0 | §2.F.1 Responsibility Boundary Matrix: 8 steps, each with owning squad/service, trigger, effect, failure handler, PRD anchor. Crisp chatbot-vs-Data&AI boundary; no new sync cross-service dependency. |
| 17 | CPA — Pattern Alignment | Merged | 8.0 | FE: §2.0 Patterns-to-Follow names exact files to mirror (AddKnowledgeDrawer.vue, knowledge-list.vue MpBadge, ai-assist.ts $apiMain), one honest deviation (no i18n). BE: Grape/use-case/repo/worker/serializer patterns each cite a reference file. Cross: snake_case API consumed directly, existing ErrorException envelope — consistent. All re-verified live. |
| 18 | CDG — Compliance & Data Governance | BE | 6.5 | Triggered (PII). §3.D classifies each field (raw chat = customer PII transient; masked vectors = de-identified rolling-90d; agent/division ids = internal), cites UU PDP + consent, encryption at rest/transit, right-to-delete. Solid framing, but masking-correctness SLA owned by Data & AI and open (REV-4); cross-border vector-store jurisdiction still unaddressed. |
Resource & Cost Advisory (non-blocking)
- §4.F: negligible compute (one daily off-peak worker), one nullable column + partial index, AI-Service calls already in use, masked vectors stored in Data & AI's store (not chatbot). Adequate advisory note; no readiness impact.
Decision Closure Assessment
Decision Index
| # | Decision | Status | Critical Gaps |
|---|---|---|---|
| 1 | Source scoping → division-scoped (division_id + one-per-division) | Partial | Needs PM+eng sign-off (§5 blocker 5). Technical spec now complete — unique index DDL concrete (REV-8 fixed). Live code re-confirmed one-per-company only. |
| 2 | Storage: reuse existing tables | Resolved | none |
| 3 | Ingest sync: async daily sidekiq-cron | Resolved | none (grounded in existing cron pattern) |
| 4 | Status propagation: poll baseline + webhook later | Resolved | poll buildable now; webhook deferral explicit and sound |
| 5 | Vector indexing: reuse AI-Service type:'conversation' | Resolved | verified live (post_knowledge_sources.rb, update_status.rb) |
| 6 | 15-agent cap: both FE + BE | Resolved | none |
| 7 | Delete semantics: hard-delete vectors only when not-in-use | Resolved | none |
| 8 | PII masking ownership: Data & AI | Partial | masking SLA/spec open (REV-4); decision sound, dependency contract not closed |
| 9 | Plan/tier gating: reuse SystemPreference | Resolved | none (grounded knowledge_stores/create.rb:36) |
| 10 | FE source-type wiring: wire existing tile | Resolved | none (tile re-verified at AddKnowledgeDrawer.vue:440, excluded from active sourceTypeOptions) |
| 11 | Directory endpoint: net-new GET /v1/divisions(/:id/agents) from Division/AgentsDivision | Resolved | none technical (was REV-1 blocker; now specified with proposed contract + chunk 2b; models verified to exist). Design frame still pending (REV-2). |
| (impl.) | Training-status webhook contract | Dangling | states/payload/retry/auth undefined (Decision 6 / §5 blocker 1, REV-3) |
Aggregate: 8 of 12 Resolved, 2 Partial (Decision 1, Decision 8), 1 Dangling (webhook). Decision 11 (directory) moved Dangling→Resolved this cycle. The dangling webhook does NOT block the baseline path (poll is the chosen baseline) — it blocks only chunk 10.
Decision: 1 — Source scoping → division-scoped
Status: Partial (technical spec complete; awaits PM+eng sign-off)
What was decided
Extend the existing source to division-scoped: add division_id to ai_knowledge_sources (canonical = the source row, not the join table) and enforce one-source-per-(company, division, type) (Detail 1.B Decision 1; §2.3).
Alternatives considered
Keep existing one-per-company scoping — rejected because PRD §6/S05/NEG-3 mandate one-Division-one-source; current code only guards one-per-company. Documented with a code-grounded reason.
Grounding in existing code
Strong and re-verified this cycle: the current guard is Repositories::Gpt::AiKnowledgeSources::FindBy({company_id, ai_knowledge_source_type_id}) at create_omnichannel_agent_conversation_knowledge_source.rb L29–31 (one-per-company); db/chatbot_gpt_schema.rb L173 confirms ai_knowledge_sources has no division_id and no length cap. The agent knows exactly what exists.
Interface specification
Now complete: §2.3 gives ADD COLUMN division_id :string + the concrete add_index :ai_knowledge_sources, %i[company_id division_id ai_knowledge_source_type_id], unique: true, where: 'deleted_at IS NULL AND division_id IS NOT NULL', name: 'index_ai_knowledge_sources_on_company_division_type' — matching the verified repo pattern (db/migrate/20240724144048_unique_knowledge_store.rb:4, where: 'deleted_at is null'). REV-8 fixed.
Failure handling
Covered: §2.E collision map (two admins → 2nd insert 422 already_exist) and §3.B error catalog.
Challenge results
- Scale: Holds — one row per (company, division) is bounded.
- Reversibility: Low risk — nullable column, backfill-then-enforce; rollback
rails db:rollback(§4.E). - Consistency: Consistent with NEG-3 and the §2.D integrity matrix.
- Agent implementability: The DDL and guard are now fully implementable; only §5 blocker 5 (PM+eng confirmation of the scoping choice) should hold chunk 2/3.
Gaps and suggestions
Missing: PM+eng confirmation of the scoping choice.
Suggested resolution: Record the sign-off in §5; the technical spec needs no further work.
Open questions: None remaining on the technical interface (the R1 "which table" ambiguity is resolved → ai_knowledge_sources).
Decision: 11 — Divisions+agents directory endpoint (net-new)
Status: Resolved (technical); design frame pending (REV-2)
What was decided
Build a net-new GET /v1/divisions + GET /v1/divisions/:id/agents from the existing Division/AgentsDivision models for the config modal (§2.4 row 6, chunk 2b).
Alternatives considered
Proxy the Omnichannel directory — noted as an alternative in §2.4. The "is there already a GET directory?" question was resolved by verification.
Grounding in existing code
Re-verified TRUE this cycle: there is no GET divisions endpoint anywhere under app/api/frontend_service/; the only divisions route is internal PATCH /v1/chat/divisions with set_role(%w[qontak_chat]) at app/api/internal_service/v1/chat/division.rb. The Division and AgentsDivision models both exist (app/models/division.rb, app/models/agents_division.rb; AgentsDivision belongs_to :agent, :division). So the net-new build is well-grounded.
Interface specification
Proposed response { data:[{division_id, name, agents:[{id,name}]}] }, set_role(owner/supervisor/admin) + company scope, 200/403. This is a proposed contract (BOT owns it; not yet built) — sufficient for an agent to implement chunk 2b, which is why ACV is 7.0 rather than higher.
Failure handling
§3.A directory-load failure → FE load error + Retry, Save disabled (ERR-1); BE 4xx. Codes consistent.
Challenge results
- Agent implementability: Yes for the BE endpoint (chunk 2b) — models and pattern exist. The FE multiselect that consumes it still needs the Figma frame (REV-2).
Gaps and suggestions
Missing: the design frame for the consuming multiselect (REV-2). Suggested resolution: Build chunk 2b now; gate the FE chunk 7 form on the Figma frame.
Decision: 8 — PII masking ownership (Data & AI)
Status: Partial
What was decided
All masking lives in the Data & AI pipeline; chatbot never masks (Decision 8; §3.D; §2.F.1 step 4).
Alternatives considered
Mask in chatbot — rejected for team-boundary + isolated-service reasons (PRD §6/§15). Documented.
Grounding in existing code
The chatbot-side contract is grounded (posts participant_ids, receives status). Masking internals are out of chatbot's repo by design — correct.
Interface specification
The boundary is specified; the masking SLA itself (100% standard phone/email, entity coverage) is not contracted — PRD OQ#1, open, owned by Data & AI.
Failure handling
On masking failure: status → FAILED, prior index retained, alert (§2.2 failure sequence, §3.A).
Challenge results
- Agent implementability: chatbot half implementable; the compliance gate is external and unconfirmed, so GA cannot proceed until the SLA is met (Internal Alpha gate, PRD §14).
Gaps and suggestions
Missing: Data & AI masking SLA + entity-coverage spec. Suggested resolution: Treat as an external dependency gate (REV-4); does not block chatbot chunks 1–6/2b but blocks Alpha exit.
UI State Audit
| Component | Loading | Empty | Error | Partial | Success | Assessment |
|---|---|---|---|---|---|---|
| Config form (division/agents) | defined | defined | defined | defined | defined | 5/5 (§2.C) — but design-pending (REV-2) |
| 15-cap counter | n/a | defined | defined | defined | defined | 4/4 applicable (§2.C) |
| Resources list | defined | defined | defined | defined | defined | 5/5 (§2.C) |
| Status badge | n/a | n/a | defined | defined | defined | 3/3 applicable (§2.C) — re-verified knowledge-list.vue MpBadge red/orange/green |
| Source detail | defined | n/a | defined | n/a | defined | 3/3 applicable (§2.C) |
Summary: All 5 surfaces define every applicable state in §2.C. Caveat unchanged: config form and training-status badge states have no Figma frame (REV-2) — "defined" means defined-in-prose, not design-resolved.
Performance Budget Check
| Metric | Target | Current Baseline | Source | Assessment |
|---|---|---|---|---|
| LCP / INP / CLS | not stated | not stated | — | missing |
| Bundle size delta | "no measurable delta beyond one form component + Pixel imports" | not stated | §3 | vague — not a numeric budget |
| Drawer interaction | "< 100ms" | not stated | §3 | target without a measurement method |
NO QUANTIFIED PERFORMANCE BUDGET for CWV/bundle (REV-9). Modest gap given the small surface.
Accessibility Review
| Aspect | Specified? | Details | Assessment |
|---|---|---|---|
| Keyboard navigation flow | yes | division select + agent checkboxes (§3.E, §2.A) | adequate |
| Focus management | yes | focus trap in drawer (§3.E) | adequate (no focus-return-on-close stated) |
| ARIA labels | yes | counter aria-live="polite"; validation aria-describedby (§2.A) | adequate |
| Heading hierarchy | no | not addressed | minor gap |
| Color contrast | partial | "color-independent status (badge text + color)" (§3.E) | adequate intent; no ratios |
| Motion sensitivity | no | not addressed | minor gap |
| Screen reader behavior | partial | implied via aria-live | thin |
Above-average a11y for a draft; named ARIA hooks are directly implementable.
Pattern Alignment Check
| Pattern | RFC Approach | Assessment |
|---|---|---|
| FE state management (Pinia) | follows (store/ai-assist action) | aligned (§2.0) |
FE component (<script setup> + Pixel) | follows AddKnowledgeDrawer.vue | aligned |
FE API client ($apiMain) | follows ai-assist.ts (re-verified $apiMain(endpoint.v1.ai_assist.*)) | aligned |
| FE i18n | deviates — hard-coded strings (no i18n framework) | honestly flagged; acceptable |
| BE Grape/use-case/repo/worker | follows, "deviation: none" with reference files | aligned (§2.0) |
| Analytics events | preserved/extended per PRD §12 | aligned |
No silent parallel systems. The one deviation (no i18n) is explicit and matches repo reality.
Data Integrity Deep-Dive
| Write Path | Transaction Scope | Partial Failure Behavior | Idempotency Key | Consistency Guarantee | Duplicate Handling |
|---|---|---|---|---|---|
| Create source + agent rows | ChatbotGptRecord.transaction (rows atomic); AI-Service post inside the block but non-transactional HTTP | rollback rows → 422; AI-Service retry via sync | one-per-division guard | strong (rows) / eventual (vectors) | dup → 422 already_exist |
| Daily sync re-post | per-source, no cross-source trx | one source fails → others proceed + alert | source_id + sync date | eventual (Data & AI store) | re-post upsert by participant_ids |
| Delete source | soft-delete row + vector hard-delete | vector-delete fail → keep soft-deleted, retry | source_id | eventual | re-delete no-op |
Strong (§2.D). Re-grounded subtlety (REV-11): PostKnowledgeSources is invoked inside the ChatbotGptRecord.transaction block (create_…rb L33–41), but the outbound HTTP post is not part of the DB transaction — a crash between COMMIT and a successful post can leave a committed source with no vector posted until the next 01:00 sync (≤24h). The RFC's answer is "retry via daily sync," reasonable but worth a sentence on whether create enqueues an immediate post-retry.
Concurrency Collision Map
| # | Shared Resource | Writers | Collision Scenario | Resolution Mechanism | Lock Failure Behavior | Assessment |
|---|---|---|---|---|---|---|
| 1 | ai_knowledge_sources (one/division) | two Admins, same division | both pass pre-check, both insert | DB partial-unique guard (Decision 1) — now concrete DDL | 2nd insert → 422 already_exist (NEG-3) | adequate — index DDL now concrete (REV-8 fixed) |
| 2 | A source row | daily sync worker + Admin delete | sync re-posts a deleting source | worker checks deleted_at before post | sync no-ops | adequate |
| 3 | status field | poll + future webhook | both write status | last-writer-wins on updated_at; webhook authoritative | idempotent by terminal status | adequate (forward-looking) |
§2.E is solid. The enforcement collision #1 depends on (the unique index) is now concrete DDL — collision resolution holds.
API Contract Completeness Check
| Endpoint | Request Schema | Response Schema | Error Taxonomy | Auth Spec | Idempotency | Example Payloads | Assessment |
|---|---|---|---|---|---|---|---|
POST …/omnichannel-agent-conversations | partial (fields named, no per-field validation rule) | complete (grounded JSON, entity-verified) | complete (§3.B) | specific (set_role) | defined (one-per-division) | yes (create-202) | 5/6 |
GET …/omnichannel-agent-conversation/:id | complete (path) | partial (shape named, no full example) | partial | specific | n/a | no | 3/6 |
GET …/ list | partial (filters) | complete (grounded JSON, entity-verified) | partial | specific | n/a | yes (list-200) | 4/6 |
DELETE :id | complete (path) | partial | complete (in_use) | specific | n/a | no | 4/6 |
GET …/usages | complete | complete (grounded JSON; qouta typo verified) | partial | specific | n/a | yes (usages-200) | 4/6 |
| divisions+agents directory | partial (proposed) | partial (proposed) | partial | specific | n/a | partial | 3/6 — now a specified net-new endpoint (REV-1 fixed) |
Remaining ACV uplift (to push 7.0 → 8.5): add per-field request validation rules (id format, name length) and full example bodies per error code (422 each, 403) and for detail/delete success; once the directory endpoint is built, replace its proposed contract with the verified shape. The grounded create/list/usages examples already closed the largest R1 gap.
Async Job / Event Consumer Spec
| Job/Consumer | Trigger | Input Shape | Retry Policy | DLQ | Concurrency Limit | Idempotency Key | Timeout | Assessment |
|---|---|---|---|---|---|---|---|---|
ConversationHistoryDailySyncWorker | sidekiq-cron 0 1 * * * Asia/Jakarta | specified (queries active sources) | specified (retry: 3) | named (Sidekiq dead set) | specified (queue application_maintenance) | specified (source_id + date) | specified (per AI-Service client) | 7/7 |
ConversationHistoryStatusSyncWorker (poll) | periodic while any IN_PROGRESS | specified (company_id) | specified (retry: 1) | n/a (rescues) | low | specified (terminal-status) | specified (client) | 6/6 applicable |
| training-status webhook consumer | inbound POST | partial ({company_id,status,...}) | per contract (unknown) | per contract | n/a | specified (company_id,source_id,status) | n/a | BLOCKED (REV-3) |
The two BOT-owned workers are fully specified and grounded — an agent can write them (cron pattern + application_maintenance queue re-verified in schedule.yml). The webhook consumer is correctly marked unbuildable until OQ#4.
Compliance Trigger Check
| Trigger | Found? | Data Location | Classification | Assessment |
|---|---|---|---|---|
| PII (email, phone, address, financial, national-ID) | yes | raw chat pre-mask (transient) | customer PII | handled — masked by Data & AI before indexing; not persisted post-processing (§3.D) |
| Payment data | yes (within chat) | raw chat | redacted to tag | handled via masking (§3.D, PRD S06) |
| Health data | no | — | — | N/A |
| User content with retention | yes | masked vectors | de-identified, rolling 90d | handled — purge on sync + hard-delete on source delete |
| Auth/session data | yes | company_id/organization_id scoping | internal | handled — PaperTrail audit |
| Cross-border data transfer | not addressed | — | — | gap — RFC does not state vector-store jurisdiction. UU PDP relevant. |
CDG Status: Active — scored 6.5. Framing good; masking-correctness SLA external/open (REV-4); cross-border storage location unaddressed.
Cross-Layer Contract Verification
| Endpoint | Backend Response Schema | Frontend Expected Schema | Match? | Gaps |
|---|---|---|---|---|
POST …/omnichannel-agent-conversations | { status, code, message, data:[{id, agent_id, agent_name, ai_knowledge_source_id}] } (entity-verified) | form sends {division_id, agents[]}; reads data[].id | Yes | BE does not echo division_id (entity has no such field) — RFC example correctly omits it; 422 codes pinned in §3.B and consumed by string in §3.A (REV-7 fixed) |
GET …/ list | { id, name, type:{code,name}, status, updated_at, url } (entity-verified) | status enum + updated_at | Yes | confirm ACTIVE/IN_PROGRESS/FAILED ↔ badge mapping (badge logic verified in knowledge-list.vue) |
DELETE :id | { status, code, message } / 422 in-use | dialog expects in-use code | Partial | define in_use error code/message envelope (covered in §3.B) |
| divisions+agents directory | proposed { data:[{division_id, name, agents:[{id,name}]}] } | division-first multiselect | net-new (specified) | endpoint is BOT-owned net-new (chunk 2b); models verified to exist — no mismatch (REV-1 fixed) |
Checks performed:
- Field name casing consistent — snake_case API consumed directly via
$apiMain; no transform needed. - Nullability — list
urlnullable (entity default present); create response omitsdivision_id(not exposed) — now explicit. - Error response shape matched — existing
ErrorExceptionenvelope; §3.A confirms FE↔BE code consistency. - Pagination format aligned — reuses existing offset list (
meta:{page,limit,total_data,...}). - [~] Auth token format —
$apiMaincarries session; consistent with existing.
Mismatches found: 0 hard. (R1's "No — unverified directory" row is resolved to a specified net-new endpoint; the create row is now "Yes.") The R1 6.5 cap is lifted. One "partial" remains (delete in-use envelope, covered in §3.B).
Cross-Layer Rollout Compatibility Matrix
| Scenario | Frontend | Backend | Works? | Notes |
|---|---|---|---|---|
| Pre-deploy (baseline) | Old | Old | Yes | tile un-wired (re-verified excluded from sourceTypeOptions) |
| Backend first | Old | New | Yes | new column nullable; old FE never sends division_id |
| Frontend first | New | Old | No | FE needs BE to accept division_id — explicitly avoided |
| Both deployed (target) | New | New | Yes | target |
| Backend rollback | New | Old (rolled back) | Partial | flag OFF hides FE tile; FE degrades to no-conv-history |
| Frontend rollback | Old (rolled back) | New | Yes | BE accepts but no UI creates; harmless |
Deploy order: Backend first (specified, with rationale — §4 Rollout Strategy + §4.A). Incompatible scenarios: 1 ("Frontend first") — addressed by the deploy-order rule. ROL not capped at 6.0. Model rollout section.
End-to-End Data Flow
Flow: Create a Conversation History source
Admin selects division + ≤15 agents
→ FE: ConversationHistoryForm → store/ai-assist ADD_CONVERSATION_HISTORY → ai-assist.ts add_conversation()
→ API: POST /v1/gpt/ai_knowledge_sources/omnichannel-agent-conversations { division_id, agents[≤15] } (company_id from session)
→ BE: set_role → CreateOmnichannelAgentConversationKnowledgeSource (validate ≤15, division-not-sourced)
→ DB: BEGIN; INSERT agent-conversation rows; COMMIT (+ AI-Service post in-block, non-transactional)
→ Side effects: PostKnowledgeSources → AI-Service { id, type:'conversation', participant_ids }; analytics conv_history_config_save; PaperTrail audit
→ Response: 202 { status, code, message, data:[{id, agent_id, agent_name, ai_knowledge_source_id}] } (no division_id echoed)
→ FE: refetch list → row renders "Training in Progress" (orange MpBadge)
→ (async) StatusSync poll → get_vector_status → success → UPDATE status=ACTIVE, updated_at=now
→ FE: next list refetch → "Active" (green) + Last Updated
Gaps in flow: (1) The divisions+agents read that precedes this flow (to populate the multiselect) is now a specified net-new endpoint (chunk 2b) — no longer an unknown shape (REV-1 fixed). (2) The COMMIT→AI-Service-post window is non-atomic; recovery relies on the daily sync, which could delay post by up to 24h (REV-11). Otherwise the path is fully traceable from the RFC alone, and the response body is now entity-verified — a real strength.
Agentic Readiness Deep-Dive
Vague Word Audit
| # | Word/Phrase | Location | Impact | Concrete Replacement |
|---|---|---|---|---|
| 1 | "no measurable bundle delta" | §3 Performance | agent skips bundle budgeting | "≤ 15KB gzip delta for the new form chunk" |
| 2 | "follow AiService client" (timeout) | §2.F | agent guesses timeout value | state the numeric timeout |
| 3 | directory contract "(proposed)" | §2.4 row 6 | agent builds against a proposed (not verified-built) shape | acceptable for net-new; lock the shape in chunk 2b |
Total vague words in spec sections: ~3 material (down from ~4 in R1 — the "finalize during chunk 1" payload deferral and the "confirm/extend directory route" guess are both gone).
Dangling Alternatives
| # | Alternatives | Location | Impact |
|---|---|---|---|
| 1 | UUID-pattern "or" HMAC webhook auth | §2.4 inbound | dangling — but inside the already-blocked chunk 10 |
Total dangling alternatives: 1 live (webhook auth), confined to the blocked chunk. (R1's poll-vs-webhook is resolved by design.)
Task Decomposition Assessment
| Chunk | Acceptance Criteria | Assessment |
|---|---|---|
| 1 BE confirm contracts | "existing specs green (baseline)" | verifiable |
| 2 BE migration | "column + index present; rollback clean" | verifiable |
| 2b BE directory endpoint | "returns divisions + nested agents, company-scoped, set_role enforced" | verifiable — now buildable (models exist) |
| 3 BE create extend | "16 agents→422; dup division→422; valid→202" | verifiable |
| 4 BE delete guard | "in-use→422; unused→vectors deleted" | verifiable |
| 5 BE daily sync | "re-posts per ACTIVE source; failure isolated + alert" | verifiable |
| 6 FE service+tile | "tile selectable; service hits create endpoint" | verifiable |
| 7 FE config form | "counter 0–15; 16th disables Save" | verifiable but design-blocked (REV-2) |
| 8 FE resources list | "badges per status; delete in-use blocked" | verifiable but design-blocked (REV-2) |
| 9 reference link | "cross-division open → 403" | verifiable (Could-Have) |
| 10 webhook | "only when OQ#4 published" | correctly gated (REV-3) |
§4.D is a strong, verifiable task manifest. Chunks 1–6 + 2b are agent-ready now; 7–8 wait on design; 10 waits on contract.
Strengths
- Anti-hallucination grounding (§2.0 Source Verification), re-verified at HEAD
fa6dd8b79/f5b80d5a. Every backend anchor re-checked TRUE this cycle: the one-per-company guard (create_…rbL29–31),participant_idspost (post_knowledge_sources.rb), poll (update_status.rb), cron (schedule.yml), un-wired tile (AddKnowledgeDrawer.vue:440, excluded from activesourceTypeOptions), and the new grounded JSON examples that match the real Grape entities (incl. theqoutatypo atusage.rb:11). This is the single most valuable property for agentic execution. - Responsive to R1 review (§6 Comment log + §5/§7). Four R1 findings were closed by genuine RFC edits — payload examples (REV-6), directory endpoint specification (REV-1), one-per-division DDL (REV-8), create cross-layer match (REV-7) — and all were independently re-grounded against code this cycle. This is exactly the open→fixed improvement the cumulative-review discipline is designed to capture.
- Honest readiness gating (§5 + §7). §7 stays
nowith the remaining cross-team blockers enumerated and owners named, rather than over-claiming. Decision 4 ships the poll baseline and defers the webhook. This is why HOLD (not BLOCKED) is correct, and the score rises into Strong. - Rollout rigor (§4.A + §4 Rollout Strategy). Deploy order specified with rationale, full compatibility matrix, single-flag coordination, deploy-order-aware rollback recipe.
Biggest Gaps
- Config-form + training-status design is DRAFT with missing frames (§1 Design References, §2.A, §5 blocker 3). The only located UI is a throwaway prototype that diverges from the PRD (toggle + fixed 9-agent list; no division-first multiselect, no 15-cap, no tooltip, only Active/Inactive). Chunks 7–8 cannot be built against it. This is now the single biggest blocker (the data contracts are resolved; the visual/interaction spec is not). (REV-2)
- Training-status webhook contract undefined (§2.4 inbound, Decision 6, §5 blocker 1). Chunk 10 correctly unbuildable; poll baseline ships. External (Data & AI, OQ#4). (REV-3)
- Masking SLA owned by Data & AI and open (§3.D, Decision 8, §5 blocker 2). The actual compliance gate (100% standard phone/email) is contracted elsewhere and unconfirmed; blocks Alpha exit, not chatbot chunks. (REV-4)
Priority Actions
- §5 blocker 3 / §2.A — config-form + status-badge Figma frames — Designer delivers frames for the division-first multiselect, the 15-cap validation, the expertise tooltip, and the Training-in-Progress/Error list states. This is now the top lever (the data contracts are resolved). Unblocks chunks 7–8 (REV-2).
- Decision 1 / §5 blocker 5 — PM+eng sign-off on division-scoping — The technical spec (nullable
division_id+ concrete partial-unique index) is complete; only the scoping choice needs confirmation before chunk 2/3 land (REV-5). - §2.4 inbound / Decision 6 — track webhook contract as an external gate — Keep chunk 10 deferred; confirm Data & AI publishes states/payload/retry/auth (OQ#4, due 2026-07-15). Do not let it block chunks 1–6/2b (REV-3).
- §2.4 ACV polish (optional, lifts 7.0→8.5) — Add per-field request validation rules and full example bodies per error code (422/403); once chunk 2b ships, replace the directory's proposed contract with the verified shape. Add an immediate AI-Service post-retry on create rather than waiting up to 24h for daily sync (REV-11).
Backend Contract Addendum
Endpoint Contract Details
| Endpoint | Method/Path | AuthZ | Request Contract | Response Contract | Error Contract | Idempotency/Versioning | Status |
|---|---|---|---|---|---|---|---|
| Create | POST /v1/gpt/ai_knowledge_sources/omnichannel-agent-conversations | set_role(owner/supervisor/admin) + company_id from session | { division_id (NEW), agents:[{id,name}] (≤15 NEW) } — missing: per-field validation (id format, name length) | { status, code, message, data:[{id, agent_id, agent_name, ai_knowledge_source_id}] } (entity-verified; division_id NOT echoed) | complete (§3.B: agent_limit_exceeded/already_exist/not_found) | one-per-division guard; v1 | Mostly complete — missing only per-field request validation rules |
| Detail | GET …/omnichannel-agent-conversation/:id | same | path :id | { data:[{agent_id, agent_name}], message, status_code } (built in use case) | partial (404/403) | n/a; v1 | Missing [full example] |
| List | GET …/ | same | filters incl. source_type | { status, code, message, data:[{id,name,type:{code,name},status,updated_at,url}], meta:{...} } (entity-verified) | partial | offset; v1 | Complete (grounded example) |
| Delete | DELETE :id | same | path :id | { status, code, message } | complete (in_use 422) | n/a; v1 | Missing [success/error full example] |
| Usages | GET …/usages | same | — | { ..., data:[{type, usage, qouta, is_available}], meta } (entity-verified; qouta typo) | partial | n/a; v1 | Complete (grounded example) |
| Directory | GET /v1/divisions(/:id/agents) | same | path/query | proposed { data:[{division_id, name, agents:[{id,name}]}] } | proposed (200/403) | n/a; v1 | Net-new (specified) — build chunk 2b from Division/AgentsDivision; lock contract on build |
Database Changes Details
| Change | Table/Entity | DDL / Shape Diff | Data Migration Plan | Rollback Plan | Compatibility Window | Status |
|---|---|---|---|---|---|---|
Add division_id | ai_knowledge_sources | ADD COLUMN division_id :string (nullable) | backfill from existing agent rows where determinable, else null until re-config | rails db:rollback (nullable, safe) | old/new coexist (old FE never sends division_id) | Complete |
| One-per-division uniqueness | ai_knowledge_sources | add_index %i[company_id division_id ai_knowledge_source_type_id], unique: true, where: 'deleted_at IS NULL AND division_id IS NOT NULL' | n/a | drop index on rollback | n/a | Complete (REV-8 fixed; matches unique_knowledge_store.rb pattern) |
Implementation Readiness Checklist
Unblocked (agent can proceed)
- PRD → RFC traceability matrix complete (bidirectional, §-coverage)
- [~] Technical decisions resolved (8/12; 2 partial, 1 dangling-but-deferred-by-design)
- Failure modes handled with error catalogs (§3.A/B/C)
- Configuration contract (§4.B: flag, env, cron)
- Pattern alignment verified (§2.0, enhancement)
- Rollout plan with flag + rollback (§4, §4.E)
- Observability metrics + alerts (§3, PRD §12)
- Task decomposition with acceptance criteria (§4.D)
- Endpoint request/response payload examples (create/list/usages grounded — REV-6 fixed)
- BE schema change at DDL precision (column + concrete partial-unique index — REV-8 fixed)
- Directory endpoint specified (net-new, chunk 2b — REV-1 fixed)
- Transaction boundaries + idempotency per write path (§2.D)
- Concurrency collision points listed (§2.E)
- Security: authz, validation, tenancy (§3)
- Migration: zero-downtime, nullable, rollback (§4.A/E)
- Service boundary documented (§2.F.1)
Blocked (must fix first)
- Config-form + status-badge Figma frames (REV-2) — blocks chunks 7–8
- Source-scoping decision PM+eng confirmation (REV-5) — blocks chunks 2/3
- (External gates, not chatbot-blocking for chunks 1–6/2b) webhook contract (REV-3), masking SLA (REV-4)
Verdict: Fix the design frames + scoping sign-off; BE chunks 1–6 + 2b are buildable now in isolation; FE chunks 7–8 gated on design; chunk 10 gated on the webhook contract.
Task Manifest
The RFC already specifies a strong §4.D Agent Execution Plan. Verified and reproduced with R2 readiness annotations.
| Order | Chunk | Files to Create/Modify | Acceptance Criteria | Dependencies |
|---|---|---|---|---|
| 1 | Confirm contracts (read-only) | ai_knowledge_sources.rb, entities/.../knowledge_source.rb, post_knowledge_sources.rb | existing specs green | None — buildable now |
| 2 | Migration division_id + partial index | db/chatbot_gpt_migrate/…add_division_id…, schema | migrate+rollback clean | REV-5 confirmation |
| 2b | Directory endpoint GET /v1/divisions(/:id/agents) | new Grape resource + entity (from Division/AgentsDivision) | divisions+nested agents, company-scoped, set_role | None — models exist (REV-1 fixed) |
| 3 | Extend create use case (≤15, division, guard) | create_omnichannel_agent_conversation_knowledge_source.rb | 16→422; dup→422; valid→202 | Chunk 2 |
| 4 | Delete in-use guard + conditional vector delete | …/delete.rb, lib/ai_service/... | in-use→422; unused→deleted | Chunk 1 |
| 5 | Daily sync worker + schedule.yml | app/workers/conversation_history_daily_sync_worker.rb, config/schedule.yml | re-posts per ACTIVE; failure isolated | Chunk 3 |
| 6 | FE service + endpoint + wire tile | ai-assist.ts, endpoint.ts, AddKnowledgeDrawer.vue | tile selectable; hits create | Chunk 3 |
| 7 | ConversationHistoryForm.vue | new component + store action | counter 0–15; 16th disables Save | Chunk 2b, REV-2 design |
| 8 | Resources list status states + detail | …/training-sources/index.vue, knowledge-list.vue, SourceDetailDrawer.vue | badges per status; delete in-use blocked | REV-2 design |
| 9 | Reference room-id link + permission | Inbox Copilot ref render + room authz | cross-division → 403 | Chunk 3 (Could-Have) |
| 10 | Training-status webhook | new controller + use case | only when OQ#4 published | REV-3 contract |
Dangling Decisions Log
| # | Decision | Location | Owner | Deadline |
|---|---|---|---|---|
| 1 | Training-status webhook contract (states/payload/retry/auth) | §2.4 inbound, Decision 6, §5 blocker 1 | Data & AI | 2026-07-15 (PRD OQ#4) |
| 2 | Division-scoping: PM+eng sign-off on extend one-per-company → division-scoped (technical spec complete) | Decision 1, §5 blocker 5 | PM + eng (BOT) | unset — before chunk 2/3 |
| 3 | Webhook auth mechanism (UUID-pattern vs HMAC) | §2.4 inbound | Data & AI + BOT | with OQ#4 |
Open Questions
| # | Question | Category | Severity |
|---|---|---|---|
| 1 | When do the config-form and training-status Figma frames land? (REV-2) | CNT / NFS | Blocking |
| 2 | Confirm the division-scoping decision (PM+eng sign-off). (REV-5) | TDC / DMS | Blocking |
| 3 | Data & AI masking SLA + entity coverage (100% phone/email) — contract + date. (REV-4) | CDG | Blocking (for GA, not for chatbot chunks 1–6/2b) |
| 4 | Training-status webhook contract. (REV-3) | ACV / FMC | Important (poll baseline ships without it) |
| 5 | Should create enqueue an immediate AI-Service post-retry rather than waiting up to 24h for the daily sync? (REV-11) | DIC | Important |
| 6 | Lock the directory endpoint's verified contract on chunk 2b build (replace the proposed shape). | ACV | Important |
| 7 | Where are masked vectors stored (jurisdiction) for cross-border / UU PDP? | CDG | Important |
| 8 | Quantified FE bundle/CWV budget? (REV-9) | NFS | Nice-to-have |
| 9 | Distributed trace across FE→API→Data&AI? (REV-10) | OBS | Nice-to-have |
Evidence Notes
- §2.0 Source Verification — the strongest section; file:line citations for every BE claim, all re-confirmed TRUE at HEAD
fa6dd8b79/f5b80d5athis cycle. Two immaterial line drifts noted:post_knowledge_sourcesparticipant_idsis now ~L42 (RFC says L39); the create guard is L29–31 (RFC says L30–33) — content correct. Drove PRT/CPA/SBC/DMS up. - §2.4 APIs + grounded JSON examples — the R1→R2 fix. Field shapes verified against
OmnichannelAgentConversationKnowledgeSource,KnowledgeSource,Usageentities (incl.qoutatypo). Lifted ACV 5.5→7.0 and closed REV-6. - §2.4 directory row + §2.G — re-verified: no GET divisions endpoint exists in
frontend_service(only internalPATCH /v1/chat/divisions);Division/AgentsDivisionmodels exist. R1's cross-layer "unverified No" is resolved to a specified net-new endpoint → 6.5 cap lifted; REV-1 closed. - §2.3 DDL — concrete partial-unique index matching
unique_knowledge_store.rbpattern;ai_knowledge_sourcesconfirmed to have nodivision_idyet (net-new migration). Closed REV-8. - create use case L29–41 — re-grounded REV-5 (still one-per-company only) and surfaced REV-11 (non-transactional AI-Service post inside the trx block).
- §1 Design References / §2.A / qontak-designer
cb601ea— prototype files present; config-form frame stilln/a — design pending;ConversationHistoryForm.vueabsent in chatbot-fe. Sustains REV-2 as the top blocker. - §5 + §7 + §6 Comment log — document the R1-driven edits and keep §7
no; calibrated verdict to HOLD and confirmed the author is not overclaiming.
Review History
| Cycle | Date | Reviewed RFC revision (last_updated / commit) | Score | Verdict | Findings open → fixed | Notes |
|---|---|---|---|---|---|---|
R1 | 2026-06-20 | last_updated: 2026-06-20 / working tree (uncommitted) | 6.5 | HOLD | 10 open (4B/4M/2m), 0 fixed | Initial review. Strongly grounded BE-enhancement half; FE-new-feature half gated by unverified directory endpoint, missing payload examples, and design-pending frames. Cross-layer mismatch capped at 6.5. |
R2 | 2026-06-20 | last_updated: 2026-06-20 (unchanged) / chatbot fa6dd8b79 · chatbot-fe f5b80d5a · qontak-designer cb601ea | 7.5 | HOLD | 6 open (3B/1M/2m), 4 fixed, 1 new | Delta re-review. RFC was revised in response to R1 (per §6 Comment log) without bumping last_updated. Re-grounded every citation at HEAD — all valid (2 immaterial line drifts). Fixed: REV-1 (directory now net-new spec), REV-6 (grounded JSON, entity-verified), REV-7 (create match; division_id not echoed), REV-8 (concrete unique-index DDL). New: REV-11 (non-atomic create post). Cross-layer mismatch resolved → 6.5 cap lifted; ACV 5.5→7.0. Remaining blockers all cross-team/sign-off (design, webhook, masking, scoping). |