ucode 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -0
  3. data/Gemfile.lock +2 -2
  4. data/TODO.full/00-README.md +116 -0
  5. data/TODO.full/01-panglyph-vision.md +112 -0
  6. data/TODO.full/02-panglyph-repo-bootstrap.md +184 -0
  7. data/TODO.full/03-panglyph-font-builder.md +201 -0
  8. data/TODO.full/04-panglyph-publish-pipeline.md +126 -0
  9. data/TODO.full/05-ucode-0-1-1-release.md +139 -0
  10. data/TODO.full/06-fontisan-remove-audit.md +142 -0
  11. data/TODO.full/07-fontisan-remove-ucd.md +125 -0
  12. data/TODO.full/08-archive-private-bin-build.md +143 -0
  13. data/TODO.full/09-archive-public-structure.md +164 -0
  14. data/TODO.full/10-fontist-org-woff-glyphs.md +131 -0
  15. data/TODO.full/11-fontist-org-audit-coverage.md +140 -0
  16. data/TODO.full/12-implementation-order.md +216 -0
  17. data/TODO.full/13-fontisan-font-writer-api.md +189 -0
  18. data/TODO.full/14-fontisan-table-writers.md +66 -0
  19. data/TODO.full/15-panglyph-builder-real.md +82 -0
  20. data/TODO.full/16-archive-public-sync-workflows.md +167 -0
  21. data/TODO.full/17-fontist-org-font-picker.md +73 -0
  22. data/TODO.full/18-comprehensive-spec-coverage.md +64 -0
  23. data/TODO.full/19-ucode-0-1-2-patch.md +32 -0
  24. data/TODO.full/20-fontisan-0-2-23-release.md +52 -0
  25. data/TODO.new/00-README.md +30 -0
  26. data/TODO.new/23-universal-glyph-set-source-map.md +312 -0
  27. data/TODO.new/24-universal-glyph-set-build.md +189 -0
  28. data/TODO.new/25-font-audit-against-universal-set.md +195 -0
  29. data/TODO.new/26-missing-glyph-reporter.md +189 -0
  30. data/TODO.new/27-fontist-org-consumer-integration.md +200 -0
  31. data/TODO.new/28-implementation-order-update.md +187 -0
  32. data/TODO.new/29-universal-set-curation-uc17.md +312 -0
  33. data/TODO.new/30-tier1-font-acquisition.md +241 -0
  34. data/TODO.new/31-universal-set-production-build.md +205 -0
  35. data/TODO.new/32-uc17-coverage-matrix.md +165 -0
  36. data/TODO.new/33-specialist-font-acquisition-refresh.md +138 -0
  37. data/TODO.new/34-pillar2-content-stream-correlator.md +147 -0
  38. data/TODO.new/35-universal-set-production-run.md +160 -0
  39. data/TODO.new/36-per-font-coverage-audit.md +145 -0
  40. data/TODO.new/37-coverage-highlight-reporter.md +125 -0
  41. data/TODO.new/38-fontist-org-glyph-consumer.md +141 -0
  42. data/TODO.new/39-implementation-order-update-32-38.md +258 -0
  43. data/TODO.new/40-archive-private-uses-ucode-audit.md +124 -0
  44. data/TODO.new/41-ucode-unicode-archive-bridge.md +160 -0
  45. data/config/specialist_fonts.yml +102 -0
  46. data/config/unicode17_tier1_fonts.yml +42 -0
  47. data/config/unicode17_universal_glyph_set.yml +293 -0
  48. data/lib/ucode/audit/block_aggregator.rb +57 -29
  49. data/lib/ucode/audit/browser/face_page.rb +128 -0
  50. data/lib/ucode/audit/browser/glyph_panel.rb +124 -0
  51. data/lib/ucode/audit/browser/library_page.rb +74 -0
  52. data/lib/ucode/audit/browser/missing_glyph_page.rb +87 -0
  53. data/lib/ucode/audit/browser/template.rb +47 -0
  54. data/lib/ucode/audit/browser/templates/face.css +200 -0
  55. data/lib/ucode/audit/browser/templates/face.html.erb +41 -0
  56. data/lib/ucode/audit/browser/templates/face.js +298 -0
  57. data/lib/ucode/audit/browser/templates/library.css +119 -0
  58. data/lib/ucode/audit/browser/templates/library.html.erb +42 -0
  59. data/lib/ucode/audit/browser/templates/library.js +99 -0
  60. data/lib/ucode/audit/browser/templates/missing_glyph_page.css +119 -0
  61. data/lib/ucode/audit/browser/templates/missing_glyph_page.html.erb +58 -0
  62. data/lib/ucode/audit/browser/templates/missing_glyph_page.js +2 -0
  63. data/lib/ucode/audit/browser.rb +32 -0
  64. data/lib/ucode/audit/context.rb +27 -1
  65. data/lib/ucode/audit/coverage_reference.rb +103 -0
  66. data/lib/ucode/audit/differ.rb +121 -0
  67. data/lib/ucode/audit/emitter/block_emitter.rb +52 -0
  68. data/lib/ucode/audit/emitter/codepoint_emitter.rb +87 -0
  69. data/lib/ucode/audit/emitter/collection_emitter.rb +80 -0
  70. data/lib/ucode/audit/emitter/face_directory.rb +212 -0
  71. data/lib/ucode/audit/emitter/glyph_emitter.rb +48 -0
  72. data/lib/ucode/audit/emitter/index_emitter.rb +149 -0
  73. data/lib/ucode/audit/emitter/library_emitter.rb +96 -0
  74. data/lib/ucode/audit/emitter/paths.rb +312 -0
  75. data/lib/ucode/audit/emitter/plane_emitter.rb +29 -0
  76. data/lib/ucode/audit/emitter/script_emitter.rb +29 -0
  77. data/lib/ucode/audit/emitter.rb +29 -0
  78. data/lib/ucode/audit/extractors/aggregations.rb +31 -2
  79. data/lib/ucode/audit/face_auditor.rb +86 -0
  80. data/lib/ucode/audit/formatters/audit_diff_text.rb +112 -0
  81. data/lib/ucode/audit/formatters/audit_text.rb +411 -0
  82. data/lib/ucode/audit/formatters/color.rb +48 -0
  83. data/lib/ucode/audit/formatters/library_summary_text.rb +98 -0
  84. data/lib/ucode/audit/formatters/text_formatter.rb +83 -0
  85. data/lib/ucode/audit/formatters.rb +23 -0
  86. data/lib/ucode/audit/library_aggregator.rb +86 -0
  87. data/lib/ucode/audit/library_auditor.rb +105 -0
  88. data/lib/ucode/audit/release/emitter.rb +152 -0
  89. data/lib/ucode/audit/release/face_card.rb +93 -0
  90. data/lib/ucode/audit/release/formula_audits.rb +50 -0
  91. data/lib/ucode/audit/release/library_index_builder.rb +78 -0
  92. data/lib/ucode/audit/release/manifest_builder.rb +127 -0
  93. data/lib/ucode/audit/release.rb +42 -0
  94. data/lib/ucode/audit/ucd_only_reference.rb +81 -0
  95. data/lib/ucode/audit/universal_set_reference.rb +136 -0
  96. data/lib/ucode/audit.rb +31 -0
  97. data/lib/ucode/cli.rb +339 -33
  98. data/lib/ucode/commands/audit/browser_command.rb +82 -0
  99. data/lib/ucode/commands/audit/collection_command.rb +103 -0
  100. data/lib/ucode/commands/audit/compare_command.rb +188 -0
  101. data/lib/ucode/commands/audit/font_command.rb +140 -0
  102. data/lib/ucode/commands/audit/library_command.rb +87 -0
  103. data/lib/ucode/commands/audit/reference_builder.rb +64 -0
  104. data/lib/ucode/commands/audit.rb +20 -0
  105. data/lib/ucode/commands/block_feed.rb +73 -0
  106. data/lib/ucode/commands/canonical_build.rb +138 -0
  107. data/lib/ucode/commands/fetch.rb +37 -1
  108. data/lib/ucode/commands/release.rb +115 -0
  109. data/lib/ucode/commands/universal_set.rb +211 -0
  110. data/lib/ucode/commands.rb +5 -0
  111. data/lib/ucode/coordinator/indices.rb +11 -0
  112. data/lib/ucode/coordinator.rb +138 -5
  113. data/lib/ucode/error.rb +30 -2
  114. data/lib/ucode/fetch/font_fetcher/result.rb +39 -0
  115. data/lib/ucode/fetch/font_fetcher.rb +16 -0
  116. data/lib/ucode/fetch/specialist_font_fetcher.rb +280 -0
  117. data/lib/ucode/fetch.rb +7 -3
  118. data/lib/ucode/glyphs/real_fonts/cmap_cache.rb +74 -0
  119. data/lib/ucode/glyphs/real_fonts.rb +1 -0
  120. data/lib/ucode/glyphs/resolver.rb +62 -0
  121. data/lib/ucode/glyphs/source.rb +48 -0
  122. data/lib/ucode/glyphs/source_builder.rb +61 -0
  123. data/lib/ucode/glyphs/source_config/coverage_assertion.rb +79 -0
  124. data/lib/ucode/glyphs/source_config/gap_report.rb +54 -0
  125. data/lib/ucode/glyphs/source_config.rb +104 -0
  126. data/lib/ucode/glyphs/sources/pillar1_embedded_tounicode.rb +63 -0
  127. data/lib/ucode/glyphs/sources/pillar3_last_resort.rb +51 -0
  128. data/lib/ucode/glyphs/sources/tier1_real_font.rb +104 -0
  129. data/lib/ucode/glyphs/sources.rb +20 -0
  130. data/lib/ucode/glyphs/universal_set/builder.rb +161 -0
  131. data/lib/ucode/glyphs/universal_set/coverage_report.rb +139 -0
  132. data/lib/ucode/glyphs/universal_set/idempotency.rb +86 -0
  133. data/lib/ucode/glyphs/universal_set/manifest_accumulator.rb +195 -0
  134. data/lib/ucode/glyphs/universal_set/manifest_writer.rb +61 -0
  135. data/lib/ucode/glyphs/universal_set/pre_build_check.rb +197 -0
  136. data/lib/ucode/glyphs/universal_set/validator.rb +204 -0
  137. data/lib/ucode/glyphs/universal_set.rb +45 -0
  138. data/lib/ucode/glyphs.rb +6 -0
  139. data/lib/ucode/models/audit/baseline.rb +6 -0
  140. data/lib/ucode/models/audit/block_summary.rb +7 -0
  141. data/lib/ucode/models/audit/codepoint_provenance.rb +39 -0
  142. data/lib/ucode/models/audit/release_face.rb +42 -0
  143. data/lib/ucode/models/audit/release_formula.rb +33 -0
  144. data/lib/ucode/models/audit/release_manifest.rb +43 -0
  145. data/lib/ucode/models/audit/release_universal_set.rb +37 -0
  146. data/lib/ucode/models/audit.rb +9 -0
  147. data/lib/ucode/models/block.rb +2 -0
  148. data/lib/ucode/models/build_report.rb +109 -0
  149. data/lib/ucode/models/codepoint/glyph.rb +42 -0
  150. data/lib/ucode/models/codepoint.rb +3 -0
  151. data/lib/ucode/models/glyph_source.rb +86 -0
  152. data/lib/ucode/models/glyph_source_map.rb +138 -0
  153. data/lib/ucode/models/specialist_font.rb +70 -0
  154. data/lib/ucode/models/specialist_font_manifest.rb +48 -0
  155. data/lib/ucode/models/unihan_entry.rb +81 -9
  156. data/lib/ucode/models/unihan_field.rb +21 -0
  157. data/lib/ucode/models/universal_set_entry.rb +47 -0
  158. data/lib/ucode/models/universal_set_manifest.rb +78 -0
  159. data/lib/ucode/models/validation_report.rb +99 -0
  160. data/lib/ucode/models.rb +9 -0
  161. data/lib/ucode/parsers/named_sequences.rb +5 -5
  162. data/lib/ucode/parsers/unihan.rb +50 -19
  163. data/lib/ucode/repo/aggregate_writer.rb +34 -2
  164. data/lib/ucode/repo/block_feed_emitter.rb +153 -0
  165. data/lib/ucode/repo/build_report_accumulator.rb +138 -0
  166. data/lib/ucode/repo/build_report_writer.rb +46 -0
  167. data/lib/ucode/repo/build_validator.rb +229 -0
  168. data/lib/ucode/repo/codepoint_writer.rb +50 -1
  169. data/lib/ucode/repo/paths.rb +8 -0
  170. data/lib/ucode/repo.rb +4 -0
  171. data/lib/ucode/version.rb +1 -1
  172. data/schema/block-feed.output.schema.yml +134 -0
  173. metadata +143 -2
  174. data/ucode.gemspec +0 -56
@@ -0,0 +1,167 @@
1
+ # 16 — archive-public: three sync workflows (GHA)
2
+
3
+ ## Goal
4
+
5
+ Add the three GitHub Actions workflows that sync `fontist-archive-public`
6
+ from its three upstream sources:
7
+
8
+ 1. `sync-private.yml` — pulls from `fontist-archive-private` on push
9
+ 2. `sync-ucode.yml` — pulls from `fontist/ucode` publish workflow
10
+ 3. `sync-panglyph.yml` — pulls from `fontist/panglyph` tag pushes
11
+
12
+ Each writes to a disjoint directory (`coverage/`, `unicode/`, `panglyph/`),
13
+ so concurrent runs don't conflict.
14
+
15
+ ## Why this is a separate TODO
16
+
17
+ The directory placeholders exist (committed in the
18
+ `audit/add-unicode-and-panglyph-dirs` branch). The sync workflows are
19
+ the actual machinery. Without them, the directories stay empty.
20
+
21
+ ## Scope
22
+
23
+ ### sync-private.yml
24
+
25
+ ```yaml
26
+ name: Sync from fontist-archive-private
27
+
28
+ on:
29
+ repository_dispatch:
30
+ types: [archive-private-updated]
31
+ workflow_dispatch:
32
+
33
+ jobs:
34
+ sync:
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - name: Clone private (read-only deploy key)
39
+ env:
40
+ SSH_KEY: ${{ secrets.ARCHIVE_PRIVATE_DEPLOY_KEY }}
41
+ run: |
42
+ mkdir -p ~/.ssh && echo "$SSH_KEY" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
43
+ git clone --depth 1 git@github.com:fontist/fontist-archive-private.git /tmp/private
44
+ - name: Sync coverage/
45
+ run: |
46
+ rm -rf coverage
47
+ cp -r /tmp/private/coverage .
48
+ - name: Sync woff/ (open-license only)
49
+ run: |
50
+ rm -rf woff
51
+ mkdir -p woff
52
+ for d in /tmp/private/woff/*; do
53
+ base=$(basename "$d")
54
+ [ "$base" != "macos" ] && cp -r "$d" "woff/"
55
+ done
56
+ - name: Sync fonts.json + font-metadata.json
57
+ run: cp /tmp/private/fonts.json /tmp/private/font-metadata.json . || true
58
+ - name: Commit
59
+ run: |
60
+ git config user.email "archive-bot@fontist.org"
61
+ git config user.name "archive-bot"
62
+ git add -A
63
+ git diff --quiet HEAD || git commit -m "Sync from fontist-archive-private @ $(date -u +%Y-%m-%dT%H:%M:%SZ)"
64
+ git push
65
+ ```
66
+
67
+ ### sync-ucode.yml
68
+
69
+ ```yaml
70
+ name: Sync from fontist/ucode
71
+
72
+ on:
73
+ workflow_run:
74
+ workflows: [publish-unicode-archive]
75
+ types: [completed]
76
+ branches: [main]
77
+ workflow_dispatch:
78
+
79
+ jobs:
80
+ sync:
81
+ runs-on: ubuntu-latest
82
+ if: github.event.workflow_run.conclusion == 'success'
83
+ steps:
84
+ - uses: actions/checkout@v4
85
+ - name: Download ucode artifacts
86
+ uses: actions/github-script@v7
87
+ with:
88
+ script: |
89
+ const runId = context.payload.workflow_run.id
90
+ const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
91
+ owner: 'fontist', repo: 'ucode', run_id: runId
92
+ })
93
+ // download unicode-block-feed.tar.gz + universal-glyph-set.tar.gz
94
+ // ...
95
+ - name: Sync unicode/
96
+ run: |
97
+ rm -rf unicode/block-feed unicode/universal-glyph-set
98
+ mkdir -p unicode/block-feed unicode/universal-glyph-set
99
+ tar xzf /tmp/block-feed.tar.gz -C unicode/block-feed
100
+ tar xzf /tmp/universal-glyph-set.tar.gz -C unicode/universal-glyph-set
101
+ - name: Commit
102
+ run: |
103
+ git config user.email "archive-bot@fontist.org"
104
+ git config user.name "archive-bot"
105
+ git add -A unicode/
106
+ git diff --quiet HEAD -- unicode/ || git commit -m "Sync unicode/ from fontist/ucode"
107
+ git push
108
+ ```
109
+
110
+ ### sync-panglyph.yml
111
+
112
+ ```yaml
113
+ name: Sync from fontist/panglyph
114
+
115
+ on:
116
+ repository_dispatch:
117
+ types: [panglyph-released]
118
+ workflow_dispatch:
119
+ inputs:
120
+ version:
121
+ description: 'panglyph version (e.g. 17.0.0)'
122
+ required: true
123
+
124
+ jobs:
125
+ sync:
126
+ runs-on: ubuntu-latest
127
+ steps:
128
+ - uses: actions/checkout@v4
129
+ - name: Download panglyph release
130
+ env:
131
+ GH_TOKEN: ${{ secrets.ARCHIVE_PUBLIC_BOT_TOKEN }}
132
+ run: |
133
+ VERSION="${{ inputs.version || github.event.client_payload.version }}"
134
+ gh release download "v${VERSION}" \
135
+ --repo fontist/panglyph \
136
+ --pattern "panglyph-unicode*.{ttf,woff2,otf}" \
137
+ --pattern "coverage-report.json" \
138
+ --pattern "source-manifest.json" \
139
+ --dir "/tmp/v${VERSION}"
140
+ - name: Sync panglyph/
141
+ run: |
142
+ mkdir -p "panglyph/v${VERSION}"
143
+ cp /tmp/v${VERSION}/* "panglyph/v${VERSION}/"
144
+ # Update top-level manifest.json (script in bin/update-panglyph-manifest.rb)
145
+ ruby bin/update-panglyph-manifest.rb "${VERSION}"
146
+ - name: Commit
147
+ run: |
148
+ git config user.email "archive-bot@fontist.org"
149
+ git config user.name "archive-bot"
150
+ git add -A panglyph/
151
+ git diff --quiet HEAD -- panglyph/ || git commit -m "Sync panglyph v${VERSION}"
152
+ git push
153
+ ```
154
+
155
+ ## Acceptance
156
+
157
+ - [ ] All three workflows exist + are syntactically valid YAML
158
+ - [ ] Each writes to a disjoint directory
159
+ - [ ] Manual dispatch works (`workflow_dispatch`)
160
+ - [ ] Trigger-based dispatch works (`repository_dispatch` / `workflow_run`)
161
+ - [ ] Bot commits identify as `archive-bot@fontist.org`
162
+
163
+ ## References
164
+
165
+ - [TODO.full/09](09-archive-public-structure.md) — target structure
166
+ - [TODO.full/04](04-panglyph-publish-pipeline.md) — panglyph publish trigger
167
+ - [TODO.new/41](../TODO.new/41-ucode-unicode-archive-bridge.md) — ucode publish trigger
@@ -0,0 +1,73 @@
1
+ # 17 — fontist.org: font picker on the universal Unicode browser
2
+
3
+ ## Goal
4
+
5
+ Add a font picker to the standalone `/unicode` page so users can browse
6
+ Unicode with a specific font active. Today the universal browser uses
7
+ system fallback fonts; selecting a font from `public/fonts/*.woff2`
8
+ injects `@font-face` and renders every glyph in that font.
9
+
10
+ Pairs with the existing `/fonts/{slug}/unicode` page (FontUnicodePage),
11
+ which already does this — but only for one font at a time. The universal
12
+ browser should let users switch fonts without leaving `/unicode`.
13
+
14
+ ## Why this is a separate TODO
15
+
16
+ TODO.full/10 covered the rendering primitives (useFontFace composable,
17
+ FontPicker component, etc.). The integration work — wiring the picker
18
+ into `/unicode`, persisting selection, surfacing on the block + char
19
+ pages — is separate.
20
+
21
+ ## Scope
22
+
23
+ ### Phase A — Reuse the existing FontPicker
24
+
25
+ 1. The `FontStyleUnicodePage` already injects `@font-face` via
26
+ `injectFontFace(slug, 'fonts/${slug}.woff2', true)`. Extract this
27
+ into a global composable that any page can call.
28
+
29
+ 2. Add a `<FontPicker>` component to `/unicode` (top of page, sticky).
30
+
31
+ 3. On font selection: set a global Pinia store (or provide/inject)
32
+ so all child routes inherit the active font.
33
+
34
+ ### Phase B — Coverage overlay
35
+
36
+ 4. When a font is active on `/unicode`:
37
+ - Each block row shows: `[coverage bar: 128/128 = 100%]` next to the name
38
+ - Color-coded: green ≥95%, yellow 50-95%, red <50%
39
+ - Click → navigates to `/unicode/block/<slug>?font=<active-slug>`
40
+
41
+ 5. On `/unicode/block/<slug>`:
42
+ - If `?font=X` is set, inject X's WOFF + show coverage overlay on each cell
43
+ - Cells with chars the font covers: render in WOFF
44
+ - Cells with chars the font misses: gray out + tooltip "Not in X"
45
+
46
+ ### Phase C — Persistence
47
+
48
+ 6. Active font persists across page navigation via `localStorage`.
49
+
50
+ 7. URL parameter `?font=X` overrides localStorage. Useful for sharing
51
+ links like "view this block in Noto Sans".
52
+
53
+ ### Phase D — "Best font per block" page
54
+
55
+ 8. New route `/unicode/best-fonts/{block-slug}`: lists fonts sorted by
56
+ fill ratio for that block. Top result is the canonical Tier 1 font
57
+ from ucode's universal-set manifest.
58
+
59
+ ## Acceptance
60
+
61
+ - [ ] `/unicode` shows FontPicker at top
62
+ - [ ] Selecting a font injects `@font-face` and re-renders the page
63
+ - [ ] Block list shows coverage bars when a font is active
64
+ - [ ] Block detail page respects `?font=X`
65
+ - [ ] Active font persists in localStorage
66
+ - [ ] `/unicode/best-fonts/{block}` exists
67
+
68
+ ## References
69
+
70
+ - [TODO.full/10](10-fontist-org-woff-glyphs.md) — rendering primitives
71
+ - [TODO.full/11](11-fontist-org-audit-coverage.md) — coverage data layer
72
+ - `src/pages/FontUnicodePage.vue` — existing per-font page (pattern source)
73
+ - `src/composables/useFontFace.ts` — existing composable
@@ -0,0 +1,64 @@
1
+ # 18 — Comprehensive spec coverage for new code
2
+
3
+ ## Goal
4
+
5
+ Every class added in TODO.full/13–17 must have specs covering:
6
+ - Public API behavior
7
+ - Edge cases (empty input, malformed input, boundary values)
8
+ - Round-trip serialization (write → read = same data)
9
+ - Performance (no obvious pathological cases for 299k codepoints)
10
+
11
+ ## Why a separate TODO
12
+
13
+ Quality bar per global rules: "Good specs throughout. Every public
14
+ method should have specs. Every behavioral edge case should be covered.
15
+ Specs use real model instances — never doubles."
16
+
17
+ The new code (FontWriter, panglyph Builder, font picker integration)
18
+ touches core paths. Without specs, regressions slip silently.
19
+
20
+ ## Scope
21
+
22
+ ### fontisan FontWriter (TODO.full/13, 14)
23
+
24
+ - `FontWriter#set_cmap` — empty, single entry, full BMP, supplementary
25
+ - `FontWriter#add_glyph` — simple outline, composite outline, no instructions
26
+ - `FontWriter#set_name_records` — all 6 standard name_ids
27
+ - `FontWriter#write_to` — round-trip: write → reopen via Fontisan::Font.open
28
+ - Per-table specs:
29
+ - `Tables::Head` — byte-exact comparison with a known fixture
30
+ - `Tables::Cmap` — format 4 BMP, format 12 supplementary, both
31
+ - `Tables::Glyf` — simple glyph, composite glyph, empty glyph (.notdef)
32
+ - etc.
33
+
34
+ ### panglyph (TODO.full/15)
35
+
36
+ - `Builder#call` — full pipeline with a 5-codepoint fixture
37
+ - `OutlineExtractor#extract_many` — real font, 3 codepoints
38
+ - `FontAssembler#assemble` — output TTF opens via Fontisan::Font.open
39
+ - `CoverageReport#validate_font` — known gap → reported in missing_codepoints
40
+ - `Publisher#call` — atomic sync to temp git repo fixture
41
+
42
+ ### fontist.org (TODO.full/17)
43
+
44
+ - `FontPicker.vue` — emits selection event
45
+ - `useActiveFont` composable — persists to localStorage
46
+ - `/unicode/best-fonts/{block}` — sorts by fill_ratio desc
47
+
48
+ ### Integration specs
49
+
50
+ - End-to-end: ucode parse → universal-set build → panglyph build → archive sync → fontist.org fetch
51
+ - Smoke test: built panglyph TTF renders U+0041 in a headless browser
52
+
53
+ ## Acceptance
54
+
55
+ - [ ] Every new public class has a corresponding spec file
56
+ - [ ] Every public method has at least one happy-path + one edge-case spec
57
+ - [ ] Round-trip specs exist for any serialization path
58
+ - [ ] `bundle exec rspec` passes on all repos
59
+ - [ ] No `double()` usage (per global rule)
60
+
61
+ ## References
62
+
63
+ - Global rule: "Good specs throughout"
64
+ - `spec/support/model_round_trip.rb` — shared example for round-trip models
@@ -0,0 +1,32 @@
1
+ # 19 — ucode 0.1.2 patch: include FontWriter + universal-set fixes
2
+
3
+ ## Goal
4
+
5
+ After TODO.full/13 (FontWriter) lands in fontisan, bump ucode to 0.1.2
6
+ with whatever fixes accumulated. Keep the gem install path stable for
7
+ fontist-archive-private CI.
8
+
9
+ ## Why a separate TODO
10
+
11
+ ucode 0.1.1 (TODO.full/05) ships the audit subsystem + BlockFeedEmitter.
12
+ 0.1.2 is a routine patch — no SemVer drama, just iteration. Bundles
13
+ whatever real-data fixes surface when CI actually runs against all
14
+ formulas.
15
+
16
+ ## Scope
17
+
18
+ - Bump `lib/ucode/version.rb` to `0.1.2`
19
+ - Add CHANGELOG entry (typically: 1-2 fixes)
20
+ - Run full test suite + rubocop
21
+ - Open PR + tag + release per standard release process
22
+
23
+ ## Acceptance
24
+
25
+ - [ ] Version bumped
26
+ - [ ] CHANGELOG documents 0.1.2
27
+ - [ ] Specs green
28
+ - [ ] Tag v0.1.2 + `rake release` (with explicit user authorization)
29
+
30
+ ## References
31
+
32
+ - [TODO.full/05](05-ucode-0-1-1-release.md) — prior release process
@@ -0,0 +1,52 @@
1
+ # 20 — fontisan 0.2.23: ship FontWriter
2
+
3
+ ## Goal
4
+
5
+ Once TODO.full/13 (FontWriter API) + TODO.full/14 (table writers) are
6
+ merged to main, ship fontisan 0.2.23 with the new capability. Stays
7
+ on 0.2.x because the removal of audit + UCD was framed as dead-code
8
+ cleanup, not a SemVer event.
9
+
10
+ ## Why this is a separate TODO
11
+
12
+ panglyph's gemspec depends on `fontisan, "~> 0.3"` today (the bootstrap
13
+ commit assumed the 0.3.0 bump). After your correction (no bump), the
14
+ constraint needs to relax to `~> 0.2` to pick up FontWriter in 0.2.23.
15
+
16
+ ## Scope
17
+
18
+ 1. Verify TODO.full/13 + 14 are merged to main.
19
+ 2. Bump `lib/fontisan/version.rb` to `0.2.23`.
20
+ 3. Add CHANGELOG entry:
21
+ ```markdown
22
+ ## [0.2.23] — 2026-XX-XX
23
+
24
+ ### Added
25
+
26
+ - `Fontisan::FontWriter` — new API for assembling fonts from scratch
27
+ (cmap + glyf + name + metrics). Pairs with the existing font-reading
28
+ API. Enables downstream consumers (panglyph) to build new fonts
29
+ from extracted outlines.
30
+ - `Fontisan::FontWriter::Tables::*` — one writer class per OpenType
31
+ table (Head, Cmap, Hmtx, Hhea, Glyf, Loca, Maxp, Os2, Post, Name).
32
+ - Typed structs: `Outline`, `Point`, `NameRecord`, `Metrics`,
33
+ `GlyphEntry`, `FontModel`.
34
+ ```
35
+ 4. Update panglyph gemspec: `gem "fontisan", "~> 0.2"` (was `~> 0.3`).
36
+ 5. Run full test suite + rubocop.
37
+ 6. Open PR + tag + release.
38
+
39
+ ## Acceptance
40
+
41
+ - [ ] Version bumped to 0.2.23
42
+ - [ ] CHANGELOG entry documents FontWriter addition
43
+ - [ ] panglyph.gemspec constraint relaxed to `~> 0.2`
44
+ - [ ] `bundle exec panglyph version` works against the new fontisan
45
+ - [ ] Tag v0.2.23 + `rake release` (with explicit user authorization)
46
+
47
+ ## References
48
+
49
+ - [TODO.full/06](06-fontisan-remove-audit.md) — why we stayed on 0.2.x
50
+ - [TODO.full/07](07-fontisan-remove-ucd.md) — same reasoning
51
+ - [TODO.full/13](13-fontisan-font-writer-api.md) — what 0.2.23 ships
52
+ - [TODO.full/14](14-fontisan-table-writers.md) — companion
@@ -46,9 +46,39 @@ first; these TODOs reference sections of it.
46
46
  - [20 — Canonical 4-tier resolver](20-canonical-resolver-4-tier.md)
47
47
  - [21 — Canonical Unicode 17 dataset build](21-canonical-unicode17-build.md)
48
48
 
49
+ ### Universal glyph set + UC17 curation
50
+
51
+ - [23 — Universal glyph set: Tier 1 source map](23-universal-glyph-set-source-map.md)
52
+ - [24 — Universal glyph set build](24-universal-glyph-set-build.md)
53
+ - [25 — Font audit against universal set](25-font-audit-against-universal-set.md)
54
+ - [26 — Missing glyph reporter (drill-down view)](26-missing-glyph-reporter.md)
55
+ - [27 — fontist.org consumer integration](27-fontist-org-consumer-integration.md)
56
+ - [29 — Universal glyph set: full Unicode 17 curation (Part 1)](29-universal-set-curation-uc17.md)
57
+ - [30 — Tier 1 font acquisition: specialist fonts](30-tier1-font-acquisition.md)
58
+ - [31 — Universal set production build + coverage validation](31-universal-set-production-build.md)
59
+
60
+ ### Full UC17 coverage + per-font audit (Part 1 close + Part 2)
61
+
62
+ - [32 — Universal glyph set: full UC17 coverage matrix (Part 1 master)](32-uc17-coverage-matrix.md)
63
+ - [33 — Specialist font acquisition refresh](33-specialist-font-acquisition-refresh.md)
64
+ - [34 — Pillar 2 ContentStreamCorrelator (generalize correlate-v4)](34-pillar2-content-stream-correlator.md)
65
+ - [35 — Universal set production run + glyph provenance (Part 1 close)](35-universal-set-production-run.md)
66
+ - [36 — Per-font coverage audit against universal set (Part 2 master)](36-per-font-coverage-audit.md)
67
+ - [37 — Coverage highlight reporter (missing-glyph visualizer)](37-coverage-highlight-reporter.md)
68
+ - [38 — fontist.org glyph consumer + provenance display](38-fontist-org-glyph-consumer.md)
69
+
70
+ ### Pipeline wiring (archive integration)
71
+
72
+ - [40 — fontist-archive-private bin/build uses ucode audit](40-archive-private-uses-ucode-audit.md)
73
+ - [41 — ucode Unicode artifacts → fontist-archive-public bridge](41-ucode-unicode-archive-bridge.md)
74
+ - [30 — Tier 1 font acquisition: specialist fonts](30-tier1-font-acquisition.md)
75
+ - [31 — Universal set production build + coverage validation](31-universal-set-production-build.md)
76
+
49
77
  ### Sequencing
50
78
 
51
79
  - [22 — Implementation order](22-implementation-order.md)
80
+ - [28 — Implementation order update (TODOs 23-31)](28-implementation-order-update.md)
81
+ - [39 — Implementation order update (TODOs 32-38)](39-implementation-order-update-32-38.md)
52
82
 
53
83
  ## Conventions
54
84