-
Notifications
You must be signed in to change notification settings - Fork 1
fix: preserve semantic document evidence units #302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
seonghobae
merged 22 commits into
feat/analysis-run-name-evidence-lineage
from
feat/buyer-evidence-gap-structure
Aug 21, 2026
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
48aebe2
fix: preserve semantic document evidence units
seonghobae 62d4ed2
merge: retain latest evidence timeout improvements
seonghobae f08ccb9
merge: retain current parent security fixes
seonghobae d3bc76f
fix: classify numeric superscript footnotes after prose
seonghobae 40b0a8e
merge: propagate current semantic-source parent
seonghobae da3fd48
Merge remote-tracking branch 'origin/feat/analysis-run-name-evidence-…
seonghobae fa7532f
merge current analysis-run parent into evidence stack
seonghobae 790d644
fix: scope numeric footnotes by paragraph
seonghobae a111d8c
fix: preserve independent list boundaries
seonghobae b5f86b9
fix: keep table-cell lists in row chunks
seonghobae 8ed7eac
test: close semantic chunking coverage gaps
seonghobae 6ecd42b
Merge remote-tracking branch 'origin/feat/analysis-run-name-evidence-…
seonghobae 8e1bd78
Merge branch 'feat/analysis-run-name-evidence-lineage' of https://git…
seonghobae 1ab4f2f
merge: restack buyer evidence on current #258
seonghobae 192373b
docs: pin current customer tree evidence
seonghobae db5852a
merge: restack buyer evidence on latest #258
seonghobae 1a317f2
merge: restack buyer evidence on finalized #258
seonghobae ed60fb6
Merge analysis-run-name evidence lineage into buyer evidence
seonghobae 6fe5c17
chore: restack semantic evidence on current analysis parent
seonghobae 10c8d2a
fix: preserve structured footnote blocks in body fallback
seonghobae ef907e4
fix: preserve semantic image evidence tables (#303)
seonghobae 83e0b76
merge: reconcile semantic evidence with current analysis parent
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| # ADR 0103 — Preserve semantic document evidence across source and buyer views | ||
|
|
||
| **Decision status:** Proposed on the stacked product-gap branch | ||
| **Date:** 2026-08-20 | ||
| **Figma File ID:** `1Su3lDRmiZdcUs47t1QwIX` | ||
| **Related baseline:** [Product and Technical Gap Baseline](../product-technical-gap-baseline.md) | ||
|
|
||
| ## Context | ||
|
|
||
| Live aggregate inspection identified recurring buyer-visible loss at the | ||
| source boundary: superscript footnotes, nested list order/depth, table rows, | ||
| and Markdown tables were not represented consistently between Python | ||
| ingestion, PostgreSQL units, and the React popup. A flattened string cannot | ||
| reconstruct a table row, a branch in a list, or the position of an image. It | ||
| also makes a later LLM summary less auditable. | ||
|
|
||
| The HTML Living Standard defines the semantic elements used by the source | ||
| boundary, including `ol`, `li`, `table`, `tr`, and `sup`. CommonMark provides a | ||
| versioned baseline for Markdown block parsing; table syntax remains an | ||
| extension in many Markdown dialects, so the implementation accepts only a | ||
| recognizable header/separator/data shape and otherwise preserves plain text. | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. Parse source content into ordered semantic units before embedding or | ||
| summarization. A unit retains its source label, source order, indentation | ||
| metadata, and image position. | ||
| 2. Group HTML and OOXML table cells into row units. Markdown tables are | ||
| recognized only when a header row is immediately followed by a separator | ||
| row; the separator itself is not evidence content. | ||
| 3. Treat explicit CSS/OOXML indentation as authoritative. List-container | ||
| nesting contributes structural depth but must not double-count an explicit | ||
| source width. | ||
| 4. Mark a numeric footnote only when the source uses a numeric `sup` marker; | ||
| a numeric table cell or ordinary numbered text is not a footnote by itself. | ||
| 5. Keep the frontend's raw-source fallback aligned with the persisted unit | ||
| labels. Persisted row units render as accessible tables; unresolved | ||
| structure remains visibly unresolved and actionable. | ||
| 6. Apply the same narrow Markdown-table renderer to persisted image OCR. | ||
| VISION output may use multiple `TEXT` lines so row boundaries survive; its | ||
| caption names only visible entities, relationships, layout, and document | ||
| purpose rather than offering a generic one-sentence description. The | ||
| client allows 600 seconds for deep orchestrator work; a 180-second local | ||
| cutoff already terminated a valid live response before delivery. | ||
| 7. Serialize replacement per source post and reject a same-image retry when | ||
| its content hash matches non-empty persisted OCR but the retry returns no | ||
| OCR. Provider completion is transport evidence, not permission to erase a | ||
| stronger prior observation. During an operator backfill, this typed | ||
| preservation failure skips only the affected post, records it in the | ||
| aggregate result, and allows the remaining selected posts to continue. | ||
|
|
||
| ## Rejected alternatives | ||
|
|
||
| - Flattening all bodies into one embedding string: loses row, list, and image | ||
| boundaries and cannot be repaired at display time. | ||
| - Treating every leading number as a footnote: mislabels table rows and | ||
| numbered instructions. | ||
| - Calling a provider directly from the parser: violates the orchestrator | ||
| trust boundary and makes evidence/cost lineage incomplete. | ||
| - Creating a separate parsing service: the existing shared chunker and | ||
| persistence boundary are sufficient; Ponytail favors the smaller change. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Search and summaries receive smaller, meaningful units without exposing raw | ||
| markup or image base64. | ||
| - The database keeps the existing normalized unit tables; this decision adds | ||
| no denormalized JSON field or new service. | ||
| - A weaker same-image VISION retry fails before replacement, leaving the | ||
| prior committed evidence available for a later orchestrator retry. | ||
| - A protected retry does not abort an entire operator batch; the skipped-post | ||
| count is visible to the operator without exposing raw post content. | ||
| - Markdown dialects outside the narrow recognized shape remain plain text and | ||
| are reported as a future parser extension rather than guessed. | ||
|
|
||
| ## Verification | ||
|
|
||
| The baseline's synthetic tests cover numeric superscript footnotes, marker | ||
| footnotes, nested `ol`/`ul`/`oi` order and depth, HTML/OOXML rows, Markdown | ||
| rows, React table rendering, and unresolved indentation. Full CI remains the | ||
| release gate. A persistence regression test proves that an empty same-hash | ||
| VISION retry cannot delete previously observed OCR. |
55 changes: 38 additions & 17 deletions
55
docs/adr/0105-mathematical-script-semantic-normalization.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,50 @@ | ||
| # ADR 0105: Preserve explicit metric scripts in semantic text | ||
|
|
||
| - Status: Accepted | ||
| - Date: 2026-08-21 | ||
| **Status:** Accepted on this PR; not protected-main truth | ||
| **Date:** 2026-08-21 | ||
| **Owners:** LineageWeave ingestion and buyer-surface maintainers | ||
|
|
||
| ## Context | ||
|
|
||
| Source posts may encode a metric unit such as `m<sup>3</sup>` or an indexed | ||
| quantity such as `m<sub>3</sub>`. Removing the script element loses searchable | ||
| and buyer-visible mathematical meaning, while treating every numeric | ||
| superscript as mathematics would break the existing footnote contract. | ||
| Source posts commonly encode a unit such as `m<sup>3</sup>`, `m<sub>3</sub>`, | ||
| `m^3`, or `m_3` with HTML or plain-text notation. Dropping the markup changes | ||
| the searchable meaning to `m3`, while treating every numeric `sup` element as | ||
| mathematics would break the existing numeric-footnote contract. Full MathML | ||
| parsing is not yet justified by the current product surface, but the loss of | ||
| explicit unit scripts is a buyer-visible defect. | ||
|
|
||
| MathML 4 defines `msup`, `msub`, and `msubsup` as structural script elements; | ||
| HTML `sup`/`sub` are a permitted lighter-weight notation when detailed | ||
| mathematical markup is not required. This decision therefore adds a bounded | ||
| normalization boundary and keeps the source representation unchanged. | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. Preserve the original source body unchanged. | ||
| 2. In derived semantic text, normalize only an explicit bounded metric base | ||
| 1. Preserve the immutable source body exactly as imported. | ||
| 2. In derived semantic text only, normalize an explicitly bounded metric base | ||
| (`m`, `cm`, `mm`, `km`, or `kg`, optionally preceded by a number) followed | ||
| by one-to-three numeric `sup` or `sub` elements into Unicode | ||
| superscript/subscript digits. For example, `5m<sup>3</sup>` becomes `5m³`. | ||
| 3. Leave ordinary numeric superscripts on prose under the existing footnote | ||
| role contract. | ||
| 4. Apply the same normalization in backend chunks and frontend rendering. | ||
| by numeric `sup`/`sub` markup or plain-text `^`/`_` notation into Unicode | ||
| superscript/subscript digits. For example, `5m<sup>3</sup>` and `5m^3` | ||
| become `5m³`, while `m<sub>3</sub>` and `m_3` become `m₃`. | ||
| 3. Keep ordinary numeric superscripts and caret expressions on prose under the existing footnote | ||
| role contract. Do not infer a mathematical formula from an arbitrary word. | ||
| 4. Apply the same bounded normalization in backend semantic chunks and the | ||
| React buyer display so search text and visible text agree. | ||
| 5. Defer full MathML/LaTeX parsing, expression trees, and ontology term | ||
| creation until an authorized fixture demonstrates a need beyond metric | ||
| scripts. Any such change requires a new ADR and parser contract. | ||
|
|
||
| ## Consequences | ||
|
|
||
| Metric exponents remain searchable and readable without inventing formula | ||
| semantics. Arbitrary mathematical markup beyond this bounded case remains an | ||
| explicit open gap and must be covered by a later ADR and fixture before being | ||
| normalized. | ||
| - Search and the buyer popup retain the visible distinction between `m³` and | ||
| `m3` without exposing source HTML to the embedding model. | ||
| - Existing numeric-footnote tests remain unchanged because the bounded metric | ||
| pattern is the only new conversion. | ||
| - The current implementation does not claim to understand arbitrary equations; | ||
| unsupported script markup remains ordinary source text and must not be | ||
| presented as a parsed ontology expression. | ||
|
|
||
| ## References (APA 7th) | ||
|
|
||
| World Wide Web Consortium. (2026). *Mathematical Markup Language (MathML) | ||
| Version 4.0* (W3C Recommendation). https://www.w3.org/TR/mathml4/ | ||
2 changes: 1 addition & 1 deletion
2
...tespace-is-not-authoritative-structure.md → ...tespace-is-not-authoritative-structure.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Product technical-gap references | ||
|
|
||
| These references are the normative basis for the semantic-unit boundary in | ||
| [ADR 0103](../adr/0103-semantic-document-evidence-contract.md). Dates and | ||
| versions are recorded so a later standards refresh can be reviewed rather | ||
| than silently changing parser behavior. | ||
|
|
||
| ## APA 7th edition | ||
|
|
||
| CommonMark. (2024). *CommonMark spec (Version 0.31.2)*. https://spec.commonmark.org/0.31.2/ | ||
|
|
||
| WHATWG. (2026). *HTML: Living Standard*. https://html.spec.whatwg.org/multipage/ | ||
|
|
||
| ## Applied mapping | ||
|
|
||
| | Source | Boundary used in LineageWeave | | ||
| | --- | --- | | ||
| | CommonMark (2024) | Recognizable Markdown block/header/separator shape; unrecognized dialects remain source text. | | ||
| | WHATWG (2026) | HTML list, table-row, and `sup` semantics; source order and element identity are retained as unit metadata. | | ||
|
|
||
| These standards define syntax and semantics, not an LLM extraction license. | ||
| Provider-derived summaries, image descriptions, project boundaries, and | ||
| 5W1H values still require contextual-orchestrator provenance and explicit | ||
| source evidence. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| import type { Meta, StoryObj } from "@storybook/react-vite"; | ||
| import { PostBody } from "./PostBody"; | ||
|
|
||
| const meta = { | ||
| title: "Evidence/PostBody", | ||
| component: PostBody, | ||
| } satisfies Meta<typeof PostBody>; | ||
|
|
||
| export default meta; | ||
|
|
||
| type Story = StoryObj<typeof meta>; | ||
|
|
||
| const TINY_PNG = | ||
| "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="; | ||
|
|
||
| export const MarkdownTableEvidence: Story = { | ||
| args: { | ||
| body: "| Workstream | State |\n| --- | --- |\n| Alpha | Ready |", | ||
| structureUnits: [ | ||
| { | ||
| unit_index: 0, | ||
| unit_kind_code: "plain_text", | ||
| unit_label: "markdown_tr", | ||
| unit_text: "Workstream | State", | ||
| indent_level: 0, | ||
| indent_source_code: "unresolved", | ||
| indent_confidence: 0, | ||
| indent_evidence: "Markdown table row", | ||
| }, | ||
| { | ||
| unit_index: 1, | ||
| unit_kind_code: "plain_text", | ||
| unit_label: "markdown_tr", | ||
| unit_text: "Alpha | Ready", | ||
| indent_level: 0, | ||
| indent_source_code: "unresolved", | ||
| indent_confidence: 0, | ||
| indent_evidence: "Markdown table row", | ||
| }, | ||
| ], | ||
| }, | ||
| }; | ||
|
|
||
| export const MarkdownTableFallback: Story = { | ||
| args: { | ||
| body: "Intro.\n\n| Workstream | State |\n| --- | --- |\n| Alpha | Ready |\n\nNext action.", | ||
| }, | ||
| }; | ||
|
|
||
| export const ImageOcrTableEvidence: Story = { | ||
| args: { | ||
| body: `<img src="data:image/png;base64,${TINY_PNG}" />`, | ||
| imageContent: [ | ||
| { | ||
| unit_index: 0, | ||
| mime_type: "image/png", | ||
| status_code: "completed", | ||
| extracted_text: "| Workstream | State |\n| --- | --- |\n| Alpha | Ready |", | ||
| caption: "A synthetic workstream status table.", | ||
| tags: ["table"], | ||
| }, | ||
| ], | ||
| }, | ||
| }; | ||
|
|
||
| export const NumericFootnote: Story = { | ||
| args: { | ||
| body: "<p>Evidence remains attached to the source.</p><p><sup>1</sup> Source note.</p>", | ||
| }, | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Two different ADRs share number 0105
The renamed
0105-source-whitespace-is-not-authoritative-structure.mdand the new0105-mathematical-script-semantic-normalization.mdboth title themselves ADR 0105 with different content. Any lookup by ADR number is now ambiguous, and the repo already tracks duplicate ADR numbering as a defect.Prompt for agents
Was this helpful? React with 👍 or 👎 to provide feedback.