How audit pages, extraction reviews, and improvement items close the loop back into the clone factory — for any expert, any iteration.
The clone factory produces extraction JSONs (L2), compiles them into a system prompt (L3), and tests the clone (L3.5). Automated testing scores 93.8%. But:
This PRD specifies the feedback loop — the system that takes human audit findings, routes corrections to the right upstream skill, triggers targeted re-runs, and tracks convergence until the expert signs off.
| Page | What It Tests | Items | Output |
|---|---|---|---|
| Audit v3 | Clone behavior — does it sound like the expert? | 10 scenario ratings + 5 questions + live chat + voice/text feedback | clone_feedback row + report + email |
| Extraction Review | Extraction accuracy — did we capture the right data? | 295 items across 6 JSONs (soul, voice, frameworks, resources, offers, FORGE+SHIFT) | clone_feedback row + improvement_items + report + email |
Audit Submit ──→ clone_feedback table
└──→ audit-analyze API (auto-fires)
└──→ improvement_items table (classified findings)
└──→ Improvement Dashboard (triage view)
└──→ ??? (MANUAL GAP)
Extraction Review Submit ──→ clone_feedback table
└──→ improvement_items table (per flagged item)
└──→ ??? (MANUAL GAP)
lens.feedback_loop field exists in block schema but is null for all 23 blocksfactory_block_id and factory_run_id columns exist on improvement_items but are unusedThree new blocks insert into the factory DAG between existing layers:
| Block ID | Layer | Type | Depends On | Human Gate |
|---|---|---|---|---|
| extraction-reviewer | L2.6 | validate | All L2 extractors (soul, voice, framework, resource, offer) | after |
| expert-audit | L3.6 | validate | clone-tester | after |
| corrections-router | L3.7 | orchestrate | expert-audit OR extraction-reviewer (whichever has findings) | none |
Purpose: Generate an extraction review page for the expert to verify all 6 extraction JSONs before compilation begins.
Inputs: All L2 extractor artifacts (soul.json, voice.json, frameworks.json, resources.json, offers.json) + expert-framework.json
Execution: Runs build-extraction-review.js --expert={slug} → produces HTML page → publishes to NowPage
Outputs: Published extraction review URL, extraction version tag
Gate: Expert submits review → improvement_items created → gate approved when 0 critical items remain or team accepts remaining items
Purpose: Generate an audit v3 page for the expert to validate clone behavior after compilation and automated testing.
Inputs: clone-compiler artifacts (system prompt, knowledge files) + clone-tester results
Execution: Generates audit page parameterized by expert slug → publishes to NowPage → expert runs audit
Outputs: Published audit URL, feedback_id, improvement_summary
Gate: Expert submits audit → audit-analyze fires → improvement_items created → gate approved when expert is satisfied
Purpose: Read improvement_items for this run, group by skill_target, and trigger targeted re-runs of upstream blocks with corrections as additional context.
Inputs: improvement_items where factory_run_id = {run_id} and status = 'accepted'
Execution:
pendingblock.state.corrections (new field)ready → they re-run with correction contextOutputs: List of re-triggered blocks, correction payloads
Gate: None — fully automated. The re-run cycle stops at the next human gate (expert-audit).
L0 deep-research ─┐
expert-recon ───┤ (parallel L0-recon)
masterybook-sync┘
│
L0.25 expert-framework-creator [gate: after]
│
L0.5 demo-compiler
│
L1 rubric-builder
│
L2 soul ─┐
voice ─┤
framework┤ (parallel L2-extractors)
resource ┤
offer ───┘
│
L2.6 extraction-reviewer [gate: after] ← NEW
│
L2.5 gap-analyzer [gate: after]
│
L3 clone-compiler ─┐
lead-magnet ─────┤ (parallel L3-builders)
onboarding ──────┤
design-system ───┘
│
L3.5 clone-tester [gate: after, threshold: 85]
│
L3.6 expert-audit [gate: after] ← NEW
│
L3.7 corrections-router ← NEW
│ (resets upstream blocks if corrections exist)
│ (re-triggers L2→L3→L3.5→L3.6 cycle)
│
L4 boarding-orchestrator (only reached when 0 corrections)
│
L5 marketing-page-builder ─┐
email-sequence-builder ──┤ (parallel L5-marketing)
stripe-setup ────────────┘
│
L6 audit-page-publisher ─┐
publish-orchestrator ──┘ (parallel L6-publishing)
When corrections-router executes, it:
SELECT * FROM improvement_items
WHERE factory_run_id = '{run_id}'
AND status = 'accepted'
ORDER BY priority DESC, skill_target
{
"voice-extractor": [
{ "source_text": "Too formal in high-energy mode",
"expert_correction": "I use 'Let's GO!' not 'Let us proceed'",
"priority": "high" },
...
],
"soul-extractor": [
{ "source_text": "Missing value: Community over competition",
"expert_correction": "I never rank people against each other",
"priority": "critical" },
...
]
}
New field: block.state.corrections
// Added to the targeted block's state before re-run
block.state.corrections = {
version: "v7.1→v7.2",
source: "expert-audit + extraction-review",
items: [
{ finding: "...", correction: "...", priority: "high" }
],
instruction: "IMPORTANT: The expert reviewed the previous extraction and flagged these corrections. Apply each correction to the output. Do not lose existing correct content — only fix the flagged items."
}
The execute endpoint already builds context from SKILL.md + inputs + upstream artifacts + gaps. Add corrections as a new context section:
// In /api/execute, when building the prompt:
if (block.state.corrections?.items?.length) {
contextParts.push(`
## EXPERT CORRECTIONS (MUST APPLY)
The expert reviewed the previous version and flagged these corrections.
Apply each one. Do not remove correct content — only fix flagged items.
${block.state.corrections.items.map(c =>
`- FINDING: ${c.finding}\n CORRECTION: ${c.correction}\n PRIORITY: ${c.priority}`
).join('\n\n')}
`);
}
After the targeted block completes successfully, mark the improvement_items as resolved:
UPDATE improvement_items
SET status = 'resolved',
resolved_in_version = '{new_version}',
resolution_notes = 'Auto-resolved via corrections-router re-run'
WHERE factory_run_id = '{run_id}'
AND skill_target = '{block_skill}'
AND status = 'accepted'
| Role | Weight | Who | What They Validate |
|---|---|---|---|
| Expert | 3.0 | Samuel Ngu | Voice accuracy, framework correctness, value alignment |
| Product Lead | 2.0 | Will Preble | User experience, framework completeness, offer accuracy |
| Tech Lead | 1.5 | Jason MacDonald | Governance, engineering gaps, tool behavior |
| Team | 1.0 | Derek, Aaron, Drew | General quality, user perspective |
| External | 0.5 | Beta testers | Fresh-eyes validation, usability |
Confirmed finding: Weighted score ≥ 2.0 across testers for the same item
Example: Samuel flags voice item (3.0) → auto-confirmed. Two team members flag same item (1.0 + 1.0 = 2.0) → confirmed. One external tester (0.5) → needs more validation.
Conflicting feedback: If one tester says "correct" and another says "needs work" for the same item, the higher-weighted tester wins. Ties escalate to triage.
Add to clone_feedback table:
ALTER TABLE clone_feedback ADD COLUMN tester_role TEXT DEFAULT 'team'; -- Valid: 'expert', 'product_lead', 'tech_lead', 'team', 'external'
Add to improvement_items table:
ALTER TABLE improvement_items ADD COLUMN consensus_score NUMERIC DEFAULT 0;
ALTER TABLE improvement_items ADD COLUMN flagged_by JSONB DEFAULT '[]';
-- flagged_by: [{ "tester": "samuel-ngu", "role": "expert", "weight": 3.0, "feedback_id": "..." }]
Aggregation runs after each audit submit — queries all improvement_items for the same expert_slug + source_scenario key, recalculates consensus_score across all testers who flagged it.
| Metric | Source | Goal |
|---|---|---|
| Version score (automated) | clone-tester results | ≥ 90% across all dimensions |
| Expert approval rate | audit v3 ratings | ≥ 80% "sounds like me" |
| Extraction accuracy | extraction review | ≥ 90% "correct" |
| Open critical items | improvement_items | 0 |
| Open high items | improvement_items | ≤ 3 |
| Iteration count | factory run versions | Typically 2-4 rounds |
Ready to ship when ALL of:
New page: align360.asapai.net/{expert}-convergence
Shows version-over-version comparison:
Version | Auto Score | Expert Rate | Extraction | Critical | High | Status ---------|------------|-------------|------------|----------|------|-------- v7.0 | 91.9% | — | — | 3 | 8 | superseded v7.1 | 93.8% | — | — | 0 | 5 | superseded v7.2 | — | 85% | 92% | 0 | 2 | current v7.3 | — | — | — | — | — | pending
This is the contract between the audit pages (folio-saas) and the corrections engine (process-factory).
Called by audit-analyze and extraction-review-submit after creating improvement_items. Notifies the factory that new corrections are available.
// Request
{
"run_id": "factory-run-uuid",
"expert_slug": "samuel-ngu",
"feedback_id": "clone-feedback-uuid",
"items_created": 5,
"by_skill": {
"voice-extractor": 2,
"soul-extractor": 1,
"clone-compiler": 2
},
"source": "expert-audit" | "extraction-review"
}
// Response
{
"acknowledged": true,
"run_status": "corrections-pending",
"blocks_to_rerun": ["voice-extractor", "soul-extractor", "clone-compiler"]
}
Query improvement_items for a factory run. Used by corrections-router.
// Request
GET /api/a360/improvement-items?run_id=xxx&status=accepted&skill_target=voice-extractor
// Response
{
"items": [
{
"id": "uuid",
"finding_type": "voice_gap",
"skill_target": "voice-extractor",
"priority": "high",
"source_text": "Too formal in high-energy mode",
"expert_correction": "I use contractions and exclamation marks when excited",
"consensus_score": 3.0,
"flagged_by": [{ "tester": "samuel-ngu", "role": "expert", "weight": 3.0 }]
}
],
"total": 1
}
Update improvement_item status after correction applied.
// Request
PATCH /api/a360/improvement-items/uuid
{
"status": "resolved",
"resolved_in_version": "v7.2",
"resolution_notes": "Re-extracted with correction context",
"factory_block_id": "voice-extractor",
"factory_run_id": "run-uuid"
}
-- 005-feedback-loop.sql -- 1. Tester role on clone_feedback ALTER TABLE clone_feedback ADD COLUMN IF NOT EXISTS tester_role TEXT DEFAULT 'team'; -- 2. Multi-tester consensus on improvement_items ALTER TABLE improvement_items ADD COLUMN IF NOT EXISTS consensus_score NUMERIC DEFAULT 0; ALTER TABLE improvement_items ADD COLUMN IF NOT EXISTS flagged_by JSONB DEFAULT '[]'; -- 3. Version tracking ALTER TABLE improvement_items ADD COLUMN IF NOT EXISTS prompt_version TEXT; ALTER TABLE improvement_items ADD COLUMN IF NOT EXISTS extraction_version TEXT; -- 4. Index for corrections-router queries CREATE INDEX IF NOT EXISTS idx_improvement_items_run_status ON improvement_items(factory_run_id, status) WHERE factory_run_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_improvement_items_expert_scenario ON improvement_items(expert_slug, source_scenario) WHERE source_scenario IS NOT NULL;
In lib/types.ts, add to BlockState:
corrections?: {
version: string; // "v7.1→v7.2"
source: string; // "expert-audit" | "extraction-review" | "both"
items: Array<{
finding: string;
correction: string;
priority: 'critical' | 'high' | 'medium' | 'low';
improvement_item_id: string;
}>;
instruction: string; // injected into LLM context
}
In /api/execute, when building the LLM prompt, check for block.state.corrections and prepend as a high-priority context section.
In /api/factory-run reject action, convert gate feedback into a corrections-compatible format and store in block.state.corrections so the next run actually uses it.
Add extraction-reviewer, expert-audit, and corrections-router to clone-factory-template.json with proper dependency wiring.
The corrections-router is unique — it doesn't call an LLM. It:
tester_role to audit v3 page (dropdown: expert/product_lead/tech_lead/team/external)GET /api/a360/improvement-items query endpointPATCH /api/a360/improvement-items/:id update endpointcorrections field to BlockState type/api/execute to inject corrections contextPOST /api/corrections-ingest endpointGET /api/a360/improvement-items endpoint. Session A builds it, Session B consumes it. Build Session A items 2-4 first.