Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions docs/design/vss2-02-capsule-solver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# VSS2-02: Capsule solve plans, SCC joint solving, and interface summaries

**Issue:** SLM-66
**Status:** wiring / fixture evidence. No train, eval, benchmark, model, checkpoint, or ship claim.

## What was added

A deterministic capsule solver coordinator in `src/slm_training/dsl/solver/capsule_solver.py`:

- `CapsuleInterfaceSummary`, `CapsuleProblem`, `CapsuleSolvePlan`, `PerCapsuleResult`, `CapsuleSolveResult`, `Disagreement`, `CapsuleCounters`, `BindingSummary`, `SlotSummary`, `ExternalInput` — immutable, JSON-safe dataclasses with `to_dict`/`from_dict`.
- `build_capsule_solve_plan(graph)` — topological stage planning that:
- Computes dependency stages from the capsule graph.
- Groups independent capsules that share no edges into the same stage.
- Marks strongly-connected components (SCCs) as joint problems so they are solved together.
- `solve_capsule_graph(...)` — dependency-order solve loop that:
- Solves each stage in order.
- Blocks a capsule when a predecessor summary is unknown (conservative predecessor check).
- Materializes predecessor outputs into successor inputs.
- Records local-pass/global-fail verifier disagreements.
- Updates counters for support queries, solver nodes, verifier calls, and joint solves.

Pack integration in `src/slm_training/dsl/pack.py`:

- Added optional slots to `DslPack`:
- `capsule_problem_builder`
- `capsule_summary_extractor`
- `capsule_materializer`
- `capsule_local_oracle`
- `capsule_global_oracle`
- Fixed a latent registration ordering bug: `_ensure_builtin_packs()` now uses a `_BUILTINS_LOADED` flag instead of `_PACKS` truthiness, so registering a custom pack before the builtins are loaded no longer hides `openui`/`toy-layout`/`graphql`.

Serialization helpers:

- `src/slm_training/dsl/solver/controller.py` — added `to_dict`/`from_dict` to `TerminalOutcome`, `SearchDecision`, `Nogood`, and `SearchResult`.
- `src/slm_training/dsl/solver/closure.py` — added `CertifiedDeduction.from_dict`.
- `src/slm_training/dsl/solver/__init__.py` — exports the capsule solver public API.

Support oracle tweak:

- `src/slm_training/dsl/solver/support.py` — the enumerative oracle now prefers an unresolved hole when expanding a multi-hole child, while still falling back to the first hole for singleton-hole expanders.

## Verified

- `ruff check` passes.
- `python -m compileall` passes.
- `pytest tests/test_dsl/test_solver_controller.py tests/test_dsl/test_solver_closure.py tests/test_dsl/test_solver_support.py tests/test_dsl/test_solver_state.py tests/test_dsl/test_capsule_solver.py tests/test_data/test_progspec.py -q` → 110 passed.
- `.githooks/check-changed` → all checks passed (`tests/test_dsl`, `tests/test_harnesses/model_build`: 418 passed, 5 skipped, 12 deselected).
- `python -m scripts.repo_policy` ok.
- `git diff --check` clean.

## Design boundaries preserved

- The solver is coordinator wiring: it delegates per-capsule search to the existing VSS1 controller/state/closure/support stack.
- No new model, checkpoint, or ship gate is introduced.
- Conservative blocking and disagreement recording keep the contract honest: a missing predecessor summary does not silently become a pass.

## Caveats

- This is solver wiring only. Real capsule-level verification needs model-backed oracles, pack hooks for the new `DslPack` slots, and end-to-end fixture runs.
- No model, checkpoint, or ship gate is claimed.
10 changes: 9 additions & 1 deletion src/slm_training/dsl/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ class DslPack:
scope_extractor: Callable[..., list[Any]] | None = None
prop_order: Callable[[], Mapping[str, Sequence[str]]] | None = None
incremental_engine: Callable[[], Any] | None = None
capsule_problem_builder: Callable[..., Any] | None = None
capsule_summary_extractor: Callable[..., Any] | None = None
capsule_materializer: Callable[..., Any] | None = None
capsule_local_oracle: Callable[..., Any] | None = None
capsule_global_oracle: Callable[..., Any] | None = None

def filled_slots(self) -> tuple[str, ...]:
return tuple(
Expand All @@ -121,6 +126,7 @@ def require(self, slot: str) -> Any:


_PACKS: dict[str, DslPack] = {}
_BUILTINS_LOADED: bool = False
# Backend ids that resolve to the openui pack (same language, different parser).
_ALIASES = {
"openui-lark": "openui",
Expand Down Expand Up @@ -243,7 +249,8 @@ def _toy_layout_engine() -> Any:


def _ensure_builtin_packs() -> None:
if _PACKS:
global _BUILTINS_LOADED
if _BUILTINS_LOADED:
return
shared_policy = PlaceholderPolicy(
placeholder_re=PLACEHOLDER_RE,
Expand Down Expand Up @@ -292,6 +299,7 @@ def _ensure_builtin_packs() -> None:
register_pack(build_graphql_pack())
except Exception: # noqa: BLE001 - graphql pack is optional
pass
_BUILTINS_LOADED = True


__all__ = [
Expand Down
50 changes: 42 additions & 8 deletions src/slm_training/dsl/solver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,44 @@
Verifier,
replay_support_certificate,
)
from slm_training.dsl.solver.capsule_solver import (
BindingSummary,
CapsuleCounters,
CapsuleGlobalVerifier,
CapsuleInterfaceSummary,
CapsuleMaterializer,
CapsuleProblem,
CapsuleProblemBuilder,
CapsuleSolvePlan,
CapsuleSolveResult,
CapsuleSummaryExtractor,
Disagreement,
ExternalInput,
PerCapsuleResult,
SlotSummary,
build_capsule_solve_plan,
solve_capsule_graph,
)

__all__ = [
"BaselineRanker",
"BindingSummary",
"CandidateRanker",
"CapsuleCounters",
"CapsuleGlobalVerifier",
"CapsuleInterfaceSummary",
"CapsuleMaterializer",
"CapsuleProblem",
"CapsuleProblemBuilder",
"CapsuleSolvePlan",
"CapsuleSolveResult",
"CapsuleSummaryExtractor",
"CertifiedDeduction",
"ClosureCounters",
"ClosureResult",
"Disagreement",
"DomainValue",
"Nogood",
"SearchDecision",
"SearchResult",
"SearchStatus",
"TerminalChecker",
"TerminalOutcome",
"default_hole_selector",
"search",
"ExternalInput",
"EnumerativeSupportOracle",
"EnumerativeSupportProvider",
"ExpandStatus",
Expand All @@ -75,23 +97,35 @@
"HoleDomain",
"HoleId",
"JsonScalar",
"Nogood",
"PerCapsuleResult",
"ProblemExpander",
"ReplayResult",
"SearchCounters",
"SearchDecision",
"SearchResult",
"SearchStatus",
"SlotSummary",
"SolverBounds",
"SupportCertificate",
"SupportOracle",
"SupportProvider",
"SupportQuery",
"SupportResult",
"SupportVerdict",
"TerminalChecker",
"TerminalOutcome",
"TopologyDomainAdapter",
"Verifier",
"VerifyOutcome",
"VerifyStatus",
"WitnessRef",
"build_capsule_solve_plan",
"completion_forest_state",
"default_hole_selector",
"default_query_order",
"exact_closure",
"replay_support_certificate",
"search",
"solve_capsule_graph",
]
Loading
Loading