Skip to content

osrforge.contracts.report

The extraction report: the report.json contract.

The report is regenerated on every assembly and is the complete input a review UI needs. The wire format lives here, beside the other consumer contracts, so consumers and tests pin exactly the models assembly writes.

AreaAddressString module-attribute

AreaAddressString = Annotated[str, AfterValidator(_validate_area_address_string)]

An area address in its serialized <dungeon-id>/<level-number>/<area-key> form.

FlagString module-attribute

FlagString = Annotated[str, AfterValidator(_validate_flag_string)]

A serialized report flag: <flag> or <flag>:<detail>, prefix a Flag member.

LevelAddressString module-attribute

LevelAddressString = Annotated[str, AfterValidator(_validate_level_address_string)]

A level address in its serialized <dungeon-id>/<level-number> form.

AreaAddress

Bases: BaseModel

A keyed area's address: <dungeon-id>/<level-number>/<area-key>.

osrlib allows any string id, so the address grammar is only unambiguous because osr-forge constrains what it emits: / is forbidden in dungeon ids and area keys (survey normalization enforces it at the source).

dungeon_id instance-attribute

dungeon_id: str

level_number class-attribute instance-attribute

level_number: int = Field(ge=1)

area_key instance-attribute

area_key: str

parse classmethod

parse(value: str) -> AreaAddress

Parse the <dungeon-id>/<level-number>/<area-key> form.

Parameters:

Name Type Description Default
value str

The address string.

required

Returns:

Type Description
AreaAddress

The parsed address.

Raises:

Type Description
ValueError

If the string is not three non-empty /-separated parts with an integer level number of at least 1.

AreaReport

Bases: BaseModel

One keyed area's extraction record.

id instance-attribute

The area's address, <dungeon-id>/<level-number>/<area-key>.

source_pages class-attribute instance-attribute

source_pages: tuple[int, ...] = ()

The source pages the area's content came from — an extraction fact that persists under overrides.

confidence class-attribute instance-attribute

confidence: float = Field(ge=0.0, le=1.0)

The extraction confidence — likewise a persistent extraction fact.

flags class-attribute instance-attribute

flags: tuple[FlagString, ...] = ()

The area's flags, describing the built draft.

overridden class-attribute instance-attribute

overridden: tuple[str, ...] = ()

Which override fields were applied to this area.

CustomMonsterRecord

Bases: BaseModel

One emitted custom template's review record.

The monster_custom flag is the review badge; this is the review detail: the emitted id, the extracted name it serves, the pages its stat block was transcribed from, and every field the mapping derived, defaulted, or discarded-and-rederived rather than read off the printed page.

id instance-attribute

id: str

The emitted template's id in the draft's catalog union.

name instance-attribute

name: str

The extracted monster name the template serves.

source_pages class-attribute instance-attribute

source_pages: tuple[int, ...] = ()

The pages the stat block was transcribed from.

derived class-attribute instance-attribute

derived: tuple[str, ...] = ()

Every field the mapping derived, defaulted, or discarded-and-rederived rather than read off the printed page.

ExtractionReport

Bases: BaseModel

The report.json document.

flags carries module-scope conditions with no per-area home — a defaulted adventure title or town name — in the same flag grammar as per-area flags. findings is empty from assemble() (stale lint about a changed draft is worse than none; re-assembly wipes findings by design) and populated by check().

schema_version class-attribute instance-attribute

schema_version: int = SCHEMA_VERSION

The report schema version.

osrforge_version class-attribute instance-attribute

osrforge_version: str = Field(default_factory=osrforge_version)

The producing package version.

module instance-attribute

module: ModuleInfo

The source module's identity.

validation instance-attribute

validation: ValidationResult

The validate_adventure outcome.

areas class-attribute instance-attribute

areas: tuple[AreaReport, ...] = ()

One record per keyed area, in draft order.

monsters instance-attribute

monsters: MonsterSummary

The monster-resolution summary.

usage instance-attribute

usage: TokenUsage

The conversion's total token consumption.

flags class-attribute instance-attribute

flags: tuple[FlagString, ...] = ()

Module-scope flags with no per-area home.

findings class-attribute instance-attribute

findings: tuple[LintFinding, ...] = ()

The playability findings — empty from assemble(), populated by check().

Flag

Bases: StrEnum

The report's enumerated flag vocabulary — UIs badge on these.

GEOMETRY_SYNTHESIZED class-attribute instance-attribute

GEOMETRY_SYNTHESIZED = 'geometry_synthesized'

MONSTER_UNRESOLVED class-attribute instance-attribute

MONSTER_UNRESOLVED = 'monster_unresolved'

MONSTER_CUSTOM class-attribute instance-attribute

MONSTER_CUSTOM = 'monster_custom'

LOW_CONFIDENCE class-attribute instance-attribute

LOW_CONFIDENCE = 'low_confidence'

CONNECTION_AMBIGUOUS class-attribute instance-attribute

CONNECTION_AMBIGUOUS = 'connection_ambiguous'

TRANSITION_GUESSED class-attribute instance-attribute

TRANSITION_GUESSED = 'transition_guessed'

TREASURE_UNPARSED class-attribute instance-attribute

TREASURE_UNPARSED = 'treasure_unparsed'

PAGE_UNREADABLE class-attribute instance-attribute

PAGE_UNREADABLE = 'page_unreadable'

RESOLUTION_SUSPECT class-attribute instance-attribute

RESOLUTION_SUSPECT = 'resolution_suspect'

SURVEY_DISPUTED class-attribute instance-attribute

SURVEY_DISPUTED = 'survey_disputed'

MAP_DISPUTED class-attribute instance-attribute

MAP_DISPUTED = 'map_disputed'

LevelAddress

Bases: BaseModel

A dungeon level's address: <dungeon-id>/<level-number> (geometry overrides).

dungeon_id instance-attribute

dungeon_id: str

level_number class-attribute instance-attribute

level_number: int = Field(ge=1)

parse classmethod

parse(value: str) -> LevelAddress

Parse the <dungeon-id>/<level-number> form.

Parameters:

Name Type Description Default
value str

The address string.

required

Returns:

Type Description
LevelAddress

The parsed address.

Raises:

Type Description
ValueError

If the string is not two non-empty /-separated parts with an integer level number of at least 1.

LintCheck

Bases: StrEnum

The playability lint's finding ids.

A published vocabulary UIs badge on, like Flag: growing it is additive, renaming a member is a schema-version event.

EDGE_INVALID class-attribute instance-attribute

EDGE_INVALID = 'edge_invalid'

AREA_UNREACHABLE class-attribute instance-attribute

AREA_UNREACHABLE = 'area_unreachable'

ORPHAN_CELL class-attribute instance-attribute

ORPHAN_CELL = 'orphan_cell'

SECRET_ONLY_ACCESS class-attribute instance-attribute

SECRET_ONLY_ACCESS = 'secret_only_access'

TRANSITION_UNPAIRED class-attribute instance-attribute

TRANSITION_UNPAIRED = 'transition_unpaired'

DELVE_BLOCKED class-attribute instance-attribute

DELVE_BLOCKED = 'delve_blocked'

DELVE_INCOMPLETE class-attribute instance-attribute

DELVE_INCOMPLETE = 'delve_incomplete'

LintFinding

Bases: BaseModel

One structured playability finding, as merged into report.json by check.

Severity is a field rather than a function of the id: the id→severity table is the producer's pin (check), so the contract needn't change if a check's severity is ever re-judged.

id instance-attribute

Which check fired.

severity instance-attribute

severity: Literal['error', 'warning']

error breaks the correction loop's exit code; warning records an acceptable-by-decision condition.

location instance-attribute

location: str

Where the finding points: an area address, a level address, or a dungeon id, at whichever granularity the check works.

message instance-attribute

message: str

The human-readable finding text.

ModuleInfo

Bases: BaseModel

The source module's identity in the report.

title instance-attribute

title: str

The adventure title, as built (a defaulted title is flagged).

pages class-attribute instance-attribute

pages: int = Field(ge=0)

The source module's page count.

MonsterSummary

Bases: BaseModel

The monster-resolution summary.

custom records the emitted templates actually bundled into the draft — additive and defaulted, so reports written before emission existed still validate; vetoed (likewise additive and defaulted) records the picks the stat-block veto discarded, straight from the monsters cache.

resolved class-attribute instance-attribute

resolved: int = Field(ge=0)

How many names resolved to a template in the draft's catalog union — SRD picks and emitted custom templates alike.

unresolved class-attribute instance-attribute

unresolved: tuple[str, ...] = ()

The names that resolved to nothing.

custom class-attribute instance-attribute

custom: tuple[CustomMonsterRecord, ...] = ()

The emitted custom templates actually bundled into the draft.

vetoed class-attribute instance-attribute

vetoed: tuple[VetoedResolution, ...] = ()

The resolutions the stat-block veto discarded, in name order.

ValidationResult

Bases: BaseModel

The validate_adventure outcome: a draft is allowed to be invalid.

passed instance-attribute

passed: bool

Whether osrlib's validate_adventure accepted the draft.

errors class-attribute instance-attribute

errors: tuple[str, ...] = ()

The validation error messages when it didn't.

VetoedResolution

Bases: BaseModel

One resolution the stat-block veto discarded.

The vetoed pick's review record: the extracted name, the discarded catalog pick, and the both-readings detail. unresolved stays a tuple of strings — no existing surface re-shapes.

name instance-attribute

name: str

The extracted monster name whose pick was vetoed.

vetoed_template_id instance-attribute

vetoed_template_id: str

The catalog template the LLM or fuzzy tier picked and the veto discarded.

detail class-attribute instance-attribute

detail: str | None = None

The human-readable both-readings record (orc chief → orc, printed HD 2 vs 1).

format_flag

format_flag(flag: Flag, detail: str | None = None) -> str

Serialize a flag and optional detail into the report's string form.

Parameters:

Name Type Description Default
flag Flag

The flag.

required
detail str | None

Optional free-text detail. Must be non-empty when given.

None

Returns:

Type Description
str

<flag> or <flag>:<detail>.

Raises:

Type Description
ValueError

If detail is an empty string.

parse_flag

parse_flag(value: str) -> tuple[Flag, str | None]

Split a serialized flag string into its flag and optional detail.

Serialized flags are plain strings shaped <flag> or <flag>:<detail> — e.g. monster_unresolved:hobgoblin chieftain. The prefix must be a Flag member; the detail is free text and may itself contain colons.

Parameters:

Name Type Description Default
value str

The serialized flag string.

required

Returns:

Type Description
tuple[Flag, str | None]

The flag and its detail (None when the string is a bare flag).

Raises:

Type Description
ValueError

If the prefix is not a known flag or the detail is empty.