# Report-Gates Defect Glossary This reference catalogs every defect code the report gates (`static`, `fidelity`, `layout`, `visual`, `proofread`) can raise, their severity and impact, and how to repair them. ## Defect Codes by Category ### Static Gate (Content & Structure) | Code | Severity | Integrity | What It Detects | How to Fix | |------|----------|-----------|-----------------|-----------| | `korean_no_keep_all` | major | false | Text containing Hangul uses default word breaking, causing character-level wrapping and orphaned characters on mobile or narrow containers. | Add "word-break: keep-all; overflow-wrap: anywhere" to the body or the slide text container. | | `palette_off_token` | minor | false | Color values are hardcoded literals (hex, rgb) instead of design-system tokens or are missing from the design spec palette. | Replace the literal color with a design-spec token (var(--...)) or add the color to the spec palette. | | `emoji_in_prose` | major | false | Emoji characters appear in body text, headings, or figure captions. Emoji render at inconsistent sizes and lack semantic meaning. | Remove the emoji; use an inline SVG icon or plain text. | | `em_dash_in_prose` | major | false | Em dashes (—) are used in body text for sentence breaks or parenthetical asides. Readability and accessibility suffer without a rewrite. | Rewrite the sentence with a comma, colon, or period instead of an em dash. | | `en_dash_in_prose` | minor | false | En dashes (–) are used incorrectly outside of numeric ranges. Only numeric ranges (e.g., "pages 5–10") should use en dashes. | Use a hyphen for compounds; keep the en dash only between numbers. | | `heading_too_long` | minor | false | A heading exceeds one visual line: 26 Korean characters (52 display width units) or 12 English words. | Shorten the heading to one line (26 Korean characters or 12 English words). | | `heading_generic` | minor | false | A heading uses generic labels (e.g., "Introduction", "Background") without conveying the section's specific claim or finding. | Make the heading carry the section's claim instead of a generic label. | | `unsourced_number` | major | false | A numeric value (percentage, count, duration, ratio) appears without a source attribution, measurement tag, or derivation chain. | Tag the number MEASURED / ASSUMED / DERIVED or cite its source in the same paragraph. | | `chart_without_figure` | major | false | An image or chart lacks a figure wrapper with a caption. Uncaptioned charts isolate visuals from narrative flow. | Wrap the image or chart in a figure container with a caption. | | `chart_svg_no_text` | major | false | An SVG chart or diagram lacks a title, axis labels, units, or value labels. Visuals cannot stand alone without text guidance. | Give the chart a title, axis labels, units, and value labels as SVG text. | | `chart_svg_missing_labels` | minor | false | An SVG chart has some labels (e.g., title) but is missing others (e.g., axis ticks or unit). | Add the missing title, axis ticks, or unit label to the chart. | | `report_without_visual` | major | false | The report body has no chart, diagram, or figure image at all (header logos, icons, and images marked decorative do not count; only rendered output counts, so a Mermaid source block or an image with any zero dimension (attribute, inline style, or viewBox) does not; an element with no declared size counts, because the browser sizes it, and the layout gate is where rendering is proven). A text-only report hides structure and trends the reader would see at a glance. | Add at least one captioned visual of the findings: a chart from the data table, a rendered Mermaid diagram, or a figure image (references/report-visualization.md). | | `image_missing_alt` | minor | false | A figure image has a missing or empty `alt` and is not marked decorative. Screen readers and failed image loads leave the reader with nothing. An image is decorative only with an empty `alt` plus `role="presentation"` (or `role="none"`, or `aria-hidden="true"`): it raises nothing here and does not count as a visual. | Give the image an alt attribute that states what it shows, not its file name; mark a purely decorative image alt="" with role="presentation". | | `korean_serif_font` | major | false | A serif or brush Korean font family is applied to Korean text. The design spec mandates gothic (sans) Korean typefaces. | Use a gothic (sans) Korean family; serif and brush families are banned by the design spec. | | `section_without_citation` | major | false | A major section or subsection contains no in-text citations or reference marks. Source attribution is missing. | Cite at least one source inside this section or merge it into a cited one. | | `missing_closing_section` | blocker | true | The report lacks a closing section explaining the methodology, sources, and gate outcomes. Delivery is incomplete without narrative closure. | Add the closing section that explains how the report was made (method, sources, gates). | | `missing_citation_section` | blocker | true | The report has no sources, references, or citation definitions (e.g., [S1], [S2]). Attribution is impossible. | Add a sources or references section, or [S] definition lines. | | `broken_asset_reference` | blocker | true | An asset file (image, diagram, spreadsheet) is missing or unreadable. The deliverable cannot be rendered or is incomplete. | Re-render or restore the missing asset file before delivery. | | `missing_required_section` | blocker | true | A section explicitly required by the brief (e.g., via `--require-section`) is absent. | Add the section the brief requires (matched by --require-section). | | `missing_promised_deliverable` | blocker | true | A deliverable (e.g., a slide deck, a chart, a data file) is promised in the brief but not delivered, not marked blocked_capability, and not skipped with a reason. | Produce the promised deliverable or record it as blocked_capability or skipped with a reason. | ### Fidelity Gate (Facts, Quotes & Meaning) Raised only by the fidelity verifier (the fact ledger, fill, verify, meaning, and polish steps), never by `check`. Until that verifier ships in an edition, the manifest records `fidelity: not_run`. | Code | Severity | Integrity | What It Detects | How to Fix | |------|----------|-----------|-----------------|-----------| | `unresolved_placeholder` | blocker | true | A fact or quote placeholder is left in the artifact, names an unknown ledger id, is malformed, or uses an unsupported mode. | Register the fact with fact add or remove the sentence; a placeholder is never filled with a guess. | | `value_mismatch` | blocker | true | A displayed value differs from its ledger row's rendering, or a stored derived value differs from exact recomputation (a hand edit after fill). | Re-run fact fill; never edit a filled value by hand. | | `unit_mismatch` | blocker | true | A displayed, adjacent, or header unit conflicts with the ledger row's unit or scale. | Re-run fact fill, or derive a converted row with fact derive --op convert. | | `quote_not_verbatim` | blocker | true | A quotation bound to a quote row does not match the source snapshot contiguously after normalization, or a rendered cut is not the registered cut. | Re-insert the quote through its {{Q}} placeholder; after a second failure the sentence is replaced by the quote. | | `claim_status_invalid` | blocker | true | A sentence's claim binding names an unknown claim, a refuted or unresolved claim outside Gaps and Contradictions, or a high-risk claim absent from the verified claims. | The claim id is unknown, refuted, unresolved, or not in verified-claims; move the sentence to Gaps or Contradictions, or remove it. | | `incompatible_operands` | blocker | true | A derivation's operands differ in unit, population, period, or accounting basis where the operation requires them to match; derive refuses and writes no row. | The operands differ in unit, population, period, or basis for this operation; fix the rows or declare the operation that fits. | | `pointer_label_mismatch` | blocker | true | A placeholder's label differs from the referenced ledger row's label (a typo guard on the id). | The placeholder's label is not the row's label; check the id, then the label. | | `quote_anchor_unresolved` | blocker | true | A quote cut's from or to anchor is absent, occurs more than once, or appears in reverse order in the quote. | A from or to anchor is missing or occurs more than once in the quote; pick unique anchor words. | | `unit_duplicated` | blocker | true | A unit-bearing placeholder is followed by another copy of its own unit. | The unit after the placeholder repeats the row's unit; use :value or remove the unit. | | `polish_diff_fail` | blocker | true | The polish pass changed protected fact spans, numerals or units, tickers, table rows, or any markup byte (including every attribute, comments, and script/style/template content). | Revert to the pre-polish artifact and apply text-node edits only; markup changes require the full fidelity gates. | | `fidelity_interview_incomplete` | blocker | true | A fidelity field in the brief is still pending or missing when a fidelity command runs. | A fidelity field is pending or missing in brief.md; answer the open question or record its default, then rerun. | | `derived_not_allowed` | blocker | true | A placeholder resolves to a derived, converted, or assumed row while the brief allows no derived numbers. | The brief allows no derived or assumed numbers; cite a source row directly or change the brief with the requester. | | `source_outside_floor` | blocker | true | A ledger row's source lies outside the source floor the brief records (pointed sources only, primary only, or the allowed source classes), or the row rests on no source under any floor but `standard`. | The row's source is outside the source floor the brief records; use a source the brief allows or remove the fact. | | `brief_changed` | blocker | true | `fact verify` reads an edition's fill map whose recorded brief hash is not the hash of the current locked `## Fidelity` block, or that records none. | The edition's fill map was not written under the current ## Fidelity block (it changed after fill, or the map predates the binding); re-run fact fill, then fact verify. | | `snapshot_hash_mismatch` | blocker | true | A pinned source snapshot's text no longer hashes to its recorded hash, or indexed slices no longer equal the snapshot text. | The source snapshot changed after it was pinned; re-snapshot the source and re-register its rows. | | `chart_value_untraceable` | blocker | true | A chart data label, axis tick, or chart data file does not match the ledger-backed data the chart declares, or the chart declares no data source. | A chart value, tick, or data file does not match its ledger-backed data source; re-render the chart from the exported data. | | `quote_unbound` | blocker | true | A direct quotation in the report has no ledger quote row behind it; a citation marker alone does not bind it. | A direct quotation has no ledger quote row; register it with fact quote and insert it through its placeholder. | | `edition_unverified` | blocker | true | A delivered edition has no fact verify record whose artifact hash matches the delivered source. | The delivered file has no matching fact verify record; run fact verify on that edition and record it with --verified-by. | | `untraceable_number` | major | false | A number or date in prose or a table cell resolves to no ledger row and is not structural; a citation does not exempt it. | Replace the typed number with a {{F}} placeholder backed by a ledger row, or register it as assumed with a rationale. | | `unsupported_verification` | major | false | A fact bound to a `verified` mark (`data-claim-kind="verified"`, from a `{{V:F:label}}` placeholder) cites a row whose own locator artifact does not sit in a recorded command-output snapshot, or the mark binds no fact of its own. Another command row in the same sentence never counts. Unmarked prose is never raised here. | A sentence marked verified (data-claim-kind="verified") cites a row that is not recorded command output; cite the recorded output row or drop the verified mark. | | `claim_kind_mismatch` | major | false | A fact bound to a claim kind cites a row of a kind that claim kind does not accept, a fact sits under marks of two different kinds (nested marks), a mark binds no fact of its own, or a mark names an unknown kind; the kinds are in [fidelity-claim-kinds.md](fidelity-claim-kinds.md). | An element marked with a claim kind (data-claim-kind) cites a row that kind does not accept, or the kind is unknown; cite a row of that kind or fix the mark (references/fidelity-claim-kinds.md). | | `conflicting_readings` | major | false | Two active ledger rows with the same value and unit but different periods or qualifiers are cited as one fact. | Two ledger rows with different periods or qualifiers are cited as one fact; split the sentence or retire one row. | | `hedge_dropped` | major | false | A hedge, condition, or negation class present in the source quote is absent from the report sentence. | Restore the qualifier the source uses (estimated, about, not yet). | | `direction_flipped` | major | false | The source states an increase and the sentence a decrease, or the reverse. | The source states the opposite direction; rewrite from the quote. | | `causality_added` | major | false | The sentence carries a causal connective that no supporting quote or claim statement contains. | The source states no cause; state the correlation or quote the source. | | `qualifier_dropped` | major | false | A bounding or approximation qualifier recorded on the ledger row (up to, at least, about) is absent near the value in the sentence. | The source bounds this value (up to, at least, about); restore the qualifier or use :q. | | `unbound_sentence` | major | false | A findings sentence carries no fact, quote, citation marker, or claim binding and is not marked as opinion or recommendation. | Bind the sentence to a fact, a quote, a citation, or a claim-graph node with data-claim. | | `meaning_drift_confirmed` | major | false | Two independent meaning readers flag the same drift kind on the same sentence and quote pair. | Two independent readers found the same drift; rewrite from the quote or dispute with a reason. | | `meaning_unverified` | major | false | `fact meaning-check` found a pair no reader judged, or a force_strong pair no strong reader judged; an empty drift list is not a clean check. | No meaning reader judged this pair, or no strong reader judged a force_strong pair; run the missing reader on pairs.json and rerun fact meaning-check. | | `polish_edit_rejected` | major | false | A proposed polish edit changes a protected number, hedge, qualifier, link, or claim id. | The edit changed a number, a hedge, a qualifier, a link, or a claim id; re-propose the language fix only. | | `polish_hedging_decreased` | major | false | The polish pass removed a hedge the source grounds. | The polish pass removed qualifiers; restore them. | | `polish_text_churn` | major | false | More than half of the visible prose changed during the polish pass (`POLISH_CHURN_LIMIT` in `scripts/polish-rules.mjs`). | More than half the text changed during polish; the pass composed instead of proofreading. | | `snapshot_mismatch` | major | false | An operation that needs one collection window uses operands from unequal windows or observations outside the window. | The operands come from different collection windows; use rows from one window or state the windows separately. | | `join_integrity_fail` | major | false | A table row combines facts from different source observation rows, or a declared observation id contradicts the row's lineage. | A table row combines facts from different source observations; keep each row to one observation or split it. | | `quote_stale_version` | major | false | The quoted source revision differs from the revision the report targets for the same scope. | The quoted source revision differs from the revision the report targets; re-snapshot the target revision or date the quote. | | `quote_fuzzy_match` | minor | false | An exact quote match failed but a candidate with similarity of at least 0.95 and identical protected tokens exists (OCR noise); diagnostic only. | The source text is OCR-noisy; confirm the snapshot and prefer the raw document. | | `name_outside_quote` | minor | false | An uppercase Latin token of 2 to 6 letters in the sentence is absent from the supporting source text and that source's acronym table. | A name in the sentence is not in the cited quote; add the source or remove the name. | | `verification_claim_unmarked` | minor | false | A sentence reads as a first-person run ("we ran", "we have run", "I ran", "we executed", "we verified by", "by running" after we/our, or the Korean rule in fidelity-claim-kinds.md) and cites a fact not marked `verified` (or cites none and its block is unmarked). A warning only; it never blocks. | The sentence reads as a check we ran but carries no verified mark; mark it {{V:F:label}} on a recorded command-output row, or reword it. | | `scope_widened_warn` | minor | false | The sentence uses a broader scope term than the quote's narrow term, and no supporting fact carries the broad scope. | The sentence uses a broader scope word than the quote; confirm the scope or narrow it. | | `particle_unverified` | minor | false | A particle after a filled value could not be checked against the value's final reading. | The particle after this value could not be checked; read it aloud and fix it by hand if wrong. | | `fallback_without_entity` | minor | false | A quote that replaced a twice-failed sentence has no entity for its attribution line. | The replacement quote has no entity for attribution; set one on the row. | | `meaning_drift_warn` | minor | false | One meaning reader flags a change of scope, subject, or certainty without independent agreement. | A separate reader found the sentence changes scope, subject, or certainty; rewrite from the quote. | | `polish_edit_unlocated` | minor | false | A polish edit's before-text matches zero or several places in the current text. | The edit's before-text was not found exactly once; re-propose against the current text. | | `polish_link_changed` | minor | false | The polish pass altered or removed an href link target, or an internal target stopped resolving. | A link was altered or removed during polish; restore it. | | `polish_name_changed` | minor | false | A ledger-backed proper noun present before polish is absent afterward. | A proper noun changed during polish; restore it. | ### Layout Gate (Rendering & Presentation) | Code | Severity | Integrity | What It Detects | How to Fix | |------|----------|-----------|-----------------|-----------| | `layout_overflow` | major | false | An element exceeds its container width or height, causing horizontal scroll or content spillage in PDF, DOCX, or slide renders. | Constrain the element to its container (max-width: 100%, overflow-wrap: anywhere, or a narrower measure). | | `layout_text_clipped` | major | false | Text is clipped or hidden by `overflow: hidden` or a fixed container height. Content is lost or unreadable. | Remove the overflow: hidden clip or give the text room to wrap. | | `layout_scroll_container` | minor | false | A table or list is wrapped in a scrollable container for HTML deliverables. Scrolling content prints clipped in PDF or DOCX. | Scrolling content prints clipped; make the content fit or wrap the table in a scroll wrapper only for HTML deliverables. | | `layout_image_distorted` | major | false | An image is distorted, stretched, or squeezed (aspect ratio is not preserved). The visual information is misrepresented. | Preserve the image aspect ratio (object-fit: contain inside a fixed container). | | `layout_sibling_overlap` | major | false | Two adjacent block elements overlap (via negative margins, absolute positioning, or grid collision). Layout is broken. | Separate the overlapping elements (margins, grid, or a slide split). | ## Exit Codes Gates emit a manifest-level outcome with one of three statuses: | Status | Meaning | |--------|---------| | `pass` | All checks passed; no defects raised. | | `fail` | At least one blocker defect was raised. Major and minor defects alone leave the gate at `pass`; they still go to the repair loop. | | `not_run` | The gate was not invoked (e.g., no probe file provided for layout, no visual inspection conducted). | ### Layout Gate Probe File Requirement The `layout` gate only runs when a probe file (a rendering artifact: PDF, PNG, or HTML snapshot) is provided alongside the manifest. If no probe file is available, the manifest records `layout: not_run` and the gate does not emit defects. ## Polish Text-Node Contract Polish edits change text nodes only; markup changes go through the full fidelity gates, not polish. The primary rule is a whole-document skeleton comparison. Both gates scan the pre-polish and the polished document with the same tokenizer (`scripts/html-markup.mjs`) and require the skeleton to be byte-identical in document order: every tag token, comment, bogus comment, the frozen content of `script`, `style` and `template`, and every character reference of a text node (`&` up to the first `;` or non-name character). An accepted edit carries no `<`, `>` or `&` and may not touch a character reference, so an edited span holds no skeleton token and a legitimate polish never moves this sequence. Any difference names the first differing token and its byte offset: `polish-apply` refuses the edit, and `polish-diff` reports the blocker `polish_diff_fail` ("skeleton changed at token ..."). The character guards below are the early, actionable refusals; this comparison is what makes the guarantee complete, so a hand edit from `R&D` to `R<D` is blocked even though no edit was involved. An edit's `before` and `after` may not contain `<`, `>` or `&` at all. `polish-apply` refuses such an edit before any other check, as `polish_edit_rejected` with the reason `edit contains markup characters (<, >, &): route this section through the full fidelity gates`, so the refusal is visible, never a silent drop. The rule holds whatever the tokenizer models: an edit without these characters cannot create, alter, close or escape a tag, comment, bogus comment, CDATA section, entity or script. The characters alone are not enough, because a located region can start right after an existing `&` or inside a character reference: `polish-apply` also refuses, as `edit touches a character reference: route this section through the full fidelity gates`, any edit whose located region overlaps a character reference or opens one. Literal `&` and `<` in prose are entities in the source (`R&D`, `p < 0.05`) and stay usable only by leaving their bytes untouched: the proofread lane narrows an edit to the words around an entity or tag (`budget grew` to `budget rose`), never one that spans it. Wording that cannot be fixed without touching an entity or markup goes through the full fidelity gates. Before comparing, both gates refuse with `polish_diff_fail` ("ambiguous markup: ...") any document holding a construct the polish tokenizer does not model exactly: `` included), ``, or `<` inside a tag or attribute value. Renderer output contains none of these. The tokenizer still follows the HTML spec for the bogus comment (``), the abrupt comment closes `` and ``, and the script-data escaped and double-escaped states. The shared rule in `scripts/polish-rules.mjs` runs per edit against the current and original report in `polish-apply`, and against the whole document in `polish-diff`. The sequence of opening and closing tags, every attribute name and value (including `href`, `id`, `class`, and `style`), comments, declarations, and all content of `script`, `style`, and `template` elements must be byte-identical. Attribute reordering, different quoting, tag case, whitespace inside a tag, and a self-closing slash are markup changes too. Any difference raises the blocker `polish_diff_fail`; apply rejects it with reason `markup changed`. Text nodes remain subject to the existing fact-span, number/unit, ticker, hedge, qualifier, name, and churn checks. Protected numbers, tickers, and required hedges cannot migrate between text nodes. A text edit inside an existing fact span is still a fact-span change, including under `aria-hidden`. Script/style/template content is frozen through the end of input when unclosed. No CSS or HTML visibility model is used by polish. ## Lineage and Citation Markers Defect codes and their hints are delivered via the outcome manifest under `residualDefects` (if repair did not exhaust). Each defect entry includes: - **code**: A string key from the table above (e.g., `"em_dash_in_prose"`) - **message**: A human-readable narrative or location hint describing where the defect was found (e.g., "Line 42: em dash found in paragraph 2") Citations within reports follow one of two lineage modes: - **inline**: Source marks appear as `[Sn]`, `[Source n]`, or `[^n]` hyperlinks inline with the cited text. - **section**: Sources are listed in a dedicated section with numbered entries ([S1], [S2], etc.). Both modes are valid; choose based on the report's narrative flow.