Raw Config Layer
Stores the literal user and app state: cage.enabled, rearTrellis, wireSides, bedW, bedH, wallSide (locked to "back" when cage is on), season, goal, and crop metadata.
In v4.3, the cage and bed configuration still define geometry and structure, but the scoring engine is meant to consume a cleaner layer of truth stamped onto each cell. That means render, auto fill, warnings, inspect, and crop validity all read the same structural facts instead of recomputing trellis logic in multiple places.
The whole planner pipeline in one pass. This is the clean mental model for debugging, reviewing, or extending the scoring system.
Bed size, wall side (locked to back in cage mode), season, trellis toggle, goal, crop library, cage config, and current planted state.
Computes zone facts once, including trellis row, protected cells, support availability, critter safety, and access priority.
Stamps those derived traits directly onto each cell so the rest of the app reads the same structural truth.
Evaluates crop fit using cell traits, crop metadata, neighbors, season, and access rather than ad hoc cage checks.
Separates hard failures from advisories. A crop can be structurally impossible, merely suboptimal, or fully valid.
Auto fill, manual paint feedback, warnings, issues, inspect badges, and zone classes all consume the same result.
Only the zone derivation layer should interpret raw cage structure. Everything downstream should read cell traits, not re-decide what the cage means.
Each layer has one job. That is what keeps the planner explainable instead of turning into a pile of overlapping conditionals.
Stores the literal user and app state: cage.enabled, rearTrellis, wireSides, bedW, bedH, wallSide (locked to "back" when cage is on), season, goal, and crop metadata.
Translates raw config into planner semantics. This is where structure becomes usable intelligence.
Applies derived traits to each cell so score, render, warnings, and inspect all see the same facts.
Uses crop metadata plus cell traits to compute numeric fit, adjacency fit, and structural validity.
Auto fill and manual placement both route through the same scoring and validity logic, which means one planner brain instead of two.
Bed rendering, warning cards, inspect rows, issue icons, and score summaries visualize what the planner already knows.
| Layer | Owns | Should know raw cage state? | Should recompute trellis row? |
|---|---|---|---|
| Input | Storage, toggles, persistence, user settings | Yes | No |
| Zone derivation | Structural meaning per coordinate | Yes | Yes, once |
| Cell state | Trait stamping | No | No |
| Scoring | Suitability and fit | No | No |
| Validity | Hard fail vs advisory | No | No |
| UI | Classes, badges, notes, inspect | No | No |
This is the key abstraction. Cells should know their own structural reality so the planner can stop looking sideways at UI state every time it needs to reason.
True when the cell is on the dedicated climbing row (always row 0, since wall side is locked to "back" in cage mode).
True when the cell sits in the wire enclosed interior rows, excluding the trellis row and the open front row. The front row is critter safe but not protected, because it is the primary access and harvest side.
True when a crop can actually climb here, whether because it is the trellis row or because legacy support logic still allows it.
True when wire protection exists for that structural zone, even if it is not necessarily the plannerβs main protected crop zone.
A normalized reachability score. Front row cells are easier to harvest often, so succession crops can get an access bonus.
These values should be generated in one place, then reused everywhere else without reinterpretation.
Protected and critter safe are not the same thing. Protected means interior rows shielded by wire on all sides β it excludes the trellis row (row 0) and the open front row (last row). Critter safe is broader: any cell where wire mesh exists, including the front row. Keeping those separate avoids muddy scoring later.
| Trait | Type | Allowed values | Contract |
|---|---|---|---|
| isTrellisRow | boolean | true | false |
True only for the effective trellis row after runtime wall-side override. |
| isProtected | boolean | true | false |
True only for interior protected rows. Never true on row 0 or the front row. |
| hasVerticalSupport | boolean | true | false |
Derived support capability. May reflect stored trellis state when cage mode is off. |
| isCritterSafe | boolean | true | false |
True where wire protection exists, even if the cell is not fully protected. |
| accessPriority | number | 0.0 to 1.0 |
Normalized reachability score. Higher means easier harvest access from the front side. |
Cell: row 1, col 2 (8Γ4 bed, cage on) isTrellisRow: false (row 0 is trellis) isProtected: true (interior row, not open front row) hasVerticalSupport: false isCritterSafe: true (wire sides on) accessPriority: 0.67
This single object should contain everything the scoring system needs. If scoring requires additional cage logic, the trait model is incomplete.
When cage mode is enabled, wallSide is locked to "back" and the select is disabled. The physical cage has its trellis panel bolted to the back wall β left, right, front, and none are structurally invalid in cage mode. This simplifies zone derivation: trellis row is always row 0, open front row is always the last row.
The scoring engine now decides fit in two passes: one numeric and one structural. That gives the planner a better vocabulary than just βgoodβ or βbad.β
| Rule area | Example input | Outcome | User facing effect |
|---|---|---|---|
| Trellis reward | Cherry tomato in trellis row | Structural bonus increases score | Strong recommendation, green fit |
| No support penalty | Pole beans in non support cell | Score tanks and validity becomes hard fail | β border, issue icon, warning text |
| Protected reward | Lettuce in interior row | Protected zone bonus plus critter safety bonus | Higher score, better sanctuary placement |
| Space waste advisory | Lettuce in trellis row | Mild penalty, still plantable | β advisory: wasting prime climbing space |
| Access bonus | Radish near open front | Small bonus for frequent harvest convenience | Better sorting in auto fill and inspect |
| Factor | Weight | Description | Typical Impact |
|---|---|---|---|
| Season fit | +40 / -80 | Crop matches active season window | Hard filter if completely wrong season |
| Vertical support | +35 / -120 | Trellis crops must have support | Hard invalid when missing |
| Protected zone | +15 | Greens and herbs in interior rows gain protection benefit | Improves ranking for sanctuary crops |
| Critter safety | +10 | Wire protection reduces loss risk (all cage cells) | Minor ranking improvement |
| Access priority | +5 | Frequent harvest crops prefer easy reach (front side) | Minor optimization |
| Adjacency | Β±10 | Companion or conflict effects | Small tuning adjustment |
The recommendation builder should prioritize the ideal zone pool first, then fall back to generally valid crops, and skip hard invalid placements entirely. That keeps automated layouts realistic instead of forcing a zone theory that starves the rest of the bed.
The planner feels smart only when the same logic is visible everywhere the user looks. These are the main surfaces that should consume the shared trait and validity model.
Trellis and protected classes should come straight from cell.isTrellisRow and cell.isProtected. Hard invalid cells get the visual stop sign treatment.
Shows trait badges, support status, critter protection, access cues, and validity reason so the score is legible, not mysterious.
Aggregates hard failures and softer placement advice without repeating the same message three different ways.
Hard invalid gets β. Advisory gets β . This is the fast scan layer for spotting planner trouble across the whole bed.
Zones, score, and validity should all push the automatic plan toward climbers in back, protection crops in interior rows, and frequent harvest crops near the front side.
If score, render, and warning surfaces disagree, the trait model is being bypassed somewhere. That is the first bug smell.
The planner narrows from all possible crops down to one recommendation per cell. Each stage eliminates or reranks candidates.
All crops available for the season.
Remove crops that structurally cannot grow in the cell. Hard invalid crops are eliminated here.
Compute numeric suitability based on traits and crop metadata.
Sort remaining crops by best fit. Advisory flags surface but don't block.
Validity runs before scoring. A crop that is hard invalid should never appear in score results, even with a high base fit. The funnel only narrows β it never widens downstream.
When something looks wrong, start here. Each question points to the layer most likely responsible.