V2 controller operating layer
This document records the v2 boundary introduced by the feature-architecture patch. It is intentionally conservative: controller artifacts are sparse activation interventions, not factual stores, and the serving path assumes PyTorch hooks are model-global unless wrapped by an isolation runtime.
Runtime isolation
Use ControllerRuntime for request-scoped application rather than attaching a controller directly to a shared model. The runtime validates controller shape and identity, serialises hook application by default, and removes hooks in a finally block.
from ntkmirror import ControllerRuntime
runtime = ControllerRuntime(model, tokenizer)
with runtime.apply(controller_state):
out = model(**batch)Heterogeneous per-row controllers are not silently supported. Split batches by controller or use a serving implementation that explicitly implements per-row scaling. This avoids a high-severity failure mode where one user’s controller affects another user’s request.
Artifact admission
Controller artifacts should be linted before they enter a memory store or deployment registry:
ntkmirror lint --controller controller.pt --require-revision --out lint.json
ntkmirror card --controller controller.pt --out controller.card.mdThe linter checks schema validity, model/tokenizer revision metadata, gate saturation, layer concentration, and presence of evaluation/safety metadata. The controller card is a human-readable artifact summary; it is not a safety proof.
Model doctor
Before training or loading controllers for a new model family, run:
ntkmirror doctor --model MODEL --revision REV --out doctor.jsonThe doctor reports the detected decoder layer path, hidden size, tokenizer pad/chat-template status, dtype/device, and supported hook sites.
Training hygiene
The normal fitting path now supports validation and retain-data objectives:
ntkmirror fit \
--model MODEL \
--revision REV \
--train train.jsonl \
--validation validation.jsonl \
--eval-every 20 \
--early-stop-patience 3 \
--retain retain.jsonl \
--retain-weight 0.2 \
--kl-to-base 0.05 \
--out controller.pt--retain-weight adds supervised retain-set NLL. --kl-to-base penalizes drift from the base model on retain rows. Both require --retain.
Chat data
load_jsonl_examples accepts either raw prompt/completion rows or chat rows:
{"messages":[{"role":"system","content":"..."},{"role":"user","content":"..."},{"role":"assistant","content":"..."}]}By default, chat rows train only on the final assistant turn. If a tokenizer with a chat template is supplied, --chat-template auto uses it; otherwise the fallback serialization is explicit role-prefixed text. This keeps the raw data contract visible.
Memory governance
The local memory store now has namespaces, versioned artifacts, soft delete, rollback, atomic index writes, and an audit command:
ntkmirror memory add --store store --namespace alice --id style --train style.jsonl --overwrite
ntkmirror memory delete --store store --namespace alice --id style --soft
ntkmirror memory rollback --store store --namespace alice --id style --version 1
ntkmirror memory audit --store store --namespace alice --out audit.jsonSearch excludes soft-deleted rows and defaults to a positive retrieval threshold. A no-hit query falls back to the base model in generation/eval paths instead of attaching an arbitrary zero-score controller.
Composition planning
Before composing multiple controllers, run:
ntkmirror compose-plan --controllers a.pt b.pt c.pt --out plan.jsonThe plan reports gate overlap, opposing-sign overlap, negative gate-space cosine, and preclip saturation. These are mechanical risk signals only; they do not prove behavioral compatibility.
Explicitly out of scope for this patch
This patch does not add a full LoRA/PEFT baseline runner, a vector database/RAG stack, a safetensors artifact migration, a signed controller registry, true heterogeneous per-row serving, or a GPU benchmark. Those remain separate v2 workstreams. The runtime and artifact APIs are designed so those pieces can be added without changing controller state semantics.