BOQ -> draft schedule (Primavera P6)
A planner starting a baseline types every activity, links it and cost-loads it by hand from the BOQ. This skill drafts that first pass: one activity per trade per location, logic from real schedules, BOQ money on every activity, in an XER that P6 imports. The planner owns the dates - the draft is the skeleton they correct, not a forecast.
What it needs
- A priced BOQ: .xlsx or .csv with description + quantity + amount (or rate). Headings as a column or as text rows above the lines, item numbers (1, 1.2, 1.2.3) fine. English, Portuguese or Spanish headers.
- Python 3 and
data/rates.json(mined rates and logic). Nothing else.
Workflow (Claude runs every step; the planner approves typing and answers questions)
1. Read the BOQ
py scripts/read_boq.py <boq.xlsx|.csv> --out outputs/<job>
Writes brief.csv (every line with its chapter path and a keyword guess) and types.md (the work types
and the rules for step 2). Tell the user the line count, the total and the sheet/columns it used.
If the total does not match the BOQ's own total, stop and find out why before typing anything.
2. Type every line (this is Claude's job, not the keyword guess)
Read types.md, then read ALL of brief.csv - chapter by chapter, in pages if it is long. Write
outputs/<job>/decisions.csv with columns key,type,locations,reason:
chapter=<start of chapter path>covers a chapter (case/accents ignored);id~=<regex>covers a BOQ section by item number when chapter names repeat (e.g. two chapters both called "TUBULACOES E CONEXOES"); a line id covers one line.- General rules first, exceptions after: a later rule overrides an earlier one; a line rule always wins.
- Reinforced concrete: fold formwork, rebar and pour of one element into the element's type (footing, column, tie_beam, slab), unless the BOQ prices them as job-wide trades.
- Locations: always set them (blank spreads a line over every location, including the site). Building floors lowest first; site set-up, external works, boundary wall, outside tanks -> the site location.
skiponly for money that is not site work, and say why. Mixed or doubtful chapters: pick the best type and write "planner may split" in the reason - those come back as questions.- Do not trust the keyword guess: it misreads ("tapume com telha" -> roof, "cabeamento" -> slab, "aluminio" -> light).
py scripts/apply_types.py outputs/<job> --locations "Site|GF|FF"
Stops on any unknown type, unknown location or undecided line. Money check: typed + skipped = BOQ total.
Writes lines.csv and typing.md (money per trade, who decided each line, unit warnings). Show the
user the money-per-trade table and the warnings; fix and rerun until clean.
3. Ask the planner
py scripts/questions.py outputs/<job> --start 2026-01-05
Builds an uncalibrated first draft and writes questions.md + answers.json with suggested answers.
Put the questions to the user in plain words (AskUserQuestion works well for the yes/no ones), then
fill answers.json:
| Question | Why it matters |
|---|---|
| Start date, working week (5/6/7), currency | Calendar and the money's currency in P6 |
| Locations, site, layout (floors stack / separate blocks), which run side by side | The sequence. Tested: the draft's own sequencing strategy is the main reason real jobs differ |
| Contract period (months) | Without it the length was right (0.75-1.33x) for 4-6 of 17 real jobs; with it 17 of 17 |
| Three durations they know (one structure, one envelope/finish, one services trade) | Draft durations are ~2.25x off on a line; three anchors bring it to ~1.76x and scale the rest |
| Site works early or after the structure (boundary wall, drainage, tanks, paving) | Moves real money; contractors often build the wall first |
| Typing points flagged "planner may split" | Lines Claude placed but a planner might split |
Blank answers keep the default and the report says so - never invent an answer for the planner.
4. Build
py scripts/build.py outputs/<job>
Runs plan.py + export.py: schedule.json/.csv, report.md, schedule.xer.
Every activity carries its BOQ line ids and the reason for its duration; every link says where it came from
(real schedules: n links A->B, default order, planner's answer).
Import
- P6: File > Import > Primavera PM (XER) > Create New Project, then Tools > Schedule (F9). Expenses appear per activity as " BOQ " with quantity, unit and unit price.
Evidence (all on real data, leave-one-out)
- Typing: Hassan BOQ 22/22 lines = the planner's own activity per line (keyword guess 15/22). Aracaju 643 lines and Januaria 628 lines typed, money to the cent.
- Logic: 75-80% of real links hold on 17 held-out schedules; Hassan 18/18.
- P6: the draft XER imports into P6 Professional 25 and schedules to the same dates as the engine.
- Durations: x2.25 median error, x1.76 with three planner durations.
- Not a cash-flow forecast: the draft's monthly curve was no closer to the real one than a textbook S-curve. Say this whenever someone asks about cash flow or timing from the draft.
Limits
- Text BOQs only (no scanned PDFs). One activity per trade per location: the planner splits finer.
- Rates and logic come from 73 schedules, mostly building jobs; civil/infrastructure is untested.
data/is mined from 73 publicly available P6 schedules (the raw XERs are not bundled).
Tests
py tests/smoke.py - the bundled example (public Januaria tender BOQ, 628 lines) through typing, questions, build and
the P6 XER: lines, money to the cent, tables, tasks, expenses, links, project calendar. Run after any change.
The development tests (3 real BOQs, 17 held-out schedules, the P6 import check) used files that are not bundled.