Repository navigation
Release 0.1: machine-made Weft, machine-made Sans and the specimen site - #1
Conversation
Set the metrics (1 design unit = 62.5 units, 1000 per em), the glyph list, the three axes and the FRAG stages. Record the name check for "Weft". Keep sketch 0.1 as the source of all shapes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Port the sketch 0.1 stroke table to loom/glyphs.py as plain data. Turn centre lines into outlines with square caps, miter joins at limit 2.4 and bevel joins beyond. Remove overlaps with skia-pathops. Write one UFO master: Regular, wght 400, FRAG 0, 64 glyphs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Test that every sketch glyph builds, contours are closed and not empty, the advance is constant, no overlaps remain, and the cap, join, dot and stem rules hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Build TTF and OTF with fontmake. Add a STAT table. Run Fontspector with the universal profile on each font. Fail on FAIL or worse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Render the proof sheet from the compiled TTF. Render the same lines with the sketch 0.1 code. Write a side-by-side sheet and an overlay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Install the dependencies, run the tests, build the fonts, run Fontspector and upload the fonts, the proof and the reports. Pin each action to a full commit SHA. Verify the Fontspector download with SHA-256. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Put the fonts under SIL OFL 1.1 with Reserved Font Name "machine-made". Put the code under Apache-2.0. Update README, STATUS and the changelog for phase 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Weft: the phase 1 Regular as WOFF2 (a-z, A-Z, 0-9, no axes). Sans: upstream Plus Jakarta Sans 2.7.1 variable fonts as WOFF2, with the upstream OFL. The site uses these until the 0.1 webfonts exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
site/build.py selects the fonts (FONT_SOURCE: auto, release or standin), copies them to site/_site, writes the font data as JSON (axes, cmap, stylistic sets, commits), reads proofs/weft/readability.json when it exists, and draws the wordmark and favicon from the Weft outlines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Pages: home, Weft, Sans, download and install, 404. Light and dark themes, responsive to 360 px, no third-party resources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Runs on push to main and on manual dispatch. Gets the webfonts from the 0.1 releases when they are not in the checkout, builds the site and deploys it with the Pages actions. Actions are pinned to commit SHAs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Move the generator, tests, tools, scripts, reference sketch, brief and pyproject.toml into weft/. Move the proofs into proofs/weft/. Write the build outputs to fonts/weft/ and weft/build/. Change the CI workflow to run in weft/ and only on Weft paths. Add a copy of the OFL at weft/OFL.txt. Tests, build and Fontspector pass again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Copy the Glyphs sources, the build config and the credit files from tokotype/PlusJakartaSans at commit 18d1cd2f7ea10481919d2f05c1f7064b7307fc26 (version 2.071). The files are not changed. Later commits change them, so each change is a reviewable diff against this commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Rename the sources to MachineMadeSans and set the family name "machine-made Sans", the version 0.100, the copyright, designer, description, manufacturer, licence and vendor fields. Keep the upstream copyright line and add the machine-made Sans line. Set the typo and hhea line metrics to 875/-281/0, the same as Weft. Keep the win metrics (1296/356) to prevent clipping on Windows. Repair three upstream source errors that stop the build or break a glyph: the ss04 feature name has bad escapes, the unused @noexport class lists glyphs that do not export, and upDownArrow has a component with scale (0,0). Mark the upright Bold instance as bold. Add sans/scripts/fork.py, which makes these changes from the pinned upstream sources. This commit runs it with --skip-forms; the default forms change in the next commit. Add the build and finish scripts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Swap the glyph data in the sources. For each glyph in the upstream ss01 (Lancip, sharp) or ss02 (Lurus, straight) set, the alternate is now the default glyph and the old default is X.ss01. When a glyph has both alternates (a, j, r, y and their accented forms), Lurus is the default and Lancip stays reachable as X.ss02. New sets: ss01 "Round forms (original defaults)", ss02 "Lancip: sharp a j r y", ss03 "Lingkar: swirl" (works from all forms). The f ligatures keep the round forms, and liga makes them only after ss01. Composites that use a changed glyph get new widths, offsets and anchors: Cyrillic a, er, u, short u, i, yi, je, Ka and the Bulgarian forms that use Latin g, k, m, n, u, and the digraphs ij, IJ, Lj, Nj, nj. No outline is changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
- sans/scripts/build.sh: gftools builder, then finish.py. Fixed timestamps (SOURCE_DATE_EPOCH) make the build repeatable. - sans/scripts/finish.py: PostScript names without the hyphen, WWS bit, STAT for static fonts, no "prep" table in OTF, WOFF2 of the variable fonts. - sans/scripts/check.sh: Fontspector universal profile per format, and varLib.interpolatable on the master UFOs. - sans/fontspector.toml: one override, with the reason. - sans/scripts/proof.py: proof sheets from the compiled fonts. - sans/scripts/compare_builds.py: compares a new build with the committed fonts. - sans/tests: source round trip, names, metrics, style linking, and a shaping test that each new default form draws the upstream alternate. - .github/workflows/sans.yml: build, compare, test, check, proof, upload. Actions are pinned to commit SHAs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Add the build output of sans/scripts/build.sh: two variable fonts (upright and italic, wght 200 to 800), 14 static TTF, 14 static OTF and WOFF2 of the variable fonts. The build is repeatable: a second build gives the same bytes. Add the proof sheets: default forms before and after, stylistic sets, weight waterfall, body text at 16 to 24 px, and one line with Weft. The root .gitignore ignores fonts/. These files are added with "git add -f" so that the Sans outputs are in the repository. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
- sans/OFL.txt: OFL 1.1 with the upstream copyright line, unchanged, and the machine-made Sans line with Reserved Font Name "machine-made". - sans/README.md: credit to Plus Jakarta Sans by Gumpita Rahayu, Tokotype; the fork point; all changes; the RFN decision and its reason (OFL FAQ 5.10); how to build. - sans/STATUS.md: check results, line metrics before and after, measured x-heights of Sans and Weft for 0.2, remaining WARNs with reasons, open risks. - sans/CHANGELOG.md and sans/RELEASE-NOTES-0.1.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Replace the overlap-removed outlines with fixed stroke pieces: one contour per segment, join wedge points in the end edge. Every glyph has the same contours and points in all 12 masters (wght 100, 400, 900 x FRAG 0, 15, 50, 100). The variable font keeps the overlaps. The statics remove them. Add FRAG as a fixed, seeded design: ink-trap notches at corners and T-junctions (closed at FRAG 0, open at 15), cuts across strokes (closed by an overlap up to 15, open from 16 to 50, always narrower than the stroke), and drift, shrink and drop from 51 to 100 on a 0.25 grid. Lock the drift seed per glyph in loom/drift_lock.json so that fontTools interpolatable is clean (tools/relock.py makes the file). Add the rest of printable ASCII, the Latin-1 Supplement, the Euro sign, curly quotes, ellipsis, bullet and the rupee sign. Accented letters are components placed with anchors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Add tools/readability.py: render a fixed paragraph at FRAG 0, 15, 50 and 100 at 16, 20 and 24 px, read it with Tesseract, and compute the character error rate. Test the look-alike characters at FRAG 0 and 15. Compare with DejaVu Sans Mono. Gate FRAG 0 and 15; report FRAG 50 and 100. Add tools/proof.py for five proof sheets. Keep the sketch comparison as tools/sketch_compare.py. Remove the phase 1 proofs. Build the variable font, nine statics (TTF and OTF) and three WOFF2 with fixed timestamps, so the build is reproducible. Run Fontspector on the variable font, the TTF family and the OTF family. In CI, also run the interpolation check, the OCR gate, and a check that the committed fonts match a fresh build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Add the build outputs: the variable font MachineMadeWeft[FRAG,wght].ttf, nine static instances at FRAG 0 as TTF and OTF, WOFF2 for the variable font, Regular and Bold. Add the proof sheets and the OCR readability report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Add weft/CHANGELOG.md, weft/STATUS.md and weft/RELEASE-NOTES-0.1.md for the integration session and the release page. Update the metrics in the brief. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Add machine-made Sans 0.1: the fork of Plus Jakarta Sans 2.071, the build, checks, tests, proofs, fonts and the sans workflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Add the specimen site: site/build.py, pages, assets, stand-in fonts and the site workflow for GitHub Pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Weft 0.1 changed the typo and hhea ascender from 875 to 906. Sans 0.1 was made against the phase 1 value. Change the value in fork.py, make the sources again and change the tests. The line height is now 1.187 em in both families. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Add the Sans fonts and the shared line proof from the new sources. Two builds give the same bytes. Tests, Fontspector and varLib.interpolatable pass. Change the Sans docs to the new value. The sans workflow uses the Weft 0.1 Regular TTF for the shared line proof. The root .gitignore ignored fonts/ (only fonts/weft/ was an exception), so fonts/sans needed "git add -f". Ignore only the build scratch: build/, reports/, caches, the gftools files next to the Sans sources and site/_site/. Remove the fonts exception from site/.gitignore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Remove FONT_SOURCE and the stand-in fonts. site/build.py now uses only fonts/weft/webfonts and fonts/sans/webfonts and copies each family's OFL.txt next to its fonts. It stops with an error when a font or the readability report is missing, so the deploy fails and does not ship other fonts. Read proofs/weft/readability.json in the shape that weft/tools/readability.py writes (weft.paragraph."FRAG n".cer_mean). Show the reference font and the gate result under the table. site.yml: remove the step that downloaded webfonts from the releases. Add a step that checks the built site has the fonts and the report. Point the licence links to LICENSING.md and the loom link to weft/loom. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Founder decision before the release: the Lurus (straight) forms are the default forms, and the Lancip (sharp) forms are no longer default. fork.py (the one source of the swap): - a j r y p q and their accented forms: Lurus is the default (48 glyphs per style). ss01 "Round forms (original defaults)" gives the upstream forms of these glyphs. - All Lancip forms are now X.ss02 in ss02 "Lancip: sharp forms" (175 glyphs per style). ss02 also works after ss01. - The other letters keep the upstream default form. - liga: the upstream f ligatures work in default text again. liga comes after the sets, so ss02 gives sharp f, i, l with no ligature. - Cyrillic а р у ў ј follow the Latin defaults. п ш ц do not change. Make the sources again, build the fonts again (two builds give the same bytes) and render the proofs again. Change the tests: compare the defaults, ss01 and ss02 with the upstream outlines, check the ligatures and the Cyrillic look-alikes. 117 tests pass, 1 skips (no italic Cyrillic). Fontspector: 0 FAIL, 0 ERROR. varLib.interpolatable: no problems. Change the Sans docs and the site text. The Sans page shows ss02 as a row that the reader can turn on and off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
The Sans build took the date of the last commit that changed sans/sources. A rebase or a squash merge changes that date, and then the fonts from a fresh build do not have the same bytes as the committed fonts. Use the fixed date that Weft uses (2026-10-26 00:00 UTC) and build the fonts again. Two builds give the same bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
Root: - README.md: the two families, proof images, the site and release links, how to build each family and the site, the layout, the licence split, the credit for Plus Jakarta Sans and the note that agents made the repository. - STATUS.md: stage "released 0.1", next tasks, proven items and open risks from the Weft and Sans notes. - CHANGELOG.md: index to the family changelogs, with the site entry. - LICENSING.md: which licence covers which path. - Remove the root OFL.txt. It was a copy of weft/OFL.txt. Each family has its own OFL.txt. release.yml runs on the tags weft-v*, sans-v* and site-v*. For the fonts, it builds again, fails if the build differs from the committed fonts, runs the tests and the checks, makes the zip and the single files, attests their build provenance and creates the release with gh. Every action is pinned to a full commit SHA. .github/scripts/package_release.sh makes the release files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
|
This PR has 10,309 reviewable changed lines after ignored/generated files are excluded, above this repository's 5,000-changed-line automatic review limit. The raw diff is 219,742 lines before ignored/generated files are excluded. Most of the diff comes from:
Comment |
Summary
This PR merges the release branch
release/v0.1. It has three parts.After the merge, pushing three tags makes the three GitHub releases.
machine-made Weft 0.1.0 (from
weft/phase-2): a grid-built monospace with the axeswght100 to 900 andFRAG0 to 100, 9 static weights (TTF and OTF) and WOFF2. 948 tests pass. The build is byte-reproducible. Fontspector reports 0 FAIL. The OCR gate passes (FRAG 0: 0.23 %, FRAG 15: 0.11 %, DejaVu Sans Mono: 0.46 %).machine-made Sans 0.1.0 (merged from
sans/v0.1): a fork of Plus Jakarta Sans 2.071 with 2 variable fonts, 14 TTF, 14 OTF and 2 WOFF2. The release branch makes two changes to it:sans/scripts/fork.py.SOURCE_DATE_EPOCH, so a rebase or squash merge cannot break byte reproducibility.117 tests pass and 1 skips (the italic has no Cyrillic). Fontspector reports 0 FAIL and 0 ERROR. varLib.interpolatable finds no problems.
Specimen site 0.1.0 (merged from
site/specimen):site/build.pynow uses only the committed WOFF2 files. The stand-in fonts and theFONT_SOURCEswitch are removed. A missing font or a missing readability report stops the build, sosite.ymlcannot deploy without the real fonts.proofs/weft/readability.jsonin the shape thatweft/tools/readability.pywrites.Repository root:
fonts/andproofs/are now tracked normally; nogit add -fis needed. Build scratch stays ignored.weft/OFL.txt, and each family carries its own..github/workflows/release.ymland.github/scripts/package_release.sh.Licence decision
weft/OFL.txt.sans/OFL.txt. It keeps the Plus Jakarta Sans copyright line and adds the machine-made line with the RFN (OFL FAQ 5.10).weft/loom/glyphs.py,extended.py) are font software under the OFL, as their headers say.LICENSE). This covers the generators, scripts, tests, CI and the site.LICENSING.mdmaps each path to its licence.What to review
proofs/sans/sans-stylistic-sets.pngandproofs/sans/sans-alternates-before-after.png: default, ss01, ss02, ss03, and the fi/fl ligatures.sans/scripts/fork.py. Look atplan_forms,rewrite_featuresand the tests insans/tests/test_fonts.py..github/workflows/release.yml. Three jobs, one per tag prefix:gh release create.contents: write,id-token: writeandattestations: write, on the jobs only.git ls-remote.site.yml. The release-download step is removed. A new step checks that the built site has the fonts and the report.Local run (fresh clone of
release/v0.1at 060e381, 2026-10-26)fonts/weft. varLib.interpolatable: no problems. Fontspector: 0 FAIL / 0 ERROR (VF, TTF, OTF). OCR gate: pass.fonts/sans(32 files). pytest: 117 passed, 1 skipped. Fontspector: 0 FAIL / 0 ERROR (VF, TTF, OTF, WOFF2). varLib.interpolatable: no problems. Proofs are byte-identical toproofs/sans.package_release.shmakes all three zips and the single files. The OCR report from the release step equals the committed report.Not run locally: the workflows on GitHub runners, the attestation step and
gh release create(gh is not authenticated here), and the Fontspector network checks.Release steps after the merge
main.site.ymldeploys the site to Pages. Before this, set Pages source to "GitHub Actions".mainand push the tags:release.ymlcreates "machine-made Weft 0.1.0", "machine-made Sans 0.1.0" and "machine-made type site 0.1.0". Check the assets, then check one file withgh attestation verify MachineMadeWeft-Regular.woff2 --repo machine-made/type.Open risks
compare_builds.py.Cost: one integration agent session (about 95 tool calls; token count not measured in the session). Local CPU only: 9 Sans builds (about 1.5 min each), 2 Weft builds, 3 OCR gate runs, Fontspector runs, and one fresh-clone end-to-end run. No GitHub runner minutes and no paid APIs.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UHtj1XPp34FFs8LVpY1PRF