The acknowledged overhang: "doesn't fit, shelved anyway" goes legit
Eric's office towers are 9.5 inches deep; Etherfields boxes are 11.8 inches square. Spine-out they stand 2.3 inches proud of the shelf edge — clearly livable, it's how they sit today — but the lane model will honestly refuse forever: can't stand (11.8 > 9.5 depth), can't stack flat (same), so the opening warns NO PACKING FITS. The first deliberate misfit deserves first-class status. An assignment can now carry acknowledged: true. An acknowledged box takes the standing lane WITHOUT the height/depth check — the human has seen the overhang — while the width budget stays honest (nine acknowledged monoliths still overflow a 23.25-inch row). It drops out of misfit/laneless warnings everywhere: the wall diagram, the sheet, and bggpipe dims (which now says "overhangs, acknowledged" instead). Ways in: an "accept overhang" button in the opening's sheet (appears on any resident the lanes can't take; tap again to retract), or an acknowledge column (yes) in shelve --import. Once acknowledged, later imports needn't re-state it. Re-import hardening, prompted by Eric's "will a new import make a mess?": it's an upsert, not a reset — and now genuinely so. Previously a re-import preserved notes but silently wiped lane overrides; now notes, lanes, and acknowledgments all survive when the opening is unchanged (a move resets lane and acknowledgment — new spot, new verdict). Same fix in /api/locate: the lane-flip button used to wipe the hand-entered note because LocateBody.note defaulted to "" — it's now tri-state, None means leave it alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
4ce7706521
commit
7d7d3bb8da
@@ -13,9 +13,11 @@ from bggpipe.shelves import (
|
||||
fits_opening,
|
||||
import_assignments,
|
||||
load_furniture,
|
||||
load_locations,
|
||||
new_opening_id,
|
||||
opening_report,
|
||||
save_furniture,
|
||||
save_locations,
|
||||
)
|
||||
|
||||
|
||||
@@ -1009,3 +1011,87 @@ def test_add_openings_at_top(tmp_path):
|
||||
o["label"] for o in web.get("/api/shelves").json()["units"][0]["openings"]
|
||||
]
|
||||
assert labels == ["on top", "A1", "A2", "B1", "B2"]
|
||||
|
||||
|
||||
def test_acknowledged_overhang(tmp_path):
|
||||
"""Etherfields on the office towers: 11.8\" boxes on a 9.5\"-deep
|
||||
shelf stand 2.3\" proud. The checker honestly refuses — until the
|
||||
overhang is acknowledged, after which it takes the standing lane
|
||||
(width-honest) and stops counting as a warning."""
|
||||
tower = {
|
||||
"id": "tower-e1",
|
||||
"label": "E1",
|
||||
"zone": "",
|
||||
"width_in": 23.25,
|
||||
"height_in": 19.0,
|
||||
"depth_in": 9.5,
|
||||
}
|
||||
ether = _entry("Etherfields", 11.8, 11.8, 2.9)
|
||||
# face-out it FITS the opening; it's the lane packing that refuses
|
||||
# (can't stand spine-out, can't stack flat — 11.8" > 9.5" depth)
|
||||
report = opening_report(tower, [("g1", ether)])
|
||||
assert report["misfits"] == []
|
||||
assert report["laneless"] == ["Etherfields"]
|
||||
assert report["overfull"]
|
||||
report = opening_report(tower, [("g1", ether)], acknowledged={"g1"})
|
||||
assert report["misfits"] == []
|
||||
assert report["laneless"] == []
|
||||
assert report["lanes"] == {"g1": "standing"}
|
||||
assert report["standing_in"] == pytest.approx(2.9)
|
||||
# the width budget stays honest: nine acknowledged monoliths
|
||||
# still overflow a 23.25" row
|
||||
crowd = [(f"g{i}", ether) for i in range(9)]
|
||||
report = opening_report(tower, crowd, acknowledged={k for k, _ in crowd})
|
||||
assert report["overfull"]
|
||||
|
||||
|
||||
def test_import_acknowledge_column(tmp_path):
|
||||
"""The CSV import rejects a doesn't-fit row with a hint, accepts it
|
||||
with acknowledge=yes, and a re-import keeps lane + acknowledgment
|
||||
when the opening is unchanged (upsert, not reset)."""
|
||||
cfg = Config(data_dir=tmp_path / "data")
|
||||
cfg.data_dir.mkdir(parents=True)
|
||||
cfg.games_path.write_text(json.dumps({"1": _entry("Etherfields", 11.8, 11.8, 2.9)}))
|
||||
save_furniture(
|
||||
cfg,
|
||||
[
|
||||
{
|
||||
"name": "Tower",
|
||||
"openings": [
|
||||
{
|
||||
"id": "tower-e1",
|
||||
"label": "E1",
|
||||
"zone": "",
|
||||
"width_in": 23.25,
|
||||
"height_in": 10.0,
|
||||
"depth_in": 9.5,
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
csv_path = tmp_path / "plan.csv"
|
||||
csv_path.write_text("name,opening\nEtherfields,tower-e1\n")
|
||||
result = import_assignments(cfg, csv_path)
|
||||
assert result["assigned"] == 0
|
||||
assert "acknowledge column" in result["rejects"][0]
|
||||
csv_path.write_text("name,opening,acknowledge\nEtherfields,tower-e1,yes\n")
|
||||
result = import_assignments(cfg, csv_path)
|
||||
assert result["assigned"] == 1
|
||||
locations = load_locations(cfg)
|
||||
assert locations["1"]["acknowledged"] is True
|
||||
# hand-set a lane, re-import WITHOUT the column: same opening, so
|
||||
# the note, lane, and acknowledgment all survive
|
||||
locations["1"]["lane"] = "standing"
|
||||
locations["1"]["note"] = "top row"
|
||||
save_locations(cfg, locations)
|
||||
csv_path.write_text("name,opening\nEtherfields,tower-e1\n")
|
||||
result = import_assignments(cfg, csv_path)
|
||||
assert result["assigned"] == 1
|
||||
record = load_locations(cfg)["1"]
|
||||
assert record == {
|
||||
"opening_id": "tower-e1",
|
||||
"note": "top row",
|
||||
"lane": "standing",
|
||||
"acknowledged": True,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user