Skip to content
crop-data logo

Crop Data Context

crop-data

Crop planning data model and architecture. Use when working with crops.json, bed-plan.json, TimelineCrop, or plan data structures for: (1) Understanding entity relationships (Crop → PlantingConfig → Planting → Product), (2) Determining which fields are static inputs vs calculated, (3) Identifying...

SKILL.md

Full skill instructions

Crop Data Context

This skill provides essential context for working with the crop planning data model.

CRITICAL: Data Analysis Caveats

The extracted analysis files have known gaps:

FileIssue
column-analysis.jsonMisclassifies array formulas as "static" or "mixed"
formula-analysis.jsonMissing ~20+ formulas using SWITCH, complex XLOOKUP

Always verify against the actual workbook using:

python scripts/​inspect-column.py "STH"           # Inspect specific column
python scripts/​inspect-column.py --list          # List all columns
python scripts/​inspect-column.py "DTM" --sheet "Bed Plan"

Authoritative reference: vba/​workbook-structure.md has table schemas extracted directly from Excel.

Key Timing Formulas (FROM WORKBOOK)

STH (Seed To Harvest) - NOT static, despite what analysis files say:

SWITCH(Normal Method,
  "DS": Days to Germination + DTM + IF(Days in Cells > 0, 15, 0)
  "TP": DTM + Days in Cells + IF(Days in Cells <= 0, 20, 0)
  "X":  DTM
)

DTM - Calculated from range:

=FLOOR.MATH(AVERAGE(DTM Lower, DTM Upper))

Days in Cells - Sum of tray stages:

=SUM(Tray 1 Days, Tray 2 Days, Tray 3 Days)

Target dates:

  • Target Sewing Date = LastFrostDate - 5 * Sewing Rel Last Frost
  • Target Field Date = Target Sewing Date + Days in Cells
  • Target Harvest Data = Target Sewing Date + STH
  • Target End of Harvest = Target Harvest Data + Harvest window

Entity Model

GLOBAL CONSTANTS (farm settings: LastFrostDate, BedLength, LaborRate)
       │
       ▼
CROP (~100 unique) ─────────────────────────────────────────────────┐
│ name, category, deprecated                                        │
│ Same for all configurations of a crop                             │
       │                                                            │
       │ one crop → many configurations                             │
       ▼                                                            │
PLANTING CONFIGURATION (340 total)                                  │
│ structure (Field/​Hoop/​Greenhouse)                                 │
│ method (DS/​TP), season (Sp/​Su/​Fa/​Wi/​Ow)                          │
│ product type, DTM, spacing, rows                                  │
│ Identifier: "Arugula - Baby Leaf | Field DS Sp"                  │
       │                                    │                       │
       │ one config → many plantings        │ references            │
       ▼                                    ▼                       │
PLANTING (per-plan instance)          PRODUCT (processing info) ◄──┘
│ bed, bedsCount, startDate           │ Unit, Price, Yield
│ per-planting overrides              │ Wash type, labor times
│ actual vs planned dates (future)    │ Holding period

Data Source Files

FileSizeContentsNotes
src/​data/​crops.json1.7 MB340 configs × ~155 fieldsFat export, includes calculated
src/​data/​bed-plan.json170 KB~138 plantingsCurrent plan assignments
src/​data/​products.json213 KBProduct processing dataLabor times, packaging
column-analysis.json46 KBField classificationHas gaps - verify!
formula-analysis.json29 KBFormula DAGMissing array formulas!
vba/​workbook-structure.md-Table schemasAuthoritative reference

Planting Override Fields

These bed-plan fields modify catalog values per-planting:

Override FieldModifiesPurpose
additionalDaysInFieldSTH/​DTMWeather, conditions adjustment
additionalDaysOfHarvestHarvest windowExtended/​shortened season
additionalDaysInCellsDays in CellsGreenhouse timing adjustment

Computation pattern:

const effectiveDTM = catalog.STH + planting.additionalDaysInField;
const effectiveHarvestWindow = catalog.harvestWindow + planting.additionalDaysOfHarvest;

Timeline Date Computation

The timeline uses these inputs to compute dates:

From Catalog (crops.json):

  • STH - Total days from seeding to first harvest
  • Harvest window - Days of harvest period
  • Days in Cells - Greenhouse time (0 = direct seed)

From Planting (bed-plan.json):

  • fixedFieldStartDate - The target transplant/​direct-seed date
  • additionalDays* - Per-planting adjustments

Computed:

startDate = fixedFieldStartDate (the TP/​DS date)
harvestStartDate = startDate + (STH - daysInCells) = startDate + DTM
endDate = harvestStartDate + harvestWindow

Finding Things

Run from crop-api/ directory. Use patterns (not file paths) to locate code:

# Entity types (new canonical location)
node scripts/​ast-query.js "Plan"           # What does Plan contain?
node scripts/​ast-query.js "Planting"       # What does Planting contain?
node scripts/​ast-query.js "CropConfig"     # What does CropConfig contain?

# Find type definitions
grep -r "^export interface" src/​lib/​entities/

# Find calculation functions
grep -r "^export function calculate" src/​lib

# Find what uses entities
grep -r "from.*entities" src/​lib --include="*.ts"

# Find bed length logic
grep -r "lengthFt\|ROW_LENGTHS" src/​lib

# Find plan store actions
grep -r "^  [a-z]*:" src/​lib/​plan-store.ts | head -30

Data Extraction

Re-extract from Excel when workbook changes (run from crop-api/):

python scripts/​extract-products.py

Testing & Verification

Run from crop-api/:

npx tsx scripts/​test-entities.ts        # Entity validation
npx tsx scripts/​test-slim-planting.ts   # Date computation parity
npx tsx scripts/​test-catalog-parity.ts  # Catalog lookup parity
npx tsx scripts/​test-crop-calculations.ts  # Crop calc vs Excel

# Inspect actual Excel formulas
python scripts/​inspect-column.py "STH"
python scripts/​inspect-column.py --list

Architecture

New canonical types live in lib/​entities/:

  • Bed - bed with lengthFt (F/​J=20, A-E/​G-I/​U=50, X=80)
  • Planting - one per planting, references configId
  • CropConfig - planting configuration with calculations
  • Plan - self-contained: owns beds, cropCatalog, plantings

Key principle: Plans own their data. No external references.

// What we store
interface Plan {
  beds: Record<string, Bed>;           // Plan's bed layout
  cropCatalog: Record<string, CropConfig>;  // Plan's crop configs
  plantings: Planting[];               // One per planting decision
}

// Computed at render time
const timing = calculateCropTiming(planting, config);
const bedSpan = calculateBedSpan(planting.bedFeet, planting.startBed, plan.beds);

See .claude/​plans/​state-migration.md for full migration plan.