Closes the mechanism lens's REOPEN (one MUST-FIX) and both lenses' small
residuals on the OP2 rework (b236a442); the blast lens re-review was
CLOSED outright. Fable-direct per the two-failure escalation rule.
- AP-195 filed: UIOption_CheckboxBitfield64 ports HALF of Refresh
@0x004859C0 — the ANY-set checkbox predicate is exact, but the ALL-set
LED media swap (P0x10000082=0x06004D17 / P0x10000083=0x06004D19) and
the ListBox self-sizing tail (ResizeTo/CalculatePaperSize — the block
IS a UIElement_ListBox in retail) are unported, and the block's row
stacking is a second divergent implementation beside UiTemplateListBox.
All due at OP5 before the Chat tab's connected gate; the IsSet doc
comment now names both halves instead of quoting only the ported one.
- Row tooltips: UiButton gains settable TooltipText surfaced through the
shared GetTooltipText hover pipeline (UiCatalogSlot's pattern);
UiCheckboxBitfield64.AddChild applies the row tooltip retail stamps in
CreateChildren @0x00485DF0, and documents that the 0x10000084 row-index
attribute stamp is deliberately replaced by the typed mask closure.
- AD-73 addendum: UiTemplateListBox.ConsumesDatChildren=true is inert
only while no authored Type-5 element carries children — that premise
is now conformance-PINNED across all 32 fixtures (a future DAT
regeneration surfacing an authored child fails the build instead of
silently dropping it).
- Plan doc: OP2's contract names UiTabPanel.cs (retail UIElement_Panel),
not the fictional-class-named UiTabControl.cs; ledger records OP1 and
OP2 both CLOSED.
Full Release suite: 12,871 passed / 4 skipped / 0 failed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
24 KiB
Campaign OP — retail four-tab Options panel
For agentic workers: slices are executed by ONE Sonnet implementer at a time against this contract, then dual-lens Opus-reviewed, per the binding process rules in §8. The four research docs in §1 are the spec's data appendix — implementers MUST read the cited sections before coding; every table this plan references by section number is committed there in full.
Status: ACTIVE — planned 2026-08-10. No slices landed yet.
Goal: retail's four-tab in-game Options panel (Gameplay Options /
Character / Chat / Config), retail-faithful mechanics end-to-end: authored
LayoutDescs, the real option storage/wire split (0x0005 single-option vs
the batched 0x01A1 PlayerModule blob), retail Apply/Reset/Defaults
semantics, live consumers where acdream has the subsystem and honest
store-only + register rows where it doesn't, plus the headless-bot
characterOptions seam. Campaign handoff:
docs/research/2026-08-10-settings-track-handoff.md.
Architecture: the panel is retained retail UI exactly like Campaign CH's
chat windows — LayoutDesc 0x2100002B imported through LayoutImporter,
mounted in the gmFloatyPanelUI host 0x2100006E under
RetailWindowManager, driven by a focused controller. All option
storage/policy is Runtime-owned (RuntimeCharacterOptionsState widened to
the full retail table); the graphical panel and the headless host are both
consumers of the one generation-gated IRuntimeCharacterCommands seam
(CH3's precedent). Wire builders live in AcDream.Core.Net beside the
existing 0x0005 codec.
Tech stack: existing retained UI stack (DatWidgetFactory,
LayoutImporter, UiRoot, RetailWindowManager), AcDream.Runtime
gameplay owners, AcDream.Core.Net message builders, DatCollection for
DAT reads. No new dependencies.
1. Research base (committed; the spec's data appendix)
| Doc | What it pins |
|---|---|
docs/research/2026-08-10-options-panel-structure.md (lane A) |
Layout 0x2100002B structural inventory (§10.1), tab table property 0x2E, row-template mechanism (ListBox P0x64 + AddItemFromTemplateList), Apply/Reset/Defaults + visibility semantics (§6, §10.4), Chat tab's 13 checkbox masks + defaults (§8), Config tab's 27 rows + UserPreferences.ini keys (§9), open path (F11 action 0x1000001A, toolbar button 0x1000019B) |
docs/research/2026-08-10-character-options-map.md (lane B) |
The 50-row / 6-group Character-tab inventory with per-row storage bit, wire route, ACE handling, acdream consumer state (§2–§5); implement-vs-store split (§7.1); bot tiers (§5.2) |
docs/research/2026-08-10-set-character-options-wire.md (lane C) |
The 0x01A1 body = PlayerModule::Pack field order (§2.3–§2.7), header invariant 0x460, the 21-id auto-save table (§3.2), 480 s timer + logout + Apply flush triggers (§3.3–§3.5), ACE acceptance/landmines (§5), CH3 builder post-mortem (§6) |
docs/research/2026-08-10-keyboard-config-and-gameplay-tab.md (lane D) |
The seven Gameplay-tab button behaviours with byte-verified strings (§1–§4), gmKeyboardUI structure + DAT ActionMap storage (§5–§6), per-button implementability (§7.1), Config-tab ordered dump (§7.3) |
Coordinator-verified during planning (this session): toolbar button
0x1000019B authors P0x12 = 0x1000001A (committed fixture);
retail-default.keymap.txt:148 binds ToggleOptionsPanel to DIK_F11;
PlayerModulePackHeader verbatim at acclient.h:7835; the headless
local-write gap at src/AcDream.Runtime/Session/DirectGameRuntimeCommandAdapter.cs
(SetSingleOption) vs src/AcDream.App/Net/LiveSessionRuntimeFactory.cs:347;
U1 closed: UIOption::InqDefaultGameplayOptionProperty @0x004ef8d0
resolves per-option defaults from the DAT DBPropertyCollection at
DBCache::GetDIDFromEnumStatic(0x16, 2) — the Defaults button restores
DAT-authored values.
2. Design decisions (stated, per the campaign directive; reactable at gates)
- D1 — the retail Options panel is acdream's one in-client settings
surface. Lane D established the F11
SettingsPanelwas never rendered post-V11 (ToggleSettingsPanel()no-op; onlyIPanelRendereris a test fake). Retail's own F11 ISToggleOptionsPanel. So: F11 + the toolbar button open THIS panel; acdream's client-only settings live on the Config tab (retail's own client-settings tab — its 27 rows areUserPreferences.inipreferences, nothing on the wire), backed by acdream's existing settings store. The oldSettingsPanel/SettingsVMIPanel surface is retired in OP9; its tested keybind model feeds OP8. - D2 — retail's wire split ships exactly. The 21 auto-save ids send
0x0005immediately; the rest dirty the module and ride the real0x01A1blob with retail's three flush triggers (Apply, logout, 480 s timer). No "send everything as 0x0005" shortcut (lane C: the split is load-bearing in both directions). - D3 — the 50th Character row ships. "Listen to PK death messages" is
2015-client; the DAT string exists (
0x0D16E9A3), ACE maps id0x34↔CharacterOptions2 0x02000000but never reads it. Ship the row (user-gate axiom: the user's retail memory includes it), wire+store only, register row for the ACE-sourced 2013-unverifiable mapping. - D4 — Configure Keyboard is the campaign's rebind screen (it is the
ONLY rebind screen — D1). Port
gmKeyboardUI's shape and DAT ActionMap data (lane D Option C) but persist tokeybinds.json; retail.keymapfile interchange is a register-row deferral. - D5 — dead-endpoint buttons short-circuit to their own retail failure
strings. Urgent Assistance / Report Abuse open a defunct
support.turbine.comURL in retail; acdream skips the browser launch and emits the button's own byte-verified failure notice through the interface-text seam (retail text, not invented), register rows filed. In-Game Help mirrors retail-with-missing-ACHelpPlugin.dllbehaviour. - D6 — Exit to Character Selection behaves as Exit Game (+ register
row) until a pre-world character-select flow exists, but retail's
confirmation dialog (
ID_Client_EndCharacterSessionConfirm) and mid-air refusal ("Cannot log off while in mid-air.", byte-verified) ship now. - D7 — Group C re-points to server truth. The seven currently-live
settings.json-backed options (lane B §7.1 group C) become server-bit-authoritative per CH3 precedent (reseed from PlayerDescription; local-write-then-send).GameplaySettings' 16 server-shadowing booleans die in OP9. - D8 — bots declare options by NAME. The K1 strict config gains an
optional
characterOptionsblock accepting exactly the lane-B tier-1+2 names; unknown names fail load (ACE throws on unknown ids — never let one reach the wire). Diff-then-send after PlayerDescription seeds state; idempotent on reconnect.
3. Slice map
Dependencies: OP1 → (OP4, OP7); OP2 → (OP3, OP4, OP5, OP6, OP8); OP3 → OP4/5/6 (the shell hosts the tabs). OP7 needs only OP1. Execution order below is the default; OP7 may run any time after OP1 when the tree is free.
| Slice | Contract (summary) | Gate |
|---|---|---|
| OP1 | Runtime option map + dirty model + the real 0x01A1 builder + headless local-write fix |
automated only |
| OP2 | Type 8 tab control + Type 5 template-list ListBox + remaining UIOption_* widget mappings + fixtures |
automated only |
| OP3 | Panel shell + open paths + Gameplay tab end-to-end | user (connected) |
| OP4 | Character tab: 50 rows, consumers, Apply/Reset/Defaults | user (connected) |
| OP5 | Chat tab: opacity sliders + 5 per-window filter blocks | user (connected) |
| OP6 | Config tab: 27 rows over the client settings store | user (connected) |
| OP7 | Headless characterOptions block |
automated + bot-vs-ACE run |
| OP8 | Configure Keyboard screen | user (connected) |
| OP9 | Closeout: retire dead surfaces, bookkeeping, test script | user (final matrix) |
4. Slice contracts
OP1 — the Runtime option map, dirty model, and the real blob
Files.
- Modify
src/AcDream.Runtime/Gameplay/RuntimeCharacterState.cs(RuntimeCharacterOptionsState,:628): widen to the full table. - Create
src/AcDream.Runtime/Gameplay/CharacterOptionTable.cs: the ONE typed tablePlayerOption id → (word: Options1|Options2, mask, isAutoSave, clientDefault)for ids0x00..0x34, transcribed from lane B §2 (verbatimacclient.h:4162enum names) + lane C §3.2 (auto-save column) + D3's0x34row. Reject0x35/0x36and unknown ids at the seam (lane C §5.4.3 — ACE throws). - Modify
src/AcDream.Runtime/GameRuntimeCommands.cs: addIRuntimeCharacterCommands.SaveOptions(RuntimeGenerationToken)(the blob-flush verb) besideSetSingleOption. - Modify
src/AcDream.Runtime/Session/DirectGameRuntimeCommandAdapter.csandsrc/AcDream.App/Runtime/CurrentGameRuntimeCommandAdapter.cs+src/AcDream.App/Net/LiveSessionRuntimeFactory.cs:335-350: move local-write-then-send INTO the shared Runtime seam so BOTH hosts get retail's ordering (fixes the headless gap lanes B+C independently found). TheLiveSessionRuntimeFactorylocal closure is deleted, not duplicated. - Create
src/AcDream.Core.Net/Messages/SocialActions.csaddition:BuildSetCharacterOptions(...)per lane C §2.7 EXACTLY — header always0x460OR-ed with present optional sections; four unconditional u32s (header, options1, spellbookFilters, options2 in pack order §2.3); echo last-parsed shortcuts / 8 spell lists / desired comps rather than zeroing (§5.3); NEVER set0x100(lane C U2); omit0x200while acdream packs nothing (safe per §2.5); 4-byte tail pad. - Modify
src/AcDream.Core.Net/WorldSession.cs:SendSetCharacterOptions. - Dirty model in
RuntimeCharacterOptionsState:MarkDirtyon any non-auto-save change,FirstDirtiedAt, flush triggers = explicitSaveOptionscommand, session logout, and the 480 s timer (retail constant, lane C §3.3) driven from the existing Runtime tick. - Tests:
tests/AcDream.Runtime.Tests/(table completeness ×53, auto-save split ×53 vs lane B §2's byte-verified column, local-write-then-send on BOTH adapter paths, dirty/flush state machine, unknown-id rejection);tests/AcDream.Core.Net.Tests/blob conformance: golden byte vector + round-trip throughPlayerDescriptionParser(lane C §9 S-c — the CH3 builder died of green tests pinning a wrong shape; the golden vector is non-negotiable).
Register rows (same commit): 0x34 mapping ACE-sourced (D3); the
480 s autosave if any part is deferred (target: not deferred);
GetDefaultOptionValue @0x005D2A30 vs ctor-default disagreement recorded
when the client-default column lands (lane C §8.2 — reproduce, don't fix).
Acceptance: build + FULL Release suite green; blob golden vector byte-exact; both adapters share one code path for the local write.
OP2 — the two widget primitives + remaining UIOption mappings
Files.
- Modify
src/AcDream.App/UI/Layout/LayoutImporter.cs+src/AcDream.App/UI/Layout/ElementReader.cs: read tab-table property0x2E(struct array{0x30 button, 0x31 page, 0x32 isDefault}) and ListBox template-list property0x64(entries{0x63 layout DID, 0x62 element id}) intoElementInfo. - Create
src/AcDream.App/UI/UiTabPanel.cs(element Type 8 — retailUIElement_Panel; renamed from this plan's originalUiTabControlat the OP2 rework, and a dormantUiDatElementsubclass per AD-73): tab-button ↔ page-slot switching per lane A §5; default tab honoured. - Create
src/AcDream.App/UI/UiTemplateListBox.cs(element Type 5 with authored template list):AddItemFromTemplateList(index)instantiates a row from the authored template layout/element viaDatWidgetFactory, scrollbar named byP0x72. - Modify
src/AcDream.App/UI/Layout/DatWidgetFactory.cs: map Types 5/8 andUIOption_Slider 0x10000037,UIOption_Menu 0x10000038,UIOption_CheckboxSlider 0x10000036,UIOption_CheckboxBitfield64 0x10000044(LED checkbox0x10000035already maps toBuildCheckbox). - Modify
tests/AcDream.App.Tests/UI/Layout/RetailLayoutFixtureGenerator.cs(Layouts,:17): add0x2100002B,0x2100002A,0x21000028,0x2100005C,0x21000029. Coordinator runs the generator (ACDREAM_REGENERATE_UI_FIXTURES=1, serial tree) and commits fixtures. - Conformance tests pin: the tab table (4 entries, Gameplay default), all three template arrays (lane A §10.1), the Character ListBox's 6-header / 49-toggle authored row build, scrollbar linkage.
Acceptance: build + FULL suite; fixtures committed and pinned; no
change to any existing widget's behaviour (the S2/AP-192 outline seams from
aa6635ae must be preserved in new widget builds).
OP3 — panel shell + open paths + Gameplay tab (first vertical)
Files.
- Create
src/AcDream.App/UI/Layout/OptionsPanelController.cs: mounts0x2100002Bin host0x2100006Eslot0x1000018D(stack key 10) viaRetailWindowManager; tab control wiring; close button fires0x1000001A. - Create
src/AcDream.App/UI/Layout/OptionPageModel.cs: theOptionPage/PlayerOptionPagemodel — per-page option array of(current, saved, default)triples + verbs Apply/Reset/Defaults with retail's exact semantics (lane A §10.4): LED click applies immediately (SetCurrentValue → Apply(1)); Apply commits baseline +SaveOptionsflush; Reset reverts to baseline; Defaults applies live without committing and is never disabled; Apply/Reset disable when clean; hide → revert uncommitted; show → apply + commit. Pure logic, unit-tested without DAT. - Input action
ToggleOptionsPanel(0x1000001A, F11) insrc/AcDream.UI.Abstractions/Input/(KeyBindings.RetailDefaults()— the ONLY production table; #358's lesson) +src/AcDream.App/Input/GameplayInputCommandController.csrouting + toolbar button0x1000019B(already authorsP0x12). - Gameplay tab (
0x2100002A, classgmGameplayOptionsUI), seven buttons per D5/D6 and lane D §1: Exit Game → the existing graceful-close path; Exit to Char Selection → retail confirm dialog (RetailDialogFactory) + mid-air refusal via the interface-text seam, then D6's Exit-Game behaviour; Configure Keyboard → opens OP8's screen; until OP8 lands the button is INERT (authored, clickable, no handler — no invented text, no stub screen), the OP3 gate script says so explicitly, and OP8's gate re-tests it (the campaign cannot close with the button inert); Use Mouse Turning Settings → the six-option macro (lane D §4.4) with its six retail chat lines, camera-mode consumer verified against the camera digest in-slice (register row if the mode is absent); In-Game Help / Urgent Assistance / Report Abuse per D5.
Register rows: Exit-to-char-select adaptation (D6); UA/RA dead-URL short-circuit (D5); help-plugin behaviour (D5); mouse-turning consumer row if needed.
Gate: connected — panel opens via F11 AND toolbar; tabs switch with Gameplay default; the seven buttons behave per contract; window drags / resizes / stacks like the CH6 floaties.
OP4 — the Character tab
Files.
- Create
src/AcDream.App/UI/Layout/CharacterOptionsPageController.cs: binds layout0x21000028root0x100001F9ListBox0x100001FAthrough OP2's template mechanism; 6 headers + 50 toggles (D3) in lane B §2's authored order; every row bound byPlayerOptionid through OP1's table and the shared seam (auto-save ids →0x0005on click-apply; batched ids → dirty + blob per OP1). - Defaults: extract the DAT
DBPropertyCollection(GetDIDFromEnumStatic(0x16, 2), §1 U1-closure) viaDatCollectionat import; conformance-pin extracted values; cross-check overlapping ids against lane B/C's byte-verified default words and RECORD any disagreement as a finding (never silently pick). - Consumers (lane B §7.1): group B one-line binds — timestamps + filter
language at
RuntimeCommunicationState.AddText; daylight (ForcedDayGroupIndex), weather, fog; run-as-default inRuntimeLocalPlayerMovementState; main-pack default at the pickup path; the UI-display bits at their existing retained controllers. Group C re-pointing per D7. Group A wire+store only. Group D register rows. - Conformance test: all 50 rows ↔ table ↔ storage bit ↔ wire route pinned in both directions (the CH4 registry-conformance pattern — an invented row or a dropped row fails the build).
Register rows: group D deferrals (salvage, housing, fellowship-share field, PK-deaths already rowed in OP1, mouse-turning row lives in OP3); each group-C re-point that changes an observable default.
Gate: connected — LED rows toggle + persist across relogin (server echo), timestamps/daylight/fog/weather/run-default observably switch, Apply / Reset / Defaults exercise retail semantics, tab-switch reverts uncommitted edits.
OP5 — the Chat tab
Files. Create
src/AcDream.App/UI/Layout/ChatOptionsPageController.cs binding
0x2100005C root 0x1000050A: the two LINKED opacity sliders (bound to
the existing RetailWindowOpacityController values through
ChatOpacityLink — AP-190's model, now user-reachable) and the five
per-window filter blocks (SetUserData ids 8/2/3/4/5; main window 12 rows,
floaties 13 — lane A §8's byte-decoded masks) writing the per-window filter
state CH6 already consumes (ChatWindowState.ShouldDisplay). The
0x1000008C per-window blob stays local-only (already-anticipated register
row from 2026-08-09-chat-retail-window-shell.md §6.3 — cite, don't
duplicate).
Gate: connected — filter checkboxes change window routing live; opacity sliders drive the focus fade; settings survive relogin locally.
OP6 — the Config tab
Files. Create
src/AcDream.App/UI/Layout/ConfigOptionsPageController.cs binding
0x21000029 root 0x100001FF: all 27 rows in lane A §9 / lane D §7.3's
authored order, backed by acdream's client settings store
(%LOCALAPPDATA%\acdream\ — the D1 home). Rows with live subsystems bind
now: the three volume trios → the audio pipeline, "play sound only when
active", mouse-look sensitivity + invert Y, FOV, chat font size/face if the
chat pipeline exposes them. Rows without a subsystem (resolution +
fullscreen + sync, degrades, texture detail family, multi-pass alpha)
persist store-only under ONE register row enumerating them (the goal's
"honest store-only handling"); resolution's SetConfirmChange flow ships
whenever the consumer lands, not now.
Gate: connected — audio sliders audibly change mix; mouse sensitivity observably changes; store-only rows persist across relaunch.
OP7 — headless characterOptions
Files. Modify
src/AcDream.Headless/Configuration/HeadlessConfiguration.cs +
HeadlessConfigurationLoader.cs: optional characterOptions block, strict
— keys are exactly the lane B §5.2 tier-1+2 option NAMES, values bool;
unknown key = load failure (D8). Modify the session host
(src/AcDream.Headless/Hosting/) to diff declared vs
PlayerDescription-seeded state after LoginComplete and send changes through
the shared seam (auto-save ids as 0x0005, remainder via one
SaveOptions blob), honouring #368's one-dedicated-update-thread contract.
Idempotent on reconnect (retail itself no-ops unchanged options — lane C
§3.5). Headless tests: schema rejection, diff-only sends, reconnect
idempotence, thread affinity preserved.
Gate: automated + one live bot-vs-local-ACE run (no graphical client) showing declared options land and survive reconnect.
OP8 — Configure Keyboard
Files. Create
src/AcDream.App/UI/Layout/KeyboardConfigController.cs (+ a
src/AcDream.Core/... DAT ActionMap (DBO type 0x27) reader if
DatCollection lacks one): gmKeyboardUI's six ActionClass list boxes
via OP2's Type 5 widget, rows = label + tooltip + N key buttons + Clear
from the DAT master maps (DIDs 0x14000000/0x14000002 — the exact
enum→DID pairing is lane D unknown #4, resolved in-slice by dumping both),
merged with live KeyBindings; left-click key button → InputDispatcher
modal capture; right-click erases; N-way cross-map conflicts + the
non-user-bindable refusal per lane D §5; Save/Cancel; Reset-to-defaults
reloads the DAT maps. Persistence: keybinds.json (D4).
Register rows: .keymap file interchange not implemented (D4); any
retail column/behaviour consciously narrowed.
Gate: connected — rebind a movement key, conflict prompt on a taken chord, persistence across relaunch, reset restores retail defaults.
OP9 — closeout
- Retire
SettingsPanel/SettingsVM's IPanel surface +SettingsDevToolsCompositionwiring +DevToolsGameplayCommandsno-ops; deleteGameplaySettings' 16 server-shadowed booleans (lane B §7.2.5), re-scoping true client-only settings into the D1 store. Every deletion checked against consumers; no behaviour regression. - Bookkeeping: plan status flips, ISSUES sweep (#358 retest against OP8's
screen — its Ctrl+M lesson lives in
RetailDefaults()), register reconciliation, CLAUDE.md Current-state paragraph (perfeedback_claude_md_staleness), memory digest update (project_chat_digestaddendum or a new settings digest). - Write
docs/research/2026-08-10-campaign-op-test-script.md: the connected-gate script covering every OP3–OP8 gate item, per-tab, with expected retail behaviours — the campaign's stop condition is this script ready plus all slices code-complete.
Gate: the user's final connected matrix (their eyes, their pace).
5. What is explicitly OUT of scope
- Packing the
0x200GameplayOptions blob section (per-window chat state on the wire) — a follow-on (CH6f shape), pre-anchored by lane C U1 and2026-08-09-chat-retail-window-shell.md§6.3's register row. - A pre-world character-select flow (D6 adapts; its register row carries the future work).
- Retail
.keymapfile read/write (D4 register row). - The
0x21000017dockedgmPanelUIhost variant — acdream ships the floating host only (register row in OP3 if the review deems it a divergence; retail exposes both).
6. Verification discipline
Per commit: dotnet build + FULL Release suite green (baseline at plan
time: 12,611 / 4 skips / 0 failures at 29138430+). Golden byte vectors
for every wire builder. Conformance pins for every authored inventory
(row lists, template arrays, tab table, checkbox masks). Register rows in
the SAME commit as the deviation. No user-visible placeholder text ever —
all user-facing strings resolve from the DAT string tables
(0x23000001/0x23000003) by compute_str_hash name, never hard-coded
English.
7. Review protocol
Dual-lens Opus review per slice (mechanism-faithfulness lens × regression/blast-radius lens), fixes applied by the implementer; REJECT → focused re-review; TWO failures → Fable fixes directly (user-directed 2026-08-10). Review findings that feed both a fixer and a re-reviewer are persisted to a committed findings doc BEFORE dispatch.
8. Process rules (binding, inherited from Campaign CH via the handoff)
Max 3–4 agents in parallel INCLUDING children; every agent prompt carries
an explicit no-subagent clause; ONE builder/tester on the tree at a time
(read-only research may overlap); agents never launch the graphical
client; the user runs all connected gates; screenshots transcribed into
docs immediately; ledger placeholders anchored per-row, post-amend SHAs
recorded by the coordinator; decomp claims byte-verified against the
PDB-paired binary (check_exe_pdb.py MATCH first); stalled agents resumed
via SendMessage before any redo; agent claims spot-verified at the seams
before anything builds on them.
9. Ledger
| Slice | Status | Commit(s) | Review | Gate |
|---|---|---|---|---|
| OP1 | CLOSED | 86c0a7e0 + fixes 09029f9f + residuals 6f48e341 |
dual APPROVE-WITH-FIXES (2026-08-10-op1-review-{mechanism,blast}.md) → re-review CLOSED (2026-08-11-op1-rereview.md); residuals R1/R2/R3 landed |
automated only — n/a |
| OP2 | CLOSED | df9c7a35 (REJECTED) → rework b236a442 → closure (this commit) |
dual REJECT (2026-08-11-op2-review-{mechanism,blast}.md) → re-review blast CLOSED / mechanism REOPEN-on-one (2026-08-11-op2-rereview-{mechanism,blast}.md) → coordinator closure: AP-195 filed, AD-73 addendum, tooltip port, zero-children pin |
automated only — n/a |
| OP3 | — | |||
| OP4 | — | |||
| OP5 | — | |||
| OP6 | — | |||
| OP7 | — | |||
| OP8 | — | |||
| OP9 | — |