veaf-logs — report block format¶
Audience: developers of the lots that produce or consume the
veaf-logspaste block — log analysis (FEAT-SUPPORT-LOG-ANALYSIS), which writes it, and bug intake (FEAT-SUPPORT-BUG-INTAKE), which parses it back.Schema:
veaf-logs-report/1. — 🇫🇷report-block.md.
Why a second format¶
The doctor block describes the machine. This one describes the
problem: what the user was looking at, what the catalogue made of it, and what nobody could
explain. It contains the doctor block, it does not replace it.
Like it, this block travels through Discord or a GitHub issue, pasted by hand by someone who will not reread it, and is parsed by a machine at the other end. It is therefore a contract, versioned, that neither the producer nor the consumers can change alone.
The implementation lives in src/python/veaf-tools/veaf_logs/report.py, producer (build_report)
and reader (parse_report_block) in the same module — so they cannot drift apart — and the
round-trip test is in test/python/veaf_logs/test_report.py.
That module is the reference: the shipped veaf-logs.exe, written in Go, produces the block with src/go/veaf-logs/internal/analysis/report.go, and its parity tests require it to be identical, character for character, to the Python module's.
Structure¶
=== VEAF-LOGS REPORT BEGIN ===
schema: veaf-logs-report/1
generated: 2026-09-05T09:39:29Z
excerpt.shown: 42
excerpt.selected: 3356
excerpt.total: 87989
excerpt.omitted: 3314
excerpt.excluded: levels=DEBUG,INFO,TRACE
catalogue.matched: damage_model,payload_weight
catalogue.uncatalogued: 24
proposals.count: 2
truncated: sections retirées pour tenir dans un message : proposals, analysis
--- doctor ---
=== VEAF-TOOLS DOCTOR BEGIN ===
schema: veaf-tools-doctor/1
...
=== VEAF-TOOLS DOCTOR END ===
--- doctor end ---
--- excerpt ---
[veaf-logs] 42 entrées sur 87989 indexées (3356 retenues, 3314 omises par la limite de taille)
masqué (✕) — niveaux : DEBUG, INFO, TRACE
16:28:35.388 ERROR ED_SOUND can't load proto file "/sounds/54/sdef/..."
--- excerpt end ---
--- catalogue ---
Motifs connus (texte du catalogue, tel quel) :
- Modèle de dégâts corrompu (damage_model) ×3 : Modules tiers dont le modèle de dégâts...
--- catalogue end ---
=== VEAF-LOGS REPORT END ===
The block's own prose is French: veaf-logs is a French-language application, and its output is
what the user pastes. Only the field names are part of the contract.
Four rules, and nothing else:
- The block is delimited by
=== VEAF-LOGS REPORT BEGIN ===and=== VEAF-LOGS REPORT END ===. It arrives in the middle of something else: a reader looks for those two lines, it does not assume the block starts at line one. It is usually wrapped in a```textcode fence; any triple backtick in the content is replaced by three apostrophes, otherwise it would close the fence early. - Between the delimiters, and outside the sections, every line is
key: value, split on the first:, on a single line. Same rule as thedoctorblock and for the same reason: a value carrying a newline would come back as two fields, the second of them forged. - A section is bounded by
--- <name> ---and--- <name> end ---and holds raw text. The names are those ofSECTIONS:doctor,excerpt,catalogue,analysis,proposals. An empty section is not written at all — its absence is information, not an oversight. - The
doctorsection carries a completeveaf-tools-doctor/1block, delimiters included. The two sets of delimiters are deliberately different: a reader looking for one never trips over the other, and the nested block reads back withparse_blockwith no special care.
The fields¶
The order is that of FIELD_ORDER in report.py, and it is stable.
| Key | Example | What it says |
|---|---|---|
schema |
veaf-logs-report/1 |
check it before reading anything else |
generated |
2026-09-05T09:39:29Z |
when the block was produced, in UTC |
excerpt.shown |
42 |
records actually present in the excerpt section |
excerpt.selected |
3356 |
records the filters kept, before the size ceiling |
excerpt.total |
87989 |
records indexed in the log — the denominator |
excerpt.omitted |
3314 |
records kept by the filters but dropped by the ceiling |
excerpt.excluded |
levels=DEBUG,INFO |
categories set to ✕, or aucune |
catalogue.matched |
damage_model,... |
ids of the rules.json entries recognised, or aucun |
catalogue.uncatalogued |
24 |
records the catalogue does not explain |
proposals.count |
2 |
recurring uncatalogued patterns spotted |
truncated |
non |
or the list of sections dropped, or OUI — … |
excerpt.excluded is the field to read first after the schema: it says what the excerpt cannot
contain. A report where catalogue.uncatalogued is 0 and excerpt.excluded names ERROR does not
describe a clean log, it describes a log whose errors were hidden.
What happens when it does not fit¶
The block is built to fit one Discord message — 2 000 characters, code fence included. A full
excerpt is more than ten times that: measured on 2026-09-05 against an 11.1 MB dcs.log (87 989
records), the default excerpt renders to ~16 000 characters.
The drop order is therefore fixed, and asymmetric. Pieces are removed, in this order, until the excerpt has about 500 characters to work with — and then, if the block still does not fit, until it does: giving the excerpt a decent share is the right trade for a block that fits, and no reason at all to hand back one that does not.
proposals, thenanalysis;- the
doctorblock's error records — not its fields; catalogue.
The excerpt is then shrunk into whatever room is left, and dropped only when there is no longer
enough to show a few records. The doctor block's fields never go: they are the half of the
report nobody can reconstruct afterwards.
Two of those placements are measured rather than guessed, on the real report of 2026-09-05:
doctor's stack traces took ~1 500 of the 1 988 available characters, and pushed out the excerpt, the catalogue and the proposals. A stack trace from another tool is worth less, in a report about a DCS log, than the lines the user came to report.cataloguegoes before the excerpt because its ids are already in thecatalogue.matchedfield and its wording is onerules.jsonlookup away; the log records exist nowhere else.
The truncated field names what went at each step, and excerpt (réduit) when the excerpt was kept
but shortened — a truncated: non over an excerpt cut from 157 records to 7 would be a plain
untruth. A block cut at the boundary, on the other hand, reads like a complete one to whoever
receives it, so there is no such cut.
When the excerpt is shrunk, excerpt.shown and excerpt.omitted are recomputed: the fields
describe the block, not the analysis it came from. A consumer reads that field precisely to avoid
counting, and so has no way to catch the discrepancy.
That field is inside the block it describes, so writing it changes the length it was measured
from. This is measured rather than theoretical: across 240 swept sizes on the real dcs.log, 30
blocks came back over their own limit because truncated grew after the excerpt's room had been
computed, and every OUI — N caractères was wrong, in both directions. The block is therefore
recomposed until its length stops moving, and the OUI also carries the list of what went — it used
to overwrite it, and a reader needs it precisely when the block overflows.
Redaction¶
The block is redacted assembled, not only part by part. The two are not equivalent: the excerpt
is already redacted at construction, but the model's commentary arrives from the network and has
been through nothing else. The redaction is that of
veaf_libs.redaction — the only one in the project, and it is
idempotent, so a second pass over already-treated parts damages nothing.
What the reader may assume¶
Nothing. The block travels through a public issue and anyone can type one by hand. The producer
guarantees the shape — one field, one line — never the truth of a value. A consumer acting on
excerpt.total is acting on a claim, not on a reading taken from the machine.
parse_report_block raises ValueError on a missing block or one without its end delimiter — a
truncated paste, which must be reported rather than half-read. A section left open is however
returned with what was read: losing the content as well as the ending would be doubly punishing.