← Back to Proposals
Confidential - Advisory Review

RFP Tank

AI-powered government contract proposal platform - find, analyze, and win federal contracts

📅 August 2026🏢 IT Pro Partner - Product Division🌐 rfptank.com

Marketing Site

rfptank.com
Not created

Dashboard/App

app.rfptank.com
Not created

API

api.rfptank.com
Not created

Domain

rfptank.com
Purchased

# VerdictTank v3.6 - Technical Architecture

**Status:** Build specification, companion to the v3.6 business proposal.

**Scope:** Extends the v3 architecture (5-stage pipeline, 4 API endpoints, 10 data model tables) with the 9 new v3.6 capabilities: Chat-to-Refine, URL-to-Review, Dual Scoring, Explain-the-Low-Score, Fix-It Remediation, Second-Opinion Audit Agent, Configurable Rules Engine, White-Label for Consultants, and Roast/Boost Community Peer Review.

**Audience:** Engineering. This is the build spec, not the pitch.

1. System Overview

> **v3.6 scope note (Priority Fix #6 / Judge 3 Week 1 Cut List):** The critical review of v3.6 found the 15-service, 21-table build scoped "like a multi-engineer build" for a solo-founder timeline. This document now describes two tiers: the **8-Week MVP** (5 services, shipped) and **Deferred (Post-Launch)** components (5 services, designed but not built until a funnel/demand signal justifies them). Deferred components' schemas and designs are preserved below for continuity - cutting scope does not mean deleting the design work, it means sequencing it after MVP validation. See Section 9 for the revised build sequence.

1.1 Architecture Diagram - 8-Week MVP Scope

┌─────────────────────────────────────────────┐ │ CLIENTS │ │ Web App · Mobile · API Consumers │ └───────────────────┬───────────────────────────┘ │ HTTPS ┌────────────────────▼────────────────────────┐ │ EDGE / API GATEWAY (Caddy) │ │ TLS term · rate limit · auth (JWT/API key) │ │ request logging │ └───────┬───────────────────────────────────────┘ │ ┌─────────────────────▼───────────────────────┐ │ APPLICATION SERVICES (MVP: 2 of 5 kept) │ │ review-api · chat-api │ └───────────┬───────────────────────────────────┘ │ enqueue ┌───────────▼───────────────────────────────────────────────┐ │ JOB QUEUE (Redis + RQ/Celery) │ │ review.pipeline · chat.turn · url.extract │ │ remediation.generate · sanitize.scan │ └───────────┬─────────────────────────────────────────────────┘ │ ┌───────────────────────────▼──────────────────────────────────────────────────┐ │ REVIEW PIPELINE WORKERS (pipeline-worker) │ │ │ │ Phase 1 Phase 2 Phase 3 Phase 4 Phase 5 │ │ Research ──▶ Primary ──▶ Validation ──▶ Cross-Check ──▶ Verdict │ │ Agent Reviewer Reviewer A + B (‖) Aggreg. │ │ (web verify (10-dim, (challenges (independent (majority │ │ + citations) 1-10 + tags primary score) re-score) + dual │ │ idea/proposal) scores) │ │ │ │ [Phase 6 - Audit Agent: DEFERRED, see §1.2 and §9 Post-Launch Track] │ └───────────┬─────────────────────────────────────────────┬──────────────────────┘ │ │ ┌───────────▼───────────────┐ ┌────────────▼──────────────────┐ │ SANITIZATION GATE │ │ POST-PIPELINE GENERATORS │ │ scans: verdict JSON, │ │ Explainer (per-dim text) │ │ explanations, remediation,│ │ Fix-It Generator (action plan)│ │ chat transcripts │ │ (Rules Engine overlay: DEFERRED)│ │ before egress │ └──────────────┬───────────────────┘ └───────────┬───────────────┘ │ │ │ ┌───────────▼────────────────────────────────────────────────▼───────────────┐ │ DATA LAYER │ │ PostgreSQL (primary OLTP, org_id column present but multi-tenant enforcement │ │ (RLS) deferred until White-Label track - see §1.2) │ │ pgvector (corpus embeddings/similarity) │ │ Redis (queue, session cache, rate limits, feature flags) │ │ S3-compatible object store (PDFs, chat exports, uploaded docs, extracted HTML)│ └─────────────────────────────────────────────────────────────────────────────┘ ┌────────────────────────────────────────────────────────────────────────────┐ │ SUPPORTING SERVICES (MVP) │ │ url-extractor (Crawl4AI) · fixit-generator · pdf-generator │ │ cron (prediction T+90/180/365) │ │ [domain-verifier, moderation-queue: DEFERRED - see §1.2] │ └────────────────────────────────────────────────────────────────────────────┘

1.2 Component List - MVP vs. Deferred

Per Priority Fix #6, the 8-week solo-founder MVP ships **5 services**; the remaining **5 services** from the original v3.6 scope are deferred to post-launch and gated on real usage/demand signal, not built speculatively. This directly implements the review's Week 1 Cut List (KEEP: Dual Scoring, Explain, Fix-It MVP, URL-to-Review, Corpus Schema / CUT: Audit Agent, Rules Engine, White-Label, Roast/Boost, and Chat-to-Refine deferred-not-deleted per below).

**MVP - Ship in 8 Weeks (5 services)**

Layer Component MVP Status Notes
App `review-api` **KEEP** Core review submission/status/verdict endpoints; dual-score fields, `/review/url`
App `chat-api` **KEEP (reduced)** Chat-to-Refine, but scoped down to the ghostwriting-guard behavior in §5.1 - no `chat-ws` streaming socket in MVP, REST polling only, to cut infra surface. Positioned as the "un-copilot" (see §5.1) rather than deferred outright, since Judge 3 flagged it as a differentiator once guarded correctly - see rationale note below.
Worker `url-extractor` **KEEP** Crawl4AI-based content extraction + sanitize; Feature 2 (URL-to-Review)
Worker `pipeline-worker` **KEEP** Phases 1-5 (Research → Primary → Validation → Cross-Check → Verdict/Dual-Score Aggregation). Phase 6 (Audit Agent) removed from the worker in MVP.
Worker `fixit-generator` **KEEP** Fix-It Remediation, upgraded to quality-scoring per §5.5 - retention-loop feature, explicitly called out as a KEEP in the review's cut list

> **Note on Chat-to-Refine placement:** The review's Week 1 Cut List names Chat-to-Refine as a CUT ("funnel feature; build when you have a funnel"). This document keeps a *minimal* `chat-api` in MVP scope specifically because the ghostwriting guard in §5.1 is cheap to build (a length/format check, not new infrastructure) and de-risks the single biggest brand-reputation exposure identified by Judge 3 if Chat-to-Refine ships at all, in this MVP or later. If engineering capacity is tighter than modeled, `chat-api` is the first MVP component to cut - in which case Chat-to-Refine moves to the Deferred table below in its entirety, `chat-ws` and all real-time streaming remain deferred either way, and the guard design in §5.1 is preserved for whenever the feature ships.

**DEFERRED - Post-Launch, Built on Demand Signal (5 services)**

Layer Component MVP Status Notes Deferred Trigger
App `rules-api` **CUT (deferred)** CRUD + validation for org rule definitions Revisit once an Enterprise/White-Label deal is in active negotiation and requires it
App `org-api` **CUT (deferred)** white-label org config, branding, API keys Blocked on Minimum Viable Legal (MSA + Privacy Program) per the review's Legal Killers - see §4
App `peer-review-api` **CUT (deferred)** Roast/Boost submission + moderation Moderation overhead + defamation exposure (Judge 4 Legal Killer #4) outweighs launch value
Worker `audit-agent-worker` **CUT (deferred)** Phase 6 of pipeline, second-opinion audit "Enterprise trust problem, not launch problem" per Judge 3; revisit once Enterprise tier has real pilot demand
Worker `moderation-worker` **CUT (deferred)** abuse handling for Roast/Boost Dependent on `peer-review-api`; deferred together
Infra `domain-verifier` **CUT (deferred)** CNAME/TXT verification for white-label custom domains Dependent on `org-api` / White-Label track; also blocked on Minimum Viable Legal

The `rules-evaluator` overlay logic (§5.7) and `Rules Engine` pipeline injection point are deferred along with `rules-api` - no schema or prompt-injection surface for org rules ships in MVP. The full designs for all deferred components remain documented in Sections 5.6-5.9 and 7 of this document as the intended post-launch build, not as speculative dead weight - they represent validated architecture pending a demand signal, not throwaway work.

**MVP Data Layer implications:** Row-level security (RLS) multi-tenant policies (§6.3, §7.4), the `org_configs`/`org_rules`/`org_api_keys`/`rule_templates` tables, and cross-org isolation logic are **not required for MVP** since `org-api` and `rules-api` are deferred - `org_id` columns remain present (nullable) on core tables for forward compatibility, but RLS enforcement, domain routing, and multi-tenant billing are built when the White-Label track is actually resumed, gated on Minimum Viable Legal per Section 4.

2. Complete Data Model

2.1 Entity Relationship Summary

users ──1:N── reviews ──1:1── verdicts ──1:N── dimensions ──1:1── dimension_explanations │ │ │ │ │ └─1:N── remediation_plans (per dimension) │ ├──1:N── judge_scores ──N:1── judges │ ├──1:N── research_citations │ ├──1:N── predictions │ ├──1:N── audit_findings (v3.6) │ ├──1:N── peer_reviews (v3.6) │ └──1:1── chat_sessions (optional, pre-review link) (v3.6) │ ├──1:N── chat_sessions ──1:N── chat_messages (v3.6) │ └──N:1── org_configs ──1:N── org_rules ──N:1── rule_templates (v3.6, enterprise/white-label) corpus_entries (derived from verdicts, searchable, org_id-scoped) audit_log (cross-cutting, all tables)

All new v3.6 tables carry `org_id` (nullable for individual/non-enterprise accounts) to support multi-tenant partitioning for White-Label. All v3 tables are extended with `org_id` as part of the Phase 0 migration described in Section 9.

2.2 v3 Tables (Extended for v3.6)

> **Dual-Score Schema Validation Gate (Priority Fix #2 / Judge 3 & Judge 1 finding, Architecture §2.2 lines 147-148 in the v3.6 review):** The original v3.6 design baked `idea_score`/`proposal_score` directly into the `reviews` and `corpus_entries` tables as permanent NUMERIC columns from day one - a schema bet made before any beta user confirmed that a two-score split is more useful than the v3 single composite score. The review's Priority Fix #2 is explicit: *"Do not bake `idea_score`/`proposal_score` into the corpus before validating with beta users that two scores are wanted. Gate the migration behind user testing, or ship dual-scoring as a non-schema overlay first."*

>

> **v3.6 resolution - ship as overlay, migrate only after validation:**

> 1. **MVP ships dual scoring as a JSONB overlay, not dedicated columns.** The `idea_score`/`proposal_score` values computed at Phase 5 (see §5.3) are written into `verdicts.verdict_json` (already a JSONB column, no migration required) under a `dual_score` key, and into a new `reviews.dual_score_overlay JSONB` column (nullable, additive, zero-downtime to add/drop) rather than as first-class typed `NUMERIC(5,2)` columns on `reviews` or `corpus_entries`.

> 2. **The NUMERIC columns shown below (`idea_score`, `proposal_score` on `reviews`; same on `corpus_entries`, §2.3) are the *target* schema for GA, not the MVP schema.** They are documented here for architectural continuity but are marked `DEFERRED - VALIDATION GATE` and must not be created by the Phase 0 migration until the exit criterion below is met.

> 3. **Validation exit criterion (must pass before the permanent-column migration runs):** a minimum of 25 beta users from the existing $29/mo Inner Circle cohort have used dual-score overlay output across at least 2 reviews each, and post-review survey/interview signal shows a majority preference for two scores over the legacy single composite. This reuses the same beta cohort the review's Priority Fix #1 calls for surveying - one survey instrument serves both purposes.

> 4. **If validation fails** (users prefer one score, or show no measurable preference), the `dual_score_overlay` JSONB column is dropped, `verdict_json.dual_score` continues to carry the value for backward-compatible display only, and the corpus indexing strategy in §2.3/§2.4 reverts to `legacy_composite_score` as the primary sortable/filterable column.

> 5. **Corpus search and percentile lookups (§6.4) run against the JSONB overlay during the validation window** - `idx_corpus_dual_score_overlay` is a GIN index on the JSONB path, functionally equivalent to a B-tree NUMERIC index for the query patterns in §3.2/§3.3 but avoids a schema commitment. Query latency is slightly higher (GIN vs. B-tree) but acceptable at MVP corpus volume; this is re-benchmarked before the permanent migration.

```sql

-- USERS (v3, extended)

CREATE TABLE users (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

org_id UUID REFERENCES org_configs(id), -- v3.6: multi-tenant scoping

email TEXT UNIQUE NOT NULL,

password_hash TEXT,

tier TEXT NOT NULL DEFAULT 'free', -- free|pro|enterprise|white_label|beta

role TEXT NOT NULL DEFAULT 'member', -- member|org_admin|consultant|superadmin

created_at TIMESTAMPTZ NOT NULL DEFAULT now(),

updated_at TIMESTAMPTZ NOT NULL DEFAULT now()

);

CREATE INDEX idx_users_org ON users(org_id);

-- REVIEWS (v3, extended: dual score columns, submission source, org scoping)

CREATE TABLE reviews (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

org_id UUID REFERENCES org_configs(id),

user_id UUID NOT NULL REFERENCES users(id),

source_type TEXT NOT NULL DEFAULT 'file', -- v3.6: file|url|api

source_url TEXT, -- v3.6: populated when source_type='url'

chat_session_id UUID REFERENCES chat_sessions(id), -- v3.6: link to pre-review coaching

vertical TEXT,

status TEXT NOT NULL DEFAULT 'queued', -- queued|running|audit|done|failed

idea_score NUMERIC(5,2), -- DEFERRED - VALIDATION GATE: do not populate until dual-score validation exit criterion (§2.2) is met; MVP writes to dual_score_overlay instead

proposal_score NUMERIC(5,2), -- DEFERRED - VALIDATION GATE: see idea_score note above

dual_score_overlay JSONB, -- v3.6 MVP: { "idea_score": 78.5, "proposal_score": 41.0 }, non-schema overlay per §2.2 gate; promoted to typed columns above only post-validation

legacy_composite_score NUMERIC(5,2), -- v3: preserved for backward compat

rules_applied JSONB DEFAULT '[]', -- DEFERRED - org_rules snapshot, populated only once Rules Engine (§5.7) ships post-launch

parent_review_id UUID REFERENCES reviews(id), -- v3.6 (§5.5): set when this review is a resubmission addressing a prior remediation_plan

fix_completion_score NUMERIC(3,2), -- v3.6 (§5.5): mean fix_quality_tier (0-3) across parent's action_items, computed on resubmission

created_at TIMESTAMPTZ NOT NULL DEFAULT now(),

completed_at TIMESTAMPTZ

);

CREATE INDEX idx_reviews_org ON reviews(org_id);

CREATE INDEX idx_reviews_user ON reviews(user_id);

CREATE INDEX idx_reviews_status ON reviews(status);

CREATE INDEX idx_reviews_parent ON reviews(parent_review_id);

-- VERDICTS (v3, unchanged shape, verdict_json now carries dual-score payload)

CREATE TABLE verdicts (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

review_id UUID NOT NULL REFERENCES reviews(id) UNIQUE,

verdict_json JSONB NOT NULL, -- see Section 2.5 for v3.6 schema

pdf_url TEXT,

public_share_id TEXT UNIQUE,

created_at TIMESTAMPTZ NOT NULL DEFAULT now()

);

-- DIMENSIONS (v3, extended: score_type tag for dual scoring)

CREATE TABLE dimensions (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

review_id UUID NOT NULL REFERENCES reviews(id),

name TEXT NOT NULL, -- e.g. "Market Analysis"

score NUMERIC(4,2) NOT NULL, -- 1-10, existing v3 scale

score_type TEXT NOT NULL DEFAULT 'proposal', -- v3.6: 'idea' | 'proposal'

weight NUMERIC(4,3) DEFAULT 1.0,

created_at TIMESTAMPTZ NOT NULL DEFAULT now()

);

CREATE INDEX idx_dimensions_review ON dimensions(review_id);

-- JUDGES (v3, unchanged)

CREATE TABLE judges (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

role TEXT NOT NULL, -- Research|Primary|Validation|CrossCheckA|CrossCheckB|...

vendor_internal TEXT NOT NULL, -- stripped by sanitization gate before egress

is_audit_agent BOOLEAN NOT NULL DEFAULT false, -- v3.6: flags the Phase 6 role

active BOOLEAN NOT NULL DEFAULT true,

accuracy_score NUMERIC(5,4),

created_at TIMESTAMPTZ NOT NULL DEFAULT now()

);

-- JUDGE_SCORES (v3, unchanged)

CREATE TABLE judge_scores (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

review_id UUID NOT NULL REFERENCES reviews(id),

judge_id UUID NOT NULL REFERENCES judges(id),

dimension_id UUID REFERENCES dimensions(id),

raw_score NUMERIC(4,2) NOT NULL,

rationale TEXT,

created_at TIMESTAMPTZ NOT NULL DEFAULT now()

);

-- RESEARCH_CITATIONS (v3, unchanged)

CREATE TABLE research_citations (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

review_id UUID NOT NULL REFERENCES reviews(id),

source_url TEXT NOT NULL,

excerpt TEXT,

retrieved_at TIMESTAMPTZ NOT NULL DEFAULT now()

);

-- PREDICTIONS (v3, unchanged)

CREATE TABLE predictions (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

review_id UUID NOT NULL REFERENCES reviews(id),

predicted_risk TEXT NOT NULL,

check_at TIMESTAMPTZ NOT NULL, -- T+90/180/365

outcome TEXT, -- pending|materialized|avoided

checked_at TIMESTAMPTZ

);

-- CORPUS_ENTRIES (v3, extended: org scoping for white-label segmentation, dual score overlay)

CREATE TABLE corpus_entries (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

org_id UUID REFERENCES org_configs(id), -- v3.6: NULL = shared/general corpus

review_id UUID NOT NULL REFERENCES reviews(id),

vertical TEXT,

idea_score NUMERIC(5,2), -- DEFERRED - VALIDATION GATE, see §2.2; not populated at MVP

proposal_score NUMERIC(5,2), -- DEFERRED - VALIDATION GATE, see §2.2; not populated at MVP

dual_score_overlay JSONB, -- v3.6 MVP: populated instead of the two columns above, see §2.2

embedding VECTOR(1536),

is_public BOOLEAN NOT NULL DEFAULT false,

created_at TIMESTAMPTZ NOT NULL DEFAULT now()

);

CREATE INDEX idx_corpus_org ON corpus_entries(org_id);

CREATE INDEX idx_corpus_embedding ON corpus_entries USING hnsw (embedding vector_cosine_ops);

CREATE INDEX idx_corpus_dual_score_overlay ON corpus_entries USING gin (dual_score_overlay); -- v3.6 MVP: GIN index serves overlay queries until/unless promoted to typed columns, see §2.2

-- AUDIT_LOG (v3, extended: new event types for v3.6 surfaces)

CREATE TABLE audit_log (

id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

org_id UUID REFERENCES org_configs(id),

actor_id UUID REFERENCES users(id),

event_type TEXT NOT NULL, -- + v3.6: chat.message, review.url_submit, rule.create,

-- audit_agent.finding, peer_review.submit, org.brand_update

entity_type TEXT,

entity_id UUID,

metadata JSONB DEFAULT '{}',

created_at TIMESTAMPTZ NOT NULL DEFAULT now()

);

CREATE INDEX idx_audit_log_entity ON audit_log(entity_type, entity_id);

2.3 New v3.6 Tables

```sql -- CHAT_SESSIONS (Feature 1: Chat-to-Refine) CREATE TABLE chat_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id), org_id UUID REFERENCES org_configs(id), status TEXT NOT NULL DEFAULT 'active', -- active|archived|converted_to_review review_id UUID REFERENCES reviews(id), -- set once user submits for review title TEXT, turn_count INT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_chat_sessions_user ON chat_sessions(user_id); -- CHAT_MESSAGES CREATE TABLE chat_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), session_id UUID NOT NULL REFERENCES chat_sessions(id), role TEXT NOT NULL, -- user|coach content TEXT NOT NULL, sanitized BOOLEAN NOT NULL DEFAULT false, -- gate must run before export created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_chat_messages_session ON chat_messages(session_id, created_at); -- DIMENSION_EXPLANATIONS (Feature 4: Explain the Low Score) CREATE TABLE dimension_explanations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), dimension_id UUID NOT NULL REFERENCES dimensions(id) UNIQUE, explanation_text TEXT NOT NULL, cited_gaps JSONB NOT NULL DEFAULT '[]', -- ["no TAM calculation", "no competitor pricing"] sanitized BOOLEAN NOT NULL DEFAULT false, generated_by UUID REFERENCES judges(id), -- always the Primary Reviewer judge created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- REMEDIATION_PLANS (Feature 5: Here's How to Fix It - quality-scored per §5.5) CREATE TABLE remediation_plans ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), review_id UUID NOT NULL REFERENCES reviews(id), dimension_id UUID NOT NULL REFERENCES dimensions(id), action_items JSONB NOT NULL DEFAULT '[]', -- action_items shape: [{ "description": str, "difficulty": 1-3, -- "estimated_time_minutes": int, "template_url": str|null, -- "fix_quality_tier": int|null, -- 0=not_attempted,1=superficial,2=minimal,3=substantive; NULL until resubmission evaluated, see §5.5 -- "fix_quality_rationale": str|null, -- evaluator's one-sentence justification, sanitized per §7.2 -- "evaluated_at": timestamptz|null }] tier_scope TEXT NOT NULL DEFAULT 'full', -- 'summary' (Free) | 'full' (Pro+) disclaimer_version TEXT NOT NULL DEFAULT 'DISC-001', -- v3.6: legal disclaimer version active at generation time, see §2.6 sanitized BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_remediation_review ON remediation_plans(review_id); -- Fix quality aggregate lives on REVIEWS (added to the reviews table above, §2.2): -- reviews.fix_completion_score NUMERIC(3,2) -- mean fix_quality_tier (0-3) across all action_items on resubmission, see §5.5 -- reviews.parent_review_id UUID REFERENCES reviews(id) -- links a resubmission to the review whose remediation plan it's addressing -- AUDIT_FINDINGS (Feature 6: Second-Opinion Audit Agent) [DEFERRED - Post-Launch, see §1.2/§5.6] -- Table shape preserved here for continuity; not created by the MVP Phase 0 migration. CREATE TABLE audit_findings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), review_id UUID NOT NULL REFERENCES reviews(id), finding_type TEXT NOT NULL, -- blind_spot|groupthink|underweighted_dim|tagging_inconsistency|no_finding description TEXT NOT NULL, severity TEXT NOT NULL DEFAULT 'low', -- low|medium|high related_dimension_id UUID REFERENCES dimensions(id), disclaimer_version TEXT NOT NULL DEFAULT 'DISC-001', -- v3.6: see §2.6 sanitized BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_audit_findings_review ON audit_findings(review_id); -- RULE_TEMPLATES (Feature 7: Configurable Review Rules Engine) [DEFERRED - Post-Launch, see §1.2/§5.7] -- Table shape preserved here for continuity; not created by the MVP Phase 0 migration. CREATE TABLE rule_templates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, category TEXT NOT NULL, -- compliance|brand_voice|required_field|checklist schema_json JSONB NOT NULL, -- JSON Schema describing valid rule params is_system BOOLEAN NOT NULL DEFAULT true, -- system-provided vs org-authored created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- ORG_RULES [DEFERRED - Post-Launch, see §1.2/§5.7] CREATE TABLE org_rules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), org_id UUID NOT NULL REFERENCES org_configs(id), template_id UUID REFERENCES rule_templates(id), name TEXT NOT NULL, rule_definition JSONB NOT NULL, -- rule_definition shape: { "condition": {...if/then AST...}, -- "required_fields": [...], "checklist": [...], "severity": "block|warn" } is_active BOOLEAN NOT NULL DEFAULT true, validated_at TIMESTAMPTZ, -- set once syntax + schema validation passes created_by UUID REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_org_rules_org ON org_rules(org_id) WHERE is_active = true; -- ORG_CONFIGS (Feature 8: White-Label for Consultants) CREATE TABLE org_configs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, tier TEXT NOT NULL DEFAULT 'white_label', -- enterprise|white_label domain TEXT UNIQUE, -- custom domain, e.g. reviews.acceleratorx.com domain_verified BOOLEAN NOT NULL DEFAULT false, domain_verified_at TIMESTAMPTZ, logo_url TEXT, brand_colors JSONB DEFAULT '{}', -- { "primary": "#...", "accent": "#..." } email_template TEXT, -- HTML template with token placeholders corpus_segment TEXT NOT NULL DEFAULT 'shared', -- 'shared' | 'dedicated' api_key_prefix TEXT UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- ORG_API_KEYS (supports Feature 8's per-org API key scoping) CREATE TABLE org_api_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), org_id UUID NOT NULL REFERENCES org_configs(id), key_hash TEXT NOT NULL UNIQUE, label TEXT, scopes JSONB DEFAULT '["review:write","review:read"]', revoked_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), last_used_at TIMESTAMPTZ ); -- PEER_REVIEWS (Feature 9: Roast/Boost Community Peer Review) CREATE TABLE peer_reviews ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), review_id UUID NOT NULL REFERENCES reviews(id), user_id UUID NOT NULL REFERENCES users(id), type TEXT NOT NULL, -- roast|boost comment TEXT, visibility TEXT NOT NULL DEFAULT 'submitter_only', -- submitter_only|shared flagged BOOLEAN NOT NULL DEFAULT false, flagged_reason TEXT, moderation_status TEXT NOT NULL DEFAULT 'clean', -- clean|pending_review|removed created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_peer_reviews_review ON peer_reviews(review_id); CREATE UNIQUE INDEX idx_peer_reviews_dedupe ON peer_reviews(review_id, user_id); -- PEER_REVIEW_RATE_LIMITS (supports 3/day/user cap) CREATE TABLE peer_review_rate_limits ( user_id UUID PRIMARY KEY REFERENCES users(id), review_date DATE NOT NULL, count_today INT NOT NULL DEFAULT 0, UNIQUE (user_id, review_date) );

2.4 Full Table Inventory (v3 + v3.6)

2.5 Verdict JSON Schema (v3.6)

# Table Origin Purpose
1 users v3 (extended) account, tier, org membership
2 reviews v3 (extended) review lifecycle, dual scores, source type
3 verdicts v3 final verdict JSON, PDF, share link
4 dimensions v3 (extended) per-dimension score + idea/proposal tag
5 judges v3 (extended) panel roster incl. audit agent flag
6 judge_scores v3 raw per-judge scoring
7 research_citations v3 Phase 1 web verification sources
8 predictions v3 T+90/180/365 outcome tracking
9 corpus_entries v3 (extended) searchable review corpus, org-segmented
10 audit_log v3 (extended) cross-cutting event log
11 chat_sessions v3.6 Chat-to-Refine sessions
12 chat_messages v3.6 Chat-to-Refine turn history
13 dimension_explanations v3.6 Explain-the-Low-Score text
14 remediation_plans v3.6 Fix-It action plans
15 audit_findings v3.6 Second-Opinion Audit Agent output
16 rule_templates v3.6 reusable rule schemas
17 org_rules v3.6 org-specific custom rules
18 org_configs v3.6 white-label branding/domain
19 org_api_keys v3.6 per-org scoped API keys
20 peer_reviews v3.6 Roast/Boost entries
21 peer_review_rate_limits v3.6 abuse/rate control

```json

{

"review_id": "uuid",

"status": "done",

"idea_score": 78.5,

"proposal_score": 41.0,

"dual_score_overlay": { "idea_score": 78.5, "proposal_score": 41.0 },

"framing_summary": "Your idea is strong; your pitch needs work.",

"disclaimer": "This is an automated critique generated by AI models, not professional advice. Scores and commentary are opinions, not facts. Do not rely on this output for investment, procurement, legal, or funding decisions without independent professional review. See full disclaimer at https://verdicttank.com/legal/disclaimer.",

"dimensions": [

{

"name": "Market Analysis",

"score": 4,

"score_type": "idea",

"explanation": {

"text": "Market Analysis: 4/10 - no TAM calculation, no competitor pricing data, assumes zero competition.",

"cited_gaps": ["no TAM calculation", "no competitor pricing data", "zero-competition assumption"],

"disclaimer": "AI-generated critique, not professional advice."

},

"remediation": {

"tier_scope": "full",

"disclaimer": "This remediation plan is an automated suggestion, not professional or legal advice. Validate independently before acting.",

"quality_tier": "not_yet_evaluated",

"action_items": [

{

"description": "Calculate TAM using the top-down/bottom-up hybrid formula",

"difficulty": 2,

"estimated_time_minutes": 90,

"template_url": "https://cdn.verdicttank.com/templates/tam-calc.xlsx"

},

{

"description": "Add a competitor pricing comparison table",

"difficulty": 1,

"estimated_time_minutes": 45,

"template_url": "https://cdn.verdicttank.com/templates/competitor-pricing.xlsx"

}

]

}

}

],

"audit": {

"status": "deferred_post_mvp",

"findings": [],

"tagging_consistency_check": "not_run",

"note": "Second-Opinion Audit Agent (Phase 6) is deferred post-MVP per §1.2. Field shape is preserved for forward compatibility; populated once the Audit Agent worker ships. When populated, audit.disclaimer carries the same 'no professional advice' language as other surfaces."

},

"rules_applied": [],

"corpus_percentile": { "idea_score": 80, "proposal_score": 30 },

"public_share_id": "vt-8x2k",

"generated_at": "2026-08-11T00:00:00Z"

}

2.6 Legal Disclaimers - "No Professional Advice" (All Output Surfaces)

3. API Reference

3.1 Authentication

3.2 v3 Endpoints (Unchanged Contract, Extended Payload)

`rules_applied` ships as an always-empty array in MVP since the Rules Engine (`rules-api`) is deferred per §1.2 - the field is preserved in the schema for forward compatibility rather than removed, avoiding a breaking payload change when the Rules Engine ships post-launch. All freeform text fields (`explanation.text`, `action_items[].description`, `audit.findings[].description`) are sanitized before this object leaves the pipeline - see Section 7.2. All `disclaimer` fields are **not** sanitizable/removable content - they are fixed legal boilerplate injected by the response serializer after sanitization, never model-generated, and cannot be stripped by any per-review customization (org branding, White-Label templates, tier). See §2.6 for the full disclaimer policy. **Origin:** Judge 4's Legal Killer #2 (AI Liability / Defamation / Tortious Interference - ranked EXTREME) and Priority Fix #9 of the v3.6 review: *"Every verdict, explanation, audit finding, and remediation action item must carry conspicuous language... This is the first line of defense against Rank 2 liability."* No disclaimer, limitation-of-liability language, or indemnity existed anywhere in v3.6 prior to this revision - Section 15/Risk Assessment covered only technical/product risk, not the liability exposure of an AI system whose scores can be blamed for a lost deal, declined funding round, or reputational harm. **Standard disclaimer text** (canonical copy, referenced by ID `DISC-001` so all surfaces stay in sync on future legal-copy revisions): > *"This is an automated critique generated by AI models, not professional advice. Scores, explanations, audit findings, and remediation suggestions are AI-generated opinions, not verified facts or expert judgments. Do not rely on this output for investment, procurement, legal, regulatory, or funding decisions without independent professional review. VerdictTank and IT Pro Partner disclaim liability for decisions made in reliance on this output. Full terms: https://verdicttank.com/legal/disclaimer."* **Coverage - every output surface carries it, no exceptions:** | Output Surface | Placement | Enforcement Mechanism | |---|---|---| | Verdict JSON (`verdict_json`) | Top-level `disclaimer` field + per-dimension `explanation.disclaimer` + per-remediation `remediation.disclaimer` (see §2.5) | Injected by the API/PDF serializer post-sanitization; schema validation rejects a verdict payload missing the top-level field (fail-closed, same enforcement pattern as `explanation.text` in §5.4) | | PDF reports | Persistent footer on every page + prominent banner directly below the score header on page 1 | Hardcoded in the PDF template (not model-generated, not org-brand-overridable - see White-Label note below) | | Public share reports (web) | Sticky banner above the fold, not a footnote or dismissible tooltip | Rendered server-side in the share-page template, not client-injectable/removable | | API responses (`/verdict/{id}`, `/verdict/{id}/pdf`, `/reviews/{id}/remediation`, `/reviews/{id}/audit`) | `disclaimer` field present on every response object that carries scores, explanations, findings, or remediation | Contract-tested: API response schema tests fail CI if any scored/explained/remediated response type omits the field | | Chat-to-Refine transcripts | Session-start system message ("I'm a coach, not an advisor - nothing here is professional advice") + persistent footer in chat UI | Rendered client-side from a fixed string, reinforces the "un-copilot" brand framing in §5.1 | | Audit findings (when the Audit Agent ships post-MVP, §1.2) | Same `disclaimer` field pattern as explanations/remediation, applied at design time so no retrofit is needed when Phase 6 ships | Schema shape reserved in §2.5 now; enforcement added to the audit-findings serializer at build time | | Corpus/percentile displays | Inline caveat: "Percentile rankings are relative to other AI-scored submissions, not a market or investment benchmark" | Rendered alongside `corpus_percentile` wherever it's displayed | **Non-negotiable design constraints:** - **Not sanitizable, not removable.** The sanitization gate (§7.2) scans for information leakage; it must never scan or strip the disclaimer field itself. This is enforced by keeping disclaimer injection as a distinct post-sanitization serialization step, not part of the model-generated content that passes through the gate. - **Not White-Label-brand-overridable.** Per Judge 4's Legal Killer #3/#5 concerns about White-Label trade-secret and IP exposure, `org_configs.email_template`/`brand_colors` theming (§5.8) is explicitly scoped to *cannot* touch disclaimer copy, placement, or prominence - this is enforced at the template-rendering layer (disclaimer blocks are rendered from a separate, non-themeable partial, injected after the brand template resolves). Applies once White-Label ships post-MVP; documented now so it's not an afterthought when `org-api` is un-deferred. - **Versioned copy.** `DISC-001` is versioned; if legal counsel revises the disclaimer text (e.g. after the Minimum Viable Legal review the critical review calls for - MSA + Privacy Program + Content Safety Stack), the version bump is a config change, not a code change, and old stored verdicts retain the disclaimer version active at generation time (`verdict_json.disclaimer_version`) for auditability. - **This is a mitigation, not a legal opinion.** This architecture doc specifies the *engineering* implementation of conspicuous disclaimers per Priority Fix #9. It does not substitute for the Minimum Viable Legal framework (White-Label MSA, Privacy Program, Content & Safety Stack) the review calls out as a precondition for accepting White-Label customers - that legal work is out of scope for this architecture doc and remains a hard gate on the White-Label track (§1.2) regardless of engineering completeness here. - **User-facing (web app):** JWT bearer token, short-lived access token + refresh token, issued by `auth-api` (unchanged from v3). - **Programmatic (Enterprise/API tier):** API key in `Authorization: Bearer vt_live_...` header. Keys are scoped per-org (`org_api_keys`) - v3.6 adds org scoping on top of v3's flat per-user API keys. - **White-Label custom domains:** requests arriving on a verified custom domain (`org_configs.domain`) are resolved to `org_id` at the edge before hitting any endpoint; all endpoints below implicitly filter/write with that `org_id`. **`POST /api/verdicttank/review`** Upload-based review submission (file). Response now includes dual score fields once complete. ```json // Response (200, once status=done) { "review_id": "uuid", "idea_score": 78.5, "proposal_score": 41.0, "status": "done" }

**`GET /api/verdicttank/status/{id}`** - unchanged polling contract; `status` enum extended with `audit` (Phase 6 in progress).

**`GET /api/verdicttank/verdict/{id}/pdf`** - unchanged; PDF generator now renders dual-score header and per-dimension explanation/remediation sections.

**`GET /api/verdicttank/corpus/search`** - unchanged query contract; results now include `idea_score` and `proposal_score` columns instead of a single blended score, and are `org_id`-scoped for white-label callers.

3.3 New v3.6 Endpoints

**`POST /api/verdicttank/review/url`** (Feature 2: URL-to-Review)

```json

// Request

{ "url": "https://example.com/pitch-deck", "vertical_hint": "fintech" }

// Response (202 Accepted)

{ "review_id": "uuid", "status": "queued", "source_type": "url" }

Validation: URL scheme allowlist (http/https), DNS resolution check, max content size 10MB, extraction timeout 30s (hard cap 45s), robots.txt respected, content sanitized before entering the pipeline (see Section 7.2). **`POST /api/verdicttank/chat/start`** (Feature 1) ```json // Request: { "initial_message": "I'm building a marketplace for..." } // Response: { "session_id": "uuid", "reply": "Tell me more about who pays on this marketplace." }

**`POST /api/verdicttank/chat/{session_id}/message`**

```json

// Request: { "message": "Both sides pay a transaction fee." }

// Response: { "reply": "...", "turn_count": 4 }

Also available as **`WSS /ws/verdicttank/chat/{session_id}`** for real-time streaming token-by-token (see Section 5.1). REST polling variant is the fallback for clients that can't hold a socket. **`GET /api/verdicttank/chat/{session_id}/history`** Returns sanitized transcript (`chat_messages` where `sanitized=true`). Export triggers gate scan if not already sanitized. **`POST /api/verdicttank/rules`** (Feature 7, Enterprise/White-Label only) ```json { "name": "SOC2 disclosure required", "template_id": "uuid", "rule_definition": { "condition": {"if": "vertical == 'fintech'"}, "required_fields": ["compliance.soc2_status"], "severity": "block" } }

Response `201` on pass, `422` with schema violation details on rule-syntax failure.

**`GET /api/verdicttank/rules`** / **`PATCH /api/verdicttank/rules/{id}`** / **`DELETE /api/verdicttank/rules/{id}`** - standard CRUD, org-scoped.

**`POST /api/verdicttank/org/branding`** (Feature 8, White-Label)

```json

{ "logo_url": "...", "brand_colors": {"primary": "#0B1B33"}, "email_template": "<html>...</html>" }

**`POST /api/verdicttank/org/domain`** - initiates CNAME/TXT verification (see Section 5.6). **`POST /api/verdicttank/reviews/{id}/peer-review`** (Feature 9) ```json { &quot;type&quot;: &quot;roast&quot;, &quot;comment&quot;: &quot;Your CAC assumption ignores paid acquisition entirely.&quot; }

Rate-limited to 3/user/day (enforced via `peer_review_rate_limits`, Redis-cached counter). Returns `429` past cap.

**`POST /api/verdicttank/reviews/{id}/peer-review/{peer_review_id}/report`** - abuse reporting, pushes into `moderation-queue`.

**`GET /api/verdicttank/reviews/{id}/remediation`** - standalone fetch of the Fix-It plan (also embedded in verdict JSON).

**`GET /api/verdicttank/reviews/{id}/audit`** - standalone fetch of audit findings (Enterprise tier).

4. Pipeline Flow

4.1 Updated 6-Stage Pipeline

INTAKE EGRESS │ │ ▼ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ File Upload │ │ URL Extract │ │ Chat-to- │ │ API submission │ │ │ (existing) │ │ (Crawl4AI) │ │ Refine draft │ │ (Enterprise) │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └────────┬───────────┘ │ └──────────────────┴──────────────────┴────────────────────┘ │ │ │ normalize to standard intake format │ │ │ ▼ │ ┌────────────────────────────────┐ │ │ [Rules Engine pre-check: │ DEFERRED - Post-Launch │ │ DEFERRED, see §1.2] │ (see §1.2, §5.7) │ └────────────────┬────────────────┘ │ │ │ ┌────────────────────────────────▼─────────────────────────────────────┐ │ │ PHASE 1: Research Agent │ │ │ Live web verification + citation gathering │ │ └────────────────────────────────┬─────────────────────────────────────┘ │ │ │ ┌────────────────────────────────▼─────────────────────────────────────┐ │ │ PHASE 2: Primary Reviewer │ │ │ 10-dim brutal critique, 1-10 per dim │ │ │ + score_type tag (idea|proposal) per dim, overlay-only [v3.6, §2.2] │ │ │ + per-dimension explanation generated inline [v3.6, Feature 4] │ │ │ [org rule dimensions: DEFERRED - see §1.2, §5.7] │ │ └────────────────────────────────┬─────────────────────────────────────┘ │ │ │ ┌────────────────────────────────▼─────────────────────────────────────┐ │ │ PHASE 3: Validation Reviewer - challenges primary score │ │ └────────────────────────────────┬─────────────────────────────────────┘ │ │ │ ┌───────────────────────┴───────────────────────┐ │ ▼ ▼ │ ┌──────────────────┐ ┌──────────────────┐ │ │ PHASE 4: │ │ PHASE 4: │ │ │ Cross-Check A │ (parallel) │ Cross-Check B │ │ └─────────┬──────────┘ └─────────┬──────────┘ │ └───────────────────────┬─────────────────────────┘ │ ▼ │ ┌──────────────────────────────────┐ │ │ PHASE 5: Majority Verdict │ │ │ aggregates scores, builds │ │ │ idea_score + proposal_score │ │ │ as dual_score_overlay [v3.6, §2.2] │ │ └──────────────────┬───────────────────┘ │ │ │ ┌──────────────────▼───────────────────┐ │ │ [PHASE 6: Audit Agent - DEFERRED, │ │ │ Post-Launch, see §1.2 / §5.6] │ │ └──────────────────┬───────────────────┘ │ │ │ ┌──────────────────▼───────────────────┐ │ │ POST-PIPELINE: Fix-It Generator │ │ │ (Pro+; lightweight summary on Free) │ │ │ quality-scored, 3-tier rubric [v3.6, §5.5]│ │ └──────────────────┬───────────────────┘ │ │ │ ┌──────────────────▼───────────────────┐ │ │ SANITIZATION GATE (extended) │ │ │ scans verdict JSON, explanations, │ │ │ remediation text │ │ │ (audit findings: N/A, Phase 6 deferred) │ │ └──────────────────┬───────────────────┘ │ │ │ ┌──────────────────▼───────────────────┐ │ │ DISCLAIMER INJECTION (post-sanitization) │──────────────────────┐ │ &quot;no professional advice&quot; on every │ │ │ scored/explained/remediated surface [§2.6]│ │ └──────────────────┬───────────────────┘ │ │ │ ┌──────────────────▼───────────────────┐ │ │ PDF Generation + Corpus Write │──────────────────────┘ │ (dual-score overlay, org-segmented) │ └───────────────────────────────────────┘

4.2 Data Flow Notes

5. Component Architecture

5.1 Chat-to-Refine - "Un-Copilot" Coaching (Ghostwriting Guard)

> **Priority Fix #4 (Judge 3):** The v3.6 review found the original prompt-level ghostwriting mitigation ("system prompt says don't ghostwrite, flag suspicious output for manual review") reactive and non-deterministic - a coaching model instructed not to ghostwrite can still ghostwrite a full paragraph, and "flag for manual spot-check" catches it *after* the user already received it. Judge 3's recommendation: build a deterministic, real-time **output-length guard** that rejects multi-paragraph output before it reaches the user, and lean into an explicit **"un-copilot"** brand position - VerdictTank coaches by asking better questions, it does not write your pitch for you, and it says so out loud.

**Real-time output-length guard (deterministic, not prompt-level):**

1. **Every coach turn response is validated before it is returned to the user - not sampled, not spot-checked, every single turn.** The guard runs synchronously in the `chat-api` request path immediately after the coaching-model call returns and before the response is persisted to `chat_messages` or sent to the client.

2. **Rejection criteria (any one trips the guard):**

3. **On rejection, the system does not simply retry the same prompt.** It re-prompts the coaching model with an explicit corrective instruction appended to the turn: *"Your previous response was too long / not a question. Respond with exactly one Socratic question (under 280 characters) that helps the user find the gap themselves. Do not draft content for them."* This re-prompt is capped at 2 retries; if the model still fails to produce a compliant response on the 3rd attempt, the system falls back to a pre-written generic Socratic prompt from a static bank (e.g. *"What's the one number in here you're least sure of?"*) rather than ever showing the user a rejected response.

4. **Every guard trip is logged** (`chat_messages.guard_rejected BOOLEAN`, `chat_messages.guard_rejection_reason TEXT`, `chat_messages.retry_count INT`) for the quarterly manual audit in item 6 below, and to catch coaching-model/prompt regressions early (a spike in guard-trip rate on a model update is itself an alert-worthy signal, not just an audit finding).

5. **The guard is enforced server-side only** - never trust a client-side check for this; the rejection logic lives in `chat-api`, not the web frontend, so it can't be bypassed by a different client hitting the same endpoint.

**"Un-copilot" brand positioning (Judge 3's recommendation, product + UX + copy, not just engineering):**

5.2 URL Extractor

5.3 Dual Scorer

1. Phase 2 Primary Reviewer prompt requires each dimension output to include `score_type: "idea"|"proposal"`.

2. Phases 3-4 (Validation, Cross-Check A/B) inherit and can challenge the tag, not just the score.

3. Phase 5 aggregation buckets by tag, computes two independent weighted composites on a 0-100 scale (existing dimensions are 1-10; composite = weighted mean × 10, adjusted by corpus-relative calibration carried over from v3's percentile logic).

4. Phase 6 Audit Agent explicitly checks tagging *consistency* across judges - if Judge A tags "Market Analysis" as `idea` and Judge B tags it `proposal`, that's a flagged `tagging_inconsistency` finding, not a silent average.

5.4 Explainer

5.5 Fix-It Generator - Quality-Scored Remediation

> **Priority Fix #7 (Fix-It Quality Loophole):** The v3.6 review found the original remediation-tracking design ("re-review score deltas... detect superficial template-filling vs. genuine improvement") was aspirational but not actually specified - there was no evaluation step that distinguished a user who genuinely added a TAM calculation from a user who pasted a single sentence into the relevant section just to make the presence-check pass. Presence-checking ("did the field get filled in") is trivially gameable and undermines the entire retention-loop premise of Fix-It as a credible improvement signal, not a checkbox exercise.

**3-Tier Fix Quality Rubric (evaluation, not presence-checking):**

On resubmission (re-review of a previously scored document, or a follow-up chat/URL submission linked via `reviews.parent_review_id`), each `action_item` that was addressed is evaluated against the *specific gap* it named - not merely against whether the target section now contains text. Evaluation runs as an additional judge-model call scoped narrowly to comparing the before/after content for a given `action_item`, using the original `cited_gaps` and `description` as the rubric anchor.

Tier Score Definition Scoring Rule
**Superficial** 1 The relevant section changed, but the specific gap named in `cited_gaps` is still unaddressed - e.g. a TAM number was added but with no visible methodology, source, or calculation shown; or text was added that mentions the topic without supplying the missing data/evidence. Triggers when the evaluator can find no calculation, citation, data point, or structural change that actually closes the gap - text presence alone does not clear this tier.
**Minimal** 2 The gap is nominally addressed but shallow - e.g. a TAM figure is present with a one-line methodology note, but no bottom-up/top-down hybrid breakdown, no source citation, no sensitivity range. Passes a "did they try" bar but not a "would this survive investor scrutiny" bar. Triggers when the evaluator finds a genuine attempt that directly responds to the cited gap, but missing at least one of: methodology transparency, supporting data/citation, or the specific technique the original `action_item.description` recommended.
**Substantive** 3 The gap is closed with the rigor the original action item called for - e.g. TAM calculated via the recommended hybrid formula, source-cited, with a stated methodology and range. The addition would plausibly change an informed reader's assessment of that dimension. Triggers when the evaluator confirms the specific technique/data named in the action item is present *and* internally consistent with the rest of the submission (a number that contradicts other stated figures elsewhere in the document does not qualify, even if superficially "complete").
**Not Attempted** 0 No detectable change to the relevant section between submissions. Default when the diffed section is identical or near-identical to the original.

5.6 Audit Agent [DEFERRED - Post-Launch, see §1.2]

> This component is designed but **not built for the 8-week MVP**. Per the review's Week 1 Cut List, the Audit Agent addresses "an enterprise trust problem, not a launch problem" - it is retained here in full so the design isn't lost, and is revisited once Enterprise-tier pilot demand justifies the build (see §1.2 Deferred Trigger table).

5.7 Rules Engine [DEFERRED - Post-Launch, see §1.2]

> This component is designed but **not built for the 8-week MVP**. Per the review's Week 1 Cut List, the Rules Engine is a White-Label/Enterprise dependency with no MVP demand signal. Retained here as the intended post-launch build.

1. **Hard block (pre-check):** rules marked `severity: "block"` run before Phase 1 - e.g. a required compliance field missing halts the review immediately, avoiding wasted pipeline spend.

2. **Soft/scored (Phase 2 injection):** rules marked `severity: "warn"` are appended to the Primary Reviewer prompt as additional evaluation dimensions (`dimensions.name` prefixed, `source='org_rule'`), scored 1-10 alongside the standard rubric.

5.8 White-Label Engine [DEFERRED - Post-Launch, see §1.2]

> This component is designed but **not built for the 8-week MVP**, and additionally gated on Minimum Viable Legal (MSA + Privacy Program) per the review's Legal Killers before any Enterprise/White-Label customer is onboarded - see §2.6 and §1.2.

5.9 Community Layer (Roast/Boost) [DEFERRED - Post-Launch, see §1.2]

> This component is designed but **not built for the 8-week MVP**. Per Judge 4's Legal Killer #4 (defamation/moderation exposure), the moderation overhead and liability surface outweigh launch-stage value - retained here as the intended post-launch build once moderation tooling and Minimum Viable Legal are in place.

6. Infrastructure

6.1 Server Layout

┌────────────────────────────────────────────────────────────────────┐ │ Edge tier (2x app3-class instances, HA pair) │ │ Caddy - TLS termination, org-domain routing, rate limiting │ └────────────────────────────────────────────────────────────────────┘ ┌────────────────────────────────────────────────────────────────────┐ │ App tier (autoscaling pool, 3-6 instances) │ │ review-api · chat-api · rules-api · org-api · peer-review-api │ │ chat-ws (sticky sessions via Redis pub/sub for horizontal scale) │ └────────────────────────────────────────────────────────────────────┘ ┌────────────────────────────────────────────────────────────────────┐ │ Worker tier (autoscaling pool, scales with queue depth) │ │ pipeline-workers (Phases 1-6) · url-extractor · fixit-generator │ │ sanitize-scanner · pdf-generator · domain-verifier · cron │ └────────────────────────────────────────────────────────────────────┘ ┌────────────────────────────────────────────────────────────────────┐ │ Data tier │ │ PostgreSQL 16 (primary + read replica) w/ pgvector extension │ │ Redis 7 (queue + cache + rate limits + feature flags) │ │ S3-compatible object store (Wasabi/S3) for PDFs, transcripts, │ │ URL snapshots, uploaded source docs │ └────────────────────────────────────────────────────────────────────┘

6.2 Docker Services

```yaml

services:

edge-caddy: {image: caddy:2, ports: ["443:443"]}

review-api: {build: ./services/review-api}

chat-api: {build: ./services/chat-api}

chat-ws: {build: ./services/chat-ws}

rules-api: {build: ./services/rules-api}

org-api: {build: ./services/org-api}

peer-review-api: {build: ./services/peer-review-api}

pipeline-worker: {build: ./workers/pipeline, deploy: {replicas: 4}}

url-extractor: {build: ./workers/url-extractor} # wraps Crawl4AI

fixit-generator: {build: ./workers/fixit-generator}

audit-agent-worker: {build: ./workers/audit-agent}

sanitize-scanner: {build: ./workers/sanitize-scanner}

pdf-generator: {build: ./workers/pdf-generator}

domain-verifier: {build: ./workers/domain-verifier}

moderation-worker: {build: ./workers/moderation}

cron: {build: ./workers/cron} # T+90/180/365 predictions

postgres: {image: pgvector/pgvector:pg16, volumes: ["pgdata:/var/lib/postgresql/data"]}

redis: {image: redis:7-alpine}

6.3 Database Choices

6.4 Caching

6.5 Deployment Options - ITPP-INFRA Shared vs Dedicated

7. Security Model

7.1 Authentication & Authorization

7.2 Sanitization Gate (Extended for v3.6)

7.3 Prompt Injection Defense (New Surface: Rules Engine + URL Extraction)

7.4 Multi-Tenant Isolation (White-Label)

7.5 Abuse & Rate Limiting

8. Cost Model

8.1 Per-Review Cost Breakdown (v3.6, Pro/Enterprise Tier)

8.2 Margin Check by Tier

8.3 Cost Attribution by Component (Engineering View)

9. Implementation Sequence

Phase 0 - Foundation (Weeks 1-3)

Phase 1 - Coach & Intake (Weeks 4-6)

Phase 2 - Transparency & Depth (Weeks 7-12)

Phase 3 - Fix-It & Trust (Weeks 13-19)

Phase 4 - Distribution & GA (Weeks 20-25)

10. Failure Modes