← Back to file list
QC Execution
The hard validation and QC rules, including character-limit enforcement.
Workflow Rules · /home/jonas/social-carousel-codex/docs/qc-execution.md
Last modified: 2026-03-29T12:26:54.555Z
# QC Execution ## Purpose Phase 6 introduces the first deterministic QC helper: ```bash python3 scripts/runtime_state.py run-qc <idea_id> ``` This is the current gate between draft commit and preview generation. ## Inputs Default target: - the draft referenced by `ledger.latest_draft` Optional targets: ```bash python3 scripts/runtime_state.py run-qc <idea_id> --version V2 python3 scripts/runtime_state.py run-qc <idea_id> --path /tmp/draft.json ``` Required repo-local context: - `state/ideas/<idea_id>/ledger.json` - `state/ideas/<idea_id>/research-lock.json` - `state/ideas/<idea_id>/drafts/<version>.json` ## Current Checks `run-qc` writes: - `state/ideas/<idea_id>/qc-result.json` as the backwards-compatible latest pointer - `state/ideas/<idea_id>/qc-results/<draft_version>.json` as the explicit per-version QC record It currently evaluates: 1. `draft_shape_valid` 2. `research_lock_exists` 3. `draft_lineage_valid` 4. `research_bundle_matches_lock` 5. `source_ids_within_lock` 6. `claim_envelope_within_lock` 7. `citations_within_lock` ### `draft_shape_valid` Confirms the canonical authoring JSON has the required lineage-bearing fields: - `carousel_id` - `research_bundle_id` - `draft_version_id` - `created_at` - `source_ids_used` - `citations_exact` - `claim_ids_used` - `dropped_claim_ids` - `new_claims_added` - `slide_claim_map` - `slides` This same check also enforces the frozen `fact` contract used by the app: - component character limits - slide-order and slide-type assumptions - fixed CTA and sources literals - cover-highlight and inline-citation rules If a draft violates one of those limits, `run-qc` records the exact validator message in `qc-result.json` under `draft_shape_valid`. ### `research_lock_exists` Confirms the approved research boundary exists before preview can proceed. ### `draft_lineage_valid` Checks: - `V1` may be a root draft with `parent_version_id: null` - later versions must point to an existing parent draft file ### `research_bundle_matches_lock` Confirms the draft still belongs to the active approved research bundle. ### `source_ids_within_lock` Confirms: - `source_ids_used` is a subset of the lock source ids - content-slide `source_ids` stay inside the same allowed set ### `claim_envelope_within_lock` Confirms: - `claim_ids_used` stays inside `research-lock.json.allowed_claims` - `new_claims_added` does not introduce unsupported claims outside the lock This is the current deterministic enforcement point for “no unsupported claim expansion against the lock.” ### `citations_within_lock` Confirms: - `citations_exact` stays inside the approved source citation map - the sources slide citations stay inside the same approved citation set ## Result File Canonical path: - `state/ideas/<idea_id>/qc-result.json` - `state/ideas/<idea_id>/qc-results/<draft_version>.json` Current shape: - `schema_version` - `idea_id` - `checked_at` - `draft_version` - `draft_path` - `research_lock_path` - `status` - `checks` Each check entry stores: - `name` - `status` - `details` on pass or `message` on fail The latest file stays in place for backwards compatibility, but the per-version directory is now the authoritative history model for traceability across revisions. ## Character-Limit Enforcement The source of truth for component limits is: - `apps/carousel-web/contracts/templates/fact.json` Those limits are enforced in the app layer through: - `apps/carousel-web/src/lib/schema.ts` - `apps/carousel-web/src/lib/template-validators.ts` They are enforced in the deterministic helper/QC layer through: - `scripts/runtime_state.py validate_authoring_draft` - `scripts/runtime_state.py validate_preview_payload` - `scripts/runtime_state.py run-qc` Current app-dictated limits: | Element | Field | Limit | | --- | --- | --- | | Cover | `cover_headline` / `hookText` | 35-70 visible characters | | Cover | highlighted phrase inside cover | 2-28 visible characters | | Cover | `cover_kicker` / `ctaText` | 24 characters max | | Cover | `cover_nuance` | 42 characters max | | Content | `pill_label` / `title` | 1-16 characters | | Content | `content_body` / paragraph | 70-150 characters | | CTA | `cta_keyword` | 3-6 uppercase letters | | CTA | `cta_payoff` / `subtext` | 140 characters max | | Sources | `citations` | 1-4 entries | | Request | total slides | 20 max | | Request | slide counts | `cover` exactly 1, `content` at least 2, `cta` exactly 1, `sources` at least 1 | Related non-length contract rules that still fail deterministically: - cover must contain exactly one highlighted phrase - cover must be sentence-case - highlighted cover phrase cannot be the entire headline - content paragraphs must not contain inline citation markers - CTA must render as `Comment **KEY**` - CTA payoff must begin with `to get` - sources disclaimer and brand fields must match fixed literals ## Violation Surfacing And Revise/Retry Path Character-limit violations are surfaced in more than one deterministic place: 1. `commit-draft` rejects the draft before it is persisted. 2. `run-qc` records the failure in `qc-result.json` if an invalid draft is evaluated. 3. `build-preview-payload` rejects invalid authoring JSON before preview build. 4. `apps/carousel-web/src/app/api/preview/generate/route.ts` returns structured `validationErrors` from the app schema. Expected workflow behavior: - stop preview progression - revise the draft - recommit the draft version or a new version - rerun QC - retry preview generation only after validation passes ## Downstream Preview Handoff After QC passes: - `build-preview-payload` now derives a version-specific preview folder name from the idea id and draft version - later preview revisions no longer overwrite older preview artifacts in place by default - if `verify-preview` also passes a public-host check, the public verified URL becomes the canonical review URL recorded in `preview.json` and the ledger ## Pass Behavior On pass: - the command exits `0` - both the latest QC file and the versioned QC file are written - `latest_verified_draft` is set in the ledger - `revision.latest_verified_version` is updated - the ledger moves to: - `stage: preview_ready` - `current_action: build_preview_payload` - `current_action_status: ready` - an event is appended with `event_type: QC_PASSED` - a checkpoint snapshot is written ## Fail Behavior On fail: - the command exits `1` - both the latest QC file and the versioned QC file are still written - `latest_verified_draft` stays unset - `retry_counters.validation` increments - the ledger moves to: - `stage: validation_in_progress` - `current_action: revise_draft` - `current_action_status: blocked` - an event is appended with `event_type: QC_FAILED` - a checkpoint snapshot is written ## Verified Local Examples Happy-path example: - `IDEA-local-phase6-helpers` - QC status: `pass` Blocked-path example: - `IDEA-local-phase6-qc-fail` - QC status: `fail` - failure message: `draft uses claim_ids outside the approved lock: C99` ## Current Limits This is intentionally the first deterministic QC layer, not the final one. Not yet implemented: - deeper semantic comparison against prose-level meaning changes - richer preview render assertions beyond the bounded verification step in `verify-preview` - automatic reclassification from QC failures into revision lanes ## Copy Review Expectation The deterministic validator should stay narrow. The stronger final copy review expectation is currently documented guidance rather than a brittle hard blocker. Before preview, the workflow should still check for: - whether a curiosity-led cover stays honest once later slides carry the nuance - slide-to-slide non-repetition - one distinct editorial role per content slide - logical story progression - standalone readability for each content slide - at least 4 content slides by default unless a shorter format is explicitly justified - evidence caveats rewritten into natural consumer language rather than academic-sounding disclaimers
Save
Ready