You are parsing an owner's roof replacement RFP into the normalized schema at
templates/bid_schema.json (§project and §rfp sections). Your output drives
every downstream skill in this plugin — extraction accuracy is the foundation.
Reference Material
Before extracting, be aware of the fixtures you must cite for classification decisions:
fixtures/domain_knowledge/01_ontario_roofing_codes.md— OBC Part 3 vs Part 9 triggers, SB-10/SB-12 applicability, WAH and environmental requirementsfixtures/domain_knowledge/02_roofing_materials_warranties.md— membrane/shingle system categories, thickness conventionsfixtures/domain_knowledge/03_tender_evaluation_methodology.md— default MCDA weighting, price scoring methods, mandatory gate conventions
Extraction Order
-
Classify the project. Determine
building_classification:obc_part_3if: >3 storeys, >600 m² footprint, or occupancy is assembly/institutional/business/mercantile/industrial/high-hazardobc_part_9if: houses, duplexes, small residential, or buildings ≤3 storeys AND ≤600 m²- If ambiguous, default to the more restrictive (
obc_part_3) and flag inextraction_notes.
-
Classify the roof type.
low_slope_commercial(<2:12),steep_slope_residential,steep_slope_commercial, ormixed. -
Extract scope. From the tender's scope of work section:
- Tear-off vs recover
- Specified membrane/shingle system, thickness, attachment
- Manufacturer acceptable list
- Allowances (especially wood deck replacement per board-foot)
- Insulation upgrade to SB-10/SB-12 (retrofits must comply per §1.3 of the Ontario codes fixture)
- Included items, excluded items, alternates requested
-
Extract warranty requirements. Minimum years, warranty type (material-only vs NDL — see fixture 02 §1 for taxonomy), manufacturer certification requirement.
-
Extract mandatory requirements. Each gate in §
rfp.mandatory_requirements— set to actual value if specified in RFP, otherwise leave default. Important: only set a mandatory-requirements field when the RFP actually invokes it. Do not fabricatebid_bond_percent,addenda_acknowledgment_required, or similar from "standard tender template" assumptions — those values drive gate applicability inscripts/gate_applicability.py, and setting them falsely will disqualify bidders for requirements the RFP never stated. When the RFP is silent, leave the field absent.
5a. Extract submission requirements verbatim. The RFP's "Required Content" / "Submission Requirements" block (typically §6.2 in public-sector tenders; may be any section in private tenders) enumerates what bidders must submit. Copy each enumerated item as a single string into rfp.submission_requirements[], preserving the RFP's wording. This list is consulted by the gate-applicability check: a gate is applicable if either mandatory_requirements.<field> is populated OR the item appears verbatim here. Do not paraphrase — the literal text is what future reviewers will diff against. If the RFP has no enumerated submission requirements block, write an empty array and note this in extraction_notes.
-
Extract evaluation criteria weighting. If the RFP states weights, use them. If not, apply defaults from fixture 03 §3 adjusted for project context:
- Occupied commercial → raise schedule + technical approach
- Heritage/complex → raise technical
- BPS/public → raise price (BPS Procurement Directive implications)
- Straightforward re-roof → defaults
-
Record extraction provenance. For each non-default value, record the RFP page/section where you found it in
_provenance(sidecar structure keyed by JSON path).
Output
Write the manifest to <rfp-dir>/roof-review-output/manifests/rfp.json, conforming to the project and rfp portions of the schema. Include:
{
"project": { ... },
"rfp": {
"...": "...",
"submission_requirements": [
"Form of Tender signed in ink by an authorized signing officer",
"WSIB Clearance Certificate (current validity)",
"Certificate of Insurance — CGL minimum $5,000,000 naming owner as additional insured"
],
"mandatory_requirements": { "...": "..." }
},
"_provenance": {
"rfp.scope_of_work.membrane_system_specified.category": "page 12, §3.2",
"rfp.warranty_requirements.warranty_type_required": "page 18, §5.1",
"rfp.submission_requirements": "page 22, §6.2"
},
"extraction_notes": ["classified as obc_part_3 due to 4-storey occupied office"]
}
Defaults to Apply if RFP Is Silent
owner_type: infer from owner name (condo/municipal/school board/hospital obvious; elseprivate_commercialorprivate_residentialbased on building classification)evaluation_criteria.weighting: apply defaults (Price 45 / Technical 15 / Experience 15 / Warranty 10 / Schedule 5 / Qualifications 10) — note inextraction_notesthat defaults were usedmandatory_requirements.cgl_minimum_cad: 5000000 for OBC Part 3, 2000000 for OBC Part 9warranty_requirements.warranty_type_required:total_system_ndlfor Part 3,labour_and_materialfor Part 9
Red Flags to Raise at RFP Extraction Stage
If the RFP itself is deficient, record these in extraction_notes so the owner can fix before receiving bids:
- Mandatory requirements missing entirely (bids will be non-comparable)
- Evaluation weighting not disclosed (Ontario Contract A/B law — privilege clauses don't cure undisclosed criteria; see fixture 03 §1)
- Warranty type not specified (bidders will offer cheapest interpretation)
- No allowance for wood deck replacement (extras will blow the budget)
- No specification for cover board over polyiso on commercial (voids most NDL warranties per fixture 02 §1)
Summary to User
When done, tell the user:
- Classification outcome (Part 3 or Part 9, roof type)
- Roof area and specified system
- Mandatory gate count
- Evaluation weighting applied (default or from RFP)
- Any RFP-level deficiencies noted
- Path to the written manifest