Navigam commited on
Commit
4e1a75b
·
1 Parent(s): b737c1e

refactor: update training scripts and documentation for SFT and RLVR processes

Browse files

Revised training scripts to replace GRPO with RLVR as the default training path, enhancing the workflow for SFT and RLVR tasks. Updated README.md and runbook to reflect changes in command usage, including new parameters for data preparation and training. Improved clarity on the training process and added details for using the new `run_data_pipeline.py` script. Adjusted example commands and parameters to ensure consistency and better user guidance.

README.md CHANGED
@@ -70,8 +70,7 @@ uv run python inference.py --tasks e1_launch_readiness --max-steps 25 --swd-trac
70
  Replay generated examples against the current environment before training:
71
 
72
  ```powershell
73
- uv run python scripts/verify_examples.py --input data/raw/e1_m1_examples.jsonl --clean data/processed/e1_m1_clean.jsonl --rejected data/processed/e1_m1_rejected.jsonl
74
- uv run python scripts/prepare_sft_data.py
75
  ```
76
 
77
  `prepare_sft_data.py` defaults to merging `data/processed/e1_m1_clean.jsonl` and `data/processed/h1_seed_clean.jsonl` into `data/sft/e1_m1_h1_examples.jsonl`. To build E/M only, pass e.g. `--input data/processed/e1_m1_clean.jsonl --output data/sft/e1_m1_examples.jsonl`.
@@ -90,10 +89,10 @@ Training scripts use **Unsloth** and **Hugging Face TRL** and are intended for a
90
  ```bash
91
  pip install -e ".[training]"
92
  python training/train_sft.py --model Qwen/Qwen2.5-7B-Instruct --data data/sft/e1_m1_h1_examples.jsonl --output outputs/sft_adapter --max-steps 30
93
- python training/train_grpo.py --model Qwen/Qwen2.5-7B-Instruct --adapter outputs/sft_adapter --output outputs/grpo_adapter --max-steps 30
94
  ```
95
 
96
- `train_grpo.py` defaults to `--max-steps 150`, E/M/H task mix, and comma-separated `--examples` including H1 when those files exist. For a short smoke test, add e.g. `--max-steps 30`.
97
 
98
  For a judge-rerunnable notebook, use [`notebooks/corp_env_trl_unsloth_training.ipynb`](notebooks/corp_env_trl_unsloth_training.ipynb).
99
 
 
70
  Replay generated examples against the current environment before training:
71
 
72
  ```powershell
73
+ uv run python scripts/run_data_pipeline.py --write-legacy-copies
 
74
  ```
75
 
76
  `prepare_sft_data.py` defaults to merging `data/processed/e1_m1_clean.jsonl` and `data/processed/h1_seed_clean.jsonl` into `data/sft/e1_m1_h1_examples.jsonl`. To build E/M only, pass e.g. `--input data/processed/e1_m1_clean.jsonl --output data/sft/e1_m1_examples.jsonl`.
 
89
  ```bash
90
  pip install -e ".[training]"
91
  python training/train_sft.py --model Qwen/Qwen2.5-7B-Instruct --data data/sft/e1_m1_h1_examples.jsonl --output outputs/sft_adapter --max-steps 30
92
+ python training/train_rlvr.py --model Qwen/Qwen2.5-7B-Instruct --adapter outputs/sft_adapter --output outputs/rlvr_adapter --strict-json --max-prompts 128 --rounds 3
93
  ```
94
 
95
+ This repo now uses **SFT + RLVR** as the default path. GRPO scripts remain available for reference but are not part of the recommended training flow.
96
 
97
  For a judge-rerunnable notebook, use [`notebooks/corp_env_trl_unsloth_training.ipynb`](notebooks/corp_env_trl_unsloth_training.ipynb).
98
 
data/README.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Data Layout
2
+
3
+ Use this layout for the SFT -> RLVR pipeline.
4
+
5
+ - `raw/imported/`: imported generated scenario examples (E1/M1)
6
+ - `raw/synthetic/`: synthetic seed trajectories (for example H1 seeds)
7
+ - `processed/verified/`: strict-clean and rejected trajectories plus all-records
8
+ - `sft/merged/`: final chat-format SFT training JSONL
9
+
10
+ ## Recommended command
11
+
12
+ ```powershell
13
+ uv run python scripts/run_data_pipeline.py --write-legacy-copies
14
+ ```
15
+
16
+ This command creates structured outputs and also updates legacy flat paths used
17
+ by older scripts.
data/processed/e1_m1_clean.jsonl CHANGED
The diff for this file is too large to render. See raw diff
 
data/processed/h1_seed_clean.jsonl CHANGED
The diff for this file is too large to render. See raw diff
 
data/processed/verified/e1_m1_all_records.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/processed/verified/e1_m1_clean.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/processed/verified/e1_m1_rejected.jsonl ADDED
File without changes
data/processed/verified/h1_seed_all_records.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/processed/verified/h1_seed_clean.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/processed/verified/h1_seed_rejected.jsonl ADDED
File without changes
data/raw/h1_seed.jsonl CHANGED
The diff for this file is too large to render. See raw diff
 
data/raw/imported/e1_m1_examples.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/raw/synthetic/h1_seed.jsonl ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"example_id": "seed-h1_acquisition_defence-000", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
2
+ {"example_id": "seed-h1_acquisition_defence-001", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
3
+ {"example_id": "seed-h1_acquisition_defence-002", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
4
+ {"example_id": "seed-h1_acquisition_defence-003", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
5
+ {"example_id": "seed-h1_acquisition_defence-004", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
6
+ {"example_id": "seed-h1_acquisition_defence-005", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
7
+ {"example_id": "seed-h1_acquisition_defence-006", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
8
+ {"example_id": "seed-h1_acquisition_defence-007", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
9
+ {"example_id": "seed-h1_acquisition_defence-008", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
10
+ {"example_id": "seed-h1_acquisition_defence-009", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
11
+ {"example_id": "seed-h1_acquisition_defence-010", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
12
+ {"example_id": "seed-h1_acquisition_defence-011", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
13
+ {"example_id": "seed-h1_acquisition_defence-012", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
14
+ {"example_id": "seed-h1_acquisition_defence-013", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
15
+ {"example_id": "seed-h1_acquisition_defence-014", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
16
+ {"example_id": "seed-h1_acquisition_defence-015", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
17
+ {"example_id": "seed-h1_acquisition_defence-016", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
18
+ {"example_id": "seed-h1_acquisition_defence-017", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
19
+ {"example_id": "seed-h1_acquisition_defence-018", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
20
+ {"example_id": "seed-h1_acquisition_defence-019", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
21
+ {"example_id": "seed-h1_acquisition_defence-020", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
22
+ {"example_id": "seed-h1_acquisition_defence-021", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
23
+ {"example_id": "seed-h1_acquisition_defence-022", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
24
+ {"example_id": "seed-h1_acquisition_defence-023", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
data/sft/e1_m1_h1_examples.jsonl CHANGED
The diff for this file is too large to render. See raw diff
 
data/sft/merged/e1_m1_h1_examples.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
docs/lightning_hf_runbook.md CHANGED
@@ -6,10 +6,10 @@ This runbook is optimized for short 3-4 hour H100 windows and Hugging Face credi
6
 
7
  ```powershell
8
  uv sync --extra dev --extra plots
9
- uv run python scripts/generate_sft_data.py --tasks h1_acquisition_defence --per-task 24 --output data/raw/h1_seed.jsonl
10
- uv run python scripts/verify_examples.py --input data/raw/e1_m1_examples.jsonl --clean data/processed/e1_m1_clean.jsonl --rejected data/processed/e1_m1_rejected.jsonl
11
- uv run python scripts/verify_examples.py --input data/raw/h1_seed.jsonl --clean data/processed/h1_seed_clean.jsonl --rejected data/processed/h1_seed_rejected.jsonl
12
- uv run python scripts/prepare_sft_data.py
13
  ```
14
 
15
  Keep raw examples untouched. Train only from verified `data/processed/*_clean.jsonl` and `data/sft/*.jsonl`.
@@ -45,7 +45,41 @@ python training/train_sft.py \
45
  --push-to-hub <your-user-or-org>/corp-env-sft-adapter
46
  ```
47
 
48
- This uses Unsloth + TRL `SFTTrainer` with conversational `messages` JSONL (and optional `--packing` / `--dataloader-num-workers` on a strong box). If setup time is short, use a 7B model. If the session is stable and examples are clean, try a 14B model for the SFT demo. Remove `--max-steps 30` or raise it for a real run. Install a `flash-attn` wheel that matches the session’s `torch`+CUDA for best GRPO and long-context SFT step time.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  ## 4. Lightning H100 Session 2: Eval SFT
51
 
@@ -61,31 +95,35 @@ python eval.py --policy hf \
61
 
62
  Push `results/*.jsonl` to Hugging Face or copy them back before the Lightning account expires.
63
 
64
- ## 5. Lightning H100 Session 3: GRPO
65
 
66
  ```bash
67
- python training/train_grpo.py \
68
  --model Qwen/Qwen2.5-7B-Instruct \
69
  --adapter outputs/sft_adapter \
70
  --examples data/processed/e1_m1_clean.jsonl,data/processed/h1_seed_clean.jsonl \
71
- --output outputs/grpo_adapter \
72
- --max-steps 150 \
73
- --dataloader-num-workers 2 \
74
- --push-to-hub <your-user-or-org>/corp-env-grpo-adapter
 
 
 
 
75
  ```
76
 
77
- This uses Unsloth + TRL `GRPOTrainer` with the real CORP-ENV reward path. Defaults now include H1 in `--tasks` and a 150-step run; pass `--max-steps 30` for smoke only. For throughput on H100, match FlashAttention to your `torch` build, then try larger `--batch-size` or more `--dataloader-num-workers` if the CPU and dataset I/O allow.
78
 
79
  ## 6. Final Eval And Plots
80
 
81
  ```bash
82
  python eval.py --policy hf \
83
- --label grpo \
84
  --model Qwen/Qwen2.5-7B-Instruct \
85
- --adapter outputs/grpo_adapter \
86
- --output results/grpo_eval.jsonl
87
  python plot_results.py \
88
- --inputs results/baseline_eval.jsonl results/sft_eval.jsonl results/grpo_eval.jsonl \
89
  --output-dir results
90
  ```
91
 
 
6
 
7
  ```powershell
8
  uv sync --extra dev --extra plots
9
+ uv run python scripts/generate_sft_data.py --tasks h1_acquisition_defence --per-task 24 --variant-stride 2 --output data/raw/h1_seed.jsonl
10
+ uv run python scripts/verify_examples.py --input data/raw/e1_m1_examples.jsonl --clean data/processed/e1_m1_clean.jsonl --rejected data/processed/e1_m1_rejected.jsonl --strict-json --require-stepwise-deliberation
11
+ uv run python scripts/verify_examples.py --input data/raw/h1_seed.jsonl --clean data/processed/h1_seed_clean.jsonl --rejected data/processed/h1_seed_rejected.jsonl --strict-json --require-stepwise-deliberation
12
+ uv run python scripts/prepare_sft_data.py --min-pass-rate 0.85 --min-reasoning-steps 1 --min-conflict-steps 0 --min-resolution-steps 0 --require-stepwise-deliberation
13
  ```
14
 
15
  Keep raw examples untouched. Train only from verified `data/processed/*_clean.jsonl` and `data/sft/*.jsonl`.
 
45
  --push-to-hub <your-user-or-org>/corp-env-sft-adapter
46
  ```
47
 
48
+ This uses Unsloth + TRL `SFTTrainer` with conversational `messages` JSONL (and optional `--packing` / `--dataloader-num-workers` on a strong box). If setup time is short, use a 7B model. For quality runs, target 14B with strict-clean traces. Remove `--max-steps 30` or raise it for a real run. Install a `flash-attn` wheel that matches the session’s `torch`+CUDA for best GRPO and long-context SFT step time.
49
+
50
+ ### 14B SFT presets (smoke then full)
51
+
52
+ ```bash
53
+ # Qwen3 14B smoke
54
+ python training/train_sft.py \
55
+ --model Qwen/Qwen3-14B-Instruct \
56
+ --data data/sft/e1_m1_h1_examples.jsonl \
57
+ --output outputs/sft_qwen3_14b \
58
+ --batch-size 1 --grad-accum 8 --max-steps 20
59
+
60
+ # Qwen3 14B fuller run
61
+ python training/train_sft.py \
62
+ --model Qwen/Qwen3-14B-Instruct \
63
+ --data data/sft/e1_m1_h1_examples.jsonl \
64
+ --output outputs/sft_qwen3_14b \
65
+ --epochs 2 --max-steps 200
66
+
67
+ # DeepSeek 14B smoke
68
+ python training/train_sft.py \
69
+ --model deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \
70
+ --data data/sft/e1_m1_h1_examples.jsonl \
71
+ --output outputs/sft_deepseek_14b \
72
+ --batch-size 1 --grad-accum 8 --max-steps 20
73
+
74
+ # DeepSeek 14B fuller run
75
+ python training/train_sft.py \
76
+ --model deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \
77
+ --data data/sft/e1_m1_h1_examples.jsonl \
78
+ --output outputs/sft_deepseek_14b \
79
+ --epochs 2 --max-steps 200
80
+ ```
81
+
82
+ Use `--assistant-only` only when the model template supports assistant token masks end-to-end.
83
 
84
  ## 4. Lightning H100 Session 2: Eval SFT
85
 
 
95
 
96
  Push `results/*.jsonl` to Hugging Face or copy them back before the Lightning account expires.
97
 
98
+ ## 5. Lightning H100 Session 3: RLVR
99
 
100
  ```bash
101
+ python training/train_rlvr.py \
102
  --model Qwen/Qwen2.5-7B-Instruct \
103
  --adapter outputs/sft_adapter \
104
  --examples data/processed/e1_m1_clean.jsonl,data/processed/h1_seed_clean.jsonl \
105
+ --output outputs/rlvr_adapter \
106
+ --strict-json \
107
+ --min-reasoning-steps 2 \
108
+ --rounds 3 \
109
+ --n-samples 8 \
110
+ --max-prompts 128 \
111
+ --stats-file results/runs/rlvr_stats.jsonl \
112
+ --push-to-hub <your-user-or-org>/corp-env-rlvr-adapter
113
  ```
114
 
115
+ This uses Unsloth + TRL RLVR rejection-sampling with the same strict CORP-ENV verifier path. The recommended production path is now SFT then RLVR.
116
 
117
  ## 6. Final Eval And Plots
118
 
119
  ```bash
120
  python eval.py --policy hf \
121
+ --label rlvr \
122
  --model Qwen/Qwen2.5-7B-Instruct \
123
+ --adapter outputs/rlvr_adapter \
124
+ --output results/rlvr_eval.jsonl
125
  python plot_results.py \
126
+ --inputs results/baseline_eval.jsonl results/sft_eval.jsonl results/rlvr_eval.jsonl \
127
  --output-dir results
128
  ```
129
 
env_artifacts/README.md CHANGED
@@ -1,7 +1,7 @@
1
  # Lightning AI H100 environment — reproduction artifacts
2
 
3
  Files in this folder capture the exact, *working* stack we landed on for
4
- `corp_gym` SFT/GRPO/RLVR training. Use them to bring up a fresh Lightning AI
5
  Studio (or any Linux H100 with driver >= 570, CUDA 12.8 runtime, Python 3.12).
6
 
7
  ## Contents
@@ -17,6 +17,16 @@ Studio (or any Linux H100 with driver >= 570, CUDA 12.8 runtime, Python 3.12).
17
  (treat as a ground truth, not a reinstall recipe — it does not know about
18
  `--index-url` or wheel URLs).
19
 
 
 
 
 
 
 
 
 
 
 
20
  ## Tested against
21
 
22
  - GPU: **NVIDIA H100 80GB HBM3 (sm_90)**, driver **570.148.08**
@@ -64,13 +74,11 @@ huggingface-cli login # once per container
64
  The prior run pushed both adapters to `Navigam/*`:
65
 
66
  - `Navigam/corp-env-sft-qwen2.5-7b`
67
- - `Navigam/corp-env-grpo-qwen2.5-7b`
68
 
69
  You can pull them locally on a new box with:
70
 
71
  ```bash
72
  huggingface-cli download Navigam/corp-env-sft-qwen2.5-7b --local-dir outputs/sft_adapter
73
- huggingface-cli download Navigam/corp-env-grpo-qwen2.5-7b --local-dir outputs/grpo_adapter
74
  ```
75
 
76
  Then for the RLVR run:
@@ -91,13 +99,13 @@ python training/train_rlvr.py \
91
  - **First time only on any new box**: `unsloth` writes patched-trainer classes
92
  into `corp_gym/unsloth_compiled_cache/`. Delete that folder if you ever
93
  change TRL/unsloth versions to avoid stale compiled patches.
94
- - **Flash-attn fp32 bug**: in `training/train_grpo.py` and
95
  `training/train_rlvr.py` we monkey-patch `flash_attn_func` in both
96
  `flash_attn.flash_attn_interface` and `unsloth.utils.attention_dispatch` to
97
  auto-cast Q/K/V to bf16. Without the dispatcher-level patch, Unsloth's copy
98
  of the symbol bypasses ours and FA2 rejects fp32 tensors during the no-grad
99
  reference logprob forward.
100
- - **max_prompt_length filter**: both GRPO and RLVR scripts tokenise every
101
  prompt up-front and drop rows whose chat-template-encoded length exceeds
102
  `0.9 * max_prompt_length` (long H1 trajectories otherwise produce a causal
103
  mask / attention mask size mismatch at generation time).
 
1
  # Lightning AI H100 environment — reproduction artifacts
2
 
3
  Files in this folder capture the exact, *working* stack we landed on for
4
+ `corp_gym` SFT/RLVR training. Use them to bring up a fresh Lightning AI
5
  Studio (or any Linux H100 with driver >= 570, CUDA 12.8 runtime, Python 3.12).
6
 
7
  ## Contents
 
17
  (treat as a ground truth, not a reinstall recipe — it does not know about
18
  `--index-url` or wheel URLs).
19
 
20
+ ## Recommended Lightning Studio environment
21
+
22
+ - GPU: **1x NVIDIA H100 80GB**
23
+ - Runtime: Linux with CUDA **12.8** compatible driver (**>= 570.x**)
24
+ - Python: **3.12** (base Lightning image is fine)
25
+ - Start from a fresh Studio and run `bash env_artifacts/setup_lightning_h100.sh`
26
+
27
+ This is the fastest conflict-free path for Unsloth in this repo because it
28
+ pins a known-good torch/xformers/flash-attn intersection.
29
+
30
  ## Tested against
31
 
32
  - GPU: **NVIDIA H100 80GB HBM3 (sm_90)**, driver **570.148.08**
 
74
  The prior run pushed both adapters to `Navigam/*`:
75
 
76
  - `Navigam/corp-env-sft-qwen2.5-7b`
 
77
 
78
  You can pull them locally on a new box with:
79
 
80
  ```bash
81
  huggingface-cli download Navigam/corp-env-sft-qwen2.5-7b --local-dir outputs/sft_adapter
 
82
  ```
83
 
84
  Then for the RLVR run:
 
99
  - **First time only on any new box**: `unsloth` writes patched-trainer classes
100
  into `corp_gym/unsloth_compiled_cache/`. Delete that folder if you ever
101
  change TRL/unsloth versions to avoid stale compiled patches.
102
+ - **Flash-attn fp32 bug**: in `training/train_grpo.py` (legacy) and
103
  `training/train_rlvr.py` we monkey-patch `flash_attn_func` in both
104
  `flash_attn.flash_attn_interface` and `unsloth.utils.attention_dispatch` to
105
  auto-cast Q/K/V to bf16. Without the dispatcher-level patch, Unsloth's copy
106
  of the symbol bypasses ours and FA2 rejects fp32 tensors during the no-grad
107
  reference logprob forward.
108
+ - **max_prompt_length filter**: both legacy GRPO and RLVR scripts tokenise every
109
  prompt up-front and drop rows whose chat-template-encoded length exceeds
110
  `0.9 * max_prompt_length` (long H1 trajectories otherwise produce a causal
111
  mask / attention mask size mismatch at generation time).
env_artifacts/setup_lightning_h100.sh CHANGED
@@ -1,6 +1,6 @@
1
  #!/usr/bin/env bash
2
  # Reproduce the torch 2.7.1 + cu128 stack that worked on the previous
3
- # Lightning AI H100 container for corp_gym SFT/GRPO/RLVR training.
4
  #
5
  # Target environment:
6
  # - NVIDIA H100 80GB HBM3 (sm_90), driver >= 570.x, CUDA runtime 12.8
@@ -63,7 +63,7 @@ import flash_attn; print("flash_attn", flash_attn.__version__)
63
  import xformers, xformers.ops; print("xformers", xformers.__version__, "ops OK")
64
  import torchao; print("torchao", torchao.__version__)
65
  from unsloth import FastLanguageModel # noqa
66
- from trl import SFTConfig, GRPOConfig # noqa
67
  print("unsloth + trl OK")
68
  PY
69
 
 
1
  #!/usr/bin/env bash
2
  # Reproduce the torch 2.7.1 + cu128 stack that worked on the previous
3
+ # Lightning AI H100 container for corp_gym SFT/RLVR training.
4
  #
5
  # Target environment:
6
  # - NVIDIA H100 80GB HBM3 (sm_90), driver >= 570.x, CUDA runtime 12.8
 
63
  import xformers, xformers.ops; print("xformers", xformers.__version__, "ops OK")
64
  import torchao; print("torchao", torchao.__version__)
65
  from unsloth import FastLanguageModel # noqa
66
+ from trl import SFTConfig, SFTTrainer # noqa
67
  print("unsloth + trl OK")
68
  PY
69
 
results/data_pipeline/e1_m1_summary.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "total": 200,
3
+ "by_status": {
4
+ "clean": 200
5
+ },
6
+ "by_reject_reason": {
7
+ "clean": 200
8
+ },
9
+ "by_task": {
10
+ "e1_launch_readiness": {
11
+ "clean": 100
12
+ },
13
+ "m1_budget_reallocation": {
14
+ "clean": 100
15
+ }
16
+ },
17
+ "clean_avg_terminal_reward": 0.926666,
18
+ "clean_avg_verifier_pass_rate": 1.0,
19
+ "clean_avg_reasoning_steps": 1.0,
20
+ "clean_phase_progression_ok_rate": 0.0
21
+ }
results/data_pipeline/h1_seed_summary.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "total": 24,
3
+ "by_status": {
4
+ "clean": 24
5
+ },
6
+ "by_reject_reason": {
7
+ "clean": 24
8
+ },
9
+ "by_task": {
10
+ "h1_acquisition_defence": {
11
+ "clean": 24
12
+ }
13
+ },
14
+ "clean_avg_terminal_reward": 1.05,
15
+ "clean_avg_verifier_pass_rate": 1.0,
16
+ "clean_avg_reasoning_steps": 5.0,
17
+ "clean_phase_progression_ok_rate": 1.0
18
+ }
scripts/_trajectory_utils.py CHANGED
@@ -8,7 +8,7 @@ import re
8
  import sys
9
  from dataclasses import dataclass
10
  from pathlib import Path
11
- from typing import Any, Dict, Iterable, List, Optional
12
 
13
  ROOT = Path(__file__).resolve().parents[1]
14
  if str(ROOT) not in sys.path:
@@ -20,6 +20,8 @@ from server.environment import CorpEnvironment # noqa: E402
20
 
21
 
22
  DEFAULT_TASKS = ("e1_launch_readiness", "m1_budget_reallocation", "h1_acquisition_defence")
 
 
23
 
24
 
25
  @dataclass
@@ -36,6 +38,10 @@ class ReplayResult:
36
  missed_milestones: List[str]
37
  invalid_action_count: int
38
  env_error_count: int
 
 
 
 
39
  final_swd_version: int
40
  final_swd: Dict[str, Any]
41
 
@@ -62,6 +68,10 @@ class ReplayResult:
62
  "missed_milestones": self.missed_milestones,
63
  "invalid_action_count": self.invalid_action_count,
64
  "env_error_count": self.env_error_count,
 
 
 
 
65
  "final_swd_version": self.final_swd_version,
66
  "actions": self.actions,
67
  "final_swd": self.final_swd,
@@ -95,7 +105,19 @@ def write_jsonl(path: Path, rows: Iterable[Dict[str, Any]]) -> None:
95
  f.write(json.dumps(row, ensure_ascii=False) + "\n")
96
 
97
 
98
- def extract_json_object(text: str) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
 
 
 
 
99
  cleaned = text.strip()
100
  cleaned = re.sub(r"^```(?:json)?\s*", "", cleaned)
101
  cleaned = re.sub(r"\s*```\s*$", "", cleaned).strip()
@@ -136,19 +158,38 @@ def extract_json_object(text: str) -> Dict[str, Any]:
136
  raise ValueError("unbalanced JSON object")
137
 
138
 
139
- def normalize_action_obj(raw: Any) -> Dict[str, Any]:
 
 
 
 
 
140
  if isinstance(raw, str):
141
- raw = extract_json_object(raw)
142
  if not isinstance(raw, dict):
143
  raise ValueError("action must be a JSON object or string containing one")
144
  raw = dict(raw)
 
 
 
 
 
 
 
145
  raw.pop("thought", None)
 
 
146
  if "payload" in raw and not isinstance(raw["payload"], str):
 
 
147
  raw["payload"] = json.dumps(raw["payload"], ensure_ascii=False)
148
  raw.setdefault("payload", "")
149
  if raw.get("agent_id") == "":
150
  raw["agent_id"] = None
151
- return CorpAction.model_validate(raw).model_dump(mode="json", exclude_none=True)
 
 
 
152
 
153
 
154
  def extract_actions(example: Dict[str, Any]) -> List[Dict[str, Any]]:
@@ -181,6 +222,44 @@ def extract_actions(example: Dict[str, Any]) -> List[Dict[str, Any]]:
181
  return [normalize_action_obj(action) for action in raw_actions]
182
 
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  def observation_message(step: int, obs: CorpObservation) -> str:
185
  parts = [
186
  f"--- Step {step} ---",
@@ -203,7 +282,7 @@ def observation_message(step: int, obs: CorpObservation) -> str:
203
  if obs.error:
204
  parts.append(f"Error: {obs.error}")
205
  parts.append(f"Reward (last step): {obs.reward}")
206
- parts.append("Respond with your next JSON action.")
207
  return "\n".join(parts)
208
 
209
 
@@ -253,6 +332,8 @@ def replay_actions(
253
  task_id: str,
254
  actions: List[Dict[str, Any]],
255
  strict_thresholds: bool = True,
 
 
256
  ) -> ReplayResult:
257
  os.environ.setdefault("CORP_STUB_WORKERS", "1")
258
  os.environ.setdefault("CORP_DISABLE_LLM_JUDGE", "1")
@@ -264,10 +345,18 @@ def replay_actions(
264
  invalid_action_count = 0
265
  env_error_count = 0
266
  reject_reason = ""
 
 
 
 
267
 
268
  for idx, action_obj in enumerate(actions, start=1):
 
 
269
  try:
270
- action = CorpAction.model_validate(action_obj)
 
 
271
  except Exception as exc:
272
  invalid_action_count += 1
273
  reject_reason = f"invalid_action_at_step_{idx}: {exc}"
@@ -315,6 +404,10 @@ def replay_actions(
315
  missed_milestones=missed,
316
  invalid_action_count=invalid_action_count,
317
  env_error_count=env_error_count,
 
 
 
 
318
  final_swd_version=int(final_swd.get("swd_version", 0)),
319
  final_swd=final_swd,
320
  )
 
8
  import sys
9
  from dataclasses import dataclass
10
  from pathlib import Path
11
+ from typing import Any, Dict, Iterable, List, Optional, Sequence
12
 
13
  ROOT = Path(__file__).resolve().parents[1]
14
  if str(ROOT) not in sys.path:
 
20
 
21
 
22
  DEFAULT_TASKS = ("e1_launch_readiness", "m1_budget_reallocation", "h1_acquisition_defence")
23
+ _ACTION_ALLOWED_KEYS = {"action_type", "agent_id", "payload", "metadata"}
24
+ _ACTION_ALLOWED_EXTRAS = {"thought"}
25
 
26
 
27
  @dataclass
 
38
  missed_milestones: List[str]
39
  invalid_action_count: int
40
  env_error_count: int
41
+ reasoning_steps: int
42
+ conflict_steps: int
43
+ resolution_steps: int
44
+ phase_progression_ok: bool
45
  final_swd_version: int
46
  final_swd: Dict[str, Any]
47
 
 
68
  "missed_milestones": self.missed_milestones,
69
  "invalid_action_count": self.invalid_action_count,
70
  "env_error_count": self.env_error_count,
71
+ "reasoning_steps": self.reasoning_steps,
72
+ "conflict_steps": self.conflict_steps,
73
+ "resolution_steps": self.resolution_steps,
74
+ "phase_progression_ok": self.phase_progression_ok,
75
  "final_swd_version": self.final_swd_version,
76
  "actions": self.actions,
77
  "final_swd": self.final_swd,
 
105
  f.write(json.dumps(row, ensure_ascii=False) + "\n")
106
 
107
 
108
+ def _strict_json_object(text: str) -> Dict[str, Any]:
109
+ cleaned = text.strip()
110
+ if cleaned.startswith("```"):
111
+ raise ValueError("strict_json_disallows_fenced_blocks")
112
+ obj = json.loads(cleaned)
113
+ if not isinstance(obj, dict):
114
+ raise ValueError("strict_json_requires_single_object")
115
+ return obj
116
+
117
+
118
+ def extract_json_object(text: str, *, strict: bool = False) -> Dict[str, Any]:
119
+ if strict:
120
+ return _strict_json_object(text)
121
  cleaned = text.strip()
122
  cleaned = re.sub(r"^```(?:json)?\s*", "", cleaned)
123
  cleaned = re.sub(r"\s*```\s*$", "", cleaned).strip()
 
158
  raise ValueError("unbalanced JSON object")
159
 
160
 
161
+ def normalize_action_obj(
162
+ raw: Any,
163
+ *,
164
+ strict: bool = False,
165
+ allowed_extra_keys: Optional[Sequence[str]] = None,
166
+ ) -> Dict[str, Any]:
167
  if isinstance(raw, str):
168
+ raw = extract_json_object(raw, strict=strict)
169
  if not isinstance(raw, dict):
170
  raise ValueError("action must be a JSON object or string containing one")
171
  raw = dict(raw)
172
+ allowed = set(_ACTION_ALLOWED_KEYS)
173
+ extra = set(allowed_extra_keys or ())
174
+ allowed.update(extra)
175
+ if strict:
176
+ unknown = sorted(k for k in raw if k not in allowed and k not in _ACTION_ALLOWED_EXTRAS)
177
+ if unknown:
178
+ raise ValueError(f"unexpected_action_keys: {unknown}")
179
  raw.pop("thought", None)
180
+ if "action_type" not in raw or not isinstance(raw["action_type"], str):
181
+ raise ValueError("missing_or_invalid_action_type")
182
  if "payload" in raw and not isinstance(raw["payload"], str):
183
+ if strict:
184
+ raise ValueError("strict_json_requires_payload_string")
185
  raw["payload"] = json.dumps(raw["payload"], ensure_ascii=False)
186
  raw.setdefault("payload", "")
187
  if raw.get("agent_id") == "":
188
  raw["agent_id"] = None
189
+ out = CorpAction.model_validate(raw).model_dump(mode="json", exclude_none=True)
190
+ if strict and set(out) - _ACTION_ALLOWED_KEYS:
191
+ raise ValueError("non_canonical_action_shape")
192
+ return out
193
 
194
 
195
  def extract_actions(example: Dict[str, Any]) -> List[Dict[str, Any]]:
 
222
  return [normalize_action_obj(action) for action in raw_actions]
223
 
224
 
225
+ def deliberation_features(actions: List[Dict[str, Any]]) -> Dict[str, Any]:
226
+ reasoning_steps = sum(1 for a in actions if a.get("action_type") == "log_reasoning")
227
+ conflict_steps = sum(1 for a in actions if a.get("action_type") == "log_conflict")
228
+ resolution_steps = sum(1 for a in actions if a.get("action_type") == "log_resolution")
229
+ phases = [
230
+ str(a.get("payload", "")).strip().lower()
231
+ for a in actions
232
+ if a.get("action_type") == "advance_phase"
233
+ ]
234
+ expected = ["analysis", "decision", "execution"]
235
+ idx = 0
236
+ for phase in phases:
237
+ if idx < len(expected) and phase == expected[idx]:
238
+ idx += 1
239
+ phase_progression_ok = idx == len(expected)
240
+ return {
241
+ "reasoning_steps": reasoning_steps,
242
+ "conflict_steps": conflict_steps,
243
+ "resolution_steps": resolution_steps,
244
+ "phase_progression_ok": phase_progression_ok,
245
+ }
246
+
247
+
248
+ def validate_stepwise_deliberation(task_id: str, actions: List[Dict[str, Any]]) -> str:
249
+ features = deliberation_features(actions)
250
+ reasoning_min = 1 if task_id in {"e1_launch_readiness", "m1_budget_reallocation"} else 3
251
+ if features["reasoning_steps"] < reasoning_min:
252
+ return "insufficient_stepwise_reasoning"
253
+ if task_id in {"m1_budget_reallocation", "h1_acquisition_defence"}:
254
+ if features["conflict_steps"] < 1:
255
+ return "missing_conflict_logging"
256
+ if features["resolution_steps"] < 1:
257
+ return "missing_resolution_logging"
258
+ if task_id == "h1_acquisition_defence" and not features["phase_progression_ok"]:
259
+ return "invalid_phase_progression"
260
+ return ""
261
+
262
+
263
  def observation_message(step: int, obs: CorpObservation) -> str:
264
  parts = [
265
  f"--- Step {step} ---",
 
282
  if obs.error:
283
  parts.append(f"Error: {obs.error}")
284
  parts.append(f"Reward (last step): {obs.reward}")
285
+ parts.append("Respond with exactly one JSON object for your next action (no markdown fences).")
286
  return "\n".join(parts)
287
 
288
 
 
332
  task_id: str,
333
  actions: List[Dict[str, Any]],
334
  strict_thresholds: bool = True,
335
+ strict_json: bool = False,
336
+ require_stepwise_deliberation: bool = False,
337
  ) -> ReplayResult:
338
  os.environ.setdefault("CORP_STUB_WORKERS", "1")
339
  os.environ.setdefault("CORP_DISABLE_LLM_JUDGE", "1")
 
345
  invalid_action_count = 0
346
  env_error_count = 0
347
  reject_reason = ""
348
+ features = deliberation_features(actions)
349
+
350
+ if require_stepwise_deliberation:
351
+ reject_reason = validate_stepwise_deliberation(task_id, actions)
352
 
353
  for idx, action_obj in enumerate(actions, start=1):
354
+ if reject_reason:
355
+ break
356
  try:
357
+ action = CorpAction.model_validate(
358
+ normalize_action_obj(action_obj, strict=strict_json)
359
+ )
360
  except Exception as exc:
361
  invalid_action_count += 1
362
  reject_reason = f"invalid_action_at_step_{idx}: {exc}"
 
404
  missed_milestones=missed,
405
  invalid_action_count=invalid_action_count,
406
  env_error_count=env_error_count,
407
+ reasoning_steps=int(features["reasoning_steps"]),
408
+ conflict_steps=int(features["conflict_steps"]),
409
+ resolution_steps=int(features["resolution_steps"]),
410
+ phase_progression_ok=bool(features["phase_progression_ok"]),
411
  final_swd_version=int(final_swd.get("swd_version", 0)),
412
  final_swd=final_swd,
413
  )
scripts/generate_sft_data.py CHANGED
@@ -19,16 +19,17 @@ if str(ROOT) not in sys.path:
19
  from scripts._trajectory_utils import DEFAULT_TASKS, oracle_actions, write_jsonl # noqa: E402
20
 
21
 
22
- def build_examples(tasks: List[str], per_task: int) -> List[Dict[str, Any]]:
23
  rows: List[Dict[str, Any]] = []
24
  for task_id in tasks:
25
  for idx in range(per_task):
 
26
  rows.append(
27
  {
28
  "example_id": f"seed-{task_id}-{idx:03d}",
29
  "task_id": task_id,
30
  "source": "scripted_seed",
31
- "actions": oracle_actions(task_id, idx),
32
  }
33
  )
34
  return rows
@@ -38,11 +39,17 @@ def main() -> None:
38
  parser = argparse.ArgumentParser(description="Generate CORP-ENV seed trajectories.")
39
  parser.add_argument("--tasks", default="h1_acquisition_defence")
40
  parser.add_argument("--per-task", type=int, default=8)
 
 
 
 
 
 
41
  parser.add_argument("--output", default="data/raw/h1_seed.jsonl")
42
  args = parser.parse_args()
43
 
44
  tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
45
- rows = build_examples(tasks, args.per_task)
46
  write_jsonl(Path(args.output), rows)
47
  print(f"Wrote {len(rows)} seed trajectories to {args.output}")
48
  print("Next: run scripts/verify_examples.py on this file before using it for SFT.")
 
19
  from scripts._trajectory_utils import DEFAULT_TASKS, oracle_actions, write_jsonl # noqa: E402
20
 
21
 
22
+ def build_examples(tasks: List[str], per_task: int, variant_stride: int) -> List[Dict[str, Any]]:
23
  rows: List[Dict[str, Any]] = []
24
  for task_id in tasks:
25
  for idx in range(per_task):
26
+ variant = idx * max(1, variant_stride)
27
  rows.append(
28
  {
29
  "example_id": f"seed-{task_id}-{idx:03d}",
30
  "task_id": task_id,
31
  "source": "scripted_seed",
32
+ "actions": oracle_actions(task_id, variant),
33
  }
34
  )
35
  return rows
 
39
  parser = argparse.ArgumentParser(description="Generate CORP-ENV seed trajectories.")
40
  parser.add_argument("--tasks", default="h1_acquisition_defence")
41
  parser.add_argument("--per-task", type=int, default=8)
42
+ parser.add_argument(
43
+ "--variant-stride",
44
+ type=int,
45
+ default=1,
46
+ help="Sample wider oracle variants by stepping this much between synthetic traces.",
47
+ )
48
  parser.add_argument("--output", default="data/raw/h1_seed.jsonl")
49
  args = parser.parse_args()
50
 
51
  tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
52
+ rows = build_examples(tasks, args.per_task, args.variant_stride)
53
  write_jsonl(Path(args.output), rows)
54
  print(f"Wrote {len(rows)} seed trajectories to {args.output}")
55
  print("Next: run scripts/verify_examples.py on this file before using it for SFT.")
scripts/prepare_sft_data.py CHANGED
@@ -9,6 +9,7 @@ Pass one or more processed JSONLs (e.g. `e1_m1_clean` + `h1_seed_clean`) from
9
  from __future__ import annotations
10
 
11
  import argparse
 
12
  import sys
13
  from pathlib import Path
14
  from typing import Any, Dict, List
@@ -19,13 +20,22 @@ if str(ROOT) not in sys.path:
19
 
20
  from scripts._trajectory_utils import ( # noqa: E402
21
  actions_to_sft_messages,
 
22
  extract_actions,
23
  read_jsonl,
 
24
  write_jsonl,
25
  )
26
 
27
 
28
- def convert_example(example: Dict[str, Any], min_pass_rate: float) -> Dict[str, Any] | None:
 
 
 
 
 
 
 
29
  if example.get("status") and example.get("status") != "clean":
30
  return None
31
  pass_rate = float(example.get("verifier_pass_rate", 1.0))
@@ -35,6 +45,16 @@ def convert_example(example: Dict[str, Any], min_pass_rate: float) -> Dict[str,
35
  if not task_id:
36
  return None
37
  actions = extract_actions(example)
 
 
 
 
 
 
 
 
 
 
38
  messages = actions_to_sft_messages(task_id, actions)
39
  return {
40
  "example_id": str(example.get("example_id") or example.get("id") or "unknown"),
@@ -43,6 +63,10 @@ def convert_example(example: Dict[str, Any], min_pass_rate: float) -> Dict[str,
43
  "num_actions": len(actions),
44
  "terminal_reward": example.get("terminal_reward"),
45
  "verifier_pass_rate": example.get("verifier_pass_rate"),
 
 
 
 
46
  }
47
 
48
 
@@ -75,11 +99,26 @@ def main() -> None:
75
  )
76
  parser.add_argument("--output", default="data/sft/e1_m1_h1_examples.jsonl")
77
  parser.add_argument("--min-pass-rate", type=float, default=0.80)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  args = parser.parse_args()
79
  raw_inputs = list(args.inputs) if args.inputs else [default_inputs]
80
  input_paths = _parse_input_paths(raw_inputs)
81
 
82
  rows: List[Dict[str, Any]] = []
 
83
  seen_ids: set[str] = set()
84
  skipped = 0
85
  for path in input_paths:
@@ -92,7 +131,14 @@ def main() -> None:
92
  skipped += 1
93
  continue
94
  try:
95
- row = convert_example(example, args.min_pass_rate)
 
 
 
 
 
 
 
96
  except Exception as exc:
97
  skipped += 1
98
  print(f"skip {example.get('example_id', 'unknown')}: {exc}")
@@ -100,7 +146,11 @@ def main() -> None:
100
  if row is None:
101
  skipped += 1
102
  continue
 
 
 
103
  rows.append(row)
 
104
  eid2 = str(row.get("example_id") or "unknown")
105
  if eid2 and eid2 != "unknown":
106
  seen_ids.add(eid2)
 
9
  from __future__ import annotations
10
 
11
  import argparse
12
+ from collections import defaultdict
13
  import sys
14
  from pathlib import Path
15
  from typing import Any, Dict, List
 
20
 
21
  from scripts._trajectory_utils import ( # noqa: E402
22
  actions_to_sft_messages,
23
+ deliberation_features,
24
  extract_actions,
25
  read_jsonl,
26
+ validate_stepwise_deliberation,
27
  write_jsonl,
28
  )
29
 
30
 
31
+ def convert_example(
32
+ example: Dict[str, Any],
33
+ min_pass_rate: float,
34
+ min_reasoning_steps: int,
35
+ min_conflict_steps: int,
36
+ min_resolution_steps: int,
37
+ require_stepwise_deliberation: bool,
38
+ ) -> Dict[str, Any] | None:
39
  if example.get("status") and example.get("status") != "clean":
40
  return None
41
  pass_rate = float(example.get("verifier_pass_rate", 1.0))
 
45
  if not task_id:
46
  return None
47
  actions = extract_actions(example)
48
+ if require_stepwise_deliberation:
49
+ if validate_stepwise_deliberation(task_id, actions):
50
+ return None
51
+ features = deliberation_features(actions)
52
+ if int(features["reasoning_steps"]) < min_reasoning_steps:
53
+ return None
54
+ if int(features["conflict_steps"]) < min_conflict_steps:
55
+ return None
56
+ if int(features["resolution_steps"]) < min_resolution_steps:
57
+ return None
58
  messages = actions_to_sft_messages(task_id, actions)
59
  return {
60
  "example_id": str(example.get("example_id") or example.get("id") or "unknown"),
 
63
  "num_actions": len(actions),
64
  "terminal_reward": example.get("terminal_reward"),
65
  "verifier_pass_rate": example.get("verifier_pass_rate"),
66
+ "reasoning_steps": int(features["reasoning_steps"]),
67
+ "conflict_steps": int(features["conflict_steps"]),
68
+ "resolution_steps": int(features["resolution_steps"]),
69
+ "phase_progression_ok": bool(features["phase_progression_ok"]),
70
  }
71
 
72
 
 
99
  )
100
  parser.add_argument("--output", default="data/sft/e1_m1_h1_examples.jsonl")
101
  parser.add_argument("--min-pass-rate", type=float, default=0.80)
102
+ parser.add_argument("--min-reasoning-steps", type=int, default=1)
103
+ parser.add_argument("--min-conflict-steps", type=int, default=0)
104
+ parser.add_argument("--min-resolution-steps", type=int, default=0)
105
+ parser.add_argument(
106
+ "--require-stepwise-deliberation",
107
+ action="store_true",
108
+ help="Require task-specific SWD step-wise deliberation checks from verification utilities.",
109
+ )
110
+ parser.add_argument(
111
+ "--max-per-task",
112
+ type=int,
113
+ default=0,
114
+ help="Optional cap for kept SFT rows per task (0 = unlimited).",
115
+ )
116
  args = parser.parse_args()
117
  raw_inputs = list(args.inputs) if args.inputs else [default_inputs]
118
  input_paths = _parse_input_paths(raw_inputs)
119
 
120
  rows: List[Dict[str, Any]] = []
121
+ by_task_kept: Dict[str, int] = defaultdict(int)
122
  seen_ids: set[str] = set()
123
  skipped = 0
124
  for path in input_paths:
 
131
  skipped += 1
132
  continue
133
  try:
134
+ row = convert_example(
135
+ example,
136
+ args.min_pass_rate,
137
+ args.min_reasoning_steps,
138
+ args.min_conflict_steps,
139
+ args.min_resolution_steps,
140
+ args.require_stepwise_deliberation,
141
+ )
142
  except Exception as exc:
143
  skipped += 1
144
  print(f"skip {example.get('example_id', 'unknown')}: {exc}")
 
146
  if row is None:
147
  skipped += 1
148
  continue
149
+ if args.max_per_task > 0 and by_task_kept[row["task_id"]] >= args.max_per_task:
150
+ skipped += 1
151
+ continue
152
  rows.append(row)
153
+ by_task_kept[row["task_id"]] += 1
154
  eid2 = str(row.get("example_id") or "unknown")
155
  if eid2 and eid2 != "unknown":
156
  seen_ids.add(eid2)
scripts/run_data_pipeline.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Run a structured CORP-ENV data pipeline for SFT -> RLVR.
2
+
3
+ This script standardizes where artifacts are written so data prep stays tidy:
4
+
5
+ - raw/imported: imported E1/M1 generated examples
6
+ - raw/synthetic: synthetic seed traces (H1 by default)
7
+ - processed/verified: clean/rejected outputs after strict verification
8
+ - sft/merged: final SFT chat JSONL
9
+
10
+ It wraps existing scripts and keeps compatibility by optionally writing the
11
+ legacy flat output files as copies.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ import shutil
18
+ import subprocess
19
+ import sys
20
+ from pathlib import Path
21
+
22
+ ROOT = Path(__file__).resolve().parents[1]
23
+
24
+
25
+ def run(cmd: list[str], *, use_uv: bool) -> None:
26
+ final_cmd = (["uv", "run"] + cmd) if use_uv else cmd
27
+ print("+", " ".join(final_cmd))
28
+ subprocess.run(final_cmd, check=True, cwd=ROOT)
29
+
30
+
31
+ def ensure_dirs(paths: list[Path]) -> None:
32
+ for path in paths:
33
+ path.mkdir(parents=True, exist_ok=True)
34
+
35
+
36
+ def copy_if_exists(src: Path, dst: Path) -> None:
37
+ if src.exists():
38
+ dst.parent.mkdir(parents=True, exist_ok=True)
39
+ shutil.copy2(src, dst)
40
+
41
+
42
+ def main() -> None:
43
+ parser = argparse.ArgumentParser(description="Structured data pipeline for CORP-ENV.")
44
+ parser.add_argument("--python", default=sys.executable)
45
+ parser.add_argument(
46
+ "--use-uv",
47
+ action="store_true",
48
+ help="Run each pipeline stage with `uv run` to use the project environment.",
49
+ )
50
+ parser.add_argument("--h1-per-task", type=int, default=24)
51
+ parser.add_argument("--h1-variant-stride", type=int, default=2)
52
+ parser.add_argument("--min-pass-rate", type=float, default=0.85)
53
+ parser.add_argument("--min-reasoning-steps", type=int, default=1)
54
+ parser.add_argument("--min-conflict-steps", type=int, default=0)
55
+ parser.add_argument("--min-resolution-steps", type=int, default=0)
56
+ parser.add_argument("--max-per-task", type=int, default=0)
57
+ parser.add_argument(
58
+ "--write-legacy-copies",
59
+ action="store_true",
60
+ help="Also copy outputs to legacy flat paths for backward compatibility.",
61
+ )
62
+ args = parser.parse_args()
63
+
64
+ data = ROOT / "data"
65
+ raw_imported = data / "raw" / "imported"
66
+ raw_synthetic = data / "raw" / "synthetic"
67
+ processed_verified = data / "processed" / "verified"
68
+ sft_merged = data / "sft" / "merged"
69
+ summaries = ROOT / "results" / "data_pipeline"
70
+ ensure_dirs([raw_imported, raw_synthetic, processed_verified, sft_merged, summaries])
71
+
72
+ e1m1_raw = raw_imported / "e1_m1_examples.jsonl"
73
+ h1_raw = raw_synthetic / "h1_seed.jsonl"
74
+ e1m1_clean = processed_verified / "e1_m1_clean.jsonl"
75
+ e1m1_rejected = processed_verified / "e1_m1_rejected.jsonl"
76
+ h1_clean = processed_verified / "h1_seed_clean.jsonl"
77
+ h1_rejected = processed_verified / "h1_seed_rejected.jsonl"
78
+ sft_out = sft_merged / "e1_m1_h1_examples.jsonl"
79
+
80
+ run(
81
+ [
82
+ args.python,
83
+ "scripts/import_generated_examples.py",
84
+ "--inputs",
85
+ "data/raw/e1_to_e100_tasks.py",
86
+ "data/raw/m1_to_m100_tasks.py",
87
+ "--output",
88
+ str(e1m1_raw),
89
+ ],
90
+ use_uv=args.use_uv,
91
+ )
92
+ run(
93
+ [
94
+ args.python,
95
+ "scripts/generate_sft_data.py",
96
+ "--tasks",
97
+ "h1_acquisition_defence",
98
+ "--per-task",
99
+ str(args.h1_per_task),
100
+ "--variant-stride",
101
+ str(args.h1_variant_stride),
102
+ "--output",
103
+ str(h1_raw),
104
+ ],
105
+ use_uv=args.use_uv,
106
+ )
107
+ run(
108
+ [
109
+ args.python,
110
+ "scripts/verify_examples.py",
111
+ "--input",
112
+ str(e1m1_raw),
113
+ "--clean",
114
+ str(e1m1_clean),
115
+ "--rejected",
116
+ str(e1m1_rejected),
117
+ "--all-records",
118
+ str(processed_verified / "e1_m1_all_records.jsonl"),
119
+ "--summary",
120
+ str(summaries / "e1_m1_summary.json"),
121
+ "--strict-json",
122
+ "--require-stepwise-deliberation",
123
+ ],
124
+ use_uv=args.use_uv,
125
+ )
126
+ run(
127
+ [
128
+ args.python,
129
+ "scripts/verify_examples.py",
130
+ "--input",
131
+ str(h1_raw),
132
+ "--clean",
133
+ str(h1_clean),
134
+ "--rejected",
135
+ str(h1_rejected),
136
+ "--all-records",
137
+ str(processed_verified / "h1_seed_all_records.jsonl"),
138
+ "--summary",
139
+ str(summaries / "h1_seed_summary.json"),
140
+ "--strict-json",
141
+ "--require-stepwise-deliberation",
142
+ ],
143
+ use_uv=args.use_uv,
144
+ )
145
+ prep_cmd = [
146
+ args.python,
147
+ "scripts/prepare_sft_data.py",
148
+ "--input",
149
+ str(e1m1_clean),
150
+ "--input",
151
+ str(h1_clean),
152
+ "--output",
153
+ str(sft_out),
154
+ "--min-pass-rate",
155
+ str(args.min_pass_rate),
156
+ "--min-reasoning-steps",
157
+ str(args.min_reasoning_steps),
158
+ "--min-conflict-steps",
159
+ str(args.min_conflict_steps),
160
+ "--min-resolution-steps",
161
+ str(args.min_resolution_steps),
162
+ "--require-stepwise-deliberation",
163
+ ]
164
+ if args.max_per_task > 0:
165
+ prep_cmd.extend(["--max-per-task", str(args.max_per_task)])
166
+ run(prep_cmd, use_uv=args.use_uv)
167
+
168
+ if args.write_legacy_copies:
169
+ copy_if_exists(e1m1_raw, data / "raw" / "e1_m1_examples.jsonl")
170
+ copy_if_exists(h1_raw, data / "raw" / "h1_seed.jsonl")
171
+ copy_if_exists(e1m1_clean, data / "processed" / "e1_m1_clean.jsonl")
172
+ copy_if_exists(e1m1_rejected, data / "processed" / "e1_m1_rejected.jsonl")
173
+ copy_if_exists(h1_clean, data / "processed" / "h1_seed_clean.jsonl")
174
+ copy_if_exists(h1_rejected, data / "processed" / "h1_seed_rejected.jsonl")
175
+ copy_if_exists(sft_out, data / "sft" / "e1_m1_h1_examples.jsonl")
176
+
177
+ print("\nStructured data pipeline complete.")
178
+ print(f"SFT dataset: {sft_out}")
179
+ print(f"Summaries: {summaries}")
180
+
181
+
182
+ if __name__ == "__main__":
183
+ main()
scripts/verify_examples.py CHANGED
@@ -32,7 +32,12 @@ from scripts._trajectory_utils import ( # noqa: E402
32
  )
33
 
34
 
35
- def verify_one(example: Dict[str, Any], strict_thresholds: bool) -> Dict[str, Any]:
 
 
 
 
 
36
  example_id = str(example.get("example_id") or example.get("id") or "unknown")
37
  task_id = str(example.get("task_id") or example.get("task") or "")
38
  if example.get("_load_error"):
@@ -67,6 +72,8 @@ def verify_one(example: Dict[str, Any], strict_thresholds: bool) -> Dict[str, An
67
  task_id=task_id,
68
  actions=actions,
69
  strict_thresholds=strict_thresholds,
 
 
70
  )
71
  except Exception as exc:
72
  return {
@@ -101,6 +108,16 @@ def summarize(records: List[Dict[str, Any]]) -> Dict[str, Any]:
101
  if clean
102
  else 0.0
103
  ),
 
 
 
 
 
 
 
 
 
 
104
  }
105
 
106
 
@@ -116,10 +133,25 @@ def main() -> None:
116
  action="store_true",
117
  help="Only require replay validity; do not apply task reward/pass thresholds.",
118
  )
 
 
 
 
 
 
 
 
 
 
119
  args = parser.parse_args()
120
 
121
  records = [
122
- verify_one(example, strict_thresholds=not args.lenient)
 
 
 
 
 
123
  for example in read_jsonl(Path(args.input))
124
  ]
125
  clean = [r for r in records if r["status"] == "clean"]
 
32
  )
33
 
34
 
35
+ def verify_one(
36
+ example: Dict[str, Any],
37
+ strict_thresholds: bool,
38
+ strict_json: bool,
39
+ require_stepwise_deliberation: bool,
40
+ ) -> Dict[str, Any]:
41
  example_id = str(example.get("example_id") or example.get("id") or "unknown")
42
  task_id = str(example.get("task_id") or example.get("task") or "")
43
  if example.get("_load_error"):
 
72
  task_id=task_id,
73
  actions=actions,
74
  strict_thresholds=strict_thresholds,
75
+ strict_json=strict_json,
76
+ require_stepwise_deliberation=require_stepwise_deliberation,
77
  )
78
  except Exception as exc:
79
  return {
 
108
  if clean
109
  else 0.0
110
  ),
111
+ "clean_avg_reasoning_steps": (
112
+ round(sum(float(r.get("reasoning_steps", 0.0)) for r in clean) / len(clean), 6)
113
+ if clean
114
+ else 0.0
115
+ ),
116
+ "clean_phase_progression_ok_rate": (
117
+ round(sum(1.0 for r in clean if r.get("phase_progression_ok")) / len(clean), 6)
118
+ if clean
119
+ else 0.0
120
+ ),
121
  }
122
 
123
 
 
133
  action="store_true",
134
  help="Only require replay validity; do not apply task reward/pass thresholds.",
135
  )
136
+ parser.add_argument(
137
+ "--strict-json",
138
+ action="store_true",
139
+ help="Require strict single-object JSON action format with canonical action keys.",
140
+ )
141
+ parser.add_argument(
142
+ "--require-stepwise-deliberation",
143
+ action="store_true",
144
+ help="Enforce task-specific step-wise deliberation checks (reasoning/conflict/resolution/phases).",
145
+ )
146
  args = parser.parse_args()
147
 
148
  records = [
149
+ verify_one(
150
+ example,
151
+ strict_thresholds=not args.lenient,
152
+ strict_json=args.strict_json,
153
+ require_stepwise_deliberation=args.require_stepwise_deliberation,
154
+ )
155
  for example in read_jsonl(Path(args.input))
156
  ]
157
  clean = [r for r in records if r["status"] == "clean"]
training/train_grpo.py CHANGED
@@ -87,6 +87,7 @@ from scripts._trajectory_utils import ( # noqa: E402
87
  DEFAULT_TASKS,
88
  extract_actions,
89
  extract_json_object,
 
90
  observation_message,
91
  oracle_actions,
92
  read_jsonl,
@@ -94,6 +95,20 @@ from scripts._trajectory_utils import ( # noqa: E402
94
  from server.agents.master_prompts import build_system_prompt # noqa: E402
95
  from server.environment import CorpEnvironment # noqa: E402
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  def prompt_for_prefix(task_id: str, prefix_actions: List[Dict[str, Any]]) -> List[Dict[str, str]]:
99
  env = CorpEnvironment()
@@ -113,7 +128,12 @@ def _examples_paths(examples_path: str) -> List[Path]:
113
  return [Path(p.strip()) for p in examples_path.split(",") if p.strip()]
114
 
115
 
116
- def build_prompt_dataset(examples_path: str, tasks: List[str], repeats: int) -> List[Dict[str, Any]]:
 
 
 
 
 
117
  """Load examples from one or more JSONLs (comma-separated). Falls back to oracle prefixes if all missing/empty."""
118
  rows: List[Dict[str, Any]] = []
119
  for path in _examples_paths(examples_path):
@@ -122,6 +142,8 @@ def build_prompt_dataset(examples_path: str, tasks: List[str], repeats: int) ->
122
  for example in read_jsonl(path):
123
  if example.get("status") and example.get("status") != "clean":
124
  continue
 
 
125
  task_id = str(example.get("task_id") or "")
126
  if task_id not in tasks:
127
  continue
@@ -153,6 +175,77 @@ def build_prompt_dataset(examples_path: str, tasks: List[str], repeats: int) ->
153
  return rows
154
 
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  def environment_reward(
157
  completions: List[Any],
158
  task_id: List[str],
@@ -161,37 +254,24 @@ def environment_reward(
161
  ) -> List[float]:
162
  rewards: List[float] = []
163
  for completion, tid, prefix_raw in zip(completions, task_id, prefix_actions):
164
- env = CorpEnvironment()
165
- obs = env.reset(task_id=tid)
166
- try:
167
- prefix = json.loads(prefix_raw)
168
- for action_obj in prefix:
169
- obs = env.step(CorpAction.model_validate(action_obj))
170
- if obs.done:
171
- break
172
- except Exception:
173
- rewards.append(-0.25)
174
- continue
175
-
176
- text = completion
177
- if isinstance(completion, list) and completion:
178
- text = completion[0].get("content", "")
179
- elif isinstance(completion, dict):
180
- text = completion.get("content", "")
181
- try:
182
- obj = extract_json_object(str(text))
183
- obj.pop("thought", None)
184
- if "payload" in obj and not isinstance(obj["payload"], str):
185
- obj["payload"] = json.dumps(obj["payload"], ensure_ascii=False)
186
- action = CorpAction.model_validate(obj)
187
- except Exception:
188
- rewards.append(-0.25)
189
- continue
190
- obs = env.step(action)
191
- reward = float(obs.reward or 0.0)
192
- if obs.error:
193
- reward -= 0.15
194
- rewards.append(max(-1.0, min(1.0, reward)))
195
  return rewards
196
 
197
 
@@ -252,6 +332,17 @@ def main() -> None:
252
  help="When dataloader_num_workers>0, optional prefetch depth (e.g. 2).",
253
  )
254
  parser.add_argument("--push-to-hub", default="")
 
 
 
 
 
 
 
 
 
 
 
255
  args = parser.parse_args()
256
 
257
  os.environ.setdefault("CORP_STUB_WORKERS", "1")
@@ -269,9 +360,10 @@ def main() -> None:
269
  ) from exc
270
 
271
  PatchFastRL("GRPO", FastLanguageModel)
 
272
 
273
  tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
274
- rows = build_prompt_dataset(args.examples, tasks, args.repeats)
275
 
276
  model, tokenizer = FastLanguageModel.from_pretrained(
277
  model_name=args.model,
 
87
  DEFAULT_TASKS,
88
  extract_actions,
89
  extract_json_object,
90
+ normalize_action_obj,
91
  observation_message,
92
  oracle_actions,
93
  read_jsonl,
 
95
  from server.agents.master_prompts import build_system_prompt # noqa: E402
96
  from server.environment import CorpEnvironment # noqa: E402
97
 
98
+ REWARD_CFG: Dict[str, Any] = {
99
+ "strict_json": True,
100
+ "invalid_json_penalty": -0.8,
101
+ "invalid_action_penalty": -0.6,
102
+ "prefix_error_penalty": -0.4,
103
+ }
104
+ REWARD_STATS: Dict[str, int] = {
105
+ "calls": 0,
106
+ "prefix_fail": 0,
107
+ "json_fail": 0,
108
+ "action_fail": 0,
109
+ "env_error": 0,
110
+ }
111
+
112
 
113
  def prompt_for_prefix(task_id: str, prefix_actions: List[Dict[str, Any]]) -> List[Dict[str, str]]:
114
  env = CorpEnvironment()
 
128
  return [Path(p.strip()) for p in examples_path.split(",") if p.strip()]
129
 
130
 
131
+ def build_prompt_dataset(
132
+ examples_path: str,
133
+ tasks: List[str],
134
+ repeats: int,
135
+ min_reasoning_steps: int,
136
+ ) -> List[Dict[str, Any]]:
137
  """Load examples from one or more JSONLs (comma-separated). Falls back to oracle prefixes if all missing/empty."""
138
  rows: List[Dict[str, Any]] = []
139
  for path in _examples_paths(examples_path):
 
142
  for example in read_jsonl(path):
143
  if example.get("status") and example.get("status") != "clean":
144
  continue
145
+ if int(example.get("reasoning_steps", 0)) < min_reasoning_steps:
146
+ continue
147
  task_id = str(example.get("task_id") or "")
148
  if task_id not in tasks:
149
  continue
 
175
  return rows
176
 
177
 
178
+ def completion_to_text(completion: Any) -> str:
179
+ text = completion
180
+ if isinstance(completion, list) and completion:
181
+ text = completion[0].get("content", "")
182
+ elif isinstance(completion, dict):
183
+ text = completion.get("content", "")
184
+ return str(text)
185
+
186
+
187
+ def score_completion(
188
+ *,
189
+ completion_text: str,
190
+ task_id: str,
191
+ prefix_raw: str,
192
+ ) -> tuple[float, Dict[str, float], str]:
193
+ env = CorpEnvironment()
194
+ obs = env.reset(task_id=task_id)
195
+ components: Dict[str, float] = {
196
+ "format_valid": 0.0,
197
+ "action_valid": 0.0,
198
+ "env_step": 0.0,
199
+ "verifier_pass": 0.0,
200
+ "terminal_reward": 0.0,
201
+ "error_penalty": 0.0,
202
+ }
203
+ status = "ok"
204
+ try:
205
+ prefix = json.loads(prefix_raw)
206
+ for action_obj in prefix:
207
+ obs = env.step(CorpAction.model_validate(action_obj))
208
+ if obs.done:
209
+ break
210
+ except Exception:
211
+ status = "prefix_fail"
212
+ return REWARD_CFG["prefix_error_penalty"], components, status
213
+
214
+ try:
215
+ obj = extract_json_object(completion_text, strict=bool(REWARD_CFG["strict_json"]))
216
+ components["format_valid"] = 0.15
217
+ except Exception:
218
+ status = "json_fail"
219
+ return REWARD_CFG["invalid_json_penalty"], components, status
220
+
221
+ try:
222
+ action_obj = normalize_action_obj(obj, strict=bool(REWARD_CFG["strict_json"]))
223
+ action = CorpAction.model_validate(action_obj)
224
+ components["action_valid"] = 0.20
225
+ except Exception:
226
+ status = "action_fail"
227
+ return REWARD_CFG["invalid_action_penalty"], components, status
228
+
229
+ obs = env.step(action)
230
+ components["env_step"] = 0.15 if not obs.error else -0.10
231
+ verifier = env.task.verifier(obs.swd)
232
+ if verifier:
233
+ components["verifier_pass"] = sum(1.0 for v in verifier.values() if v) / len(verifier)
234
+ components["terminal_reward"] = float(obs.reward or 0.0)
235
+ if obs.error:
236
+ status = "env_error"
237
+ components["error_penalty"] = -0.2
238
+ score = (
239
+ components["format_valid"]
240
+ + components["action_valid"]
241
+ + components["env_step"]
242
+ + 0.25 * components["verifier_pass"]
243
+ + 0.50 * components["terminal_reward"]
244
+ + components["error_penalty"]
245
+ )
246
+ return max(-1.0, min(1.0, score)), components, status
247
+
248
+
249
  def environment_reward(
250
  completions: List[Any],
251
  task_id: List[str],
 
254
  ) -> List[float]:
255
  rewards: List[float] = []
256
  for completion, tid, prefix_raw in zip(completions, task_id, prefix_actions):
257
+ score, _components, status = score_completion(
258
+ completion_text=completion_to_text(completion),
259
+ task_id=tid,
260
+ prefix_raw=prefix_raw,
261
+ )
262
+ REWARD_STATS["calls"] += 1
263
+ if status in REWARD_STATS:
264
+ REWARD_STATS[status] += 1
265
+ if REWARD_STATS["calls"] % 100 == 0:
266
+ print(
267
+ "reward_stats "
268
+ f"calls={REWARD_STATS['calls']} "
269
+ f"prefix_fail={REWARD_STATS['prefix_fail']} "
270
+ f"json_fail={REWARD_STATS['json_fail']} "
271
+ f"action_fail={REWARD_STATS['action_fail']} "
272
+ f"env_error={REWARD_STATS['env_error']}"
273
+ )
274
+ rewards.append(score)
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  return rewards
276
 
277
 
 
332
  help="When dataloader_num_workers>0, optional prefetch depth (e.g. 2).",
333
  )
334
  parser.add_argument("--push-to-hub", default="")
335
+ parser.add_argument(
336
+ "--strict-json",
337
+ action="store_true",
338
+ help="Require completion to be a single strict JSON object.",
339
+ )
340
+ parser.add_argument(
341
+ "--min-reasoning-steps",
342
+ type=int,
343
+ default=1,
344
+ help="Filter training examples to traces with at least this many log_reasoning actions.",
345
+ )
346
  args = parser.parse_args()
347
 
348
  os.environ.setdefault("CORP_STUB_WORKERS", "1")
 
360
  ) from exc
361
 
362
  PatchFastRL("GRPO", FastLanguageModel)
363
+ REWARD_CFG["strict_json"] = bool(args.strict_json)
364
 
365
  tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
366
+ rows = build_prompt_dataset(args.examples, tasks, args.repeats, args.min_reasoning_steps)
367
 
368
  model, tokenizer = FastLanguageModel.from_pretrained(
369
  model_name=args.model,
training/train_rlvr.py CHANGED
@@ -79,8 +79,12 @@ ROOT = Path(__file__).resolve().parents[1]
79
  if str(ROOT) not in sys.path:
80
  sys.path.insert(0, str(ROOT))
81
 
82
- from training.train_grpo import build_prompt_dataset, environment_reward # noqa: E402
83
- from scripts._trajectory_utils import DEFAULT_TASKS # noqa: E402
 
 
 
 
84
 
85
 
86
  def _sft_config_field_names() -> set[str]:
@@ -164,6 +168,7 @@ def rollout_round(
164
  top_p: float,
165
  max_new_tokens: int,
166
  reward_threshold: float,
 
167
  log_every: int = 10,
168
  ) -> Tuple[List[Dict[str, Any]], Dict[str, float]]:
169
  kept_rows: List[Dict[str, Any]] = []
@@ -171,6 +176,7 @@ def rollout_round(
171
  best_rewards: List[float] = []
172
  per_task_kept: Dict[str, int] = {}
173
  per_task_total: Dict[str, int] = {}
 
174
 
175
  t0 = time.time()
176
  for idx, row in enumerate(rows):
@@ -192,21 +198,29 @@ def rollout_round(
192
  print(f" [{idx}] {task_id}: generation error {exc!r}")
193
  continue
194
 
195
- rewards = environment_reward(
196
- completions=completions,
197
- task_id=[task_id] * len(completions),
198
- prefix_actions=[prefix_json] * len(completions),
199
- )
 
 
 
 
200
  all_rewards.extend(rewards)
201
  best_idx = max(range(len(rewards)), key=lambda j: rewards[j])
202
  best_r = float(rewards[best_idx])
203
  best_rewards.append(best_r)
204
 
205
  if best_r >= reward_threshold:
 
 
 
 
206
  kept_rows.append(
207
  {
208
  "messages": list(row["prompt"])
209
- + [{"role": "assistant", "content": completions[best_idx]}],
210
  "task_id": task_id,
211
  "reward": best_r,
212
  }
@@ -235,6 +249,8 @@ def rollout_round(
235
  }
236
  for tid in per_task_total:
237
  stats[f"keep_rate/{tid}"] = per_task_kept.get(tid, 0) / per_task_total[tid]
 
 
238
  return kept_rows, stats
239
 
240
 
@@ -364,6 +380,17 @@ def main() -> None:
364
  parser.add_argument("--repeats", type=int, default=32)
365
  parser.add_argument("--seed", type=int, default=3407)
366
  parser.add_argument("--push-to-hub", default="")
 
 
 
 
 
 
 
 
 
 
 
367
  parser.add_argument(
368
  "--stats-file",
369
  default="",
@@ -386,7 +413,13 @@ def main() -> None:
386
  ) from exc
387
 
388
  tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
389
- full_rows = build_prompt_dataset(args.examples, tasks, args.repeats)
 
 
 
 
 
 
390
  print(f"Built {len(full_rows)} prompts from {args.examples}")
391
 
392
  max_seq_len = args.max_prompt_length + args.max_completion_length
@@ -449,6 +482,7 @@ def main() -> None:
449
  top_p=args.top_p,
450
  max_new_tokens=args.max_completion_length,
451
  reward_threshold=args.reward_threshold,
 
452
  )
453
  stats["round"] = round_idx
454
  print(
 
79
  if str(ROOT) not in sys.path:
80
  sys.path.insert(0, str(ROOT))
81
 
82
+ from training.train_grpo import ( # noqa: E402
83
+ REWARD_CFG,
84
+ build_prompt_dataset,
85
+ score_completion,
86
+ )
87
+ from scripts._trajectory_utils import DEFAULT_TASKS, normalize_action_obj # noqa: E402
88
 
89
 
90
  def _sft_config_field_names() -> set[str]:
 
168
  top_p: float,
169
  max_new_tokens: int,
170
  reward_threshold: float,
171
+ strict_json: bool,
172
  log_every: int = 10,
173
  ) -> Tuple[List[Dict[str, Any]], Dict[str, float]]:
174
  kept_rows: List[Dict[str, Any]] = []
 
176
  best_rewards: List[float] = []
177
  per_task_kept: Dict[str, int] = {}
178
  per_task_total: Dict[str, int] = {}
179
+ reward_status_counts: Dict[str, int] = {}
180
 
181
  t0 = time.time()
182
  for idx, row in enumerate(rows):
 
198
  print(f" [{idx}] {task_id}: generation error {exc!r}")
199
  continue
200
 
201
+ rewards: List[float] = []
202
+ for text in completions:
203
+ score, _components, status = score_completion(
204
+ completion_text=text,
205
+ task_id=task_id,
206
+ prefix_raw=prefix_json,
207
+ )
208
+ rewards.append(score)
209
+ reward_status_counts[status] = reward_status_counts.get(status, 0) + 1
210
  all_rewards.extend(rewards)
211
  best_idx = max(range(len(rewards)), key=lambda j: rewards[j])
212
  best_r = float(rewards[best_idx])
213
  best_rewards.append(best_r)
214
 
215
  if best_r >= reward_threshold:
216
+ best_text = completions[best_idx]
217
+ if strict_json:
218
+ action_obj = normalize_action_obj(best_text, strict=True)
219
+ best_text = json.dumps(action_obj, ensure_ascii=False)
220
  kept_rows.append(
221
  {
222
  "messages": list(row["prompt"])
223
+ + [{"role": "assistant", "content": best_text}],
224
  "task_id": task_id,
225
  "reward": best_r,
226
  }
 
249
  }
250
  for tid in per_task_total:
251
  stats[f"keep_rate/{tid}"] = per_task_kept.get(tid, 0) / per_task_total[tid]
252
+ for status, count in reward_status_counts.items():
253
+ stats[f"reward_status/{status}"] = float(count)
254
  return kept_rows, stats
255
 
256
 
 
380
  parser.add_argument("--repeats", type=int, default=32)
381
  parser.add_argument("--seed", type=int, default=3407)
382
  parser.add_argument("--push-to-hub", default="")
383
+ parser.add_argument(
384
+ "--strict-json",
385
+ action="store_true",
386
+ help="Require sampled completions to parse as a strict single JSON object action.",
387
+ )
388
+ parser.add_argument(
389
+ "--min-reasoning-steps",
390
+ type=int,
391
+ default=1,
392
+ help="Filter prompt dataset to trajectories with at least this many reasoning steps.",
393
+ )
394
  parser.add_argument(
395
  "--stats-file",
396
  default="",
 
413
  ) from exc
414
 
415
  tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
416
+ REWARD_CFG["strict_json"] = bool(args.strict_json)
417
+ full_rows = build_prompt_dataset(
418
+ args.examples,
419
+ tasks,
420
+ args.repeats,
421
+ args.min_reasoning_steps,
422
+ )
423
  print(f"Built {len(full_rows)} prompts from {args.examples}")
424
 
425
  max_seq_len = args.max_prompt_length + args.max_completion_length
 
482
  top_p=args.top_p,
483
  max_new_tokens=args.max_completion_length,
484
  reward_threshold=args.reward_threshold,
485
+ strict_json=args.strict_json,
486
  )
487
  stats["round"] = round_idx
488
  print(