feat: enrich I Ching data layers and Legge coverage#4
Draft
pro-vi wants to merge 22 commits into
Draft
Conversation
Plan covers 11 units across two tracks (zh canon + types + renderer + CLI on Track A; Legge cleanup + integration on Track B). data-acquisition/ holds verified pulls and intermediate scaffolding; the structured JSON outputs feed U2/U3/U8/U10 backfills but only the resulting data/ modules ship. Source pulls do not travel with the repo.
Extends Hexagram with optional gc, gcEn, yaoXiao, yaoXiaoEn, legge. Extends TrigramInfo with optional assoc. Adds new interfaces: LeggeHexagram, TrigramAssoc, XuGuaEntry, ZaGuaEntry, ShuoguaChapter, ShuoguaCitation, CastConnections. Style/QuoteStyle union extension deferred to U7 (lockstep with the CLI consumer sites at format/reading.ts:31 and output/plain.ts:96 that do g[style] indexed access). New optional fields are fully usable via direct access (gua.gc, gua.legge, etc.) in U2-U6.
xugua.ts: XU_GUA — 64 序卦傳 entries in King Wen order, sourced from ctext.org with zh.wikisource cross-check. Editorial notes on hex 1, 2, 30, 31 preserved from the verified pull's _meta. Provenance in XU_GUA_META. zagua.ts: ZA_GUA — 53 雜卦傳 entries preserving the canonical disordered tail. ZA_GUA_BY_HEX is the 64-key reverse index (many-to-one for the self-mirror pairs where two hexagrams share an entry — 乾/坤 at index 0). Provenance in ZA_GUA_META. index.ts: re-exports the new modules plus the U1 type additions (XuGuaEntry, ZaGuaEntry, ShuoguaChapter, ShuoguaCitation, CastConnections, LeggeHexagram, TrigramAssoc) that were missed in U1. Tests: 19 integrity assertions — exact lengths, sequential indices, verification anchors (opening cosmological premise on hex 1+2; 雜卦 opens with 乾/坤 pair), every hex 1..64 covered exactly once, reverse index correctness for the 8 self-mirror pairs. Plan: docs/plans/2026-05-30-001-feat-data-enrichment-plan.md
shuogua.ts: SHUOGUA — 11 canonical chapters per the standard 王弼/孔穎達 division (周易正義). TRIGRAM_ASSOC — 8-trigram structured catalogue keyed by zh char. Sourced from ctext.org with 4 cross-checks. Provenance in SHUOGUA_META. Canonical fields (image, family, body, animal, direction, attribute, extendedImages) normalized to pure canonical chars per 說卦 ch.7-10 — phonological variants ((悅) on 兌) and alternate images (木 on 巽) stripped from canonical columns; the alternates already live in extendedImages where the canon places them. Optional editorial fields (season, cosmologicalRole, other) carry English glosses; UI must label non-canonical per the U4 plan. Tests: 16 integrity assertions — chapter count + sequential numbers, verification anchors (ch5 directional cycle, ch8 animals, ch11 extended catalogue), every canonical column matches 說卦 verbatim (馬牛龍雞豕雉狗羊 animal column, 健順動入陷麗止說 attribute column, 父母三男三女 family column via 索 scheme), 乾 extendedImages has the 14-image catalogue, editorial fields present on 乾 spot-check. Plan: docs/plans/2026-05-30-001-feat-data-enrichment-plan.md
trigrams.ts: wires TRIGRAM_ASSOC into each of the 8 TRIGRAMS entries via the new .assoc field (by reference, not copy — same identity as TRIGRAM_ASSOC[zhName]). Adds TRIGRAM_ASSOC_EN — project-authored short English glosses for family/body/animal/direction/attribute, keyed by trigram zh char. NOT from Legge or Wilhelm/Baynes; these are project-supplied UI convenience labels. UI surfaces MUST label them as "project gloss" per the U6/Q5 decision. Excludes extendedImages (no terse en for 14-item polysemous catalogue — render in zh). Tests (9): TRIGRAMS shape preserved (sym/n/img unchanged from baseline), every .assoc points into TRIGRAM_ASSOC by reference, all canonical fields populated, TRIGRAM_ASSOC_EN has 8 keys + 5 fields each, no extendedImages leak, ASCII-only check for the en glosses (defense against accidental zh paste), family-scheme spot check (father/mother/eldest/middle/youngest). Plan: docs/plans/2026-05-30-001-feat-data-enrichment-plan.md
derivation/connections.ts: pure function
connections(cast: Pick<Cast, "primary">): CastConnections
that joins the numeric derivations with canonical text data — XU_GUA
(序卦 sequence narrative), ZA_GUA_BY_HEX (雜卦 contrast pair), and a
static DERIVATION_CITATIONS map (5 ops → 說卦 chapter). No I/O.
detail.ts: HexagramDetail extends with .connections; buildHexagramDetail
synthesizes a minimal {primary: kw} for the lookup. Existing fields
unchanged.
index.ts: re-exports connections.
Tests (14): overlay correctness on hex 1/3/29/30, all-hex coverage,
shuoguaCitation chapter validity, fresh-array per call (no shared
mutable state), HexagramDetail integration. The satisfying adversarial
check is the orbit-integrity invariant — for every non-tail hex with
a 2-element 雜卦 pair, the pair contains the geometric partner
(mirror, with polarity fallback for the 8 self-mirror hexagrams). All
56 non-tail hexagrams pass; the famously disordered final 8 (27, 28,
43, 44, 53, 54, 63, 64) are whitelisted by Set membership.
Plan: docs/plans/2026-05-30-001-feat-data-enrichment-plan.md
detail-model.ts: DerivedLink gains an `op: DerivedType` field so the renderer can look up each row's 說卦 chapter citation. Populated in the constructor for all 4 numeric derivations (nuclear/polarity/mirror/ diagonal). detail-renderer.ts adds: - VoiceNode interface + renderStackedVoices helper. Voice-driven, content-gated, fixed order (zh → modern → wilhelm → legge). Empty voices skip. Scaffolded for U10's full activation; used in U6 for the 卦辭 path. - Compact 說卦 trigram catalogue (family / body / animal / direction) inline under the trigram line. Always-on (U4 populates assoc for all 8 trigrams). - 卦辭 section at the top of the commentary block — gated on gua.gc (absent for legacy data until U8 backfills, so legacy renders unchanged). - 小象傳 lines under each 爻辭 in the yao block — gated on gua.yaoXiao[i] (absent until U8). - Relations block (renamed from "Derived"): each numeric derivation row carries a [說卦 ch.N] annotation pulled from U5's connections.shuoguaCitations; followed by 序卦 (sequence narrative) and 雜卦 (contrast pair) text-bearing rows from connections.xuGua / zaGuaPair. Both always present (U5 populates for every valid hex). Tests (10): always-on additions visible (trigram catalogue, Relations header, 說卦 chapter annotations, 序卦 with 故受之以 transition, 雜卦 with 剛/柔 contrast); gated sections correctly absent for legacy data (no 卦辭 header, no 小象 lines); legacy commentary sections preserved (大象傳, 彖傳, Image, Judgment, Wilhelm, 爻辭). Plan: docs/plans/2026-05-30-001-feat-data-enrichment-plan.md
Extends Style to include "gc" (卦辭 root oracle). Hardcoded lists move
in lockstep, with one deliberate asymmetry:
- types.ts Style += "gc"
- data/trigrams.ts STYLES += "gc"; QUOTE_STYLES intentionally unchanged
- commands/hexagram.ts VALID_STYLES += "gc"
- output/plain.ts formatCastPlain + formatHexagramPlain print 卦辭 row
when hex.gc present (gated on field, legacy unchanged)
- output/json.ts castToJson + hexagramToJson conditionally include gc
via spread-when-defined
QUOTE_STYLES stays at 5 entries (dx/tu/en/te/w) — 卦辭 is the root
oracle, not a random-quotable commentary lineage, so the random-quote
selector never picks it. CLI's VALID_STYLES does include "gc" for
explicit --style gc lookup.
format/reading.ts:31 (g[style] dynamic access) and output/plain.ts:96
gain ?? "" fallback so optional Style keys can't push undefined.
commands/hexagram.ts errors cleanly when --style gc requested before U8
backfills (rather than emit an empty line).
Tests (5): STYLES parity (7 keys), QUOTE_STYLES asymmetry (5 keys, no
gc/no st), VALID_STYLES parity (6 keys = STYLES minus st), conditional
gc emission in hexagramToJson (absent when undefined, present when
populated).
Plan: docs/plans/2026-05-30-001-feat-data-enrichment-plan.md
Workflow pulled all 64 hexagrams' 卦辭 (root oracle) + 6 小象傳 entries (per-line Small Image commentary) from ctext.org with zh.wikisource cross-check at hex 1/30/32/64. Adversarial verification passed completeness + source-fidelity; structure lens caught one real blocker — JSON used 无 (77 occurrences) while gua.ts uses 無 — fixed by targeted normalization (无 → 無) with the canonical hex-25 name 无妄 explicitly protected. 74 swaps applied; remaining 无 occurrences are all in 无妄 (verified at script time). Backfill into packages/core/src/data/gua.ts: every entry gains optional `gc` (root judgment text) and `yaoXiao[6]` (per-line commentary) fields, inserted after `yaoEn` and before the closing `}`. 1797 → 2373 lines (+576). All existing fields verbatim — typecheck green + 570/570 tests pass. Tests (10): every hex has non-empty gc; every hex has yaoXiao with 6 non-empty entries; hex 1 anchors (元亨/利貞, 潛龍勿用陽在下也); hex 3 anchors (雖磐桓/志行正也); hex 25 name preserved as 无妄; encoding-correctness scan asserts no stray 无 outside 无妄 across all 64 × (gc + 6 yaoXiao) text fields; hex 1/64 legacy fields unchanged. U6's two "gated-section absent" tests flipped to assert the now- shipped state (卦辭 header + 雖磐桓 line render for hex 1/3 respectively). U6's gating LOGIC (`if (gua.gc)` branches in the renderer) is unchanged. Plan: docs/plans/2026-05-30-001-feat-data-enrichment-plan.md
data/legge.ts: standalone Wings translations from Legge SBE XVI (1882,
PD). Three exports:
- LEGGE_XUGUA_EN: 64 keyed entries (parallels XU_GUA by hex number)
- LEGGE_ZAGUA_EN: Legge's 46 rhymed couplets (kept separate from
ZA_GUA's 53-entry ctext source order — couplet structure differs)
- LEGGE_SHUOGUA_EN: Legge's 22-paragraph Appendix V (Legge's own
numbering; SHUOGUA.chapters uses the 11-chapter 王弼/孔穎達 division)
- LEGGE_META: source + license
Backfill into the zh data modules:
- GUA[i].legge: { leggeName, judgment, image, lines[6 or 7] } per hex
- GUA[i].gcEn: English translation of 卦辭 (Legge's judgment line)
- XU_GUA[i].textEn: per-entry English (sourced from LEGGE_XUGUA_EN)
- ZA_GUA / SHUOGUA backfill skipped — the 46↔53 and 22↔11 mismatches
need editorial mapping work outside this unit's scope
gua.ts: 2373 → 3271 lines (+898 from the 64 Legge embeds).
detail-renderer.ts: Legge voice activated as a dim sibling row under
the existing Image and Judgment sections, and under each yao English
translation. The U6 stacked-voice scaffolding for 卦辭 picks up the
newly-populated gcEn automatically.
Tests (17): all 64 hexagrams have populated .legge + .gcEn; hex 1 + 2
have 7-entry lines arrays (用九 / 用六 included); hex 3..64 have
exactly 6 entries; no Legge line starts with the rejected baharna
"In the Nth NINE/SIX" form; XU_GUA's 64 entries all have non-empty
textEn with zero residue of the U9-fixed artifacts (letter-fused,
paren-suffix, space-separated digits, "axe subjected"); LEGGE_ZAGUA_EN
has 46 entries opening with Khien/Khwan and ending free of nav chrome;
LEGGE_SHUOGUA_EN has 22 paragraphs.
Plan: docs/plans/2026-05-30-001-feat-data-enrichment-plan.md
docs/data-layers.md documents the canonical/editorial split, the file-per-corpus convention, the optional-field discipline on Hexagram and TrigramInfo, the stacked-voice rendering composition, and the orbit-integrity invariant as the canonical sanity check for the 雜卦 data. The orbit-integrity TEST (the geometric probe that mirror(h)/polarity fallback matches the canonical pair for every non-tail hexagram) landed in U5 inside connections.test.ts and is referenced from the doc rather than duplicated as its own test file. The disordered tail (27, 28, 43, 44, 53, 54, 63, 64) is whitelisted per recognized textual feature; the test fails on any non-tail mismatch. This closes the data-enrichment plan (docs/plans/2026-05-30-001-feat-data-enrichment-plan.md). All 11 units shipped: U1 types, U2 序卦/雜卦, U3 說卦, U4 trigram enrichment, U5 connections overlay, U6 detail-renderer + scaffolding, U7 Style union lockstep, U8 卦辭/小象 backfill, U9 Legge cleanup, U10 Legge module + voice activation, U11 docs.
Three renames from the /gate /naming lens: 1. SHUOGUA → SHUO_GUA (and SHUOGUA_META → SHUO_GUA_META). Sibling consistency with XU_GUA / ZA_GUA — all three Ten Wings now split on the pinyin word boundary uniformly. 2. TRIGRAM_ASSOC_EN → TRIGRAM_ASSOC_GLOSS_EN. The "_GLOSS_EN" token telegraphs the project-authored editorial role at the identifier level rather than relying solely on the file-head comment. TrigramAssocEn interface renamed to TrigramAssocGloss to match. 3. connections (the function in derivation/connections.ts) → buildConnections. The bare noun-form read as "fetch existing connections" at call sites; the verb-form makes the construction intent visible. The import alias in detail.ts is dropped. Touches: types.ts (1 doc comment), index.ts (3 re-exports), data/shuogua.ts + trigrams.ts + legge.ts (declarations + 1 doc comment), derivation/connections.ts (function decl), detail.ts (alias dropped), 3 test files (call sites + describe labels), detail-renderer.ts (1 comment), docs/data-layers.md (3 references). docs/plans/2026-05-30-001-... left as-is per build-skill discipline (do not edit the plan body during execution). Typecheck green, 587/587 tests pass. Pure rename — no behavior change.
Addresses the "enrichment outran wiring" cluster surfaced by the multi-persona review. Five of seven findings shared the same shape — data declared/populated but never reached its consumer — so the fix is one coherent wiring pass plus a coverage-invariant test. P1 — Dead 雜卦 English path data/legge.ts adds LEGGE_ZAGUA_BY_HEX, a hex→Legge-couplet reverse index built from LEGGE_ZAGUA_EN. derivation/connections.ts joins it onto zaGuaPair.textEn via shallow-spread so the existing renderer branch (detail-renderer.ts:258) finally has English to surface. Coverage: 62 of 64 hexes get textEn; hexes 39 and 49 stay zh-only due to documented Legge typography anomalies, pinned by the coverage test. P2 — Legge 7th line silently dropped on hex 1/2 detail-renderer.ts adds a 用九/用六 section after the 6-line yao loop. Only fires when legge.lines.length > 6 (hex 1 & 2 only). P2 — Asymmetric word-wrapping in the Relations block Both 序卦 and 雜卦 zh rows now go through wordWrap matching the English siblings; long entries (hex 30/31 cosmological preamble) no longer clip at terminal width. P3 — yaoXiaoEn dead branch removed Legge does not translate per-line 小象 (his Appendix II isn't in our pull). The dead `if (gua.yaoXiaoEn ...)` branch is gone; the coverage test pins "yaoXiaoEn = 0/64 populated" as a deliberate gap so a future partial backfill is caught. P3 — QuoteStyle precision drift types.ts: QuoteStyle = Exclude<Style, "st" | "gc"> (was just "st", letting gc leak in as a quotable style). trigrams.ts: QUOTE_STYLES retyped Style[] → QuoteStyle[]. format/reading.ts: drop the now- unnecessary `as QuoteStyle` cast. P3 — Hex 1 xuGua doc misleading types.ts CastConnections.xuGua comment was "Absent for hex 1"; in fact hex 1 and hex 2 share the canonical opening cosmological preamble. Comment corrected. Design Q — gcEn duplicates legge.judgment Both fields carry the same text. Rather than derive one from the other (which would change the data layer), the coverage test pins their equality across all 64 hexagrams — silent divergence is caught. Design Q — Legge Judgment rendered twice The Judgment section (gua.te = English of 彖傳, commentary on 卦辭) was emitting gua.legge.judgment as a Legge row, but legge.judgment IS the 卦辭 translation, already shown in the 卦辭 section. Removed from the Judgment section so each Legge sentence renders exactly once. Deferred for data-owner adjudication The xugua textEn segmentation-alignment finding (zh hex-3 introduces 屯; en hex-3 describes the post-屯 elaboration). The reviewer flagged this needs domain judgment — surfaced in the response, not auto-fixed. NEW: __tests__/data-coverage.test.ts 14 tests that catch the entire "enrichment outran wiring" pattern in one place. For every optional Hexagram/Wings field, asserts EITHER "populated for all 64" OR "documented gap, this is the exception." Plus consumer-completeness checks against buildConnections() so any future field added to the type but not wired to a consumer fails here. Typecheck green, 601/601 tests pass (+14 from data-coverage).
Applies F1 + F2 from the /refactor lens findings.
F1 — detail-renderer.ts: extract pushWrapped + 8 per-section helpers
buildContentLines went from a 270-line monolith coordinating 8
concerns to a 25-line orchestrator that reads as a table of contents:
renderGlyphPlaceholder
renderHeader
renderLineDiagram
renderTrigramCatalogue
[separator]
renderCanonicalOracle ← 卦辭
renderCommentarySections ← 大象/彖/Image/Judgment/Wilhelm
renderYaoBlock ← 爻辭 + en + legge + 小象 + 用九/用六
renderRelationsBlock ← derivations + 序卦 + 雜卦
renderFooterTrails ← locked pair + history
pushWrapped(lines, textWidth, text, style) collapses the
wordWrap+for+push pattern that appeared at ~14 sites — 3 lines → 1
each. Pure mechanical extraction; behavior unchanged. The U6
renderStackedVoices helper stays as-is (right shape for 卦辭's zh +
multi-en stack); per-section Legge sibling rows stay on the Section
type (different problem from voice stacking — single optional sibling
vs full voice stack).
Dropped two unused imports along the way (stringWidth, DerivedLink).
F2 — scripts/data-acquisition/: commit the data-import tooling
The 5 Python generator/backfill scripts that produced the data layer
(originally lived in /tmp, discarded after each run) are now
permanent in scripts/data-acquisition/. The recipe IS the tooling
— read one to understand the pattern, copy + adapt for the next
data layer (文言, 繫辭, oracle bones, etc.).
README.md — the recipe + how to use these
lib.py — ts_str, ts_str_arr helpers
gen-xugua-zagua.py — U2 (zh canonical)
gen-shuogua.py — U3 (zh canonical)
backfill-guaci-xiaoxiang.py — U8 (with 无→無 normalization,
preserving 无妄 hex-25 name)
cleanup-legge.py — U9 (post-pull structural fixes,
idempotent in-place)
backfill-legge.py — U10 (generate legge.ts +
backfill gua.ts + xugua.ts)
Scripts read from data-acquisition/ (gitignored — produced by the
pull workflows documented in docs/plans/) and write to
packages/core/src/data/ (committed). Each script is self-contained,
documented, idempotent where possible, and uses repo-relative paths
via lib.py.
Also anchors the data-acquisition/ gitignore rule to root (/data-
acquisition/) so it no longer accidentally swallows
scripts/data-acquisition/.
Typecheck green, 601/601 tests pass — pure structure refactor + new
tooling, no behavior change.
Two review findings:
P2 — LEGGE_ZAGUA_BY_HEX overwrites valid mappings (legge.ts:157)
The naive `for (const hex of entry.pair) idx[hex] = entry.text` let the
two documented Legge typography anomalies clobber legitimate
neighbouring couplets. After this fix all 64 hexes get a couplet, and
hex 41 / hex 51 retain their legitimate text.
Inspection of the actual entry text settled the routing:
pair=[41] "Hard toil and danger have in Kien their home."
→ single-hex content; route to hex 39 only.
pair=[50,51] "Ting takes what's new; the old is left by Ko."
→ couplet content for Ko + Ting; route to BOTH hex
49 AND hex 50. (Hex 50 had no legitimate Legge couplet
of its own; the anomaly is the only source.)
The reroute is expressed as `Record<string, number[]>` so the
single-hex and couplet cases share one shape: `targets = reroute ??
entry.pair`. Iteration order no longer matters because the anomalous
entries' writes never overlap with the legitimate entries' targets.
Backfill script (backfill-legge.py) mirrors the fix so future
regenerations produce the same legge.ts.
data-coverage.test.ts updated: closes the documented gap (was 62/64
with hex 39+49 as exceptions; now 64/64). Adds two adversarial
assertions — hex 39/49 carry their rerouted Kien/Ko text, AND hex
41/51 do NOT (proving the legitimate couplets are preserved).
P3 — cleanup-legge.py metadata not idempotent (cleanup-legge.py:72-77)
The unconditional `extend(...)` of `postPullFixes` appended duplicate
entries on every re-run, even when zero fixes were applied. Now gated
on `fixes_applied` (the in-script flag list that ALREADY tracks
whether each fix ran). Also dedupes against any pre-existing entries
in `_meta.postPullFixes` so two runs that both applied the same fix
don't create two identical records.
Typecheck green, 602/602 tests pass.
Add project-authored modern translations and derivation context for every 說卦 chapter, preserve the selected derivation op through navigation, and render chapter pages with canonical text, working translation, relevance notes, and trigram association tables. Covers the reader path with core data, terminal scene, detail navigation, and CLI factory tests.
Show the selected derived hexagram's own 卦辭 and Legge judgment before the 說卦 source link, keeping source citations secondary. Remove the project-authored derivation relevance layer from the 說卦 reader so chapter pages remain source-and-translation oriented.
This reverts commit b25f882.
Persist a display language preference with canonical values for simplified Chinese, traditional Chinese, and English. Expose the setting through the CLI config command and the terminal settings scene while keeping current text rendering unchanged.
5 tasks
pro-vi
added a commit
that referenced
this pull request
Jun 20, 2026
…k gap (review #4) Review finding #4 (P2). The dangling-paste flush re-armed a 500ms timer on every feed() (and feed() cleared it on entry), so PASTE_TIMEOUT_MS measured the gap BETWEEN chunks, not the paste's age. A real paste split by a >500ms stall (a backgrounded process, a slow pipe) flushed a PARTIAL paste, left paste mode, and the unterminated tail re-entered normal parsing — a trailing \r became a real Enter and submitted the form mid-paste. Make the flush an absolute age backstop: a separate pasteTimer armed ONCE at the ESC[200~ start, never re-armed, and never cleared by feed() — so it bounds the paste's total age (PASTE_FLUSH_MS, 10s), not the inter-chunk gap. A genuine paste terminates via ESC[201~ or the 64KB byte cap long before it fires (terminals deliver a paste to the pty in ms), so it never truncates a real paste; it only recovers a never-terminated paste-start so the parser can't wedge (the byte cap alone could otherwise swallow Ctrl-C). The timeout is injectable via a constructor option (default 10s) purely so tests exercise the backstop without a 10s wait. Tests: a >500ms mid-paste stall stays one paste with the CR as content, never a stray Enter (mutation-verified); the backstop fires for a truly abandoned paste; a no-content dangling start emits nothing. The two prior tests that pinned the old 500ms per-chunk timing are rewritten for the absolute-age contract. Gates: 1338 tests pass, typecheck clean, language-surface PASS, smoke 5/5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the committed data-enrichment layer for Wings, trigram associations, Legge translations, and renderer/CLI surfaces that consume those fields. Local JSON acquisition inputs and the JSON -> TS acquisition tooling are ignored; the PR ships the generated TypeScript data as the reviewable source.
This branch now also adds the first language-preference scaffold: a persisted display language setting for simplified Chinese, traditional Chinese, and English. It exposes the control in CLI config and terminal settings, while deliberately leaving text rendering unchanged until we decide which surfaces should spend real estate on translated variants.
Key Changes
Data and Types
GUAwith 卦辭 English, 小象 text, and Legge judgment/image/line data.DisplayLanguagetype with canonical valueszh-Hans,zh-Hant, anden.Connections and Rendering
buildConnectionscoverage for sequence, contrast-pair, and 說卦 citation overlays.gccommentary style across CLI/style parity paths.說卦傳 ch.Nreader: focus derived rows withtab, then presssfor the chapter orenterfor the related hexagram.說卦chapter reader with canonical text, project-authored working translation, derivation relevance, and trigram association tables for catalogue chapters.Configuration
zh-Hantto preserve current behavior.iching config get/set/list languagewith canonical valueszh-Hans,zh-Hant, anden.简,繁, andEN.UserConfig.Documentation and Repo Hygiene
data-acquisition/andscripts/data-acquisition/ignored because they depend on local verified JSON pull artifacts.Review Fixes
62/64gap now that Legge 雜卦 coverage is64/64.QA Checklist
bun test --path-ignore-patterns='**/dist/**'->623 pass,0 fail,225630 expect()calls.bun run typecheckexits0.bun test packages/storage/src/__tests__/config-store.test.ts packages/storage/src/__tests__/schema-shape.test.ts apps/cli/src/__tests__/config-command.test.ts packages/terminal/src/__tests__/settings-scene.test.ts->30 pass,0 fail,107 expect()calls.bun test packages/core/src/__tests__/shuogua.test.ts packages/terminal/src/__tests__/detail-renderer.test.ts packages/terminal/src/__tests__/detail-scene.test.ts packages/terminal/src/__tests__/shuogua-chapter-scene.test.ts apps/cli/src/__tests__/scene-factories.test.ts->57 pass,0 fail.Kan starts; Kan stops.bun run bundleemitspublish/iching.jsat ~1.40 MB, andrginspection finds no acquisition-script markers.Acceptance Evidence
Acceptance was rerun against this branch after reverting
b25f882; the branch has since added the language-setting scaffold. The latest head commit isb2cdadb; the active rich-reader implementation commit isf751b90.bun test packages/core/src/__tests__/data-coverage.test.ts-> coverage includes Legge anomaly reroute and consumer-completeness assertionsbun test --path-ignore-patterns='**/dist/**'->623 pass,0 fail,225630 expect()callsbun run typecheckexited0zh-Hant, CLIconfig get/set language, invalid-value rejection, alias normalization, and Settings row choices简,繁,ENsemitsopenShuoguaChapter, routers constructShuoGuaChapterScene, and the selected derivation op is preserved39 -> Kien,41 -> Sun and Yi,49/50 -> Ting ... Ko,51 -> Kan starts; Kan stops.bun run bundlesucceeded at ~1.40 MB; `rg -n "data-acquisitionNo browser DOM visual ACs were present for this terminal/data/core change, so no screenshot evidence was required.
Notes
No Linear ticket detected for this branch.