Skip to content

Release v0.1.0 - #206

Merged
BattermanZ merged 253 commits into
mainfrom
dev
Sep 28, 2026
Merged

BattermanZ merged 253 commits into
mainfrom
dev

Conversation

@BattermanZ

@BattermanZ BattermanZ commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Release v0.1.0, dev into main.

The first release. main holds only the "Initial commit", so this PR is the whole project. Full detail is in CHANGELOG.md.

Pre-merge checklist

  • Versions match. Cargo.toml, Cargo.lock, ui/package.json and ui/package-lock.json all say 0.1.0.
  • README is current. Its install commands pull :latest, so there is no tag to bump. It says how to get a health check with the Podman images.
  • CHANGELOG has a dated v0.1.0 entry. It is dated 2026-09-28. Nothing exists to upgrade from, so it records no upgrade action. It describes what 0.1.0 contains rather than what changed, so it has no fixed section. Going back to Recipes loses the search #191 landed after the entry was written, but it fixes behaviour no release had shipped yet, so it is not listed. The images were rebuilt for it instead (below).
  • The ADRs agree with the code. Going back to Recipes loses the search #191 added a note to ADR 0027 in the same commit: going back to Recipes is not leaving it.
  • Checks and tests pass on this head. See Verification.

Already published

The four Docker Hub tags are live, built from this branch's tree at b49d126: 0.1.0, latest, podman-0.1.0 and podman-latest, each amd64 and arm64. The Docker tags point at sha256:9e810b38a61b… and the Podman tags at sha256:ccecbee7eb84….

They replace an earlier build from 3401e97, which predates #191. The only shipped code that differs between the two trees is #191's change to the Recipes screen. The rest is docker-compose.yml, CONTEXT.md, the README and docs. Searching each binary for the keys the shelf keeps when you leave it finds them in the new image and not in the old one.

Both images started on an empty data folder, served the first-run page with a 200 and passed their health check. The Docker image was tested straight from Docker Hub, the Podman image from the same digest just before it was copied there.

The GitHub release for v0.1.0 is a draft and nothing is tagged yet. Its target is currently dev. It should point at main before it is published, so the tag lands on the merge commit.

Merge

Merge commit, not a squash. main is an ancestor of dev, so the merge commit's second parent is the dev tip and the two branches share history afterwards. A squash would put a commit on main that dev has never seen, and it would collapse 253 commits, with their (#NNN) ticket links, into one.

Verification

  • just check exits 0. That covers fmt, clippy, Prettier, ESLint, svelte-check, the freshness gates and the copy guard.
  • just test exits 0. It ran 707 Rust tests and 794 screen tests, none failing.
  • Docker and Podman image smoke tests, as described above.

Release statistics

Base main @ 0397541
Head dev @ b49d126
Commits 253
Diff 472 files, +233,230 lines, none removed
Issues named in commit subjects 183

After merge

  1. Fetch and confirm the merge commit is on main with the dev tip as its second parent. Push main so every remote carries it.
  2. Point the draft v0.1.0 release at main and publish it, which creates the tag.

🤖 Generated with Claude Code

https://claude.ai/code/session_011aJcQGaXqh6r8L9zZ3ddo8

BattermanZ and others added 30 commits August 20, 2026 11:28
Adds CONTEXT.md with the terms settled while resolving the one-core shape
question: Operation, Catalogue, Core, Door, Parity, Credential, Immediate
Operation, Job, Import, Export.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
…sue #8)

Guards the MCP/web parity guarantee: the RPC-shaped web API is deliberate,
and hand-writing REST routes would silently destroy it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
The written Ingredient Line is the truth; structure is an optional
Reading over it. Adds the recipe vocabulary to CONTEXT.md and records
the ingredient decision as ADR 0002.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
The database is the truth; the Markdown vault is an optional, per-user,
write-only published copy. Inverts Hatchdoor's ADR-01, which does not
transfer to a multi-user app holding accounts and cooking history.

Adds ADR 0003 and the Vault and Backup terms.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
…sue #16)

A recipe's identity is a Lineage: one id minted at creation, carried by
every copy anywhere, never changed and never joined. Editing a shared
recipe makes a new Version on your Branch of its Lineage. A Version is
one saved state named by a fingerprint of its own content, so the Branch
Point between two Branches is computed rather than declared. Kamosu holds
divergence and never merges.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Adds ADR 0005 and the Attempt / As Cooked / Promotion vocabulary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Same Lineage, carrying a Language — translation as the most natural case
of never-merge rather than an exception to it. Each Version on a
translation records which Version of the source it translates, so
staleness is computed and travels.

ADR 0006; Language, Translation and Reading Language added to CONTEXT.md;
Branch, Food and Tag amended.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
ADR 0008 settles both kinds of link between recipes.

Composition is an ordinary Ingredient whose Reading names a Recipe
instead of a Food, so ADR 0002's polite degradation covers it for free
and no feature learns about a third list. It names a Lineage rather than
a Version, keeping the outer recipe's fingerprint stable; how much is
computed from the inner recipe's Yield and stored nowhere; it unfolds in
place and never splices its Steps into the outer method; it travels as a
passenger without changing its own Visibility; cycles are stopped at
unfold time rather than refused at save time.

Association is a Related Recipe: untyped, two-way, held against the
Lineage, and never travelling.

CONTEXT.md: Reading amended to target a Food or a Recipe; Component and
Related Recipe added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Cooking mode is an Attempt being lived rather than a thing of its own.
Held on the server so a cook survives phone to iPad; real from the moment
it starts, so never finishing it still counts as a cooking, with deleting
as the counterweight for a false start. Holds the Step, the ticked
Ingredients and the Yield being cooked to — a fact about one afternoon,
not an As Cooked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
The mirror doc told agents to create a token; one already existed at
~/.config/forgejo/kamosu.env, so a fresh session would mint a duplicate or
stall on a missing variable. Point AGENTS.local.md, the tracker doc and the
mirror doc at that path, with the sourcing line that keeps the secret out of
the transcript.

Also commits the mirror itself — the script and its doc were still untracked,
which would have left the tracker doc linking to a file not in the repo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
)

Settles the frontend stack and the offline posture.

Svelte 5 + SvelteKit (adapter-static) + TypeScript + Tailwind 4 +
Paraglide, chosen against the benchmark evidence: Web-Bench favours
React five models of six, but the gap is a capability gap that closes
with reasoning, SvelteBench is saturated at frontier, and the stale-
knowledge failure it measures is closable because Svelte ships docs
written for models. Every layer picked so the build fails rather than
the kitchen does.

Offline, the line is Promotion: write your own history, never the
recipe's. An offline edit queue is a merge, which ADR 0004 refuses;
the permitted writes were already last-one-wins under ADR 0010; and
fingerprint pinning plus append-only Versions mean offline Attempts
cannot conflict at all.

New term: Display Copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
There is no difference screen. Holding two Branches of a Lineage shows two
recipes, whole and cookable, with a switch between them — because everything
else in Kamosu already says a recipe is a thing you stand in, and because you
can cook what you are looking at.

The claim that this shape cannot show a deletion was wrong. A Ghost puts the
line on the page struck through, where it sits in the recipe that really has
it. A removal seen from one side and an addition seen from the other are the
same object, so one mechanism makes the page symmetric.

Also settled: one card per Lineage; carrying a change across is a gesture that
leaves an unsaved edit and has no "take all"; history is the Thread, from which
any Version can be read and cooked; a Version's name is the one thing about it
that can change later.

Adds ADR 0014, the terms Thread and Ghost, and amends Version.
Generated #30 — whether an Ingredient Line has an identity of its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Attribution across instances is a courtesy, not a proof. A Version records
the Hand of the Person who wrote it — a name plus a permanent id minted with
the account — and a Branch has an id of its own recording the Kitchen's Hand.
Both travel in a share bundle; neither is ever verified, and Kamosu says so
rather than implying a check it does not make.

Forces a structural finding not previously written down: the fingerprint
covers the recipe state alone, so the what-changed line is frozen metadata
rather than identity.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Records ADR 0016 and adds Unit, Cup Weight and Reading Measures to
CONTEXT.md; amends Reading and Step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Truncation refused: a share always carries the complete chain of Versions
back to the first, commentary included, and no Version may be withheld or
removed by anyone holding it — author or recipient. A missing Version now
has one meaning only, so #17 inherits a rule instead of a marker, and the
Branch Point is always computable.

The Share Link page shows the Thread beneath the recipe, making the promise
self-evidencing, and the share screen carries a standing line in the same
words every time.

Records ADR 0018; closes ADR 0004's open question; amends Version, Share
Link and Branch Point in CONTEXT.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Resolves the share bundle format: one format, not two. A Bundle is a
readable folder — a Markdown note per recipe with the Thread beneath it,
plus a .kamosu/ sidecar of complete past Versions — handed over as a
plain zip. The deferred Vault is the same folder with every recipe a
subject, written to a directory instead of zipped.

Also settles receiving (a Branch has one Kitchen writing it, so a Copy
happens at the moment of change, not of receipt) and the damaged case
(refuse the history, offer the words, drop the Lineage id).

A second decision fell out and sharpens ADR 0004: a Reading is Kamosu's
reading, not the recipe. The fingerprint covers the words and the
Photographs alone, so a parser disagreement can never report two people
as diverged. That freed what #20 was waiting on — a Food travels as its
names, all of them, no id.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD2qPHMD8sc8AZG4Cp4ABx
Records ADR 0022 and the glossary terms for how an arriving Food meets
the instance's own Food list: silent conservative matching by name in
the same Language, a collision landing as a new Food plus a Merge
Suggestion, accretion of unknown names on a clean match, one rule at
every door, and an irreversible Operator Merge that states its blast
radius first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CqVogK756vrRwwYXPAjGB7
…ssue #25)

Splits the feature at the line ADR 0009 cares about: the choosing is
stored, the rows are computed. A Person's list, not a Kitchen's — the
recommendation was vetoed and the veto was right, since the way out is
a Shortcut into one person's Notes. Adds ADR 0024 and the Shopping
List, Shopping Row and Loose Item terms.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LefyPutHzGcRrRuCKsned
An import leaves nothing on the recipe — no foreign id on the Branch,
no mark on a line Kamosu had to assemble. What it remembers it keeps
itself, in a ledger of foreign id against Lineage, so re-importing
matches instead of doubling the library and the whole thing is
deletable the day Crouton is gone. Three of the ticket's questions
dissolved rather than resolving: with no mark there is nothing to
un-mark and nothing to carry in a Bundle. Adds ADR 0025 and the
Import Report term; amends Import and Passenger.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LefyPutHzGcRrRuCKsned
…29)

Answering "which library" required collapsing Visibility a level up: a
recipe is seen by its Kitchen, or by anyone with its Share Link. Everyone
here and Chosen are removed, retiring the term Visibility and finishing
the collapse #11 claimed to have made.

Recipes is then one merged shelf of what your Kitchens hold, alphabetical,
one card per Lineage, with filters that never stick. Search blends words
and meaning, reaches your own Attempts alone, quotes what matched, and
offers a way out when nothing is close. A recipe with no photograph is
led by a Cover generated from its Lineage id.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LefyPutHzGcRrRuCKsned
Deployment, packaging and upgrades. The image is one executable with the
frontend compiled in, one /data mount, and no model at all — Meaning Search
is optional and conditioned on the Operator accepting Gemma's terms, which
dissolves the licence problem rather than modelling around it.

Upgrades go forward only: SQLite rolls schema and data back together, so a
failed migration means nothing happened, and the pre-upgrade Snapshot is the
database alone because a Photograph cannot change.

ADR 0028, ADR 0029, ADR 0030. Meaning Search and Snapshot added to
CONTEXT.md; Backup amended with where it goes and that Kamosu never sends it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LefyPutHzGcRrRuCKsned
BattermanZ and others added 29 commits September 27, 2026 16:14
An Ingredient Line with a standalone or / ou / o (or and/or, et/ou,
y/o) offers the cook two things to buy. It now keeps its amount and
Unit and names no Food, at any length. Before, a short one became an
invented Food such as "butter or oil", and a long one fell past the
six-word limit. The first alternative alone is no better, because the
two usually share their last word: "soft or silken tofu" is not tofu
called "soft". An "or" after an elided article is French for gold and
stays in the name.

"Thick bellota steak burgers from the market" stays unread on purpose,
since nothing marks "from the market" as an aside. The comment on
LONGEST_FOOD_NAME and ADR 0036 now name both exceptions instead of
calling everything past six words prose.

The corpus test scores the named share over lines offering no choice,
and fails if any choice line names a Food. 21 of 863 corpus lines
changed, each from an "X or Y" Food to none.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Before a new instance is set up, a hostile web page opened on the same
network can do the setup itself through DNS rebinding. Aurélien chose to
accept that and say so rather than add a setup code: the page cannot sign
in with what it made, the owner sees "Welcome back" instead of "Set up
Kamosu", and emptying /data undoes it.

The line goes into ADR 0034, the README and the Settings list in en, fr
and es, since the honest-list test requires every item in all of them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
A Backup reported "0 of 1" when it started and never again, so get_job
went on saying 0 of 1 after it had finished. I took the general route
rather than patching take_backup: when a Job completes having declared
a total, finish() now sets done to that total and the message to
"finished". No Job can forget it that way. Reading the ingredient lines
had the same slip, stopping at N-1 of N, and is fixed by the same rule.

A failed Job keeps its count, since how far it got is worth knowing. A
Job that never gave a total (the web link importer) is not handed one.
Imports and Sheets already ended on total of total with "finished", so
they read the same as before.

probe_job, the test-only demonstration Job, gains stop_after and
without_total so the tests can reach both of those cases.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
…app (#156)

On the share screen, "Save it" pointed the page at /api/bundles/<id>.
In the app installed on an iPhone that address stays inside the app's
own window, which has no Share or Save to Files. It is the trap #149
fixed for the Sheet, and the fix is the same (option A, Aurélien):
there the first tap fetches the zip ("Getting the file…"), the button
then reads "Share the file", and the second tap hands it to the share
sheet. Closing the share sheet keeps the file for another try, and
leaving the screen drops it. A Safari tab, a computer, Android, and an
iPhone that will not share a zip keep the plain navigation.

#149's pieces are shared rather than copied. sheet.ts becomes files.ts:
one fetcher for a PDF or a zip, sharesFiles for the installed-on-Apple
check, and shareFile for the share call. The Sheet uses all three.

Tried on Aurélien's iPhone from the dev URL. canShare said yes to a
zip, and Save to Files kept it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
)

A Kitchen is a group of people cooking from each other's Cookbooks, so
"this kitchen's Operator" taught the wrong meaning of the word in the
first sentence an Operator reads. Aurélien chose "this Kamosu's
Operator" (de ce Kamosu, de este Kamosu).

The same line now also writes Opérateur with a capital and Operador in
the masculine, as every other French and Spanish message already does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
The reader named Foods like "to taste sea salt" and "boneless". Now:

- "To taste" (and au goût, selon le goût, à volonté, al gusto, a gusto)
  is dropped from either end of a line before anything is read, so a
  line reads exactly as it would without it. "salt and pepper to taste"
  names salt and pepper, the choice made on the ticket.
- A Food made only of describing words (minced, divided, boneless,
  haché, picado, ...) is no Food. Where a comma cut such a run off from
  its food, the name carries on past it into a clause that opens with
  another describing word: "boneless, skinless chicken thighs".
  "4 cloves, minced" and "1 lb boneless, cut into pieces" name nothing.
- The corpus check #160 added now also lists any Food that is only
  describing words or still holds a to-taste phrase.

Existing Readings do not re-read themselves (#166).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
A re-save inside the collapse window used to write the call's name and
change_note straight over the Version being shaped, so a quick second
save that sent neither erased both. The collapse now keeps what the row
already holds unless the call sends a replacement.

An edit that changes nothing but brings a new name or change note writes
them onto the Version still being shaped, under the same conditions a
collapse uses. Past that point it is refused with a sentence saying why,
instead of being dropped. Sending what is already there stays a no-op.

The variation screen saved its draft and note in one call onto a head it
inherited, which the Core now refuses. It saves the draft first and sends
the note only when the draft made a Version for it to describe.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
)

A save carries an unchanged line's Reading forward, so a fix to the reader
never reached a line it had already misread. The Operator can now have the
whole library read again, as a Job.

- A Reading remembers whether a person set it (`by_hand`). set_reading
  sets it and a save carries it. Migration 41 guesses it for what is
  already there: a Component, or a Reading changed more than a second
  after its Version, plus such a correction carried onto later Versions.
- reread_ingredient_lines covers every Version a Branch holds, history
  too, so a misread Food can end up with nothing pointing at it and be
  deleted. It leaves hand-set Readings and Components alone and reports
  each changed line once per recipe, before and after, the Foods it
  emptied, and how many hand-set lines it kept.
- On the Operator page, "Read every line again" replaces "Read the unread
  lines" and shows the report (Aurélien's option B).
- ADR 0021: a re-read someone asks for, which reports its changes, is not
  a silent recomputation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
The reader named Foods like *petits poireaux* and *beurre froid*, worse than
the Readings already in the library, so a re-read (#166) would have made them
worse.

- SIZES lists every plural a French or Spanish size takes.
- SIZES_AFTER drops a size written after the food (*tomates moyennes*),
  leaving *entier* and *gros* alone: whole-wheat flour and coarse salt are
  products of their own.
- WARMTH drops how warm a food is at either end of its name, with a *very*
  in front of it (Aurélien's choice on #185). *hot* stays, as does every
  strength word such as *corsé*.
- A few names that open with such a word keep it: *petits pois*, *cold cuts*.
- A Step's amount names its Food the same way, so it still joins its line.
- The corpus test now fails on a Food that keeps a size or a temperature.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
`2-3 basil leaves` used to read as a Food called "2-3 basil leaves", and
`2 to 3 cloves garlic` as two of a Food called "to 3 cloves garlic". The
reader now takes a range as the amount, kept as written: two amounts
joined by a hyphen, a dash, or to/or/ou/o/a/à with nothing between them.
The Unit and Food after it read as they do after any other amount.

A range is worth nothing, as Aurélien chose: it is never scaled,
converted or added up, so its shopping row names the Food with no
number. ADR 0016 now says so, and ADR 0036 notes that `1 or 2 onions`
is a range rather than a #148 choice. The Crouton corpus's three range
lines now read back their Unit, so that test demands all 351.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
…it (#177)

Every entry from search_recipes, home_shelves and the Tag filter now
carries cookbook, writes and mine, computed the way get_recipe computes
them. A caller can read the shelf once and know which recipes it may
change, instead of opening each one or collecting refusals.

writes alone is not enough: a recipe that arrived in your own Cookbook
has writes false (an edit starts your own copy) but mine true (tagging
is allowed). The Catalogue's descriptions say which question each
answers, and that the entry's mine is not the search_recipes mine
filter, which stays a history.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Claude Code shows a model a tool's name, description and inputs, and
drops any outputSchema. So the MCP listing now writes each Operation's
answer-field meanings, taken from the Catalogue's output schema, into
the tool's description. Each distinct meaning appears once, beside the
shortest path to a field that carries it.

Claude Code also cuts a description at 2,048 characters. A tool that
would pass that lists what fits and says how many meanings it left out.
Only the four import Jobs need to. The listing grows from 85 KB to
114 KB.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
#183)

A save whose words match the Branch head used to drop the
translates_version_id it named, so a Translation whose words needed no
change after a source fix stayed "1 version behind" for good.

The identical-content shortcut now moves the head row's pointer to the
named source Version. It writes no Version and appends no history row,
and the fingerprint does not change. The pointer moves at any time,
being shaped or not, but only on a Branch the caller's Cookbook writes.
A name or change note that #165 refuses in the same call refuses the
whole call. The name, the note and the pointer go in one UPDATE.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
… it (#184)

A Step linked an Ingredient Line only when its text held the Reading's
whole target, so "the syrup" missed maple syrup, "le cacao" missed cacao
en poudre, and "ginger and garlic" missed lines read as "fresh ginger"
and "garlic clove". Any run of the name's words now counts, provided it
holds a word a cook would call the food by: never only "the", "fresh",
"crispy", a unit or a form word like "paste" or "cubes".

Aurélien's three choices on #184 settle the rest:
- a short word two different foods share links both;
- one food on two lines links the first no earlier Step used;
- a mention after until / jusqu'à / hasta, to the next comma or full
  stop, is not linked, unless an amount follows ("jusqu'à 100 ml").

A match on the whole name always stands, so every link the old rule made
survives except those two exceptions. Over the 90-recipe dev library
links went from 541 to 1008; the 21 dropped are 15 duplicated foods and
6 mentions after "until". The #150 in-Step amount join is untouched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
"1 good pinch salt" was read as one of a Food called "good pinch salt",
because the reader skipped sizes before a Unit but not words like "good",
"heaping", "bonne" or "buena". A new PRAISE list is skipped straight before
a Unit, and at the start of a Food that follows a measure, so "200 g good
dark chocolate" is dark chocolate. Anywhere else the word stays, which was
Aurélien's choice: "poulet à la bonne femme" keeps its name. "glug" is now
a Unit.

The Unit search is one helper, unit_at, called with and without the praise.
On the 86-recipe corpus only the two "good-quality" lines read differently.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
…them (#188)

Recipe.svelte was 2,542 lines with a 1,130-line script. Each concern that
stands on its own now lives beside it: the Sheet, deleting, the phone's
kept copy, Components unfolding, correcting a Reading, the Divergence's
marks, and the smaller rows. State that must outlive the page being
swapped out for writing sits in .svelte.ts modules made once by the
screen; the rest is in child components. Nothing a person sees or does
changes, and no screen spec's assertions moved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
#189)

src/core/sharing.rs held three features in 2,494 lines. It is now
share_links.rs, sheets.rs and carrying_bundles.rs, one area per file like
the rest of the Core. carrying_bundles takes a verb because crate::bundles
is already the zip format.

A pure move: no function body changed. person_name, called by all three,
moved to shared.rs as pub(super). ensure_link_live went with the Sheet,
its only caller.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Hermes lists a tool it has not loaded as its name plus the first sentence
of its description, cut at 60 characters. 83 of the 120 Operations opened
with a longer sentence, so the line an agent picks tools from ended
mid-thought.

Each of those summaries now opens with a sentence of at most 60
characters that says what the Operation does. Whatever the old opening
said moves into the sentences after it, so no summary loses anything.
The test-only probe_job gets the same treatment.

every_tools_first_sentence_fits_a_short_listing applies Hermes's own
sentence rule (from _short_desc in tools/tool_search_catalog.py) to the
MCP door's tools/list and fails the build on any line it would cut.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Kamosu goes public under the AGPL-3.0, with a ready-made image on
Docker Hub. This adds what a stranger meets first:

- LICENSE (AGPL-3.0 only), named in Cargo.toml and ui/package.json,
  whose version moves from 0.0.1 to 0.1.0 to match the server.
- A rewritten README: what Kamosu is and is not, installing from the
  published image, why HTTPS is required to sign in, connecting an
  agent, where the data lives, and troubleshooting from the symptom.
  Four screenshots from the dev library sit under the introduction.
- SECURITY.md: supported versions, how to report a vulnerability, and
  the list of what Kamosu does and does not defend, moved out of the
  README. tests/defences.rs now reads it there, and ADR 0034 records
  the move.
- CHANGELOG.md with the v0.1.0 entry, dated when the tag is made.
- THIRD_PARTY_NOTICES.md: the two bundled Zen fonts, and EmbeddingGemma
  as downloaded on request and never shipped.
- docker-compose.yml for the one-folder, one-port install.
- A GitHub check that refuses a pull request into main from any
  branch but dev.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Review of the first rewrite:

- The wordmark (the seal beside "Kamosu" in Zen Old Mincho, light and
  dark) and a row of badges open the page, as Hatchdoor's does.
- Kitchens and Cookbooks get a section of their own, and the pitch
  leads with them: who sees a recipe, who changes it, and what happens
  on leaving.
- Meaning Search is among the highlights.
- The screenshots sit in a captioned table, and move to docs/readme/.
- Docker and Podman are shown side by side rather than Podman as an
  aside.
- Connecting an agent covers Claude Code, Codex (which needs its
  2026-07-28 switch, kept to a profile) and Hermes.
- The ingredient-line highlight is gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
The dev hostname vite allows moves out of vite.config.ts into
KAMOSU_DEV_HOST in the gitignored repo-root .env; unset, vite accepts
only its defaults. ADR 0037 stops naming the dev URL.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
…not shown

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
…e place in the list (#191)

Opening a result destroyed the shelf, and going back built a new one from
nothing. The route now exports SvelteKit's `snapshot`: the words, the
Kitchen or Mine filter, the Tag and the scroll are kept as the reader
leaves and restored only on back and forward, so the tab bar and a Tag
chip still start the whole shelf.

The shelf scrolls itself once the restored answer and the chip rows have
drawn, since SvelteKit's own scroll lands on a page one "Loading…" tall.
A failed search cancels that scroll, a Kitchen filter with no chip left
to turn it off falls back to All, and a kept value that is not a shelf
is ignored. ADR 0027 carries a note: going back is not leaving.

Closes #191

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QVfUHRQ2wJoKPWTS6SzaV
… role

ADR 0044 records the grilling behind #192: one PWA on tablets and
computers, a wide layout that needs both width and height, device roles
deciding which screens get real design, a sidebar, tidying screens side
by side, sheets as centred windows, and a back arrow for the iPad.
The glossary now says tablet where it said iPad.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aJcQGaXqh6r8L9zZ3ddo8
@BattermanZ
BattermanZ merged commit 0a2bf34 into main Sep 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant