Bash SFT note 02: 3,000 executed examples, four arms, and a fair-base correction
21 August 2026 · cumulative Session 2 report · corrected 21 August 2026 · research note, not a paper · follows note 01
Session 1 taught us that lower imitation loss can coexist with zero real-task wins. Session 2 replaced “exit code 0 means verified” with synthetic fixtures, exact outputs, and filesystem snapshots. The original prompt explicitly required exactly one raw command with no prose or Markdown fence, but its strict 0/98 stock score still conflated adherence to that form with Bash semantics; it supplied no format demonstration and did not disclose the judge’s exact command-policy allowlist and restrictions. We corrected the comparison: with the same contract-calibrated prompt, stock passed 8/98 and Arm 2 passed 89/98. The raw-format confound is gone; the large in-domain gap and the failed external canary both remain.
What improved. In the original run, Arm 2 emitted one policy-safe Bash command on all 98 prompts, executed all 98, mutated no fixture, and returned the required result on 88. In the corrected contract-calibrated generation it passed 89/98. This is strong execution-backed in-domain performance, not another token-overlap bump.
Correction · fair base-model comparison. The original 0/98 stock score was response-contract-confounded. A symmetric whole-response Markdown adapter recovered 9/98 from the original stock completions versus 88/98 for Arm 2. A stronger check regenerated both pinned models with an identical prompt that disclosed the exact output format and command policy: stock passed 8/98 and Arm 2 passed 89/98, a paired +82.7 percentage-point gap (95% component-bootstrap interval +74.5 to +89.8). The base was treated unfairly before; correcting that mistake does not erase the substantive gap.
Claim boundary. Train and test components are disjoint, but both came from the same GFR generator and share family templates. The 98 tasks were also used to choose among four arms, so they are now a development/selection set—not an untouched final test. The selected model failed the post-selection external canary. We have not established broad Bash transfer or a win over distil-lfm25-shellper.
Everything since Session 1
Session 1: imitation moved; real probes did not.
The 4,000-row response-only SFT lowered teacher-forced loss to 0.725 and raised exact Bash imitation to 7.25%, yet the AWS and local file-search probes remained 0/3. The source mix contained false “verification,” mismatched single tool calls, and randomly split paraphrases.
20 August · baseline lessonSession 2 pilot: a better method smoke, still an imitation score.
We built 164 cleaner rows, split 137 train / 27 validation by held-out families, and trained one stock-to-SFT arm for 4.51 seconds. On the first 500 historical validation rows, exact Bash moved 61/500 to 70/500. Full-text F1 jumped mostly because the model copied a fixed wrapper.
20–21 August · published as Bash SFT note 01Latest completed run: 3,000 execution-backed rows and four controlled arms.
The batch is described as trace-inspired and uses privacy-screened synthetic fixtures; the public evidence does not map rows back to source archetypes. We executed every final row version on macOS, grouped related variants before splitting, trained four recipes from the same pinned 1.5B base revision, and selected on locked execution.
21 August · this note’s main resultExternal transfer alarm: the winner still failed “current directory.”
A historical handoff-search task was reconstructed only after model selection. All six compared models failed. The winning arm treated the phrase “current directory” as a literal directory and omitted the required NUL-safe, newest-first long listing.
21 August · qualitative canary, n=1Timed follow-up: no result claimed.
The operator record says four transfer-repair arms entered provisioning, the fixed one-hour clock expired before training, and the four temporary GPUs were deleted. No durable training log or checkpoint exists, so this produced no candidate and no score.
21 August · operator-recorded operational failure, not a model experimentFair-base correction: normalize symmetrically, then regenerate identically.
First we applied one deterministic whole-response Markdown adapter to stock, prior S2, and Arm 2. Then we independently regenerated pinned stock and Arm 2 with the same contract-calibrated prompt on separate RTX 6000 Ada GPUs. Stock moved from a confounded strict 0/98 to 9/98 after adaptation and 8/98 after calibrated generation; Arm 2 scored 88/98 and 89/98 respectively.
21 August · correction added; both temporary GPUs deleted after artifacts were pulledDo not merge the two Session 2 tests
The earlier 500-row result and the latest 98-task result answer different questions. Their percentages are not comparable.
| Test | Rows | Primary measure | Stock | Selected SFT | What it establishes |
|---|---|---|---|---|---|
| Historical imitation slice | 500 | Exact gold Bash text | 61/500 12.2% | 70/500 14.0% | A small copy/format shift; no command execution |
| GFR locked execution | 98 components | Correct exit/stdout/stderr + no mutation | 0 strict* 9 adapted 8 calibrated | 88 strict/adapted 89 calibrated | Strong fit to the generator’s held-out task distribution; the corrected comparison removes raw-format ignorance |
| Historical handoff canary | 1 | Semantic file result + long listing | 0/1 | 0/1 | Transfer remains unproven; qualitative alarm only |
*The original strict stock score is retained as an audit trail, not as the fair headline comparison. See the dated correction below.
The 137-row pilot, for completeness
| Imitation metric, n=500 | Stock | 137-row SFT | Delta |
|---|---|---|---|
| Exact Bash | 12.2% | 14.0% | +1.8 pp |
| Command stem | 50.4% | 56.8% | +6.4 pp |
| First tool | 86.4% | 91.6% | +5.2 pp |
| Bash-token F1 | 41.5% | 43.8% | +2.4 pp |
| Full-text F1 | 41.4% | 66.3% | +24.9 pp wrapper effect |
| Cloud exact, n=9 | 0.0% | 0.0% | no change |
The data gate changed
The project describes the 3,000 GFR candidates as trace-inspired synthetic examples, not copies of private sessions, but the public evidence has no source-hash or archetype-to-row mapping to audit that claim independently. We first distilled 16 execution-backed seed examples plus explicit generation constraints. The released prompts, paths, files, expected outputs, and decoys were synthetic and privacy-screened. Candidates stayed quarantined until a separate program could prove their results.
- Execution first: 3,000/3,000 final rows passed expected exit code, stdout, stderr, and before/after filesystem immutability on macOS Bash 3.2.
- Repairs disclosed: 10 rows had fixture or wording corrections applied before the recorded promotion execution. They were not silently relabeled.
- Grouped split: split groups connected by identical normalized commands or fixture setups stayed together. Exact normalized-command overlap among train, validation, and test was zero. We did not complete a semantic-near-duplicate or source-time leakage audit.
- Release: 2,402 train / 297 validation / 301 test rows across 998 connected components. The locked score uses one deterministic representative from each of 98 test components.
- Scope: 20 families across
find,rg,jq, andsed. This is natural-language file search and retrieval, not all of Bash.
Evaluator security limit. The managed macOS host could not apply its sandbox-exec profile. The judge instead used a fail-closed lexical policy, restricted PATH, resource limits, timeouts, and full fixture snapshots. That is useful for these audited read-only commands, not a security boundary for hostile shell input.
Fixture-blind process, imperfect artifact hygiene. Generation code opened only the prompts-only JSONL and rejected fixture-bearing records. However, the broader upload left the locked gold file elsewhere on each remote filesystem. There is no evidence the process read it, but future runs should never upload gold or fixtures to generation hosts.
Four arms from the same pinned base
All arms started from the same stock Qwen/Qwen2.5-Coder-1.5B-Instruct revision. Training was response-only full-parameter bf16 SFT, batch 4, gradient accumulation 4, max length 1,024, 10 warmup steps, seed 42. The three full-data arms used 297 validation rows; the representative arm used 98. Generation was greedy.
| Recipe | Rows | Epochs | LR | Train wall | Eval loss | Passes | pass@1 |
|---|---|---|---|---|---|---|---|
| Arm 1 · full | 2,402 | 1 | 2e-5 | 60.3 s | 0.0262 | 84/98 | 85.7% |
| Arm 2 · full | 2,402 | 2 | 2e-5 | 113.7 s | 0.0213 | 88/98 | 89.8% |
| Arm 3 · lower LR | 2,402 | 1 | 1e-5 | 61.7 s | 0.0415 | 79/98 | 80.6% |
| Arm 4 · representatives | 802 | 1 | 2e-5 | 20.2 s | 0.0544 | 71/98 | 72.4% |
Arm 2 minus Arm 1 is +4.1 percentage points; paired component-bootstrap 95% interval 0.0 to +9.2 pp (10,000 resamples). Two epochs is the best observed recipe, not a settled rule. Arm 4 changes row count and update count together, so its loss cannot isolate a causal “variants help” effect.
All trained GFR arms learned substantial in-domain behavior. The two-epoch full-data recipe was best observed; its interval versus Arm 1 touches zero.
Correction: a fair base-model comparison
Why this correction exists. The original base prompt did state “exactly one raw command,” “no prose,” and “no Markdown fence.” Stock failed that explicit instruction. But the prompt gave no format demonstration and did not disclose the judge’s exact command-policy allowlist and restrictions, while Arm 2 had repeatedly seen the target response shape during SFT. Treating every format miss as a semantic miss therefore gave the trained model an avoidable calibration advantage.
Original strict score: retained, but no longer the headline comparison
| Original strict run, same 98 prompts | Stock Qwen | Previous S2 | Arm 2 |
|---|---|---|---|
| Raw one-line contract | 0/98 | 46/98 | 98/98 |
| Policy accepted | 0/98 | 0/98 | 98/98 |
| Syntax valid | not executed | not executed | 98/98 |
| Execution pass | 0/98 | 0/98 | 88/98 |
| Exact gold command | 0/98 | 0/98 | 80/98 |
| Filesystem unchanged | 98/98* | 98/98* | 98/98 |
*No command was executed for the gated models, so “unchanged” is vacuous. Stock wrapped all replies in fences or prose despite a system instruction requiring one raw command. Previous S2 produced 46 one-line replies, but its generated shell constructs failed the read-only policy.
Check 1: symmetric whole-response Markdown adaptation
We re-scored the original completions with the same deterministic adapter for every model. It accepts only a whole response that is already a single command or a single Markdown-fenced command; it does not select a promising snippet from prose or rewrite shell. The extracted command then faces the unchanged policy, syntax, execution, output, and immutability checks.
| Original completions, symmetric adapter | Stock Qwen | Previous S2 | Arm 2 |
|---|---|---|---|
| Original raw one-line contract | 0/98 | 46/98 | 98/98 |
| Whole-response command accepted | 98/98 | 97/98 | 98/98 |
| Policy, syntax, and execution accepted | 69/98 | 84/98 | 98/98 |
| Execution pass | 9/98 9.2% | 10/98 10.2% | 88/98 89.8% |
Arm 2 minus stock is +80.6 percentage points; paired component-bootstrap 95% interval +71.4 to +88.8 pp, 10,000 resamples with seed 42. The adapter fixes Markdown presentation, not command semantics.
Check 2: identical contract-calibrated generation
This is the stronger correction. Pinned stock and the pinned Arm 2 checkpoint received byte-identical effective prompts that restated the exact one-command output format and disclosed the evaluator’s allowed-command policy. The shared context included one neutral no-op user→assistant format demonstration whose assistant answer was true; it taught response shape without demonstrating a file-retrieval solution. Each model generated on a separate NVIDIA RTX 6000 Ada Generation GPU with greedy decoding and seed 42. The same judge then scored both. No adapter was needed.
| Contract-calibrated generation, same 98 tasks | Stock Qwen | Arm 2 |
|---|---|---|
| Raw one-line contract | 98/98 | 98/98 |
| Policy accepted | 86/98 | 98/98 |
| Syntax valid and executed | 85/98 | 98/98 |
| Execution pass | 8/98 8.2% | 89/98 90.8% |
| Exact gold command | 0/98 | 80/98 |
Arm 2 minus stock is +82.7 percentage points; paired component-bootstrap 95% interval +74.5 to +89.8 pp, 10,000 resamples with seed 42. The two temporary generation droplets were deleted after the aggregate artifacts were pulled.
Corrected conclusion. Stock is not a 0% semantic baseline. It passes roughly 8–9% after the format disadvantage is removed. Arm 2 still passes roughly 90% under both fair checks. The raw-format confound is removed; an approximately 81–83 point in-domain execution gap remains. Because this is still a within-generator selection set, it does not establish broad Bash transfer.
What the winner can and cannot do
Arm 2 passed 14 of 20 small held-out families perfectly. It produced 98/98 one-line, policy-safe, syntax-valid commands and made 0 mutations. Ten semantic failures remained: four exit-code mismatches and six stdout mismatches; no timeouts.
| Weak held-out family | Pass rate | Observed misses |
|---|---|---|
jq_json_filter | 2/4 · 50% | one stdout mismatch, one exit mismatch |
rg_files_candidates | 3/6 · 50% | one stdout mismatch, two exit mismatches |
rg_regex_hidden_exclude | 2/4 · 50% | two stdout mismatches |
find_size_threshold | 3/4 · 75% | one stdout mismatch |
find_directories | 4/5 · 80% | one stdout mismatch |
rg_count | 4/5 · 80% | one exit mismatch |
The post-selection canary
The locked historical task asks for handoff-like files under the current directory, prunes dependency/build/release trees, handles spaces, and returns a NUL-safe newest-first long listing. Its key vocabulary appears zero times in GFR train, validation, or locked test. Stock, previous S2, and all four GFR arms scored 0/1.
Arm 2 generated:
find 'current directory' \( -type d \( -name 'node_modules' -o -name 'dist' -o -name 'release-*' \) -prune \) -o -type f \( -iname '*handoff*' -o -iname '*hand-off*' -o -iname '*handover*' \) -print | sort -rn
It recovered all three name patterns and much of the pruning intent. It also literalized “current directory,” omitted NUL-safe filename handling, and sorted path text rather than long-listing files by modification time. That is exactly the compositional boundary the next training set should target.
What Session 2 taught us
1. Execute the outcome; do not bless the process
Exit status zero is not task verification. The old corpus often proved only that a command ran. The new promotion gate checks exact results and forbidden changes. This changed model selection from “looks like gold” to “returned the requested files.”
2. Split semantic relatives together
Random row splits leak paraphrases and fixture variants. GFR joins rows by declared split group, normalized command, and fixture setup before assigning components. That removes exact command crossover. It still does not remove generator-family similarity, so an external suite remains mandatory.
3. The response contract is learnable—and can confound a benchmark
The original strict run made stock look like a semantic 0/98 because it fenced every answer. Once we adapted Markdown symmetrically, stock passed 9/98. Once we disclosed the exact interface identically, stock emitted raw commands 98/98 and passed 8/98. Contract adherence still matters for direct execution, but it must be reported separately from command semantics.
4. More rows and more updates are confounded in our representative arm
The 802-row Arm 4 lost to the 2,402-row Arm 1 by 13.3 points (95% interval −21.4 to −6.1). Because Arm 4 also received one third as many examples and updates, this experiment does not prove which variants mattered. A causal repetition test should equalize optimizer steps or sampled tokens.
5. In-domain execution is necessary, not sufficient
89.8% on a frozen execution set is meaningful. The 0/1 external canary is also meaningful. The pair says “the pipeline can teach a narrow capability” and “we have not yet shown it transfers.” Publishing only either number would distort the result.
6. Exact command match is not the task
Arm 2 exactly reproduced 80 gold commands but passed 88 fixtures. Eight semantically correct alternatives would be marked wrong by string matching. Exact text remains a useful diagnostic; executable outcomes must select the model.
7. Freeze external canaries before generating repairs
The historical canary was reconstructed after arm selection, which makes it a useful alarm but only one data point. The next run needs 20 or more generator-independent tasks frozen first, covering deictic roots, compound constraints, safe filenames, metadata, and ordering.
8. Reproducibility includes operational failures
The winning 2.9 GB checkpoint was hashed locally and remotely before cleanup. All completed-run GPU droplets were deleted. The later four-arm repair attempt spent its remaining clock on transfer/provisioning and trained nothing; those four GPUs were also deleted. The fair comparison used two additional temporary GPUs, one per pinned model, and both were deleted after artifact pull. Prebuilt environments and an explicit stop-new-work cutoff would recover that time.
9. The evaluator is not yet a hostile-shell sandbox
Lexical rejection plus snapshots caught the read-only task class used here, but it should not be exposed to arbitrary model output. Add an AST-aware command policy or OS/container boundary before production evaluation.
The next path forward
- Freeze at least 20 real-use, generator-independent execution tasks before creating new training rows. Keep the exact historical canary out of training.
- Build unrelated repair examples for “here,” “this folder,” and “current repo,” plus commands that compose pruning, multiple patterns, spaces/NUL safety, metadata, and time ordering.
- Compare a fresh stock-to-original-plus-repair arm with a low-learning-rate continuation from Arm 2. Use the external suite for selection and the 98-task GFR set only as a regression gate.
- Retain all three audit views: original strict product behavior, symmetric whole-response adaptation, and identical contract-calibrated generation. Never present the strict 0/98 alone as stock semantics.
- For a
distil-lfm25-shellpercomparison, reproduce its public benchmark contract separately. Tool-routing accuracy is not interchangeable with direct natural-language-to-Bash execution. - Equalize optimizer steps when testing whether repeated fixture/paraphrase variants add information.
Next success criterion: pass previously failed external tasks without losing the winner’s 100% raw-command, policy, syntax, and immutability rates—not merely raise the GFR aggregate.
Evidence and reproducibility
Public packet: ./bash-sft-02/. It contains aggregate metrics, the sanitized data manifest, recipe comparison, fair-evaluation summaries, canary summary, and hashes. It contains no private traces, prompts, model completions, fixtures, local or remote paths, machine addresses or identifiers, credentials, or weight tensors.
- summary.json — stock, prior S2, and all four arms
- arm-comparison.json — paired component-bootstrap recipe deltas
- fair-eval-summary.json — aggregate protocol and result counts for both corrections
- fair-eval-comparison.json — sanitized paired deltas and bootstrap intervals
- data-manifest.json — sanitized promotion counts and leakage checks
- canary-summary.json — aggregate external result and failure taxonomy
- report.md — compact machine-readable companion narrative
- SHA256SUMS
Winning checkpoint: Qwen2.5-Coder-1.5B-Instruct full SFT, 2,402 rows × 2 epochs, LR 2e-5. The unpublished model.safetensors SHA-256 is 6b1eb660ba974b292da07fe9c93359536abae2550044d6076cf645da2ed3b554. Training wall 113.7 s; peak GPU memory 15.22 GiB on one NVIDIA RTX 6000 Ada Generation GPU.
The experiment ran from a dirty, uncommitted source worktree. This packet freezes sanitized aggregate evidence, not the private full run directory; it should not be described as a hermetic reproduction bundle.
Bottom line
Session 1 found a broken measurement target. Session 2 built a much better one and produced a model that is genuinely strong inside it. We then found and corrected an unfair base-model interface disadvantage. The honest result is not “Bash solved,” nor is it “stock scored zero.” It is: stock passed 8–9% after fair interface treatment, Arm 2 passed about 90%, and transfer remains the next unsolved problem.