osrforge.reconcile
Deterministic reconciliation of prose edge facts with map proposals, plus the shared entrance selection.
Pure code consumed by both geometry synthesis and the eval scorer — the
shared-predicate precedent (transition_via,
usable_stat_block) extended from a
function to the whole policy, so the two consumers can never merge or pick
differently. The shared code owns the map-versus-prose policy only — the
prose side of the merge stays each consumer's own derivation (geometry's
_GraphEdge merge, the scorer's edge-fact dedup), including their recorded
door-kind asymmetry on two-mention edges.
The precedence rule, the doctrine extended across evidence sources: a
stated prose fact survives any map conflict, flagged; a prose absence fills
from the map, flagged as adopted; agreement is silent. The door: "none"
corner, pinned: a proposed pair's door reading is complete (the truth
convention's own posture), so a prose-stated door on a pair the map proposes
with door: "none" is a presence disagreement — prose survives, flagged —
while prose with no stated door on that pair agrees silently, whatever its
non-door mechanism (the map's vocabulary doesn't carry mechanisms).
EntranceSelection
dataclass
EntranceSelection(
level_number: int | None,
area_key: str | None,
provenance: Literal["map", "heuristic"],
dispute: str | None,
dropped: tuple[tuple[int, str], ...],
)
One dungeon's entrance selection — the single source geometry and the scorer consume.
Attributes:
| Name | Type | Description |
|---|---|---|
level_number |
int | None
|
The selected entrance level, or |
area_key |
str | None
|
The selected entrance area's canonical key, or |
provenance |
Literal['map', 'heuristic']
|
|
dispute |
str | None
|
The both-picks detail when the map candidate and the
heuristic differ ( |
dropped |
tuple[tuple[int, str], ...]
|
|
MergedLevelEdges
dataclass
MergedLevelEdges(
adopted_doors: Mapping[frozenset[str], Literal["door", "secret_door"]],
map_only: tuple[tuple[str, str, Literal["door", "secret_door"] | None], ...],
disputes: tuple[tuple[str, str], ...],
dropped: tuple[str, ...],
)
One level's merge outcome, in the shapes both consumers map from.
Attributes:
| Name | Type | Description |
|---|---|---|
adopted_doors |
Mapping[frozenset[str], Literal['door', 'secret_door']]
|
Undirected pair → the map door kind adopted onto an existing prose edge whose mentions stated no mechanism. |
map_only |
tuple[tuple[str, str, Literal['door', 'secret_door'] | None], ...]
|
|
disputes |
tuple[tuple[str, str], ...]
|
|
dropped |
tuple[str, ...]
|
Bare details for dropped proposals (unresolvable endpoints, self-pairs) — the caller prefixes its level address. |
adopted_doors
instance-attribute
map_only
instance-attribute
ProseEdge
dataclass
One undirected prose edge's merged facts, as the consumer derived them.
Attributes:
| Name | Type | Description |
|---|---|---|
stated_via |
str | None
|
The merged stated (non- |
door_kind |
Literal['door', 'secret_door'] | None
|
The merged prose door kind ( |
merge_level_edges
merge_level_edges(
prose: Mapping[frozenset[str], ProseEdge], reading: MapLevelReading | None, level_keys: Sequence[str]
) -> MergedLevelEdges
Merge one level's prose edge facts with its map proposals under the precedence rule.
Proposal normalization first: each endpoint resolves exactly-then-slug
against level_keys; unresolvable endpoints and self-pairs drop into
dropped with a detail; duplicate proposals of one pair dedup with the
more-specific door winning (secret_door beats door beats none —
the scorer's precedent). Then, per undirected pair, the enumerated rule:
- agreement → silent;
- a map door onto a prose edge with no stated mechanism → adopted, flagged;
- a prose-stated door (or kind, or non-door mechanism) contradicted by the map → prose survives, flagged;
- a prose edge absent from a read map → kept, flagged;
- a map-only pair → adopted as an edge (plus its door), flagged.
An unread (or absent) reading produces no adoptions and no
disagreements — the prose-only path, bit for bit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prose
|
Mapping[frozenset[str], ProseEdge]
|
Undirected pair → the consumer's own merged prose fact. |
required |
reading
|
MapLevelReading | None
|
The level's map reading, or |
required |
level_keys
|
Sequence[str]
|
The level's canonical keys in survey order — the resolution universe and the endpoint-ordering authority. |
required |
Returns:
| Type | Description |
|---|---|
MergedLevelEdges
|
The merge outcome; every field empty when the level is unread. |
resolve_key
Resolve a stated key against one level's canonical keys: exact match, else slug match.
The endpoint-resolution rule, public for the shared-predicate reason: geometry resolves prose connection targets and this module resolves map proposal endpoints through the same function, so the two evidence sources can never resolve one printed spelling differently.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
str
|
The key as stated (a prose |
required |
level_keys
|
Sequence[str]
|
The level's canonical keys. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
The resolved canonical key, or |
select_entrance
select_entrance(dungeon: SurveyDungeon, readings: Mapping[int, MapLevelReading]) -> EntranceSelection
Select one dungeon's entrance: a resolvable map proposal wins over the positional heuristic.
The heuristic pick is the first listed area of the lowest-numbered
non-empty level — geometry's zero-evidence guess, kept as the fallback.
The map candidate is the first resolvable entrance_key in level-number
order over the dungeon's read levels, resolved exactly-then-slug
against its own level's keys; evidence beats position, so a present
candidate wins, with a dispute detail when the two differ. An
unresolvable proposal drops with map names '<key>'; the survey does
not; a secondary resolvable proposal (another level also labeling a
way in) is neither adopted nor discarded silently — it drops with
entrance also proposed <level>/<key>, a reviewable oddity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dungeon
|
SurveyDungeon
|
The surveyed dungeon. |
required |
readings
|
Mapping[int, MapLevelReading]
|
Level number → the level's map reading, for this dungeon. |
required |
Returns:
| Type | Description |
|---|---|
EntranceSelection
|
The selection, provenance, and dispute channels. |