Consolidated, numbered list of the constraints and rules that govern oxFlow’s behaviour. Pulls every rule out of the glossary and domain model into one sign-off-ready reference.
How to read this doc:
- Every rule has a stable ID (e.g.,
BR-017) that won’t change. Downstream docs (PRDs, entity specs, test plans) reference rules by ID. - Rules are grouped by category. The quick reference table in §2 is the scan layer; §3 is the detailed layer with rationale and references.
- If a rule changes, the ID stays but the text and changelog are updated.
Related docs: glossary.md · concept-map.md
1. Summary by category
| Category | Count | Locked | Working | Open |
|---|---|---|---|---|
| Hierarchy & nesting | 7 | 5 | 2 | 0 |
| Items | 14 | 8 | 5 | 1 |
| Resources | 10 | 5 | 3 | 0 |
| Recipes | 5 | 5 | 0 | 0 |
| Worksheets | 11 | 5 | 5 | 1 |
| Price Books & Suppliers | 7 | 7 | 0 | 0 |
| Adjudications | 14 | 14 | 0 | 0 |
| Commercials & Submission | 7 | 5 | 1 | 1 |
| Companies & Users | 8 | 8 | 0 | 0 |
| Tenders & Estimates | 7 | 6 | 1 | 0 |
| Programs & Tasks | 7 | 5 | 2 | 0 |
| Units & Conversions | 4 | 3 | 1 | 0 |
| Categorization, Flags & Codes | 15 | 8 | 7 | 0 |
| Content Blocks | 3 | 3 | 0 | 0 |
| Integrations | 4 | 2 | 2 | 0 |
| Total | 123 | 88 | 31 | 3 |
2. Quick reference table
Scan the whole catalogue on one page.
| ID | Rule | Status |
|---|---|---|
| Hierarchy & nesting | ||
| BR-001 | Heading can nest up to 5 levels deep | 🟡 |
| BR-002 | Item can nest up to 5 levels deep (sub-Items) | 🟡 |
| BR-003 | A Schedule Item cannot be nested under another Schedule Item | 🟢 |
| BR-004 | A Schedule Item must sit at the highest level of its branch | 🟢 |
| BR-005 | Every Item has exactly one Worksheet | 🟢 |
| BR-006 | Every Recipe has exactly one Worksheet | 🟢 |
| BR-007 | A Worksheet has exactly one parent — Item OR Recipe, not both | 🟢 |
| Items | ||
| BR-010 | Every Item has a Unit (required) | 🟢 |
| BR-011 | Every Item has an Item Type | 🟢 |
| BR-012 | Provisional Items are only valid as Schedule Items | 🟢 |
| BR-013 | Rate-Only Items are only valid as Schedule Items | 🟢 |
| BR-014 | Excluded / Included Elsewhere statuses only apply to Schedule Items | 🟢 |
| BR-015 | Direct / Indirect is derived structurally by default | 🟢 |
| BR-016 | Indirect Cost flag overrides the structural derivation | 🟢 |
| BR-017 | Indirect Cost definition (what qualifies as indirect in Oxcon’s world) | 🔴 |
| BR-018 | Normal Items can be flagged Inactive (preserve record, exclude from costing) | 🟢 |
| BR-019 | Risk Items are a distinct Item Type for contingency/risk tracking | 🟡 |
| BR-019a | Item Status derivation: Unpriced / Plugged / Priced auto-derived from Worksheet + plug_rate | 🟡 |
| BR-019b | plug_rate and Worksheet cost are mutually exclusive | 🟡 |
| BR-019c | Submission block: Estimate cannot submit while any Item is Unpriced or Plugged | 🟡 |
| BR-019d | Distinction between Plugged (Item status) and Plug Rate (Resource flag) | 🟡 |
| Resources | ||
| BR-020 | Every Resource has a Unit (required; LS valid) | 🟢 |
| BR-021 | Every Resource belongs to exactly one Price Book | 🟢 |
| BR-022 | Resource Types are fixed — users cannot add new types | 🟢 |
| BR-023 | Flag Catalog is admin-defined; Flags picked per Resource filtered by Resource Type scope | 🟡 |
| BR-024 | All Subcontract Resources live in a Price Book (user-created or system-generated by Adjudication) | 🟢 |
| BR-025 | Resource Type “Other” has no Flags and no type-specific Anomaly checks | 🟢 |
| BR-026 | Full Flag + Modifier catalog seeding at launch — Oxcon confirmation during admin catalog setup | 🟡 |
| BR-027 | Modifier Catalog is admin-defined; Modifiers carry a math operation + value | 🟡 |
| BR-028 | Modifier values flow Resource → Worksheet Resource as defaults; per-instance override allowed | 🟡 |
| BR-029 | Modifier math application order: quantity_multiplier → rate_adder → lump_sum_add → total_multiplier | 🟡 |
| Recipes | ||
| BR-030 | Every Recipe has at least one Input Parameter AND declares an Output Unit | 🟢 |
| BR-031 | Recipes live in a shared Recipe Library accessible to all Estimates | 🟢 |
| BR-032 | An Item can be promoted to a Recipe via the Recipe Builder | 🟢 |
| BR-033 | A Recipe’s total cost is computed by evaluating its Worksheet with supplied Input Parameter values | 🟢 |
| BR-035 | Items can be duplicated (copy/paste) within or across Estimates | 🟢 |
| Worksheets | ||
| BR-040 | Worksheet Resources store a snapshot of the Resource’s rate, Unit, flags, and modifier default values | 🟢 |
| BR-041 | Worksheet Recipes store a snapshot of Input Parameter values | 🟢 |
| BR-042 | Snapshots do not auto-sync when the underlying Resource or Recipe changes | 🟢 |
| BR-043 | Snapshot divergence is flagged by Anomaly Review; estimator explicitly pushes updates through | 🟢 |
| BR-044 | Worksheet child entities (Variable, Calculation, Content, Worksheet Resource, Worksheet Recipe) each carry their own ordering | 🟢 |
| BR-045 | Canonical terminology for Worksheet Resource / Worksheet Recipe (keep or rename) | 🔴 |
| BR-046 | Production Rate links quantity and duration (Variable pattern, not a new entity) | 🟡 |
| BR-047 | Default rate-edit on a Worksheet Resource: per-instance override | 🟡 |
| BR-048 | ”Apply to this Estimate” bulk rate override | 🟡 |
| BR-049 | ”Fork to new Resource” creates a Resource in the Estimate’s Project-Specific Price Book | 🟡 |
| BR-049a | Rate change cascades affected Items from Reviewed back to Priced | 🟡 |
| Price Books & Suppliers | ||
| BR-050 | External Price Books must have a Supplier (Company with Supplier role) | 🟢 |
| BR-051 | Internal Price Books do not require a supplier | 🟢 |
| BR-052 | Project-Specific Price Books do not require a supplier but must be scoped to a Tender | 🟢 |
| BR-053 | Resources inherit their parent Price Book’s scope (date range, region, project/tender link). No per-Resource scope override in v1; Categorization provides orthogonal tagging. | 🟢 |
| BR-054 | System-generated Price Books are hidden from the default Price Book list | 🟢 |
| BR-053a | Price Books auto-archive when scope_end_date < today. Logical check at query time; status auto-transitions to Archived. | 🟢 |
| BR-053b | Multiple Price Books per Supplier for the same date range are allowed (e.g., different product categories). Names must be unique workspace-wide. | 🟢 |
| Adjudications | ||
| BR-060 | Price Book Adjudication scopes over Resources selected from the Estimate | 🟢 |
| BR-061 | Subcontract Package Adjudication scopes over a Subcontract Package (bundle of Items) | 🟢 |
| BR-062 | Price Book Adjudication updates rates on existing Resources (by replacement). Supports multiple rounds — each round enters Draft → Adjudicated; re-awarding produces a new round. Consistency with Subcontract Package Adjudication. | 🟢 |
| BR-063 | Subcontract Package Adjudication creates/updates a system-generated Price Book; Items reference Resources in it | 🟢 |
| BR-064 | Adjudications can be re-opened (Adjudicated → Draft) at any time while the parent Estimate is in an editable state (In Progress / Reviewed). Once the Estimate transitions to Submitted (or cascaded states — Won / Lost / Archived), Adjudications are frozen alongside the Estimate (BR-094 cascade). | 🟢 |
| BR-065 | A Subcontract Package can have multiple Adjudication rounds. Items in the Package can be added or removed while the current Adjudication round is in Draft state. Once the Adjudication is locked (Adjudicated), Item membership is frozen until re-opened (which cascades to re-adjudication). | 🟢 |
| BR-066 | Adjudicated Subcontract Resources retain a lineage pointer to the Adjudication that produced them | 🟢 |
| BR-067 | On re-opening a locked Adjudication, existing Resources persist until re-adjudicated | 🟢 |
| BR-068 | Adjudication six-step workflow (applies to both Price Book and Subcontract Package) | 🟢 |
| BR-068a | Normalisation items are transient to the Adjudication | 🟢 |
| Commercials & Submission | ||
| BR-070 | Commercials Rules apply in sequence order; rules apply to running state of scope | 🟢 |
| BR-068b | Adjudication return import uses an Import Wizard — Excel is the primary format. The Wizard provides smart column-mapping, preview validation, and manual field override. | 🟢 |
| BR-068c | Variance, inclusion, and exclusion notes captured during adjudication are stored as Content Block instances on the affected Item’s Worksheet — not on the Adjudication itself. | 🟢 |
| BR-069 | Supplier/Subcontractor role enforcement is validated at the first gate — whichever of (adding the Company to the Adjudication as a competing supplier/subcontractor) or (importing their priced return) happens first. | 🟢 |
| BR-069a | Subcontract Package does not carry a pre-qualified Subcontractor list. Supplier/Subcontractor selection lives in the Adjudication workflow (step 1: Generate). | 🟢 |
| BR-071 | A Rule has a type: Percentage / Lump Sum | 🟢 |
| BR-072 | Submission Values are computed by Commercials but manually over-ridable | 🟢 |
| BR-073 | Full list of Rule scope targets (v1 list proposed; Oxcon review) | 🟡 |
| BR-074 | Rules apply linearly; no cross-rule dependencies or conflict resolution | 🟢 |
| BR-074a | Lump Sum Rule scope across multiple Items: proportional distribution | 🟢 |
| BR-075 | Publisher Output retains latest version only | 🟢 |
| Companies & Users | ||
| BR-080 | Companies sync from Xero; Xero → oxFlow is one-way; no Phase 2 bi-directional plan | 🟢 |
| BR-080a | Archival cascade: Company archived in Xero auto-archives in oxFlow on next sync | 🟢 |
| BR-081 | Users sync from Microsoft 365; not created directly in oxFlow | 🟢 |
| BR-082 | A Company can carry any combination of Company Roles (Client / Supplier / Subcontractor) | 🟢 |
| BR-083 | Company Role is seeded from Xero metadata on sync; default fallback is Supplier | 🟢 |
| BR-084 | Every User has exactly one Role (Admin / Lead Estimator / Estimator) | 🟢 |
| BR-085 | Only Admins can edit admin-managed lookups (Units, Categorizations, Content Block Definitions, etc.) | 🟢 |
| BR-086 | Role assignment happens in oxFlow after M365 sync (admin-controlled) | 🟢 |
| Tenders & Estimates | ||
| BR-090 | A Tender must have a Client (Client held at Tender level; multi-client scope handled as separate Tenders) | 🟢 |
| BR-091 | A Tender can have multiple Estimates | 🟢 |
| BR-092 | Tender state transitions: Active → Submitted → Won/Lost/Archived; Active → Archived; Admin + Lead Estimator can transition | 🟢 |
| BR-093 | Every Estimate has a Lead Estimator (required) | 🟢 |
| BR-093a | Estimate In Progress → Reviewed transition is automatic when all Items = Reviewed | 🟢 |
| BR-094 | Submitted Estimate fully locked; cascade: Won keeps submitted + archives others, Lost/Archived archives all | 🟢 |
| BR-095 | Real-time collaboration on Estimates with explicit per-Item locking (flagged for Oxcon discussion) | 🟡 |
| Programs & Tasks | ||
| BR-100 | The Tender Program is uploaded at the Tender level, not the Estimate level | 🟡 |
| BR-101 | A Program Task can be linked to one or more Items | 🟡 |
| BR-102 | Linking a Program Task to an Item adds a duration Variable to the Item’s Worksheet | 🟢 |
| BR-103 | Program Task ↔ Item is M:M (items can link multiple tasks; tasks can be linked by multiple items) | 🟢 |
| BR-104 | Program Task durations are adopted directly from MS Project export; oxFlow does not recalculate | 🟢 |
| BR-105 | Summary-task handling: only summary task duration exposed as Variable, not subtasks | 🟢 |
| BR-106 | Program re-upload conflict resolution: Task ID governs; same ID = same task, new ID = new task | 🟢 |
| Units & Conversions | ||
| BR-110 | LS is one of the standard Units | 🟢 |
| BR-111 | Unit conversions are done manually in the Worksheet (v1) — no auto-conversion | 🟢 |
| BR-112 | Unit reconciliation anomaly check (heuristic TBD; not a simple “units differ” check) | 🟡 |
| BR-113 | Unit library is admin-managed and extensible | 🟢 |
| Categorization & Flags | ||
| BR-120 | Categorization is user-defined and never affects system behaviour (reporting only) | 🟢 |
| BR-120a | Categorizations and Categorization Options use soft-delete (archive) | 🟢 |
| BR-121 | A Categorization defines its own scope (Items / Resources / Tenders / Estimates / combination) | 🟢 |
| BR-121a | Categorization inheritance: sub-Items inherit parent’s Categorization Options at creation; can override | 🟢 |
| BR-122 | Flags are type-specific attributes on Resources (per Resource Type) | 🟢 |
| BR-123 | Item Flags exist but the v1 set is minimal (Indirect Cost only) | 🟢 |
| BR-124 | Company Roles are a form of flag on Company (multi-select) | 🟢 |
| BR-125 | Codes are required classifiers on Items and Resources only (not Tenders/Estimates) | 🟡 |
| BR-125a | Custom Code creation is Admin-only; Lead Estimators cannot create new Codes | 🟢 |
| BR-126 | A Code has the same flexibility as a Categorization (name, scope, options) | 🟡 |
| BR-127 | Required Code enforcement — soft in Estimate/Publisher; hard block on Cost Management export | 🟡 |
| BR-128 | Codes sync is one-time initial migration + on-demand manual refresh (not scheduled) | 🟢 |
| BR-144 | Reference Rates are admin-defined benchmarks used in Anomaly Review | 🟡 |
| BR-145 | Reference Rate rules-first matching (Unit + optional Categorization) — Alpha | 🟡 |
| BR-146 | AI-assisted Reference Rate matching by semantic item description — Release | 🟡 |
| Content Blocks | ||
| BR-130 | Four Content Blocks ship by default: Inclusions, Exclusions, Task Breakdown, Risks | 🟢 |
| BR-131 | Admins can define additional Content Blocks (Name + Explainer) | 🟢 |
| BR-132 | Content Block Definitions are system-wide; instances are per-Worksheet | 🟢 |
| Integrations | ||
| BR-140 | Xero is the source of truth for Companies; oxFlow does not push changes back | 🟢 |
| BR-141 | Microsoft 365 is the source of truth for Users (basic info and identity) | 🟢 |
| BR-142 | MS Project .mpp is the expected format for Tender Program upload | 🟡 |
| BR-143 | Workbench is the downstream cost management system for won projects; oxFlow pushes on project promotion | 🟡 |
3. Rules in detail
Detailed view with rationale, references, and open questions. Same order as §2.
3.1 Hierarchy & nesting
BR-001 — Heading can nest up to 5 levels deep 🟡
Rationale: client schedules can have deeply nested structure; a cap prevents unbounded recursion. Cap value (5) needs real-world confirmation. Refs: glossary §3; concept-map Heading entity. Open: realistic max depth in Oxcon’s estimates — is 5 right, or is 3 the actual max?
BR-002 — Item can nest up to 5 levels deep (sub-Items) 🟡
Rationale: matches Heading depth. Sub-Items are used for cost build-up (headings are for organisation). Refs: glossary §4; concept-map Item entity. Open: confirm realistic max in practice.
BR-003 — A Schedule Item cannot be nested under another Schedule Item 🟢
Rationale: Schedule Items drive what appears in the client submission. Nesting would create ambiguity about what gets priced at which level. Refs: glossary §5 (Schedule Item).
BR-004 — A Schedule Item must sit at the highest level of its branch 🟢
Rationale: corollary of BR-003. Structure-wise, Schedule Items are the roots of their sub-trees. Use Headings to group Schedule Items. Refs: glossary §5; concept-map Item.
BR-005 — Every Item has exactly one Worksheet 🟢
Rationale: consistent mental model, consistent UI shape, consistent audit trail. Simple Items carry sparse content (a single Worksheet Resource line for a rate) — no separate “no-worksheet” mode. Refs: glossary §4 (Worksheet); concept-map Item.
BR-006 — Every Recipe has exactly one Worksheet 🟢
Rationale: Recipes are methodology on top of Resources; the Worksheet IS the methodology. Refs: glossary §4 (Recipe, Worksheet).
BR-007 — A Worksheet has exactly one parent — Item OR Recipe, not both 🟢
Rationale: Worksheet is always owned; no orphan Worksheets. Ownership determines behaviour (Recipe Worksheet has Input Parameters; Item Worksheet does not). Refs: concept-map Worksheet.
3.2 Items
BR-010 — Every Item has a Unit (required) 🟢
Rationale: cost = quantity × rate; without a Unit, that operation is undefined. Refs: glossary §9 (Unit); concept-map Item.
BR-011 — Every Item has an Item Type 🟢
Rationale: Type governs behaviour (schedule, provisional, rate-only, etc.). “Normal Item” is the default type. Item Types: Normal, Schedule, Provisional Sum, Rate-Only, Excluded/Included Elsewhere, Risk. Refs: glossary §5.
BR-012 — Provisional Items are only valid as Schedule Items 🟢
Rationale: provisional treatment affects what appears in the client submission. Nested internal Items cannot be provisional. Refs: glossary §5 (Provisional Item).
BR-013 — Rate-Only Items are only valid as Schedule Items 🟢
Rationale: rate-only (no committed quantity) is a submission-facing concept. Refs: glossary §5 (Rate-Only Item).
BR-014 — Excluded / Included Elsewhere statuses only apply to Schedule Items 🟢
Rationale: inclusions/exclusions are a client-facing concern; internal Items can’t be out-of-scope without being deleted. Refs: glossary §5.
BR-015 — Direct / Indirect is derived structurally by default 🟢
Rationale: any Item that is not a Schedule Item AND is not nested under one → indirect. Removes a user-decision step. Refs: concept-map §5 (derived attributes).
BR-016 — Indirect Cost flag overrides the structural derivation 🟢
Rationale: some Items sit under a Schedule Item structurally but represent overhead (supervision, preliminaries). Flag provides explicit override. Refs: glossary §5 (Indirect Cost).
BR-017 — Indirect Cost definition (what qualifies as indirect in Oxcon’s world) 🔴
Open: examples of Items that sit under a Schedule Item but should be flagged indirect. Need Oxcon input.
BR-018 — Normal Items can be flagged Inactive to preserve the record but exclude from costing 🟢
Rationale: soft-disable for items that were priced and then scratched, deferred, or made redundant. Keeps the history and allows easy reactivation without re-entering work. Behaviour:
- Inactive Items stay in the Estimate tree (not deleted)
- Their cost does not roll up to parent Items or Estimate totals
- UI renders them visually distinct (greyed / struck-through)
- Any Item can be toggled back to Active Applicability: Normal Items only. Schedule Items use Excluded or Included Elsewhere (BR-014) for the equivalent semantics, because those statuses carry client-facing meaning. Refs: glossary §5 (Item Flags).
BR-019 — Risk Items are a distinct Item Type for contingency/risk tracking 🟡
Rationale: risk and contingency allowances benefit from being tracked as discrete line items rather than folded into generic commercials adjustments. Risk Items are valued like any other Item but carry semantic meaning for reporting (Construction Budget risk review, portfolio exposure). Behaviour:
- Functions like a Normal Item but flagged semantically as Risk
- Always treated as Indirect Cost by default (the structural derivation + Indirect Cost flag still apply)
- Surfaced separately in Anomaly Review (flag: Risk Items unidentified / missing valuation)
- Visible as a distinct line in reporting Refs: glossary §5 (Item Types); BR-011 (Item Type list); BR-016 (Indirect Cost flag).
BR-019a — Item Status derivation: Unpriced / Plugged / Priced 🟡
Derivation: the lower three Item statuses are computed automatically from Worksheet content and plug_rate presence:
| Worksheet has cost-contributing children | plug_rate set | Derived status |
|---|---|---|
| No | No | Unpriced |
| No | Yes | Plugged |
| Yes | (auto-cleared) | Priced |
“Cost-contributing children” means ≥1 Worksheet Resource, Worksheet Recipe, or sub-Item producing non-zero cost.
Reviewed and Locked are manual/cascaded states built on top of Priced — they are not derived from Worksheet content (once Reviewed, further edits downgrade back to Priced manually, not via derivation).
Refs: glossary Item Status entry; Item entity spec §5.
BR-019b — plug_rate and Worksheet cost mutual exclusion 🟡
Rule: an Item cannot simultaneously have a plug_rate set AND Worksheet cost-contributing children. If the estimator adds cost to the Worksheet, plug_rate is cleared automatically (with a confirmation prompt). Conversely, setting a plug_rate when the Worksheet is already populated is rejected.
Rationale: the two are alternative pricing paths — a direct rate entry OR a build-up, not both.
Refs: BR-019a; Item entity spec §8 invariants 13–14.
BR-019c — Submission block: Estimate cannot submit while any Item is Unpriced or Plugged 🟡
Behaviour: the Estimate Submit action is gated by every Item having status ∈ {Priced, Reviewed, Locked}. Items in Unpriced or Plugged block the Submit action with a clear anomaly list.
Not blocked: navigation to the Publisher / submission preview is allowed — estimators can see exactly what would go out, which Items are missing pricing, and what the current totals look like. Only the final Submit action is blocked.
Rationale: prevents accidental submission of rough numbers without creating friction for in-flight preview / review.
Refs: BR-019a; BR-075 (Publisher Output); BR-094 (Estimate cascade on Submit).
BR-019d — Distinction between Plugged (Item status) and Plug Rate (Resource flag) 🟡
Rule: Plugged (Item Status) and Plug Rate (Resource flag) are distinct concepts:
- Plugged — Item’s rate was entered directly at Item level; no Worksheet build-up. Blocks submission (BR-019c).
- Plug Rate — flag on a Resource marking it as placeholder pricing. Lives on Resources in Price Books. Flagged by Anomaly Review when used in any Worksheet, but does not change the Item’s status.
A Priced Item can contain Plug Rate Resources in its Worksheet and still be submission-ready. The Plug Rate Resources are surfaced in Anomaly Review for resolution but don’t force Item status changes.
Refs: Item entity spec §5, Examples D and E; Resource entity spec (Plug Rate flag).
3.3 Resources
BR-020 — Every Resource has a Unit (required; LS valid) 🟢
Rationale: required for rate math. Lump-sum items use LS.
Refs: glossary §9 (Unit).
BR-021 — Every Resource belongs to exactly one Price Book 🟢
Rationale: Price Book is the source-of-truth container. No free-floating Resources. Refs: glossary §7 (Price Book); concept-map Resource.
BR-022 — Resource Types are fixed 🟢
Rationale: each type has baked-in behaviour (Flag set, adjudication path, Anomaly checks). User-created types would be empty shells. Flex happens via Categorization. Refs: glossary §6.
BR-023 — Flag Catalog is admin-defined; Flags picked per Resource filtered by Resource Type scope 🟡
Rationale: admin-managed catalog of flags with scope per entry (All / single Resource Type / multiple Types). User picks applicable catalog entries when creating/editing a Resource. Narrow, curated list. Refs: Resource entity spec §4; glossary Flag entry.
BR-024 — All Subcontract Resources live in a Price Book (user-created OR system-generated) 🟢
Locked rule: every Subcontract Resource lives in a Price Book, consistent with BR-021. There are two routes for a Subcontract Price Book to exist:
- User-created — admin/estimator manually creates a Price Book for a subcontractor with known indicative pricing
- System-generated — created automatically by a Subcontract Package Adjudication on award (see BR-063)
The Subcontractor (Company with Subcontractor role) is the Supplier on either kind. System-generated Price Books are hidden from the default Price Book list (see BR-054).
Refs: glossary §7; BR-021, BR-054, BR-063.
BR-025 — Resource Type “Other” has no Flags and no type-specific Anomaly checks 🟢
Rationale: “Other” is the deliberate catch-all for things that don’t fit the four main types. Refs: glossary §6.
BR-026 — Full Flag + Modifier catalog seeding at launch 🟡
Rationale: the catalog MODEL is now locked (BR-023, BR-027); only the specific catalog entries and defaults need Oxcon confirmation during admin catalog setup.
BR-027 — Modifier Catalog is admin-defined; Modifiers carry a math operation + value 🟡
Definition: Admin maintains a Modifier Catalog with entries defining:
- Name and description
- Scope (applies to All Resource Types / single Type / multiple Types)
- Math operation (one of:
quantity_multiplier/rate_adder/lump_sum_add/total_multiplier) - Value unit (%, $, unit-agnostic, etc.)
- Default numeric value (optional)
When a user picks a modifier on a Resource, they enter a numeric value (defaulted from catalog entry if provided).
Rationale: curated, reusable modifiers with predictable semantics; clear scope prevents confusion about applicability. Refs: Resource entity spec §4b; glossary Modifier entry.
BR-028 — Modifier values flow Resource → Worksheet Resource as defaults; per-instance override allowed 🟡
Snapshot & override model: When a Worksheet Resource is created from a Resource (drag-in, Recipe use, duplication), modifier default values are snapshotted.
Estimator can override any modifier value per-use:
- Override is local to that Worksheet Resource instance only
- Overrides do NOT propagate back to the underlying Resource
- Each Worksheet Resource carries its own modifier values (isolated from other Worksheet Resources using the same Resource)
Rationale: per-estimate flexibility; Resource-level defaults provide starting points but don’t force uniformity across uses. Refs: Resource entity spec §4b; Worksheet entity spec; BR-040 (snapshot mechanism).
BR-029 — Modifier math application order 🟡
Application sequence (deterministic): When multiple modifiers apply to a Worksheet Resource, they’re applied in this order:
quantity_multiplier— adjust quantityrate_adder— add/subtract from rate- (qty × rate) — compute intermediate subtotal
lump_sum_add— add/subtract flat amounttotal_multiplier— apply final multiplier
Rationale: predictable, predictable; avoids ambiguity in stacking order. Matches estimator expectations (modifiers as adjustments that cascade, not commutative operations). Refs: Resource entity spec §4b; Worksheet Resource.
3.4 Recipes
BR-030 — Every Recipe has at least one Input Parameter AND declares an Output Unit 🟢
Rationale: Input Parameters drive the Recipe’s internal calculation; the Output Unit declares how the Recipe’s result is expressed when used in an Item. A Recipe evaluates to a rate per Output Unit, which the host Worksheet multiplies by a quantity.
Example — “Concrete Pump” Recipe:
- Input Parameters:
Concrete Quantity (m³),Number of Trips - Internal calc: determines cost of mobilising, running, and demobilising the pump based on inputs
- Output Unit:
day(orpump, depending on how Oxcon prefers to express it) - Result: a $X/day rate. When dropped into an Item, the estimator supplies a quantity (days) and the contribution = rate × days.
Output Unit is declared on the Recipe definition; the Recipe’s Worksheet produces cost-per-Output-Unit.
Refs: glossary §9 (Input Parameter, Unit); concept-map Recipe.
BR-031 — Recipes live in a shared Recipe Library accessible to all Estimates 🟢
Rationale: Recipes are company-level knowledge (e.g., “2-person concrete crew”). Locking them to a single Estimate would destroy the reuse value. Refs: glossary §4 (Recipe).
BR-032 — An Item can be promoted to a Recipe via the Recipe Builder 🟢
Rationale: captures the “I built this up for one estimate but I’ll use it again” workflow. Access paths to the Recipe Builder:
- From an Item’s Worksheet — promote this Item’s build-up into a reusable Recipe
- From an Item’s Worksheet — create a brand-new Recipe from scratch (inline with the current estimating context)
- From the Recipe Library — create a new Recipe or edit an existing one Refs: glossary §4 (Recipe Builder).
BR-033 — A Recipe’s total cost is computed by evaluating its Worksheet with supplied Input Parameter values 🟢
Rationale: Recipe cost is not a stored field; it’s computed by running the Recipe’s Worksheet logic with the current Input Parameter values provided by the host Worksheet. Refs: concept-map §5 (derived attributes).
BR-035 — Items can be duplicated within or across Estimates 🟢
Behaviour: copying an Item duplicates:
- The Item itself and its attributes
- Its Worksheet and all child entities: Variables, Calculation Blocks, Content Blocks, Worksheet Resources, Worksheet Recipes
- Snapshot rates on Worksheet Resources are carried forward as-is (same snapshot semantics, BR-040)
Links behave as follows on copy:
- References to shared entities (Recipes in the Recipe Library, Resources in Price Books) remain as references — not cloned
- Program Task links are not carried — they’re Tender-scoped and don’t traverse Estimates
- Categorization and Code tags are copied within-Tender; cross-Tender copy drops Tender-specific tags
Scope: copy works within an Estimate (between Headings/sections), between Estimates in the same Tender, and across Tenders. Refs: Item, Worksheet, BR-040 (snapshot semantics).
3.5 Worksheets
BR-040 — Worksheet Resources store a snapshot of the Resource’s rate, Unit, flags, and modifier default values 🟢
What a Worksheet Resource holds (two kinds of data):
- A pointer to the underlying Resource (for identity — “which Resource is this?“)
- Two categories of data:
- Snapshot fields (copied from the Resource at drag-in, frozen after):
rate,Unit,flags - Estimator fields (set per-use, always editable):
quantity(value or expression),wastage factor,notes,ordering
- Snapshot fields (copied from the Resource at drag-in, frozen after):
What happens when the underlying Resource changes:
| Change | Effect on existing Worksheet Resource |
|---|---|
| Resource rate updated in Price Book | Snapshot rate unchanged. Anomaly Review flags divergence. |
| Resource flags changed | Snapshot flags unchanged. Divergence flagged. |
| Resource Unit changed | Snapshot Unit unchanged. Divergence flagged (rare case). |
Modifier overrides (BR-028): When a Worksheet Resource is created from a Resource, modifier default values are snapshotted. Estimator can override any modifier value per-use (local to that Worksheet Resource only; overrides do not propagate back). | Resource deleted | Worksheet Resource becomes an orphan — Anomaly Review flags. |
The “push through” action: When an estimator sees a divergence in Anomaly Review and decides to accept the new Resource values, they explicitly push the update through:
- Snapshot fields refresh to current Resource values
- Estimator fields (quantity, wastage, etc.) are preserved
- Divergence flag clears
Worked example:
- Time T1: estimator drags “Steel rebar @ $2.50/kg” into Worksheet. Worksheet Resource snapshotted with rate=$2.50, Unit=kg.
- Estimator sets quantity=1000kg, wastage=5%. Line total = $2,625.
- Time T2: supplier updates rate to $2.80/kg in the Price Book.
- Anomaly Review shows: “Steel rebar: worksheet rate $2.50, current rate $2.80”.
- Estimator decides either:
- Push through → Worksheet Resource rate becomes $2.80, line total becomes $2,940. Quantity and wastage preserved.
- Keep as-is → nothing changes. Line total stays $2,625, but the divergence flag stays on Anomaly Review until dismissed or pushed.
Rationale (why this design):
- Estimate stability — rates in Price Books update constantly (supplier price changes, new adjudications). Silent propagation would mean an estimate’s numbers shift between when you checked them yesterday and when you publish today.
- Audit trail — “at time of pricing, this Item used rate $X” is recoverable.
- Explicit control — estimator decides when to accept new pricing, not the system.
- Visibility — divergence can’t be ignored silently; it’s always in Anomaly Review.
Refs: concept-map Worksheet Resource; Anomaly Review (glossary §9).
BR-041 — Worksheet Recipes store a snapshot of Input Parameter values 🟢
Rationale: same principle — Recipe changes shouldn’t silently propagate into existing estimates. Refs: concept-map Worksheet Recipe.
BR-042 — Snapshots do not auto-sync when the underlying Resource or Recipe changes 🟢
Rationale: determinism. An estimate in review should not shift under the estimator’s feet. Refs: concept-map §5 (not-derived stored).
BR-043 — Snapshot divergence is flagged by Anomaly Review; estimator explicitly pushes updates through 🟢
Rationale: divergence needs to be visible (otherwise stale rates sneak into submission). Push-through action is explicit and auditable. Refs: glossary §9 (Anomaly Review).
BR-044 — Worksheet child entities carry their own ordering 🟢
Rationale: Variables, Calculation Blocks, Content Blocks, Worksheet Resources, Worksheet Recipes are all orderable within a Worksheet. The Worksheet itself has no single list order. Refs: concept-map Worksheet.
BR-045 — Canonical terminology for Worksheet Resource / Worksheet Recipe 🔴
Open: consider rename to Resource Line / Build-up Line for estimator friendliness. See also concept-map §9 open items for the same question.
BR-046 — Production Rate links quantity and duration 🟡
Definition: Production Rate expresses work output per unit of time (e.g., 100 m³/day, 25 m/hr). It is not a separate entity — it’s a named Variable pattern inside a Worksheet.
Three scenarios:
- Quantity + explicit Production Rate → duration derived (
duration = quantity ÷ production_rate) - Quantity + linked Program Task duration → Production Rate derived implicitly (
production_rate = quantity ÷ task_duration) - All three present → consistency-checked; divergence flagged by Anomaly Review
Rationale: unifies time-and-quantity reasoning across estimating and program linking. Uses the existing Variable/Calculation Block infrastructure — no new entity needed.
Refs: BR-102 (Program Task linking); Anomaly Review (glossary §9).
BR-047 — Default rate-edit behaviour on a Worksheet Resource: per-instance override 🟡
When an estimator edits the rate on a Worksheet Resource, the default behaviour is to change only that specific Worksheet Resource’s snapshot_rate. Other Worksheet Resources using the same underlying Resource — whether in this Estimate or any other — are unaffected. The underlying Resource in the Price Book is not changed.
The Worksheet Resource’s snapshot_rate diverges from the current Price Book rate; this divergence is surfaced by Anomaly Review (BR-042/043) so the estimator can see exactly which rates are locally overridden.
Estimators can choose two alternative actions instead of a per-instance override (BR-048 and BR-049).
Rationale: matches the existing snapshot-override mental model, no new concepts, audit-logged, simplest behaviour. Rate edits are a normal part of estimating and shouldn’t require explicit decision-making every time.
Refs: BR-040 (snapshot semantics); BR-042/043 (divergence flagging); BR-048, BR-049.
BR-048 — “Apply to this Estimate” bulk rate override 🟡
Estimator can explicitly elevate a rate change from per-instance (BR-047) to Estimate-scoped.
Behaviour:
- Finds all Worksheet Resources within the current Estimate that reference the same underlying Resource
- Updates each of their
snapshot_ratevalues to the new rate (the estimator’s edited value) - Does not change the underlying Resource in the Price Book
- Does not affect Worksheet Resources in any other Estimate
- Triggers BR-049a cascade (Reviewed Items on affected Worksheet Resources drop back to Priced)
- Audit-logs: old rate, new rate, count of affected Worksheet Resources, affected Items list
Prospective behaviour: new drag-ins of the same Resource after a bulk override still snapshot from the Price Book’s current rate (the override is retroactive only). If the estimator wants future-proof consistency within the Estimate, they should consider BR-049 (fork to new Resource in the Estimate’s Project-Specific Price Book). Inconsistency (same Resource used at two different rates within one Estimate) is flagged by Anomaly Review.
Rationale: captures the real use case of “this entire project uses $240/m³ concrete, not $230” — bulk update in one action, without forking Resources.
Refs: BR-047, BR-049, BR-049a; Anomaly Review.
BR-049 — “Fork to new Resource” creates a Resource in the Estimate’s Project-Specific Price Book 🟡
Estimator can explicitly fork a modified Worksheet Resource into a new, distinct Resource.
Behaviour:
- If the current Estimate doesn’t already have an auto-generated Project-Specific Price Book, one is created (named e.g. “Estimate {estimate_number} — Project Overrides”; type = Project-Specific; source_type = user-created)
- A new Resource is created in that Price Book
- The new Resource inherits from the original: description (estimator can rename during fork), Unit, Resource Type, Flags, Modifiers (with defaults), Categorization Options
- The new Resource takes the edited rate as its
rateattribute - The current Worksheet Resource’s reference is updated to point at the new Resource (fresh snapshot from new Resource)
- Other Worksheet Resources referencing the original Resource are unchanged
- The Project-Specific Price Book is archived along with the Estimate at end-of-life
Rationale: when a rate change is semantically a distinct Resource (“pile driving — offshore conditions” is a genuinely different rate, not an override), forking creates a clean, nameable artifact that can be reused within the Estimate without confusion. Prevents sprawl by keeping forks scoped to the Estimate.
Refs: BR-047, BR-048, BR-052 (Project-Specific Price Book).
BR-049a — Rate change on a Worksheet Resource cascades affected Items from Reviewed back to Priced 🟡
When any of the three rate-edit mechanisms (BR-047 per-instance override, BR-048 bulk “Apply to this Estimate”, BR-049 fork) changes a Worksheet Resource’s effective rate, any parent Item that was in status Reviewed automatically cascades back to Priced.
Rationale: the Item’s rate has materially changed; the senior review that approved the prior number is no longer current. Review must be re-done before submission. Items in Locked state are protected by their cascade from Estimate submission (BR-094) and are not affected by rate edits.
Refs: BR-047, BR-048, BR-049; BR-094 (Estimate lock).
3.6 Price Books & Suppliers
BR-050 — External Price Books must have a Supplier 🟢
Rationale: External Price Books represent supplier pricing; without a supplier the type is meaningless. Refs: glossary §7 (Price Book Type).
BR-051 — Internal Price Books do not require a supplier 🟢
Rationale: Internal = in-house reference rates. Refs: glossary §7.
BR-052 — Project-Specific Price Books do not require a supplier but must be scoped to a Tender 🟢
Rationale: project-specific rates apply only to that Tender’s Estimates. Refs: glossary §7.
BR-053 — Resources inherit their parent Price Book’s scope 🟢
Rationale: Resources inherit their parent Price Book’s scope (date range, region, project/tender link). No per-Resource scope override in v1; Categorization provides orthogonal tagging. Refs: glossary §7.
BR-054 — System-generated Price Books are hidden from the default Price Book list 🟢
Rationale: Price Books generated by Subcontract Package Adjudications (BR-024, BR-063) exist as a backend modelling convenience — they aren’t curated content the user browses or edits directly. They are filtered out of the default Price Book list view. They can be surfaced via a “show system-generated” toggle or navigated to via their Adjudication. Behaviour:
- Marked with a
source_type=adjudication(vsuserfor user-created) - Read-only from the Price Book UI (rates are set by the Adjudication workflow, not direct edit)
- Deletion is tied to the Adjudication lifecycle, not manual user action Refs: BR-024, BR-063, BR-067.
BR-053a — Price Books auto-archive when scope_end_date < today 🟢
Rationale: Expired Price Books are logically removed from new Worksheet Resource creation but existing snapshot references are retained for audit and reconstruction purposes. Behaviour:
- Logical check at query time; status auto-transitions to Archived
- Read-only and not offered for new Worksheet Resource creation
- Existing Worksheet Resources retain their snapshot references to archived Price Books Refs: BR-053, BR-040 (snapshots).
BR-053b — Multiple Price Books per Supplier for the same date range are allowed 🟢
Rationale: Different product categories or service lines may require separate Price Books even for the same Supplier and date range. Constraints:
- Multiple Price Books per Supplier for the same date range are allowed (e.g., different product categories)
- Names must be unique workspace-wide Refs: BR-050 (External Price Books), BR-051 (Internal).
3.7 Adjudications
BR-060 — Price Book Adjudication scopes over Resources selected from the Estimate 🟢
Rationale: scope = cross-Estimate Resources (any type — Materials, Labour, Plant). Not tied to a bundle of Items. Refs: glossary §7.
BR-061 — Subcontract Package Adjudication scopes over a Subcontract Package (bundle of Items) 🟢
Rationale: scope = the entire scope of work given to a subcontractor; priced as a bundle. Refs: glossary §7.
BR-062 — Price Book Adjudication updates rates on existing Resources (by replacement) 🟢
Rationale: a Resource on an Item doesn’t get multiple rates stacked — the winning supplier’s rate replaces the current one. Multiple rounds are supported — each round enters Draft → Adjudicated; re-awarding produces a new round (consistency with Subcontract Package Adjudication). Behaviour:
- Each Adjudication round updates rates on Resources selected from the Estimate
- Multiple rounds supported (round 1, round 2, etc.)
- Each round has its own Draft → Adjudicated lifecycle Refs: concept-map §5 (stored vs derived); BR-062 (Price Book Adjudication scope).
BR-063 — Subcontract Package Adjudication creates/updates a system-generated Price Book; Items reference Resources in it 🟢
Model: on award, a Subcontract Package Adjudication creates (or updates) a system-generated Price Book (BR-054) containing Resources of Type Subcontract — one Resource per Item in the Package. The Subcontractor (winning Company) is the Price Book’s Supplier. Each Item in the Package gets a Worksheet Resource pointing to its corresponding Resource in that Price Book.
Behaviour across rounds:
- First adjudication round on a Package → creates the system-generated Price Book and populates it with awarded Resources
- Subsequent rounds → updates the same Price Book. Each Item’s Resource is replaced in place (same Resource identity, new rate/flags). Worksheet Resources referencing those Resources keep their link; divergence is surfaced via standard snapshot mechanics (BR-040)
- Different Subcontractor wins the re-award → the system-generated Price Book’s Supplier updates to the new Subcontractor
Rationale: one subcontract rate per Item per Package at any time. Routing through a Price Book unifies the Resource model (BR-021, BR-024) and gives every rate a traceable container. Note: per Subcontract Package. An Item can be in multiple Packages and carry one Subcontract Resource per Package (each in its own system-generated Price Book). Refs: BR-024, BR-054, BR-021; concept-map Subcontract Package Adjudication.
BR-064 — Adjudications can be re-opened (Adjudicated → Draft) 🟢
Rationale: scope changes, new supplier pricing, errors discovered post-lock. Re-opening is explicit and auditable. The parent Estimate’s state determines lock status. Behaviour:
- Adjudications can be re-opened at any time while the parent Estimate is in an editable state (In Progress / Reviewed)
- Once the Estimate transitions to Submitted (or cascaded states — Won / Lost / Archived), Adjudications are frozen alongside the Estimate
- Re-opening returns the Adjudication to Draft state; Resources persist until re-adjudicated (BR-067) Refs: glossary §7 (Adjudication); BR-094 (Estimate state cascade); BR-067 (Resource persistence).
BR-065 — A Subcontract Package can have multiple Adjudication rounds 🟢
Rationale: mirrors reality — packages often go out to tender more than once. Item membership in the Package is gated by the Adjudication’s state. Behaviour:
- Multiple rounds supported per Subcontract Package
- Items in the Package can be added or removed while the current Adjudication round is in Draft state
- Once the Adjudication is locked (Adjudicated), Item membership is frozen until re-opened (which cascades to re-adjudication)
- Each round produces or updates a system-generated Price Book (BR-063) Refs: concept-map Subcontract Package; BR-063 (system-generated Price Book); BR-067 (re-open behaviour).
BR-066 — Adjudicated Subcontract Resources retain a lineage pointer to the Adjudication that produced them 🟢
Rationale: audit trail — “where did this rate come from?” Any Subcontract Resource set by an Adjudication carries a reference back to the specific Adjudication round. Refs: concept-map Resource.
BR-067 — Re-opening a locked Adjudication — existing Resources persist until re-adjudicated 🟢
Behaviour: when an Adjudication is re-opened (from Adjudicated → Draft), the Resources in its system-generated Price Book remain unchanged. The Items’ Worksheet Resources continue to reference them as before. The Price Book is only updated when the re-opened Adjudication is locked again with the new award (BR-063). Rationale: preserves the Estimate’s committed state during re-work. The Estimate never briefly “loses” its pricing while an Adjudication is being rerun. Refs: BR-054, BR-063; concept-map Subcontract Package Adjudication.
BR-068 — Adjudication six-step workflow (applies to both Price Book and Subcontract Package) 🟢
Workflow:
- Generate — assemble the comparison package:
- Price Book Adjudication: select Resources from across the Estimate
- Subcontract Package Adjudication: define the Subcontract Package (Items bundle)
- Export / Send — export the package as Excel or PDF to send to competing suppliers or subcontractors
- Import — import the priced returns back into the Adjudication
- Compare — side-by-side view of competing returns; apply +/- variances for inclusions/exclusions
- Normalise — add missing items to competing returns for like-for-like comparison (transient to the Adjudication, not written back to the underlying Resources or Items)
- Transfer to buildup — the selected winner is applied to the Estimate:
- Price Book Adjudication: winning rates replace existing Resources (BR-062)
- Subcontract Package Adjudication: winning prices populate the system-generated Price Book (BR-063)
Rationale: a single consistent workflow across the two Adjudication flavours simplifies UX and training. The mechanics of export/import/compare are the same; only the scope (Resources vs Items) and the transfer mechanism differ.
Refs: BR-060 / BR-062 (Price Book Adjudication scope & transfer); BR-061 / BR-063 (Subcontract Package Adjudication scope & transfer).
BR-068a — Normalisation items are transient to the Adjudication 🟢
Items added during the Normalise step (BR-068 step 5) exist only within the Adjudication for like-for-like comparison. They are not written back to the Estimate’s Item tree or to the underlying Resources. On transfer (step 6), only the selected winner’s pricing (including any normalisations that affect it) is applied.
Refs: BR-068.
3.8 Commercials & Submission
BR-070 — Commercials Rules apply in sequence order; rules apply to running state of scope 🟢
Rationale: order matters because later rules can stack on earlier ones (e.g., margin applied after contingency). Rules apply sequentially to the running state of each rule’s scope at the time the rule executes, not to the original baseline. The order in which rules are arranged determines final totals. Refs: glossary §8 (Rule).
BR-068b — Adjudication return import uses an Import Wizard 🟢
Rationale: standardized, user-friendly import process for supplier and subcontractor priced returns. Behaviour:
- Excel is the primary format for return imports
- The Wizard provides:
- Smart column-mapping (auto-detection of Price Book structure)
- Preview validation (check for errors before commit)
- Manual field override (estimator can adjust mapped fields)
- Shared UX pattern with the client Schedule import at Tender level Refs: BR-068 (Adjudication workflow step 3: Import).
BR-068c — Variance, inclusion, and exclusion notes persist on Items as Content Blocks 🟢
Rationale: notes captured during adjudication provide line-level context and audit trail after award. Associating them with Items (not the Adjudication) keeps the trail attached to the line regardless of Adjudication state. Behaviour:
- Variance, inclusion, and exclusion notes captured during adjudication are stored as Content Block instances on the affected Item’s Worksheet — not on the Adjudication itself
- Notes persist on the Items after award, providing an audit trail and context at the line level
- Content Blocks are user-defined or system-provided (BR-130) Refs: BR-068 (Adjudication workflow step 4: Compare, step 5: Normalise); BR-130 (Content Blocks).
BR-069 — Supplier/Subcontractor role enforcement at first gate 🟢
Rationale: ensures only properly-configured Companies are added to Adjudications, preventing role mismatches downstream. Behaviour:
- Supplier/Subcontractor role enforcement is validated at the first gate — whichever of (adding the Company to the Adjudication as a competing supplier/subcontractor) or (importing their priced return) happens first
- The Company must carry the matching Company Role (BR-082, BR-083)
- Error raised if role is missing at gate entry Refs: BR-082 (Company Roles); BR-083 (role seeding).
BR-069a — Subcontract Package has no pre-qualified Subcontractor list 🟢
Rationale: Supplier/Subcontractor selection lives in the Adjudication workflow, not on the Package itself. This decouples the Package definition (Items scope) from the competitive selection process. Behaviour:
- A Subcontract Package does not carry a pre-qualified Subcontractor list
- Supplier/Subcontractor selection lives in the Adjudication workflow (step 1: Generate)
- The Package itself is purely a scope (Items bundle); competition is run through Adjudication Refs: BR-068 (Adjudication workflow).
BR-071 — A Rule has a type: Percentage / Lump Sum 🟢
Rationale: two fundamental rule shapes. Rate Adjustment was considered and dropped — estimator-level rate adjustments happen via Submission Value overrides (BR-072), not as a Commercials Rule type. Refs: glossary §8.
BR-072 — Submission Values are computed by Commercials but manually over-ridable 🟢
Rationale: estimators always get a final manual pass before publish. Commercials does the math; estimator has veto. Refs: glossary §8 (Submission Value).
BR-073 — Full list of Rule scope targets 🟡
Scope targets brainstormed across three dimensions:
By structural position:
- All — apply to the whole Estimate (baseline)
- Direct-only — structurally derived: Schedule Items + items nested under them
- Indirect-only — structurally derived inverse, plus items with Indirect Cost flag
- Specific Heading subtree — everything under a chosen Heading
By entity attribute:
- Item Type — Schedule / Normal / Provisional / Rate-Only
- Item Flag — e.g., all Indirect-flagged items
- Specific Item — one-off surgical adjustment
- Resource Type — Labour / Material / Plant / Subcontract / Other (applies to the cost of Resources of that Type across the Estimate)
- Specific Supplier — all Resources from Supplier X (useful for e.g. “add 3% to this supplier’s pricing only”)
By classifier:
- Categorization Option — e.g., all items categorized “Mechanical”
- Code value — e.g., all items with Workcentre = “Civil”
- Combinations (AND/OR) — v2 likely
Recommended v1 scope targets:
- All
- Direct-only / Indirect-only
- Specific Heading (subtree)
- Item Type
- Resource Type
- Categorization Option
- Code value
- Specific Item
Deferred to v2: Specific Supplier, combinations (AND/OR), cost/quantity ranges.
Open: confirm v1 list with Oxcon; check whether Specific Supplier is needed at launch (it’s simple enough to add but needs a UI surface). Refs: BR-070, BR-074.
BR-074 — Rules apply linearly in sequence; no cross-rule dependencies or conflict resolution 🟢
Rationale: simplicity and predictability. Each Rule takes the current running total (after preceding Rules) and applies its adjustment. No Rule references another Rule; no precedence graph; no conflict resolution. Refs: glossary §8; BR-070.
BR-074a — Lump Sum Rule scope across multiple Items: proportional distribution 🟢
Behaviour: when a Lump Sum Rule targets a scope containing multiple Items, the lump sum amount is divided proportionally across those Items based on their cost share within the scope.
Formula: item_share = item_cost / total_in_scope_cost × lump_sum_amount
Example: A Lump Sum Rule adds $10k to “Mechanical” Items. Scope contains three Mechanical Items with costs $100k, $50k, $30k (total $180k). Lump Sum is distributed:
- Item 1: ($100k / $180k) × $10k = $5,556
- Item 2: ($50k / $180k) × $10k = $2,778
- Item 3: ($30k / $180k) × $10k = $1,667
Each affected Item receives its proportional share.
Rationale: Lump Sum Rules are indivisible; when applied across multiple Items, proportional sharing is the only fair distribution method. Refs: BR-070, BR-071, BR-073.
BR-075 — Publisher Output retains latest version only 🟢
Rationale: previous drafts are not kept. Each publish replaces the prior output. Version history lives in the Estimate’s state (Submitted / Locked), not in the Publisher. Refs: concept-map Publisher Output.
3.9 Companies & Users
BR-080 — Companies sync from Xero; Xero → oxFlow is one-way; no Phase 2 bi-directional plan 🟢
Sync direction: Xero → oxFlow only. oxFlow does not push Company changes back to Xero.
Rationale: Xero is the financial source of truth. Creating companies in two places would cause drift. Read-only sync is simpler, safer, and aligns with the current architecture.
Phase 2 plan: any bi-directional sync (if needed) is deferred beyond Phase 1 scope.
Refs: glossary §10, §11.
BR-080a — Archival cascade: Company archived in Xero auto-archives in oxFlow 🟢
Behaviour: when a Company is archived in Xero, oxFlow automatically archives it on the next sync (scheduled or on-demand).
Visibility in oxFlow:
- Archived Companies remain visible in UI for historical reporting and past-relationship visibility
- Archived Companies are read-only — cannot be selected for new relationships (new Tender, new Estimate, new Adjudication)
- Archived Companies can be un-archived (sync-driven)
Rationale: archival preserves history while preventing accidental selection of out-of-business suppliers or inactive clients.
Refs: BR-080.
BR-081 — Users sync from Microsoft 365; not created directly in oxFlow 🟢
Rationale: M365 is the identity source of truth (SSO, directory). Refs: glossary §10, §11.
BR-082 — A Company can carry any combination of Company Roles 🟢
Rationale: a supplier can also be a subcontractor — oxFlow shouldn’t force duplicate Company records. Refs: glossary §10 (Company Role).
BR-083 — Company Role is seeded from Xero metadata on sync; default fallback is Supplier 🟢
Rationale: reduce manual setup; Xero customer → Client, Xero supplier → Supplier; ambiguous → Supplier. Refs: glossary §10.
BR-084 — Every User has exactly one Role 🟢
Rationale: simpler permissions model for v1. Multi-role users can be added later if needed. Refs: glossary §10 (Role).
BR-085 — Only Admins can edit admin-managed lookups 🟢
Rationale: protects shared config (Units, Categorizations, Content Block Definitions, Branding). Refs: glossary §10 (Admin Role).
BR-086 — Role assignment happens in oxFlow after M365 sync 🟢
Rationale: M365 brings identity; oxFlow decides access. Keeps IT separate from product admin. Refs: glossary §10.
3.10 Tenders & Estimates
BR-090 — A Tender must have a Client 🟢
Rationale: a pricing opportunity without a client is undefined. Client is held at the Tender level (not the Estimate level). If the same scope is being priced to two different main contractors, create two Tenders — in practice this is rare at Oxcon and typically involves different scope variations anyway, so treating them as distinct opportunities is pragmatic and accurate. Refs: concept-map Tender.
BR-091 — A Tender can have multiple Estimates 🟢
Rationale: estimators often build base + alternative + strategy variants. Refs: glossary §3.
BR-092 — Tender state transitions; Admin + Lead Estimator can transition 🟢
State transitions:
- Active → Submitted (Publisher delivers)
- Submitted → Won / Lost (client decides)
- Active → Archived (not pursued)
- Submitted → Archived (winds down)
Terminal states: Won, Lost, Archived.
Permissions: Admin and Lead Estimator (role-based) can trigger state transitions. For a Tender with multiple Lead Estimators across its Estimates, any Lead Estimator can transition the Tender state.
Rationale: Tender-level state decisions are typically made by estimation leadership; role-based access keeps it simple without needing Tender-specific permission assignments.
Refs: concept-map §7; roles-permissions.md.
BR-093 — Every Estimate has a Lead Estimator (required) 🟢
Rationale: the Lead Estimator is the nominal owner for reporting, accountability, and audit trail purposes. Not an access-control gate — v1 runs a flat model where any User with the Lead Estimator Role can perform Lead Estimator actions on any Estimate (see roles-permissions.md §2).
The Lead Estimator field’s purpose:
- Identifies who is primarily accountable for this Estimate
- Appears in reports and the Tender/Estimate list views
- Surfaced in audit logs (when they exist)
It does not restrict who can edit, lock, submit, or publish — those capabilities are driven by Role alone (BR-084, BR-085).
Refs: glossary §3, §10; roles-permissions.md §2 (scoping).
BR-093a — Estimate In Progress → Reviewed transition is automatic 🟢
Trigger: the Estimate transitions from In Progress → Reviewed automatically when all Items in the Estimate have status = Reviewed.
Reverse transitions:
- Reviewed → In Progress can happen manually (estimator re-opens the Estimate for editing)
- Reviewed → In Progress can happen automatically if any Item drops below Reviewed status via cascade (e.g., BR-049a: rate change cascades Items from Reviewed back to Priced)
Rationale: once all Items are reviewed, the Estimate as a whole is reviewed. Manual re-opening allows estimators to add/adjust Items. Automatic cascade ensures consistency — if underlying assumptions shift (e.g., rate change), the Estimate status reflects that.
Refs: BR-049a, item-status lifecycle.
BR-094 — Locking an Estimate — fully locked; cascade from Tender state 🟢
Submission gate (per BR-019c): Estimate cannot be Submitted while any of its Items are in Unpriced or Plugged status. Submit action blocked with an anomaly list; navigation to the Publisher preview remains allowed.
Behaviour on submission: once an Estimate is submitted, it’s fully locked — no fields editable. Audit trail and commitment integrity.
Cascade from Tender state:
| Tender state | Effect on Estimates |
|---|---|
| → Won | The one submitted Estimate is kept and promoted into a project (project management flow, outside the Estimate itself). All other Estimates under the Tender are archived. |
| → Lost | All Estimates under the Tender are archived. |
| → Archived | All Estimates under the Tender are archived. |
Rationale: once the Tender closes, only the relevant Estimate (the winning one, if any) survives. Losing tenders shouldn’t leave open Estimates cluttering the workspace. Archived Estimates are read-only but recoverable.
Refs: concept-map §7 (states); BR-092.
BR-095 — Real-time collaboration on Estimates (explicit per-Item locking) 🟡
Model: multiple Users with access can open the same Estimate simultaneously. Editing locks apply at Item granularity:
- When a User begins editing an Item (or any of its Worksheet children), the Item enters a lock state
- Other Users see it as “currently edited by [User]” — read-only
- The lock releases on:
- User closes/navigates away from the Item
- Idle timeout (default 10 minutes — admin-configurable)
- Admin override
Concurrent editing on different Items is unrestricted — multiple Users can work across the Estimate simultaneously.
Rationale: “real-time collaboration” per the proposal (A12.4) with the simplest model that avoids concurrent-edit conflicts. Explicit locking is safer and cheaper than Google-Docs-style OT/CRDT for a financial document.
🔴 Open: confirm model with Oxcon in workshop. Alternative is Google-Docs-style field-level concurrent editing — more elegant but significantly more complex to build and operate.
Refs: User, Estimate, Item.
3.11 Programs & Tasks
BR-100 — The Tender Program is uploaded at the Tender level 🟡
Rationale: programs typically apply to the whole opportunity, not a specific estimate variant. Refs: glossary §9 (Tender Program). Open: confirm with Oxcon.
BR-101 — A Program Task can be linked to one or more Items 🟡
Rationale: a single task (e.g., “concrete works”) often maps to several Items. Confirm with BR-103 below. Refs: concept-map Program Task.
BR-102 — Linking a Program Task to an Item adds a duration Variable to the Item’s Worksheet 🟢
Rationale: task duration is the primary thing estimators want to reference from the program. Refs: glossary §9 (Variable, Tender Program).
BR-103 — Program Task ↔ Item is M:M 🟢
Confirmed M:M cardinality — an Item can be linked to multiple Program Tasks, and a Program Task can be linked to multiple Items.
Example scenarios justifying M:M:
- Task referenced by many Items: “Contractor Supervision” cost Item references the project’s overall duration — that duration is driven by many Program Tasks (e.g., piling + structure + fit-out). The same top-level task can equally be referenced by “Site Accommodation”, “Project Management Fees”, etc.
- Item referencing many Tasks: “Bridge Piles” Item references two Program Tasks (
Piling North,Piling South). In the Item’s Worksheet, the estimator uses both as variables:Dur_Piling_North + Dur_Piling_South.
Each linked Task exposes its duration as a Variable inside the Item’s Worksheet (named per the Task, e.g., Dur_Piling_North).
Refs: concept-map Program Task; BR-102.
BR-104 — Program Task durations are adopted directly from MS Project export 🟢
Behaviour: oxFlow does NOT recalculate durations. Task durations from the .mpp file export are used verbatim.
Rationale: MS Project already includes working-day calendar math, dependencies, and resource levelling. Recalculating in oxFlow would duplicate logic, introduce divergence, and risk silent errors. The MS Project schedule is the source of truth for duration.
Scope: applies to all Program Tasks imported from the Tender Program upload. Task duration is a fixed value in oxFlow, not a recalculated field.
Refs: BR-100, BR-142.
BR-105 — Summary-task handling: only summary task duration exposed as Variable 🟢
Behaviour: when an Item links a summary task (a task with subtasks in the MS Project schedule):
- Only the summary task’s duration is exposed as a Variable (e.g.,
Dur_Piling_Works) - Individual subtasks are NOT exposed as separate Variables, even though they exist in the schedule
Rationale: summary tasks represent the estimating cost driver (e.g., “all piling work”). Exposing every subtask would clutter the Worksheet with granular-level tasks the estimator doesn’t care about. If an estimator needs subtask-level detail, they can inspect the Program/schedule directly; Variables are for costing references.
Refs: BR-102, BR-103, BR-104.
BR-106 — Program re-upload conflict resolution: Task ID governs 🟢
Behaviour: when a Tender Program is re-uploaded (e.g., updated schedule), oxFlow uses Task ID to determine whether a task is the same or new:
| Scenario | Outcome |
|---|---|
Same task_id on re-upload | Same task; update fields (duration, name, etc.) |
New task_id on re-upload | New task; created separately |
| Task missing from re-upload (orphan) | Flagged for Items that linked it; Import Wizard notes |
Rationale: Task ID is the stable identifier across schedule iterations. Name changes (or renaming) don’t delete the task; only explicit removal does (flagged as orphan).
Management: Import Wizard (BR-068b) displays conflicts and allows manual override (e.g., map old task_id to new one if the ID changed unexpectedly).
Refs: BR-100, BR-102, BR-103, BR-068b.
3.12 Units & Conversions
BR-110 — LS is one of the standard Units 🟢
Rationale: lump-sum pricing is a normal unit type alongside m, m², hr, etc. Nothing special about it — it sits in the Unit library like any other Unit. Refs: glossary §9 (Unit).
BR-111 — Unit conversions are done manually in the Worksheet (v1) 🟢
Rationale: conversion factors are domain-specific (concrete density varies, pipe lengths vary). Auto-conversion risks silent errors. Estimator handles in Worksheet logic. Refs: glossary §9.
BR-112 — Unit reconciliation anomaly check (heuristic TBD) 🟡
Rationale: it’s entirely normal and expected for Resource Units and Item Units to differ (e.g., Labour priced in hr, a Material in m³, rolling up to an Item priced in m²). Simple “units differ” is not a useful anomaly — it would fire on almost every Item. The check needs a smarter heuristic that flags suspicious combinations, not different ones.
Open: what precisely does this check look for? Candidate heuristics:
- Flag when an Item’s build-up has no clear conversion path to the Item’s Unit (e.g., only Resources in
hron an Item priced inm²with no time-to-area relationship declared) - Flag when the Item’s computed total cost per Item Unit falls outside a sane range given its Resources’ Units
- Flag specific mismatched combinations known to cause errors (domain-specific rules) Needs Oxcon input to define the rule. Refs: glossary §9 (Anomaly Review); BR-111.
BR-113 — Unit library is admin-managed and extensible 🟢
Rationale: admin can add new units (e.g., project-specific units like truckload).
Refs: glossary §9.
3.13 Categorization & Flags
BR-120 — Categorization never affects system behaviour 🟢
Rationale: the deliberate contract — Type governs behaviour, Categorization is for reporting. Keeping this clean prevents the model degenerating into “any attribute can change anything.” Refs: glossary §9 (Categorization, Type).
BR-120a — Categorizations and Categorization Options use soft-delete (archive) 🟢
Behaviour: Categorizations and their Options cannot be hard-deleted. Instead, they are archived (soft-deleted):
- Archived entries remain visible on historical entities that used them (for reporting, history)
- Archived entries are not available for new selections (new Items, new Resources, etc.)
- Archived entries can be un-archived by admin if needed
Rationale: historical integrity — past estimates used specific Categorizations that no longer reflect current taxonomy. Archiving preserves the record without forcing retroactive updates.
Refs: BR-120, BR-121.
BR-121 — A Categorization defines its own scope 🟢
Rationale: flexibility — some categorizations apply only to Items, others to Resources, others cross-cut. Refs: glossary §9.
BR-121a — Categorization inheritance: sub-Items inherit parent’s Categorization Options 🟢
Behaviour: when a sub-Item is created under a parent Item:
- The sub-Item inherits the parent Item’s Categorization Options at creation time
- The Estimator can override the inherited value on a per-sub-Item basis
- Parent changes do NOT retroactively propagate to existing sub-Items (one-time inheritance at creation)
Rationale: reduces admin burden for homogeneous breakdowns (e.g., a “Mechanical” parent Item naturally has “Mechanical” sub-Items), while allowing exceptions without forcing template overhead.
Refs: BR-120, BR-121.
BR-122 — Flags are type-specific attributes on Resources 🟢
Rationale: Labour flags ≠ Material flags ≠ Plant flags. Each type has its own small set. Refs: glossary §6.
BR-123 — Item Flags exist; v1 set is minimal (Indirect Cost only) 🟢
Rationale: Items generally don’t need many flags; most behaviour lives on Item Type. Indirect Cost is the one exception. Refs: glossary §5.
BR-124 — Company Roles are a form of flag on Company (multi-select) 🟢
Rationale: Company Role shares Flag semantics (boolean domain-precision attribute) but gets its own label because the role-like meaning matters to users. Refs: glossary §10.
BR-125 — Codes are required classifiers on Items and Resources only 🟡
Scope: Codes apply to Items and Resources only. They do NOT apply to Tenders, Estimates, or other entities.
Definition: a Code is a required, admin-defined classifier on an entity, intended primarily for downstream integration with cost management software. Examples:
- Activity Code (required on every Resource) — for Workbench integration
- Workcentre (required on every Item) — for Workbench integration
- Other Codes as downstream systems demand
A Code shares most of its structure with a Categorization: it has a name, a scope (which entity type it applies to), and a set of allowed options. The key difference:
- Categorization = optional, for reporting
- Code = required, for integration
Rationale: splitting this out (rather than overloading Categorization with a “required” flag) keeps the reporting-vs-integration distinction clean. Codes are a first-class concept because they block key lifecycle transitions (e.g., can’t submit an Estimate if required Codes are missing). Constraining scope to Items and Resources keeps enforcement tractable.
Refs: glossary §9 (proposed addition); BR-125a. Open: which Codes ship by default vs. admin-defined; Oxcon input on the initial Code set for Workbench.
BR-125a — Custom Code creation is Admin-only 🟢
Behaviour: only Admins can create new Codes. Lead Estimators cannot create new Codes.
Lead Estimator interaction: Lead Estimators can assign existing Codes to Items and Resources during estimation. They cannot create or modify Code definitions.
Rationale: Codes are integration-critical (Workbench exports depend on them); unbounded creation risks downstream conflicts. Admin curation ensures Code taxonomy stays clean and Workbench-compatible.
Refs: BR-085, BR-125, BR-126.
BR-126 — A Code has the same flexibility as a Categorization (name, scope, options) 🟡
Rationale: admins can create, rename, and scope Codes the same way they do Categorizations. The only structural difference is the required flag and the integration-sourcing capability (BR-128). Refs: glossary §9.
BR-127 — Required Code values — enforcement tiered by context 🟡
Working proposal — enforcement tiered by how downstream the action is:
| Action | Enforcement |
|---|---|
| Estimating (working in the Estimate) | Soft — missing Codes shown in Anomaly Review, estimator notified but nothing blocked |
| Publishing to client (Publisher output) | Soft / Warn — warning surfaced, estimator can still publish (Codes don’t affect the client submission itself) |
| Integration export to Cost Management (e.g., Workbench) | Hard block — export cannot run until all required Codes are set on Resources and Items |
Rationale: the impact of missing Codes is at the integration layer (broken references in Workbench etc.), not at the client-facing submission. Hard-blocking the Publisher would frustrate estimators without protecting the system it actually matters for. Hard-blocking the export is where the teeth belong.
Open: confirm this tiered enforcement model with Oxcon. The alternative is hard-blocking at submission (simpler model but more friction).
Refs: glossary §9 (Anomaly Review); BR-125, BR-143 (Workbench integration).
BR-128 — Codes sync is one-time initial migration + on-demand manual refresh 🟢
Sync model: Code options are sourced from external cost management systems (e.g., Workbench) via a one-time initial migration, optionally followed by on-demand manual refresh.
Behaviour:
- Initial migration: admin configures a Code to pull from an external source (Workbench); initial sync brings in all current options
- On-demand refresh: admin can manually trigger a refresh to pull new/updated Code options from the source
- No scheduled sync: continuous/scheduled sync is NOT implemented in Phase 1
Rationale: avoids continuous sync overhead while allowing admins to stay current with Workbench’s master Code list when needed.
Refs: BR-125, BR-127, glossary §11 (Integrations, proposed addition).
BR-144 — Reference Rates are admin-defined benchmarks used in Anomaly Review; not versioned 🟡
Definition: a Reference Rate is an admin-defined expected unit rate for a category of work, used by Anomaly Review as the anchor layer for rate-based outlier detection. Not used in cost build-up.
Attributes:
name(e.g., “Reinforced concrete works”)Unit(e.g., m³)expected rate— point value with tolerance OR min/max range- Optional
Categorization Optionscope notes
Examples:
- Reinforced concrete works: $450–550/m³
- Bored piles: $650–850/lm
- Concrete deck pour: $380–450/m²
Versioning: Reference Rates are not versioned. Edits overwrite the prior value. Historical rate comparison at Anomaly Review Layer 3 is a separate AI mechanism using archived Price Books, not a Reference Rate version history.
Refs: glossary §9; Anomaly Review layers 2 and 3; BR-145, BR-146.
BR-145 — Reference Rate rules-first matching (Alpha) 🟡
Behaviour: Anomaly Review compares an Item against a Reference Rate when:
- the Item’s Unit matches the Reference Rate’s Unit, AND
- either the Reference Rate has no Categorization scope, OR the Item carries the scoped Categorization Option
Outside-range Item rates raise a firm anomaly flag with a deterministic explanation (e.g., “Unit rate $620/m³ exceeds Reference Rate range $450–550/m³”). Multiple Reference Rates can match one Item; all are surfaced.
Refs: BR-144, Anomaly Review Layer 2.
BR-146 — AI-assisted Reference Rate matching (Release) 🟡
Behaviour: when an Item isn’t matched by BR-145 (usually because the Item isn’t tagged with the right Categorization), the AI layer attempts a semantic match from the Item’s description to the Reference Rate library.
- High-confidence matches → raise a firm anomaly flag as if matched by BR-145
- Low-confidence matches → surface as “possible anomalies” that the estimator can accept or dismiss
- Acceptance/dismissal data is retained to improve future matching
- AI also explains why each flag was raised (cross-referencing similar items, historical rates, project context)
Milestone: Release (requires AI layer from proposal C4.3 / C4.4).
Refs: BR-144, BR-145, Anomaly Review Layer 2; proposal C4.3 / C4.4.
3.14 Content Blocks
BR-130 — Four Content Blocks ship by default: Inclusions, Exclusions, Task Breakdown, Risks 🟢
Rationale: these four cover the standard estimating vocabulary. “Risks” chosen over “Risk Breakdown” as more natural phrasing. Refs: glossary §9 (Content Blocks).
BR-131 — Admins can define additional Content Blocks 🟢
Rationale: captures Oxcon-specific needs (Assumptions, Access Notes, etc.) without hard-coding. Refs: glossary §9.
BR-132 — Content Block Definitions are system-wide; instances are per-Worksheet 🟢
Rationale: one definition (“Risks”), many instances (one per Worksheet using it). Refs: concept-map Content Block Definition vs Instance.
3.15 Integrations
BR-140 — Xero is the source of truth for Companies; oxFlow does not push changes back 🟢
Rationale: read-only sync keeps the data model simple and avoids reconciliation headaches. Refs: glossary §11.
BR-141 — Microsoft 365 is the source of truth for Users 🟢
Rationale: IT-managed identity; oxFlow layers access and roles on top. Refs: glossary §11.
BR-142 — MS Project .mpp is the expected Tender Program upload format 🟡
Rationale: matches Oxcon’s current toolchain. Open: support for alternative formats (XML export, CSV) deferred to v2. Refs: glossary §11.
BR-143 — Workbench is the downstream cost management system for won projects 🟡
Rationale: when an Estimate transitions from a won Tender into a project, it’s exported into Workbench for delivery / cost management. This is where Codes (BR-125) earn their keep — Activity Code (on Resources) and Workcentre (on Items) populate Workbench’s hierarchy.
Integration direction:
- oxFlow → Workbench: push at project promotion (one-way initially)
- Workbench → oxFlow: pull Code options (Activity Codes, Workcentres) to keep the Code libraries in sync (BR-128)
Enforcement on export: hard-block (BR-127) — export cannot run until all required Codes are set.
Open:
- Scope of the initial export (which entities sync over, at what granularity)
- Trigger (automatic on Tender = Won, or manual button)
- Error handling (retry, partial success, conflict resolution)
- Whether any data flows back from Workbench into oxFlow during project delivery (e.g., actual costs, committed costs)
Refs: glossary §11; BR-125, BR-127.