literary-clock is a Kindle-oriented literary clock that tells time through quotations from
literature. Each frame displays one literary quotation containing the current time expression,
with that phrase emphasized inside the author's original wording and quiet book/author attribution
below it.
The primary device is a Kindle Paperwhite 4 / 10th Generation, rendered natively at 1448 × 1072 landscape, 300 ppi. Portrait remains supported. PW4 landscape includes an optional quiet renderer-owned short date in the upper-left; there is no standalone digital clock, weather, dashboard, iconography, border, Kindle status bar, or decorative interface.
- All 1,440 minutes have at least one effective display candidate.
- The local operational corpus contains 7,091 unique selectable literary quotes and 8,730 quote-minute eligibility relationships.
- Neutral exact 12-hour expressions can serve both matching AM and PM clock moments.
- Selection uses persistent per-minute shuffle bags, 24-hour global quote cooldown, 12-hour book cooldown, and 6-hour author cooldown.
- The PW4 landscape audit leaves 8,727 display-safe relationships and zero empty display pools.
- Corpus expansion is frozen for V1; 97 accepted sparse-tail exceptions remain below three candidates.
Exact phase snapshots and renderer measurements are in docs/reports/.
Those V1 figures are the pre-audit baseline. The English semantic revalidation documented in
CORPUS_SEMANTIC_REVALIDATION_REPORT.md
reduces the validated selectable view rather than preserving coverage with questionable records.
The frozen renderer produces reusable quote frames on the build Mac rather than typesetting on the Kindle. A bounded Phase 4B pilot and two-hour power study have proved a tiny, offline Kindle-native selector: it reads the Kindle's local clock, chooses a pre-rendered frame, displays it with FBInk, and owns persistent history without a Mac or network. No indefinite scheduler or boot hook is enabled by deployment. Weather, dashboards, clock icons, and other ambient-display features remain out of scope: the screen is a date marginal note, one quotation, its inline time phrase, and discreet literary attribution.
legacy corpora + Standard Ebooks + Project Gutenberg + English Wikisource
↓
normalization + provenance
↓
time semantics
↓
quote-minute eligibility
↓
selector + persistent anti-repeat state
↓
device renderability gate
↓
Pillow bundle builder (Mac, offline)
↓
TSV manifest + deduplicated 1-bit assets
↓
Kindle clock + shell selector + transactional history
↓
FBInk display + RTC-aware scheduling
The project is deliberately small and uses Python 3.11+, SQLite, PyYAML for the one authoritative
YAML source, mwparserfromhell for structural Wikisource cleanup, pytest, ruff, and the standard
library everywhere else.
src/litclock/importers/pins, fetches, parses, and merges upstream corpora.src/litclock/normalize.pyparses clock times, normalizes text, and recovers highlight offsets.src/litclock/db.pyowns the SQLite schema.src/litclock/stats.pycalculates full-day coverage and writes JSON, CSV, and Markdown reports.src/litclock/selector.pyimplements persistent per-minute shuffle bags and soft attribution cooldowns.src/litclock/render/models.pydefines the renderer-onlyRenderQuotecontract, explicitly separates canonical fields from display fields, and rejects unsafe highlight offsets.src/litclock/render/presentation.pydetects serialized/multi-record contamination, normalizes catalog attribution for display only, and constructs exact sentence-aligned excerpts.src/litclock/render/layout.pyperforms adaptive, highlight-aware wrapping with real font metrics and proportional optical composition.src/litclock/render/pillow_renderer.pyproduces deterministic grayscale and 1-bit PNG frames; selection logic is deliberately absent from the renderer.src/litclock/render/production.pydefines the fail-closedpw4-v1production contract used by the bundle builder.src/litclock/bundle.pypre-renders deduplicated quote frames and tiny reusable date overlays, calculates storage projections, and writes the compact Kindle manifest.src/litclock/runtime_bundle.pyowns the line-oriented manifest and deployment-integrity rules.src/litclock/deploy.pystages and validates a versioned bundle on USB-visible Kindle storage, atomically activates it, and retains the previous version for rollback.src/litclock/render/profiles.pycontains proportional profiles for early Kindle, Paperwhite, Basic 11, Paperwhite 5/11, Oasis, explicit PW4 portrait/landscape, and custom dimensions.src/litclock/standard_ebooks.pyindexes Standard Ebooks on GitHub, performs resumable sparse acquisition, and records OPF metadata, commit IDs, and content checksums.src/litclock/xhtml.pyextracts semantic prose and exact source offsets from XHTML.src/litclock/timeparse.pydetects and conservatively resolves explicit literary time phrases.src/litclock/semantic.pydeterministically distinguishes clock-time grammar from durations, references, scores, ratios, identifiers, and context-poor lexical matches.src/litclock/semantic_audit.pyrevalidates every existing English quote-minute relationship, exports review evidence, and atomically activates a reversible audited selection view.src/litclock/mining.pyscores candidates, deduplicates passages, prioritizes sparse minutes, manages the review queue, and performs capped high-confidence imports.src/litclock/phase2a5.pyaudits candidate disposition, runs the evidence-recording contextual AM/PM pass, exports sparse-minute review packets, and performs controlled recovery imports.src/litclock/gutenberg.pyindexes the official Project Gutenberg bulk catalogs and performs resumable, checksum-tracked plain-text acquisition from official rsync mirrors.src/litclock/gutenberg_text.pystrips Gutenberg boilerplate and extracts prose paragraphs while retaining source offsets.src/litclock/gutenberg_mining.pystreams books through the shared time parser, rejects source-specific false positives, deduplicates across all corpora, imports only into deficient buckets, and produces the Phase 2B report and review queue.src/litclock/phase2c.pyseparates textual clock-face semantics from display eligibility, audits ambiguous candidates, plans capped counterfactuals, and activates shared AM/PM minute relationships only after the counterfactual is saved.src/litclock/wikisource.py,wikisource_text.py, andwikisource_mining.pyacquire an official English Wikisource multistream dump, parse XML and wiki markup structurally, quarantine uncertain licenses, and target only minute pools below three.src/litclock/phase2d.pyfreezes the exact sparse-tail target set, recovers retained candidates, exports the focused review queue, and produces the Phase 2D report.data/third_party/holds checksum-verified upstream snapshots and their provenance files.data/public_domain/standard_ebooks/holds the source index and manifest. Its ignoredbooks/cache contains sparse working copies and is not part of the main Git history.data/public_domain/gutenberg/holds the ignored bulk catalogs, resumable text cache, and source manifests. Full-run texts may be pruned after checksums and candidates are committed.data/public_domain/wikisource/is an ignored local cache for the official compressed XML dump, multistream index, checksum manifest, and disposable target-page spools. The dump is never expanded permanently or added to normal Git history.data/generated/holds the rebuildable SQLite database and coverage outputs.data/local/is ignored scratch space for future locally mined material.
See architecture, corpus design, renderer behavior, Kindle runtime helpers, and the Phase 4B standalone runtime report for focused design documentation.
The normalized quotes table has one canonical row per distinct literary passage. Textual meaning
is recorded separately in quote_time_semantics, while quote_minute_eligibility maps that one
quote to one or more legitimate display minutes. An unresolved exact 12-hour clock-face expression
can therefore have AM and PM relationships without duplicating the canonical quote. Explicit
meridiem or deterministic source context keeps only the resolved side. Every upstream row that
merged into a canonical quote remains in quote_provenance; invalid-time and malformed rows remain
in import_issues. import_runs and sources make each build auditable, while shuffle_state and
display_history provide selection persistence.
Highlight validation has five corpus statuses:
VERIFIED_EXACT: one literal occurrence oftime_textin the display quote.VERIFIED_NORMALIZED: one occurrence after Unicode/case/punctuation/spacing normalization, with offsets mapped back into the display quote.AMBIGUOUS: more than one plausible occurrence; no offsets are trusted.TIME_TEXT_NOT_FOUND: no plausible occurrence; no offsets are trusted.INVALID_TIME: the source time is not a valid 24-hourHH:MMvalue and the row is quarantined.
Only the two verified statuses are automatically selectable.
Highlight verification is necessary but not sufficient. An activated English semantic audit also requires the highlighted phrase to derive the claimed minute under accepted clock grammar and its context not to force a duration, reference, score, ratio, measurement, identifier, or structural reading. Unknown cases are REVIEW and are excluded from the precision-first selectable view; no canonical quote or provenance record is deleted.
The initial corpus imports pinned snapshots from:
- kapoorankush/litclock, whose assembled quote database is distributed upstream under CC BY-NC-SA 4.0. Its MIT license applies to its software, not its quote database.
- zenbuffy/LiteraryClock. The repository has no
explicit root corpus license and credits substantial data derived from the JohsEnevoldsen
corpus. This project records the license as
NOASSERTION; no permission is implied here. - JohsEnevoldsen/literature-clock, licensed upstream under CC BY-NC-SA 2.5 Generic.
The Zenbuffy YAML is used instead of its derived CSV because the YAML is current and structurally preserves multiline records; the current CSV has unescaped physical newlines and cannot be parsed without conflating quote fragments with records.
Phase 2A mines the source repositories published by
Standard Ebooks. The pipeline reads only
src/epub/text/*.xhtml, src/epub/content.opf, and repository license files. Standard Ebooks'
contributors dedicate their contributions through CC0; each repository states that its source
text and artwork are believed to be in the United States public domain and warns that other
jurisdictions may differ. Commit, repository, rights, and checksum metadata are retained per book.
Project Gutenberg is intentionally not used in Phase 2A. Phase 2B independently mines only works
whose RDF rights field is exactly Public domain in the USA.; it does not treat permission-only
works as public domain. Catalog and literary-filter metadata, source identifiers, checksums, and
acquisition timestamps remain attached to each book and candidate.
Phase 2D uses the official English Wikisource database dumps, not individual web pages. Wikisource is a mixed-rights collection: automatic imports require work-level public-domain evidence, while unknown or freely licensed material is quarantined for review. Each accepted quote records its page title, revision, dump date, source URL, work metadata, license evidence, and source locator.
Our source code is MIT-licensed. Third-party corpus files, the normalized database, and corpus-derived reports are not thereby relicensed as MIT. The normalized database and bulk source datasets are intentionally not published in this Git repository. See NOTICE.md for exact commits, source paths, and license cautions. No font binaries are stored here.
Install uv and then create the locked project environment:
uv syncuv reads .python-version and can provision a compatible Python automatically.
Download and checksum-verify the pinned source files:
uv run litclock fetchBuild a fresh database atomically at data/generated/litclock.sqlite3:
uv run litclock importAn existing database is replaced only after the complete new import succeeds. Use --db PATH on
import, stats, or selection commands to work with another SQLite file.
uv run litclock statsThis prints the core coverage metrics and writes:
data/generated/coverage.jsondata/generated/minute_coverage.csvdata/generated/COVERAGE_REPORT.md
Coverage metrics and thresholds count effective quote-minute eligibility relationships backed by selectable quotes with verified highlight offsets. Reports distinguish canonical literary quotes, unique selectable quotes, and quote-minute relationships; a shared quote counts once in the first two totals but once in each legitimate minute pool. The canonical total also includes retained ambiguous and unmatched records. The report ranks mining targets by effective candidate count, canonical candidate count, author diversity, book diversity, and time. The machine-readable minute report includes all 1,440 rows.
Run the audit before mutation, inspect the ignored review artifacts, then explicitly activate the reviewed run:
uv run litclock semantic-audit
uv run litclock semantic-report --run-id RUN_ID
uv run litclock semantic-apply RUN_ID
uv run litclock semantic-report --run-id RUN_IDThe first command classifies all underlying selectable English relationships without changing the
active minute pool. Detailed CSVs under data/generated/semantic-audit/ include quotation context
and are deliberately ignored. Activation filters QUARANTINE and unresolved REVIEW decisions from
quote_minute_pool while preserving the canonical quote, source provenance, and full versioned
decision record. It does not mine or manufacture replacements.
Start with a bounded sample. Completed books and cached downloads are skipped on later runs:
uv run litclock mine-standard-ebooks --limit-books 20
uv run litclock mine-standard-ebooks --limit-books 100 --workers 8The catalog is discovered from the Standard Ebooks GitHub organization rather than a hardcoded
title list. Acquisition uses shallow, blob-filtered sparse clones with retry/backoff. The manifest
at data/public_domain/standard_ebooks/manifest.jsonl records repository URL, commit, metadata,
rights, timestamps, processing state, counts, and checksums. Use --refresh-catalog to update the
cached repository index and --reprocess to re-run indexed books after parser changes.
The miner parses XHTML structurally, excludes identifiable front/back matter and navigation, and preserves file, section, paragraph, and character locators. Exact 24-hour or strongly resolved AM/PM candidates with clean context and exact highlights can be imported; ambiguous, approximate, ranged, malformed, and lower-quality passages remain reviewable.
uv run litclock mining-stats
uv run litclock review-export
uv run litclock import-mined --confidence high
uv run litclock statsHigh-confidence import dynamically favors sparse buckets and author/book diversity, and stops
adding to a minute once it has seven selectable quotes. The generated Phase 2A comparison is
data/generated/PHASE2A_REPORT.md. The CSV review export is intentionally ignored because it is a
large rebuildable artifact.
Phase 2A.5 does not fetch another corpus. It accounts for every original high-confidence candidate, simulates capped counterfactual imports, and revisits only AM/PM-ambiguous candidates whose two possible buckets are not already full:
uv run litclock phase2a5The contextual resolver automatically accepts only a daypart directly linked to the time phrase in
its containing sentence or deterministic elapsed-time arithmetic from a nearby explicit absolute
time. It stores the evidence and source locator for every automatic decision. Weak narrative
continuity remains unresolved. Use --audit-only to generate the analysis without importing newly
resolved candidates.
The command regenerates coverage files and writes PHASE2A5_CANDIDATE_AUDIT.csv,
PHASE2A5_AUDIT.md, PHASE2A5_REVIEW_PRIORITY.csv, and PHASE2A5_REPORT.md under
data/generated/. By default, it also copies the final report to
~/Desktop/LITERARY_CLOCK_PHASE2A5_REPORT.md; override that path with --desktop-report PATH.
The Gutenberg pipeline follows the project's bulk-access guidance: it downloads the compressed
CSV and RDF catalogs and selectively retrieves generated UTF-8 plain text from official rsync
mirrors. It never crawls normal ebook pages. Acquisition and processing are restartable; the
SQLite book table is the live manifest and data/public_domain/gutenberg/books_manifest.jsonl is
an exported snapshot. target_expressions.json records the exact parser-supported variants and
live deficit for every target minute. Catalog feeds, working texts, and the large database remain
outside normal Git history.
Run the deterministic staged rollout separately:
uv run litclock mine-gutenberg --stage pilot-a --workers 4
uv run litclock mine-gutenberg --stage pilot-b --workers 8
uv run litclock mine-gutenberg --stage full --workers 10Or run all three stages without a manual gate:
uv run litclock mine-gutenberg --stage allPilot A is cumulative to about 500 eligible books, Pilot B to about 5,000, and the full stage scans
all eligible indexed books. The full stage prunes only successfully processed raw texts by default
to bound disk usage; pass --keep-text-cache when sufficient storage is available. A resumed run
does not download or process books already marked complete.
The semantic parser is shared with Standard Ebooks. Gutenberg-specific extraction removes the header/footer license and rejects catalog, citation, ratio, timetable, legal-reference, and other nonliterary patterns. Automatic import requires explicit U.S. public-domain rights metadata, English literary metadata, an exact unambiguous minute, exact highlight offsets, clean context, cross-source novelty, and a target bucket below seven.
uv run litclock gutenberg-stats
uv run litclock gutenberg-review-export
uv run litclock import-gutenberg
uv run litclock gutenberg-revalidategutenberg-review-export includes unresolved AM/PM candidates only when either possible bucket is
below three. Reports are written to data/generated/PHASE2B_REPORT.md,
PHASE2B_REVIEW_PRIORITY.csv, and PHASE2B_EMPTY_MINUTE_AUDIT.csv; the ordinary coverage JSON,
CSV, and Markdown files are regenerated after import. gutenberg-revalidate reapplies the current
rights, metadata, parser, and prose gates to a resumable scan, safely revokes any obsolete imports,
and refills sparse buckets only with candidates that still pass every gate.
Phase 2C changes only the AM/PM policy for an exact, unresolved 12-hour expression. A phrase such
as 3:46, a quarter past three, or nineteen minutes past four may serve both corresponding
clock moments when neither the displayed excerpt nor trusted source context establishes a
daypart. Explicit AM/PM, directly linked daypart wording, and deterministic local temporal evidence
remain authoritative. Approximate times, ranges, false positives, duplicates, bad highlights,
invalid provenance, and low-quality context remain excluded.
The counterfactual must be created before activation:
uv run litclock phase2c-counterfactual
uv run litclock phase2c-activate
uv run litclock phase2c-reportScenario B fills deficient minute pools with quality-ranked relationships; Scenario C also prefers
new authors and books, then relaxes diversity preferences when needed. Both stop at seven effective
candidates per minute. Outputs include PHASE2C_COUNTERFACTUAL.md, PHASE2C_REPORT.md,
PHASE2C_1546_AUDIT.csv, and a sparse-bucket-only PHASE2C_CONTEXT_REVIEW.csv.
Phase 2D freezes the exact pools below three, reviews retained candidates first, then scans one
official English Wikisource pages-articles-multistream dump. The multistream index permits
deterministic parallel decompression. Each page is scanned once; only pages whose raw expression
maps through the shared parser to a live target are rendered with a wiki-markup AST. Mainspace is
processed before Page: transcriptions, preventing the same passage from being counted twice.
uv run litclock phase2d-targets
uv run litclock phase2d-recover-existing
uv run litclock acquire-wikisource --dump-date YYYYMMDD
uv run litclock mine-wikisource --dump-date YYYYMMDD
uv run litclock phase2d-revalidate
uv run litclock phase2d-review-export
uv run litclock phase2d-reportAutomatic import requires a verified U.S. public-domain work, clean literary attribution, an exact
time and highlight, cross-source novelty, and a still-active pool below three. Unknown licensing,
missing attribution, and genuinely uncertain context remain in the compact review export; citations,
ratios, logs, official documents, broken prose, and OCR corruption do not. Outputs are
PHASE2D_TARGETS.csv, PHASE2D_REVIEW_PRIORITY.csv, and PHASE2D_REPORT.md.
uv run litclock show 16:37
uv run litclock show-nowThe terminal preview marks the stored highlight span in ANSI bold when the output is a compatible
terminal and uses Markdown **bold** markers otherwise. --seed INTEGER injects a deterministic
RNG for repeatable experiments. --sfw-only restricts candidates to records explicitly marked
safe; it excludes both unsafe and unknown records.
Selection uses a persistent shuffle bag for each minute. A quote does not repeat until every currently available quote for that minute has been used. Shared relationships retain one global quote ID, so displaying a quote from its AM pool updates the same persistent history consulted by its PM pool. The selector first prefers not to repeat an exact quote within 24 hours, then prefers books not shown in the past 12 hours and authors not shown in the past 6 hours, progressively relaxing these preferences when a minute has no alternative.
Pillow is the bitmap backend. It discovers a supported local serif family (EB Garamond, Linux Libertine, Georgia, DejaVu Serif, or Liberation Serif, in preference order) and records the exact paths and face availability in each JSON metadata sidecar. Font files are never copied into this repository.
The physically approved PW4 V1 path is one authoritative command. It fixes the landscape profile, renderer-owned date, picturesque emphasis, book/author attribution, and crisp 1-bit threshold output. Georgia is resolved by family name. Apple Chancery remains a local proprietary font and must be supplied through the environment; a missing file or wrong embedded family fails closed.
export LITCLOCK_TIME_FONT='/local/path/to/Apple Chancery.ttf'
uv run litclock render-pw4-v1 16:37 --preview --output /tmp/litclock-frame.png
# Omit 16:37 to use the current local minute.No absolute font path or font binary is stored in the repository. --preview makes selection
non-mutating for QA; without it, the normal persistent anti-repeat history applies. The generated
JSON sidecar records production_preset: pw4-v1. Generic render, render-now, and render-id
remain configurable and retain their existing portable defaults.
For a production-quality generic explicit family, pass all four faces:
uv run litclock render 15:46 --device pw4 --orientation landscape --preview \
--font-regular /path/Family-Regular.ttf \
--font-bold /path/Family-Bold.ttf \
--font-italic /path/Family-Italic.ttf \
--font-bold-italic /path/Family-BoldItalic.ttfThe four structured options are all-or-nothing, and supplied bold/italic files are checked against
their embedded style names. The backward-compatible --font PATH and LITCLOCK_FONT forms are
explicit single-face fallbacks: the CLI warns that bold time emphasis and italic attribution are
unavailable, and metadata reports those missing faces instead of pretending the regular file is a
complete family. Automatic discovery still requires and returns a complete family.
The highlighted phrase may use an independent accent family. Pass a real pair with
--time-font-regular and --time-font-bold; both paths are required and the bold face is checked
using its embedded style metadata. --time-font PATH is an honest single-face option: that exact
face is used, and the CLI warns when it is not actually bold. The picturesque treatment may add
a measured ink stroke to a single-face accent; diagnostics report both the absent bold face and
the stroke width rather than pretending a bold file exists. With no time-font option, wrapping and
drawing use the body family's bold face exactly as before. No configured font failure silently
substitutes a different family.
uv run litclock render 15:46 --device pw4 --orientation landscape --preview \
--date 2026-09-05 --time-emphasis subtle-lift \
--time-font-regular /path/Accent-Regular.ttf --time-font-bold /path/Accent-Bold.ttf
uv run litclock render-id 6730 --time 10:04 --device pw4 --orientation landscape \
--mode 1bit --dither threshold --date 2026-09-05 --time-emphasis picturesque \
--time-font '/path/to/Apple Chancery.ttf'
uv run litclock render-now --device pw4 --orientation landscape --mode 1bit --show-date
uv run litclock render-id 42 --device pw4 --orientation landscape
uv run litclock render 16:37 --device custom --width 800 --height 1200 --previewThe V1 primary profile is Kindle Paperwhite 4 landscape: 1448 × 1072 at 300 ppi. Its layout is
computed natively rather than rotating a 1072 × 1448 portrait frame. pw4_portrait remains a
built-in profile. Earlier generations are represented accurately as paperwhite-1-2 (758 × 1024,
212 ppi) and paperwhite-3 (1072 × 1448, 300 ppi); the legacy paperwhite-1-3 CLI name remains an
alias for the early 758 × 1024 profile. The landscape body uses a 38 px hard minimum, prefers 3–7
lines, soft-limits at 8, and never allows more than 10. Long passages become exact,
sentence-aligned excerpts around the highlighted time phrase instead of shrinking indefinitely.
render and render-now use the existing selector and normally persist shuffle/history state.
Add --preview for non-mutating visual work. render-id is always non-mutating; for a shared
clock-face quote, --time HH:MM chooses one of its eligible display relationships. Available
modes are grayscale and 1bit; crisp threshold conversion is the 1-bit default, while
--dither floyd-steinberg exists for deliberate comparison. The clock face never includes a
separate digital time.
PW4 landscape shows the date by default as locale-independent English text such as
Sat, Sep 5. --date YYYY-MM-DD makes preview output reproducible; --show-date and
--hide-date explicitly control it. Other profiles keep the label off unless requested. The date
source is independent from quote selection and contains no clock time or Kindle system UI. Its
size is computed from the selected attribution size and is always at least one pixel smaller. The
layout candidate gate uses real two-dimensional date/body rectangles, so vertical overlap alone
does not unnecessarily shrink or excerpt a horizontally separate quote.
Physical PW4 comparison selected Georgia for body and attribution, a locally installed Apple
Chancery face for the time phrase, and picturesque emphasis. The highlighted characters use a
deterministic uneven baseline while preserving their order and original wording. PW4 landscape
uses bounded book-style justification: non-final lines expand only until a word gap reaches 0.5 em
and remain partially ragged rather than forming wide rivers. The font file is neither copied nor
redistributed; systems without it must provide another local accent or use the portable body-family
fallback.
Before layout, the presentation gate rejects raw serialized corpus rows, concatenated records,
and corrupt excerpt/source mappings. A rejected selection is not written to display history; the
selector tries another quote from the same minute without disturbing the rejected item in its
shuffle bag. Canonical quote/title/author values are never changed. Display-only attribution
removes life dates and catalog roles, shortens clear subtitle tails, compresses three or more
creators, and renders collections with a named editor as Edited by …. Title occupies at most two
lines, creator at most one, and the entire attribution at most three.
Generate the deterministic corpus-extreme visual suite and its diagnostic reports with:
uv run litclock render-qaThis audits every selectable quote and all 1,440 effective minute pools for PW4 landscape, then
writes data/generated/RENDER_QA.json, RENDER_QA.md,
PHASE3_RENDER_FINALIZATION_REPORT.md, and ignored PNG/contact-sheet artifacts under
data/generated/render_previews/pw4_landscape/. Every frame records canonical/display length,
excerpt offsets, font families/sizes, line counts, attribution transformations,
body/attribution/date bounds, occupancy, highlight wrapping, clipping, fallback-font use, output
mode, and unsupported glyphs. It also emits a curated Phase 4A.3 date/time-font contact sheet.
Phase 4B preserves the validated typography by pre-rendering it on the Mac. Each display-safe canonical quote has one 1072 × 1448 CCW transport PNG regardless of how many minute pools reference it. A compact TSV manifest maps all 1,440 local minutes to those quote IDs. Dynamic dates are tiny separate 1-bit overlays, so the runtime does not need Pillow and the bundle does not multiply every quote by every calendar date.
The builder uses the frozen pw4-v1 contract and fails if the locally configured Apple Chancery
accent is unavailable:
export LITCLOCK_TIME_FONT='/local/path/to/Apple Chancery.ttf'
# Render a bounded sample and estimate full deployment size.
uv run litclock build-pw4-bundle \
--output data/generated/pw4-bundle-estimate \
--estimate-only --sample-size 200
# Build every display-safe frame and all reusable date overlays.
SOURCE_DATE_EPOCH=1788712440 uv run litclock build-pw4-bundle \
--output data/generated/pw4-v1-001SOURCE_DATE_EPOCH makes the manifest timestamp reproducible. The bundle contains no font file,
SQLite database, Python dependency, proprietary path, or duplicate image for shared AM/PM
relationships. Generated assets are ignored because quotations retain their separate source
licenses.
Deploying is a separate, non-scheduling operation:
uv run python scripts/deploy_pw4_bundle.py \
--mount /Volumes/Kindle \
--bundle data/generated/pw4-v1-001 \
--release-version literary-clock-v1 \
--production-release
uv run python scripts/deploy_pw4_bundle.py --mount /Volumes/Kindle --rollbackThe deployer verifies source and copied assets, available storage, dimensions, crisp 1-bit mode,
and SHA-256 values before atomically switching the current-release pointer. The Kindle runtime
uses a static ARMv7 C one-shot engine, POSIX-shell service/recovery helpers, FBInk, and an optional
official KindleCron binary. It captures epoch, calendar date, and minute from one Kindle-local
timestamp and contains no timezone database. History is committed only after FBInk returns success.
The shell engine remains a state-compatible fallback. Code, assets, release metadata, and the
controlled one-shot reboot helper share one checksummed release, so rollback cannot combine new
runtime code with old manifest data.
The bounded pilot changed frames autonomously across eight consecutive local minutes and followed
a user-initiated one-hour Kindle timezone change without a Mac, network, configuration change, or
replay of missed minutes. A later two-hour exact-mode run completed 121 displays, used no Wi-Fi,
and moved the coarse battery reading from 100% to 97%, but necessarily prevented suspend to remain
minute-accurate. Deep-sleep tests at two, three, and five minutes were not reliably recurring on the
tested PW4/firmware; a long five-minute target became stale until USB wake. See
kindle/README.md for recovery commands and
PHASE4B2_POWER_LIFECYCLE_REPORT.md for the
measured cadence matrix and current deployment boundary.
The final appliance uses a persistent, checksummed KMC user-storage hook. It resolves the active release, validates it before the framework is paused, and calls the idempotent exact-mode service:
uv run python scripts/deploy_pw4_bundle.py \
--mount /Volumes/Kindle --enable-production-autostart
uv run python scripts/deploy_pw4_bundle.py \
--mount /Volumes/Kindle --disable-production-autostartThe reversible reboot-test hook remains available for bounded qualification work and self-disarms before launch:
uv run python scripts/deploy_pw4_bundle.py --mount /Volumes/Kindle --enable-boot-hook
uv run python scripts/deploy_pw4_bundle.py --mount /Volumes/Kindle --disable-boot-hookuv run pytest
uv run ruff check .The third-party inputs and reports are small enough to inspect. The rebuildable SQLite database, virtual environment, caches, and local scratch corpus are ignored rather than committed.
- Phase 1 — corpus core: reproducible import, validation, deduplication, coverage, and selection.
- Phase 2A — deterministic Standard Ebooks mining and conservative automatic acceptance.
- Phase 2A.5 — high-confidence accounting and evidence-backed recovery from ambiguous detections.
- Phase 2B — coverage-driven Project Gutenberg mining with conservative cross-source import.
- Phase 2C — shared 12-hour clock-face semantics, multi-minute eligibility, and global quote cooldowns.
- Phase 2D — targeted retained-candidate recovery and official English Wikisource dump mining for the below-three sparse tail, stopping automatic imports as each pool reaches three.
- Phase 3 — device-independent literary page layout, bitmap rendering, and visual QA.
- Phase 4A — physical PW4 renderer validation and frozen production typography.
- Phase 4B.1 — bounded offline Kindle runtime pilot, transactional history, and local-time test.
- Phase 4B.2 — measured exact/eco power behavior, release integrity, reversible startup, and bounded reboot recovery; indefinite 24/7 activation remains an explicit post-test decision.
- Phase 4C — static ARMv7 native one-shot runtime, checksummed engine selection, and preserved shell fallback on the frozen English V1 bundle.
- Phase 4D — 25-hour offline appliance qualification across midnight and the history-retention boundary. Literary Clock V1 is complete.