mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-07-12 13:28:55 +00:00
docs(adr): run record is an app-created task, not a new table
Amends ADR-0008 after challenge: the Modelling run reuses the BulkUpload trigger convention (app inserts the tasks row + config subtask with inputs JSON, passes task_id to the distributor) instead of a dedicated modelling_run table. No migration; status and progress come from the task system by construction; dispatch failure marks the task failed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
adcc9b8b83
commit
c7dfe95e15
2 changed files with 33 additions and 17 deletions
|
|
@ -105,7 +105,7 @@ A Scenario constraint that takes a measure off the table for the optimiser. The
|
|||
_Avoid_: inclusions, allowed measures (as a stored concept — UI may present "allowed/excluded" toggles, but what's captured is the exclusion set)
|
||||
|
||||
**Modelling run**:
|
||||
One user-initiated act of triggering modelling: a set of Scenarios crossed with one filter-defined property selection within a Portfolio. An insert-only record of *what was asked* — the Run filters, the Scenarios, who triggered it, when, and the matched-property count shown at preview — so Plans can be traced back to the selection that produced them. Execution progress is a separate concern (the pipeline's task records own it); a Modelling run is the request and its provenance, not the work. A Scenario is the *question*; a Modelling run *asks* it (possibly again, possibly for a different property selection). Re-running an already-modelled property appends a newer Plan; latest wins on read.
|
||||
One user-initiated act of triggering modelling: a set of Scenarios crossed with one filter-defined property selection within a Portfolio. A durable record of *what was asked* — the Run filters, the Scenarios, who triggered it, when, and the matched-property count shown at preview — so Plans can be traced back to the selection that produced them, alongside how the work is going. A Scenario is the *question*; a Modelling run *asks* it (possibly again, possibly for a different property selection). Re-running an already-modelled property appends a newer Plan; latest wins on read.
|
||||
_Avoid_: job, batch, trigger request, modelling task (the pipeline's execution records)
|
||||
|
||||
**Run filter**:
|
||||
|
|
|
|||
|
|
@ -23,14 +23,23 @@ explicit marker when trigger wiring lands. This is that wiring.
|
|||
|
||||
## Decision
|
||||
|
||||
- **A `ModellingRun` is recorded, insert-only, by Next.js at trigger time**:
|
||||
portfolio, the selected scenario ids, the Run filters, the matched-property
|
||||
count shown at preview, who triggered it, when. It is the request and its
|
||||
provenance — Plans become traceable to the selection that produced them.
|
||||
It carries no status column (mirrors ADR-0003's derived-state rule).
|
||||
- **The distributor receives `{run_id, portfolio_id, scenario_ids, filters}`**
|
||||
- **A Modelling run is recorded as an app-created task** — the BulkUpload
|
||||
trigger convention (`triggerAddressMatching`): Next.js inserts the `tasks`
|
||||
row (service `modelling_run`, `source_id` = portfolio, status `waiting`)
|
||||
plus one config `sub_task` whose `inputs` JSON carries
|
||||
`{scenario_ids, filters, previewed_property_count, triggered_by}`, then
|
||||
passes `task_id` to the distributor, which attaches its execution subtasks
|
||||
to the same task. **No new table, no migration.** Run history and the
|
||||
per-scenario in-flight badge read the portfolio's own `modelling_run` tasks
|
||||
(portfolio-scoped, a handful of rows) and parse the app-authored inputs
|
||||
JSON; execution progress ("61 of 104 ready") falls out of the existing
|
||||
subtask-count summary pattern.
|
||||
- **The distributor receives `{task_id, portfolio_id, scenario_ids, filters}`**
|
||||
— filters, never property-id lists. The payload stays tiny at any portfolio
|
||||
size; the run row is the durable copy of the same config.
|
||||
size; the config subtask is the durable copy of the same request.
|
||||
- **Dispatch failure marks the task `failed`** (an improvement on the
|
||||
bulk-upload flow, which leaves failed triggers at `waiting`), so history
|
||||
never shows a ghost run as pending.
|
||||
- **The modelling backend is the single authority for resolving filters to
|
||||
properties.** Resolution precedence: landlord-override fact → EPC-derived
|
||||
value (new-approach EPC graph, lodged over predicted, or legacy property-row
|
||||
|
|
@ -46,12 +55,11 @@ explicit marker when trigger wiring lands. This is that wiring.
|
|||
the static canonical enums plus Unknown. Filters are bounded (cap the
|
||||
postcode multi-select) as a UX guard; "all" is an absent filter, never an
|
||||
enumeration.
|
||||
- **Run status is derived by correlation, not stored**: the distributor stamps
|
||||
`run_id` on the task records the pipeline already writes
|
||||
(`tasks`/`sub_tasks`, service `plan_engine`). Scenario badges gain a third
|
||||
derived state — "Modelling in progress" — and a per-portfolio run history
|
||||
lists each run's filters, counts, initiator and derived status. No live
|
||||
progress bars in v1.
|
||||
- **Run status is the task's status** — no correlation machinery needed: the
|
||||
run and the execution share one record by construction. Scenario badges
|
||||
gain a third derived state — "Modelling in progress" — and a per-portfolio
|
||||
run history lists each run's filters, counts, initiator and derived status
|
||||
with subtask-count progress.
|
||||
- **Concurrent runs warn, never block**: triggering a scenario with a run in
|
||||
flight shows who started it and when, but proceeds. Plans append and
|
||||
latest-per-property wins on read, so overlap is safe; a hard lock could leak
|
||||
|
|
@ -59,6 +67,13 @@ explicit marker when trigger wiring lands. This is that wiring.
|
|||
|
||||
## Alternatives considered
|
||||
|
||||
- **A dedicated `modelling_run` table (+ scenarios join table)**: relational
|
||||
scenario linkage and typed columns for who/filters/counts, but a migration,
|
||||
a second record system beside tasks, and correlation machinery (either the
|
||||
backend stamping run ids into tasks, or the app storing returned task ids).
|
||||
The task system already records app-initiated pipeline work with inputs,
|
||||
progress and status — and the badge/history queries are portfolio-scoped
|
||||
over a handful of rows, so the JSON parse costs nothing that matters.
|
||||
- **Pass resolved property ids to the distributor**: makes preview-vs-run
|
||||
drift impossible by construction, but ~50k ids is a payload the async entry
|
||||
point can't take, and it moves the resolution rule into the app — the
|
||||
|
|
@ -74,9 +89,10 @@ explicit marker when trigger wiring lands. This is that wiring.
|
|||
|
||||
## Consequences
|
||||
|
||||
- Two backend asks must land with the distributor: accept + persist `run_id`
|
||||
into task records, and expose the dry-run/count endpoint sharing the
|
||||
resolution code path.
|
||||
- Two backend asks must land with the distributor: accept an app-created
|
||||
`task_id` and attach execution subtasks to it (rather than creating its own
|
||||
task, as the current `plan_engine` entrypoint does), and expose the
|
||||
dry-run/count endpoint sharing the resolution code path.
|
||||
- The preview's already-modelled callout and matched counts are only as fresh
|
||||
as trigger time; a property added between preview and trigger is picked up
|
||||
by the run (filters re-resolve) — the run row records the previewed count so
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue