MethodInternal23:xx
King Seiko missing-42 hunt list; the gap is source material, not decoding
Goal
Decide whether the 42 King Seiko references we lack are recoverable from our data or need new sources.
Method
- For every King Seiko reference on kingseiko.info that we do not hold, search all pipeline/data/extractions JSON and the DB for its JDM code.
- Confirm which 1969-1976 catalogs we have and which are extracted.
- Save the gap and the acquisition targets to research/king_seiko_missing.md.
Results
- All 1969-1976 KS-year catalogs are present, extracted, and in the DB (we captured 100+ KS appearances from the 1972-75 volumes).
- Of 57 missing codes, 50 appear nowhere in our data; the 7 that do appear are in page text with zero watch rows.
- So the 42 missing references are not pictured in our catalog volumes — hunt, not decode.
- King Seiko is complete to the limit of our catalogs: 47 of 89.
Rejected
Re-decoding or re-extracting our current catalogs to recover them — the watches are not in the volumes we have. Acquisition targets: the 1972 Vanac brochure (most of the 18 Vanac), a 1971 catalog (52KS), more 1974-75 volumes.
App22:xx
Rebrand the header to seikobase.com (top right)
Goal
Name the site seikobase.com.
Method
Replace the Vintage Seiko — WatchDB wordmark with seikobase.com, placed top-right in the header as the home link.
Results
The header now reads seikobase.com, top-right.
Data21:xx
King Seiko: decode JDM codes from the source-of-truth tables
Goal
Recover and complete the King Seiko sub-collections from the authoritative reference tables.
Method
- Build research/king_seiko.md and research/king_seiko_refs.csv from kingseiko.info (the source of truth): 86 distinct casebacks across 44/45/56/52KS and Vanac, each keyed by its JDM code.
- build_king_seiko_refs.py normalizes the abbreviated 44-/45- casebacks and tags collection by source page (56KAW and 52KWL appear in both a base series and Vanac; only the page and number tell them apart).
- decode_king_seiko.py joins our rows' codes (KSSK 010, 56KAW 267) to the table, giving caseback + sub-collection + caliber, so rows with no case design in the text still decode.
- Set the registry est to the site's distinct-caseback counts (44KS 3, 45KS 10, 56KS 37, 52KS 9, Vanac 27), Confirmed.
- Dry-run, back up, apply.
Results
- Decoded 123 King Seiko rows (was ~0 identified mechanical).
- Held vs est: 44KS 3/3 (100%), 45KS 8/10, 56KS 23/37, 52KS 4/9, Vanac 9/27 — 47 King Seiko models identified.
- 154 rows still undecoded (no recognizable code, or a number not in the table).
Rejected
Decoding by the case design in our own OCR text — only 25 rows had one. Keying on the JDM code against the site table decoded 123.
watchdb.sqlite.bak-20260816-135155-decodeKS
Data20:xx
Recover the 62MAS — both 6217-8000 and 6217-8001 (collection complete)
Goal
Find the 62MAS, Seiko's first diver, which held 0 identified models.
Method
- The catalog prints the caliber as 62MAS, not 6217, so the earlier caliber search missed it.
- Two rows carry 62MAS 010 (800-800) at 17 jewels: id 356 in the 1967 catalog and id 616 in the 1968 No.1 catalog.
- TheSeikoGuy documents the catalog-year mapping: 1966-67 shows the small crown 6217-8000, 1968 vol.1 the big crown 6217-8001.
- Boley lists both references.
- Set caliber 6217, caseback_ref, and collection 62MAS: id 356 to 6217-8000, id 616 to 6217-8001.
- Widen the 62MAS window to 1965-1968 in the registry so the rule stays reproducible.
Results
- 62MAS now holds both references, 6217-8000 and 6217-8001: 2 of 2, 100% complete.
- First hunt-collection closed from 0 to done.
watchdb.sqlite.bak-20260816-133136-62mas
App19:xx
Status: 6159 Diver at 100%, two-decimal headline shares
Goal
Reflect the completed 6159 collection and show precise headline percentages.
Method
- Set 6159 Diver est to 3 (its three references 7000, 7001, 7010) and Confirmed in research/collections-logic.md; regenerate collections.ts.
- Show the Identified, Right name, and Right collection headline shares to two decimals on the Status page.
Results
- 6159 Diver reads 3 / 3 · 100% on Status.
- Headline shares now show two decimals (right name 66.56%, right collection 0.05%, identified 0.03%).
Data18:xx
Recover the missing 6159-7001 (1969 300m hi-beat diver)
Goal
Add the missing 6159 reference — the 1969 300m hi-beat monocoque diver that we lacked.
Method
- Two caliber-6159 Seiko Diver rows (1969 and 1970) carry the catalog case code 011; the 010 code is the proven 6159-7000, so 011 is the increment.
- Confirm 6159-7001 exists in Boley independently, with its own parts (crystal 325W14GA).
- Domain-expert confirmation that 6159-7001 is a real JDM reference.
- Set caseback_ref = 6159-7001 on ids 2148 and 2678.
Results
- 6159 Diver now holds its three references: 6159-7000, 6159-7001, and 6159-7010.
- ids 2148 and 2678 recovered from caseback-less to identified.
watchdb.sqlite.bak-20260816-132228-6159-7001
App17:xx
Hide the case-material field and filter
Goal
Stop showing case material until we trust the data.
Method
- Hide the Material filter in the sidebar and the case material row on the watch page.
- Keep both in code, commented, so they are easy to restore once the material data is trusted.
Results
Case material no longer appears in the filter sidebar or on the watch page.
App16:xx
One page container across the app + a prominent collection callout
Goal
Give every page one container, and make a watch's collection and details easy to read.
Method
- Add a shared Container (max-w-5xl, centered, one padding) used by the watch, changelog, and status pages.
- Narrow the browse index to max-w-6xl (was full-bleed) with the same centering and padding; drop the 5-column grid tier.
- On the watch page, turn the collection paragraph into a callout: accent panel, icon, collection-name heading, readable foreground text, placed first under the name.
- Show every specification in one simple card, one per line (identity, case, dial, and caliber specs together).
Results
- The index, watch page, changelog, and status share one container width and padding.
- The collection reads as a clear callout, not gray small print.
- Every watch specification sits in one card, one per line.
Rejected
A single identical width for every page. We kept the browse index one step wider (max-w-6xl) than the reading pages (max-w-5xl) for grid density.
App15:xx
Show only identified watches; unify model identity + title format
Goal
Make counted, shown, and distinct mean one thing, and display only watches that have a real model name.
Method
- Define one identity: the model name = caseback reference, else sales code, else pre-1966 case code (caseback first, so one watch keeps one identity even when a later catalog also prints its sales code; ERA_NAME_SQL in lib/db.ts).
- Show a watch only if it has that name; hide the rest across the grid, the API feed, and the facet counts.
- Dedupe the catalog by that same name, so shown count equals distinct count.
- Change the title to <model name> — <collection> (lib/identity.ts, modelName).
- Count the facet sidebar and the Status have-column by that same identity, so a collection shows the same number everywhere.
Results
- The app shows 7,753 identified models.
- 6159 Diver: 2 identified models, 6159-7000 and 6159-7010, each titled e.g. 6159-7010 — 6159 Diver.
- Un-referenced watches no longer appear anywhere.
- Counted, shown, and distinct now agree everywhere: 6159 Diver reads 2 on the grid, the facet sidebar, and Status.
Rejected
A year-gated rule (sales code for 1975+). It split one physical watch into two cards when a later catalog also printed its sales/order code (6159-7010 vs YAQ028). Caseback-first fixes it.
App14:xx
Catalog shows distinct models, random order, collection-aware names
Goal
Show the catalog as distinct watches in random order, each named by its collection.
Method
- Grid dedupes to one card per distinct model, keyed by caseback reference, then modern sales code, then model_key; a window function keeps the best appearance (has a crop, has a reference, earliest).
- Every appearance stays in the DB as provenance; the watch page still lists the other catalog appearances.
- Default order is a fresh random seed per page load, threaded through infinite scroll so the pages stay consistent.
- Card and page titles now prefer the assigned collection over a soft hint or a raw catalog heading (lib/identity.ts).
Results
- The catalog shows 12,836 distinct wristwatch models instead of 35,412 appearances.
- 6159 Diver: 14 appearances collapse to 5 distinct models.
- Tagged watches read e.g. 6159 Diver 6159-7010, and caseback-less ones read 6159 Diver 1969 instead of Special Watches 1969.
- Arriving on the home page shows a fresh random order, not oldest-first.
Rejected
Relabeling duplicate appearances as unknown in the DB. Appearances are real provenance (the same model in another catalog year, with its own crop), not errors; we dedupe at the query layer instead, which is reversible and keeps the history.
MethodInternal13:xx
Defined the database-completion method (Part V of the README)
Goal
Define one robust, repeatable process for completing the database, so tagging never drifts.
Method
- Adopt a conservation law: every distinct model resolves to one collection or to Unclassified, and per year window the collection counts plus Unclassified equal the total.
- Work in vertical slices, one family end to end.
- Names before collections, by era (case_code, caseback_ref, sales_code).
- Assign each collection with the same six-step recipe: rule, dry-run, verify, assign, reconcile, log.
- Keep all tagging as re-runnable rules in assign_collections.py, with a reconciliation gate after every batch.
- Write it as Part V of the README.
Results
- The completion approach is documented in the README (Part V).
- No code is written yet; the spec and the scripts come next.
Rejected
Tagging the whole corpus at once, and leaving unclassified watches as NULL. We chose vertical slices and an explicit Unclassified bucket so the sum always balances.
Tagging13:xx
First collection slice: 6159 Diver (Tuna) + the assign_collections tool
Goal
Build the re-runnable collection assigner and complete the first vertical slice.
Method
- Add pipeline/scripts/assign_collections.py: registry-driven rules (caliber or name), idempotent, with a dry-run, a per-slice apply that backs up the DB first, and a conservation report by decade.
- A model that matches two collections is left Unclassified, never guessed.
- Verify the 6159 candidates against the catalog text, then apply.
Results
- 6159 Diver: 7 models (14 rows) assigned, all caliber 6159, verified.
- Dry-run map: base rules auto-assign 4,960 of 15,065 models (33%); 10,105 Unclassified; 110 ambiguous models flagged for tiebreakers.
- Conservation balances in every decade.
- Collections assigned in the DB: 1.
Rejected
Assuming the caliber spine is complete. Many Grand Seiko and King Seiko families hold 0 models by caliber, because their calibers were never extracted; those families need hunting or name recovery first, not tagging.
watchdb.sqlite.bak-20260816-122541-assign-6159Diver
App12:xx
Structured change log (CSV) + /changelog page
Goal
Keep one structured file we append to for every change, and show it clearly at /changelog.
Method
- Store every change as a row in CHANGELOG.csv (date, time, type, title, goal, method, results, rejected, backup).
- Parse the CSV at request time in apps/web/lib/changelog.ts.
- Render each change as a card with a type badge and labeled Goal, Method, and Results sections.
- Add a per-collection paragraph on each watch page from research/collections-paragraphs.md.
- Rename research docs to kebab-case and add research/README.md.
- Add CLAUDE.md with the logging rule and the change-type taxonomy.
Results
- /changelog parses the CSV directly, so there is no regenerate step.
- 81 of 81 collection paragraphs matched.
- typecheck passed.
- /changelog and /watch/176 returned HTTP 200.
Rejected
An intermediate generated TS file for the changelog. We replaced it by parsing the CSV at runtime, so appending a row needs no script.