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.
ModelInternal23:xx
Identity model: collapse to two tiers (caseback is the identity 1966-1998)
Goal
Fix the identity model so identified means a real, findable reference, per an expert review.
Method
- An external Seiko-expert review confirmed the caliber-case caseback reference is the primary physical identity for every watch 1966-1998; the sales/order code is a secondary cross-reference.
- Collapse the 3-tier model to 2: case code before 1966, caseback from 1966 on.
- A watch held only by a sales code is caseback pending, not identified.
- Update NAME_SQL and the eras in lib/db.ts, the Status page, research/identification-methodology.md, and save research/identification-expert-review.md.
- Size the recovery: 2,075 sales-code-only models (518 need the case, 1,199 need the caliber, 606 need both).
Results
- Right-name share drops from 67.32% to 54.33% — the honest number.
- 2,075 models reclassified from named to caseback pending, shown on Status.
- The sales code is now a secondary cross-reference, never the primary identity.
Rejected
Keeping the sales code as the 1975-98 identity. It is a retailer ordering code, not the physical identity; sales-code-only watches are not findable (AQC834 vs the caseback 2320-5420).
DataInternal23:xx
Recover 502 casebacks for sales-code watches from the extraction captions
Goal
Turn caseback-pending modern watches into real, findable references by recovering the missing half.
Method
- The missing half is usually still in the extraction caption raw_text — the case in a <NNN NNN> bracket (numeric like <314 014> for vintage, alphanumeric like <5G7-5J 2> for modern quartz), the caliber as NNNN NN-ishi — that the DB build dropped.
- recover_caseback_from_captions.py joins the caption to the row by NORMALIZED sales code (HDF 928 == HDF928), scanning every text block (caption + loose entries), and rebuilds caliber-design0.
- A strict format guard rejects OCR junk: a false caliber 17QS (17 jewels QS) and truncated 3-digit calibers (137 from a misread 3137) were caught before writing.
- Boley confirms where it covers the caliber; own-caption evidence is trusted otherwise.
- Dry-run, back up, apply, idempotent.
Results
- 502 models gained a caseback across the passes.
- Caseback pending 2075 -> 1573.
- Right-name 54.33% -> 57.42%.
- e.g. HDF928 -> 6810-3140, MNC824 -> 4120-8020, SABR001 -> 5P31-5A90.
Rejected
Web / Jules Borel lookup by sales code (JDM codes not indexed). Sibling self-join and own-DB-refs (~70; the refs column dropped the bracket). Page-level entries (they strip the bracket). The remaining 1,573 have no case in our extraction at all.
watchdb.sqlite.bak-20260816-155537-scodecaseback
MethodInternal23:xx
External sales-code lookup is a dead end (JDM codes not indexed)
Goal
Test whether an external source can map a JDM sales code to its caseback for the 1,891 remaining caseback-pending watches.
Method
Search the web and Jules Borel for sample JDM sales codes (AAL010, ACD021, ATR020, AQS023), and by caliber + line.
Results
- Searching a sales code returns only generic line pages — the JDM ordering codes are not indexed anywhere (confirms the earlier LinkUp result and the expert note).
- Searching by caliber returns real casebacks but many per caliber (9520 -> 5110/5120/5130/5140/5190), with no public concordance to pin which one a sales code is.
- Jules Borel is keyed by caliber-case, not by sales code.
- The web validates our recovered casebacks (9520-5130, 2435-3010 are real) but cannot map a sales code to its caseback.
Rejected
Web / Jules Borel lookup by sales code. The reliable remaining sources are a structured JDM concordance (Plus9Time 1991 catalog, 1981 USA microfiche) or re-extracting page-level text from the catalogs we already have.
MethodInternal23:xx
Save the Plus9Time Seiko case-material codes reference
Goal
Keep the Seiko case-material code table for decoding catalog case-material codes.
Method
Save the Plus9Time case-material-code table (built from the 1991 JDM catalog Vol.1) to research/seiko-case-material-codes.md: abbreviation, description, and case-back stamping.
Results
research/seiko-case-material-codes.md added (~180 codes).
ModelInternal23:xx
Make the two-tier identity consistent across all docs and Status
Goal
Remove the leftover three-tier language after adopting the two-tier identity model.
Method
- Collapse the README identity table and the Status Names-present-by-era section to two rows: family case code before 1966, caliber-case caseback for 1966-1998.
- Add the two-tier note (sales code is a secondary cross-reference, caseback-pending is not identified) to README Part V, pipeline/docs/PIPELINE.md, and the Status intro.
Results
- README, the Status page, PIPELINE.md, and identification-methodology.md now all state two tiers.
- The Status era section shows two rows, not three.
- typecheck clean.
AppInternal22: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.
DataInternal21: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
DataInternal20: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
AppInternal19: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%).
DataInternal18: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
AppInternal17: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.
AppInternal16: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.
AppInternal15: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.
AppInternal14: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.
TaggingInternal13: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
AppInternal12: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.