Source: docs/superpowers/plans/2026-09-20-learned-prefix-policy.md · Plain text

Learned Prefix Action Policy Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Add a deterministic, prefix-only learned action-value policy and its separately bound checkpoint/training path while preserving existing dynamic.v1 carrier checkpoints, fixed policies, and runner APIs.

Architecture: Runtime control data remains in mcss.dynamic.types; control_to_features is the sole ordered feature-schema encoder. LearnedActionPolicy owns a small MLP and masks infeasible actions at choose(control, feasible_actions). Policy checkpoints are independent files carrying carrier/write content hashes, feature schema, render/budget/utility bindings, teacher hashes, and training-only normalization. Offline fitting consumes the shared all-actions-per-prefix JSONL rows and validates by scene split.

Tech Stack: Python 3.11, PyTorch, dataclasses, JSONL, pytest.

Global Constraints

Task 1: Runtime prefix control and deterministic feature schema

Files: - Modify: src/mcss/dynamic/types.py - Modify: src/mcss/dynamic/feedback.py - Modify: src/mcss/dynamic/policy.py - Test: tests/dynamic/test_policy.py

Interfaces: - ControlInput keeps existing required fields and adds optional observed-prefix fields with deterministic defaults. - control_to_features(control: ControlInput) -> torch.Tensor returns the fixed one-dimensional schema tensor. - LearnedActionPolicy.choose(control, feasible_actions) -> Action masks infeasible actions, validates finite values, and resolves ties in OFF,FUSE,COMPLETE,ALL order.

Task 2: Causal runner feedback and policy-work accounting

Files: - Modify: src/mcss/dynamic/feedback.py - Modify: src/mcss/dynamic/runner.py - Modify: src/mcss/dynamic/budget.py - Test: tests/dynamic/test_runner.py - Test: tests/dynamic/test_budget.py

Interfaces: - preupdate_feedback continues returning old scalar keys and can add serializable current-prefix summaries. - Runner calls policy.choose(control, feasible_actions) and records the exact serializable control_input. - WorkBudget.policy_units(policy) charges fixed policies one unit and learned MLP inference from declared dimensions.

Task 3: Independent policy checkpoint binding

Files: - Create: src/mcss/dynamic/policy_checkpoint.py - Test: tests/dynamic/test_policy_checkpoint.py

Interfaces: - save_policy_checkpoint(path, policy, *, binding, provenance) -> str saves CPU state, architecture, normalization, schema, and binding. - load_policy_checkpoint(path, *, expected_binding=None, device='cpu') -> tuple[LearnedActionPolicy, dict] rejects mismatches before loading.

Task 4: Teacher-row fitter and training script

Files: - Create: src/mcss/training/policy_fit.py - Create: scripts/train_action_policy.py - Test: tests/dynamic/test_policy_fit.py

Interfaces: - fit_action_policy(rows, *, hidden_dim, seed, validation_scene_ids, ...) trains action-value regression from the shared all-action row schema, retaining near ties and fitting normalization on training rows only. - JSONL parser validates exact row fields and fixed action order; heldout validation is scene-based. - CLI loads JSONL and writes the independent policy checkpoint plus provenance/binding metadata.

Verification