feat(ahbg): close construction (UCNS authority) + pass-5 release assets - #24
Conversation
Construction (core mechanics): - Pin libs/ucns to The-Interdependency/ucns@828c0b8 (PR #218), the new authoritative mobius_seed_construction state. - ahbg.runtime.construction binds AHBG construct to UCNS buildable-next; no parallel geometry. - Harness contract adds executable 'construct' capability; A0 and external harnesses build through the same observe/plan/act path (regression tests). - Construct intents resolve with moves each turn and persist in a replayable ledger beside the engine state. Android release-grade: - versionCode 2 / versionName 0.2.0; production HTTPS runtime endpoint; release signing config via gradle properties (keys never committed); network security config (cleartext only for emulator hosts in debug); adaptive launcher icon. Submission assets: - ahbg/submission/: store listing, privacy policy, demo storyboard, Devpost material, RevenueCat provisioning runbook, and explicit external blockers. Gates local: runtime 12 OK, grok 6 OK, presentation 11 OK, node check OK, compileall OK, UCNS construction import OK.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 359ba7e359
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ledger.dump(self.out_dir) | ||
| self._ledger = ledger |
There was a problem hiding this comment.
Make construction and turn persistence atomic
If the process exits or _keep.dump_field() fails after this write, construction.json records the build while the field/event chain remains on the previous turn; start_observation() then reloads and combines those mismatched snapshots, and the construct event cannot be recovered from the chain. Persist the construction transition in the same recoverable transaction/journal as the turn state rather than writing the two authorities independently.
Useful? React with 👍 / 👎.
| legal = list(build_legal_actions(opened)) | ||
| if "construct" in capabilities: | ||
| for unit in opened.occupants.values(): | ||
| for tile_id in ledger.legal_build_tiles(opened): | ||
| legal.append( |
There was a problem hiding this comment.
Put construct actions where the shipped client can select them
In the Android/web flow, board.js submits only lastObservation.legal[0]; this code always places relocation actions before the newly appended construction actions. With the default single unit there is always an empty neighbor, so every Play Turn submits a relocation and the production client never exercises construction, making the advertised build flow and demo impossible unless the client explicitly selects a construct action or these actions are prioritized.
Useful? React with 👍 / 👎.
| "field": field.snapshot(), | ||
| "presentation": field_to_presentation(field), | ||
| "construction": ConstructionLedger.load(field, session.out_dir).as_dict(), |
There was a problem hiding this comment.
Project construction state into the presentation
After a construct succeeds, this response still derives presentation solely from the unchanged field, which already contains all seven tiles; the new construction object is separate, and board.js renders only payload.presentation. Consequently a build has no visible board consequence and persisted reload looks identical, so the release storyboard's rings cannot appear even for clients that submit construct intents.
Useful? React with 👍 / 👎.
| built = [str(slot) for slot in raw.get("built", [])] | ||
| from ucns.mobius_seed import BandSlot | ||
|
|
||
| slots = [BandSlot(slot) for slot in built if slot in {item.value for item in BandSlot}] | ||
| return cls(from_built(slots)) |
There was a problem hiding this comment.
Reject unknown persisted construction slots
When a ledger contains an unknown slot—for example after corruption or loading state written by a newer schema—this comprehension silently drops it and reconstructs a smaller built set. The next dump then permanently normalizes away the missing construction instead of failing closed, so validate every persisted entry and raise ConstructionError on any value outside BandSlot.
Useful? React with 👍 / 👎.
| chain = Chain() | ||
| chain.append(KIND_PLANE_INIT, 0, {"field": opened.snapshot()}) | ||
| cycle = Cycle(opened, chain) | ||
| ledger = ConstructionLedger.load(opened, output_root) if (output_root / "construction.json").exists() else ConstructionLedger.open(opened) |
There was a problem hiding this comment.
Reset construction when starting a new run
When callers reuse an output directory, run_plane() creates a fresh field and event chain but loads the previous run's construction ledger. A second run—even with a different seed—therefore starts with the old built set and can report a fully constructed plane at turn zero; this breaks repeatable CLI runs and contaminates experiment results unless the field and ledger are resumed together or both are reset.
Useful? React with 👍 / 👎.
| - Purchase/restore Benchmark Lab: code path complete; live verification needs | ||
| the RevenueCat dashboard and a store sandbox purchase. |
There was a problem hiding this comment.
Keep purchase and restore in the source blocker list
The repository has no purchase or restore implementation: PremiumStore only calls getCustomerInfo(), and the Benchmark Lab button has no purchase/restore handler. Provisioning a RevenueCat product therefore cannot make a new user buy or restore it in-app, so declaring this code path complete incorrectly removes a release blocker; implement both flows and entitlement refresh before moving them to live-only verification.
Useful? React with 👍 / 👎.
| <domain-config cleartextTrafficPermitted="true"> | ||
| <domain includeSubdomains="false">10.0.2.2</domain> | ||
| <domain includeSubdomains="false">localhost</domain> | ||
| <domain includeSubdomains="false">127.0.0.1</domain> |
There was a problem hiding this comment.
Scope emulator cleartext exceptions to debug builds
The domain-config is outside debug-overrides, so its cleartext exceptions apply to release builds as well as debug builds. A release assembled with a localhost or emulator runtimeUrl can therefore send the runtime protocol over HTTP despite the documented debug-only boundary; put these domains in a debug-specific network-security resource or manifest overlay.
Useful? React with 👍 / 👎.
| build_events: list[dict[str, Any]] = [] | ||
| for build in builds: | ||
| ledger, event = ledger.apply_build( | ||
| opened, | ||
| unit_id=build.unit_id, | ||
| from_tile_id=build.from_tile_id, | ||
| to_tile_id=build.to_tile_id, | ||
| ) |
There was a problem hiding this comment.
Include construction in the canonical state digest
Construction is applied only after cycle.close_turn() has produced the recorded state_digest, and neither the ledger nor its events update that digest. Two runs at the same field position and turn therefore receive the same digest even when they built different UCNS slots, so result comparison and cache/integrity checks cannot distinguish materially different game states; incorporate the construction state into the canonical digest.
Useful? React with 👍 / 👎.
| for build in builds: | ||
| ledger, event = ledger.apply_build( | ||
| field, | ||
| unit_id=build.unit_id, | ||
| from_tile_id=build.from_tile_id, | ||
| to_tile_id=build.to_tile_id, | ||
| ) |
There was a problem hiding this comment.
Return a client error for invalid construct intents
When a client submits a construct target that is already built or outside the UCNS boundary, ledger.apply_build() raises ConstructionError, which is not a ProtocolError; the HTTP handler consequently reports an internal 500 rather than rejecting the plan with 422. This is reachable with a stale observation or concurrent plan submission and misclassifies an expected contract violation as a server failure, so validate construction before resolution and translate this exception at the protocol boundary.
Useful? React with 👍 / 👎.
| - **Benchmark Lab** (premium): advanced scenarios, saved/replayed run | ||
| comparison, and adversarial benchmark packs. Basic play and external | ||
| harness connectivity stay free. |
There was a problem hiding this comment.
Do not advertise unimplemented Benchmark Lab features
The shipped premium surface only changes a status label based on isBenchmarkLabUnlocked(); there are no UI or runtime paths for advanced scenarios, saved-run comparison, or adversarial benchmark packs. An already-entitled customer therefore receives none of the features promised in this store listing, independently of the missing purchase flow, so implement the premium surfaces or remove these claims before publication.
Useful? React with 👍 / 👎.
Pass 5 — close construction, then shipping
1. Construction closed (core-mechanics blocker)
The-Interdependency/ucnsPR #218(
ucns.mobius_seed_construction@0.1.0, merge828c0b8) — smallest correctconstruction state: built slots + buildable-next from the seed's own
structural-vesica relations. No AHBG semantics in UCNS.
ahbg/runtime/construction.pymaps UCNS buildable-nextonto UCNS-derived tiles;
libs/ucnspin bumped instack-manifest.json.construct; A0 and externalharnesses build through the same observe/plan/act path.
out-of-boundary builds fail closed.
2. Android release-grade
signing config via gradle properties (keys never committed); network
security config restricts cleartext to debug emulator hosts; adaptive icon.
3–4. RevenueCat + publish/submission assets
ahbg/submission/: store listing, privacy policy, ≤2-min demo storyboard,Devpost material, RevenueCat provisioning runbook, and explicit external
blockers (Play Console, keystore, live RevenueCat project, demo recording).
Gates
UCNS construction import verified locally; CI will run runtime/android/grok.
hmmm
RevenueCat provisioning require the production accounts (documented in
ahbg/submission/SUBMISSION_BLOCKERS.md).