Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2581afd26 | ||
|
|
059c2f743b |
+1
-1
@@ -3707,7 +3707,7 @@
|
|||||||
"template": "P23 life observation: <checks> checks, <failures> failures",
|
"template": "P23 life observation: <checks> checks, <failures> failures",
|
||||||
"required": true,
|
"required": true,
|
||||||
"fixed_check_count_used": false,
|
"fixed_check_count_used": false,
|
||||||
"source_sha256_at_schema_audit": "5c52db4dd70f9702c096e918d849703d14aec1acb88bb2458ca01049da71ce3c",
|
"source_sha256_at_schema_audit": "750254af7d4b8b00508e25d1ef744837d3a7d14ddebe12398e8014bf5e8c68e8",
|
||||||
"authority": {
|
"authority": {
|
||||||
"path": "docs/spec/acceptance/P23-03.json",
|
"path": "docs/spec/acceptance/P23-03.json",
|
||||||
"scope": "component and read-only observation only; native unexecuted; whole unit blocked"
|
"scope": "component and read-only observation only; native unexecuted; whole unit blocked"
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Repair36 worker submission
|
||||||
|
|
||||||
|
Scope: world_preview observation labels, independent checker, Python/GD controls.
|
||||||
|
No Runtime, service, data, art, policy, original negative, or acceptance changes.
|
||||||
|
Input authority: registered BUILD `audit-support/badge-repair36-exact-inputs/EXACT_INPUTS.json`, source 26f60095; original paths are provenance identifiers.
|
||||||
|
|
||||||
|
The resolver places fixed cabin rectangles/badges first, then business, supply,
|
||||||
|
landing, aggregation in array order. A positive-area collision advances to the
|
||||||
|
first blocking rectangle's right edge and repeats. With finite rectangles of
|
||||||
|
positive width this always terminates on the anchor row; the specified upper
|
||||||
|
and lower fallback rows are therefore unreachable. No margin, pixel threshold,
|
||||||
|
clipping, truncation, member alteration, or new viewport rule is introduced.
|
||||||
|
Original anchors and ordered blocker identities are retained in deep-copy reports.
|
||||||
|
Traffic obstacles use the resolved positions; actor placement/traffic subtraction
|
||||||
|
and cabin/transit guards retain their existing implementations.
|
||||||
|
|
||||||
|
The checker separately projects anchors back into the original report shape for
|
||||||
|
existing raw-state queue/business/aggregation recomputation. It independently
|
||||||
|
replays label geometry and validates reported positions and blocker identities.
|
||||||
|
All label pairs must have zero positive-area intersection. Both production image
|
||||||
|
review consumers require explicit indexed `judgments`; the legacy `rows` helper
|
||||||
|
interface remains available for unchanged historical unit controls only. Compact
|
||||||
|
bits are never accepted for new image-review evidence.
|
||||||
|
|
||||||
|
## Static all40 comparison
|
||||||
|
|
||||||
|
Inputs: all40 original manifests plus their unchanged original requests in
|
||||||
|
`p23-normal-os-26f-r17`, read only. This is a counterfactual static projection,
|
||||||
|
not a new rendered screenshot or native/OS acceptance.
|
||||||
|
|
||||||
|
Original manifest label pairs: exactly one intersection, B/writer-request-0021,
|
||||||
|
rows 5 (queue) and 26 (aggregation). Resolved labels: zero intersections in all
|
||||||
|
40 frames, including fixed cabin labels. Thirty frames have no movement.
|
||||||
|
The following is the complete movement list in local canvas coordinates:
|
||||||
|
|
||||||
|
| Frame | Label | Original | Resolved | displaced_by |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| A/reader-request-0003 | landing/0 | 563,468 | 755,468 | cabin/0/rect |
|
||||||
|
| A/writer-request-0014 | landing/0 | 563,468 | 845,468 | cabin/0/badge |
|
||||||
|
| A/writer-request-0021 | landing/0 | 563,468 | 875,468 | cabin/0/badge |
|
||||||
|
| A/writer-request-0025 | landing/0 | 563,468 | 855,468 | cabin/0/badge |
|
||||||
|
| A/writer-request-0030 | landing/0 | 563,468 | 755,468 | cabin/0/rect |
|
||||||
|
| B/reader-request-0003 | landing/1 | 819,468 | 1111,468 | cabin/0/badge, cabin/1/badge |
|
||||||
|
| B/writer-request-0014 | landing/0 | 563,468 | 1101,468 | cabin/0/badge, cabin/1/badge |
|
||||||
|
| B/writer-request-0014 | landing/1 | 819,468 | 1217,468 | cabin/1/badge, landing/0 |
|
||||||
|
| B/writer-request-0021 | landing/0 | 563,468 | 755,468 | cabin/0/rect |
|
||||||
|
| B/writer-request-0021 | landing/1 | 819,468 | 1011,468 | cabin/1/rect |
|
||||||
|
| B/writer-request-0021 | aggregation/0 | 563,372 | 825,372 | landing/2, cabin/0/badge |
|
||||||
|
| B/writer-request-0025 | landing/1 | 819,468 | 1111,468 | cabin/0/badge, cabin/1/badge |
|
||||||
|
| B/writer-request-0030 | landing/1 | 819,468 | 1111,468 | cabin/0/badge, cabin/1/badge |
|
||||||
|
|
||||||
|
The affected queue anchor remains local [563,372], corresponding to original
|
||||||
|
manifest screenshot [642,469]. Its five members remain hidden and its complete
|
||||||
|
label remains unchanged. The historical reviewer index24/text26 disagreement
|
||||||
|
and all prior negatives remain untouched; no staff defect or acceptance credit
|
||||||
|
is inferred.
|
||||||
|
|
||||||
|
## Validation boundary
|
||||||
|
|
||||||
|
Worker runs Python syntax/local semantic tests only. GD controls are added but
|
||||||
|
not executed here. Root still must replay currentbad, run localized/native,
|
||||||
|
new normal OS A/B with indexed independent image review, then one full regression
|
||||||
|
and formal review/integration/ledger in the recorded order. No formal acceptance,
|
||||||
|
original88 credit, Windows, held-gesture, performance, or asset credit is claimed.
|
||||||
|
|
||||||
|
Local result: Python AST syntax PASS; 146 Python tests PASS (142 retained plus
|
||||||
|
four focused controls). All 142 pre-existing test method ASTs match HEAD exactly.
|
||||||
|
`git diff --check` PASS. Only the existing P23 GD marker source SHA was refreshed;
|
||||||
|
no marker requirements or policies changed. No Godot/native/OS/full was run.
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Repair37 portable recorded negative fixture
|
||||||
|
|
||||||
|
Candidate starts at `059c2f743be655c11627a2b19d8eab9f86a000a6` under the unchanged P23-03 contract. No Git write or Runner operation was performed.
|
||||||
|
|
||||||
|
`tests/fixtures/p23-03/recorded-badge-negative.json.gz` is copied byte-for-byte from Root's `.runtime/P23-03/build/audit-support/badge-repair37-portable-fixture/PROPOSED_PORTABLE_FIXTURE.json.gz` in the parent worktree directory. SHA256: `17b1596fb1dca0436cd656ac21a9dc754c75b4485335a0aebe533f70e1c1b5ac`; gzip length 83,518 bytes; decompressed length 3,490,211 bytes. Repeated decompression yields identical bytes.
|
||||||
|
|
||||||
|
The bundle records observed source `26f60095bff015efbf7d7cf89b5b0bbab1fec31e`, observed tested commit `dfe0dc44b0b88ace598c1b8bd05283c6084cb081`, and original PID 87103. These are preserved negative-input provenance, not acceptance evidence for 059 or this candidate. Original relative paths below are relative to the preserved `p23-normal-os-26f-r17` evidence root:
|
||||||
|
|
||||||
|
| Payload | Original relative path | Bytes | SHA256 |
|
||||||
|
| --- | --- | ---: | --- |
|
||||||
|
| request | `os-pair/B/writer-request-0021/request.json` | 655285 | `f9cd2d5ff9dd63d9cf7ae4a295ccef8eea16f75ac784696dc40906703731e1d8` |
|
||||||
|
| state | `os-pair/B/writer-stream/objects/724d8fe362ba3e05eddd83c4b7c54da101899df6cd91ae88250feea3d4feff82.json` | 1867011 | `724d8fe362ba3e05eddd83c4b7c54da101899df6cd91ae88250feea3d4feff82` |
|
||||||
|
| manifest | `os-pair/B/writer-request-0021/review-image-0/manifest.json` | 75726 | `a98782ec1263aba400d542862dd084d2f1dd4cf7a0851cf7f0679867e545a338` |
|
||||||
|
|
||||||
|
The loader verifies all three payload lengths and hashes on every call. Request uses JSON, report and state use the existing typed decoder; state retains the whole-Dictionary check. There is no runtime lookup, fallback, cache, extraction, or skip.
|
||||||
|
|
||||||
|
Local validation: all 146 Python tests pass; AST audit against 059c2f7 confirms 145 test methods are source-byte and AST identical, and the remaining method is AST identical after replacing only the manifest assignment value with the same fixed placeholder. Python syntax compilation passes. A fresh isolated copy of Git-tracked candidate files plus the fixture, without `.runtime`, passes Repair36LabelTests 4/4. Separate corrupt-payload and missing-fixture controls fail with fatal errors. No extra fixture is retained.
|
||||||
|
|
||||||
|
Frozen whole-file SHA256 values remain unchanged: renderer `4bc49a19a38b9c3b4d6bfc243ddf46f1f25469a8273adb5e812911ef9424a1f2`; checker `e41479ae51bccdfab3f31bd348794b441c2f341179bbe33222e2aacdf88f987f`.
|
||||||
|
|
||||||
|
Native and OS checks are still pending. Root's final local run on the new tested commit, raw geometry/native checks, new normal OS run, every indexed-image review, mandatory full 179, formal all 7, integration and ledger remain pending. No Godot or full suite was run by this worker.
|
||||||
@@ -637,6 +637,7 @@ func _build_life_observation() -> Dictionary:
|
|||||||
&"at": rect.position + Vector2(2, 16), &"requests": requests,
|
&"at": rect.position + Vector2(2, 16), &"requests": requests,
|
||||||
&"text": RoomPresentation.supply_label(requests)})
|
&"text": RoomPresentation.supply_label(requests)})
|
||||||
model[&"badges"] = badges
|
model[&"badges"] = badges
|
||||||
|
_resolve_observation_labels(model)
|
||||||
var obstacles: Array[Rect2] = []
|
var obstacles: Array[Rect2] = []
|
||||||
for cabin: Dictionary in model[&"cabins"]:
|
for cabin: Dictionary in model[&"cabins"]:
|
||||||
obstacles.append(cabin[&"rect"])
|
obstacles.append(cabin[&"rect"])
|
||||||
@@ -691,6 +692,39 @@ func _traffic_segments(band: Rect2, cell: Rect2, obstacles: Array[Rect2]) -> Arr
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
func _resolve_observation_labels(model: Dictionary) -> void:
|
||||||
|
var placed: Array[Dictionary] = []
|
||||||
|
for i: int in model[&"cabins"].size():
|
||||||
|
var cabin: Dictionary = model[&"cabins"][i]
|
||||||
|
placed.append({&"id": "cabin/%d/rect" % i, &"rect": cabin[&"rect"]})
|
||||||
|
placed.append({&"id": "cabin/%d/badge" % i, &"rect": cabin[&"badge_rect"]})
|
||||||
|
for kind: String in ["business", "supply", "landing", "aggregation"]:
|
||||||
|
var collection: Array = model[&"badges"] if kind in ["business", "supply"] else model[&"landing_badges"] if kind == "landing" else model.get(&"actor_aggregations", [])
|
||||||
|
for i: int in collection.size():
|
||||||
|
var badge: Dictionary = collection[i]
|
||||||
|
if kind in ["business", "supply"] and badge[&"kind"] != kind:
|
||||||
|
continue
|
||||||
|
var anchor: Vector2 = badge[&"at"]
|
||||||
|
var at := anchor
|
||||||
|
var displaced_by: Array[String] = []
|
||||||
|
# Each collision advances to an existing right edge. Positive widths
|
||||||
|
# guarantee termination; touching edges are allowed.
|
||||||
|
while true:
|
||||||
|
var blocker: Dictionary = {}
|
||||||
|
for previous: Dictionary in placed:
|
||||||
|
if _readable_badge_rect(at, badge[&"text"]).intersection(previous[&"rect"]).has_area():
|
||||||
|
blocker = previous
|
||||||
|
break
|
||||||
|
if blocker.is_empty():
|
||||||
|
break
|
||||||
|
displaced_by.append(blocker[&"id"])
|
||||||
|
at.x = (blocker[&"rect"] as Rect2).end.x
|
||||||
|
badge[&"anchor"] = anchor
|
||||||
|
badge[&"at"] = at
|
||||||
|
badge[&"displaced_by"] = displaced_by.duplicate(true)
|
||||||
|
placed.append({&"id": "%s/%d" % [kind, i], &"rect": _readable_badge_rect(at, badge[&"text"])})
|
||||||
|
|
||||||
|
|
||||||
func _draw_life_badges(canvas: CanvasItem) -> void:
|
func _draw_life_badges(canvas: CanvasItem) -> void:
|
||||||
for badge: Dictionary in _life_observation.get(&"badges", []):
|
for badge: Dictionary in _life_observation.get(&"badges", []):
|
||||||
_draw_readable_badge(badge[&"at"], badge[&"text"], canvas)
|
_draw_readable_badge(badge[&"at"], badge[&"text"], canvas)
|
||||||
|
|||||||
Binary file not shown.
@@ -100,6 +100,31 @@ func repair28_geometry() -> void:
|
|||||||
func _initialize() -> void:
|
func _initialize() -> void:
|
||||||
call_deferred("run")
|
call_deferred("run")
|
||||||
|
|
||||||
|
func repair36_labels() -> void:
|
||||||
|
var view := WorldPreview.new()
|
||||||
|
var members: Array[String] = ["guest/a", "guest/b", "guest/c"]
|
||||||
|
var identities: Array[Dictionary] = []
|
||||||
|
for key: String in members:
|
||||||
|
identities.append({&"key": key, &"id": key.trim_prefix("guest/"), &"state": 0, &"cell": Vector3i.ZERO, &"mode": &"idle"})
|
||||||
|
var queue := {&"at": Vector2(40,40), &"text": "待ち3人(他3人)", &"cell": Vector3i.ZERO, &"members": members.duplicate(), &"visible_count": 0, &"overflow": members.size()}
|
||||||
|
var aggregate := {&"at": queue[&"at"], &"text": "+3", &"members": identities.duplicate(true), &"drawn_keys": []}
|
||||||
|
var model := {&"cabins": [], &"badges": [], &"landing_badges": [queue], &"actor_aggregations": [aggregate]}
|
||||||
|
var blocked := Rect2(100,100,20,20)
|
||||||
|
model[&"cabins"] = [{&"rect": blocked, &"badge_at": Vector2(200,200), &"badge": "EV", &"badge_rect": view._readable_badge_rect(Vector2(200,200), "EV")}]
|
||||||
|
model[&"shaft_footprint"] = []
|
||||||
|
var region: Dictionary = view._life_landing_region({&"bounds": blocked, &"transit": false}, Vector3i.ZERO, model)
|
||||||
|
expect(region.get(&"no_lane", false) and region[&"badge_at"] == queue[&"at"], "lane-less stranded actor uses hidden queue anchor")
|
||||||
|
view._resolve_observation_labels(model)
|
||||||
|
expect(not view._readable_badge_rect(queue[&"at"], queue[&"text"]).intersection(view._readable_badge_rect(aggregate[&"at"], aggregate[&"text"])).has_area(), "hidden queue and stranded aggregate labels do not overlap")
|
||||||
|
expect(queue[&"text"] == "待ち3人(他3人)" and aggregate[&"text"] == "+3", "complete counts remain readable")
|
||||||
|
expect(queue[&"members"] == members and aggregate[&"members"] == identities and aggregate[&"drawn_keys"].is_empty(), "label placement preserves member identities and visibility")
|
||||||
|
expect(queue[&"at"] == queue[&"anchor"] and aggregate[&"anchor"] == queue[&"anchor"], "uncontested queue keeps anchor and stranded anchor remains recorded")
|
||||||
|
var solitary := {&"at": Vector2(400,400), &"text": "+1"}
|
||||||
|
model[&"actor_aggregations"] = [solitary]
|
||||||
|
view._resolve_observation_labels(model)
|
||||||
|
expect(solitary[&"at"] == solitary[&"anchor"] and solitary[&"displaced_by"].is_empty(), "uncontested label stays at anchor")
|
||||||
|
view.free()
|
||||||
|
|
||||||
func expect(value: bool, message: String) -> void:
|
func expect(value: bool, message: String) -> void:
|
||||||
checks += 1
|
checks += 1
|
||||||
if not value:
|
if not value:
|
||||||
@@ -118,6 +143,7 @@ func run() -> void:
|
|||||||
var clock: Variant = root.get_node("GameClock")
|
var clock: Variant = root.get_node("GameClock")
|
||||||
clock.set_paused(true)
|
clock.set_paused(true)
|
||||||
repair28_geometry()
|
repair28_geometry()
|
||||||
|
repair36_labels()
|
||||||
# Isolated real typed components test state labels, without claiming gameplay.
|
# Isolated real typed components test state labels, without claiming gameplay.
|
||||||
var labels: Dictionary = {}
|
var labels: Dictionary = {}
|
||||||
for state: int in FacilityInstance.State.values():
|
for state: int in FacilityInstance.State.values():
|
||||||
|
|||||||
@@ -310,6 +310,9 @@ def paired_crop_noise(before, after, rect):
|
|||||||
|
|
||||||
def drawing_rectangle_rows(report):
|
def drawing_rectangle_rows(report):
|
||||||
"""Derive identities and local positions; do not read audit verdicts."""
|
"""Derive identities and local positions; do not read audit verdicts."""
|
||||||
|
if any('anchor' in b for key in ('badges', 'landing_badges', 'actor_aggregations')
|
||||||
|
for b in report.get(key, [])):
|
||||||
|
validate_observation_labels(report)
|
||||||
expected = []
|
expected = []
|
||||||
tower = 'scripts/ui/pixel_tower_art.gd'
|
tower = 'scripts/ui/pixel_tower_art.gd'
|
||||||
people = 'scripts/ui/pixel_people.gd'
|
people = 'scripts/ui/pixel_people.gd'
|
||||||
@@ -549,13 +552,16 @@ def write_frame_review(directory, frame, provenance):
|
|||||||
return manifest, digest
|
return manifest, digest
|
||||||
|
|
||||||
|
|
||||||
def reviewed_pixel_categories(manifest, digest, verdict_path):
|
def reviewed_pixel_categories(manifest, digest, verdict_path, *, require_judgments=False):
|
||||||
"""The worker never writes verdicts; absent independent review earns nothing."""
|
"""The worker never writes verdicts; absent independent review earns nothing."""
|
||||||
if not Path(verdict_path).is_file():
|
if not Path(verdict_path).is_file():
|
||||||
return set()
|
return set()
|
||||||
verdict = json.loads(Path(verdict_path).read_bytes())
|
verdict = json.loads(Path(verdict_path).read_bytes())
|
||||||
require(verdict['manifest_sha256'] == digest, 'review manifest SHA mismatch')
|
require(verdict['manifest_sha256'] == digest, 'review manifest SHA mismatch')
|
||||||
require(verdict['reviewer'] == 'independent-opus-direct-image', 'independent image reviewer required')
|
require(verdict['reviewer'] == 'independent-opus-direct-image', 'independent image reviewer required')
|
||||||
|
if require_judgments:
|
||||||
|
require(isinstance(verdict.get('judgments'), list), 'explicit indexed judgments required')
|
||||||
|
verdict = dict(verdict, rows=verdict['judgments'])
|
||||||
eligible = [r for r in manifest['rows'] if r['eligibility'] != 'outside']
|
eligible = [r for r in manifest['rows'] if r['eligibility'] != 'outside']
|
||||||
require([v['row_index'] for v in verdict['rows']] == [r['row_index'] for r in eligible],
|
require([v['row_index'] for v in verdict['rows']] == [r['row_index'] for r in eligible],
|
||||||
'review row coverage')
|
'review row coverage')
|
||||||
@@ -651,7 +657,7 @@ def inspect_offline_mirror(directory):
|
|||||||
receipt_sha256=sha(folder/'cua-receipt.json')))
|
receipt_sha256=sha(folder/'cua-receipt.json')))
|
||||||
review_dir = ROOT/'docs/plans/original-completion-2026-10-02/execution/p23-03/offline-r07-review'/name
|
review_dir = ROOT/'docs/plans/original-completion-2026-10-02/execution/p23-03/offline-r07-review'/name
|
||||||
manifest, digest = write_frame_review(review_dir, frame, provenance)
|
manifest, digest = write_frame_review(review_dir, frame, provenance)
|
||||||
credited = reviewed_pixel_categories(manifest, digest, review_dir/'independent-verdict.json')
|
credited = reviewed_pixel_categories(manifest, digest, review_dir/'independent-verdict.json', require_judgments=True)
|
||||||
present = {r['category'] for r in frame['rows'] if r['eligibility'] != 'outside'}
|
present = {r['category'] for r in frame['rows'] if r['eligibility'] != 'outside'}
|
||||||
overlays = [dict(cell=r['cell'], rect=r['rect']) for r in report['traffic']
|
overlays = [dict(cell=r['cell'], rect=r['rect']) for r in report['traffic']
|
||||||
if (r['traffic']['current_density'] > 0 or r['traffic']['temporary_block'])
|
if (r['traffic']['current_density'] > 0 or r['traffic']['temporary_block'])
|
||||||
@@ -803,6 +809,61 @@ def badge_rectangle(at, text):
|
|||||||
return list(at) + [len(text) * 10 + 6, 14]
|
return list(at) + [len(text) * 10 + 6, 14]
|
||||||
|
|
||||||
|
|
||||||
|
def observation_label_layout(report):
|
||||||
|
"""Independent ordered rectangle placement; never consume reported at/reasons."""
|
||||||
|
placed, result = [], []
|
||||||
|
for i, cabin in enumerate(report['cabins']):
|
||||||
|
placed.extend([(f'cabin/{i}/rect', cabin['rect']),
|
||||||
|
(f'cabin/{i}/badge', badge_rectangle(cabin['badge_at'], cabin['badge']))])
|
||||||
|
for kind, collection in [('business', 'badges'), ('supply', 'badges'),
|
||||||
|
('landing', 'landing_badges'), ('aggregation', 'actor_aggregations')]:
|
||||||
|
for i, row in enumerate(report.get(collection, [])):
|
||||||
|
if collection == 'badges' and row['kind'] != kind:
|
||||||
|
continue
|
||||||
|
anchor = list(row.get('anchor', row['at']))
|
||||||
|
at, reasons = anchor[:], []
|
||||||
|
while True:
|
||||||
|
conflict = next(((identity, rect) for identity, rect in placed
|
||||||
|
if intersects(badge_rectangle(at, row['text']), rect)), None)
|
||||||
|
if conflict is None:
|
||||||
|
break
|
||||||
|
identity, rect = conflict
|
||||||
|
reasons.append(identity)
|
||||||
|
at[0] = rect[0] + rect[2]
|
||||||
|
result.append((collection, i, dict(anchor=anchor, at=at, displaced_by=reasons)))
|
||||||
|
placed.append((f'{kind}/{i}', badge_rectangle(at, row['text'])))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def observation_anchor_report(report):
|
||||||
|
"""Projection for existing raw-state semantic recomputation, not evidence."""
|
||||||
|
import copy
|
||||||
|
raw = copy.deepcopy(report)
|
||||||
|
for collection in ('badges', 'landing_badges', 'actor_aggregations'):
|
||||||
|
for row in raw.get(collection, []):
|
||||||
|
if 'anchor' in row:
|
||||||
|
row['at'] = row.pop('anchor')
|
||||||
|
row.pop('displaced_by', None)
|
||||||
|
return raw
|
||||||
|
|
||||||
|
|
||||||
|
def validate_observation_labels(report):
|
||||||
|
require(all(key in report for key in ('cabins', 'badges', 'landing_badges', 'actor_aggregations')),
|
||||||
|
'missing observation label collections')
|
||||||
|
rectangles = [badge_rectangle(c['badge_at'], c['badge']) for c in report['cabins']]
|
||||||
|
rectangles += [badge_rectangle(b['at'], b['text'])
|
||||||
|
for key in ('badges', 'landing_badges', 'actor_aggregations')
|
||||||
|
for b in report.get(key, [])]
|
||||||
|
for i, rect in enumerate(rectangles):
|
||||||
|
require(not any(intersects(rect, other) for other in rectangles[:i]),
|
||||||
|
'observation label intersection')
|
||||||
|
for collection, index, wanted in observation_label_layout(report):
|
||||||
|
row = report[collection][index]
|
||||||
|
for key, value in wanted.items():
|
||||||
|
require(key in row, 'missing label ' + key)
|
||||||
|
close(row[key], value, 'observation label/' + key)
|
||||||
|
|
||||||
|
|
||||||
def intersects(a, b):
|
def intersects(a, b):
|
||||||
cut = rect_intersection(a, b)
|
cut = rect_intersection(a, b)
|
||||||
return cut[2] > 0 and cut[3] > 0
|
return cut[2] > 0 and cut[3] > 0
|
||||||
@@ -965,6 +1026,9 @@ def validate_life_overlay(report, root=ROOT, snapshot=None):
|
|||||||
|
|
||||||
|
|
||||||
def compare_queues(snapshot, report):
|
def compare_queues(snapshot, report):
|
||||||
|
if any('anchor' in b for b in report.get('landing_badges', [])):
|
||||||
|
validate_observation_labels(report)
|
||||||
|
report = observation_anchor_report(report)
|
||||||
runtime = snapshot['systems']['runtime']
|
runtime = snapshot['systems']['runtime']
|
||||||
transform = Transform(runtime, report)
|
transform = Transform(runtime, report)
|
||||||
facilities = codec.indexed(runtime['facilities']['instances'], 'instance_id')
|
facilities = codec.indexed(runtime['facilities']['instances'], 'instance_id')
|
||||||
@@ -1222,6 +1286,9 @@ def compare_goods(snapshot, report, root=ROOT):
|
|||||||
|
|
||||||
|
|
||||||
def compare_badges(state, report):
|
def compare_badges(state, report):
|
||||||
|
if any('anchor' in b for b in report.get('badges', [])):
|
||||||
|
validate_observation_labels(report)
|
||||||
|
report = observation_anchor_report(report)
|
||||||
runtime = state['systems']['runtime']
|
runtime = state['systems']['runtime']
|
||||||
transform = Transform(runtime, report)
|
transform = Transform(runtime, report)
|
||||||
expected = []
|
expected = []
|
||||||
@@ -1490,6 +1557,9 @@ def landing_actor_region(state, report, transform, cell, region, transit):
|
|||||||
|
|
||||||
|
|
||||||
def compare_actor_geometry(state, report):
|
def compare_actor_geometry(state, report):
|
||||||
|
if any('anchor' in b for b in report.get('actor_aggregations', [])):
|
||||||
|
validate_observation_labels(report)
|
||||||
|
report = observation_anchor_report(report)
|
||||||
runtime = state['systems']['runtime']
|
runtime = state['systems']['runtime']
|
||||||
compare_queues(state, report)
|
compare_queues(state, report)
|
||||||
compare_room_services(state, report)
|
compare_room_services(state, report)
|
||||||
@@ -1800,6 +1870,7 @@ def inspect_stream(directory, root=ROOT):
|
|||||||
report = read_object(directory, row['report'])
|
report = read_object(directory, row['report'])
|
||||||
fields = compare_actor_rows(state, report, root)
|
fields = compare_actor_rows(state, report, root)
|
||||||
require(report['elevators'] == elevator_report(state), 'typed elevator report mismatch')
|
require(report['elevators'] == elevator_report(state), 'typed elevator report mismatch')
|
||||||
|
validate_observation_labels(report)
|
||||||
compare_room_services(state, report, root)
|
compare_room_services(state, report, root)
|
||||||
compare_rest(state, report, read_object(directory, row['rest_audit']), root)
|
compare_rest(state, report, read_object(directory, row['rest_audit']), root)
|
||||||
compare_queues(state, report)
|
compare_queues(state, report)
|
||||||
@@ -2240,7 +2311,7 @@ def inspect_os_requests(directory, layout, role, pid, root=ROOT):
|
|||||||
require(frame['mechanical_pass'], 'frame mechanical checks: ' + str(frame['errors'])
|
require(frame['mechanical_pass'], 'frame mechanical checks: ' + str(frame['errors'])
|
||||||
+ str([r['failures'] for r in frame['rows'] if not r['mechanical_pass']]))
|
+ str([r['failures'] for r in frame['rows'] if not r['mechanical_pass']]))
|
||||||
pixel_categories.update(reviewed_pixel_categories(
|
pixel_categories.update(reviewed_pixel_categories(
|
||||||
manifest, manifest_sha, review_dir/'independent-verdict.json'))
|
manifest, manifest_sha, review_dir/'independent-verdict.json', require_judgments=True))
|
||||||
records.append(dict(request=request, ack=ack, observed=observed, cua=cua, request_sha256=digest))
|
records.append(dict(request=request, ack=ack, observed=observed, cua=cua, request_sha256=digest))
|
||||||
require(len(seen) == len(bindings), 'unattributed screenshot bindings')
|
require(len(seen) == len(bindings), 'unattributed screenshot bindings')
|
||||||
return records, pixel_categories
|
return records, pixel_categories
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import contextlib
|
|||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import gzip
|
||||||
import struct
|
import struct
|
||||||
import zlib
|
import zlib
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -2293,5 +2294,101 @@ class FrameReviewTests(unittest.TestCase):
|
|||||||
self.assertEqual(check.reviewed_pixel_categories(manifest, digest, verdict_path), set())
|
self.assertEqual(check.reviewed_pixel_categories(manifest, digest, verdict_path), set())
|
||||||
|
|
||||||
|
|
||||||
|
class Repair36LabelTests(unittest.TestCase):
|
||||||
|
inputs = check.ROOT / 'tests/fixtures/p23-03/recorded-badge-negative.json.gz'
|
||||||
|
|
||||||
|
def _recorded_payloads(self):
|
||||||
|
bundle = json.loads(gzip.decompress(self.inputs.read_bytes()))
|
||||||
|
expected = {
|
||||||
|
'request': (655285, 'f9cd2d5ff9dd63d9cf7ae4a295ccef8eea16f75ac784696dc40906703731e1d8'),
|
||||||
|
'state': (1867011, '724d8fe362ba3e05eddd83c4b7c54da101899df6cd91ae88250feea3d4feff82'),
|
||||||
|
'manifest': (75726, 'a98782ec1263aba400d542862dd084d2f1dd4cf7a0851cf7f0679867e545a338'),
|
||||||
|
}
|
||||||
|
payloads = {}
|
||||||
|
for name, (length, digest) in expected.items():
|
||||||
|
payload = bundle['files'][name]['raw_utf8'].encode('utf-8')
|
||||||
|
check.require(len(payload) == length and hashlib.sha256(payload).hexdigest() == digest,
|
||||||
|
'recorded fixture payload mismatch: ' + name)
|
||||||
|
payloads[name] = payload
|
||||||
|
return payloads
|
||||||
|
|
||||||
|
def recorded(self):
|
||||||
|
payloads = self._recorded_payloads()
|
||||||
|
request = json.loads(payloads['request'])
|
||||||
|
typed_state = json.loads(payloads['state'])
|
||||||
|
check.require(typed_state.get('type') == 'Dictionary', 'whole dictionary required')
|
||||||
|
return request, check.decode(typed_state), check.decode(request['report'])
|
||||||
|
|
||||||
|
def resolved(self, report):
|
||||||
|
result = copy.deepcopy(report)
|
||||||
|
for collection, index, fields in check.observation_label_layout(report):
|
||||||
|
result[collection][index].update(fields)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def test_recorded_negative_and_positive_with_raw_semantics(self):
|
||||||
|
request, state, old = self.recorded()
|
||||||
|
rectangles = check.drawing_rectangle_rows(old)
|
||||||
|
self.assertTrue(check.intersects(rectangles[5][2], rectangles[26][2]))
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
check.validate_observation_labels(old)
|
||||||
|
report = self.resolved(old)
|
||||||
|
check.validate_observation_labels(report)
|
||||||
|
check.compare_queues(state, report)
|
||||||
|
check.compare_badges(state, report)
|
||||||
|
check.compare_actor_geometry(state, report)
|
||||||
|
queue = next(b for b in report['landing_badges'] if b['visible_count'] == 0)
|
||||||
|
manifest = json.loads(self._recorded_payloads()['manifest'])
|
||||||
|
original = manifest['rows'][5]
|
||||||
|
self.assertEqual(queue['anchor'], original['local_rect'][:2])
|
||||||
|
self.assertEqual(original['screenshot_rect'][:2], [642,469])
|
||||||
|
self.assertEqual(queue['members'], next(b['members'] for b in old['landing_badges'] if b['visible_count'] == 0))
|
||||||
|
|
||||||
|
def test_recorded_forgery_controls(self):
|
||||||
|
_, state, old = self.recorded()
|
||||||
|
good = self.resolved(old)
|
||||||
|
for attack in ('at', 'displaced_by', 'count', 'hidden', 'anchor'):
|
||||||
|
report = copy.deepcopy(good)
|
||||||
|
row = report['actor_aggregations'][0]
|
||||||
|
if attack == 'at': row['at'][0] += 1
|
||||||
|
elif attack == 'displaced_by': row['displaced_by'] = []
|
||||||
|
elif attack == 'count': row['text'] = '+'
|
||||||
|
elif attack == 'hidden': report['actor_aggregations'] = []
|
||||||
|
else: row['anchor'][0] += 1
|
||||||
|
with self.subTest(attack=attack), self.assertRaises(ValueError):
|
||||||
|
check.validate_observation_labels(report)
|
||||||
|
check.compare_queues(state, report)
|
||||||
|
check.compare_actor_geometry(state, report)
|
||||||
|
|
||||||
|
def test_generic_order_and_edge_contact(self):
|
||||||
|
for kinds in [('business', 'aggregation'), ('supply', 'landing'),
|
||||||
|
('business', 'supply', 'landing', 'aggregation')]:
|
||||||
|
report = dict(cabins=[], badges=[], landing_badges=[], actor_aggregations=[])
|
||||||
|
for kind in kinds:
|
||||||
|
key = 'badges' if kind in ('business', 'supply') else 'landing_badges' if kind == 'landing' else 'actor_aggregations'
|
||||||
|
report[key].append(dict(kind=kind, at=[10,20], text='abc'))
|
||||||
|
result = self.resolved(report)
|
||||||
|
check.validate_observation_labels(result)
|
||||||
|
positions = [fields['at'] for _, _, fields in check.observation_label_layout(result)]
|
||||||
|
self.assertEqual(positions, [[10+36*i,20] for i in range(len(kinds))])
|
||||||
|
report = dict(cabins=[dict(rect=[0,50,20,20], badge_at=[10,20], badge='abc')],
|
||||||
|
badges=[], landing_badges=[dict(at=[10,20], text='x')], actor_aggregations=[])
|
||||||
|
self.assertEqual(self.resolved(report)['landing_badges'][0]['at'], [46,20])
|
||||||
|
report['landing_badges'][0]['at'] = [46,20]
|
||||||
|
self.assertEqual(self.resolved(report)['landing_badges'][0]['displaced_by'], [])
|
||||||
|
|
||||||
|
def test_future_review_requires_indexed_judgments(self):
|
||||||
|
manifest = dict(rows=[dict(row_index=7, category='queue', eligibility='visible', mechanical_pass=True)], mechanical_pass=True)
|
||||||
|
verdict = dict(manifest_sha256='digest', reviewer='independent-opus-direct-image',
|
||||||
|
state_bits='1', position_bits='1', category_bits='1')
|
||||||
|
with tempfile.TemporaryDirectory() as folder:
|
||||||
|
path = Path(folder)/'verdict.json'
|
||||||
|
path.write_text(json.dumps(verdict))
|
||||||
|
with self.assertRaisesRegex(ValueError, 'indexed judgments'):
|
||||||
|
check.reviewed_pixel_categories(manifest, 'digest', path, require_judgments=True)
|
||||||
|
verdict['judgments'] = [dict(row_index=7, category=True, state=True, position=True)]
|
||||||
|
path.write_text(json.dumps(verdict))
|
||||||
|
self.assertEqual(check.reviewed_pixel_categories(manifest, 'digest', path, require_judgments=True), {'queue'})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user