Skip to content

Foundry training suite: gated specialist-model pipeline + THE FOUNDRY panel section - #41

Merged
hardcoreerik merged 3 commits into
masterfrom
feat/foundry-training-suite
Jul 8, 2026
Merged

Foundry training suite: gated specialist-model pipeline + THE FOUNDRY panel section#41
hardcoreerik merged 3 commits into
masterfrom
feat/foundry-training-suite

Conversation

@hardcoreerik

@hardcoreerik hardcoreerik commented Jul 7, 2026

Copy link
Copy Markdown
Owner

What

The executable half of docs/THEORC_FOUNDRY.md — begins the F-1 → F-2 path for theorc-toolcaller, the docs-designated first proof model, and scaffolds the other five specialist tracks as gated templates.

New Training Pit section: training_pit/foundry/

  • Six recipe configs in the follow-on order. Only toolcaller is active (Qwen2.5-1.5B-Instruct bf16 LoRA); dataset-judge / fabric / router / reviewer / boss-v2 are template and 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''s progress.json/checkpoints/summary contract. Real runs require --confirm-experiment (one explicit approval per experiment) and freeze an immutable run_manifest.json (F-1 deliverable arch: establish hard boundary discipline — tool policy abstraction, swarm staging, fake backend, single roadmap source of truth #9).

UI

  • ⚒ THE FOUNDRY expander in the Training Pit panel: track list read from the configs, staged/accepted/exported capture counts, validate / export / train actions (dry-run default), heartbeat progress bar, GPU-collision guards in both directions against Forge/Generator/Harvest.

Bug fix

  • The canonical frozen-inventory hash (c456ca…) is the LF/git-blob form, but ToolcallerBench hashed raw disk bytes — on a core.autocrlf checkout 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

  • Exporter end-to-end on the reference captures: validator PASS 4/4, splits honored, balance warning fires.
  • All six configs gate exactly as designed (templates blocked with docs-derived reasons; toolcaller blocked only on "dataset not exported yet").
  • Avalonia build clean (0 warnings), 10/10 headless panel-construction tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a new “Foundry — Specialist Models” section to the Training Pit, including live counts/status, validate/export/train controls, and stop/progress updates.
    • Extended GPU availability gating to cover Foundry start/review/capture actions.
    • Added Foundry recipe templates and curated example captures, plus config-driven preflight, dataset export, and a dedicated training launcher.
  • Bug Fixes
    • Prevented stale frozen-tool inventory mismatches by normalizing line endings during hashing.
  • Documentation
    • Updated the Training Pit guide with Foundry workflow/gating details and added a comprehensive Foundry README.

… 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>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bc033745-78fe-4309-bf6a-e8fa40383b89

📥 Commits

Reviewing files that changed from the base of the PR and between 47efc30 and 6a9a03e.

📒 Files selected for processing (2)
  • OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs
  • training_pit/foundry/scripts/export_toolcaller_dataset.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • training_pit/foundry/scripts/export_toolcaller_dataset.py
  • OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs

📝 Walkthrough

Walkthrough

Adds 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.

Changes

Foundry specialist training feature

Layer / File(s) Summary
Foundry panel UI layout
OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml
Adds a new Foundry expander and shifts the queue sections down in the grid.
GPU exclusivity guards
OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs
Extends GPU-busy checks so Foundry blocks harvest, Forge, generator, review, and capture actions.
Foundry panel state and handlers
OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs
Adds Foundry track state, refresh logic, UI backing type, process execution, and validate/export/train/stop/progress handlers.
Toolcaller dataset export
Tools/ToolcallerBench/Program.cs, training_pit/foundry/scripts/export_toolcaller_dataset.py
Normalizes tool hashing and exports accepted captures into chat-formatted train/eval datasets with provenance metadata.
Foundry preflight and training launcher
training_pit/foundry/scripts/foundry_preflight.py, training_pit/foundry/scripts/train_foundry.py
Validates configs and datasets, enforces gates and manifests, launches training, and records progress and summaries.
Foundry configs, examples, and docs
training_pit/foundry/configs/*.json, training_pit/foundry/examples/*.json, docs/TRAINING_PIT_GUIDE.md, training_pit/foundry/README.md
Adds recipe templates, example capture fixtures, and Foundry workflow documentation.

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

Possibly related PRs

  • hardcoreerik/TheOrc#34: Adds the shared ToolcallerBench and toolcaller dataset flow that this PR extends with hash normalization, export, and Foundry training orchestration.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a gated Foundry specialist-model pipeline plus a new Training Pit panel section.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 feat/foundry-training-suite

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4205f6f and 09f490a.

📒 Files selected for processing (19)
  • OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml
  • OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs
  • Tools/ToolcallerBench/Program.cs
  • docs/TRAINING_PIT_GUIDE.md
  • training_pit/foundry/README.md
  • training_pit/foundry/baselines/.gitkeep
  • training_pit/foundry/configs/boss_v2.json
  • training_pit/foundry/configs/dataset_judge_v0.json
  • training_pit/foundry/configs/fabric_v0.json
  • training_pit/foundry/configs/reviewer_v0.json
  • training_pit/foundry/configs/router_v0.json
  • training_pit/foundry/configs/toolcaller_v0.json
  • training_pit/foundry/examples/toolcaller_capture_call_001.json
  • training_pit/foundry/examples/toolcaller_capture_clarify_001.json
  • training_pit/foundry/examples/toolcaller_capture_no_tool_001.json
  • training_pit/foundry/examples/toolcaller_capture_unsupported_001.json
  • training_pit/foundry/scripts/export_toolcaller_dataset.py
  • training_pit/foundry/scripts/foundry_preflight.py
  • training_pit/foundry/scripts/train_foundry.py

Comment thread OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs
Comment thread OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs
Comment thread training_pit/foundry/scripts/export_toolcaller_dataset.py Outdated
Comment thread training_pit/foundry/scripts/export_toolcaller_dataset.py
Comment thread training_pit/foundry/scripts/foundry_preflight.py Outdated
Comment thread training_pit/foundry/scripts/foundry_preflight.py
Comment thread training_pit/foundry/scripts/train_foundry.py
Comment thread training_pit/foundry/scripts/train_foundry.py
Comment thread training_pit/foundry/scripts/train_foundry.py Outdated
Comment thread training_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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 win

Unguarded gates["min_train_examples"]/gates["min_eval_examples"] can crash preflight.

load_config only validates that top-level gates exists (Line 30), not its sub-keys. If a recipe config's gates dict omits min_train_examples/min_eval_examples, this raises an unhandled KeyError instead 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 clean SystemExit).

🛡️ 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 win

Launch failures in RunProcessAsync crash the async-void handlers.

Process.Start(psi)! is null-forgiving and the body has no try/catch. If the executable is missing (e.g., python/toolcaller-bench not on PATH), Process.Start throws a Win32Exception inside Task.Run; the awaiting BtnFoundryValidate_Click / BtnFoundryExport_Click are async void, so the exception is unobserved and can tear down the app. It also leaves BtnFoundryValidate/BtnFoundryExport permanently 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 win

Use the shared Python resolution here too. BtnFoundryExport_Click launches the exporter with bare python, while the other Python entrypoints fall back to Environment.GetEnvironmentVariable("PYTHON") ?? "python3". On many Unix setups that makes export fail immediately and bubble out of RunProcessAsync; 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 win

Use the OS-specific bench filename
ToolcallerBench is an Exe with assembly name toolcaller-bench, so the built apphost is toolcaller-bench on Linux/macOS and toolcaller-bench.exe on Windows. Hardcoding .exe makes 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 win

Missing 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 lacks train_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 when require_lineage_split_isolation is enabled but meta doesn't carry lineage_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_required handling added above for the top-level meta_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

📥 Commits

Reviewing files that changed from the base of the PR and between 09f490a and 47efc30.

📒 Files selected for processing (4)
  • OrchestratorIDE.Avalonia/UI/Panels/TrainingPitPanel.axaml.cs
  • training_pit/foundry/scripts/export_toolcaller_dataset.py
  • training_pit/foundry/scripts/foundry_preflight.py
  • training_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>
@hardcoreerik
hardcoreerik merged commit 25a2964 into master Jul 8, 2026
2 checks passed
@hardcoreerik
hardcoreerik deleted the feat/foundry-training-suite branch July 18, 2026 02:23
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.

1 participant