Skip to content

slm-training issues: verified-scope-solver (SLM-57/59) + LDI0-01 contract (SLM-114)#322

Merged
Tyler-R-Kendrick merged 28 commits into
mainfrom
claude/slm-training-issues-d8l0ba
Jul 18, 2026
Merged

slm-training issues: verified-scope-solver (SLM-57/59) + LDI0-01 contract (SLM-114)#322
Tyler-R-Kendrick merged 28 commits into
mainfrom
claude/slm-training-issues-d8l0ba

Conversation

@Tyler-R-Kendrick

@Tyler-R-Kendrick Tyler-R-Kendrick commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Rolling integration branch for autonomously-worked slm-training Linear issues (developed on the designated branch claude/slm-training-issues-d8l0ba per the environment's branch policy). Contents so far:

SLM-57 (VSS0-01) — verified-scope-solver contract

docs/design/verified-scope-solver.md + research-lineage anchors (the settled dependency SLM-59 builds on).

SLM-59 (VSS0-03) — Torch-free finite-domain support lattice

New src/slm_training/dsl/solver/ (state.py, adapters.py, __init__.py):

  • Frozen, JSON-safe HoleId, SolverBounds, SupportVerdict, DomainValue,
    HoleDomain, FiniteDomainState. Order-insensitive SHA-256 fingerprint over
    hard state/config only (excludes decision lineage, logits, scores, timestamps).
    Monotone refine(), with_decision() lineage, multi-hole meet(), summary(),
    deterministic to_dict/from_dict. completion_forest_state() adapter
    (empty → bottom; singleton → structurally-solved-for-projection-only). Lazy-loaded
    so importing the package stays Torch-free.
  • tests/test_dsl/test_solver_state.py (28 tests) incl. a subprocess-verified
    Torch-free import.

SLM-114 (LDI0-01) — local-decision evidence contract

  • local-decision-sources.json: nine verified prior-art works added (LoRA, AdaLoRA,
    PCGrad, MGDA, PICARD, Grammar-Aligned Decoding, Min-p, DeepSeekMath/GRPO, TAB-PO),
    all Adjacent (inventoried, not reimplemented); metadata verified against arXiv.
  • local-decision-interventions.md: separation-of-concerns invariants, owners +
    no-second-stack rule, the E249–E284 falsification chain and current blocker,
    DecisionEventV2 first-contract note, dated Measured-status note.
  • research-lineage.md refresh + quality-experiment-matrix.md LDI0 index (no
    claimed rows, no reserved E-IDs).
  • tests/test_autoresearch/test_harness.py: updated counts + stronger assertions.

Testing

  • pytest tests/test_dsl/test_solver_state.py tests/test_models/test_compiler_decode.py -q → 63 passed.
  • pytest tests/test_autoresearch/test_harness.py tests/test_autoresearch/test_lattice_recursive_sources.py -q → 44 passed.
  • ruff check clean on touched Python; python -m scripts.repo_policy ok; git diff --check clean.

Notes

  • No model or ship claim in any of these commits: SLM-59 is a model-independent
    data structure + adapter not on the default decode path; SLM-114 is documentation +
    a source inventory. No checkpoint, experiment, or eval.
  • Pre-existing failures in some tests/test_dsl / tests/test_harnesses/model_build
    suites (e.g. tokenizer vocab-size drift) reproduce on main without these changes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8

Summary by CodeRabbit

  • New Features
    • Added replayable causal decision tracing with constrained-generation telemetry and forced-action replay.
    • Added structured decision evidence, objective views, support checks, and migration from earlier event records.
    • Added removable low-rank adapters for TwoTower models, including adapter-only training, merging, and save/load support.
    • Added bounded diagnostics for objective conflicts and authorized parameter-subset analysis.
  • Bug Fixes
    • Training can now refuse corpora lacking sufficient held-out objective support.
    • Added fail-closed validation for identity, legality, provenance, and incomplete evidence.
  • Documentation
    • Expanded design documentation, experiment scope, source inventories, and reproducible trace fixtures.

claude added 2 commits July 17, 2026 20:50
Specify the guarantee boundary separating prefix legality from support
(participation in a bounded, fully verified completion) for the Verified
Scope Solving & Hybrid Realization project. Spec-only: no solver code,
dependency, experiment, or checkpoint; decode behavior is unchanged until a
later VSS issue enables new flags.

- docs/design/verified-scope-solver.md: term definitions, SUPPORTED /
  UNSUPPORTED / UNKNOWN semantics (UNKNOWN never removes a candidate), a
  deterministic bounded-enumeration reference backend (no SMT/Z3) plus an
  optional future backend protocol, certificate replay and the
  deduction-vs-decision transitions with every destructive step citing an
  exact proof/replay, capsule SCC boundaries, late realization, a
  relationship-to-existing-code table, and an end-to-end partial-coverage
  live-UNKNOWN example.
- research-lineage.md: VSS0 research anchors (DeepCoder, CEGIS, egg /
  e-graphs, EDLM, TreeDiff, LDT) with the existing Faithful/Adapted/
  Surrogate/Adjacent fidelity vocabulary; none tagged Faithful (spec-only).
- README.md: register the new doc in the design-doc navigation index.

Verified: python -m scripts.repo_policy ok, check-changed selects no tests
(docs-only), git diff --check clean, all relative links resolve.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MeD6nTTabPQhva7pXtTMNj
VSS0-03: add the model-independent solver state used by exact closure,
certified search, capsule solving, and later learned guidance, per the
contract in docs/design/verified-scope-solver.md (VSS0-01 / SLM-57).

New package src/slm_training/dsl/solver/ (Torch-free, stdlib-only core):
- state.py: frozen, JSON-safe HoleId, SolverBounds, SupportVerdict,
  DomainValue, HoleDomain, FiniteDomainState. Order-insensitive SHA-256
  fingerprint over hard state/config only (excludes decision_level,
  parent_fingerprint, and by construction logits/scores/timestamps/PIDs/
  caches). Monotone refine() (rejects added values / unknown holes),
  with_decision() lineage, multi-hole meet(), domain() lookup, summary
  metrics, deterministic to_dict/from_dict. FiniteDomainProjection seam.
- adapters.py: completion_forest_state() projects a CompletionForest into
  one prefix-keyed hole carrying full CompletionPath.token_ids + kind and
  the coverage guarantee in metadata (empty -> bottom; singleton ->
  structurally solved for that projection only). Lazy-loaded via package
  __getattr__ so importing the package pulls in no compiler/torch machinery.

tests/test_dsl/test_solver_state.py (28 tests) covers order-insensitive
fingerprints, hard-state/version sensitivity, JSON round-trip stability,
bottom/solved semantics, monotone refinement + expansion rejection, meet
intersection + mismatched-identity rejection, decision lineage, the
empty/singleton/multi-path/forced-suffix/partial-coverage adapter fixtures,
and a subprocess-verified Torch-free import.

No model or ship claim: a model-independent data structure plus a projection
adapter the default decode path does not invoke. No checkpoint, experiment,
or eval. Exact closure, proof certificates, capsule solving, and learned
guidance remain later VSS issues.

Verified: pytest tests/test_dsl/test_solver_state.py
tests/test_models/test_compiler_decode.py -q (63 passed); ruff clean;
python -m scripts.repo_policy ok; git diff --check clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
slm-training Ready Ready Preview, Comment Jul 18, 2026 2:46am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d8767bea-180f-4b9f-b14e-108f39bedacc

📥 Commits

Reviewing files that changed from the base of the PR and between 1ddfddc and dd533b0.

📒 Files selected for processing (11)
  • docs/design/iter-ldi2-01-twotower-adapter-20260718.md
  • docs/design/quality-experiment-matrix.md
  • docs/design/verified-scope-solver.md
  • src/slm_training/models/adapters/low_rank.py
  • src/slm_training/models/adapters/spec.py
  • src/slm_training/models/adapters/twotower_adapter.py
  • src/slm_training/models/causal_trace.py
  • src/slm_training/models/twotower.py
  • tests/test_models/test_adapter_spec.py
  • tests/test_models/test_low_rank_adapter.py
  • tests/test_models/test_twotower_adapter.py

📝 Walkthrough

Walkthrough

The change adds LDI decision-event contracts, objective-support admission, bounded diagnostics, causal constrained-trace capture and replay, removable TwoTower low-rank adapters, related fixtures and tests, plus design-document updates for LDI and verified-scope solver status.

Changes

LDI evidence and admission

Layer / File(s) Summary
Decision-event V2 contract and admission
src/slm_training/harnesses/preference/decision_events_v2.py, tests/test_harnesses/preference/test_decision_events_v2.py, docs/design/iter-ldi0-02-*
Adds validated V2 states, outcomes, objective views, migration, materializers, manifests, and fail-closed objective-support admission.
Bounded diagnostics and training preflight
src/slm_training/harnesses/preference/decision_diagnostics.py, src/slm_training/harnesses/preference/local_*.py, tests/test_harnesses/preference/test_*
Adds cumulative diagnostic budgets, atomic reports, objective geometry metrics, Tier-2 refusal/planning, and optional objective-support training refusal.
LDI source inventory and documentation
docs/design/iter-ldi0-01-*, docs/design/quality-experiment-matrix.md
Records source-inventory provenance rules, LDI namespace registration, experiment boundaries, and iteration status.

Causal decision capture

Layer / File(s) Summary
Torch-free causal trace core
src/slm_training/models/causal_trace.py, docs/design/iter-ldi1-01-*
Captures raw and legal token selections, identifies constraint shadows, emits V2 artifacts, persists traces, and validates identity on load.
OpenUI traced generation and replay
src/slm_training/models/causal_lm_openui.py
Adds adapter-aware capture identity, traced constrained generation, and forced-action replay returning pre-judge outcomes.
Causal trace fixtures and validation
tests/test_models/test_causal_trace*.py, tests/test_models/fixtures/ldi1/*
Validates deterministic capture, replay, persistence, admission refusal, telemetry, manifests, and fixture honesty.

Removable TwoTower adapters

Layer / File(s) Summary
Adapter specification and runtime implementation
src/slm_training/models/adapters/*, src/slm_training/models/twotower.py, docs/design/iter-ldi2-*
Adds strict adapter configuration, low-rank wrappers, deterministic target attachment, lifecycle controls, identity hashing, merge copies, and validated save/load.
Adapter specifications and model validation
tests/test_models/test_*adapter.py, tests/test_models/test_low_rank_adapter.py
Tests configuration validation, output and gradient behavior, RNG stability, merging, persistence, identity changes, and fail-closed errors.

Verified-scope solver documentation

Layer / File(s) Summary
Solver implementation status and boundaries
docs/design/verified-scope-solver.md
Documents the implemented Torch-free finite-domain state subset and retains the boundaries excluding default decode integration, runtime experiments, and model-quality claims.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and directly references real parts of the changeset, including verified-scope-solver and the LDI0-01 contract.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/slm-training-issues-d8l0ba

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

claude added 4 commits July 17, 2026 23:10
LDI0-01: add nine verified prior-art works to
local-decision-sources.json (LoRA, AdaLoRA, PCGrad/Gradient Surgery,
MGDA/MTL-as-MOO, PICARD, Grammar-Aligned Decoding, Min-p sampling,
DeepSeekMath/GRPO, TAB-PO). Metadata (title, authors, date) verified
against arXiv. All are labeled implementation_status "Adjacent" — none is
reimplemented — and carry category, local_decision_takeaway, repo_relevance,
and limitations. source_scope honestly records that 25 works came from the
original ChatGPT share and 9 were added by LDI0-01.

Strengthen test_local_decision_source_manifest_is_complete to assert the new
counts (42 rows / 34 papers), unique source_ids and URIs, presence of the
required provenance metadata fields, and the Adapted/Adjacent status
vocabulary (nothing claimed Faithful).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
LDI0-01: make the local-decision architecture and evidence boundaries
canonical in the repo.

- Add a "Separation of concerns" section encoding the invariants: evidence /
  objective / actuator / experiment / promotion are separately owned; constraint
  shadows certify legality only (never preference labels without same-state
  counterfactual verification); adapters (LoRA/DoRA/PiSSA/TwoTower delta/ReFT/SAE)
  are actuator choices, not event schemas; hard grammar/compiler constraints stay
  deployed; new experiments draw unique E-IDs from allocation (none reserved).
  Name the real owners (local_decisions.py, local_train.py, trace_store.py,
  run_quality_matrix.py + autoresearch) and forbid a second orchestration/training
  stack. Name DecisionEventV2 / action-table evidence as the first new contract.
- Add "Measured falsification chain and current blocker": summarize E249-E289 and
  record the E284 blocker (stable state support is necessary but not sufficient
  for the objective because independently judged legal alternatives yield
  different bad-token sets at the same state).
- Refresh the stale V10 status (E248/E249 measured; E250-E254 unrun) and the source
  audit (34 works; nine Adjacent additions mapped to clusters).
- Add a dated Measured-status note: docs/inventory only, no model or ship claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
LDI0-01:
- research-lineage.md: update the "Exact-state local decision preference" entry
  from the stale "Proposed/unrun V10 rows E248-E254" to the measured V10 status
  (E248/E249 measured, E249 rejected, E250-E254 unrun), point at the full 34-paper
  audit, and label the nine new prior-art works (LoRA/AdaLoRA, PCGrad/MGDA,
  PICARD/GAD/Min-p, GRPO, TAB-PO) Adjacent -- inventoried, not reimplemented.
- quality-experiment-matrix.md: add an "LDI (local decision interventions) index"
  that points runnable work at the existing V10 section, forbids a second trainer,
  and registers the LDI0 namespace with no claimed matrix rows and no reserved
  E-IDs (future rows draw unique E-IDs from allocation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
Records the reviewed commit, source-inventory before/after (33->42 rows,
25->34 papers), duplicate/alternate-URL handling, the nine Adjacent additions
with verified arXiv metadata, the documents updated, and an explicit
no-code/no-model-quality/no-ship honesty statement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
@Tyler-R-Kendrick Tyler-R-Kendrick changed the title Implement Torch-free finite-domain support lattice (SLM-59) slm-training issues: verified-scope-solver (SLM-57/59) + LDI0-01 contract (SLM-114) Jul 17, 2026
…(SLM-114 follow-on / LDI0-02)

Introduces src/slm_training/harnesses/preference/decision_events_v2.py, the V2
contract that separates a model DECISION STATE from per-action verifier EVIDENCE
and from a materialized OBJECTIVE VIEW — the fix for the E284 blocker (stable
grammar-state support does not imply objective/action-partition support).

- DecisionStateV2: canonical state_id over exact model state + immutable runtime
  identities only (no labels, rollouts, or candidate order), so two label samples
  of one state share a state_id and evidence reordering/augmentation never changes
  it. Generalizes V1's twotower-only shape to twotower + causal.
- ActionOutcomeV2: append-only per-candidate evidence; preserves the complete
  ordered G0-G12 gate vector verbatim (validated, never set-collapsed). Merge
  dedups by content identity, not array order.
- ObjectiveView + materializers: materialize_pareto (trainable) and
  materialize_constraint_shadow (trainable=False, so semantic trainers cannot
  consume legality-only evidence).
- migrate_v1_event: one-way, evidence-honest V1->V2 migration (partial evidence
  marked incomplete; no fabricated rollout/verifier vectors).
- Atomic JSONL writer + a manifest that separately, order-independently
  fingerprints states / evidence / views.

tests/test_harnesses/preference/test_decision_events_v2.py (16 tests): state-id
order-independence and per-field sensitivity, tampered-id rejection, complete-
G0-G12 enforcement, append-only dedup, materializer trainability + foreign-state
rejection, honest V1 migration, and order-independent manifest fingerprints. V1
suite unchanged (27 passed with test_local_decisions.py). ruff + repo_policy clean.

Scope: this is the core schema/materializer/migration foundation. The remaining
materializer views (thresholded scalar, single-best/worst, set-valued) and the
Markdown/CSV renderers from the issue remain follow-on work; no model, checkpoint,
training, or quality claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…emo (SLM-116)

Finishes the objective-view materializer set required by LDI0-02 and adds the
design memo evidence artifact.

- materialize_thresholded: threshold one scalar reward metric, gated by an
  evidence-confidence floor (low-confidence/no-reward actions stay ambiguous).
- materialize_single_best_worst: single-best good / single-worst bad control.
- materialize_set_valued: good/bad partitions from verifier verdicts (all G-gates
  pass => good). All three are trainable; constraint_shadow remains non-trainable.
- Add _action_summary so materializers aggregate multiple append-only rollout rows
  per action instead of double-classifying.
- docs/design/iter-ldi0-02-decision-events-v2-20260717.md: worked fixture-corpus
  walk-through (one state, two actions, two materializers producing different
  views, stable state fingerprint vs changing objective fingerprint) with an
  explicit no-training/no-ship honesty statement and named follow-ons.

Tests: 20 in test_decision_events_v2.py (adds thresholded/single-best-worst/
set-valued views + idempotent migration); V1 suite unchanged (31 passed with
test_local_decisions.py). ruff + repo_policy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
Adds the V2 objective-support coverage gate that fixes the E284 blocker:
a corpus can pass state-support (good-only signature) yet fail objective
support because the sampled bad-action set changes the FTPO gradient.

- objective_view_signature: keys on materializer id/config hash + the good/bad
  partition (unlike the state-support signature, which excludes negatives).
- objective_view_support: held-out objective-view coverage (the V2 analogue of
  decision_signature_support) — covered/uncovered/passed by split.
- admit_semantic_corpus: fail-closed admission that refuses non-trainable
  (constraint_shadow) views, materializer mismatches, and corpora lacking train
  support for any held-out objective signature; each refusal names what is missing.

4 tests (24 total in test_decision_events_v2.py): the E284 pattern (passes
state support, fails objective support), covered-passes, constraint-shadow
refusal, materializer-mismatch refusal. ruff + repo_policy clean.

Scope: Part A (admission) core, torch-free. Part B (the bounded two-tier
runtime diagnostic with a cumulative-wall-budget deadline + fake-clock tests)
and the trainer-entry wiring + memo are the next commits. No model, checkpoint,
training, or quality claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…/ LDI0-03 Part B core)

Implements the runtime-budget mechanism that makes the objective-geometry
diagnostic bounded, fixing the E285/E286 failure mode (a profile that ran past
the envelope and produced invalid evidence).

- DiagnosticBudget: cumulative wall budget, default and hard-capped at 5 minutes.
- Deadline: one monotonic deadline shared across stages (read through the module
  `time` ref so a fake clock can be injected).
- run_bounded_stages: runs named stages under the shared deadline; on expiry
  returns status "expired" with result=None (no partial result is ever presented
  as a diagnostic result) and per-stage telemetry showing what ran before the stop.
- not_authorized_report: refuses an unauthorized full-parameter Tier-2 request
  (status "not_authorized", no result) instead of replaying the invalid E285
  full-parameter profile.
- write_diagnostic_report: atomic mkstemp + fsync + os.replace JSON write.

5 tests (deterministic fake clock): 5-minute cap, completed run, deadline expiry
produces no result and runs no further stage, not_authorized has no result,
atomic write. ruff + repo_policy clean.

Scope: the bounded-runner core. Remaining for LDI0-03: the Tier-1 logit-space and
Tier-2 adapter-subspace diagnostic content that runs on this runner, the
train_local_from_paths admission wiring, and the design memo. No model,
checkpoint, training, or quality claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…SLM-117)

Documents the objective-support admission gate and bounded-diagnostic runner
landed for LDI0-03, compares V1 state-support / V1 objective-signature / V2
objective-view support, and marks E285 and E286 as invalid historical evidence
(ran past the five-minute envelope with no report). Notes the remaining Tier-1/
Tier-2 diagnostic content and train_local_from_paths admission wiring as follow-on.
No training, checkpoint, model-quality, or ship claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…DI0-03)

Wires the E284 fix into the semantic trainer so a non-admitted corpus is refused
before the first optimizer step (and before the checkpoint is even loaded),
mirroring the existing constraint_shadow refusal.

- local_decisions.objective_signature_support: held-out coverage of the OBJECTIVE
  signature (decision_signature, good + bad) — the analogue of the good-only
  decision_signature_support. A corpus can pass state support yet fail here.
- train_local_from_paths gains require_objective_support; when set it computes
  objective-signature coverage on the loaded events and raises, naming the
  uncovered held-out signatures, before TwoTowerModel.from_checkpoint.

2 tests: the E284 pattern (state support passes, objective support fails) and the
trainer refusing an objective-unsupported corpus (the refusal fires before the
missing checkpoint is reached). 13 passed in test_local_decisions.py; ruff +
repo_policy clean.

Remaining for LDI0-03: the Tier-1/Tier-2 diagnostic content on the bounded runner.
No training run, checkpoint, or model-quality claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…7 / LDI0-03)

Complete the bounded-diagnostic content on run_bounded_stages:

- tier1_objective_geometry: read-only geometry over already-materialized
  objective views (getattr on good/bad action ids, decoupled from
  decision_events_v2). Reports per-corpus objective-contradiction count (an
  action scored good by one view and bad by another — the logit-space shadow
  of the E284 conflict) and the mean pairwise Jaccard overlap of per-state
  good-action sets. Computes no gradient, trains nothing.
- tier2_subspace_gradients: refuses an empty/None (full-parameter) request as
  not_authorized rather than replaying the invalid E285 full-parameter
  profile; for an explicit adapter subset it records a bounded plan only, with
  the gradient computation deferred to a model stage this module never runs.

Both run under the shared cumulative five-minute DiagnosticBudget, so an
expired run still yields result: None. Adds fake-clock tests for the Tier-1
contradiction/agreement/deadline paths and the Tier-2 refuse/plan paths, and
updates the LDI0-03 memo to reflect what landed vs. the remaining V2
admit_semantic_corpus CLI wiring. No training, checkpoint, or model-quality
claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…19 / LDI1-01)

Instrument the causal OpenUI plug-in so every supervised decision is recoverable
from exact prefix token ids and model logits, not decoded strings, and emit
DecisionEventV2 evidence compatible with the shared preference harness (SLM-116).

- models/causal_trace.py (torch-free core): capture_raw_steps drives a greedy
  constrained decode through injected forward_logits/allowed_ids callables, so the
  raw pre-mask argmax and the constrained selection come from the same logits and a
  constraint_shadow (illegal raw winner overridden by a legal selection) is exact.
  Returns policy-retained RawStepObservations, the full generated sequence, and an
  honest stop_reason. Bounded selection policies (every / constraint_shadow_only /
  margin_threshold / sampled_positions / named_roles) never change what is emitted.
- Emission: emit_causal_decision maps observations onto causal DecisionStateV2 with
  the exact integer prefix as context_ids; base+adapter identity is folded into
  policy_checkpoint_sha (no dedicated adapter field), so adapter on/off yields
  distinct state identities. A shadow yields a legality-only ActionOutcomeV2 and a
  non-trainable materialize_constraint_shadow view that admit_semantic_corpus refuses.
- Persistence: CausalTraceWriter appends to the shared TraceStore (kind=causal_decision,
  no second format) with a reproducible manifest; load_causal_decision_states fails
  closed on a checkpoint/tokenizer mismatch before returning any state.
- Plug-in: generate_constrained_traced stores context_ids as the full prefix so
  model(context_ids).logits[:, -1, :] replays exactly; replay_causal_action applies a
  forced action to the exact prefix and returns a pre-judge GeneratedOutcome for the
  counterfactual owner (no judge here). generate_constrained (trace-off) is unchanged.

Tests: torch-free capture/emission/persistence/fail-closed/policy suite; a torch suite
for the real forward through the loop, stored-logit replay within tolerance, greedy
equivalence, reproducible forced replay, and adapter identity; and a committed
fixture-grade trace (exact prefix replay, raw/legal telemetry, one non-admittable
constraint shadow, one forced legal counterfactual outcome). Adds no FTPO trainer, no
judge, no matrix run, no training, and no model-quality or ship claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
The causal-capture fixture test asserted that a fixed program validated against
the OpenUI catalog. That passed under the lenient lark backend used locally but
failed CI, where the strict lang_core backend rejects "Text" as not-in-catalog.
The component catalog is environment/pack-dependent, so asserting catalog-validity
in a committed fixture is both fragile and an overreach.

Set the forced-outcome fixture's canonical_program to null — which is exactly what
a strict-backend replay yields for a non-catalog program — and assert only
backend-independent honesty properties (forced action id, finish reason, no judge
label). Update the fixture README and LDI1-01 memo to state that canonicalization
is deferred to the strict validator rather than claimed here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🧹 Nitpick comments (1)
tests/test_harnesses/preference/test_decision_events_v2.py (1)

124-131: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover every field contributing to state_id.

Despite the test name, several identity fields are omitted, including tokenizer/decode hashes, abstract role, canvas/context IDs, generation step, architecture, and group. Parameterize the field mutations so future hash regressions cannot pass unnoticed.

As per coding guidelines, “Non-trivial logic must leave one runnable check behind.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_harnesses/preference/test_decision_events_v2.py` around lines 124
- 131, Expand test_state_id_changes_for_every_hard_state_field to cover every
field used by _state().state_id, including tokenizer and decode hashes, abstract
role, canvas/context IDs, generation step, architecture, and group. Parameterize
the field mutations so each change is asserted against the base state_id,
preserving the existing checks and leaving a runnable regression check for every
identity field.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/design/iter-ldi0-02-decision-events-v2-20260717.md`:
- Around line 34-35: Update the recorded test count from 20 to 24 in both
references in the decision-events walkthrough, including the additional
occurrence around the later referenced section. Do not change the test suite or
surrounding documentation.

In `@docs/design/verified-scope-solver.md`:
- Around line 3-9: Update the document’s status section to remove the stale
claim that no solver code exists or that the project is specification-only.
Describe VSS0-01 as the contract and identify VSS0-03 as the implemented subset,
while preserving accurate statements about unchanged checkpoints, decode
behavior, and feature-gated rollout.

In `@src/slm_training/dsl/solver/adapters.py`:
- Around line 63-82: Update the projection flow around prefix, completion-path,
pack, and constraint-version inputs to validate their types explicitly at the
trust boundary instead of coercing them with int() or str(). Reject booleans,
strings, floats, and other invalid token values; preserve validated token values
unchanged for prefix and path construction, and validate pack_id and
constraint_version before using them in problem identity/state fields.
- Around line 34-38: Update _prefix_fingerprint to return the complete SHA-256
digest instead of truncating it to the first 16 hexadecimal characters, while
preserving the existing normalized ID serialization and hashing behavior.

In `@src/slm_training/dsl/solver/state.py`:
- Around line 136-143: Update HoleId.from_dict and the other solver-state
deserializers to fail closed at the JSON boundary: validate mapping, container,
and scalar types before constructing objects, access required fields directly,
and reject missing or null required domains. Remove str()/int() coercions and
data.get(...) or () fallbacks so dataclass validators receive original values
and malformed payloads cannot become valid empty or solved states.
- Around line 98-130: Remove ordering generation from the HoleId dataclass by
dropping order=True, while preserving dataclass immutability and all existing
fields, validation, and canonical_key behavior. Callers should use canonical_key
for sorting and lookup instead of generated comparisons.
- Around line 561-570: Update _merge_metadata to detect metadata conflicts using
canonical JSON representations rather than Python equality, so values such as 1
and 1.0 are treated distinctly. When representations match, retain a
deterministic value independent of operand order, ensuring left.meet(right) and
right.meet(left) produce identical metadata and fingerprints.
- Around line 53-64: Update _is_json_scalar to accept floats only when they are
finite, while continuing to allow None, strings, integers, and booleans. Use the
standard floating-point finiteness check so NaN and positive or negative
infinity are rejected before _canonical_json serializes or fingerprints state.

In `@src/slm_training/harnesses/preference/decision_events_v2.py`:
- Around line 754-770: Update objective_view_signature to include the existing
state-support identity in its hashed payload alongside materializer
configuration and good/bad action IDs. Reuse the established state-support
signature helper or symbol rather than reconstructing state identity, while
preserving the objective partition fields and truncation behavior.
- Around line 415-418: Update the outcome validation loops around the shown
state_id checks, including the corresponding logic near the second occurrence,
to reject any outcome whose action_id is not in state.legal_action_ids or whose
legal flag is false. Perform this validation before semantic materialization and
before adding action IDs to observed, while preserving the existing state
ownership validation.
- Around line 321-322: Update _normalize_reward_vector to validate reward
vectors at the input boundary before materialization: require each entry to
match the expected metric identity, reject missing metrics, and reject duplicate
metrics instead of allowing row.get(name, 0.0) to fabricate zero scores.
Preserve valid vectors’ normalized tuple output and ensure malformed inputs fail
before any consumer processes them.
- Around line 413-421: Update the shared _action_summary aggregation to collect
and flatten all underlying reward vectors per action, then compute one overall
average rather than averaging batch means or retaining the last batch. Replace
the local by_action/Pareto aggregation and other materializer-specific logic
with this canonical summary, preserving state validation and observed-action
tracking across every caller.
- Around line 773-781: Validate min_train_support at the start of
objective_view_support and require it to be a positive integer, rejecting zero,
negative values, and non-integer inputs before processing items. Preserve the
existing coverage behavior for valid thresholds.
- Around line 284-308: Update ActionOutcomeV2.from_dict to validate the
serialized version against the supported V2 schema version before
reconstruction, rejecting unsupported values. Validate that legal is an actual
boolean rather than coercing arbitrary inputs with bool(), and preserve the
existing field parsing for valid records.

In `@src/slm_training/models/causal_lm_openui.py`:
- Around line 278-300: The policy identity currently hashes compatibility
metadata and LoRA parameter names rather than actual weights. Update
active_adapter_identity and the capture_identity fingerprint path to use a
stable digest of the active base and adapter checkpoint/artifact tensors, so any
weight mutation changes policy_checkpoint_sha while preserving empty-adapter
behavior. Add a regression test that mutates an existing LoRA tensor without
renaming it and verifies the captured identity changes.
- Around line 383-390: In the causal replay method surrounding state.context_ids
validation, validate that forced_action_id belongs to state.legal_action_ids
before running the model or constructing base_prefix. Reject non-members with
the method’s existing input-validation error behavior, while preserving the
current flow for legal forced actions.
- Around line 385-411: Update replay output construction in the flow containing
forward_logits and capture_raw_steps so raw_text decodes the full generated
suffix already present in state.context_ids, starting at the decision position,
followed by forced_action_id and the continuation tokens. Preserve the existing
prompt prefix exclusion and add a regression covering a state with
decision_position greater than zero to verify earlier generated tokens remain in
the materialized program.

In `@src/slm_training/models/causal_trace.py`:
- Around line 532-539: Update the state-loading loop around
DecisionStateV2.from_dict to validate the deserialized state’s checkpoint and
tokenizer hashes against expected_checkpoint_sha and expected_tokenizer_sha, in
addition to the existing row-level checks. Reject mismatches before appending
the state to states, preserving the current ValueError behavior.
- Around line 276-286: Update the logits handling in the causal-trace decoding
flow to validate every value from forward_logits(prefix) is finite before any
selection, softmax, or evidence persistence. Raise CausalTraceError for
non-finite logits, while preserving the existing empty-vector and legal-token
validation behavior.

In `@tests/test_dsl/test_solver_state.py`:
- Around line 388-392: Update the subprocess.run call in the Torch-free import
test to use a short timeout, and catch subprocess.TimeoutExpired so the test
fails explicitly with useful timeout details instead of hanging indefinitely.
Preserve the existing return-code and TORCH_FREE_OK assertions for successful
completion.

---

Nitpick comments:
In `@tests/test_harnesses/preference/test_decision_events_v2.py`:
- Around line 124-131: Expand test_state_id_changes_for_every_hard_state_field
to cover every field used by _state().state_id, including tokenizer and decode
hashes, abstract role, canvas/context IDs, generation step, architecture, and
group. Parameterize the field mutations so each change is asserted against the
base state_id, preserving the existing checks and leaving a runnable regression
check for every identity field.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 601ee29f-7fdf-4580-abf8-20283616b8e1

📥 Commits

Reviewing files that changed from the base of the PR and between c7cb099 and ff1417e.

📒 Files selected for processing (31)
  • README.md
  • docs/design/iter-ldi0-01-20260717.md
  • docs/design/iter-ldi0-02-decision-events-v2-20260717.md
  • docs/design/iter-ldi0-03-objective-support-admission-20260718.md
  • docs/design/iter-ldi1-01-causal-capture-20260718.md
  • docs/design/local-decision-interventions.md
  • docs/design/quality-experiment-matrix.md
  • docs/design/research-lineage.md
  • docs/design/verified-scope-solver.md
  • src/slm_training/dsl/solver/__init__.py
  • src/slm_training/dsl/solver/adapters.py
  • src/slm_training/dsl/solver/state.py
  • src/slm_training/harnesses/preference/decision_diagnostics.py
  • src/slm_training/harnesses/preference/decision_events_v2.py
  • src/slm_training/harnesses/preference/local_decisions.py
  • src/slm_training/harnesses/preference/local_train.py
  • src/slm_training/models/causal_lm_openui.py
  • src/slm_training/models/causal_trace.py
  • src/slm_training/resources/autoresearch/local-decision-sources.json
  • tests/test_autoresearch/test_harness.py
  • tests/test_dsl/test_solver_state.py
  • tests/test_harnesses/preference/test_decision_diagnostics.py
  • tests/test_harnesses/preference/test_decision_events_v2.py
  • tests/test_harnesses/preference/test_local_decisions.py
  • tests/test_models/fixtures/ldi1/README.md
  • tests/test_models/fixtures/ldi1/causal_trace_manifest.json
  • tests/test_models/fixtures/ldi1/forced_counterfactual_outcome.json
  • tests/test_models/fixtures/ldi1/traces.jsonl
  • tests/test_models/test_causal_trace.py
  • tests/test_models/test_causal_trace_fixture.py
  • tests/test_models/test_causal_trace_plugin.py

Comment thread docs/design/iter-ldi0-02-decision-events-v2-20260717.md Outdated
Comment thread docs/design/verified-scope-solver.md Outdated
Comment thread src/slm_training/dsl/solver/adapters.py Outdated
Comment thread src/slm_training/dsl/solver/adapters.py Outdated
Comment on lines +63 to +82
if not isinstance(forest, CompletionForest):
raise ValueError(f"completion_forest_state requires a CompletionForest, got {type(forest).__name__}")
prefix = tuple(int(token) for token in prefix_ids)
fingerprint = _prefix_fingerprint(prefix)
hole_id = HoleId(namespace=_NAMESPACE, path=(len(prefix), fingerprint), kind="next_action")
values = tuple(
DomainValue(
tag=_TOKEN_PATH_TAG,
token_ids=tuple(int(token) for token in path.token_ids),
kind=path.kind,
)
for path in forest.paths
)
hole = HoleDomain(hole_id=hole_id, values=values, metadata=(("coverage", str(forest.coverage)),))
return FiniteDomainState(
problem_id=f"{_NAMESPACE}:{fingerprint}",
pack_id=str(pack_id),
constraint_version=str(constraint_version),
bounds=bounds,
holes=(hole,),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate projection inputs instead of coercing them.

int() aliases values such as True, "1", and 1.9 to the same token, while str() accepts invalid pack/version identifiers. This bypasses the stricter state validators and can assign malformed compiler inputs a valid state identity.

Pass values through unchanged after explicit type checks, including every prefix and completion-path token.

As per coding guidelines, “Always perform input validation at trust boundaries.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/slm_training/dsl/solver/adapters.py` around lines 63 - 82, Update the
projection flow around prefix, completion-path, pack, and constraint-version
inputs to validate their types explicitly at the trust boundary instead of
coercing them with int() or str(). Reject booleans, strings, floats, and other
invalid token values; preserve validated token values unchanged for prefix and
path construction, and validate pack_id and constraint_version before using them
in problem identity/state fields.

Source: Coding guidelines

Comment thread src/slm_training/dsl/solver/state.py Outdated
Comment thread src/slm_training/models/causal_lm_openui.py
Comment thread src/slm_training/models/causal_lm_openui.py
Comment thread src/slm_training/models/causal_trace.py
Comment thread src/slm_training/models/causal_trace.py
Comment thread tests/test_dsl/test_solver_state.py Outdated
…low-up)

Address correctness/validation findings from PR review on the already-landed
SLM-59/116/119 code:

- causal replay (causal_lm_openui): reject a forced action that is not legal for
  the stored state, and keep the tokens generated before the decision
  (context_ids[prompt_len:]) so a decision_position > 0 replay materializes the
  full program rather than a truncated one. active_adapter_identity now digests
  adapter tensor *values*, so a weight mutation changes policy_checkpoint_sha.
- causal_trace: reject non-finite logits before selection/persistence; validate the
  deserialized state identity against the requested checkpoint/tokenizer, not only
  the row envelope.
- decision_events_v2: ActionOutcomeV2.from_dict validates schema version and rejects
  non-boolean legal; objective_view_support requires a positive min_train_support.
- solver state: reject non-finite floats from JSON-safe state (allow_nan=False +
  finite check), drop HoleId's unusable generated ordering (paths mix str/int; use
  canonical_key), and make _merge_metadata commutative via canonical JSON.
- solver adapters: keep the full SHA-256 prefix digest (no 64-bit truncation).
- tests: bound the torch-free import subprocess with a timeout; add regression tests
  for the replay suffix, illegal forced action, and adapter-weight identity change.
- docs: correct the decision-events test count (24) and the verified-scope-solver
  status (state schema + adapter implemented, decode integration not).

Regression tests + full solver/preference/models suites stay green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
… part 1)

The self-contained, repository-owned adapter primitive (no Hugging Face PEFT):

- models/adapters/spec.py: TwoTowerAdapterSpec, a torch-free frozen, versioned,
  validated config that round-trips through to_dict/from_dict (rejects unknown
  fields) so it can persist in an adapter_config.json and through CLI/config.
- models/adapters/low_rank.py: LowRankAdapter, a pure-torch wrapper for a frozen
  nn.Linear implementing y = W x + (alpha/rank) * B(A(dropout(x))) with B
  zero-initialized (fresh adapter is bit-identical to the parent), enable/disable,
  adapter_parameters, and a one-way merged_linear() on a copy that never mutates
  the parent or the removable adapter.

Torch-free spec tests and torch-gated wrapper tests cover zero-init identity,
disable-restore, gradients only on A/B, dtype/device + batched dims, merge parity
without base mutation, and validation. TwoTower model integration (attach/save/load/
merge APIs, adapter-only trainable_parameters, checkpoint allow-list) follows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
… (review)

Matching state_id alone let an outcome with an arbitrary action_id, or legal=False,
become a trainable good/bad label. Add a shared _require_semantic_outcome guard
(state ownership + legal flag + membership in the state's legal action set) and call
it in materialize_pareto and the shared _action_summary, so every semantic
materializer fails closed before an illegal action is preferred. The legality-only
constraint_shadow diagnostic is unchanged (it emits no good/bad labels).

Adds a regression test for the illegal and out-of-domain cases; updates the memo
test count. Full preference suite green (25 tests in the V2 suite).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
_normalize_reward_vector (the ActionOutcomeV2 construction boundary) now rejects a
non-empty reward vector with missing or duplicate metrics, so a downstream mean can
no longer fabricate a 0.0 for absent evidence. Empty reward vectors (migrated /
legality-only outcomes) remain valid. Adds a regression test; preference suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…2-01, part 2)

Attach/resolve the low-rank adapter onto the denoiser and expose adapter-only training:

- models/adapters/twotower_adapter.py: deterministic target resolution mapping spec
  target-module names (attn_q/k/v/out, cross_attn_*, mlp_in/out) onto the denoiser
  block linears, with fail-closed errors for unknown targets, non-linear matches, and
  out-of-range layers; attaches LowRankAdapter wrappers in place under a forked RNG so
  attachment never shifts the training RNG stream. The context tower is never adapted.
- TwoTowerModel: attach_adapter (base-fingerprint fail-closed + freeze every non-adapter
  parameter so the existing trainable_parameters() naturally yields only adapter
  tensors), enable/disable_adapter, adapter_parameters, has_adapter, and
  active_adapter_identity (content digest of adapter tensors).

Tests cover fresh-adapter output identity at every site, disable-restore after weight
change, gradients only on adapter tensors, deterministic resolution + actionable
errors, base-fingerprint mismatch, no-RNG-shift, and adapter-only trainable_parameters.
Existing twotower suite unchanged. Save/load/merge, the checkpoint allow-list, config/CLI
round-trip, and the wiring memo follow. No training run, no quality claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…art 3)

merge_adapter_copy folds each denoiser LowRankAdapter into a plain nn.Linear on a
deepcopy, so the merged model carries no active wrappers and trains as an ordinary
full model, while this model and its removable adapter are left untouched (merge is
one-way on a copy). Tests cover merged-vs-enabled parity at an adapted site, the
original staying adapter-bearing, and the no-adapter error.

Adds the LDI2-01 wiring memo recording what landed (spec, LowRankAdapter, deterministic
attachment, adapter-only training, merge) and the honest remaining scope (save/load
directory, checkpoint allow-list, config/CLI round-trip, artifact_identity distinction).
No training run, no promoted checkpoint, no quality claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
…LDI2-01, part 4)

save_adapter writes the removable adapter to its own directory — adapter_config.json
(the spec), adapter_model.pt (only the lora tensors, not the base checkpoint), and
adapter_manifest.json (module map, parameter names/shapes, trainable parameter count,
adapter bytes, and base/tokenizer identity). load_adapter fails closed before copying
any tensor when the tokenizer identity or base compatibility fingerprint does not match
this model, then attaches, loads the tensors, and sets requires_grad per `trainable`.

Tests cover the save→load round trip (a fresh same-seed model reproduces the adapted
logits) and the fail-closed base-fingerprint mismatch. Existing twotower suite
unchanged; the base .pt checkpoint path is untouched. The checkpoint allow-list and
config/CLI round-trip remain the documented follow-on. No training run, no quality claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
… subtlety

Record that save_adapter/load_adapter (fail-closed removable adapter directory) landed,
and correct the remaining-scope note: attaching an adapter renames the wrapped base
weight key (q_proj.weight -> q_proj.base.weight), so the base-.pt checkpoint interplay
is a wrapper-stripping design task, not a one-line adapter-key allow-list. Doc only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
Add concise docstrings to the public methods/properties of the new
LDI modules that lacked them — the spec's scaling/to_dict/from_dict,
the low-rank adapter's construction/enable/disable/forward surface,
the two non-obvious target-resolution helpers, and the causal-trace
observation/capture/writer/outcome API. Docstrings only; no behavior
change (all affected tests unchanged and green).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/slm_training/models/adapters/low_rank.py`:
- Around line 28-42: Update LowRankAdapter.__init__ to validate alpha as finite
and positive, and dropout as finite and non-negative, before assigning scaling
or constructing dropout. Reject invalid values with ValueError, while preserving
valid zero-dropout behavior and existing rank validation.

In `@src/slm_training/models/adapters/spec.py`:
- Around line 40-78: Validate schema_version in AdapterSpec.__post_init__ by
requiring it to equal the supported ADAPTER_SCHEMA_VERSION. Raise a clear
ValueError for any unsupported version so from_dict() cannot interpret future
schemas as the current format.

In `@src/slm_training/models/adapters/twotower_adapter.py`:
- Around line 76-99: Update the adapter target-resolution flow around the
existing block traversal to either resolve the output head when
spec.include_output_head is true or explicitly reject that configuration with a
clear ValueError. Ensure the option never silently has no effect, while
preserving current denoiser-layer resolution behavior.
- Around line 115-120: Update the RNG context around LowRankAdapter construction
in the adapter attachment loop to include the target linear layer’s CUDA device
when applicable, while preserving CPU behavior. Use the device associated with
linear to populate fork_rng(devices=...), ensuring lora_A initialization does
not advance the external CUDA generator.

In `@src/slm_training/models/twotower.py`:
- Around line 1002-1016: Update the adapter-loading flow around
self.attach_adapter and torch.load to fully load and validate the artifact
before mutating the model: verify the exact tensor key set, shapes, manifest
module map, and parameter identities, including both missing and unexpected
entries. Only after all validation succeeds should attach_adapter run, tensors
be copied, and trainability flags be updated; preserve atomic behavior by
leaving the model unchanged when loading or validation fails.
- Around line 872-876: Update the adapter attachment validation near
compatibility_fingerprint() in src/slm_training/models/twotower.py (lines
872-876) to compare base_checkpoint_sha with the model’s loaded base-checkpoint
identity and fail on mismatch. Revise
docs/design/iter-ldi2-01-twotower-adapter-20260718.md (lines 47-52) to avoid
claiming fail-closed base identity until this check is implemented. Add a test
in tests/test_models/test_twotower_adapter.py (lines 173-184) covering identical
architectures with different checkpoints and asserting attachment is rejected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 535f7611-a9bb-4d2f-b852-10535951d79d

📥 Commits

Reviewing files that changed from the base of the PR and between ff1417e and 1ddfddc.

📒 Files selected for processing (23)
  • docs/design/iter-ldi0-02-decision-events-v2-20260717.md
  • docs/design/iter-ldi1-01-causal-capture-20260718.md
  • docs/design/iter-ldi2-01-twotower-adapter-20260718.md
  • docs/design/verified-scope-solver.md
  • src/slm_training/dsl/solver/adapters.py
  • src/slm_training/dsl/solver/state.py
  • src/slm_training/harnesses/preference/decision_events_v2.py
  • src/slm_training/models/adapters/__init__.py
  • src/slm_training/models/adapters/low_rank.py
  • src/slm_training/models/adapters/spec.py
  • src/slm_training/models/adapters/twotower_adapter.py
  • src/slm_training/models/causal_lm_openui.py
  • src/slm_training/models/causal_trace.py
  • src/slm_training/models/twotower.py
  • tests/test_dsl/test_solver_state.py
  • tests/test_harnesses/preference/test_decision_events_v2.py
  • tests/test_models/fixtures/ldi1/README.md
  • tests/test_models/fixtures/ldi1/forced_counterfactual_outcome.json
  • tests/test_models/test_adapter_spec.py
  • tests/test_models/test_causal_trace_fixture.py
  • tests/test_models/test_causal_trace_plugin.py
  • tests/test_models/test_low_rank_adapter.py
  • tests/test_models/test_twotower_adapter.py
🚧 Files skipped from review as they are similar to previous changes (11)
  • tests/test_models/fixtures/ldi1/forced_counterfactual_outcome.json
  • docs/design/iter-ldi1-01-causal-capture-20260718.md
  • tests/test_models/fixtures/ldi1/README.md
  • tests/test_models/test_causal_trace_fixture.py
  • docs/design/iter-ldi0-02-decision-events-v2-20260717.md
  • src/slm_training/models/causal_lm_openui.py
  • docs/design/verified-scope-solver.md
  • src/slm_training/dsl/solver/state.py
  • src/slm_training/harnesses/preference/decision_events_v2.py
  • tests/test_harnesses/preference/test_decision_events_v2.py
  • src/slm_training/models/causal_trace.py

Comment thread src/slm_training/models/adapters/low_rank.py
Comment thread src/slm_training/models/adapters/spec.py
Comment thread src/slm_training/models/adapters/twotower_adapter.py
Comment thread src/slm_training/models/adapters/twotower_adapter.py
Comment on lines +872 to +876
expected = getattr(spec, "base_compatibility_fingerprint", "")
if expected and expected != self.compatibility_fingerprint():
raise ValueError(
"adapter base compatibility fingerprint does not match this model"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Enforce exact base-checkpoint identity across runtime, documentation, and tests.

The adapter stores base_checkpoint_sha, but attachment only checks a shape/configuration fingerprint.

  • src/slm_training/models/twotower.py#L872-L876: compare the adapter SHA against the model’s loaded base-checkpoint identity.
  • docs/design/iter-ldi2-01-twotower-adapter-20260718.md#L47-L52: avoid claiming fail-closed base identity until that check exists.
  • tests/test_models/test_twotower_adapter.py#L173-L184: add an identical-architecture, different-checkpoint mismatch test.
📍 Affects 3 files
  • src/slm_training/models/twotower.py#L872-L876 (this comment)
  • docs/design/iter-ldi2-01-twotower-adapter-20260718.md#L47-L52
  • tests/test_models/test_twotower_adapter.py#L173-L184
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/slm_training/models/twotower.py` around lines 872 - 876, Update the
adapter attachment validation near compatibility_fingerprint() in
src/slm_training/models/twotower.py (lines 872-876) to compare
base_checkpoint_sha with the model’s loaded base-checkpoint identity and fail on
mismatch. Revise docs/design/iter-ldi2-01-twotower-adapter-20260718.md (lines
47-52) to avoid claiming fail-closed base identity until this check is
implemented. Add a test in tests/test_models/test_twotower_adapter.py (lines
173-184) covering identical architectures with different checkpoints and
asserting attachment is rejected.

Comment thread src/slm_training/models/twotower.py
Address CodeRabbit findings on the removable low-rank adapter:

- low_rank: validate alpha (finite, positive) and dropout (finite, in
  [0,1)) in the wrapper constructor, so direct construction — which
  bypasses TwoTowerAdapterSpec — cannot silently produce an invalid
  adapter (poisoned scaling / dropout degrading to Identity).
- spec: reject an unsupported schema_version in __post_init__ so a newer
  schema can never be reinterpreted as V1 through from_dict().
- twotower_adapter: fail closed on include_output_head=True (the output
  head is not a resolvable target, so the flag would otherwise silently
  have no effect); include the target CUDA device in the RNG fork so
  attachment never advances the external CUDA generator.
- twotower.load_adapter: validate the whole artifact against its manifest
  (tokenizer + base fingerprint agreement, exact tensor key set, shapes)
  before mutating the model, and check the key set in both directions, so
  a mismatched or truncated adapter leaves the model unchanged instead of
  half-attached.
- Document that base_checkpoint_sha is provenance only (attachment is
  gated on architecture fingerprint + tokenizer_sha, which do not tell
  two checkpoints of the same shape apart); exact base-checkpoint
  enforcement is deferred with the checkpoint-identity work.

Regression tests added for each fail-closed path. Full tests/test_models
suite green (172 passed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
Resolve conflicts against main's checkpoint + decode-invariance advance:
- local-decision-sources.json / test_autoresearch: take main's canonical
  data+test pair (both branches landed the same SLM-114 source expansion:
  34 works / 42 rows; main's test additionally verifies the 9 arxiv IDs).
- verified-scope-solver.md: keep this branch's version — it documents the
  torch-free solver state/adapter implementation this PR actually ships
  (main's doc was the earlier spec-only stub).
- research-lineage.md, local-decision-interventions.md: take main's
  canonical, further-advanced LDI synthesis (same conclusions; richer
  E248-E286 ledger).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
Second re-merge to catch up to main's fast-moving tip. Only conflict was
docs/design/verified-scope-solver.md — both branches appended a dated
"Measured status" note (main's VSS0-02/SLM-58 explain-seam note, this
branch's VSS0-03/SLM-59 solver-impl note); kept both in numeric order.
Main's choice_tokenizer / compiler_draft / compiler_decode changes
auto-merged. Focused tests green (80 passed), repo_policy ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
The fleet independently shipped SLM-59 (Torch-free finite-domain support
lattice) to main via #321/#326. This branch had its own SLM-59
implementation, now redundant. Per "do not reimplement what already
exists," resolve the add/add collision by taking main's canonical
dsl/solver/ (state.py, adapters.py, __init__.py) and test_solver_state.py
verbatim, dropping this branch's duplicate. #322 collapses to its unique,
solver-independent LDI contribution (DecisionEventV2 + diagnostics,
causal capture, removable adapter). Verified: main's solver test + all
LDI suites pass together (141), repo_policy ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5Fy8PnMTV54p675y9T3B8
@Tyler-R-Kendrick
Tyler-R-Kendrick merged commit c8387dc into main Jul 18, 2026
2 of 4 checks passed
@Tyler-R-Kendrick
Tyler-R-Kendrick deleted the claude/slm-training-issues-d8l0ba branch July 18, 2026 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants