A block can currently pin only one output to a target rate. The block doc (BlockData in app/src/db/schema.ts) stores a single target + rate, plus extraGoals: string[] for additional co-products. Those extra goals are unpinned: in buildBlock (app/src/server/factorio.ts) they are just relabeled from surplus exports into "primary outputs" and come out at whatever ratio the chosen recipes happen to produce. There is no way to give them their own target rate.
This breaks down for blocks that intentionally make several end products. Example: a single "logistics" block producing transport belts, undergrounds, splitters, and loaders. I want to set an independent goal rate for each (e.g. splitters @ 2/s, undergrounds @ 4/s, belts @ 10/s), not pin one and let the rest fall out.
The solver already supports this. BlockInput.targets in app/src/solver/block.ts is a { name: string; rate: number }[] and each entry becomes a pinned equation. The gap is the block doc model and the UI, not the math.
Proposed change
- Extend
BlockData so every goal can carry its own rate. Either replace target/rate/extraGoals with a goals: { name: string; rate: number }[] list, or add per-name rates alongside the existing fields. Keep a migration that maps the current { target, rate, extraGoals } shape onto the new one (add a case in app/src/server/migrations.ts, covered by migrations.test.ts).
- Pass each goal as a pinned
target to solveBlock instead of relabeling surplus exports. Drop the "extra goals = relabeled exports" path in factorio.ts (a goal with no recipe producing it stays an unmet/over-pinned target the solver reports, same as today).
- UI in
app/src/routes/block.$id.tsx: let each goal row have an editable rate, with add/remove. The first goal still drives auto-naming and the default icon.
- Update everything that reads
data.target / data.extraGoals to iterate the goal list: db/queries.ts, factory-solve.ts, agent-tools.ts, the assistant draft-a-block path, and the coherence/browse views.
Notes / open questions
- Naming and the default icon currently key off the single primary goal; pick the first goal in the list for both.
- Decide whether a goal can also be a negative rate (a consumption target) — the solver already allows it, but the UI may not need to expose that yet.
Part of #31.
A block can currently pin only one output to a target rate. The block doc (
BlockDatainapp/src/db/schema.ts) stores a singletarget+rate, plusextraGoals: string[]for additional co-products. Those extra goals are unpinned: inbuildBlock(app/src/server/factorio.ts) they are just relabeled from surplus exports into "primary outputs" and come out at whatever ratio the chosen recipes happen to produce. There is no way to give them their own target rate.This breaks down for blocks that intentionally make several end products. Example: a single "logistics" block producing transport belts, undergrounds, splitters, and loaders. I want to set an independent goal rate for each (e.g. splitters @ 2/s, undergrounds @ 4/s, belts @ 10/s), not pin one and let the rest fall out.
The solver already supports this.
BlockInput.targetsinapp/src/solver/block.tsis a{ name: string; rate: number }[]and each entry becomes a pinned equation. The gap is the block doc model and the UI, not the math.Proposed change
BlockDataso every goal can carry its own rate. Either replacetarget/rate/extraGoalswith agoals: { name: string; rate: number }[]list, or add per-name rates alongside the existing fields. Keep a migration that maps the current{ target, rate, extraGoals }shape onto the new one (add a case inapp/src/server/migrations.ts, covered bymigrations.test.ts).targettosolveBlockinstead of relabeling surplus exports. Drop the "extra goals = relabeled exports" path infactorio.ts(a goal with no recipe producing it stays an unmet/over-pinned target the solver reports, same as today).app/src/routes/block.$id.tsx: let each goal row have an editable rate, with add/remove. The first goal still drives auto-naming and the default icon.data.target/data.extraGoalsto iterate the goal list:db/queries.ts,factory-solve.ts,agent-tools.ts, the assistant draft-a-block path, and the coherence/browse views.Notes / open questions
Part of #31.