Blog ยท release
Latch 0.19.0
Your AI Handler in the room, proof on demand, a day on the calendar, and the demo. No migration.
- Talk to your AI Handler from the chat bot's room:
!ask(#14). A fifth switch on Consent โ The chat bot, Talk to your AI Handler here, off to start with. Switched on,!ask <anything>is a turn of the AI page's own conversation โ the same history, the same grants (your reflections, your kit, the clock), the same budget โ and the answer is posted in the room as the model wrote it, as a message that rings. That is the player's call, not the room's rule: the model is your own, and so is letting its words into a room a homeserver keeps. The game still holds: chat is never shown a hidden lock's clock and an answer about time is masked; an answer written knowing what you have since paused is not posted (it waits on the AI page); a switch turned off while the model thought stops it. The bot asks on a thread of its own, one question at a time, so a!pausebehind a question is acted on at once; it logs that it ran!ask, never what was asked or answered. New routePOST /api/v1/bot/ask(bot token only, in its scope only while the switch is on);askflag on/bot/today; the AI page marks turns said in the room. No migration (ai_log.targetsays where a chat turn was said). - Demo mode: an open, throwaway playground on a hosted instance (#36).
LATCH_DEMO=1on top ofLATCH_HOSTED=1. A visitor gets a door (/demo): three plain rules, an I am 18 or over tick and one button, which makes them a kennel of their own โ a random handle, no password, ~150 days of invented play ending now โ signed in by cookie, with a banner saying when it goes. DeletedLATCH_DEMO_HOURS(24) after it was made and on every restart; capped atLATCH_DEMO_MAXlive andLATCH_DEMO_PER_HOURnew. Login, register and feedback are gone; every upload is refused at the middleware before any route reads it; calendar feeds are never fetched. It refuses to start while the box holds any invited profile โ the door is open to anyone, and a tester's journal does not belong behind it. The demo seeder moved into the app (latch/demoseed.py, reentrant, generated photos cached on the volume; the CLI attools/demo/seed_demo.pyis unchanged), so a kennel takes about a second. Every choice and its reason:docs/hosted.md#demo-mode. - Challenges: your Handler asks for proof at a moment you did not choose (#29). Off until
you set about N a day. While you are locked, inside your asking hours, a challenge arrives โ
a word and a number (OTTER 47) and ten minutes to send a photo with it in frame and your
cage in the shot. The word is the control, not the camera: no browser can be made to use only
its camera, and no old photo can carry a word minted minutes ago. Answer โ a verification photo
captioned with the word, +10 XP, proof ยท OTTER 47 on its stamped export. Miss โ the
missed-reflection penalty, stamped at the deadline, undoable; ๐ I can't right now costs the
same minutes and the lighter Mischief mark (Owned up, not Ghosted). None is asked while you are
paused, out for hygiene, on a recovery day or already answering one; a pause or a hygiene
window during one holds the penalty back, out loud. Random in the strict sense โ the next one
never depends on the last. New reason code
challenge(it outranks every duty),challengeon/api/v1/handler/today,POST /api/v1/handler/challenge(+/decline) for a Shortcut, and the chat bot posts the ask as a ringing message while reminders are on. No migration. Every choice and its reason:docs/handler.md#challenges-proof-on-demand. - The calendar opens a day (#44). Click a day and a sheet lists what happened, grouped by
activity, in time order, each line linked to its record; click a dot and it opens at that
activity. The sheet is a page of its own,
/calendar/day/YYYY-MM-DD(it works with no script and can be linked), and the dialog is that page fetched, so the two cannot disagree. It counts what the dot counts โ the one matcher, with voids read as of the instant the grid reads them. A reflection has a permalink at last,/handler/entry/{id}(an HTML page behind your login, never the API). The grid is calmer: four dots and+n, legend names that hide an activity (remembered per browser), the selected day outlined, arrow keys to move. Its rows are the Log's own, so a hidden lock keeps its rules there. Modules say where their records live through a newModule.record_url, asked softly. - The release guard reads what git holds, never the working copy (#37).
pack.py --checkused to read each tracked file off the disk. The mirror publishes blobs: a fingerprint staged under a clean working file went out with the next commit, and one edited out on disk but never staged shipped anyway. Every path is now read from the index (plus any blob HEAD's tree still holds that the index does not โ a push sends HEAD), a tracked symlink or submodule is refused outright, and the tarball is built from the very blobs the scan read, each entry stamped with the commit time instead of the build machine's file times. - A share card can say your standing โ only if you put it there (#51). A new opt-in token,
standing(๐พ Good boy ยท ๐ Watched ยท ๐ฆฎ Short leash ยท ๐ In the doghouse), in no default list. The word alone: never a mark, its kind, a count or a lapse's prose. As-of the photo like every token โ the Mischief ledger now reads voids contemporaneously, so a mark undone a week later does not rewrite the standing a photo was stamped with.docs/mischief.md's never on a share card is now only if you put it there. - The release guard reads the history, not just the files.
pack.py --checknow refuses when any commit reachable from HEAD or a tag is authored or committed by anyone but the project's pseudonym, when an annotated tag is tagged by anyone else, when an identity trailer (Co-Authored-By:,Signed-off-by:โฆ) is neither the pseudonym nor the AI co-author, or when a commit or tag message carries an estate fingerprint โ the mirror publishes all of these the moment they are pushed, and the scan could not see them. Commits already published under another identity are excused by full SHA (KNOWN_PUBLISHED_EXCEPTIONS), each from the one check it fails, until a decision on rewriting them; any new one fails. - The chat bot answers a near-miss.
statuswithout the!used to get silence, and a silent bot reads as a broken one. Now a plain message gets Did you mean !status? (also for/today,Pause?,๏ผpause), and small talk gets the way to!helpโ at most once a quarter hour, and not right after an answer. Nothing plain is ever acted on, logged or said back.