Grower Tools

Structured JSON contracts

Download Markdown reference

# Secondary grower modules — canonical input formats

Calculation version: **1.0.0**. Report contract version: **1.0.0**.

These dependency-free JavaScript functions accept parsed **canonical JSON objects**, perform deterministic analysis, and return plain JSON-compatible reports:

```js
import { analyzeLab, analyzeIPM, analyzeLog, analyzeEconomics, analyzeTrial } from './secondary.mjs';
const report = analyzeLab(JSON.parse(uploadedText));
```

No function reads a file, fetches a URL, executes uploaded instructions, writes a record, controls equipment, sends a message, or calls a model. The integrating application supplies upload validation, safe rendering, downloads and any process/time limits. This asset does not claim a tested CSV, XLSX, PDF, vendor-export or free-text extraction parser for these five modules.

## Shared envelope and limits

```json
{
  "data_label": "reported",
  "source_filename": "my-records.json",
  "source_id": "optional-user-source-id",
  "records": []
}
```

`data_label` is `measured`, `reported`, `synthetic_demo`, `mixed` or `unverified`; omission means `unverified`. It is a source declaration, not independently verified provenance. All shipped examples are explicitly `synthetic_demo`.

The companion uploader has a **5 MiB upload limit**. The engine independently rejects more than **20,000 records**, JSON above **8 MiB**, nesting deeper than 12, more than 1,000,000 nodes, or any individual string above 16,384 characters. Most labels are limited to 512 characters. The lower application limit governs the user workflow. Financial scenarios are limited to 50; protocol definitions to 100.

Numbers must be finite JSON numbers, not numeric strings. General numeric magnitudes are limited to 1e12; nonzero magnitudes below 1e-12 are unsupported. Counts and resets require nonnegative integers. Unknown numbers use `null` or an omitted field. Explicit zero remains zero. Positive effort, area and quantities used as denominators cannot be zero. Area and sold quantity require at least 1e-9 in their declared units.

All operational timestamps use `YYYY-MM-DDTHH:mm:ssZ` or an explicit numeric offset, for example `2026-11-01T01:30:00-05:00`. Ambiguous local times are rejected for user correction; no timezone is guessed. Calendar dates are additionally supported for lab sample dates, economic item dates and payment dates. Supported calendar years are 1900–2200. Economic period boundaries are inclusive `YYYY-MM-DD` dates.

Every row may include `source_filename`, `source_id`, `source_row` and `source_page`. Row/page references must be positive integers. The report also supplies a one-based `record_index`; it does not misrepresent that index as an original source row. Filename and evidence reference strings are labels only and are never used as filesystem paths or fetched URLs.

Every report includes:

- `contract_version`, `schema_version`, `calculation_version`, `product`, and declared provenance.
- `original_records`: **all original rows**, including rejected, duplicate and conflicting records, with original ingestion timestamps, metadata and correction fields intact.
- `source_metadata`: all original top-level fields other than `records`.
- `normalized`: accepted records, each with original `raw` values, normalized `values`, and an `evidence` reference.
- `established`, `calculations`, `possible_explanations`, `missing_or_rejected`, and `next_measurements`.
- Accepted/rejected counts and explicit gap/error messages. No demo replacement on failure.

An exact duplicate is excluded from calculations. Conflicting records with one identity are all excluded pending correction. Unknown metadata is retained in `original_records`; it is not silently interpreted. Structured JSON exports preserve raw text; the host must escape it when rendering HTML or Markdown and protect any future CSV export against formulas.

## 1. Streamline Soil & Water Lab

Example: `examples/lab.json`.

Each record requires `sample_id`, `sample_date`, `site`, and `analyte`. Optional `zone` identifies the sampling location. Interpretation fields:

| Field | Accepted meaning |
| --- | --- |
| `laboratory` | Supplied laboratory identity; required for comparison. |
| `matrix` | `field_soil`, `container_substrate`, `irrigation_water`, `tissue`, or `unknown`. |
| `method` | Exact analytical/extraction method and relevant conditions, including conductivity temperature compensation where applicable. |
| `context_key` | Matching sampling depth/location/procedure/crop context. Grower-defined, and required for comparison. |
| `analyte` | Laboratory analyte name. EC aliases normalize to `electrical_conductivity`; other analytes stay distinct. |
| `analyte_basis` | Explicit reported basis, such as `N`, `NO3`, `P`, `P2O5`, or `CaCO3 equivalent`. Required for non-pH/non-EC comparisons. |
| `value` | Numeric value or `null`. Non-pH values cannot be negative. |
| `units` | Explicit laboratory units. No default nutrient concentration unit is assumed. |
| `qualifier` | `exact`, `less_than`, `greater_than`, `estimated`, or `unknown`. Only exact values enter change calculations. |
| `detection_limit`, `uncertainty` | Nonnegative values in the declared units; retained alongside the result. |
| `uncertain_fields` | Array of extracted field names requiring confirmation. |
| `extraction_confirmed` | Explicit Boolean declaration that those fields have been confirmed. |

Unknown/missing methods, bases, units, matrix or context block comparison. Only samples sharing site, zone, matrix, laboratory, exact method, normalized units, analyte basis, context and date precision are compared. Different sample IDs and dates are required; endpoint replicates require an explicit aggregation protocol rather than silent averaging.

The only automatic lab unit conversions are conductivity: `uS/cm`, `µS/cm` and `μS/cm` multiplied by 0.001 to `mS/cm`; `dS/m` equals `mS/cm`. pH accepts `pH`, `dimensionless` or `1` as declared units, and remains distinct from alkalinity. There are no ppm-to-mg/L, soil-to-substrate, nutrient-compound or mass-basis conversions. Below-detection values never become zero.

Outputs include a sample inventory, method-matched earliest/latest differences, original values, uncertain fields and an agronomist-facing gap brief. No universal reference ranges, deficiency diagnoses or amendment prescriptions are provided.

## 2. Streamline IPM Planner

Example: `examples/ipm.json`.

Every row requires `record_type`, `timestamp`, `site` and `zone`. `record_id` provides deduplication identity. All types may include `crop`, `context_key`, `notes`, and `photos` (up to 100 reference strings, never fetched).

For `record_type: "observation"`, supply:

- `pest` and `identification`: `confirmed`, `tentative` or `unknown`.
- `count`: a nonnegative integer or `null`; `count_unit`, such as `individuals`.
- `sampling_method`, positive numeric `effort`, and `effort_unit`, such as `leaves` or `trap_days`.
- `crop` and `context_key`, identifying comparable sample selection.

Rate is `count / effort`. Temporal comparisons require matching location, crop, pest, sampling method, count unit, effort unit and context, plus a confirmed identification. A different amount of the same sampling effort can be normalized; different methods or contexts cannot. Duplicate-time endpoints require an explicit pooling protocol. Zero observations and absent counts are separate.

For `beneficial_release`, supply `beneficial`, `quantity`, `quantity_unit` and optional `status`. For `intervention`, supply `action`, optional `product`, and optional `status`. Activity status can be `planned`, `reported_complete` or `verified`; missing status defaults to source-reported completion. These are imported claims, not independently verified actions.

Outputs include a chronological and spatial timeline, count/effort rates, matched comparisons and repeat-scouting/identification follow-ups. Intervention timing does not establish efficacy. No pesticide rate, compatibility, registration, crop approval, PHI, REI, supplier availability or treatment recommendation is supplied.

## 3. Streamline Crop Log & Handoffs

Example: `examples/log.json`.

Rows require `timestamp`, `site` and `text`. Use `event_id` for identity; optionally provide `zone`, `author`, `assignee` and `event_type` (`observation`, `task`, `handoff`, `correction`). Missing authors remain unknown.

`status` can be `draft`, `assigned`, `reported_complete`, or `verified`. The default is `draft`. Imported `assigned` becomes `reported_assigned`, never a new assignment. A `verified` claim becomes `verification_documented` only with a `verifier`, a `verification_timestamp` at or after the event, and at least one `verification_evidence` string. Otherwise it becomes `verification_unsubstantiated`. Even documented verification is a source claim rather than this engine's physical verification.

`correction_history` accepts up to 100 objects with `timestamp`, `author`, `field`, `previous_value`, `new_value`, `reason`, optional `confirmed`, and optional `evidence`. All are preserved. Unconfirmed corrections remain proposals. Correction history does not silently mutate another record; supply the confirmed corrected record if the preview should use a new current value. `correction_of` can reference the original event ID.

Outputs group events by site, zone and assignee. `mode` is `preview_only`, `writes_performed` is zero, and the report explicitly states that no events were saved, no tasks assigned and no notifications sent. Persistence, confirmed write handling and tenant authorization are not implemented in this asset.

## 4. Streamline Crop Economics

Example: `examples/economics.json`.

Top-level required fields:

- `currency`: an explicitly declared three-letter uppercase currency code; no FX conversion.
- `period`: inclusive `start` and `end` calendar dates.
- `revenue_basis`: `gross_before_recorded_losses` or `net_after_recorded_losses`.

Optional top-level fields: `cycle_id`; `area: {value, unit}` with `m2`, `ft2`, `ha` or `acre`; paired `sold_quantity` and `sold_unit`; and `scenarios`.

Declare completeness explicitly with `coverage`, whose `revenue`, `variable_costs`, `losses`, `overhead`, `capital` and `cash` fields each use `complete`, `partial` or `unknown`. Missing scope declarations mean unknown. A scope declared complete with no line items is treated as a user-declared zero. Any rejected financial row fails closed for complete-model totals.

Each row requires `record_type` and `item`; use `record_id` to identify it. Types are `revenue`, `variable_cost`, `overhead`, `loss` and `capital`. Optional `date` must lie inside the production period; an undated item is included on the explicitly declared production-period basis and flagged.

Enter `amount`, or `quantity` × `unit_price` with `quantity_unit`. If both forms exist, they must agree. Amounts cannot be negative. A record's optional currency must equal the model currency. Preserve `evidence_kind` as `actual`, `reported`, `estimate`, `scenario` or `unknown`.

Variable costs require `category`: `labor`, `energy`, `consumables`, `packaging` or `other`. Every loss requires `treatment`:

- `informational`: retains quantity/value without subtracting it again.
- `revenue_deduction`: reduces gross revenue; rejected when revenue is already net of recorded losses.
- `variable_cost`: adds the explicitly valued loss to variable costs and shows it separately from ordinary cost categories.

Formulas:

```text
net revenue = entered revenue − explicit revenue-deduction losses
variable costs = entered variable costs + losses treated as variable costs
contribution margin = net revenue − variable costs
operating result after entered overhead = contribution margin − entered overhead
per-area result = result / explicitly entered area
break-even unit price at entered volume/cost = variable costs / sold quantity
```

These full totals remain `null` if dependent amounts or completeness declarations are missing. Known subtotals remain visible and explicitly partial. Capital expenditure is separate. Unentered depreciation, taxes, financing and owner compensation are not invented.

Cash requires explicit `cash_amount`, `cash_date` and `cash_status` (`actual` or `planned`) on each paid/to-be-paid record. Payment dates may fall outside the production period. No payment is inferred from an invoice amount or date. Known actual and planned net cash are separate; cash coverage completeness requires payment evidence for all economic rows.

Scenario objects require `name` and may supply signed `revenue_change`, `variable_cost_change`, `overhead_change`, nonnegative `capital_cost`, and positive `annual_cycles`. Omitted deltas hold the entered baseline unchanged. Negative resulting revenue or cost is rejected. Simple undiscounted payback uses entered capital divided by positive per-cycle operating improvement times entered annual cycles. It is an explicitly entered operational hypothesis, not predicted ROI, tax advice or investment advice.

## 5. SAGE Trial Reviewer

Example: `examples/trial.json`.

`protocols` contains definitions with required `id`, `version`, `task`, `success_definition`, and optional `duration_basis`: `elapsed_including_downtime`, `active_only` or `unknown`. Duplicate id/version definitions are rejected for confirmation.

Each run requires `run_id`, `condition_id`, `timestamp` and `site`. Additional fields:

| Field | Meaning |
| --- | --- |
| `zone`, `protocol_id`, `protocol_version`, `comparison_context` | Exact grouping/comparison context; missing protocol/context blocks condition comparison. |
| `evidence_kind` | `measured`, `reported`, `simulated`, `unverified`; defaults to unverified. |
| `outcome` | `success`, `failure`, `aborted`, `unknown`; missing is unknown. |
| `human_assistance` | True, false or null. Missing assistance does not imply autonomy. |
| `duration_s`, `downtime_s` | Nonnegative recorded durations, or null. Downtime cannot exceed elapsed duration when that basis is declared. |
| `resets` | Nonnegative integer or null. |
| `damage_occurred`, `damage_description` | True/false/null plus optional recorded description. |
| `excluded`, `exclusion_reason`, `exclusion_predefined` | Preserve every exclusion request. Only a reason plus explicit prespecified rule qualifies for the separate analysis-set exclusion. |
| `evidence_references`, `evidence_hash` | Original references and optional identity hash. Never fetched or treated as proof of physical truth. |

Cohorts split by location, condition, protocol version, comparison context and evidence kind. Measured, reported, simulated and unverified runs are never pooled into one success rate.

The primary denominator includes all accepted attempts, including failures, aborts and excluded attempts. The separate analysis set excludes only supported prespecified exclusions. Unknown outcomes keep the point success rate unknown; the report shows observed lower/upper bounds. Assisted successes remain successes but are separate from unassisted successes.

Recorded durations, downtime, resets and damage gaps remain visible. `successes_per_attempt_hour` uses the sum of attempt elapsed durations only when the elapsed basis and all durations/outcomes are known. It is not throughput per wall-clock site hour, particularly when runs overlap. Active-only timing is never presented as elapsed timing.

Matching protocol/context cohorts may be shown side by side descriptively. No randomization, independence, equal difficulty, significance, causal benefit, physical equipment validation or partnership is inferred.

## Primary reference notes

Checked 2026-09-14:

- [NIST unit conversion factors](https://www.nist.gov/pml/us-surveyfoot/revised-unit-conversion-factors): current international foot-based units. Area factors used here are 1 ft² = 0.09290304 m², 1 acre = 4046.8564224 m², and 1 ha = 10,000 m². Legacy US survey-foot land areas require explicit conversion outside this initial contract.
- [NIST SP 811](https://www.nist.gov/pml/special-publication-811): SI prefix/unit conventions underpin the conductivity scale conversion.
- [USGS Specific Conductance](https://pubs.usgs.gov/tm/09/a6.3/tm9-a6_3.pdf): laboratory/measurement temperature and method context matter when comparing conductivity. This engine converts declared units; it does not invent temperature correction.

The remaining calculations are the explicitly stated arithmetic definitions above. No agronomic thresholds or external treatment rules are embedded. Numerical verification is not independent agronomic validation.

## Reproducible verification and review scenarios

Run:

```sh
node --test secondary.test.mjs
```

Actual result after final raw-preservation integration: **45 tests passed; 0 failed**. Tests run the five shipped examples through their real engines; fixtures contain independent numerical expectations, not values computed by the engine under test.

| Module | Five successful-use scenarios | Three safe-failure/gap scenarios |
| --- | --- | --- |
| Lab | Convert conductivity units; compare matched samples; preserve pH separately; retain detection limits; confirm uncertain extraction | Mismatched matrices/methods; conflicting sample identities; compound bases or unknown extraction |
| IPM | Normalize effort; show explicit zeros; organize activity timeline; handle explicit DST offsets; retain photo evidence | Zero effort/negative counts; tentative identification; changed sampling methods |
| Log | Preview assignments; preserve reported completion; show documented verification; retain correction history; group handoff locations | Unsupported verification; conflicting IDs; malicious text stays inert |
| Economics | Cycle contribution; overhead result; per-area/unit comparison; actual/planned cash; explicit equipment scenario | Missing cost/coverage; currency or arithmetic conflict; duplicate loss deduction |
| Trial | All-attempt rate; assisted-success separation; prespecified analysis subset; evidence-kind separation; compatible descriptive comparison | Unknown outcome; changed/unknown protocol; impossible downtime/conflicting run ID |

Shared tests cover numeric strings/non-finite values, invalid calendar dates, row/text/depth limits, executable getters, prototype keys, synthetic labeling and no input mutation. Host upload/export, browser rendering, MCP protocol, tenant boundaries and deployment are integration gates handled by the release owner; this asset does not claim them passed.