Visitar URL original
GitHub - kaanisthatyou/redstring: A dossier-styled gym for observation, people-reading and memory. Offline, local-only, no account, no network at runtime. · GitHub
Skip to content
kaanisthatyouPublic

About

A dossier-styled gym for observation, people-reading and memory. Offline, local-only, no account, no network at runtime.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RED STRING — start here

A dossier-styled gym for observation, people-reading and memory. Offline, local-only, no account, no network at runtime.

The goal changed. It used to be "exercise the memory muscle". It is now:

To become Patrick Jane. Observation, people-reading, memory — for a regular human, with no fictional gifts, ten minutes a day.

The association loop that was the whole app is now one track of three. Read PIVOT.md before anything else: it is the decision record for the change, and where it disagrees with SPEC.md, it is newer.

Name is a working title, not confirmed.


Read in this order

File What it holds
PIVOT.md Start here. What the goal is now, the twelve rulings that settled it, the nine drills, the honest-measurement contract, and what is built versus what is next.
MENTALIST.md The skill itself, taken apart — what cold reading actually is, what baselining actually is, what a beginner who learns half of it becomes — and the drills that train each piece in a browser with no camera. Also the Reader's line bank.
OBJECTS.md The object model. Own / Derive / Freeze, the card-as-role design, and the migration. This is the answer to "a note object should have all of its contents stored with it".
ALCHEMY.md Motion and material. The five signature animations, the token vocabulary, the components, and an unsparing audit of what is still flat.
SPEC.md The original design. Non-negotiables, anti-features, drills, battle modes, handlers, architecture, risks, decisions. Start at §2 — the constraints explain everything downstream. Superseded on the goal by PIVOT.md; still authoritative on everything it does not touch.
IMPLEMENTATION.md Build plan. Stack, invariants, repo layout, data model, M0 broken to 34 sized tasks, exit criteria.
FRONTEND-TOOLKIT.md Study reference for the corkboard UI — physics, spatial indexing, rope rendering, paper texture, named libraries. Reference material, not decisions.
REVAMP.md The M0 verdict and what follows from it. Read this before building anything else — M0 shipped correct and bare, and the shell is the current work.
PINBOARD.md The art direction of the tactile rebuild — the thesis, the token contract, the case-status colour system, materials, fasteners, the shadow ladder, the motion vocabulary. Where this and a component disagree, this is right. It also records which of SPEC.md's restraint rules were dropped, and on whose authority.
COMPONENTS.md Where finished UI can be taken from (Uiverse, React Bits, 21st.dev, Magic UI), what each licence allows, and why the rule is take behaviour, not skins.

Running it

npm install
npm run dev            # http://localhost:5173 — this is the one you want
npm run check          # typecheck + lint + unit tests
npm run test:e2e       # Playwright, against a real production build

Desktop on localhost is the current way to use it. localhost counts as a secure origin, so offline and the service worker behave normally there.

Phone comes after a deploy, not before. Serving to a phone over a plain http:// LAN address means no service worker, which means no offline and no install prompt — worth skipping in favour of doing it properly on real HTTPS later.

The Consultant tab (not in this repository)

One tab talks to a network, and it is the only one. /consultant is a conversation with a persona answered by a hosted model on NVIDIA NIM. It wears a character who is not ours to redistribute, so the tab is a private-build feature in the literal sense: src/core/consultant/, src/features/consultant/, content/consultant/ and public/consultant/ are ignored by git and are not in this repository. What is here is the seam it plugs into and the proxy it uses, both of which are general.

  • src/app/privateFeatures.ts is the seam. Any src/features/<x>/private.tsx that exports a feature gets a route and a tab; when the folder is absent the glob resolves to nothing and the shell has one fewer tab. No tracked file imports a private folder or names one, so a clone builds green and never mentions the gap.
  • Everything below describes how that tab is wired in the tree it lives in, and how to point the proxy at your own persona if you want one.
cp .env.example .env.local     # then paste your nvapi-… key into NIM_API_KEY
npm run dev
  • The key is read by the dev/preview server, never by the browser. vite.config.ts proxies /nim/* to integrate.api.nvidia.com/v1/* and adds the Authorization header on the way out, which also settles CORS. NIM_API_KEY is deliberately not a VITE_ variable, because anything with that prefix is inlined into the bundle.
  • With no key the tab says so and the composer is disabled, rather than swallowing your first message and blaming the network.
  • VITE_NIM_MODEL picks the model. Default nvidia/nemotron-3-ultra-550b-a55b, chosen by asking a canon question with a checkable answer — who is Craig O'Laughlin? — rather than by speed or size. It is correct and specific; meta/llama-3.3-70b-instruct was confidently wrong and took 35s to say so, openai/gpt-oss-120b invented a career in the state senate, and the small fast models assemble an answer out of whichever character names they do know. A fast wrong answer is worse than a slow right one here, which is the opposite of the first conclusion this file recorded. Two traps if you override: reasoning models spend their whole budget before speaking (and nemotron-3-super-120b-a12b leaks its reasoning straight into the reply), and on the free tier latency belongs to the endpoint rather than the model — the same id measured 0.5s and 40s an hour apart. That variance is why there is a 90s deadline and a clock on the waiting state.
  • The persona is a JSON prompt under content/consultant/, because the prompt is the only part of this that cannot be wrong. The model choice is what stops it inventing the things the prompt does not cover, and it is told to admit a blank rather than fill it.
  • A statically served dist/ has no proxy and therefore no Consultant. That is the correct failure: every other surface works with the network unplugged.
  • The transcript lives in localStorage, not in Dexie — so it is outside the backup file, outside the export, and outside every privacy guarantee the database makes, because it is the one thing here that has been over a wire. Burning it is a button on the tab itself.
  • Nothing from your board, file or address book is ever in the request. There is a test beside the screen that fails if that changes — in the tree that has it.

Where the pivot stands

Everything below is built, typechecked, linted and tested — 830 unit tests across 42 files, 56 end-to-end.

The engines are done. All nine drills have a finished, seeded, unit-tested generator and grader behind them, each with its own validated content bank:

Module What it is
core/scene The §5 scene graph: facts → prose → questions, with answerability guaranteed by construction. Ten rooms, 170 objects, 124 names.
core/faces Composite sketches as parameter vectors. Difficulty is a target distance, not a floor — the first version was a floor and a test proved that is not a difficulty knob at all.
core/reception Cohorts, the re-accessorised mingle, near-neighbour distractors, and the feature-space blind-spot diagnostic.
core/deception The Interview Room. Veracity decided before rendering; demeanour noise dosed so demeanour-reading scores below chance.
core/coldread The Sitting. score = truth × commitment, so a true Barnum scores exactly zero.
core/rhetoric The Phrasebook. Grades form, not fact.
core/hypothesis The Second Explanation. Two scores, never blended, and overreach is its own field.
core/stakeout A person's reference and deviations from it, with false positives on clean days counted separately, forever.
core/stats/calibration Wilson intervals, Brier, the Murphy decomposition. Every function refuses rather than guesses below n = 20.

The object model is done. Cards are a role rather than a table; a note owns its text, its photograph, its position and its appearance in one row; boardNodes, sync.ts and preserve.ts are gone. Fourteen migration tests prove real v7 data survives to v11 with every card exactly where the user left it.

Three surfaces are built: The Room, The Reception, and The Address Book. Five more drills have engines waiting for screens — see PIVOT.md §6.


State (of the original M0)

  • Design: settled. Ten boundary decisions closed in SPEC.md §15.
  • Code: M0 is complete — every task A1–F6 in IMPLEMENTATION.md §5.1. The loop runs: forge → 24h → review → grade → your own image handed back → re-forge if it died. The board is pinned, strung and draggable. 183 unit tests, 14 end-to-end.
  • Verdict, given immediately: the mechanism works and the product around it does not exist — no landing, no navigation to speak of, no settings, no personnel file, no personality, no motion. See REVAMP.md. The plan's "no fiction in M0" was read as "no design", which is not what SPEC.md §3.1 says.
  • The revamp is built. Every surface in REVAMP.md §3: the board is the home of the app and you pin your own material to it, real navigation, settings wired to the core, the personnel file, both drills rebuilt, self-hosted typewriter faces, simulated strings and diegetic sound. Matter.js was evaluated and rejected (§3.9).
  • Next: the 14-day self-test, on this app rather than the M0 one.

What the board turned out to be

A card is a cue, not a link. Two links that both touch MERCURY share one card, so the strings genuinely meet there — that is what makes it a wall rather than a list of pairs, and it is the reading of SPEC.md §3 ("the app visualises the association graph") that the data model quietly allowed. Positions are seeded from the link id so the wall is identical on every open; dragging overrides them into a boardNodes table keyed by cue.

Three defects worth recording, because none were findable by reading the code:

  • the 300ms commit ran ~1400ms, because performance.now() and the rAF timestamp do not share a time origin everywhere
  • the string layer drew perfectly into a 300×150 canvas in the corner — a <canvas> is a replaced element, so inset: 0 gives it its intrinsic size rather than filling the parent
  • the board's pointer capture retargeted the click that followed, making its own controls unclickable

All three were caught by driving a real browser and reading pixels back out. There is now a regression test for each.

Decisions taken during the build

Recorded here because they are not derivable from the docs:

  1. oxlint instead of ESLint — the current Vite template ships it. The core/ import boundary is enforced by lint, not by discipline.
  2. A re-forge creates a new link record pointing back at the dead one, rather than resetting it in place. The board keeps the failure visible, and "survival at first review" stays one honest number per encoding attempt.
  3. partial is split in two: a failure in the metric, a survivor in the schedule.
  4. A second pair source (Targeted) was added. §6's exit criteria require two sources; the task list only ever built one, so the criterion was unsatisfiable as written.
  5. NSFW toggle: off with one-time opt-in, per §16.3. Confirmed by the user.

Still awaiting sign-off (both blocked only at M1): handler authoring method (§16.1) and Dispatcher VO timing (§16.1b).


Running the self-test

The next step is not code. IMPLEMENTATION.md §6 is the source of truth; this is what doing it actually looks like.

Daily: open it and forge. Use both sources — Random for volume, Targeted for things you genuinely need (a door code, someone's name). Review whatever is due. Take an export from Stats in the first few days, because M1 will change the schema.

The gate is honest self-observation, not a metric. It comes down to one question: did you keep opening it without making yourself? Required before declaring M0 passed: 14 consecutive days of unforced use, ≥150 links across at least two sources, enough reviews for survival @24h to mean something, and an export taken.

Then answer these in writing:

  1. Does forging feel good, or like homework?
  2. Does the reveal-your-own-image moment land? (If this isn't delightful, a core assumption is wrong.)
  3. Do you look forward to the review, or dread it?
  4. Did you open the board unprompted?
  5. Did survival @24h move — and did you care that it moved?

Failure signals, worth catching honestly rather than explaining away: skipping days and not minding · writing lazy images to clear the queue (the mechanic is being gamed, so it isn't intrinsically rewarding) · never opening the board voluntarily (the whole visual thesis in §3 is wrong) · wanting to use it for something real and finding you can't (not failure — that's §4.1 arriving early).

Keep a plain text journal. Do not add analytics to answer these — it is a sample of one, and instrumenting it is procrastination.


Language, keys, and what is still open

Three things the design docs commit to that are easy to lose track of once there is code. This section says where each one actually stands in the repo, not in the plan.

Turkish (SPEC.md §15.1)

In scope as a shipping language, and the cheap half is already done:

  • src/core/i18n/ holds the catalogue, t(), and a tr slot that is deliberately empty and falls back to English. Every user-facing string in the app already has a key — oxlint will not catch a hardcoded one, so this is a review habit, not a guarantee.
  • lower() / upper() use toLocaleLowerCase(locale). Naive toLowerCase() maps I→i, but Turkish needs I→ı and İ→i; this is the bug that silently breaks the app for half its users.
  • Not done, and not needed yet: fonts are system fallbacks, so nothing is subsetted and the ı İ ğ Ğ ş Ş ç Ç ö Ö ü Ü check has not been run against a real display face.
  • Not done by design: suffix-tolerant answer matching. M0 grading is self-report with no text input at all, which is what defers the entire agglutinative-matching problem to M2.

The NIM keys (SPEC.md §13)

No key exists anywhere in this repo, and the product does not touch a network. That is the whole point: LLM inference is a build-time content compiler, never a runtime dependency. The banks in content/ are hand-authored JSON, shipped static. When the content engine arrives in M2, it runs as a build step that writes more static JSON — if the app ever breaks because a key died, the key was used wrong.

The Consultant tab is the one stated exception, and it is a private-build feature rather than part of the product. It uses a licensed character, which SPEC.md §10.1 rules out, and it calls a model at runtime, which §2 rules out. It exists because it was asked for on an unpublished build. What keeps it from eroding either rule is that it is additive, never load-bearing: no other surface imports core/consultant, nothing it produces is written into the database, no drill or handler line depends on it, and with the key removed every other tab behaves exactly as it did before. If this app is ever published, deleting the tab is deleting four directories and three lines of wiring.

Open, and blocking nothing in M0

Both need sign-off before M1 copy is written, and neither affects the self-test:

  • §16.1 — handler authoring. Recommendation on the table: hand-written line banks with templated continuity slots, bulk-expanded at build time. Keeps the voice consistent and survives §2's key-independence rule. With four handlers this is four banks, so M1 ships the Consultant only.
  • §16.1b — Dispatcher VO. Recommendation: defer. Ship diegetic sound (§10.5) first and decide whether atmosphere already lands without a performer. Easy to add later, impossible to unspend.
  • §16.2 — Sense Lab audio sourcing. Not needed until M6.
  • §16.4 — monetization. Deliberately deferred; the one boundary set is that drills are never paywalled.

Settled during the build and recorded above: §16.3, the NSFW toggle, is off with a one-time opt-in — confirmed by the user, implemented, and covered by a test.


Things easy to get wrong

Learned across the design conversation. All are in the docs, but these are the ones that get broken by accident:

  • The first review interval is pinned at 24h regardless of scheduler. The headline stat depends on it. (IMPLEMENTATION.md invariant 7) Also true one layer up: the due queue puts first reviews ahead of everything, or a daily ceiling silently breaks the same guarantee.
  • Forged image text never leaves the device. Not a privacy nicety — a correctness constraint. If users think anything reads it, they self-censor and the method stops working. (SPEC.md §2.1) Local export deliberately does include it: a file on your own device is not the network.
  • Visible card count is the constraint, not total count. Total becomes irrelevant once culling exists. (SPEC.md §3.2)
  • Physics on the board, stillness in the drills. Ambient motion during recall is a defect, not a flourish. (SPEC.md §3.1) Enforced by the FullBleed primitive; the forge commit opts back in explicitly.
  • Difficulty is adjustable; measurement is not. (SPEC.md §4.2)
  • Never name or imitate the licensed characters. Archetypes only. Sherlock is the one exception. (SPEC.md §10.1)
  • The board is built last in M0 and deliberately does not scale. (IMPLEMENTATION.md §5.3)

Working notes

  • User is Turkish; Turkish is in scope as a shipping language, which drives the pluggable renderer seam and suffix-tolerant answer matching. (SPEC.md §15.1) The i18n seam and locale-aware casing exist from the first commit; the tr catalogue is empty.
  • User has NVIDIA NIM API access — used as a build-time content compiler, never a runtime dependency. (SPEC.md §13)
  • User prefers plain files in the repo, not in-Claude artifacts.
  • Mode of work so far: conversational design, opinions and pushback welcome, decisions written down as they settle.

Where spec and plan conflict, one of them is wrong — say which before writing code.

About

A dossier-styled gym for observation, people-reading and memory. Offline, local-only, no account, no network at runtime.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages