Correct what is_key does, and record the trap in it
CLAUDE.md said is_key "marks dimensions used in slice WHERE clauses", which is not true and never was: assertSelective and buildWhere validate against filterCols, which is every dimension plus every date column regardless. What it actually drives is the key of a dim_group, value completion, the sibling autofill trigger, and the dim_period anchor -- and the first of those wants exactly one column per group while the others want several. When a group has more than one, find() silently takes the lowest opos, which is how segment_new outranked part and a master-data refresh keyed on a column that is null throughout: zero members, reported as success. Written down because the failure gives no signal at all -- the refresh succeeds, the list is simply empty, and every symptom appears somewhere else entirely. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
89026e3440
commit
708e5662ab
29
CLAUDE.md
29
CLAUDE.md
@ -58,7 +58,7 @@ ui/src/
|
|||||||
## Database schema (`pf`)
|
## Database schema (`pf`)
|
||||||
|
|
||||||
- **`pf.source`** — registered source tables
|
- **`pf.source`** — registered source tables
|
||||||
- **`pf.col_meta`** — column roles: `dimension` | `value` | `units` | `date` | `filter` | `ignore`; `is_key` marks dimensions used in slice WHERE clauses; `dim_group` groups functionally dependent columns (e.g. date + its derived year/month dimensions); `dim_period_col` maps a dimension to a `pf.dim_period` column so date-adjacent values are derived at load time rather than copied raw; `in_grain` flags dimension/date columns that define the **display grain** (see below)
|
- **`pf.col_meta`** — column roles: `dimension` | `value` | `units` | `date` | `filter` | `ignore`; `dim_group` groups functionally dependent columns (e.g. a part and its attributes, or a date and its derived year/month dimensions); `dim_period_col` maps a dimension to a `pf.dim_period` column so date-adjacent values are derived at load time rather than copied raw; `in_grain` flags dimension/date columns that define the **display grain** (see below); `is_key` is described under §`is_key` below
|
||||||
- **`pf.version`** — named forecast scenarios; `exclude_iters` (default `["reference"]`) blocks those iter values from all operations
|
- **`pf.version`** — named forecast scenarios; `exclude_iters` (default `["reference"]`) blocks those iter values from all operations
|
||||||
- **`pf.fc_{tname}_{version_id}`** — one forecast table per version; contains both operational rows (`pf_iter = baseline|scale|recode|clone`) and reference rows (`pf_iter = reference`). Indexed on `pf_logid`, which is how undo, the change-log aggregate and the grain key all find their rows — without it each is a sequential scan of the whole table. Tables created before that index was added do not have it.
|
- **`pf.fc_{tname}_{version_id}`** — one forecast table per version; contains both operational rows (`pf_iter = baseline|scale|recode|clone`) and reference rows (`pf_iter = reference`). Indexed on `pf_logid`, which is how undo, the change-log aggregate and the grain key all find their rows — without it each is a sequential scan of the whole table. Tables created before that index was added do not have it.
|
||||||
- **`pf.log`** — audit log; every write gets one entry; `slice` + `params` stored as jsonb
|
- **`pf.log`** — audit log; every write gets one entry; `slice` + `params` stored as jsonb
|
||||||
@ -67,6 +67,33 @@ ui/src/
|
|||||||
- **`pf.session`** — express-session store (connect-pg-simple layout)
|
- **`pf.session`** — express-session store (connect-pg-simple layout)
|
||||||
- **`pf.dim_period`** — calendar lookup table (2018–2035); one row per month keyed on `sdat` (month start date); provides cal/fiscal year, quarter, and month columns; populated by `setup_sql/gen_dim_period.sql` with a configurable fiscal year start month
|
- **`pf.dim_period`** — calendar lookup table (2018–2035); one row per month keyed on `sdat` (month start date); provides cal/fiscal year, quarter, and month columns; populated by `setup_sql/gen_dim_period.sql` with a configurable fiscal year start month
|
||||||
|
|
||||||
|
### `is_key`
|
||||||
|
|
||||||
|
Read in four places, and **not** the one it sounds like — slices are validated
|
||||||
|
against `filterCols`, which is every `dimension` plus every `date` column
|
||||||
|
regardless of `is_key`.
|
||||||
|
|
||||||
|
1. **The key of a `dim_group`** — `resolveGroup()` in `routes/sources.js` takes
|
||||||
|
`members.find(c => c.is_key)`, the column every other member is keyed on for
|
||||||
|
`pf.dim_member`
|
||||||
|
2. **Value completion** — only `is_key` columns get a dropdown, and
|
||||||
|
`/sources/:id/values/:col` refuses anything else
|
||||||
|
3. **Sibling autofill** — fires on blur only when `is_key && dim_group`
|
||||||
|
4. **The `dim_period` anchor** — `role === 'date' && is_key && dim_group` picks the
|
||||||
|
date whose siblings are derived from the calendar
|
||||||
|
|
||||||
|
Uses 2 and 3 want several columns flagged; use 1 needs exactly one per group.
|
||||||
|
**When a group has more than one, `.find()` silently takes the lowest `opos`.**
|
||||||
|
That is not hypothetical: `segment_new` (opos 12) outranked `part` (opos 15) in
|
||||||
|
the `part` group, so a refresh keyed on a column that is null throughout, matched
|
||||||
|
nothing, and reported success with zero members. `customer`'s group has four keys
|
||||||
|
and picks the right one only by `opos` luck.
|
||||||
|
|
||||||
|
The two meanings want separating — a per-group key choice, or a rule that the
|
||||||
|
group key is the column named by the group (which these groups nearly follow
|
||||||
|
already, except `sdate` → `sdate_e`). Until then, a refresh that finds two keys
|
||||||
|
should refuse rather than guess.
|
||||||
|
|
||||||
### Key token substitution tokens
|
### Key token substitution tokens
|
||||||
`{{fc_table}}`, `{{where_clause}}`, `{{exclude_clause}}`, `{{logid}}`, `{{pf_user}}`, `{{value_incr}}`, `{{units_incr}}`, `{{pct}}`, `{{set_clause}}`, `{{scale_factor}}`, `{{date_offset}}`, `{{filter_clause}}`
|
`{{fc_table}}`, `{{where_clause}}`, `{{exclude_clause}}`, `{{logid}}`, `{{pf_user}}`, `{{value_incr}}`, `{{units_incr}}`, `{{pct}}`, `{{set_clause}}`, `{{scale_factor}}`, `{{date_offset}}`, `{{filter_clause}}`
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user