Foundry training suite: gated specialist-model pipeline + THE FOUNDRY panel section - #41
Conversation
… THE FOUNDRY panel section Executable half of docs/THEORC_FOUNDRY.md, starting the F-1 -> F-2 path for theorc-toolcaller (the docs-designated first proof): - training_pit/foundry/configs/: one recipe per specialist track in the follow-on order. Toolcaller is ACTIVE (Qwen2.5-1.5B-Instruct bf16 LoRA); dataset-judge/fabric/router/reviewer/boss-v2 are status=template and refuse to train until their baseline evidence exists. - export_toolcaller_dataset.py: ToolcallerBench-gated capture -> chat-JSONL export with lineage-safe deterministic split, decision-balance report, and a provenance meta sidecar (hashes, validator verdict, lineage groups). - foundry_preflight.py: blocks training on template tracks, missing/undersized datasets, pending-review captures, validator non-PASS, lineage/exact-call leakage across splits, and frozen-inventory drift. - train_foundry.py: config-driven LoRA/QLoRA trainer reusing the boss trainer's progress.json/checkpoints/summary contract; real runs require --confirm-experiment and freeze an immutable run_manifest.json (F-1 deliverable #9). - THE FOUNDRY expander in TrainingPitPanel: track list read from the configs, staged/accepted/exported capture counts, validate/export/train actions (dry-run default), heartbeat progress, GPU-collision guards both directions. - Fix: canonical frozen-inventory hash is the LF form; ToolcallerBench and the new scripts now LF-normalize before hashing so core.autocrlf checkouts do not reject every capture as stale-hash. - Reference captures (one per decision type) + suite README + Training Pit guide section. Verified: exporter end-to-end on the reference captures (validator PASS 4/4), all six configs gate as designed, Avalonia build clean, 10/10 headless panel tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds a Foundry specialist-model training suite: new panel UI, GPU gating, export/preflight/training CLIs, frozen-tool hash normalization, track configs, example captures, and related docs. ChangesFoundry specialist training feature
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
🤖 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 `@OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs`:
- Line 701: The GPU exclusivity guard is only applied in some entry points, so
other actions can still start while Foundry is running. Update the remaining
start/entry guards in TrainingPitPanel (the harvest/review/capture paths
alongside the existing forge/generator checks) to also consult FoundryRunning
and return early through OnActivity with the same refusal behavior used
elsewhere.
- Around line 1584-1588: The Foundry run setup in TrainingPitPanel should also
clear any stale training summary before starting a new run, since FoundryDone()
can later refresh from an old training_summary.json and overwrite a newly
blocked/failed state. Update the same startup block that already resets
FoundryProgressPath to also delete the existing summary file used by
RefreshFoundry/FoundryDone, so each run begins with a clean state.
In `@training_pit/foundry/scripts/export_toolcaller_dataset.py`:
- Around line 213-214: The output setup in export_toolcaller_dataset.py only
creates out_dir, so a fresh checkout can still fail when writing or opening the
train_*.jsonl files because training_pit/datasets may not exist. Update the
dataset export flow around the directory setup used by the train/dev/test
writing paths to also create DATASETS_DIR before any file I/O, and make sure the
same initialization is applied in the related export steps that write those
JSONL outputs.
- Around line 133-134: The validation copy step in the export flow is
overwriting same-named captures because it uses tmp / f.name, so
repeated/default source directories can collapse into one file. Update the copy
logic in the dataset export routine that iterates over files to preserve every
selected capture by writing each source to a unique destination name or
subdirectory, and ensure the validation pass in ToolcallerBench sees all
selected captures rather than only the last duplicate.
In `@training_pit/foundry/scripts/foundry_preflight.py`:
- Around line 85-88: The format validation in the preflight flow only checks
that messages is a list with at least two entries, so malformed items can still
reach _answers() and crash leakage scanning. Tighten the validation in
foundry_preflight.py by checking each message object’s shape before calling
_answers() or any leakage logic: ensure every element is a dict-like message,
and that assistant messages have string content. Use the existing row scan
around the train/eval loop and the _answers() helper as the key places to block
malformed rows with FORMAT findings.
- Around line 30-32: The preflight validation in foundry_preflight.py is missing
the shared job config contract, so configs without job still pass and later fail
in train_foundry.py when cfg["job"]["name"] is used. Update the required key
check in the preflight loader to include job, or add an immediate validation in
train_foundry.py right after loading the config, using the existing cfg
validation flow so missing job is rejected before manifest or summary writing.
- Around line 91-107: In foundry_preflight.py, the meta-driven gate checks in
the meta handling block are currently skipped when meta_path is missing, which
lets active tracks bypass required validations. Update the meta loading flow so
that missing metadata becomes a blocking finding whenever reject_pending_review,
require_validator_pass, lineage checks, or a pinned tool_schema_hash are
enabled, and make the existing validator/review/schema logic in the meta branch
explicitly fail when those fields are absent or mismatched. Use the meta, gates,
ds.get("meta_path"), and the later schema/lineage checks in the same preflight
function as the main places to adjust.
In `@training_pit/foundry/scripts/train_foundry.py`:
- Around line 161-162: The CUDA availability guard in train_foundry.py currently
uses an assert, which can be skipped under optimized Python runs. Replace that
assert in the training setup with an explicit runtime check in the same area and
fail fast with the same message, while also updating the heartbeat/logging path
used by the training flow so the status reflects that CUDA is unavailable before
any later training work begins.
- Around line 113-115: The skip-gates path in train_foundry.py currently
bypasses run_preflight(), which lets non-active/template tracks train when they
should still be blocked. Update the training flow around args.skip_gates so it
only skips the Arena/promotion gates, but still enforces the cfg["status"] ==
"active" check before any training starts. Use the existing run_preflight() and
args.skip_gates branching to keep the bypass non-bypassable for template-track
blocking.
- Around line 242-246: The resume handling in train_foundry.py should not
silently fall back to a fresh run when args.resume is set but no checkpoint
exists. Update the logic around ckpt_dir, resume, and trainer.train() so that
--resume with a missing/empty checkpoints directory raises an explicit error
instead of passing None to trainer.train. Keep the current checkpoint detection,
but gate it in the main training flow so the TrainFoundry script only resumes
through trainer.train(resume_from_checkpoint=...) when a valid checkpoint is
present.
- Around line 60-73: The manifest handling in train_foundry.py should keep
immutable fields enforced for both resume and dry-run paths. Update the logic
around manifest_path.exists(), prior_cmp, and the args.resume/args.dry_run
checks so --resume still rejects any config/dataset/base/seed mismatch, and so a
later dry-run cannot overwrite an existing real manifest. Preserve the existing
record by returning early in the dry-run-vs-real and resume cases where the
manifest should remain unchanged.
🪄 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: f457e512-c143-4c0a-a0b4-a0fce282f2eb
📒 Files selected for processing (19)
OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axamlOrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.csTools/ToolcallerBench/Program.csdocs/TRAINING_PIT_GUIDE.mdtraining_pit/foundry/README.mdtraining_pit/foundry/baselines/.gitkeeptraining_pit/foundry/configs/boss_v2.jsontraining_pit/foundry/configs/dataset_judge_v0.jsontraining_pit/foundry/configs/fabric_v0.jsontraining_pit/foundry/configs/reviewer_v0.jsontraining_pit/foundry/configs/router_v0.jsontraining_pit/foundry/configs/toolcaller_v0.jsontraining_pit/foundry/examples/toolcaller_capture_call_001.jsontraining_pit/foundry/examples/toolcaller_capture_clarify_001.jsontraining_pit/foundry/examples/toolcaller_capture_no_tool_001.jsontraining_pit/foundry/examples/toolcaller_capture_unsupported_001.jsontraining_pit/foundry/scripts/export_toolcaller_dataset.pytraining_pit/foundry/scripts/foundry_preflight.pytraining_pit/foundry/scripts/train_foundry.py
All 11 findings from the PR #41 review: - Complete Foundry GPU exclusivity: harvest start, review, capture-increment, and the gpuFree predicate now also check FoundryRunning. - Delete the stale training_summary.json when starting a Foundry run so FoundryDone() cannot repaint a blocked/failed status with an old summary. - Exporter: index-prefix temp copies so same-named captures from different source dirs are not collapsed before validation; create DATASETS_DIR before writing outputs. - Preflight: require the "job" config key; validate message-item shape (dict with string role/content) before the leakage scan; make a missing meta sidecar a blocking finding when gates depend on it; flag an unrecorded exported tool_schema_hash, not just a mismatched one. - Trainer: manifest immutability now also applies to --resume and prevents a dry-run from overwriting a real run record; --skip-gates can no longer train a template track (authorization is non-skippable); the CUDA assert is an explicit check with a heartbeat; --resume fails fast when no checkpoint exists instead of silently starting fresh over prior artifacts. Re-verified: exporter smoke PASS 4/4, preflight/template gates behave as designed, skip-gates-on-template blocked, Avalonia build clean, 10/10 headless panel tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
training_pit/foundry/scripts/foundry_preflight.py (1)
79-84: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winUnguarded
gates["min_train_examples"]/gates["min_eval_examples"]can crash preflight.
load_configonly validates that top-levelgatesexists (Line 30), not its sub-keys. If a recipe config'sgatesdict omitsmin_train_examples/min_eval_examples, this raises an unhandledKeyErrorinstead of producing a clean blocking finding — undermining the script's purpose of reporting gate failures gracefully (and breaking downstream callers like the panel/trainer that expect either a findings list or a cleanSystemExit).🛡️ Proposed fix
- if len(train_rows) < gates["min_train_examples"]: - findings.append(f"COUNT: train has {len(train_rows)} examples, gate requires " - f">= {gates['min_train_examples']}") - if len(eval_rows) < gates["min_eval_examples"]: - findings.append(f"COUNT: eval has {len(eval_rows)} examples, gate requires " - f">= {gates['min_eval_examples']}") + min_train = gates.get("min_train_examples") + min_eval = gates.get("min_eval_examples") + if min_train is None or min_eval is None: + findings.append("CONFIG: gates.min_train_examples / gates.min_eval_examples must be set") + else: + if len(train_rows) < min_train: + findings.append(f"COUNT: train has {len(train_rows)} examples, gate requires >= {min_train}") + if len(eval_rows) < min_eval: + findings.append(f"COUNT: eval has {len(eval_rows)} examples, gate requires >= {min_eval}")🤖 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 `@training_pit/foundry/scripts/foundry_preflight.py` around lines 79 - 84, The gate checks in foundry_preflight currently index `gates["min_train_examples"]` and `gates["min_eval_examples"]` directly, which can raise a `KeyError` if those sub-keys are missing. Update the preflight validation flow around `load_config` and the findings logic to safely handle absent gate thresholds by validating or defaulting those keys before use, and emit a blocking finding instead of crashing. Keep the fix localized to the gate-count checks so `findings` is still returned or `SystemExit` is raised cleanly.OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs (3)
1765-1784: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winLaunch failures in
RunProcessAsynccrash the async-void handlers.
Process.Start(psi)!is null-forgiving and the body has notry/catch. If the executable is missing (e.g.,python/toolcaller-benchnot on PATH),Process.Startthrows aWin32ExceptioninsideTask.Run; the awaitingBtnFoundryValidate_Click/BtnFoundryExport_Clickareasync void, so the exception is unobserved and can tear down the app. It also leavesBtnFoundryValidate/BtnFoundryExportpermanently disabled because the re-enable line is never reached.Proposed hardening
private static Task<(int Code, string Output)> RunProcessAsync(string fileName, string args) => Task.Run(async () => { var psi = new ProcessStartInfo { FileName = fileName, Arguments = args, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, }; - using var p = Process.Start(psi)!; + Process? p; + try { p = Process.Start(psi); } + catch (Exception ex) { return (-1, $"failed to launch '{fileName}': {ex.Message}"); } + if (p is null) return (-1, $"failed to launch '{fileName}'"); + using var _ = p; var stdout = p.StandardOutput.ReadToEndAsync(); var stderr = p.StandardError.ReadToEndAsync(); if (!p.WaitForExit(120_000)) { try { p.Kill(entireProcessTree: true); } catch { } return (-1, "process timed out after 120s"); } p.WaitForExit(); return (p.ExitCode, await stdout + await stderr); });🤖 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 `@OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs` around lines 1765 - 1784, RunProcessAsync can throw on Process.Start(psi) when the executable is missing, which bubbles out of the async-void click handlers and can leave BtnFoundryValidate/BtnFoundryExport disabled. Wrap the process launch and wait logic in try/catch inside RunProcessAsync, return a nonzero exit code plus the exception message on failure, and keep the existing timeout handling for the process tree. Use the RunProcessAsync helper and the BtnFoundryValidate_Click / BtnFoundryExport_Click call sites as the main references while ensuring the UI re-enable path is always reached.
1561-1561: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUse the shared Python resolution here too.
BtnFoundryExport_Clicklaunches the exporter with barepython, while the other Python entrypoints fall back toEnvironment.GetEnvironmentVariable("PYTHON") ?? "python3". On many Unix setups that makes export fail immediately and bubble out ofRunProcessAsync; route this through the same resolution for consistency.🤖 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 `@OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs` at line 1561, The exporter launch in BtnFoundryExport_Click still hardcodes bare python, unlike the other Python entrypoints that resolve the executable via Environment.GetEnvironmentVariable("PYTHON") ?? "python3". Update this call site to use the same shared Python resolution before invoking RunProcessAsync so the exporter runs consistently on Unix and other environments where python is unavailable.
1526-1535: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUse the OS-specific bench filename
ToolcallerBenchis anExewith assembly nametoolcaller-bench, so the built apphost istoolcaller-benchon Linux/macOS andtoolcaller-bench.exeon Windows. Hardcoding.exemakes validation report “not built” on non-Windows even after a successful build.🤖 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 `@OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs` around lines 1526 - 1535, Update the bench path detection in TrainingPitPanel’s validation logic so it uses the OS-specific apphost filename instead of hardcoding toolcaller-bench.exe. In the block that builds the bench candidate list and checks File.Exists, choose toolcaller-bench on non-Windows and toolcaller-bench.exe on Windows so the FoundryStatus check works after successful builds on Linux/macOS as well as Windows.
♻️ Duplicate comments (1)
training_pit/foundry/scripts/foundry_preflight.py (1)
121-126: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winMissing meta sub-fields silently bypass drift/lineage gates.
Same failure class as the previously flagged "missing metadata bypasses gates" issue, but now at the field level:
- Line 124:
if recorded and recorded != sha256_file(path):skips the DRIFT check entirely when meta lackstrain_sha256/eval_sha256— a dataset could silently drift undetected.- Line 142:
if meta and isinstance(meta.get("lineage_groups"), dict):skips the lineage-overlap check entirely whenrequire_lineage_split_isolationis enabled but meta doesn't carrylineage_groups— the gate is effectively unenforced without any finding.Both should treat an absent required sub-field as a blocking finding rather than a silent no-op, consistent with the
meta_requiredhandling added above for the top-levelmeta_path.🛡️ Proposed fix
for name, path in paths.items(): key = ("train_sha256" if name == "train_path" else "eval_sha256") recorded = meta.get(key) - if recorded and recorded != sha256_file(path): + if not recorded: + findings.append(f"META: {key} missing from sidecar — cannot verify {path.name} drift") + elif recorded != sha256_file(path): findings.append(f"DRIFT: {path.name} changed since export " f"(meta {key} mismatch) — re-run the exporter")if gates.get("require_lineage_split_isolation"): - if meta and isinstance(meta.get("lineage_groups"), dict): + if meta and isinstance(meta.get("lineage_groups"), dict): overlap = set(meta["lineage_groups"].get("train", [])) & \ set(meta["lineage_groups"].get("eval", [])) if overlap: findings.append(f"LEAKAGE: {len(overlap)} lineage group(s) present in both splits: " f"{sorted(overlap)[:5]}") + elif meta and not isinstance(meta.get("lineage_groups"), dict): + findings.append("META: lineage_groups missing/invalid in sidecar — cannot verify split isolation")Also applies to: 140-147
🤖 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 `@training_pit/foundry/scripts/foundry_preflight.py` around lines 121 - 126, The drift and lineage gates in foundry_preflight.py are being skipped when required meta sub-fields are missing, so update the checks around the sha256 loop and the lineage_groups validation to emit blocking findings when train_sha256, eval_sha256, or lineage_groups are absent under the relevant mode. In the path-handling logic inside the main preflight flow, make the recorded/meta lookup treat missing required sub-fields as failures rather than falsey no-ops, and keep the existing DRIFT and lineage-overlap messages as the basis for the new missing-field findings.
🤖 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.
Outside diff comments:
In `@OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs`:
- Around line 1765-1784: RunProcessAsync can throw on Process.Start(psi) when
the executable is missing, which bubbles out of the async-void click handlers
and can leave BtnFoundryValidate/BtnFoundryExport disabled. Wrap the process
launch and wait logic in try/catch inside RunProcessAsync, return a nonzero exit
code plus the exception message on failure, and keep the existing timeout
handling for the process tree. Use the RunProcessAsync helper and the
BtnFoundryValidate_Click / BtnFoundryExport_Click call sites as the main
references while ensuring the UI re-enable path is always reached.
- Line 1561: The exporter launch in BtnFoundryExport_Click still hardcodes bare
python, unlike the other Python entrypoints that resolve the executable via
Environment.GetEnvironmentVariable("PYTHON") ?? "python3". Update this call site
to use the same shared Python resolution before invoking RunProcessAsync so the
exporter runs consistently on Unix and other environments where python is
unavailable.
- Around line 1526-1535: Update the bench path detection in TrainingPitPanel’s
validation logic so it uses the OS-specific apphost filename instead of
hardcoding toolcaller-bench.exe. In the block that builds the bench candidate
list and checks File.Exists, choose toolcaller-bench on non-Windows and
toolcaller-bench.exe on Windows so the FoundryStatus check works after
successful builds on Linux/macOS as well as Windows.
In `@training_pit/foundry/scripts/foundry_preflight.py`:
- Around line 79-84: The gate checks in foundry_preflight currently index
`gates["min_train_examples"]` and `gates["min_eval_examples"]` directly, which
can raise a `KeyError` if those sub-keys are missing. Update the preflight
validation flow around `load_config` and the findings logic to safely handle
absent gate thresholds by validating or defaulting those keys before use, and
emit a blocking finding instead of crashing. Keep the fix localized to the
gate-count checks so `findings` is still returned or `SystemExit` is raised
cleanly.
---
Duplicate comments:
In `@training_pit/foundry/scripts/foundry_preflight.py`:
- Around line 121-126: The drift and lineage gates in foundry_preflight.py are
being skipped when required meta sub-fields are missing, so update the checks
around the sha256 loop and the lineage_groups validation to emit blocking
findings when train_sha256, eval_sha256, or lineage_groups are absent under the
relevant mode. In the path-handling logic inside the main preflight flow, make
the recorded/meta lookup treat missing required sub-fields as failures rather
than falsey no-ops, and keep the existing DRIFT and lineage-overlap messages as
the basis for the new missing-field findings.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 934953c3-abf5-4a26-8b7a-67e47681b7e7
📒 Files selected for processing (4)
OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cstraining_pit/foundry/scripts/export_toolcaller_dataset.pytraining_pit/foundry/scripts/foundry_preflight.pytraining_pit/foundry/scripts/train_foundry.py
🚧 Files skipped from review as they are similar to previous changes (1)
- training_pit/foundry/scripts/export_toolcaller_dataset.py
…diagnostics - Validate/Export buttons now work on Linux/macOS: platform-aware apphost name (toolcaller-bench vs .exe), python3 fallback via the PYTHON env var, and RunProcessAsync sets WorkingDirectory = _pitRoot so toolcaller-bench reports land under the pit root instead of the app-launch directory. Process.Start failures now return an error result instead of escaping the async void handler and leaving the Export button permanently disabled. - Exporter split assignment is now two-pass: only genuinely conflicting explicit split annotations within a lineage group abort (with a message that names both values); unannotated siblings inherit the group''s explicit split instead of getting an independent hash split that could straddle the boundary. Note the mechanical validator already hard-fails partially annotated groups, so this path matters as defense-in-depth for --skip-validator exports. - Corrected the no-captures guidance: ToolcallerDatasetCaptureEnabled is off by default (opt-in), matching AppSettings and the Foundry README. - Exporter bench candidates include the extensionless non-Windows apphost. Verified: inherit/conflict/regression split paths, validator PASS 4/4 on the reference captures, Avalonia build clean, 10/10 headless panel tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What
The executable half of
docs/THEORC_FOUNDRY.md— begins the F-1 → F-2 path fortheorc-toolcaller, the docs-designated first proof model, and scaffolds the other five specialist tracks as gated templates.New Training Pit section:
training_pit/foundry/active(Qwen2.5-1.5B-Instruct bf16 LoRA); dataset-judge / fabric / router / reviewer / boss-v2 aretemplateand their gates refuse to train until each track''s baseline evidence exists.export_toolcaller_dataset.py— ToolcallerBench-gated capture → chat-JSONL export with a lineage-safe deterministic split, decision-balance report, and provenance meta sidecar.foundry_preflight.py— blocks training on template tracks, missing/undersized datasets, pending-review captures, validator non-PASS, lineage/exact-call leakage across splits, and frozen-inventory drift.train_foundry.py— config-driven LoRA/QLoRA trainer reusing the boss trainer''sprogress.json/checkpoints/summary contract. Real runs require--confirm-experiment(one explicit approval per experiment) and freeze an immutablerun_manifest.json(F-1 deliverable arch: establish hard boundary discipline — tool policy abstraction, swarm staging, fake backend, single roadmap source of truth #9).UI
Bug fix
c456ca…) is the LF/git-blob form, but ToolcallerBench hashed raw disk bytes — on acore.autocrlfcheckout it rejected every capture as stale-hash. The bench and the new scripts now LF-normalize before hashing.Governance
No governance change: no automatic promotion, deterministic validation before any model judge, baseline report remains a hard kill gate for promotion (loud warning before a first experiment). A no-training outcome stays an acceptable Foundry result.
Verified
🤖 Generated with Claude Code
Summary by CodeRabbit