1. Purpose

A Subcontract Package is a persistent scope object — a named bundle of Items selected from a single Estimate to be priced as a package by competing subcontractors. It is purely a scope container without internal state (state lives on the Subcontract Package Adjudications beneath it). Critically, the Package does not carry a list of pre-qualified Subcontractors; supplier/subcontractor selection lives inside the Adjudication workflow (step 1: Generate/select competing Subcontractors).

A Package can be re-adjudicated multiple times (scope changes, new suppliers) over its lifetime. Each adjudication round produces or updates a single system-generated Price Book containing Subcontract Resources — one per Item in the Package. This unified Price Book model ensures consistent pricing across rounds.

Analogy (glossary cooking model): the Subcontract Package is the shopping list — a named group of ingredients to be sourced from a single vendor, revisited when prices shift or suppliers change.


2. Attributes

AttributeTypeRequiredDefaultNotes
idUUIDgeneratedSystem-managed
namestring (short text)Human-readable name, e.g., “Electrical Package”, “Structural Steel”
scope_descriptionstring (long text)nullFree-text summary of what work is covered (inclusions/exclusions)
estimate_idEstimate refParent Estimate; Package is scoped to a single Estimate (BR-065)
created_at / updated_attimestampsystemAudit
created_by / updated_byUser refsystemAudit

Derived attributes (computed, not stored) — see §9.


3. No Internal State

The Subcontract Package itself carries no state machine or status attribute. State lives entirely on its child Subcontract Package Adjudications (BR-065):

  • A Package in Draft state means all its Adjudications are in Draft
  • A Package is Adjudicated if its latest Adjudication is Adjudicated
  • A Package can be re-opened by reopening its latest Adjudication (back to Draft)

This design keeps the Package lightweight and focused on its single responsibility: grouping Items for procurement.


4. Relationships

Inbound (things referring to Subcontract Package)

FromCardinalityNotes
Subcontract Package AdjudicationSPA M:1 PackageEach Adjudication round belongs to exactly one Package; a Package has one-or-more rounds
ItemItem M:M PackagePackage membership (Items can belong to multiple Packages; rare but allowed)

Outbound (things Subcontract Package references)

ToCardinalityRequiredNotes
EstimatePackage M:1 EstimateParent Estimate; Package is estimate-scoped (BR-065). Locked: a Package belongs to exactly one Estimate (no cross-estimate bundling in v1).
ItemPackage M:M ItemThe Items bundled for subcontract pricing; must have ≥1. Item membership is editable while current Adjudication is in Draft state; frozen when Adjudicated. Re-open Adjudication to Draft to modify Items (cascades to re-adjudication).
Subcontract Package AdjudicationPackage 1:M SPAAll adjudication rounds (one per re-pricing cycle)
Subcontractor listLocked: Package does NOT carry a pre-qualified Subcontractor roster. Supplier/Subcontractor selection lives in the Adjudication workflow (BR-068 step 1: Generate — the step where the Item bundle and competing Subcontractors are both assembled).

5. Validation / Invariants

Rules that must hold at all times:

  1. Exactly one Estimate. A Subcontract Package belongs to exactly one Estimate; cannot span estimates (BR-065).
  2. Non-empty Item set. A Package must contain at least one Item (enforced on create and on update).
  3. Items belong to parent Estimate. All Items in a Package must belong to the same parent Estimate (cascading validation on Item addition).
  4. Unique name within Estimate. Package names must be unique within their parent Estimate (simple key constraint).
  5. Item membership locked on Adjudication lock. Item membership is editable while the latest Adjudication is in Draft state. Once latest Adjudication is Adjudicated, Item membership is frozen. To modify Items, estimator must re-open the Adjudication (back to Draft), which cascades to re-adjudication (BR-065a).
  6. At least one Adjudication. A Package must have at least one Subcontract Package Adjudication (created atomically on Package creation).
  7. State consistency. A Package’s derived state is determined by its latest Adjudication; no separate state attribute is stored.
  8. No pre-qualified Subcontractor list. Package does not carry a pre_qualified_suppliers attribute or equivalent (BR-069a).

6. Worked Examples

Example 1 — Electrical Package: Multi-round Adjudication

Setup:

  • Estimate: Bridge Project, Rev A
  • Package: “Electrical Package” — covers 12 electrical Items (cabling, switchgear, lighting, etc.)
  • Initial scope: 12 Items, total Estimated value ~$85,000

Round 1 — First adjudication:

Subcontract Package:
  name: "Electrical Package"
  scope_description: "All electrical supply, installation, testing, and commissioning"
  estimate_id: <Bridge Project, Rev A>
  item_ids: [E01, E02, ..., E12] (12 electrical items)

Subcontract Package Adjudication #1:
  name: "Electrical Package — Round 1"
  round_number: 1
  status: Adjudicated
  competing_subcontractors: [SubCo A, SubCo B, SubCo C]
  awarded_subcontractor: SubCo A
  system_generated_price_book: <PB-ELEC-R1>
    source_type: system-generated
    supplier: SubCo A

  Price Book PB-ELEC-R1 contains:
    Resource #1: "Electrical Item E01" — rate $6,800, unit LS
    Resource #2: "Electrical Item E02" — rate $4,200, unit LS
    ... (12 Resources total, one per Item)

  Each Item's Worksheet now has:
    Worksheet Resource linking to its corresponding Resource in PB-ELEC-R1
    (e.g., Item E01's Worksheet has WR → Resource #1 @ $6,800)

Derived state of Package:
  latest_adjudication_status: Adjudicated
  current_system_generated_price_book: PB-ELEC-R1
  total_awarded_value: $52,400 (sum of all awarded Resource rates)

Round 2 — Scope increases; new suppliers:

Client requests two additional electrical items (E13, E14). Estimate is revised; 14 Items total.

Subcontract Package (updated):
  item_ids: [E01, E02, ..., E14] (14 items — E13, E14 added)

Subcontract Package Adjudication #2:
  name: "Electrical Package — Round 2 (scope increase)"
  round_number: 2
  status: Adjudicated
  competing_subcontractors: [SubCo A, SubCo D, SubCo E] (B drops out, D & E are new)
  awarded_subcontractor: SubCo D
  system_generated_price_book: <PB-ELEC-R2>
    source_type: system-generated
    supplier: SubCo D

  Price Book PB-ELEC-R2 (updated, same PB from Round 1):
    All 14 Resources now present
    Resources for E01–E12: re-priced by SubCo D (rates differ from SubCo A's Round 1 quotes)
    Resources for E13–E14: newly added by SubCo D

Derived state of Package:
  latest_adjudication_status: Adjudicated
  current_system_generated_price_book: PB-ELEC-R2 (same ID, contents updated)
  total_awarded_value: $59,800 (new SubCo D pricing across all 14 items)

Side-effect on Items:
  Items E01–E12 keep their Worksheet Resources pointing to PB-ELEC-R2
  (system silently updated the underlying Resource rates in PB-ELEC-R2)
  Items E13–E14 have Worksheet Resources newly created, referencing PB-ELEC-R2

Example 2 — Structural Steel Package: Single Estimate, Persistent Object

Setup:

  • Estimate: High-rise project
  • Package: “Structural Steel” — 8 Items bundled under steel section headings
Subcontract Package:
  name: "Structural Steel"
  scope_description: "Supply, fabrication, and erection of all primary and secondary structural steelwork"
  estimate_id: <High-rise project>
  item_ids: [S01, S02, ..., S08] (8 steel Items)

Subcontract Package Adjudication #1:
  round_number: 1
  status: Adjudicated
  awarded_subcontractor: Steel Fabricator Inc.
  system_generated_price_book: <PB-STEEL>

  All 8 Items now reference Resources in PB-STEEL.

Later (end of detailed design):
  Scope change identified — one Item (S05) must be re-detailed
  New Adjudication #2 opened to re-price:

Subcontract Package Adjudication #2:
  round_number: 2
  status: Draft
  (estimator can import new quotes from Steel Fabricator Inc. or invite new suppliers)

The Package persists; its name and Item membership remain stable across rounds. State flows through the Adjudication(s) beneath it.


7. Derived / Computed Attributes

AttributeDerivationNotes
current_adjudication_statusThe status of the latest (by round_number) Subcontract Package AdjudicationDraft or Adjudicated
current_system_generated_price_bookReference to the Price Book linked to the latest AdjudicationSingular across all rounds; ID remains stable, contents updated
total_awarded_valueSum of snapshot_rates of all Resources in current_system_generated_price_bookOnly meaningful if current_adjudication_status = Adjudicated
total_itemsCount of Items in item_idsUsed for Package summary display
item_unit_cost_varianceOptional: if Package’s Items span different Units, a flag indicating Unit mismatchHelpful for audit trail

8. Lifecycle (via Adjudication)

The Package itself has no explicit state machine. Its effective state is derived from its Adjudications:

Package is in Draft:

  • Its latest Adjudication is in Draft
  • No Resources have been awarded yet
  • Estimator CAN adjust the Item membership (add/remove Items)

Package is Adjudicated:

  • Its latest Adjudication is Adjudicated
  • System-generated Price Book is locked and in use
  • Items reference Resources from it
  • Item membership is FROZEN. Attempting to add/remove Items while latest Adjudication is Adjudicated must raise a validation error.

Package is Re-opened (to modify Items):

  • Estimator clicks “Re-open Adjudication” on the latest (Adjudicated) Adjudication
  • Adjudication state moves back to Draft
  • Resources remain in the Price Book but are marked as “pending update”
  • Estimator can now add/remove Items
  • On re-award (step 6 of BR-068 workflow), Price Book is re-adjudicated and updated in place