Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
a499609
The trial says which mark, softly enough not to say where
omars-lab Aug 12, 2026
cbf7b9a
Sixty placements say the correction is right and a tenth of a unit low
omars-lab Aug 12, 2026
9138abe
Two marks on one page were never two facts
omars-lab Aug 12, 2026
91b6fb6
The sitting is re-read, and the answers come home
omars-lab Aug 12, 2026
fc03646
The shift file says how much of the mushaf it speaks for
omars-lab Aug 12, 2026
1ee106b
Six hundred pages answer the question forty could not
omars-lab Aug 12, 2026
a36fa6c
A sitting can now be built on pages the correction has never seen
omars-lab Aug 12, 2026
fdd2111
The prediction is written down before anybody places a rectangle
omars-lab Aug 12, 2026
8103735
The second hand, and the one thing it must never see
omars-lab Aug 12, 2026
49f3722
The registers learn that a sitting has a hand in it
omars-lab Aug 12, 2026
81fcf42
The forced choice stops asking about the pages it was fitted to
omars-lab Aug 12, 2026
5020b41
The runbook that releases the mark layer asks about pages nobody has …
omars-lab Aug 12, 2026
d4cb144
A page that asks about one mark at a time, and a count it can no long…
omars-lab Aug 14, 2026
edf9ce0
The registers learn where the marks go, and the question about it get…
omars-lab Aug 14, 2026
1bc6ab4
How far one press moves the rectangle is a range, not a choice of two
omars-lab Aug 14, 2026
fe3d506
The map says why the size of a press is a range now
omars-lab Aug 14, 2026
1889803
The route is not the statement; the resting place is
omars-lab Aug 15, 2026
e5b950c
The sitting gets read back, and the reading is a skill
omars-lab Aug 15, 2026
c92f51a
Tap the ink, and read back the answers that were never handed over
omars-lab Aug 15, 2026
83041f3
What the leftover is made of, and how many marks there were all along
omars-lab Aug 15, 2026
9771584
The page that answers whose turn it is
omars-lab Aug 15, 2026
70d78e7
The rendered page, and the pointer to it from the front door
omars-lab Aug 15, 2026
1760776
The finger that stayed still, and the ink it was actually on
omars-lab Aug 15, 2026
99797f5
A phantom placement is a well-formed placement
omars-lab Aug 15, 2026
3ab43bd
The runbook says what a resting finger does now
omars-lab Aug 15, 2026
79a84b4
The line that bends, and the line that still will not
omars-lab Aug 15, 2026
8a48b43
Half an answer to the question that was left open, and which half
omars-lab Aug 15, 2026
3fe650c
The sixth card on the board, and the column that would have hidden it
omars-lab Aug 15, 2026
0980c99
Option I in the register, and the record that has to name it in full
omars-lab Aug 15, 2026
765f19e
The front door counts the rooms instead of remembering them
omars-lab Aug 15, 2026
7ede79d
Two attempts nobody recorded, and a builder that needed a row
omars-lab Aug 15, 2026
87b4241
The second parent the shards had all along, and the notice that never…
omars-lab Aug 16, 2026
5676128
The notice the shards owed, and the gate that reads imports instead o…
omars-lab Aug 16, 2026
a97fa98
Two rows closed by a gate, and a third opened by it
omars-lab Aug 16, 2026
a053bbd
The repo's most-repeated claim was false, and now a gate holds it
omars-lab Aug 17, 2026
7fe43be
One run writes both copies, and the measurement is recomputed each time
omars-lab Aug 17, 2026
c7525ee
The registers catch up, and a fourth question goes on the board
omars-lab Aug 17, 2026
2cd94af
A sixth option, which is two of the others with the colours re-assigned
omars-lab Aug 17, 2026
bb8273e
The crop question is answered, and the build it now owes has a row
omars-lab Aug 17, 2026
5669dec
The whole register on one page, with the lines between the decisions …
omars-lab Aug 17, 2026
9a45641
Where the name came from, and what was decided before there was a repo
omars-lab Aug 17, 2026
27261d9
Nine pages had been published and the tree knew about five
omars-lab Aug 17, 2026
8aa0d79
The board grows a shelf, and the four pages with no copy lead it
omars-lab Aug 17, 2026
2d0e8a0
Quoting a receipt is not a purchase
omars-lab Aug 17, 2026
2ec9f4d
The panel that got rebuilt had never been on the map
omars-lab Aug 17, 2026
507b02d
The plan file was describing work that had already been done
omars-lab Aug 17, 2026
cb48eb2
The step of the routine that was an instruction rather than a command
omars-lab Aug 17, 2026
a9a6266
Where the freshness check is written down, and what it found
omars-lab Aug 17, 2026
7e7a99d
The generator that draws one real question at the size it is really a…
omars-lab Aug 17, 2026
e911e38
Where the sittings live, asked as a question somebody can answer
omars-lab Aug 17, 2026
24fd30e
One address, one way in, and two taps that are two taps
omars-lab Aug 17, 2026
157c0a1
Where the sittings live, settled the day it was asked
omars-lab Aug 17, 2026
ecef343
The front door stops being a photograph of the day it was built
omars-lab Aug 17, 2026
0a76b40
What the second by-eye sitting said, and where the front door now lives
omars-lab Aug 17, 2026
a82341e
The two ways a rectangle can be wrong are not on the same marks
omars-lab Aug 17, 2026
8a891c0
The search may not answer from outside the room it was given
omars-lab Aug 17, 2026
044c0af
The guess that was reachable, consistent and wrong, left standing
omars-lab Aug 17, 2026
fdd94d4
Moving on without reaching for the corner
omars-lab Aug 17, 2026
01a1d9a
Saying it is odd without reaching for the row
omars-lab Aug 17, 2026
e38376f
Two populations wanting opposite fixes, and a tally that stopped coun…
omars-lab Aug 18, 2026
67f1905
Reach for the ink instead of resizing a guess toward it, checked agai…
omars-lab Aug 18, 2026
4a138e3
Run "reach for the ink" as a real escalation, over every mark refused…
omars-lab Aug 18, 2026
3e7173a
Finish the wrong-size sitting, and reread ㉙'s outlier as a rate, not …
omars-lab Aug 19, 2026
1872c83
Ask whether the marks that ran out of room and still shipped are actu…
omars-lab Aug 19, 2026
902d08f
Point item ㉛ at its own ruling, not just its own numbers
omars-lab Aug 19, 2026
0b2af57
Nine more items were arguing sittings instead of showing them
omars-lab Aug 19, 2026
0283aa0
Design the guard a piece union too large to trust needs, before writi…
omars-lab Aug 19, 2026
650ecbd
Rebuild the piece-union script the doc's numbers came from, so ㉜ has one
omars-lab Aug 20, 2026
e333652
The guard ㉜ proposed catches a quarter of what it needs to; both ways…
omars-lab Aug 20, 2026
ac3443a
Five bands of the placed set now agree: the ink match predicts size o…
omars-lab Aug 20, 2026
99abeda
Doubling the guard's ground truth moves the cutoff, not just confirms it
omars-lab Aug 20, 2026
f75acf9
Colour the exact letter, and let the palette page know it
omars-lab Aug 28, 2026
a5c4160
Say what a reader would notice, not just how far the rectangle is off
omars-lab Aug 28, 2026
a5d41af
A confusion map may leave the phone when it serves the reader, not never
omars-lab Aug 31, 2026
6dc0b81
Remember: artifacts speak English, only scripture stays Arabic
omars-lab Aug 31, 2026
a3e64b6
Track the task files, so a clean session opens where the last one closed
omars-lab Sep 1, 2026
b2ffce3
Draw one swipe per line, and forgive the print its rounding
omars-lab Sep 1, 2026
7d4402e
Number every cell on the revision map, and grow the page cells to hol…
omars-lab Sep 1, 2026
b90b450
Desktop spread triage: edge-grab turns, zoom open, level leaves, soli…
omars-lab Sep 1, 2026
b6933ba
Record the zoom decisions, and note the desktop turn for next time
omars-lab Sep 1, 2026
0ab9619
The map's date reads as a date a person writes, not a stamp
omars-lab Sep 1, 2026
631278b
The map keys "not in this build" only when a cell in fact is
omars-lab Sep 1, 2026
ae8d01d
The map can be asked "what did I touch this month"
omars-lab Sep 1, 2026
c1d702c
A turned leaf moves your place, so the address moves with it
omars-lab Sep 1, 2026
ba436e6
Guard the turn's landing keeps the two leaves level, as the jump's does
omars-lab Sep 1, 2026
ff81f8e
Remove a torn-down leaf from the page, not only from the memory of it
omars-lab Sep 1, 2026
3a9396a
The page bar gets thirty juz marks, a page-shaped handle and a bubble…
omars-lab Sep 2, 2026
0035a4d
Every road into the stage lands level and centred, by one settle step
omars-lab Sep 2, 2026
0873c77
A whole-book sweep finds the next misdrawn verse box by tool, not by eye
omars-lab Sep 2, 2026
bab3d41
The licensing map is organised by door, and the adjacency tree names …
omars-lab Sep 2, 2026
69c1d74
Two page-bar questions put in front of a person: does a mark pull, an…
omars-lab Sep 2, 2026
0e643ce
Every design is public: the site serves each page under docs/ at its …
omars-lab Sep 2, 2026
cc1ec1d
The hook refuses a stale rendered register, and the plan says what th…
omars-lab Sep 2, 2026
5657eea
Lighthouse audits the app, not the staged design pages; two lint erro…
omars-lab Sep 2, 2026
75db3cd
The twelve goldens agree with the settle step, on both platforms, bec…
omars-lab Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .claude/memory/MEMORY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Memory index

- [Hifth app identity](hifth-app-identity.md) — Quran navigation app for huffaz, renamed from "Linker", loops plan in docs/PLAN.md
- [Artifacts in English by default](artifacts-english-by-default.md) — mocks/walkthroughs use English chrome, only scripture stays Arabic; watch published artifacts for feedback
- [Desktop edge-grab page turn](desktop-edge-grab-turn.md) — desktop spread turns by grabbing the outer fore-edge (hand cursor); mid-page drags never turn
- [Juz-jump leaf alignment](juz-jump-leaf-alignment.md) — a hop revealed the incoming leaf before centring it (1-frame ~9px flash); fixed by centerCurrent() in navigateTo; rebuild dist before e2e
- [Desktop zoom StrictMode orphan](desktop-zoom-strictmode-orphan.md) — "zoom broken on spread" was a dev-only StrictMode orphan host; teardown now removes hosts; prod was never affected
- [SPA hash nav is not a reload](spa-hash-nav-no-reload.md) — a #-only navigate keeps the old bundle on 5173; force location.reload() before calling a change "not rendered"
- [Playwright WebKit missing](playwright-webkit-missing.md) — iphone e2e project cannot launch locally; run the Chromium projects, ask before installing WebKit
- [Designs public on the site](designs-public-on-site.md) — tenet since 2026-09-01: docs/ pages are served from blog.bytesofpurpose.com/hifth/docs/ by the build; never publish a decision page to claude.ai as its address
25 changes: 25 additions & 0 deletions .claude/memory/artifacts-english-by-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
name: artifacts-english-by-default
description: Build design artifacts/mocks with English UI chrome by default; keep only scripture Arabic; watch them for comment feedback
metadata:
node_type: memory
type: feedback
originSessionId: e1a37078-db9f-4e9e-a2a4-0d297cd099ff
modified: 2026-08-29T19:28:22.343Z
---

When building a visual artifact / mock / walkthrough for review (e.g. the confusion-points
"Where You Slip" page), the **app chrome must default to English** — button labels, menu
items, warnings, list rows, legends. The only Arabic that stays is the **Qur'an text itself**
(ayah words, surah names as they appear on the mus'haf) — that is scripture, not chrome.

Also: **publish these design artifacts in English mode and set a comment watch** so feedback
can be captured for future sessions.

**Why:** the owner reviews and leaves feedback in English; Arabic-only chrome in the mocks
makes the experience unreadable to a reviewer and to anyone they show it to, and undercuts the
CLAUDE.md tenet that a decision page be followable by a stranger.

**How to apply:** default new mock UI labels to English; render ayah text in Arabic; keep a
short glossary for domain words; after publishing, establish/keep a comment watch. See
[[hifth-app-identity]].
26 changes: 26 additions & 0 deletions .claude/memory/designs-public-on-site.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: designs-public-on-site
description: "Tenet since 2026-09-01 — every design page under docs/ is public on the app's own site at the same path; never publish decision pages to claude.ai as their address"
metadata:
node_type: memory
type: feedback
originSessionId: c8c77742-fa0c-48ea-9c25-4e720245832a
modified: 2026-09-02T02:13:50.680Z
---

The owner wants every design and decision page public-facing, for transparency, and made it a
tenet of the repo (CLAUDE.md "Every design is public"). The web build stages `docs/**/*.html`
onto the site at the same path, so `docs/design/x.html` is
`https://blog.bytesofpurpose.com/hifth/docs/design/x.html`, with a front door at `/docs/` linked
from the colophon. A decision's `artifact` is derived from its `page` and the gate refuses any
other address.

**Why:** "i want all the designs to be public facing for transparency" / "this should be a tenant
for our repo". Earlier, nine pages went to claude.ai and the tree named five; a link on another
host dies with the host.

**How to apply:** publishing a design = merging it. Do not publish pages to claude.ai as the
decision's address; a conversational copy is allowed but goes in `docs/artifacts.json` as a copy.
Pages reference vendored art as `../../apps/web/public/assets/…` and the staging step rewrites
it; any other dangling relative link fails the build. See [[hifth-app-identity]] and
[[artifacts-english-by-default]].
45 changes: 45 additions & 0 deletions .claude/memory/desktop-edge-grab-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
name: desktop-edge-grab-turn
description: "On the desktop spread the page turns only by grabbing its outer fore-edge, not by dragging across the middle"
metadata:
node_type: memory
type: project
originSessionId: c8c77742-fa0c-48ea-9c25-4e720245832a
modified: 2026-09-01T17:31:44.144Z
---

On the desktop two-page spread, turning a page is an **edge grab**, not a swipe
across the page. The user was explicit: hovering the outer sides shows a hand
cursor, grabbing a fore-edge and dragging turns/flips the leaf, and **any drag
that does not start at a page edge must not turn the page** — the middle is left
free to pan and select. Grab zone shape: a strip down each leaf's outer edge that
**widens at the top/bottom corners** (pinched to a sliver at the vertical middle);
the centre fold/gutter is never grabbable.

**Why:** a hafiz turns a physical mus'haf by its fore-edge; the old
drag-anywhere swipe-to-turn fought selecting and panning on a big screen.

**How to apply:** the rails live on the book (one outer edge belongs to the
*facing* leaf, which the live stage feels no pointer on) and drive the live
stage's exposed turn verbs; left edge pulls forward, right edge pulls back
(drag-right = forward, matching the swipe convention). Desktop swipe-to-turn is
off (the live stage gets `dragToTurn={!desktop}`); wheel and arrow keys still
turn. The phone is untouched — it keeps swipe-to-turn and has no rails. See
[[hifth-app-identity]].

**The grab is a trigger, not a tracked band (as of the peel decision).** Reusing
the phone's finger-locked band on desktop drew a thin fore-edge strip creeping
1:1 over a spread whose two pages had not changed yet — the reader saw "a
vertical bar on the same page," not a turn. So the desktop edge verbs now draw
nothing while the hand moves; on release the commit rule decides, and a
committed grab is handed to the *ordinary* animated turn (the same flip a wheel
or arrow plays — grab, wheel and arrows all funnel through `onTurn → turnTo →
runTurn`). A faithful drag-to-**peel** that reveals the destination *opening*
under a lifting corner is the larger job tracked as the desktop page-turn
animation (#11): it must drive **both** leaves at once (the leaf that lifts and
the destination pages sit on opposite sides of the gutter, and which leaf lifts
depends on turn direction), respect "no 170KB SVG glyph moves" (reveal by
masking a corner off the current page over the already-mounted destination
beneath — never a transform on the page), and wants a live browser to tune.
Because every trigger shares `runTurn`, building the peel there gives scroll and
arrows the peel for free — keep that coupling.
21 changes: 21 additions & 0 deletions .claude/memory/desktop-zoom-strictmode-orphan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: desktop-zoom-strictmode-orphan
description: "Zoom broken on desktop spread" (one leaf grows, one stays) was a dev-only React StrictMode orphan host, not a prod bug
metadata:
type: project
---

The desktop-spread "zooming is broken / don't see full pages on zoom" report reproduced
**only in the dev server** (`pnpm dev`, port 5173), never in the production build (`vite
preview`, 4173). Cause: React StrictMode's dev double-invoke mounts → tears down → remounts
against the *same* layer. `PageStage`'s teardown effect cleared the page Map but did **not**
`host.remove()`, so the first mount's host lingered as an orphan `<div>` outside the Map. Zoom
transforms only the Map's host (`applyTransform` reads `pagesRef.get(currentPage)`), so the
orphan twin sat un-magnified under the one that grew — reading as "one leaf zooms, one doesn't."

Fix (in `apps/web/src/components/PageStage.tsx`): teardown now removes each host from the
layer; `mountPage` re-reads the Map after its last await and yields to a rival that already
landed the page. Guarded by a StrictMode regression test in `PageStage.budget.test.tsx`
(confirmed it fails `-2/+1` on original code). Production was never affected — StrictMode is a
dev-only wrapper — but the dev server is what a developer previews in. See also the cold-link
blank-leaf reveal fix in the same file (showPage/navigateTo reveal unconditionally).
21 changes: 21 additions & 0 deletions .claude/memory/hifth-app-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: hifth-app-identity
description: "What the hifth project is — Quran navigation app for huffaz, renamed from design-phase \"Linker\", built in loops per docs/PLAN.md"
metadata:
node_type: memory
type: project
originSessionId: 0da21e77-a946-4228-877a-aee0949749f7
modified: 2026-07-20T09:33:16.009Z
---

Hifth (حفظ) is a pure navigation web app for huffaz: tap/highlight an ayah on real
mushaf SVG pages → hop to similar verses (mutashabihat), shared roots, etc. Knowledge
graph = static adjacency JSON; no backend. Designed in a claude.ai conversation
(shared: https://claude.ai/share/9d43fe10-4c1a-4880-86eb-4adcadb3dfbb) under the
codename "Linker · رابط"; user renamed it **Hifth** on 2026-07-20.

Key user decisions (2026-07-20): web app first with mobile support (PWA, touch-first);
implement iteratively in loops (each loop = vertical slice demoed on a phone); proper
component architecture is a requirement, not an afterthought. Spec of record:
`docs/reference/linker-spec.md`; plan: `docs/PLAN.md`; interactive mock and architecture
diagram also in `docs/reference/`.
36 changes: 36 additions & 0 deletions .claude/memory/juz-jump-leaf-alignment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
name: juz-jump-leaf-alignment
description: "Why a juz jump could flash the two desktop leaves at different heights, and the one-line fix"
metadata:
node_type: memory
type: project
originSessionId: c8c77742-fa0c-48ea-9c25-4e720245832a
modified: 2026-09-01T17:53:16.966Z
---

On the desktop two-page spread, a jump to another juz (or any non-adjacent
relocation) could flash the two leaves at **different heights** for one frame —
the reported "sometimes things aren't aligned after a juz jump."

**Root cause.** A juz jump is a *hop*, not a page turn: it runs the live stage's
`navigateTo`, which reveals the incoming page (`setCurrentPage` → `display:block`)
and only *frames* it afterwards, inside an `await`ed tween. A freshly mounted host
wears no transform, so for the one paint before the tween's first frame the
incoming page sat at its leaf's top-left — one centring offset (~9px) above the
facing leaf beside it. On a fast machine that is a single frame; on a real one the
incoming ~170KB SVG's parse stalls the correcting frame long enough to see and
screenshot. (The page-*turn* path, `crossFade`, never had this — it applies the
transform synchronously and forces a reflow *before* revealing: the "arrive
already wearing your transform" rule.)

**Fix.** One line in `navigateTo`: call `centerCurrent()` right after
`setCurrentPage`, so the incoming leaf arrives centred before the tween can yield.
A zoom-1 hop then simply rests there; a closer hop (an ayah hop) tweens out from
fit, which is the correct motion anyway.

**Guard.** `desktop.spec.ts` → "a juz jump keeps the two leaves level through
every frame" polls both leaves' top edges every frame across the settle and fails
if any frame caught them >1.5px apart (proven to bite without the fix). Note the
e2e gotcha: `vite preview` serves the pre-built `dist/`, so **rebuild (`pnpm
build`) after every source edit** or the test runs stale bytes. See
[[desktop-edge-grab-turn]], [[hifth-app-identity]].
20 changes: 20 additions & 0 deletions .claude/memory/playwright-webkit-missing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: playwright-webkit-missing
description: "The e2e \"iphone\" project runs on WebKit, which is not installed on this laptop — every iphone test fails at browser launch, not in the app; run desktop/android/golden (Chromium) and ask before installing"
metadata:
node_type: memory
type: project
originSessionId: c8c77742-fa0c-48ea-9c25-4e720245832a
modified: 2026-09-02T00:04:00.172Z
---

On 2026-09-01 the full `npx playwright test` run failed every `[iphone]` test with
`browserType.launch: Executable doesn't exist at ~/Library/Caches/ms-playwright/webkit-2311/pw_run.sh`.
The `desktop`, `android` (Pixel 7) and `golden` projects are Chromium and run fine.

**Why:** WebKit was never installed via `npx playwright install webkit` on this machine. Installing it
is a download from Playwright's CDN, which needs the user's explicit go-ahead first.

**How to apply:** Verify a change with `--project desktop --project android --project golden`. Report
the iphone project as *not run*, never as failing. Offer the install as a one-line ask rather than
starting it. See [[spa-hash-nav-no-reload]] for the rest of the verification recipe.
26 changes: 26 additions & 0 deletions .claude/memory/spa-hash-nav-no-reload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: spa-hash-nav-no-reload
description: "Navigating the dev app to a hash-only URL does not reload the SPA — the DOM keeps the old bundle even after a Vite restart; force location.reload() before concluding a change \"did not render\""
metadata:
node_type: memory
type: feedback
originSessionId: c8c77742-fa0c-48ea-9c25-4e720245832a
modified: 2026-09-01T23:47:36.435Z
---

When verifying a UI change on `localhost:5173`, navigating the tab to `http://localhost:5173/#/...`
(a hash-only change) does **not** reload the document. The React tree keeps running the old
module graph. Restarting Vite and clearing `node_modules/.vite` does not help either, because
the browser never re-fetches the entry. The served source can be verified new (fetch the module
and grep it) while the DOM is still old.

**Why:** In this session the finished page-bar redesign (30 juz detents, page-icon handle) read as
"not rendering" for several turns — the DOM had zero detents while the served `PageSlider.tsx`
already contained them. One `location.reload()` showed everything. See also
[[desktop-zoom-strictmode-orphan]] for the sibling trap: a fault that shows only under the dev
server's StrictMode double-mount (5173) and not in the built app (4173).

**How to apply:** After editing, force a real reload (JS `location.reload()` or navigate to a
different path and back) before reading the DOM. If something reproduces on 5173 and not 4173,
say it is dev-only. Rebuild `dist` before e2e and free port 4173 first. The `run-app` skill
carries the recipe.
Loading
Loading