← back to Dw Yolo Loop
scripts/price-sheets/RESUME-COUNTER-NOTE.md
108 lines
# resume-roll-adds.sh REMAIN-counter — verification note
Council flagged that the `REMAIN` edge-set count in `resume-roll-adds.sh` (line 29) may
under-count if some edge-CSV rows carry the bare/`-Roll` sku at field index 5 instead of
`-Sample`. This note records the verification against **current data**. **Do not modify
`resume-roll-adds.sh` — it gates a live write loop (Steve-gated).**
## What the counter does (line 29)
```js
const edge = fs.readFileSync("/tmp/schu-addroll-edge.csv","utf8").trim().split("\n")
.filter(l => { const [pid,,optName,optVal,,ssku] = l.split("|");
return optName==="Title" && optVal==="Default Title" && /-sample$/i.test(ssku) && !edone.has(pid) })
.length;
```
It re-derives "is this an edge target" from the CSV row shape (`optName/optVal/ssku-suffix`)
instead of from the reliable done-signal (`addroll-edge-created.json` PIDs).
## Field-shape inspection — `data/price-sheets/schu-addroll-edge.csv` (40 rows, `|`-delimited, 9 fields)
Field layout: `pid|optId|optName|optVal|svid|ssku|rsku|retail|cost` (ssku = index 5, rsku = index 6).
| field | shape | count |
|---|---|---|
| `optName` (idx 2) | `Title` | 39 |
| | `Type` | 1 |
| `optVal` (idx 3) | `Default Title` | 39 |
| | `Sample` | 1 |
| `ssku` (idx 5) | ends `-Sample` | 36 |
| | **bare (no suffix)** | **4** |
The council's premise is confirmed present in the data: **4 of 40 edge rows carry a bare
sku at field index 5** (the `-Roll` sku sits at index 6 instead). The current
`/-sample$/i.test(ssku)` filter counts only the 36 `-Sample` rows and drops those 4.
## Is the bug REAL or HYPOTHETICAL with current data?
**Hypothetical — but only by a fragile coincidence, not by design of the filter.**
The 4 rows the filter drops are exactly these PIDs:
```
7774638014515 DWLK-824290 (rollsku DWLK-824290-Roll)
7774642077747 DWLK-824210 (rollsku DWLK-824210-Roll)
7774849433651 DWLK-824650 (rollsku DWLK-824650-Roll)
7774907007027 DWLK-824640 (optName=Type/optVal=Sample, rollsku DWLK-824640-Roll)
```
Those 4 PIDs are **identical** to the 4 rows in `data/price-sheets/schu-addroll-oddball.csv`,
which the **oddball** bucket counts separately (line 31, `odd`). They are also exactly the
rows that `add-roll-variant-edge.mjs` itself classifies as `oddballs` (line 34) and leaves
for manual handling rather than auto-creating.
So dropping them from the edge count is **correct de-duplication**: each of those 4 products
is counted once, in the oddball bucket — not zero times, not twice. Tonight's live
`remaining 81 = 41 clean + 36 edge + 4 oddball` was therefore accurate (edge=36 true
auto-targets, oddball=4 = the same 4 rows). The eyeball reconciliation matched reality.
**Why it is still fragile:** correctness here is protected by the *separate oddball CSV*
mirroring those PIDs, NOT by the `/-sample$/` row-shape test. The test is the wrong instrument
for the job — it infers "pending" from CSV cosmetics. If a future edge extraction ever emits a
row with a bare/`-Roll` sku at index 5 that is **not** also mirrored into
`schu-addroll-oddball.csv`, that genuine pending target would silently vanish from `REMAIN`,
the count could hit 0 prematurely, and the script would **self-disable its launchd job**
(line 36-38) with work still undone. The current 4-row coincidence masks exactly that failure
mode.
## Proposed one-line fix (key the count off the created-PID signal, not row shape)
Both `add-roll-variant-edge.mjs` (and the clean/oddball scripts) decide "done" purely by PID
membership in their `*-created.json` revert logs — never by sku suffix. The counter should use
the same reliable signal: count any edge-CSV row whose PID is not yet in
`addroll-edge-created.json`, regardless of `optName/optVal/ssku` cosmetics.
Replace the `.filter(...)` body on line 29 with a PID-only predicate:
```js
const edge = fs.readFileSync("/tmp/schu-addroll-edge.csv","utf8").trim().split("\n")
.filter(l => !edone.has(l.split("|")[0])).length;
```
Caveat for whoever applies it (do NOT apply now): this counts all 40 edge rows including the
4 oddball PIDs, so it would **double-count** those 4 against the separate `odd` term on line 31.
To keep the buckets disjoint, exclude the oddball PIDs from the edge predicate, e.g.:
```js
const oddPids = new Set(fs.readFileSync("data/price-sheets/schu-addroll-oddball.csv","utf8")
.trim().split("\n").map(l => l.split("|")[0]));
const edge = fs.readFileSync("/tmp/schu-addroll-edge.csv","utf8").trim().split("\n")
.filter(l => { const pid = l.split("|")[0]; return !oddPids.has(pid) && !edone.has(pid) })
.length;
```
This makes the edge count immune to ssku field-shape drift (the council's concern) while
preserving the current correct edge=36 / oddball=4 split.
## Verdict
- **Bug real with current data:** No — the count is correct today (edge 36 + oddball 4, the
4 dropped rows are de-duplicated into the oddball bucket, not lost).
- **Concern legitimate:** Yes — correctness rests on a CSV-mirroring coincidence, not on the
filter logic; the `/-sample$/` shape test is the wrong signal and would silently undercount
(and could trip the self-disable gate early) the moment a non-mirrored bare-sku edge row
appears.
- **Recommendation:** adopt the PID-keyed predicate above (Steve-gated; `resume-roll-adds.sh`
drives a live write loop, untouched here).