Skip to content

Commit 04253ab

Browse files
feat(ahbg): presentation board — fixed occupancy offsets and viewBox bounds (#20)
Recovered the presentation-only board from closed #17 (built from current main). Repairs the two remaining renderer defects: - initial vs final multi-unit occupancy offsets: a unit now uses the initial occupancy group of its motion origin for the start marker and the final group of its presented tile for the end marker, instead of applying the final-tile offset to both endpoints. - SVG viewBox bounds: viewBox now derives from seed circles plus every displaced unit marker extent at both motion endpoints, so offset markers cannot clip at the board edge. Keeps UCNS as geometry authority (presentation only scales supplied centers). Regression tests mirror the frozen offset math and assert the corrected source structure. Co-authored-by: erinepshovel-code <250928284+erinepshovel-code@users.noreply.github.com>
1 parent aea67dd commit 04253ab

11 files changed

Lines changed: 1250 additions & 0 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: ahbg-presentation
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "ahbg/presentation/**"
7+
- ".github/workflows/ahbg-presentation.yml"
8+
push:
9+
branches: [main]
10+
paths:
11+
- "ahbg/presentation/**"
12+
- ".github/workflows/ahbg-presentation.yml"
13+
14+
permissions:
15+
contents: read
16+
17+
jobs:
18+
presentation:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v6
22+
- uses: actions/setup-python@v6
23+
with:
24+
python-version: "3.12"
25+
- name: Python presentation contract
26+
run: python -m unittest discover -s ahbg/presentation/tests -p 'test*.py'
27+
- name: Browser JavaScript syntax
28+
run: node --check ahbg/presentation/board.js

ahbg/presentation/README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# AHBG presentation
2+
3+
Grok-owned graphics surface. It renders validated presentation snapshots and
4+
already-resolved motion traces. It does **not** define game mechanics or derive
5+
UCNS geometry.
6+
7+
## Boundary
8+
9+
- Included: Seed-of-Life circle rendering, UCNS-supplied tile centerpoints,
10+
unit markers, selection/inspection, public feed text, and visual traces of
11+
already-resolved moves.
12+
- Excluded: legal movement, adjacency authority, turns, War resolution,
13+
construction, permissions, RNG, DM state, private prompt state, or agent policy.
14+
- Snapshot standing is `ahbg.presentation.snapshot` / `not-mechanics`.
15+
- Every snapshot carries an exact `geometry_source` identity plus each tile's
16+
UCNS-derived `x`, `y`, and `source_slot`. The renderer only scales those
17+
coordinates for SVG display; it never rebuilds centers from local axial rules.
18+
- The current sample pins `The-Interdependency/ucns@1975fe70cf4e0826a8020c2da3047569e277af64`,
19+
the canonical `libs/ucns/` identity in the stack manifest when this surface was
20+
authored. Its `mobius_seed` module remains an explicit nonselecting UCNS
21+
candidate; presentation consumption does not promote its standing.
22+
- `project.py` accepts already-sanitized observation data, source-backed display
23+
coordinates, and resolved move events. It never decides whether those moves
24+
were legal and copies only declared browser-facing fields.
25+
- `geometry.py` scales supplied source coordinates into display pixels only.
26+
27+
## Usage guidance
28+
29+
Validate the package:
30+
31+
```bash
32+
python -m unittest discover -s ahbg/presentation/tests -p 'test*.py'
33+
node --check ahbg/presentation/board.js
34+
```
35+
36+
Serve the board locally:
37+
38+
```bash
39+
cd ahbg/presentation
40+
python -m http.server 8765 --bind 127.0.0.1
41+
# open http://127.0.0.1:8765/board.html
42+
```
43+
44+
Project a sanitized observation only after the owning adapter has attached the
45+
UCNS-derived centers and exact source identity:
46+
47+
```python
48+
from ahbg.presentation.project import snapshot_from_observation
49+
50+
geometry_source = {
51+
"repository": "The-Interdependency/ucns",
52+
"commit": "1975fe70cf4e0826a8020c2da3047569e277af64",
53+
"module": "src/ucns/mobius_seed.py",
54+
"schema_id": "ucns.mobius-seed-of-life",
55+
"schema_version": "0.1.0",
56+
"projection_id": "seed-of-life-seven-equal-circles",
57+
"selection_effect": "none",
58+
}
59+
observation = {
60+
"turn": 1,
61+
"geometry_source": geometry_source,
62+
"tiles": [
63+
{"tile_id": "CENTER", "ucns_slot": "CENTER", "x": 0.0, "y": 0.0},
64+
{"tile_id": "RING_0", "ucns_slot": "RING_0", "x": 1.0, "y": 0.0},
65+
],
66+
"units": [{"unit_id": "A0", "tile_id": "RING_0", "label": "A0"}],
67+
}
68+
move = {
69+
"kind": "move",
70+
"data": {"unit_id": "A0", "from_tile_id": "CENTER", "to_tile_id": "RING_0"},
71+
}
72+
snapshot = snapshot_from_observation(
73+
observation,
74+
plane_id="plane-0",
75+
move_events=[move],
76+
feed=[{"turn": 1, "text": "public display text only"}],
77+
)
78+
```
79+
80+
The snapshot validator is allowlist-based. Unknown root, geometry, tile, unit,
81+
feed, or motion fields fail closed. The projector drops undeclared observation,
82+
move-event, and feed metadata before validation, preventing a visual snapshot
83+
from becoming a transport for private/internal fields.
84+
85+
The browser exposes tile inspection to pointer and keyboard input, permits
86+
interactive descendants under an SVG `group` role, scales unit-marker spread by
87+
occupant count, and renders the selection ring above the unit layer.
88+
89+
## hmmm
90+
91+
- whether later Flower-of-Life rings belong on this presentation surface;
92+
- the exact live engine-to-observation adapter that attaches UCNS positions is
93+
owned by the eventual engine integration, not by this graphics package;
94+
- construction animation remains unavailable until an owning mechanics layer
95+
emits an already-resolved construction event contract.

ahbg/presentation/__init__.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# === MODULE_BUILD ===
2+
# id: ahbg_presentation_public_boundary
3+
# module_name: presentation
4+
# module_kind: adapter
5+
# summary: exposes the validated presentation snapshot and projection boundary without exporting mechanics
6+
# owner: AHBG presentation
7+
# public_surface: PresentationSnapshotError, load_snapshot, snapshot_from_observation, validate_snapshot
8+
# internal_surface: none
9+
# auth_boundary: none
10+
# storage_boundary: none
11+
# network_boundary: none
12+
# user_data_boundary: none
13+
# admin_only: false
14+
# tests: ahbg/presentation/tests/test_presentation.py
15+
# rollout: import explicitly from ahbg.presentation
16+
# rollback: remove package exports with presentation package
17+
# requires: ahbg_presentation_snapshot_contract, ahbg_presentation_observation_projector
18+
# since: 2026-08-31
19+
# unresolved: none
20+
# === END MODULE_BUILD ===
21+
22+
"""Presentation-only AHBG graphics boundary.
23+
24+
Usage guidance: import only validated presentation helpers from this package;
25+
game mechanics remain outside this namespace.
26+
"""
27+
28+
from .project import snapshot_from_observation
29+
from .snapshot import PresentationSnapshotError, load_snapshot, validate_snapshot
30+
31+
__all__ = [
32+
"PresentationSnapshotError",
33+
"load_snapshot",
34+
"snapshot_from_observation",
35+
"validate_snapshot",
36+
]

ahbg/presentation/board.css

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
:root {
2+
--ink: #1b1a17;
3+
--paper: #f4efe4;
4+
--tile: #e6dcc8;
5+
--tile-stroke: #6b5d44;
6+
--selected: #c45c26;
7+
--unit: #1f4b99;
8+
}
9+
10+
html,
11+
body {
12+
margin: 0;
13+
background: var(--paper);
14+
color: var(--ink);
15+
font: 16px/1.4 "Iowan Old Style", "Palatino Linotype", Palatino, serif;
16+
}
17+
18+
main {
19+
display: grid;
20+
grid-template-columns: minmax(0, 1fr) 18rem;
21+
min-height: 100vh;
22+
}
23+
24+
.board-wrap { display: flex; flex-direction: column; padding: 1.5rem; }
25+
h1, h2 { font-weight: 600; letter-spacing: 0.04em; margin: 0 0 0.75rem; }
26+
.note { margin: 0 0 1rem; font-size: 0.95rem; }
27+
svg { width: min(100%, 36rem); height: auto; align-self: center; }
28+
.seed-circle { fill: none; stroke: var(--tile-stroke); stroke-width: 1.5; pointer-events: none; }
29+
.tile-point { fill: var(--tile-stroke); stroke: var(--paper); stroke-width: 1.5; pointer-events: none; }
30+
.tile-point.selected { fill: var(--selected); stroke: var(--selected); }
31+
.motion-path { fill: none; stroke: var(--selected); stroke-width: 2.5; stroke-dasharray: 6 4; pointer-events: none; }
32+
.tile-hit { fill: transparent; cursor: pointer; stroke: transparent; stroke-width: 3; }
33+
.tile-hit:focus { outline: none; stroke: var(--selected); }
34+
.tile-label { fill: var(--ink); font-size: 11px; pointer-events: none; text-anchor: middle; }
35+
.unit { fill: var(--unit); stroke: var(--paper); stroke-width: 2; pointer-events: none; }
36+
.unit-label { fill: var(--paper); font-size: 10px; pointer-events: none; text-anchor: middle; }
37+
.selection-ring { fill: none; stroke: var(--selected); stroke-width: 3; pointer-events: none; }
38+
.feed { background: #efe7d6; border-left: 1px solid var(--tile-stroke); padding: 1.5rem; }
39+
.feed ol { margin: 0; padding-left: 1.2rem; }
40+
.inspect { margin-top: 1rem; font-size: 0.95rem; }
41+
42+
@media (max-width: 720px) {
43+
main { grid-template-columns: 1fr; }
44+
.feed { border-left: 0; border-top: 1px solid var(--tile-stroke); }
45+
}

ahbg/presentation/board.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>AHBG presentation board</title>
7+
<link rel="stylesheet" href="board.css" />
8+
</head>
9+
<body>
10+
<main>
11+
<section class="board-wrap">
12+
<h1>AHBG plane</h1>
13+
<p class="note">Presentation only. Not mechanics. Tile centers are supplied by the declared UCNS source; the browser only scales them for display. Dashed traces show already-resolved motion. Click a center, or focus it and press Enter/Space, to inspect.</p>
14+
<svg id="board" role="group" aria-label="Interactive Seed of Life presentation with inspectable tile centers and resolved unit motion traces"></svg>
15+
</section>
16+
<aside class="feed">
17+
<h2>Feed</h2>
18+
<ol id="feed-list"></ol>
19+
<p class="inspect" id="inspect" aria-live="polite"></p>
20+
</aside>
21+
</main>
22+
<script src="board.js"></script>
23+
</body>
24+
</html>

0 commit comments

Comments
 (0)