Git commit history
Complete log of every commit in the df-product-db repository, newest first.
9b16df3cd676 — 2026-09-08 — Viktor
docs+site: trim open-questions to truly-open, logo hero, mermaid visuals
5b2ca0511f7f — 2026-09-08 — Viktor
data: §1.6 media-ladder cleanup + presumed-exotics, §1.3/§1.8 holdovers
aa7e46234811 — 2026-09-07 — Viktor
data: §1.2 backfill 3 DFI-resolved IDs + re-key
01a7bf9e42a9 — 2026-09-07 — Viktor
docs: §2.16 tooling docs + §1.2 impact re-measure
30169d1d8a1e — 2026-09-07 — Viktor
data: add sku_status_notes for flagged §1.3/§1.8 SKUs
4370e8a9e924 — 2026-09-07 — Viktor
docs: §2.1–§2.16 rulings — DFI authoritative, SB temporary, media fallback, DF part number
39919bfff8b2 — 2026-09-07 — Viktor
fix: §1.5 delete phantom wrap ladder variants — 8 marked DELETE
e99fff5e998b — 2026-09-07 — Viktor
docs: correct §1.8 drift diagnosis — 033k4l4/p031792 are wide-pleat media re-derivation, not duplicate classes
864268840703 — 2026-09-07 — Viktor
fix: §1.3 OD==ID correction — 10 re-keyed, 1 flagged (10005085)
b2b038a7642a — 2026-09-07 — Viktor
docs: record Viktor's 12 open-question rulings + rewrite §1.8 drift detail
d1013aa98288 — 2026-09-07 — Viktor
docs: fix review-pass defects — §1.4 grade split, README merge list, count-guard scope
Codex + Claude re-review of bba3127. Fixed the false 'zero A-C' claim in open-questions §1.4 (f4jfj884r is natural C; 74 of 93 blank-SKU rows ungraded), the wrong blank-SKU list pointer (build_review_sheet.py drops them), the README refresh list omitting bdf.* + Variant Cost, and added the missing site/build.py renderer test. Scoped the count-guard docstring instead of extending the regex.
bba31274a0f9 — 2026-09-07 — Viktor
docs: de-hardcode three stale counts outside the count-guard scan list
Review of the seven unpushed docs-consistency commits against live data confirmed every dated claim in open-questions.md. Found stale counts in column-map (5,862 primary rows), metafield-definitions (sku.in_stock 2342/13066/38), and domain-model §7 (Cross_Ref 62 / Compatible_Sys 73) — all drifted and all live in files the no_hardcoded_counts invariant doesn't scan.
576ce5248a69 — 2026-09-07 — Viktor
docs: enrich open-questions human-review items with examples + A-C/D-F impact
Every §1 item now names real part numbers with the OD/ID/length/media/endcap under discussion, and carries the natural-grade split (how many A/B/C vs D/F flagged rows) so the stakes are legible at a glance. Intro note explains the split: A-C = repeat buyers (higher volume), D = one or two lifetime orders, F = essentially none — so 'mostly D/F' is tail hygiene, a handful of A-C leads. In-stock SKUs are house-grade a and lead regardless of natural grade. Counts are dated 2026-09-07 snapshots; re-run natural_grade_triage.py for current.
No data changed. Verified: invariants 0 FAIL (3 standing warn), 99 tests OK, site build clean.
472a11e3c55b — 2026-09-07 — Viktor
docs: address Claude deep-pass review — D1-D6, nit, + extend count guard
- D1 duckdb: correct the duplicate-SKU count (93 blank + 4 rows across 2 duplicated SKUs, not '95 duplicated'), dated 2026-09-07.
- D2 domain-model §6.1: drop the phantom 6.25" length breakpoint — the floor binds below 12.5" on every axis; cap would need a 50" dimension.
- D3 CHANGELOG: add an entry covering daf7f95/605965d/b371535 (the three commits had settled decisions with no changelog trace).
- D4: name the italics→bold normalization in that entry (content-neutral).
- D5 README: name the project-owned cells (63-65, 68-69, 92-93); 66/67 are empty TEMP, 70/71 and 91 native.
- D6 domain-model §4: bare (no suffix) = OEM marketing SKU; the 80/20 reading is the code fallback, not authoritative.
- nit: bin_map.py comments for bins 3/5 12.75 → 12.74 (match modal dims).
- guard: extend docs.no_hardcoded_counts to architecture.md + duckdb-analysis.md (the two prose docs where wrong counts landed); update test fixtures. Not column-map.md — its 'N | class' rows are offset-table cells, not prose counts.
No data changed. Verified: invariants 0 FAIL (3 standing warn), 99 tests OK.
b37153597f0c — 2026-09-07 — Viktor
docs: address Codex review — DFI policy, bands taxonomy, DF numbering, renderer emphasis
- open-questions §2.3: DFI is validation-only, never an authority; drop seed/override as DFI-role options (disagreements adjudicate, never auto-apply). Keep only the join-key + cross-ref-metafield seed-vs-validate question.
- open-questions §2.4 + domain-model §7 #2: 'bands' is construction, not endcap hardware; build hardware is already class-defining (unique_build.py). The unenumerated remainder is residual endcap free-text, not 'gate nothing'.
- open-questions §2.5 + domain-model §7 #1: settle only the DF prefix + no-media-suffix; the numbering scheme (sequential vs encoded) is still open.
- site/build.py: bold regex now spans a literal * (bdf.* protection renders); single-asterisk italics remain deliberately unsupported (wildcard ambiguity), so the 13 *italic* prose instances were converted to bold.
No data changed. Verified: invariants 0 FAIL (3 standing warn), 99 tests OK, site build clean, no residual raw ** or <em> in dist. (ruff not installed locally, as the reviewer noted.)
605965df847a — 2026-09-07 — Viktor
docs: address Claude re-review — F1/F2/F3 + duckdb nit
- n8n §7: restore the 09-04 class-description decision record in place; add a dated superseding line (the 5-part form omitted two of the 8 class attributes) instead of rewriting the dated log. §3.3 keeps the corrected live format.
- domain-model §6.1: note MAX_AXIS_SLACK / SLACK_BUDGET are legacy code names for the per-axis ratio cap / sum cap (prose now says ratio, not slack).
- data-sources: Clark provenance date 09-05 -> 09-06 (matches CHANGELOG heading).
- duckdb: lead with the Variant-ID join (pairs 15,589/15,589, 0 blank); SKU flagged non-unique with current blank/duplicate counts.
No data changed. Verified: invariants 0 FAIL (3 standing warn), 99 tests OK, site build clean.
daf7f95dc15c — 2026-09-07 — Viktor
docs: apply doc-consistency review fixes (Codex review)
- archive KITCHEN-SINK-DEMO (stale: recommended re-running retired mutators, fixed 1/16 tolerance, dead anchor-skus.md refs)
- reconcile field levels: anchor variant-only (final-export-fields, metafield-definitions); non-primary in-stock grade = a (grade-pricing)
- DFI bolt-hole = validation reference, not authority (competitor-study handoff)
- duckdb join on Variant ID, never SKU
- n8n class_description names top+bottom endcap + build (8 attributes)
- README refresh = supervised Variant-ID merge, not a copy
- tolerance vocabulary: ratio/axis-allowance, not slack/band (AGENTS, domain-model)
- CHANGELOG: annotate same-day media-derivation supersession
- column-map: PLAN-2026-08-13 ref -> archive path
- open-questions: deletion-review = one-off artifact, no producer
- data-sources: cite 92/98% provenance, flag 45% as unverified
- domain-model: drop ~5,000 count; size_type = single_line_text_field
No data changed. Verified: invariants 0 FAIL (3 standing warn), 99 tests OK, site build clean.
ecb5e4db677e — 2026-09-07 — Viktor
docs: reconcile docs vs code/data (Claude review) + rewrite open-questions backlog
- Mark media-derivation step 3 (Option1 standard label) NOT IMPLEMENTED — code still falls to the SKU suffix (domain-model §4/§5.4, AGENTS, media-taxonomy)
- ANCHOR is col 92 (43 TRUE), not 66; bdf.* is cols 72–87 (2nd_Descr at 72)
- README refresh range 61–67 + 104–149 + native-metafield overwrite warning
- Define DFI(=EFI) and Canon; full docs/ index in AGENTS Read first
- architecture: SKU suffix diagnostic-only; drop config.yaml; de-hardcode dupes
- domain-model §8 bin table refreshed to bin_map.py (bins 3/5/8)
- open-questions.md rewritten: prioritized human-review + decision backlog, wide-pleat moved to Resolved, + 4 new questions (2.13–2.16)
- misc: stock_id 1–10, D9 margin rule→TBD, #12–#68, reserved-word fixes
No data changed. invariants 0 FAIL, 99 tests OK.
4d0c90c758fc — 2026-09-07 — Viktor
docs: reserved words, media taxonomy, + add static handbook site
1b4bcb144788 — 2026-09-07 — Viktor
docs: document optional tooling (ruff/duckdb/openpyxl) in AGENTS.md + architecture
- AGENTS.md: clarify pipeline is stdlib-only while optional read-only/dev tools are declared in requirements-optional.txt.
- docs/architecture.md: new §7 Dependencies and tooling (ruff dev linter, duckdb read-only SQL, openpyxl provenance; rapidfuzz deferred).
3395774abdbc — 2026-09-07 — Viktor
ci: pin ruff to 0.16.6
83a611dbd0af — 2026-09-07 — Viktor
chore: adopt Ruff as dev-only linter (F401/F811/F821)
- ruff.toml: select F401 (unused imports) / F811 (shadowing) / F821 (undefined names); excludes src/archive + generated/scratch/data/dfi. No auto-format.
- CI: install ruff + 'ruff check' step ahead of tests.
- Fixed 20 pre-existing unused imports across one-shot + report scripts (mechanical removal; fixture_builder Report re-export marked noqa).
- Documented in requirements-optional.txt + README.
fa88766b30a7 — 2026-09-07 — Viktor
fix: drift-report re-bucket + Type-uniform WARN invariant (Fable review)
- override_drift.py: split deleted-row + class-note out of active-drift (the headline bucket was ~10x noise); active-drift is now the 4-row decision list. Live rows keyed preferring the primary row.
- invariants.py: product.type_uniform WARN check + tests (flags the 4 8PP wrap products with mixed Type — open ruling).
- CHANGELOG: credit Claude Fable 5.1 (was 'Claude 3.5').
- requirements-optional.txt: docs/duckdb-analysis.md (was references/).
abbd37386cea — 2026-09-07 — Viktor
docs: attribute the substrate-rule expert opinion to Brandon Brigham (DF owner)
The 2026-09-06 substrate rule ('nanofiber is a surface layer, not a media') was confirmed by Brandon Brigham, owner of DF — named explicitly in donaldson-media-mapping.md, domain-model.md, media.py, and CHANGELOG instead of the anonymous 'expert'.
4bc379b003f5 — 2026-09-07 — Viktor
feat: override-drift report + de-type remaining 7 wrap accessories
- src/override_drift.py (read-only -> generated/): compares the class-overrides.csv last-wins log against live edit_db.csv, bucketing each difference (active-drift / dead-class / gone-sku / blank-sku). Turns the 09-06 'silently undone corrections' finding into a recurring check.
- First run surfaced 7 more wrap reversions (3EA-* + p031xxx-016-210, 'Dryflo Wrap' in body copy): restored Type -> Accessories + cleared class and the 09-01 A4/A5 bdf stamps, matching the 09-06 8PP-* fix. bdf edits logged to class-overrides.csv.
03e9c3b4c149 — 2026-09-07 — Viktor
docs: adopt optional analysis tooling (openpyxl declared, duckdb read-only)
- requirements-optional.txt: declare openpyxl (provenance_join) + duckdb (read-only analysis); rapidfuzz explicitly deferred.
- provenance_join.py: fail with a pip-install hint instead of a bare ModuleNotFoundError.
- README: re-scope the stdlib-only claim to the pipeline; add Optional tools.
- docs/duckdb-analysis.md: verified read config (header=true, skip=1, all_varchar=true), the new_line caveat, and the SKU join. Parse verified field-by-field, 0 mismatches vs stdlib csv.
64e854acad6d — 2026-09-07 — Viktor
fix: restore wrap-accessory de-type + P032931 stainless price (Claude review)
- 4 wrap SKUs (8PP-39654-00, 8PP-35880-01/02/03) back to Accessories; cleared class + the 09-01 A4/A5 bdf stamps (Shape/Bottom/Bolt_Hole).
- P032931_DAMN2-NF: restore 138 + 3% = 142.14 (prev_price 138.00); the 09-04 pass had applied +3% to the pre-correction 115. bdf edits logged to class-overrides.csv.
f00dbe38b775 — 2026-09-07 — Viktor
chore: WARN invariants (OD==ID, dup primary SKU); decommission apply_overrides
- New WARN checks class.od_equals_id + data.duplicate_primary_sku (positive + negative fixture tests): make the 11 OD==ID cartridges and 2 duplicate primary SKUs visible and bounded.
- Mark apply_overrides.py DO NOT RUN: its last-wins re-applier would change 483 cells and resurrect retired classes.
- gitignore imports/orrstien-files/ + Clark cross-ref PDF (vendor-confidential, local-only like dfi/).
79535687bbfe — 2026-09-06 — Viktor
fix: body-copy field alignment + blank-shape backfills (Gemini review)
- NF40197/198/322 body copy: OD/ID/Top labels shifted one slot left; realigned (bdf already correct, copy-only fix).
- P280475: bdf.Shape blank -> Oval (shape leaked into sq-ft slot); body restored Shape: Oval.
- P527081-016-340: bdf.Shape blank -> Conical (Donaldson 'Coned Ultra-Web FR' + Clark xref 'OO Conical'); wrong-end dims 16.0x11.5 corrected to small-end 12.75x8.375 (cross-ref sibling NF40029 already in cls-NR007); re-keyed out of round cls-0379 into conical cls-NR007/049/050. bdf edits logged to class-overrides.csv; invariants 0 FAIL, tests 89 OK.
4d3d761c50b6 — 2026-09-06 — Viktor
fix: correct 09-06 Donaldson/spunbond passes (Claude + Codex reviews)
- revert/exemption collision: P031122 re-reverted (ladder DELETE), P031790 note media fixed
- de-stock (not delete) 3 in-stock NF twins (never delete an in-stock variant)
- tolerance-window merge: 28 within-tolerance dup classes collapsed (9 scoped + 19 full)
- scrape_parser: nf-on-sb + polyester-felt exotic codes; media.py SB-family comment
- script fidelity: base-matching scope, idempotency guard, in-stock de-stock guard, EXCLUDE + class-overrides logging
- 7 new media/norm_media tests
02082922cc04 — 2026-09-06 — Viktor
feat: Donaldson-OEM media revert (D/F) + downgrade exemptions (A/C + spunbond)
00a7512aef5a — 2026-09-06 — Viktor
docs: substrate rule + nf-on-sb/polyester-felt exotic media codes (Donaldson mapping)
d7432bc24b8f — 2026-09-06 — Viktor
docs: ingest Donaldson catalog + Clark cross-ref PDFs (reference DBs, provenance)
88c8de84eebf — 2026-09-05 — Viktor
docs: Codex handoff — resolve 134 dims-drift rows via OEM/distributor scrape
4d515f5604b4 — 2026-09-05 — Viktor
docs: n8n order manifest spec (Canon → n8n PO flow)
846b0621801e — 2026-09-05 — Viktor
feat: fold DIRECTOR spec directory into provenance join
damnaddsforvictor.xls.xlsx (DIRECTOR + Dups removed 1, 2,223 base PNs) is a master spec/price directory — dims/endcap/bolt/shape/price, no media. Adds 4,190 more cartridge rows of spec provenance: 12,889 -> 6,945 original (v2) / 4,190 director / 1,754 later-add (86% covered). 142 dims-drift candidates.
b3747a48bf56 — 2026-09-05 — Viktor
feat: provenance join — Orrstein source files to edit_db (original vs later-add)
Read-only src/provenance_join.py joins imports/orrstien-files/DAMNNEWADDSv2.xlsx (original Shopify catalog + wash spec sheets) to edit_db.csv on part number. Emits source_origin / source_media / dims / endcap / shape / price per row. First run: 12,889 cartridge variants -> 6,945 original (53%) / 5,944 later-add (46%); source media agrees 6,915/6,944 (29 disagreements all exotic-corrections); 112 dims-drift candidates.
767b20d563a7 — 2026-09-05 — Viktor
feat: SKU media indicators are diagnostic, not classification (audit script)
Viktor 2026-09-05: bare/suffix/NF-prefix/-orig SKU indicators must never assign media — they flag problems or confirm accuracy, resolved via OEM/competitor/DFI lookup. Locked rule in AGENTS.md + skill; new read-only src/media_indicator_audit.py cross-checks derived vs declared vs body vs SKU-indicator vs scrape-cache evidence (298 flagged, 67 in-stock, no data mutated).
4c36aff7d3e4 — 2026-09-05 — Viktor
docs: changelog entry for OEM source research + two-axis registry model
e97320efb8e0 — 2026-09-05 — Viktor
feat: tail-scan source registry additions (Mikropor, C&W DustTech, Filtration Group)
164d71107b32 — 2026-09-05 — Viktor
fix: separate non-publishing OEMs from retail tiers (is_active, not tier-3)
Reverts the tier-3 demotion. Non-publishing OEMs stay tier-0 (their authority class) but are marked is_active=0 so the loader skips them — distinct from the tier-3/4 retail competitors we still need spec info on. Loader now skips inactive sources.
689d725051da — 2026-09-05 — Viktor
feat: source registry from OEM research (demote non-publishing OEMs, add per-PN subdomains)
- TIER_ADJUSTMENTS applies research-driven tier changes after seed: demote 12 OEM domains that publish no per-PN specs (TDC, Wheelabrator, Micro Air, etc.) to tier-3 reseller-only; add/promote per-PN spec sources (filters.camfilapc.com, filters.filpro.com, royalfilter.com, shop.isystemsweb.com, wynnenv.com).
- vendor-scope guard now subdomain-aware (_same_oem): filters.camfilapc.com is Camfil's own per-PN lookup, not a cross-OEM replacement site.
8ce26220a713 — 2026-09-04 — Viktor
fix: vendor-scope Tier-0 authority (OEM domain only for its own vendor)
Add VENDOR_TO_OEM map + oem_domain_for_vendor() to scrape_cache.py; the loader demotes a Tier-0 domain to tier-3 when it is not the SKU vendor's own OEM, so robovent.com no longer surfaces as authoritative 'Tier-0' spec for a Donaldson part number (its replacement-page 80/20 was contradicting Donaldson's Ultra-Web NF).
7d3dea92b58e — 2026-09-04 — Viktor
fix: norm_media reads spec block only (not site chrome)
Loosened the labeled 'Media Type' regex to match non-bold + dropdown structures and capture ~250 chars across newlines, so base-is-default sees the 80/20-base + NF-upgrade ladder instead of the first dropdown line. Title/body fallback now runs norm_media over a ±200-char context window instead of the bare keyword. Media flags in the comparison drop 204 -> 78.
383789291b2f — 2026-09-04 — Viktor
feat: Firecrawl scrape-cache pipeline + A/B/C competitive re-scrape
- src/scrape_cache.py / scrape_parser.py / scrape_loader.py: 4-table SQLite store + 5-tier source registry; markdown->observations parser; loader with 90-day dedupe. 843 scrapes / 6536 obs / 274 PNs.
- parser fixes: error-note clobber; >40mm heuristic no longer corrupts explicit-inch lengths.
- re-scraped natural A+B+C(orders>5) into the cache; comparison + mismatch adjudication generated.
0fc15b1a2227 — 2026-09-04 — Viktor
feat: supplemental import (61E45 price + 40 position collisions)
- 61E45 Variant Price 147.89 -> 107.70 (cls-0038 class price; prev_price already 147.89, so the rollback snapshot is implicit).
- Renumber 40 Variant Position collisions to 3: 38 spunbond bare-80/20 rows colliding with their SB primary at pos 1, + 2 keep-ladder NF rows colliding with bare at pos 2.
- build_matrixify_supplemental.py emits a minimal 41-row / 8-col supplemental (all MERGE, no DELETE) for upload after the running main import completes.
Verified: invariants 0 FAIL, 82 tests OK.
9684a9bb2332 — 2026-09-04 — Viktor
fix: 61E45 variant MPN corruption; drop double file write in builder
- 61E45 bare row's Variant MPN (col 96) was 6.10E+46 (Excel sci-notation); corrected to 61E45 (feeds Google Shopping product matching). SKU fixed in the prior commit; MPN was the second half.
- Builder wrote the output file twice (header-only first pass discarded); collapsed to a single write.
a6426d6cd240 — 2026-09-04 — Viktor
fix: sort Matrixify import product-grouped (dry-run 'Handle repeats' warning)
edit_db.csv is ordered primaries-then-variants; Matrixify flags 4,405 handles as 'repeats after other rows'. Builder now re-sorts the output product-grouped (Product ID -> primary first -> Variant Position). Non-contiguous handles 0.
1a6bdf5afa4c — 2026-09-04 — Viktor
feat: finalize Matrixify import (61E45 SKU fix, force-MERGE deletions)
- 61E45: base 80/20 SKU corrected from Excel-corrupted 6.10E+46 to 61E45 (edit_db + priced CSV); title/handle + NF/SB siblings confirm.
- Force-MERGE: 114 variant + 1 product DELETE markers neutralized to MERGE in the import output only; edit_db keeps the intent for a signed-off deletion pass later.
- import rebuilt: 129 cols, 15,589 rows, no DELETE commands.
Verified: invariants 0 FAIL, 82 tests OK
84fdc61ed90a — 2026-09-04 — Viktor
feat: build first Matrixify import (vendor.cost, class re-sync, 3% + in-stock pricing)
Data pass (matrixify_import_data_pass.py, one-shot):
- vendor.cost re-added (col 149) = Variant Cost on 964 passed-review PO-match rows
- class labels 65/68 re-mirrored from working col 104 (stale after 09-01..09-04 re-keys)
- +3% on 10,545 non-in-stock cartridges; in-stock CSV schedule by SKU (recommended_price if present, else sell_price, for all 2,353 CSV SKUs)
- prev_price snapshotted before every numeric price change
Import builder (build_matrixify_import.py, re-runnable):
- 149->129 col remap onto Matrixify_Full.csv live-store header
- sku.in_stock TRUE/FALSE lower-cased to boolean JSON true/false
Verified: invariants 0 FAIL, 82 tests OK
b72ae003173b — 2026-09-04 — Viktor
fix: revert 22111/22211 dims correction — Daniel measured 13.84x9.48 O/O (not 10.37)
d703560acf08 — 2026-09-04 — Viktor
docs: physical measurement > scrape > body summary line (Known traps)
3ed1daf335a5 — 2026-09-04 — Viktor
fix: competitor-study corrections (33-0230 O/O; 22111+22211 13.8x10.37) — Rachel adjudicated
32cc3a02d5b7 — 2026-09-04 — Viktor
docs: add one-writer-at-a-time rule to AGENTS.md pitfalls
cd5bbbffcfa5 — 2026-09-03 — Viktor
fix: remove original-Donaldson anchor; log E040014 conical dims flag
436b547af626 — 2026-09-03 — Viktor
feat: designate 43 anchor SKUs (Viktor April ad-spend review) — col 92 custom_label_3
d2c838a8539e — 2026-09-03 — Viktor
fix: mark build_po_backfill + populate_natural_grade as mutators; fix cost-model §8 paths (Claude review)
ba792f65ebf6 — 2026-09-03 — Viktor
docs: add Known traps + one-commit-per-pass to AGENTS.md
0b4ed29f3f61 — 2026-09-03 — Viktor
docs: merge grading docs into grade-pricing.md; move meetings to archive/
dd2f5429ebb9 — 2026-09-03 — Viktor
chore: mark applied one-shot scripts (# APPLIED header) + src/README index + scratch/
57d6c4035bfe — 2026-09-03 — Viktor
feat: class-sibling probe — anchor natural-B, sample non-B siblings (grade-weighted)
3eeae14be672 — 2026-09-02 — Viktor
feat: natural-A competitive study + class-sibling probe + primary swaps + A4 recheck
- natural-A competitive study (34 cartridges) vs competitors; TEMP cols 138-148
- class-sibling probe (100 A/B/C) + media-primary swaps (P191544 NF, 2 spunbond)
- A4 blank-endcap recheck: 46/129 flip candidates flagged (not applied)
- search backend: Firecrawl-only (Tavily + SearXNG dropped)
- handoff + B/C runbook in docs/HANDOFF-2026-09-02-competitor-study.md
a9334b828b83 — 2026-09-02 — Viktor
feat: web-gap dim backfill + competitor pricing + class backfill (2026-09-02)
Firecrawl re-key (new 5000/mo account), source-tier lookup + price capture; high/med dim writeback (72 SKUs); 6 duplicate classes merged; dcf-default reclass (58 classified, 49 minted); competitor_list_price col 137 (331 rows); $2500 placeholder re-fix. Bundles the uncommitted grade-fix pass (col63/col93 cleanup + de-stock demotions).
ca4879086a2b — 2026-09-02 — Viktor
feat: media taxonomy (wide-pleat-nf) + cls-0038 media split + NF->80/20 primary swap
- media.py: wide-pleat-nf distinct media (nanofiber on spunbond), Option1-first for exotic/wide-pleat, bands construction via SB_FAMILY_EXOTIC
- cls-0038 split: 3 wide-pleat + 4 carbon + 1 teflon re-keyed out of the 80/20 bin class; 22 Default Title Option1 backfilled
- catalog-wide wide-pleat sweep: 8 more bare-suffix wide-pleat re-keyed; 2 pulled from bins 5/8; 4 carbon moved to non-flanged cls-1667
- primary swap NF->80/20 for 9 products (2026-08-28 swap missed them)
- docs: domain-model 5.4 media taxonomy, AGENTS.md locked rule, grade-pricing.md, CHANGELOG
Verified: invariants 43 checks 0 FAIL, 82 tests OK, --baseline bdf-immutable OK
77d09243fb98 — 2026-09-01 — Viktor
fix: spun-bond reclass — add stock+grade guards (Claude review)
The spun-bond pass deleted 26 in-stock SKUs and 19 higher-selling listings on a body-copy signal weaker than Option1 + order history. Reverted the DELETE half and re-ran with two guards: never delete an in-stock variant; never delete a variant whose natural grade is A/B or outranks the SB row it replaces.
Result: 38 promoted + 76 DELETEd (was 66/132); 28 conflict + 13 no-SB-variant -> review list. In-stock preserved at 2,342.
eac03cbb6820 — 2026-09-01 — Viktor
docs: reflect 2026-09-01 remediation state
- AGENTS.md: tolerance window rule, blank shape/endcap backfills, SB-only, A8, bin-10 merge, terminology
- domain-model: spun-bond primary, A4/A5 applied notes, tolerance window in §5.9/§6.2, bin table
- open-questions: Resolved 2026-09-01 (A1-A8)
e35f71f559fc — 2026-09-01 — Viktor
feat: spun-bond reclass + consolidated review list
- 79 'stated spun bond' primaries: 66 SB-promoted (ladder DELETEd), 13 review-only
- review list (356 rows, 6 buckets) incl A8's 109 DFI-dims for adjudication
- invariants 0-FAIL, 82 tests OK
901eb4867215 — 2026-09-01 — Viktor
feat: class remediation A5+A4+A2 (shape/endcap backfill, tolerance-window merge)
- A5: 403 blank shapes -> Round (2 contrary kept blank)
- A4: 143 blank endcaps -> closed/closed+bolt (in-stock bin-defined)
- A2: tolerance window replaces fixed 1/16in rule (invariant + domain-model)
- A1/A2/A6: merge sweep collapses 73 near-dup classes
- A8 (DFI dims) deferred to human-review list (false-bridge risk)
9228cf58f1b8 — 2026-09-01 — Viktor
chore: gitignore archived Matrixify export snapshots
d871737894eb — 2026-09-01 — Viktor
fix: preserve Variant Cost (PO backfill), normalize boolean casing
- Variant Cost (col 49) is project-managed (PO-cost backfill, blank = unmatched) — was store-copied, refilling 1,172 unmatched blanks. Now preserved (4,980 nonblank, -2 from dropped variants)
- Normalize boolean columns (Top Row, Gift Card, Published, Taxable, Requires Shipping) to uppercase TRUE/FALSE on write (694 cells)
- CHANGELOG: correct row split (53 new products + 100 on existing = 153)
5499c7e39d19 — 2026-09-01 — Viktor
fix: address Claude review — preserve Google Shopping cols, normalize SKU copy, Variant Command, header aliases
- Preserve project-managed Google Shopping columns (class 65/68, ad-group 64/69, anchor 92, cleared 70) from edit_db instead of store-copying — the first pass wiped ~12k cls-* cells from 65/68 and 7 anchors from 92
- Normalize store-copy (float-equal / boolean-equal = identical): reverts Jason's 16 number-formatting SKU corruptions (102903 -> 102903.00), preserves project rename P199612; only 6 MERV/size renames adopted
- New rows now carry Variant Command=MERGE (38 D1 DELETE re-applied after)
- Adopt gmc_id + V_Rank values via header-name aliases (were silently frozen)
- docs/column-map.md row count 15,446 -> 15,589
c37d82a5909e — 2026-09-01 — Viktor
feat: merge 09-01 Matrixify export (adopt Jason changes, preserve project data)
- Rebuild edit_db.csv from Matrixify_Full_09-01-2026.csv: +53 products, +143 variant rows, -10 store-deleted variants (15,589 rows total)
- Overlay project-owned columns by Variant ID (bdf.*, grades, 104-136, Variant Command) — never SKU (6 renames kept their Variant ID)
- Adopt Jason: 100 prices, 6 SKU renames, body/title/images, Falcon IX Type changes (clear 3 dcf-default)
- Re-apply 38 D1 Variant Command=DELETE markers
- Mark duplicate P281426-016-002 (prod 7147161976893) Command=DELETE
- Refresh Matrixify_Full.csv to the 09-01 export (129 cols)
- Verified: invariants 0 FAIL, 82 tests OK, classification unchanged
7fcdab704d44 — 2026-09-01 — Viktor
docs: 09-01 Matrixify export review + locked merge decisions
8691f0a82cdb — 2026-08-31 — Viktor
fix: background review — DFI/self-test correctness, transpose A/B, metafield renderer, v_rank import
- dfi_review: parse_title_pn spaced-hyphen anchor (hyphenated PNs no longer truncated); sorted(efis) determinism; unvalidated verdict for blank-our-dims bridges; split dfi-no-dims vs dfi-no-cost-row; deterministic .NN base lookup; drop dead assignments
- class_self_test: in-stock classes from BIN_MAP values (was 0); per-axis adjudicable denominator; flange/build marked derived (circular with classifier)
- fix_body_copy_transpose: find_after tag-masking (A) + NUM fullmatch (B) + SHAPE_FIX re-key guard (#14)
- build_metafield_mutations: ownerType emitted unquoted (GraphQL enum)
- import_matrixify: v_rank accepts bare + global. header spellings
- docs: sku.in_stock type change to single_line_text_field (pending Viktor manual)
f7981e5c9f71 — 2026-08-31 — Viktor
fix: harden fix_body_copy_transpose.py (Claude review, 6 fixes)
- Idempotency guard: swap only when labeled OD < ID — re-running --apply can no longer transpose correct data back (verified 0/16 on live data).
- Backup edit_db.csv to *.bak before writing (shutil import was dead).
- class-overrides log written from b_changes (actual changes), not SHAPE_FIX.
- Match on SKU AND product id (pid was decorative; SKU is non-unique).
- find_after anchors on closing '>', so numeric tag attrs (class="col-2") are never read as the diameter.
- CHANGE B idempotency guard (beyond review): re-run no longer re-logs duplicate class-overrides entries.
Dry-run 0/16 + 0/2 on correct data; 82 tests OK; invariants 0 FAIL.
41fd3bed2333 — 2026-08-31 — Viktor
feat: metafield definitions created on Shopify (28 defs)
- Created 28 definitions live on damnfilters-com via browser automation: 27 variant-level (sku.* x13, vendor.* x9, dfi.* x5) + 1 product-level google.gmc_id. Names, namespace.keys, and types verified on the store.
- vendor.cost reinstated (reverses 2026-08-13 'vendor_cost redundant' decision); PO-asserted cost, PO-match rows only. Value column still to re-add.
- global.title_tag / global.description_tag deferred (bare namespace, 'n/a' in field map) — removed from manifest (29 -> 28 defs).
- google.gmc_id flagged out-of-scope (field map) after create; left in place, revert deferred.
- CHANGELOG + open-questions #3/#15 + column-map + final-export-fields updated.
- invariants 0 FAIL (2 warn); 82 tests OK.
261830f7f977 — 2026-08-31 — Viktor
feat: final-phase kickoff — metafields, triage, DFI review, self-test, fixes
- docs/metafield-definitions.md: 29 metafield defs + Shopify types (scripted GraphQL create)
- src/tools/metafield_definitions.json + build_metafield_mutations.py: manifest + renderer
- src/natural_grade_triage.py: Phase 1 cross-tab of issue buckets by natural grade
- src/dfi_review.py: Phase 2 full dims-validated DFI bridge
- src/class_self_test.py: Phase 3 stratified self-test vs body copy (0/30 all axes)
- src/tools/fix_body_copy_transpose.py: 16 body-copy OD/ID swaps + 2 oval->round re-keys
- V_Rank namespace -> global.V_Rank (header + import_matrixify + schema)
- open-questions #3: metafield token access now available (dev dashboard)
Invariants 0 FAIL / 2 warn; 82 tests OK; classes unchanged 1573.
d55d3eab61c7 — 2026-08-31 — Viktor
feat: check_doc_column_map — validate column-map.md positions against the file
column_count_matches catches width drift; this catches positional drift — a column inserted/deleted below a documented row silently invalidates every position below it even when the width is updated (the failure mode of the recent column surgery). Validates the machine-checkable 104+ table (backticked literal names + enumerated ranges); fails closed on a missing section. 3 fixtures.
34b4e1cb7a11 — 2026-08-31 — Viktor
fix: correct stale column citations + add docs.column_count_matches invariant
- column-map.md 138->137 (headline), 104-137->104-136; README.md, CHANGELOG.md, natural-grades.md, final-export-fields.md col 137->136 (natural grade shifted)
- invariants: new check_doc_column_count verifies column-map.md's stated width matches the file (fails closed on missing/malformed headline); would have caught the 138->137 drift before it landed
- tests: 3 fixtures (match / mismatch / missing-headline)
2a503a5e43d6 — 2026-08-31 — Viktor
fix: D1 DELETE into native Variant Command (col 27), drop duplicate col 136
- edit_db.csv: 38 rows' col 27 MERGE->DELETE; duplicate Variant Command col 136 removed (138 -> 137 cols). The D1 pass had appended a second same-name column, making name resolution (Db.idx first-match) and the Matrixify export ambiguous.
- remove_exotic_variants.py: write into the existing Variant Command column via idx_of, append only if absent (idempotent, no duplicate).
- docs: column-map.md + CHANGELOG corrected (native col 27, natural grade now col 136); skill note updated.
- test_tools.py: golden test asserts DELETE lands in col 27 with no duplicate.
f5b2d2d00917 — 2026-08-31 — Viktor
test: Tier 2 (media + unique_build unit tests) + Tier 3 (golden tests for --apply tools)
- test_media.py: suffix/option/canonical precedence, cellulose retirement, body-exotic scan (bare carbon is not a hit), construction media
- test_unique_build.py: bands/prewrap/stainless/SS-shorthand/hardware detection, is_neither (no-liner excluded), build_signature, is_unique_build
- test_tools.py: golden tests running remove_exotic_variants + promote_in_stock_grade via subprocess against a fixture edit_db.csv, asserting exact mutation
c4e971edc862 — 2026-08-31 — Viktor
test: positive controls for every tested check (catch always-fail); CI --baseline on push
A negative fixture catches a check that can never fail; a positive control catches one that can never pass (dropped 'not' / hardcoded False), which gates CI on clean data and tempts deletion. Add a clean-fixture control for each of the 15 tested check-names. Also run --baseline on direct pushes (event.before), not just PRs — the bdf.* immutability gate is the point of the deep clone.
1d971b5f2d89 — 2026-08-31 — Viktor
test: pin check_doc_counts branches + CI --baseline wiring; hygiene hardening
- check_doc_counts takes root= for testability; 4 new tests (missing-doc fails closed, undated count caught, dated count allowed, CWD-independent)
- signature-collision / class-id / cartridge-only fixtures (Claude gap list)
- CI: wire --baseline on PRs (justifies fetch-depth 0), || true on JSON step, narrow push trigger to main+feat to avoid PR double-runs
- hygiene: widen doc-citation check to all live docs + bare paths; guard fixture_builder header read with a clear message
- gitignore the de-cited popularity CSV
592d3284b693 — 2026-08-31 — Viktor
test: negative fixtures for locked-rule checks + hygiene guards + CI workflow
9a0f03a450bd — 2026-08-31 — Viktor
fix: makedirs in generated/ writers; no_hardcoded_counts resolves docs via __file__ (fails closed)
2ddb5127282f — 2026-08-31 — Viktor
docs: de-cite external popularity source; fix stale script paths (src/archive + src/tools)
f53dfe103c5f — 2026-08-31 — Viktor
chore: gitignore vendor-confidential dfi/ + vt-final-note scratch files
793c90ea3d15 — 2026-08-31 — Viktor
feat: natural grade, D1 exotic-variant removal, PO vendor corrections (2026-08-30/31)
- Natural grade: new variant-level metafield sku_natural_grade (col 137), per-SKU pageviews+orders grade A/B/C/D/F, no sibling inheritance. Populated from imports/natural_grade.csv joined by Variant ID (15,446 rows). New docs/natural-grades.md.
- D1 exotic-variant removal: 38 standard-media variant rows on exotic products marked DELETE (Variant Command, col 136) + class/stock fields cleared; log imports/exotic-variant-removals.csv.
- PO sibling backfill reverted: only the 954 natural seeds keep vendor+cost; 10,232 class siblings restored to pre-backfill state (Variant Cost <- sku_prev_cost, vendor_* blanked, under-review).
- PO near-miss seeds fixed: 12 SKUs whose QuickBooks string differs trivially (SKU prefix / case / notes) got their vendor+cost attached.
- Invariant fix: class.no_bottom_endcap_span resolves the bottom endcap via the primary row (was structurally blind to non-primary members).
- Docs: column-map 138 cols, house-grades + domain-model + final-export-fields cross-refs, CHANGELOG.
1639fb379540 — 2026-08-28 — Viktor
feat: integrate popularity data (GA4 pageviews + Shopify orders) as TEMP cols 132-135
Zach's GA4 + Shopify pull, appended to edit_db.csv as four TEMP fields: pageviews_ga4 (handle-level), orders_sku (variant-level), pageviews_clean, pageviews_shopify. Joined positionally (source mirrors our 8 duplicate variant SKUs). Column map, CHANGELOG and README updated to 136 cols.
69c8ced41d5e — 2026-08-28 — Viktor
feat: DFI media + primary-flag corrections (round 2)
- Retire "100% Cellulose" exotic -> 80/20 (DFI #10 retired, Viktor). Remove from media.py OPTION_EXOTIC + BODY_EXOTIC and domain-model §4; re-key 6 cellulose primaries to their 80/20 classes.
- Swap primary flag from _DAMN2-NF to bare 80/20 on 13 products where DFI says the OEM spec is 80/20: move product-level fields (Body/bdf.*/product metafields/labels) + swap Top Row (61), Variant Position (34), variant grade (93). NF/SB variants left in place. P25.70E (SB-primary) held.
Invariants 0 FAIL / 2 warn.
988328ac0a1a — 2026-08-28 — Viktor
feat: DFI bolt-hole review applied (cartridge-only)
Bridge cartridge catalog to DFI (ex-EFI) via title OEM PN -> cross-ref -> cost sheet, validating each bridge by dims agreement (1/16"). Applied:
- bdf.Bottom/Bolt_Hole/Bolt_Hole_Dim backfill on 10 primary rows (0.5/0.9).
- 5 bdf.Inside_Diameter backfills from DFI (8.38 x4, 5.84 x1).
- 21 class re-keys: 80/20->cls-0038, NF->cls-0459, SB->cls-0460 (6 products); P199469-016-002 (flanged) -> new cls-1617/1618/1619.
- 2 false bridges discarded (1319234 PN collision; 10000940 shape dispute).
- Fixed 2 stale class-overrides.csv entries (drift from 08-27 split/merge).
Invariants 0 FAIL / 2 warn. 50 rows logged to class-overrides.csv.
a927140c9dae — 2026-08-28 — Viktor
docs: PO-match survey (cartridge-only) — cost coverage snapshot
- CHANGELOG 2026-08-28: 8,630/12,882 cartridge SKUs (67%) have vendor+price; 1,176/1,595 classes (74%) have zero PO-match members (4,080 classed SKUs).
- open-questions #8 (sibling-cost->PO): note current cost reach ~26% of classes; rest need a fresh PO/match.
342a1b9b4bc6 — 2026-08-28 — Viktor
feat: promote in-stock grade into live grade field
promote_in_stock_grade.py: 93 = 'a' wherever new-grade (107) = 'a' (2,145 rows, incl. 1,147 non-primary x->a), then reconcile 63 <- 93 (998 primary). Old grade preserved in sku_prev_grade (116) — verified 116 == 93 on every row before the pass.
Invariants 0 FAIL / 2 warn.
4666b7c25bd4 — 2026-08-27 — Viktor
feat: apply final-export field directives to edit_db.csv
Steps 1-6 from docs/final-export-fields.md, via new src/tools/ apply_final_export_fields.py (gated + backed up):
- Row-1 guidance labels: ALL CAPS / TEMP: / dot-namespace (33 cells).
- ANCHOR 66 -> variant custom_label_3 (92), cleared 66 (7 rows).
- new-class 104 -> product custom_label_2 (65, primary-only) + variant custom_label_2 (68, all rows) for 12,292 rows (incl. 348 dcf-default).
- Grade reconcile: variant 93 -> product 63 on 113 primary rows.
- Cleared product custom_label_3 (70) + h2412a strays (64/65/71/117).
- 104/107 kept (invariant/report scripts read them); removal = Phase E.
Housekeeping: .gitignore *.bak backups; column-map.md ANCHOR rename superseded; CHANGELOG lag fixed. Invariants 0 FAIL / 2 warn.
ed53d3810fdc — 2026-08-27 — Viktor
docs: final-export field/metafield plan — Viktor directives + custom-label audit
- docs/final-export-fields.md: conventions (ALL CAPS / TEMP: / shortest label), Google Shopping pass-through mapping (grade=0, ad-group=1, class=2, anchor=variant _3 only), grade duplication rule (63==93, variant authoritative), full temp/permanent rundown (sku_*/vendor_*/ dfi_* -> dot namespace), legacy-restore deferred.
- CHANGELOG.md: dated pass entry under 2026-08-27.
- docs/open-questions.md: legacy-restore flagged deferred.
70bf8ce45e28 — 2026-08-27 — Viktor
feat: final-export data pass — dim write-back + class split/merge + bin remap
- fix web search/extract backends (Tavily search + Firecrawl extract)
- 520-SKU dimension + bottom-endcap lookup (Tavily/Firecrawl, then Claude bulk-harvest)
- write back 1,032 bdf corrections (sanity + class-consistency gated, logged)
- split 3 overspread od==id classes (build-aware, typo-normalized)
- merge 26 duplicate classes (exact-signature collisions), re-key 1,247 rows
- remap bin_map bins 3/5/8 to merged class ids
- invariants: 0 FAIL / 2 warn (pre-existing shape-inference + body-copy flags)
bc5885e41111 — 2026-08-20 — Viktor
fix: report-output hygiene + no_hardcoded_counts scope
- pricing_report.py + in_stock_matrix.py: os.makedirs generated/ before write (fresh-clone FileNotFoundError fix)
- repoint partition_violations/classification_backlog/refigure_bins off imports/ onto generated/; add os.makedirs
- in_stock_matrix.py: derive dims from data (was hand-maintained DIMS table)
- invariants.py no_hardcoded_counts: also scan docs/open-questions.md
- open-questions.md: date-provenance the dcf-default count
- add KITCHEN-SINK-DEMO-2026-08-20.md capability demo
0a8565bef762 — 2026-08-20 — Viktor
docs: archive historical skill notes + mark archive/ as non-citable
Move 3 reusable how-to recipes from the Hermes skill into archive/hermes-skill-notes/ (bannered as historical). Add AGENTS.md guardrail: archive/ is a record, not a source. Delete the other 36 redundant pass-notes from the skill (content already distilled into CHANGELOG/domain-model/class-overrides).
dec024f85068 — 2026-08-20 — Viktor
chore: archive superseded files, consolidate src/, gitignore generated/
Archive superseded handoffs/plans/meeting notes + the 2026-07-28 architecture draft. Move one-shot passes -> src/archive/ and re-runnable tools -> src/tools/. Delete dated report CSVs (now in generated/). Repoint pricing_report + in_stock_matrix output to generated/ with runtime-dated filenames. .gitignore: add generated/.
cdd0f75a8d24 — 2026-08-20 — Viktor
docs: thin agent docs + canonical domain-model + architecture doc
AGENTS.md/README.md thinned to pointers; CHANGELOG.md spins up the dated pass log. docs/domain-model.md rewritten as the canonical 8-attribute rules doc; media-taxonomy/cost-model/open-questions/architecture added (ARCHITECTURE.md still-true subset -> docs/architecture.md). invariants.py: check_doc_counts (docs.no_hardcoded_counts FAIL).
e1f6a8340ed9 — 2026-08-20 — Viktor
data: F4 conical re-orientation + F5 gasket re-file + P199612 confirm
edit_db.csv: 34 row edits (conical dims swapped to OD>ID per F4; gasket wording moved Bottom_Config->Top_Config per F5). class-overrides.csv: 56 new override rows + P199612 nomex authority confirmed (Viktor). invariants.py: ID_GT_OD_ALLOWED emptied, check_gasket_is_top_endcap + check_shape_inference added.
010424aab75c — 2026-08-20 — Viktor
fix: enumerate_unique_build.py dates its survey output (was frozen 08-18)
Derive the output filename from datetime.now() like build_in_stock_csv.py, so a read-only verification run stops silently rewriting the committed 08-18 artifact. Regenerate today's survey (585 rows vs 260 — the primary-row build fix exposes 8,318 previously-blind non-primary variants).
f7d93c9c137d — 2026-08-20 — Viktor
data: retire stale "ub: bands" rationale on 55 SB class-notes (F1)
The 2026-08-18 unique-build pass predated F1 and split SB rows out with the note fragment ub: bands (from cls-XXXX). F1 makes bands the DEFAULT for Spunbond, so that rationale is retired -- the phantom splits were already merged back (70686f1), but 55 notes across 25 classes still asserted the old reason, inviting the next reader to undo or re-split a class.
Rewrites only that fragment, only on SB rows whose signature is now a default. Lineage is preserved (the from cls-XXXX parent is real provenance the eventual renumber depends on), the reason marked superseded:
ub: bands (from cls-0460) -> re-keyed from cls-0460 (ub: bands retired by F1 2026-08-20)
The 163 rows where bands is still a genuine deviation (80/20 and NF) are untouched.
Written through class-overrides.csv so the correction survives a from-scratch re-run; apply_overrides.py verified 55 cells changed field-by-field against a pre-edit snapshot, and a re-run applies 0 (idempotent). Only column touched is class-note. Invariants 38 checks / 0 FAIL / 2 warn; classes 1,613, in-stock 2,353 -- both unchanged, as expected for a notes-only pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
c07d29b32698 — 2026-08-20 — Viktor
fix: SB-family exotics default to bands (construction_media normalizer)
Claude review: rekey_exotic_media.py passed media=<exotic code>, but _construction_default() only tested media=='SB', so Spunbond-family exotic codes (aluminized, aluminized-ptfe, teflon-sb, ptfe-sb) fell through to the cage branch. Add media.construction_media() — maps SB-family exotic codes down to 'SB' — and route all three media=='SB' tests in unique_build through it (including the build_signature fallback label, a second missed site).
28 SB-family exotic rows now resolve correctly: 25 default-bands + 3 with real hardware/stainless. The 2 rows Claude flagged (156996-w-alum-sb-nordson, P19-1700-016-429_DAMN3-SB) are now default-bands; both are singleton classes at distinct dims, so no data merge is needed.
Also fix enumerate_unique_build.py to read title/body from the PRIMARY row (not the variant), matching the checker.
7f7aa87ece9a — 2026-08-20 — Viktor
fix: thread media= through unique-build generators (Claude review)
rekey_unique_build.py, enumerate_unique_build.py, rekey_exotic_media.py were still calling detect()/build_signature() with the media='80/20' default, so a re-run of the class-minting generator would re-create the 19 phantom SB 'bands' classes the F1 pass just merged. Thread the variant's own media (canonical_media) through; rekey_unique_build.py dry-run now reports 0 mixed classes.
Also: doc precision — SB construction 'defaults to bands and is unobservable from body text' (not 'is always bands'), since the cage wording is 80/20-default contamination and no SB deviation is expressible from the input signal.
af1237c84982 — 2026-08-20 — Viktor
resolve Rachel items #7 (F2 neither-check) and #8 (conical dims)
#7 — build F2 invariant build.neither_enumerated: flags cartridges whose copy says "no cage / no bands" but aren't enumerated in class-overrides.csv. "Neither" is a human-review flag, NOT a class attribute; "8\" or less" is a general standard (Brandon 2026-08-20), not coded as a size threshold. Detector unique_build.NEITHER / is_neither (conservative; excludes "no inner/outer liner").
#8 — conical F4 resolved: keep published conical dims as industry "shorthand" (nominal, orientation-unstated, verified with customer pre-purchase). ID>OD conical family (cls-NR008/053/054) NOT re-oriented; ID_GT_OD_ALLOWED is a permanent documented exemption.
2bc3b045a835 — 2026-08-20 — Viktor
docs: align class definition to 8 attributes + refresh counts + drop vestiges
Class = OD x ID x length x shape x media x top endcap x bottom endcap x build (8 attrs) — shape was omitted from AGENTS.md/README.md; now explicit per domain-model §5 and PLAN-2026-08-19 §5. Refresh counts (1,613 classes, 2,353 in-stock, 348 dcf-default). Remove 'anchor-based tolerance clustering' vestige; note conflict/status_note machinery removal.
2c47ff0bc773 — 2026-08-20 — Viktor
data: blank stale CLOSE_VARIANTS + 'In stock oval' notes (191 cells, col 112)
Human stock-status notes are stale; the class/bin model is authoritative. Removes 'NOT IN STOCK - Close variants...' (149) and 'In stock oval' (42).
eb87345ef1a9 — 2026-08-20 — Viktor
docs: domain-model 5.3 — cage/expanded-metal text is never a construction signal
Sales-history finding (2026-08-20): body HTML is product-level, so its 'expanded metal' wording describes the 80/20 default core and leaks into SB/NF variants that carry no cage. Band/strap language is the only construction signal; cage/helix/expanded-metal text is never read.
70686f15d294 — 2026-08-20 — Viktor
F1 media-aware build + remove stale notes; merge 19 phantom SB classes
- unique_build: media-aware detect/build_signature (SB default=bands, 80/20/NF default=cage; only deviations named). Merges 19 phantom 'bands' unique-build classes back into parent SB classes (50 rows); distinct classes 1632 -> 1613.
- remove stale 'NOT IN STOCK'/'MUST CHECK' human notes (127 cells across ad-notes/grade-notes/new-ad-group/sku_status_notes/sku_prev_grade/ sku_prev_price/sku_status) + conflict/SKU_NOT_STOCKED machinery. NF20322 restored to in-stock; in-stock 2353. CSV now 5 cols.
- fix body HTML: lm990ddd2 Shape Round->Oval; NF20322 OD/ID/L/Top.
- domain-model.md: drop 'pending' on F8/no_shape_span.
a60beb0f5d64 — 2026-08-20 — Viktor
reviews: Rachel review-queue CSVs (dcf no-info, flange/gasket-in-bottom, dupes, exotics)
39d954e272d1 — 2026-08-20 — Viktor
docs: AGENTS.md bin-7 note -> pre-drilled (D3)
90a1e9d5193b — 2026-08-20 — Viktor
data: pull NF20322 + normalize Cone->Conical + merge 4 phantom conical classes (1636->1632)
ed6011cad34b — 2026-08-20 — Viktor
fix: NF20322 stock regression (SKU_NOT_STOCKED gate) + verifier reads today's CSV
8a3d3f6023b7 — 2026-08-20 — Viktor
docs: 2026-08-19 Rachel session brief, plan, recording instrument, backlog
6bea227243c7 — 2026-08-20 — Viktor
artifacts: regenerate in-stock CSV (2353 SKUs, bin-7 rework cleared)
05b48235ec68 — 2026-08-20 — Viktor
D3: bin 7 pre-drilled - clear REWORK_BINS (single source of truth)
72c287802ace — 2026-08-20 — Viktor
invariants: add class.no_shape_span + shape_prefix_agree (shape is load-bearing)
f20e771e49d2 — 2026-08-20 — Viktor
data: F8 oval fix + collapse NF40198/NF20322 singletons into cls-NR015 (classes 1638->1636, in-stock 2351->2353)
823b7cb1c39d — 2026-08-20 — Viktor
docs: rewrite domain-model.md as canonical rules (8-attribute class)
5bbf93ca4747 — 2026-08-19 — Viktor
Nominal split + gray-zone/nominal-dup merges; build-primary fix; in-stock 2,351
split_nominal_overspread (36 classes/60 rows) + merge_gray_zone (18 rows) + merge_nominal_dups/merge_exotic_nominal_dups (15 dup pairs) + merge_build_label_dups (2 build-label pairs). In-stock 2,467->2,351: 14 exotic + 102 unique-build pulled (build-primary fix cleared the final 49 on 08-19).
AGENTS.md: fix nominal-split 'in-stock unchanged' (the 2,400->2,351 drop was the build-primary fix, not the split). Add verify_in_stock_list.py + diff_in_stock_csv.py.
97d1da4cb8d6 — 2026-08-18 — Viktor
Unique-build + exotic-media re-key; retire facet/axis vocabulary
Classification passes:
- unique_build.py detector + rekey_unique_build.py (178 variants -> 76 classes; 53 in-stock cleared)
- media.py canonical media + rekey_exotic_media.py (60 exotic variants -> 39 classes)
- survey/enumerate/rekey scripts + build-config + unique-build survey CSVs
Terminology scrub (Viktor): drop 'facet' and 'axis/axes' everywhere. Use 'attribute' (class-defining attribute) and 'dimension' (OD/ID/length); 'endcap configuration' / 'build configuration' for the physical groupings. Applied across src/, domain-model.md, README.md, rulebook, and the df-product-db skill. Historical dated handoffs preserved.
Verified: invariants 34 checks 0 FAIL; 1,533 classes / 2,400 in-stock unchanged.
07a8568571dd — 2026-08-18 — Viktor
AGENTS.md: distinct classes 1427 -> 1425 (duplicate oval classes merged)
22c2f9585789 — 2026-08-18 — Viktor
Fix classification_report BIN_MAP import regression; add disparity + class-grade reports
- classification_report.py imported BIN_MAP from build_stock_derivation, which the BIN_MAP consolidation removed; switch to CLASS_TO_BIN from bin_map.
- Add in-stock-price-disparity-*.csv (2467 rows: sku/vendor/class/dan-class/ grades/price/cost/margin + geometry) and class-grade-report-*.csv (1425 classes: members + old-grade counts).
- AGENTS.md: in-stock 2465 -> 2467.
0caf783495ed — 2026-08-17 — Viktor
Fix NR020/NR057 oval dupes; add in-stock CSV deliverable; consolidate BIN_MAP + NR collision check
- Merge cls-NR020->cls-NR015 (2 NF oval SKUs recovered; in-stock 2465->2467) and its spunbond twin cls-NR057->cls-NR043. Root cause: dims backfill wrote '11.40' vs the class's '11.4', and the exact-string NR classifier split one filter into two classes.
- src/build_in_stock_csv.py emits the n8n routing list (in-stock-skus-*.csv): sku/stock_id/class/media/requires_rework/status_note/conflict; 155 rows carry a conflict flag for Rachel's parallel review (in-stock-reconciliation-*.csv).
- src/bin_map.py is the single BIN_MAP source (was 4 hand copies); check_nr_signature_collisions (float-dims + shape) guards NR recurrence; build_stock_derivation.py resolves columns by name.
- Docs synced: 1425 classes, 2467 in-stock, bin 7 = 24 SKUs; 419-vs-457 reconciled to the derived count.
ebd8aa240fef — 2026-08-17 — Viktor
endcap terminology + rulebook re-tier + Viktor corrections
- Retire "gate"/"cap" for DF's in-house "endcap" / "endcap configuration" (top endcap + bottom endcap = a class axis). Rename code identifiers bottom_cap/top_cap/BOTTOM_CAP/STOCK_LABEL_BOTTOM_CAP and prose; rename backfill_bottom_cap.py -> backfill_bottom_endcap.py. Leave bdf.* fields and audit_checks' "cap" (= truncation limit) untouched.
- Rulebook rev.6+7: apply Viktor's rule corrections (panel removed from shapes; 1/16" tolerance wording; bdf.Length authoritative; x-grade = non-primary, in-stock->a; no external xref; gasket not settled), move 9 resolved rules to Locked, absorb 3, recount tally 62/13/11 = 86.
- Bin 7 relabelled "/OCB (must drill holes before shipping)"; bins 4/6 exist and are populated (was a wrong "no STOCK label" claim); bin 2 = 3 SKUs is correct, not a classing error.
- domain-model.md sweep + corrections (N-TOL-03, non-round tolerance, gasket position, bin table).
688c778f80f1 — 2026-08-17 — Viktor
fix: gate→bottom-cap rename, stale-count sync, DB staleness guard
Rename gate→bottom_cap in in_stock_matrix/classification_report/classify_dcf_default/classification_backlog + comment cleanup; add cli.py staleness warn (products.db vs Matrixify_Full.csv / edit_db.csv); close asymmetric ID>OD guard in backfill_dims; sync docs to 4,220 primary / 673/825 spread / 41 config_varies / 1,427 classes; deterministic config ordering in in_stock_matrix; README col-ref (61-67/104-131) + grade-gating (deferred) fixes.
aad3f9e20322 — 2026-08-17 — Viktor
Address Codex review findings (verified against data)
- HIGH: audit_checks.py check_missing_dimensions WHERE now includes inside_diameter (was missed — 292 rows missing only ID were invisible to the audit)
- MED: split_overspread.py assigns a distinct ID per non-modal cluster (latent 3+ cluster under-split)
- MED: refigure_bins.py falls back to the product primary's dims + bottom cap (bin 7 no longer '? x ? x ?'; now 13.8x9.479x26 closed+bolt)
- MED: pricing_report.py docstring clarified (writes report CSV, not edit_db.csv); report regenerated to 1,427 classes
- LOW: README synced (1,427 classes, 359 dcf-default, 673/825 price dispersion)
- invariants 0 FAIL
0d63bf27b3ec — 2026-08-17 — Viktor
AGENTS.md sync: 1,427 classes, 359 dcf-default, item #10 (ID bottleneck -> DFI)
a168d30e6d8f — 2026-08-17 — Viktor
Flange-aware dcf-default classification (23 rows -> 7 new classes)
- classify_dcf_default.py: flange-aware match (flanged -> flanged classes only), negation-aware detection, --dry-run/--apply
- edit_db.csv: 23 dcf-default primaries classified (15 flanged, 8 plain); 7 new classes cls-1380..1386 + matches to cls-0353/0422/0123
- only col 104 (new-class) changed, 23 rows; invariants 0 FAIL; distinct classes 1,420 -> 1,427
3fcccee72537 — 2026-08-17 — Viktor
Dims backfill from labeled body text (28 rows) + AGENTS.md sync
- src/backfill_dims.py: backfill bdf.OD/ID/Length from labeled Outside/Inside Diameter/Length fields (blank only); skips 'X to Y' ranges, unlabeled Dimensions tuples, '8..38' double-dot typos
- edit_db.csv: 28 rows backfilled (72 field writes, 0 unexpected diffs)
- class-overrides.csv: 72 log rows appended
- 23 dcf-default rows now classifiable (round+full dims+bottom cap); 15 are flanged -> classification blocked on flange-aware match (classify_dcf_default.py gap)
- AGENTS.md: dims backfill authorized+done; open item #10 updated
e06044004dcc — 2026-08-17 — Viktor
F-CAP-01 bottom-cap backfill + AGENTS.md sync (Viktor-directed)
- src/backfill_bottom_cap.py: backfill bdf.Bottom/Bolt_Hole from explicit body text (23 rows: 22 closed+no-bolt, 1 closed+bolt); logged per change
- edit_db.csv: 23 rows backfilled (only bdf.Bottom + bdf.Bolt_Hole changed, 0 unexpected diffs)
- class-overrides.csv: 46 log rows appended
- AGENTS.md: distinct classes 1,397->1,420; bdf exception expanded (bottom-cap backfill authorized); F-IDN-01 deferral (classes first, DF numbers last); F-CAP-01 status
- docs/open-questions-2026-08-17.md: F-CAP-01 resolved, F-IDN-01 deferred, dims-backfill question added
bd3bc542fa12 — 2026-08-17 — Viktor
Resolve confident rules + greenlit work (F-INT-01/02/03, tier moves, bin refigure, backfill mining)
- F-INT-02: rekey_flange.py exempts ovals (cls-NR015/NR043) from flange re-split
- F-INT-01: src/apply_overrides.py re-applies class-overrides.csv last (idempotent, 0 diffs)
- F-INT-03: src/partition_violations.py emits exact-signature duplicate-class candidates (0 today)
- rulebook rev.5: N-CLS-02/N-SHP-01/N-TOL-01 -> locked (L-CLS-09/04, L-TOL-05); F-TOL-01 retracted; tally 48/21/17 -> 51/18/17
- N-BIN-01: src/refigure_bins.py re-derives bin->class map from data (no drift; bin 7 cls-0951 has no primary)
- F-CAP-01: backlog refreshed (382 dcf-default; 27 more have Bottom:Closed/Open in body HTML)
- docs/open-questions-2026-08-17.md: genuine-need questions logged for Viktor/Rachel
755205bb4f32 — 2026-08-17 — Viktor
Split 23 overspread classes (1/16in tolerance) — flagged for Rachel
- src/split_overspread.py: new tool — clusters each overspread class within 1/16in on OD/ID/length, keeps the modal cluster's id, assigns new sequential ids to the rest.
- edit_db.csv: 37 rows moved to 23 new classes (distinct 1,397 -> 1,420); only new-class (104) + class-note (105) touched, verified field-by-field.
- class-overrides.csv: 23 rows logged, each 'review with Rachel'.
- Suite green again: 31 checks / 0 FAIL / 2 warn.
fda42815bd5a — 2026-08-17 — Viktor
Claude review: anti-drift guard, drop unsourced 'drills' claim, rulebook tier/tally
- domain-model S6.1: restore anti-drift guard without 'anchor' -- every member within 1/16in of every other (max-min <= 0.0625in); enforced by class.spread_within_tolerance.
- src/invariants.py (Claude): class.spread_within_tolerance check -- 23 classes exceed 1/16in end-to-end (legacy of the old looser tolerance; to be split in the refigure-classes pass). Suite now 31 checks / 1 FAIL / 2 warn.
- Drop unsourced 'DF drills bolt holes in-house' (was a hypothesis in a reference file, not a Viktor/Rachel statement): domain-model S8, rulebook L-CAP-06, class-overrides.csv, skill.
- Rulebook: N-TOL-01 de-TEMPORARY'd; L-CAP-05 + L-BIN-03 marked NOT LOCKED; N-SHP-01 marked LOCKED; N-BIN-01 = work item; tally 89->86 (3 retracted); rev.4.
9a45f6eeb1dc — 2026-08-17 — Viktor
Viktor rulebook corrections: 1/16in tolerance, anchor=competitive-only, gasket=flange-only
- Tolerance: single 1/16in (0.0625) threshold on OD/ID/length; drops percentage + review band (domain-model S6.1-6.3 rewritten; classify_dcf_default.py updated; rulebook N-TOL-01 rewritten, N-TOL-02 retracted).
- 'Anchor' = competitive loss-leader SKU only; anchor-based clustering language removed everywhere (rulebook L-TOL-02/03 retracted; domain-model S5.6; script anchors->existing).
- Gasket = top-cap facet that only splits flanged filters (domain-model S6.4/S7; rulebook L-CAP-04/N-CLS-02/N-CLS-03; AGENTS.md).
- 'Never' softened: Spunbond + flanged = 'not currently stocked' (AGENTS.md, rulebook L-BIN-06/L-STK-03, skill).
- Media suffix = OEM/marketing-SKU convention only (AGENTS.md, domain-model S4, rulebook L-MED-01/L-MED-03, skill).
- Grades = last pass, only grade A defined (rulebook L-GRD-03).
- N-SHP-01 resolved: two-axis body dimension overrides Round (rulebook).
- L-BIN-03 flagged for Rachel (where bin qty lives).
Deferred: N-BIN-01 (refigure classes), L-CAP-05 (bolt-hole spellings, exceptions possible).
9bfa1adad153 — 2026-08-17 — Viktor
End-cap model: kill 'gate', adopt 6-axis class + rulebook/invariants
Class = OD x ID x length x media x top end cap x bottom end cap (6 axes, was 7 with 'gate'). Bottom cap is the only opening that varies (open/closed/closed+bolt); top cap always open and carries the flange. Flange/gasket/bolt-hole are cap facets, not axes. 'Gate' was invented -- Carl's schema has bdf.Bottom + bdf.Bolt_Hole, no bdf.Top.
- Vocabulary rename: AGENTS.md, domain-model.md (S5/S6.4/S8), README.md, po-cost-matching.md, in-stock-matrix header (gate -> bottom cap; O/X -> open/closed/closed+bolt). Shelf labels stay verbatim.
- src/invariants.py (Claude): bottom-cap model, 30 checks / 0 FAIL / 2 warn. New endcap.top_cap_always_open, flange_is_top_cap + bolt_hole_is_bottom_cap (warn), body_copy.field_alignment (warn); STOCK_LABEL_BOTTOM_CAP dissolves bin-7 label contradiction.
- docs/rule-inventory.html (Claude): 89-rule rulebook (51 locked/21 not/17 fuzzy) for Viktor+Rachel review.
- HANDOFF-2026-08-17-ENDCAP-RENAME.md (Claude).
- class-note truthfulness: 382 dcf-default notes rewritten + 1,292 stale CLAUDE-FIX1 notes stripped.
- bdf geometry: 11 OD/ID swaps logged in class-overrides.csv (baseline exempts them).
- Doc sync: 1,397 classes / 2,465 in-stock; delete stale DRAFT-media-split.
- classification_backlog.py: gate hints mined from Title + Body HTML.
Deferred: 9 flange-in-Bottom_Config rows + 3 scrambled ovals stay WARN; src generators still use 'gate' internally (separate pass); rekey_flange.py has no oval exemption.
9aac8786015c — 2026-08-17 — Viktor
Flange + oval re-keys, passed-review vocab, backlog tool (2026-08-17)
Data (edit_db.csv):
- vendor_status active->passed-review (align with sku_status), 11,186 rows
- flange re-split: 677 flanged products -> 90 flange classes (cls-1267..1356); 1,321 variants moved; 327 flanged in-stock rows cleared (net)
- oval re-key (Rachel-confirmed): lm990ddd2->cls-NR042, 10020003_DAMN2-NF-> cls-NR015 (bin 1, now in stock), 10020003_DAMN3-SB->cls-NR043
- oval flange false-positives (cls-NR068/069) merged back - ovals never flanged
Tooling:
- src/rekey_flange.py (one-shot re-key), src/classification_backlog.py (dcf-default visibility), src/in_stock_matrix.py
- generated reports: classification-backlog + in-stock-matrix CSVs
Docs/skill:
- AGENTS.md + domain-model.md: flange axis, gate-no-exceptions, bin-7 O/CBH override, oval re-key, bdf col range fix
- SKILL.md full refresh; DRAFT-media-split marked superseded; passed-review vocabulary in PLAN/column-map/po-cost docs
316b683b5b95 — 2026-08-17 — Viktor
Class redefinition: media split + cartridge-only classes (2026-08-16)
- Split 18 media-spanning classes into media-specific IDs; 802 sibling classes assigned to 7,712 non-primary variants (distinct classes = 1,310).
- Retire non-cartridge dcf-default (1,283 -> blank); classes are cartridge-only.
- Shape inference: cartridge + blank bdf.Shape = Round (unless conical/oval).
- Resolve bin 7 = cls-0951 (O/CBH NF); in-stock = 2,791 across 9 bins.
- Renumber bin-10 class cls-0016 -> cls-0453 (out of stock).
- Add classification + pricing reports (classification_report.py, pricing_report.py) and their CSV outputs.
- Update AGENTS.md, README.md, domain-model.md to current state.
454efd7e6312 — 2026-08-16 — Viktor
Revise class definition: 7 axes (media + gate are class axes)
Post-meeting (2026-08-14 Rachel fact-check) revisions to planning and docs:
- Class = same OD x ID x length x gate x media x endcap x gasket (7 axes). Media is a class axis, not a variant suffix; gate is 3-state (O/O, O/C, O/CBH), not binary bolt-hole. Endcap/gasket are axes with no usable data.
- Dimensions are product-level; media is the only variant-level axis, so non-primary variants need a media-specific class ID (renaming task), not dimension re-derivation. cls-0027/0053/0059 must be split by media.
- DF part numbers: unique per class, NO media suffix (resolved).
- Fix bin tables: oval 14.38x11.38x26 (was transposed), bin 2 O/C, bin 10 7.93x3.57x16 O/CBH, bins 4/6 inferred, n-column labeled pre-B.4.
- 2,754 in-stock count pending recount (419 vs 457 unreconciled), not presumed wrong; leave build_stock_derivation.py alone.
- vendor_status gains failed-review; 4,260 no-match costs blanked (action).
- Soften Spunbond "never" as current practice, not immutable law (C-2/I-2).
- Record full fact-check answer table + open items.
78d4b3df81cf — 2026-08-14 — Viktor
Annotate PO-cost review SKUs in sku_status_notes
Viktor 2026-08-14: flag the PO-cost review SKUs (105) with a note+explanation so they can be reviewed with Rachel. src/annotate_po_review.py writes 'PO-cost review: <reason> — <detail>' into sku_status_notes (112) for the 25 in-DB SKUs (28 rows): 3 duplicate-sku, 10 media-mismatch, 11 'SKU ' prefix, 1 case-only. 79 unmatched have no catalog row (left in review CSV).
6675ae344991 — 2026-08-14 — Viktor
B.4 fix: bin 10 (cls-0016) -> no stock # (out-of-stock)
Viktor 2026-08-14: 'if it does not have an id, it should not have a stock #'. cls-0016 (bin 10) is out of stock, so its SKUs get BLANK sku_stock_id (not '10'). sku_stock_id is now only populated for in-stock bins (1-9). In-stock/grade unchanged (2,754). Verified 0 mismatches.
c3f15c9a76bb — 2026-08-14 — Viktor
B.4: derive sku_stock_id + sku_in_stock, grade in-stock A
Viktor 2026-08-14:
- sku_stock_id (106) derived from (class, media) -> bin map, replacing the dirty values (snapshotted to archive/stock-id-prefix-B4-2026-08-14.csv).
- sku_in_stock (113) = TRUE/FALSE per locked rules (SB->no bin, oval cls-NR015 NF-only, bin 10 out-of-stock, media exact) + B.0 inheritance.
- new-grade (107) = 'a' for in-stock cartridge SKUs (Viktor: in-stock -> A).
- Result: 2,754 in-stock SKUs, a strict superset of the corrected draft (2,745); +9 legitimate edge-case SKUs, 0 missed. Verified 0 mismatches outside cols 106/107/113. Fixed _DAMN2-NanoFR media detection + blank-SKU guard.
bd6800995c26 — 2026-08-14 — Viktor
Cleanup: populate sku_prev_*, drop redundant cols (132)
Viktor 2026-08-14:
- sku_prev_grade <- variant custom_label_0; sku_prev_class <- PRODUCT custom_label_2 (DAN); sku_prev_ad_group <- variant custom_label_1.
- Remove old-grade/old-ad-group/old-class/NOTES (redundant intermediates); NOTES redistributed to grade-notes (grade-change notes, 18) and sku_status_notes (everything else, 396).
- Remove vendor_cost (redundant mirror of Variant Cost; history in vendor_history_po + QBO).
- 137 -> 132 columns. Verified field-level: 0 mismatches.
docs/column-map.md rewritten; domain-model.md col ref fixed; PLAN revision note; df-product-db skill synced.
7777e061130a — 2026-08-14 — Viktor
Variant-only revert + stock-id -> sku_stock_id rename
Viktor 2026-08-14: collapse the temp/backup field level-split to variant-level only, and rename stock-id (110) to sku_stock_id.
- src/apply_variant_only_revert.py: re-points old-grade/old-ad-group/ old-class (68/69/70) from VARIANT custom_label (97/73/72) instead of product (63/64/65); drops 9 net-new product-level + -var twin columns (111,112,113,115,116,117,126,128,130); renames survivors to the brief's plain names (new-grade, new-ad-group, sku_prev_grade/class/ad_group, sku_stock_id).
- 146 -> 137 columns. Verified field-level: 0 mismatches vs B.3 outside the re-pointed columns; old-grade/old-ad-group/old-class now 12,993 / 4,572 / 286 variant values.
- docs/column-map.md rewritten; domain-model.md §8 + PLAN revision note updated; df-product-db skill synced.
37f6161df8b7 — 2026-08-14 — Viktor
Phase B.3: PO-cost + vendor_* backfill into edit_db.csv
New src/build_po_backfill.py writes the 9 vendor_* fields + native Variant Cost from the most-recent PO per SKU (natural + class + media-aware, with inheritance so non-primary siblings get costs too).
Results: 1,059 PO skus -> 954 clean natural + 10 media-mismatch + 3 duplicate-SKU + 92 unmatched. Catalog: 11,186 active (954 natural + 10,232 class-extended), 4,260 under-review (cost blanked, old value in sku_prev_cost).
Media-safe: costs only extend to same-media variants. Unmatched->blank per decision 15. Review rows -> archive/po-backfill-review-2026-08-13.csv.
0d75563ba0d8 — 2026-08-14 — Viktor
Phase B.0/B.1: class-inheritance validation + snapshot backfill
B.0: validated inheritance map (5,862 products, 1 primary each; 9,584 non-primary rows all map to a primary — 7,716 real class, 1,868 dcf-default). Inheritance kept derived (domain-model 5.1), not written.
B.1: snapshots written:
- old-grade/ad-group/class (prod 68/69/70 + var 113/115/117) = current custom_label_0/1/2 at both levels
- sku_prev_price(124)=Variant Price, sku_prev_cost(125)=Variant Cost
- sku_status(121) seeded: 606 passed-review, 14,840 under-review (Var_Pos_Confirmed)
- 29 legacy OLD* rows -> archive/old-star-legacy-values.csv (preserved first)
- 414 NOTES rows -> archive/notes-legacy-values.csv
171bcd0795d6 — 2026-08-14 — Viktor
Docs: repo-local column map + Phase E renames + B.1 hard precondition
- Add docs/column-map.md (146-col offset table, readable by any agent)
- AGENTS.md points to docs/column-map.md instead of the Hermes-only skill
- PLAN: attach the 7 renames to Phase E.3 explicitly; state B.1 step-1 (legacy OLD* preservation) as a hard precondition
7a47ddfa038e — 2026-08-14 — Viktor
Phase A: append 35 field-expansion columns to edit_db.csv
Appends 35 net-new working columns (temp -prod/-var, sku_*, vendor_*, dfi_*) after stock-id (col 110) -> 146 columns total. Existing 111 columns byte-identical (verified field-by-field); appended columns blank.
Also labels the previously-unlabeled col 67 (18 legacy draft-grade/note values, archived to archive/col-67-legacy-values.csv) and updates the df-product-db skill column-offset table.
289d92c08b24 — 2026-08-13 — Viktor
Baseline: classification work (edit_db.csv), PO-cost data, docs, and field-expansion plan
Pre-field-expansion baseline. Commits edit_db.csv (the classification work product: 5,862 class assignments, stock-id mappings, grades), po-cost.csv, in-stock SSOT, all docs/, AGENTS.md, handoff, and the field-expansion plan. No data changes — snapshot only, to give Phase A a revert point.
c719aa414aac — 2026-07-28 — Viktor
Phase 1: Matrixify import, query tools, and data-quality audits
Builds a local SQLite database from the Matrixify export of the Shopify product catalog, with query and audit tooling for staff and agents. Python stdlib only.
Three layers per ARCHITECTURE.md: immutable raw_matrixify (all 102 CSV columns as TEXT), normalized products/variants/tags/bdf_metafields, and FTS5 over products. Layer 3 operator metadata is Phase 2.
- import_matrixify.py: SHA-256 snapshot IDs, atomic single-transaction import with rollback, idempotent re-import, automatic ALTER TABLE for new metafield columns in future exports, validation logged to import_log
- query_tools.py: read-only SKU / handle / dimensional / FTS5 search; duplicate SKUs and duplicate field definitions are surfaced, not hidden
- audit_checks.py: 8 data-quality checks; capped checks emit an explicit TRUNCATED finding carrying the true total
- cli.py: import, query, audit, report; diff is a Phase 3 stub
Current snapshot matrixify_18778ce2199c: 5,862 products, 15,446 variants, 21,680 tags, 4,525 bdf records, 435 field definitions. A clean rebuild reproduces identical counts and snapshot ID.
data/products.db is gitignored and rebuilt locally via cli.py import. imports/*.csv are marked -text so their bytes, and therefore their snapshot hashes, round-trip identically across installations.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>