content_formatCtrl/Cmd+B. The catch is what bolding persists: on the
light preset TipTap saves Markdown, so defective pipes becomes
**defective pipes**. That string is not private to the editor. It flows into
matters.matter_frame_text, which is the search corpus. Bold a phrase with no
preparation and you make that matter unfindable by the phrase you just emphasised. This
Story is the gate: it makes the corpus safe before pramaan-app is allowed to flip a single preset.
The naive fix is one line — flip the four Brief blocks to preset="light" in the app and
ship it. Detonate that: the preset is not a display setting, it is a serializer selection.
plain saves editor.getText(); light saves
editor.getMarkdown(). Flipping it silently changes the bytes that land in the database, and three
things downstream read those bytes literally.
| Consumer | Where | What ** does to it |
|---|---|---|
FTS search_vector |
matter/entity.py:200, migration 0131 — a
GENERATED ALWAYS … STORED tsvector over matter_frame_text |
Degraded. The simple config carries the asterisks into tokens. |
| Trigram index | ix_matters_trgm_matter_frame_text (matter/entity.py:103) |
Breaks. Byte-literal. |
ILIKE substring fallback |
MatterService._matter_text_match (matter/service.py:417) |
Breaks. A search for defective pipes cannot match
**defective pipes** — the delimiters are wedged inside the phrase. |
Note where the damage lands: not in the tsvector everyone worries about, but in the humble substring fallback nobody thinks about. That is the one that matches raw bytes.
The obvious home for a content_format flag is the frame itself — it is a
dict[str, Any], after all. That is a trap:
class FrameOutput(BaseModel):
"""Mirror of the canonical FrameOutput - Vega's Matter Frame contract."""
model_config = ConfigDict(extra="forbid") # ← the door is closed
FrameOutput is Vega's contract, and it is parity-gated against a vendored
pramaan-agents schema artifact (Story-1995, test_matter_frame_envelope.py). Adding a key to it
turns a UI formatting ticket into a three-repo agent-contract migration, and the parity gate goes red the
moment you try.
frame / resolution / provenance. content_format becomes
the fourth compartment. Vega's contract is untouched; the parity gate stays green.
envelope = {
"frame": {...}, # Vega's contract. Untouched.
"resolution": {...},
"provenance": {...},
"content_format": {"core_dispute": "markdown"}, # ← new, and SPARSE
}
Sparse is load-bearing. plain is the default, so it is stored as
absence. An all-plain frame writes no compartment at all — which means every envelope written
before today is byte-identical to what it was, and there is no backfill and no migration. A
row becomes markdown only when a human edits it through a Markdown-serializing editor.
The ticket says the strip must use a real Markdown parser, never a regex. That sounds like taste. It is not.
Legal prose is full of literal asterisks — ***REDACTED***, footnote markers,
arithmetic. A regex cannot tell a delimiter from a character that merely looks like one, because that
distinction is contextual and only a real CommonMark parse resolves it.
The repo rule is "no green counts until it has been red" (AGENTS.md). So the first draft of the test file was checked against the forbidden implementation — and it passed. The tests were vacuous. Every case had only one lone asterisk, and a regex survives those. Real corruption needs two:
| Input (a real citation) | Regex \*(.+?)\* | CommonMark parser |
|---|---|---|
Breach at cl. 5 * and cl. 7 * |
Breach at cl. 5 and cl. 7both markers eaten |
Breach at cl. 5 * and cl. 7 *intact |
Interest 5 * 3 * 2 per annum |
Interest 5 3 2 per annum |
Interest 5 * 3 * 2 per annum |
The regex pairs two unrelated footnote markers into a phantom emphasis span and deletes both,
silently eating the text between them. Nothing reports it. The parser refuses to open emphasis on a
* followed by whitespace (not left-flanking), so both stay literal.
test_the_regex_this_forbids_would_actually_corrupt_legal_text pins that exact regex and asserts
it destroys the citation the shipped parser keeps. The constraint is now enforced by a test, not by a comment.
One subtlety that decides correctness. A field's format must survive unless an agent just overwrote its
value, because agents author plain prose. A stale markdown flag on agent-authored text lies
in both directions: it tells the strip to parse text with no markup, and tells the app to render literal text
as Markdown.
| Path | What the agent authored | What keeps its encoding |
|---|---|---|
| Vega ingest | the whole frame | only the locked fields — the lock overlay restores counsel's value, so a locked, bolded field keeps its Markdown flag along with its Markdown text |
| Mira command | only changed |
everything else (locks are discarded from changed, so they carry too) |
| Counsel accept | nothing — retypes not one character | everything |
| Counsel reject | clears the field to its contract default | everything but that field — an emptied field has no encoding |
Both agent paths collapse to one helper, _carried_content_format(prior, agent_authored=…).
| File | Change |
|---|---|
matter_frame/content_format.py | New. The vocabulary
(plain | markdown), the four Markdown-capable prose fields, the parser-based
strip_markdown, and normalize_content_format (tolerant, because it also reads
stored rows). |
matter_frame/render.py | render_matter_frame_text(frame, content_format)
— the four prose fields route through the strip. Omit the arg and the output is exactly what it always
was. |
matter_frame/dtos.py | content_format on
MatterBriefFrameView (read) and MatterFrameEditRequest (write). |
matter_frame/service.py | The compartment on every write path;
_validated_content_format (a declaration may not name fields it is not editing, nor flag a
structured field as prose) and _carried_content_format. |
requirements.in / .lock | markdown-it-py (MIT). |
openapi/app-api.json | Regenerated (R9). Surfaces as contentFormat
for pramaan-app's codegen-sync. |
Rendering. This Story makes the corpus correct and hands pramaan-app the flag. The
BriefContent render layer, the Vega proposal strip, and the actual preset flip are
pramaan-spec#2275 — which is blocked on this one, because no preset may flip until search is safe.