Canon logo
DF Canon
Product database handbook — confidential, internal use

Git commit history

Complete log of every commit in the df-product-db repository, newest first.

xychart-beta title "Commits per week — last 60 days" x-axis ["Jul 6", "Jul 13", "Jul 20", "Jul 27", "Aug 3", "Aug 10", "Aug 17", "Aug 24", "Aug 31", "Sep 7"] y-axis "commits" 0 --> 60 bar [0, 0, 0, 1, 0, 11, 43, 8, 60, 28]

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

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

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

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)

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

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

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)

fa88766b30a7 — 2026-09-07 — Viktor

fix: drift-report re-bucket + Type-uniform WARN invariant (Fable review)

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

03e9c3b4c149 — 2026-09-07 — Viktor

docs: adopt optional analysis tooling (openpyxl declared, duckdb read-only)

64e854acad6d — 2026-09-07 — Viktor

fix: restore wrap-accessory de-type + P032931 stainless price (Claude review)

f00dbe38b775 — 2026-09-07 — Viktor

chore: WARN invariants (OD==ID, dup primary SKU); decommission apply_overrides

79535687bbfe — 2026-09-06 — Viktor

fix: body-copy field alignment + blank-shape backfills (Gemini review)

4d3d761c50b6 — 2026-09-06 — Viktor

fix: correct 09-06 Donaldson/spunbond passes (Claude + Codex reviews)

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)

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

0fc15b1a2227 — 2026-09-04 — Viktor

feat: supplemental import (61E45 price + 40 position collisions)

Verified: invariants 0 FAIL, 82 tests OK.

9684a9bb2332 — 2026-09-04 — Viktor

fix: 61E45 variant MPN corruption; drop double file write in builder

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)

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):

Import builder (build_matrixify_import.py, re-runnable):

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

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

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

e35f71f559fc — 2026-09-01 — Viktor

feat: spun-bond reclass + consolidated review list

901eb4867215 — 2026-09-01 — Viktor

feat: class remediation A5+A4+A2 (shape/endcap backfill, tolerance-window merge)

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

5499c7e39d19 — 2026-09-01 — Viktor

fix: address Claude review — preserve Google Shopping cols, normalize SKU copy, Variant Command, header aliases

c37d82a5909e — 2026-09-01 — Viktor

feat: merge 09-01 Matrixify export (adopt Jason changes, preserve project data)

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

f7981e5c9f71 — 2026-08-31 — Viktor

fix: harden fix_body_copy_transpose.py (Claude review, 6 fixes)

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)

261830f7f977 — 2026-08-31 — Viktor

feat: final-phase kickoff — metafields, triage, DFI review, self-test, fixes

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

2a503a5e43d6 — 2026-08-31 — Viktor

fix: D1 DELETE into native Variant Command (col 27), drop duplicate col 136

f5b2d2d00917 — 2026-08-31 — Viktor

test: Tier 2 (media + unique_build unit tests) + Tier 3 (golden tests for --apply tools)

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

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)

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)

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:

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

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):

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

70bf8ce45e28 — 2026-08-27 — Viktor

feat: final-export data pass — dim write-back + class split/merge + bin remap

bc5885e41111 — 2026-08-20 — Viktor

fix: report-output hygiene + no_hardcoded_counts scope

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

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:

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

0caf783495ed — 2026-08-17 — Viktor

Fix NR020/NR057 oval dupes; add in-stock CSV deliverable; consolidate BIN_MAP + NR collision check

ebd8aa240fef — 2026-08-17 — Viktor

endcap terminology + rulebook re-tier + Viktor corrections

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)

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)

3fcccee72537 — 2026-08-17 — Viktor

Dims backfill from labeled body text (28 rows) + AGENTS.md sync

e06044004dcc — 2026-08-17 — Viktor

F-CAP-01 bottom-cap backfill + AGENTS.md sync (Viktor-directed)

bd3bc542fa12 — 2026-08-17 — Viktor

Resolve confident rules + greenlit work (F-INT-01/02/03, tier moves, bin refigure, backfill mining)

755205bb4f32 — 2026-08-17 — Viktor

Split 23 overspread classes (1/16in tolerance) — flagged for Rachel

fda42815bd5a — 2026-08-17 — Viktor

Claude review: anti-drift guard, drop unsourced 'drills' claim, rulebook tier/tally

9a45f6eeb1dc — 2026-08-17 — Viktor

Viktor rulebook corrections: 1/16in tolerance, anchor=competitive-only, gasket=flange-only

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.

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):

Tooling:

Docs/skill:

316b683b5b95 — 2026-08-17 — Viktor

Class redefinition: media split + cartridge-only classes (2026-08-16)

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:

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:

bd6800995c26 — 2026-08-14 — Viktor

Cleanup: populate sku_prev_*, drop redundant cols (132)

Viktor 2026-08-14:

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.

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:

171bcd0795d6 — 2026-08-14 — Viktor

Docs: repo-local column map + Phase E renames + B.1 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.

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>