Skip to main content

RFC Review: AI Agent Knowledge — Conversation History (Phase 1)

Companion review for conversation-history.md, produced by the rfc-reviewer skill. Lives beside the RFC; valid only for the RFC revision in reviewed_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.rb L29–31 (still no division_id/cap), post_knowledge_sources.rb {id,type:'conversation',participant_ids}, update_status.rb poll, schedule.yml 0 1 * * * Asia/Jakarta, the un-wired tile at AddKnowledgeDrawer.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 real add_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 framen/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)

IDSeverityFinding (one line)RFC locationStatusFirst seenResolved inEvidence / fix
REV-1blockerDivisions+agents directory endpoint was an "unverified No" cross-layer row§2.4 (row 6), §2.G (row 4), §5 blocker 4fixedR1R2 (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-2blockerConfig-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 3openR1Designer (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-3blockerTraining-status webhook contract undefined (states/payload/retry/auth)§2.4 inbound table, Decision 6, §5 blocker 1openR1Data & 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-4blockerPII masking spec/SLA owned by Data & AI; 100%-mask Alpha gate not yet contracted§3.D, Decision 8, §5 blocker 2openR1Data & 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-5majorSource-scoping decision (extend one-per-company → division-scoped) not yet confirmed with PM+engDecision 1, §5 blocker 5openR1Re-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-6majorNo request/response payload examples for any endpoint; deferred to "chunk 1"§2.4 (was "Exact … payload examples to be finalized … during chunk 1")fixedR1R2 (RFC rev)§2.4 now embeds grounded JSON for create (202), list (200), usages (200). Re-verified against real entities: CreateOmnichannelAgentConversationKnowledgeSourceOmnichannelAgentConversationKnowledgeSource {id,agent_id,agent_name,ai_knowledge_source_id}; KnowledgeSource {id,name,type:{code,name},status,updated_at,url}; Usage.qouta typo confirmed.
REV-7majorCreate-endpoint cross-layer match was "partial" — division_id echo + 422 code alignment unconfirmed§2.G (row 1), §3.A/§3.BfixedR1R2 (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-8minorOne-per-division uniqueness enforced only in prose, not as concrete DDL§2.3fixedR1R2 (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-9minorNo quantified FE performance budget (bundle delta, LCP/INP/CLS)§3 Performance Requirement ("no measurable bundle delta")openR1State a concrete bundle-size budget and CWV targets for the new form component. Unchanged this cycle.
REV-10minorNo distributed trace connecting FE request → API → Data & AI; observability is event+alert only§3 Monitoring, §12 (PRD)openR1Add a correlation/trace id across create → AI-Service post → status poll for 3am debugging. Unchanged this cycle.
REV-11minorCreate 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–41openR2Re-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 ElementRFC SectionCoverage
CONVHIST-S01 Source configuration§2.4 create (extended, +grounded JSON) · §2.A form · Detail 1.CFull (build-pending design, REV-2)
CONVHIST-S02 Remove from agent (unlink)§2.4 unlink ai_agent_knowledges · Detail 1.CFull
CONVHIST-S03 Delete source (in-use guard)§2.4 DELETE (extended) · §3.B in_use 422 · Detail 1.CFull
CONVHIST-S04 15-agent cap§2.A counter · §2.4 contract ≤15 · §3.B agent_limit_exceededFull
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 surfacingPartial — masking owned by Data & AI; SLA open (REV-4)
CONVHIST-S07 Quality gate (≥5 msgs)Detail 1.C (Cross-squad) · §2.F.1 step 3Full (Data & AI owns)
CONVHIST-S08 Exclude whispersDetail 1.C · §2.F.1 step 3 · §3.A.1Full (Data & AI scope)
CONVHIST-S09 Recency prioritizationDetail 1.C · §2.F.1 step 3Full (Data & AI scope)
CONVHIST-S10 Daily maintenance sync§2.F worker · §2.2 sync sequence · §4.D ch.5Full
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.1Full
CONVHIST-S13 Text-onlyDetail 1.C · §2.F.1 step 3Full (Data & AI scope)
NEG-1 Hide tile when not Qontak360§2.1 branch flow · §3 plan flag · §4.BFull
NEG-2 Agent with 0 qualifying rooms ignored§3.A.1 Branch & Skip CatalogFull (Data & AI scope)
NEG-3 Block 2nd source per division§2.3 guard (now concrete DDL) · §2.E collision map · §3.B already_existFull (DDL now concrete, REV-8 fixed)
NEG-4 Whisper-with-PII excluded§3.A.1 · §2.F.1 step 4Full (Data & AI scope)
PRD §12 Observability events§3 Monitoring & AlertingFull
PRD §13 Success metrics (≥30% adoption)§1 Success CriteriaFull
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)

#CategorySourceScoreEvidence-Based Rationale
1PRT — PRD TraceabilityMerged8.5FE: §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.
2TDC — Technical DecisionsMerged7.510 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).
3CNT — Contract SpecificityFE6.5TS 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.
4SCB — Scope BoundariesFE8.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).
5DEP — DependenciesFE7.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.
6NFS — Non-Functional SpecificityFE6.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).
7TPS — Test Plan SpecificityFE7.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.
8DMS — Data Model & SchemaBE8.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.
9ACV — API Contract & VersioningBE7.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.
10DIC — Data Integrity & ConsistencyBE7.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).
11FMC — Failure Mode CoverageMerged7.5FE: §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).
12CSS — Concurrency & ScalingBE6.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).
13SAS — Security & AuthorizationBE7.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]).
14ROL — Rollout & RollbackMerged7.5FE: 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).
15OBS — ObservabilityMerged7.5FE: 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+.
16SBC — Service Boundary & CouplingBE8.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.
17CPA — Pattern AlignmentMerged8.0FE: §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.
18CDG — Compliance & Data GovernanceBE6.5Triggered (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

#DecisionStatusCritical Gaps
1Source scoping → division-scoped (division_id + one-per-division)PartialNeeds 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.
2Storage: reuse existing tablesResolvednone
3Ingest sync: async daily sidekiq-cronResolvednone (grounded in existing cron pattern)
4Status propagation: poll baseline + webhook laterResolvedpoll buildable now; webhook deferral explicit and sound
5Vector indexing: reuse AI-Service type:'conversation'Resolvedverified live (post_knowledge_sources.rb, update_status.rb)
615-agent cap: both FE + BEResolvednone
7Delete semantics: hard-delete vectors only when not-in-useResolvednone
8PII masking ownership: Data & AIPartialmasking SLA/spec open (REV-4); decision sound, dependency contract not closed
9Plan/tier gating: reuse SystemPreferenceResolvednone (grounded knowledge_stores/create.rb:36)
10FE source-type wiring: wire existing tileResolvednone (tile re-verified at AddKnowledgeDrawer.vue:440, excluded from active sourceTypeOptions)
11Directory endpoint: net-new GET /v1/divisions(/:id/agents) from Division/AgentsDivisionResolvednone 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 contractDanglingstates/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

ComponentLoadingEmptyErrorPartialSuccessAssessment
Config form (division/agents)defineddefineddefineddefineddefined5/5 (§2.C) — but design-pending (REV-2)
15-cap countern/adefineddefineddefineddefined4/4 applicable (§2.C)
Resources listdefineddefineddefineddefineddefined5/5 (§2.C)
Status badgen/an/adefineddefineddefined3/3 applicable (§2.C) — re-verified knowledge-list.vue MpBadge red/orange/green
Source detaildefinedn/adefinedn/adefined3/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

MetricTargetCurrent BaselineSourceAssessment
LCP / INP / CLSnot statednot statedmissing
Bundle size delta"no measurable delta beyond one form component + Pixel imports"not stated§3vague — not a numeric budget
Drawer interaction"< 100ms"not stated§3target without a measurement method

NO QUANTIFIED PERFORMANCE BUDGET for CWV/bundle (REV-9). Modest gap given the small surface.


Accessibility Review

AspectSpecified?DetailsAssessment
Keyboard navigation flowyesdivision select + agent checkboxes (§3.E, §2.A)adequate
Focus managementyesfocus trap in drawer (§3.E)adequate (no focus-return-on-close stated)
ARIA labelsyescounter aria-live="polite"; validation aria-describedby (§2.A)adequate
Heading hierarchynonot addressedminor gap
Color contrastpartial"color-independent status (badge text + color)" (§3.E)adequate intent; no ratios
Motion sensitivitynonot addressedminor gap
Screen reader behaviorpartialimplied via aria-livethin

Above-average a11y for a draft; named ARIA hooks are directly implementable.


Pattern Alignment Check

PatternRFC ApproachAssessment
FE state management (Pinia)follows (store/ai-assist action)aligned (§2.0)
FE component (<script setup> + Pixel)follows AddKnowledgeDrawer.vuealigned
FE API client ($apiMain)follows ai-assist.ts (re-verified $apiMain(endpoint.v1.ai_assist.*))aligned
FE i18ndeviates — hard-coded strings (no i18n framework)honestly flagged; acceptable
BE Grape/use-case/repo/workerfollows, "deviation: none" with reference filesaligned (§2.0)
Analytics eventspreserved/extended per PRD §12aligned

No silent parallel systems. The one deviation (no i18n) is explicit and matches repo reality.


Data Integrity Deep-Dive

Write PathTransaction ScopePartial Failure BehaviorIdempotency KeyConsistency GuaranteeDuplicate Handling
Create source + agent rowsChatbotGptRecord.transaction (rows atomic); AI-Service post inside the block but non-transactional HTTProllback rows → 422; AI-Service retry via syncone-per-division guardstrong (rows) / eventual (vectors)dup → 422 already_exist
Daily sync re-postper-source, no cross-source trxone source fails → others proceed + alertsource_id + sync dateeventual (Data & AI store)re-post upsert by participant_ids
Delete sourcesoft-delete row + vector hard-deletevector-delete fail → keep soft-deleted, retrysource_ideventualre-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 ResourceWritersCollision ScenarioResolution MechanismLock Failure BehaviorAssessment
1ai_knowledge_sources (one/division)two Admins, same divisionboth pass pre-check, both insertDB partial-unique guard (Decision 1) — now concrete DDL2nd insert → 422 already_exist (NEG-3)adequate — index DDL now concrete (REV-8 fixed)
2A source rowdaily sync worker + Admin deletesync re-posts a deleting sourceworker checks deleted_at before postsync no-opsadequate
3status fieldpoll + future webhookboth write statuslast-writer-wins on updated_at; webhook authoritativeidempotent by terminal statusadequate (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

EndpointRequest SchemaResponse SchemaError TaxonomyAuth SpecIdempotencyExample PayloadsAssessment
POST …/omnichannel-agent-conversationspartial (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/:idcomplete (path)partial (shape named, no full example)partialspecificn/ano3/6
GET …/ listpartial (filters)complete (grounded JSON, entity-verified)partialspecificn/ayes (list-200)4/6
DELETE :idcomplete (path)partialcomplete (in_use)specificn/ano4/6
GET …/usagescompletecomplete (grounded JSON; qouta typo verified)partialspecificn/ayes (usages-200)4/6
divisions+agents directorypartial (proposed)partial (proposed)partialspecificn/apartial3/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/ConsumerTriggerInput ShapeRetry PolicyDLQConcurrency LimitIdempotency KeyTimeoutAssessment
ConversationHistoryDailySyncWorkersidekiq-cron 0 1 * * * Asia/Jakartaspecified (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_PROGRESSspecified (company_id)specified (retry: 1)n/a (rescues)lowspecified (terminal-status)specified (client)6/6 applicable
training-status webhook consumerinbound POSTpartial ({company_id,status,...})per contract (unknown)per contractn/aspecified (company_id,source_id,status)n/aBLOCKED (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

TriggerFound?Data LocationClassificationAssessment
PII (email, phone, address, financial, national-ID)yesraw chat pre-mask (transient)customer PIIhandled — masked by Data & AI before indexing; not persisted post-processing (§3.D)
Payment datayes (within chat)raw chatredacted to taghandled via masking (§3.D, PRD S06)
Health datanoN/A
User content with retentionyesmasked vectorsde-identified, rolling 90dhandled — purge on sync + hard-delete on source delete
Auth/session datayescompany_id/organization_id scopinginternalhandled — PaperTrail audit
Cross-border data transfernot addressedgap — 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

EndpointBackend Response SchemaFrontend Expected SchemaMatch?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[].idYesBE 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_atYesconfirm ACTIVE/IN_PROGRESS/FAILED ↔ badge mapping (badge logic verified in knowledge-list.vue)
DELETE :id{ status, code, message } / 422 in-usedialog expects in-use codePartialdefine in_use error code/message envelope (covered in §3.B)
divisions+agents directoryproposed { data:[{division_id, name, agents:[{id,name}]}] }division-first multiselectnet-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 url nullable (entity default present); create response omits division_id (not exposed) — now explicit.
  • Error response shape matched — existing ErrorException envelope; §3.A confirms FE↔BE code consistency.
  • Pagination format aligned — reuses existing offset list (meta:{page,limit,total_data,...}).
  • [~] Auth token format — $apiMain carries 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

ScenarioFrontendBackendWorks?Notes
Pre-deploy (baseline)OldOldYestile un-wired (re-verified excluded from sourceTypeOptions)
Backend firstOldNewYesnew column nullable; old FE never sends division_id
Frontend firstNewOldNoFE needs BE to accept division_id — explicitly avoided
Both deployed (target)NewNewYestarget
Backend rollbackNewOld (rolled back)Partialflag OFF hides FE tile; FE degrades to no-conv-history
Frontend rollbackOld (rolled back)NewYesBE 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/PhraseLocationImpactConcrete Replacement
1"no measurable bundle delta"§3 Performanceagent skips bundle budgeting"≤ 15KB gzip delta for the new form chunk"
2"follow AiService client" (timeout)§2.Fagent guesses timeout valuestate the numeric timeout
3directory contract "(proposed)"§2.4 row 6agent builds against a proposed (not verified-built) shapeacceptable 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

#AlternativesLocationImpact
1UUID-pattern "or" HMAC webhook auth§2.4 inbounddangling — 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

ChunkAcceptance CriteriaAssessment
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_…rb L29–31), participant_ids post (post_knowledge_sources.rb), poll (update_status.rb), cron (schedule.yml), un-wired tile (AddKnowledgeDrawer.vue:440, excluded from active sourceTypeOptions), and the new grounded JSON examples that match the real Grape entities (incl. the qouta typo at usage.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 no with 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

  1. §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).
  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).
  3. §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).
  4. §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

EndpointMethod/PathAuthZRequest ContractResponse ContractError ContractIdempotency/VersioningStatus
CreatePOST /v1/gpt/ai_knowledge_sources/omnichannel-agent-conversationsset_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; v1Mostly complete — missing only per-field request validation rules
DetailGET …/omnichannel-agent-conversation/:idsamepath :id{ data:[{agent_id, agent_name}], message, status_code } (built in use case)partial (404/403)n/a; v1Missing [full example]
ListGET …/samefilters incl. source_type{ status, code, message, data:[{id,name,type:{code,name},status,updated_at,url}], meta:{...} } (entity-verified)partialoffset; v1Complete (grounded example)
DeleteDELETE :idsamepath :id{ status, code, message }complete (in_use 422)n/a; v1Missing [success/error full example]
UsagesGET …/usagessame{ ..., data:[{type, usage, qouta, is_available}], meta } (entity-verified; qouta typo)partialn/a; v1Complete (grounded example)
DirectoryGET /v1/divisions(/:id/agents)samepath/queryproposed { data:[{division_id, name, agents:[{id,name}]}] }proposed (200/403)n/a; v1Net-new (specified) — build chunk 2b from Division/AgentsDivision; lock contract on build

Database Changes Details

ChangeTable/EntityDDL / Shape DiffData Migration PlanRollback PlanCompatibility WindowStatus
Add division_idai_knowledge_sourcesADD COLUMN division_id :string (nullable)backfill from existing agent rows where determinable, else null until re-configrails db:rollback (nullable, safe)old/new coexist (old FE never sends division_id)Complete
One-per-division uniquenessai_knowledge_sourcesadd_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/adrop index on rollbackn/aComplete (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.

OrderChunkFiles to Create/ModifyAcceptance CriteriaDependencies
1Confirm contracts (read-only)ai_knowledge_sources.rb, entities/.../knowledge_source.rb, post_knowledge_sources.rbexisting specs greenNone — buildable now
2Migration division_id + partial indexdb/chatbot_gpt_migrate/…add_division_id…, schemamigrate+rollback cleanREV-5 confirmation
2bDirectory endpoint GET /v1/divisions(/:id/agents)new Grape resource + entity (from Division/AgentsDivision)divisions+nested agents, company-scoped, set_roleNone — models exist (REV-1 fixed)
3Extend create use case (≤15, division, guard)create_omnichannel_agent_conversation_knowledge_source.rb16→422; dup→422; valid→202Chunk 2
4Delete in-use guard + conditional vector delete…/delete.rb, lib/ai_service/...in-use→422; unused→deletedChunk 1
5Daily sync worker + schedule.ymlapp/workers/conversation_history_daily_sync_worker.rb, config/schedule.ymlre-posts per ACTIVE; failure isolatedChunk 3
6FE service + endpoint + wire tileai-assist.ts, endpoint.ts, AddKnowledgeDrawer.vuetile selectable; hits createChunk 3
7ConversationHistoryForm.vuenew component + store actioncounter 0–15; 16th disables SaveChunk 2b, REV-2 design
8Resources list status states + detail…/training-sources/index.vue, knowledge-list.vue, SourceDetailDrawer.vuebadges per status; delete in-use blockedREV-2 design
9Reference room-id link + permissionInbox Copilot ref render + room authzcross-division → 403Chunk 3 (Could-Have)
10Training-status webhooknew controller + use caseonly when OQ#4 publishedREV-3 contract

Dangling Decisions Log

#DecisionLocationOwnerDeadline
1Training-status webhook contract (states/payload/retry/auth)§2.4 inbound, Decision 6, §5 blocker 1Data & AI2026-07-15 (PRD OQ#4)
2Division-scoping: PM+eng sign-off on extend one-per-company → division-scoped (technical spec complete)Decision 1, §5 blocker 5PM + eng (BOT)unset — before chunk 2/3
3Webhook auth mechanism (UUID-pattern vs HMAC)§2.4 inboundData & AI + BOTwith OQ#4

Open Questions

#QuestionCategorySeverity
1When do the config-form and training-status Figma frames land? (REV-2)CNT / NFSBlocking
2Confirm the division-scoping decision (PM+eng sign-off). (REV-5)TDC / DMSBlocking
3Data & AI masking SLA + entity coverage (100% phone/email) — contract + date. (REV-4)CDGBlocking (for GA, not for chatbot chunks 1–6/2b)
4Training-status webhook contract. (REV-3)ACV / FMCImportant (poll baseline ships without it)
5Should create enqueue an immediate AI-Service post-retry rather than waiting up to 24h for the daily sync? (REV-11)DICImportant
6Lock the directory endpoint's verified contract on chunk 2b build (replace the proposed shape).ACVImportant
7Where are masked vectors stored (jurisdiction) for cross-border / UU PDP?CDGImportant
8Quantified FE bundle/CWV budget? (REV-9)NFSNice-to-have
9Distributed trace across FE→API→Data&AI? (REV-10)OBSNice-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/f5b80d5a this cycle. Two immaterial line drifts noted: post_knowledge_sources participant_ids is 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, Usage entities (incl. qouta typo). 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 internal PATCH /v1/chat/divisions); Division/AgentsDivision models 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.rb pattern; ai_knowledge_sources confirmed to have no division_id yet (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 still n/a — design pending; ConversationHistoryForm.vue absent 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

CycleDateReviewed RFC revision (last_updated / commit)ScoreVerdictFindings open → fixedNotes
R12026-06-20last_updated: 2026-06-20 / working tree (uncommitted)6.5HOLD10 open (4B/4M/2m), 0 fixedInitial 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.
R22026-06-20last_updated: 2026-06-20 (unchanged) / chatbot fa6dd8b79 · chatbot-fe f5b80d5a · qontak-designer cb601ea7.5HOLD6 open (3B/1M/2m), 4 fixed, 1 newDelta 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).