The Toolkit Grows Client Portals, and Two Bugs That Only a Browser Could Find
A filter that ate every keystroke, icons that were 130 bytes of text, and a page nothing linked to — all invisible to a green build.
The Lossless Group · ship log
6,611 commits across 45 repositories, Jan 2025 to Sept 2026 — 2.25M lines of code and 1.40M of prose, mostly one person and a rotating cast of frontier models. The marks are the days we wrote it down.
Lines by kind. Lockfiles and vendored trees are excluded — nobody wrote them. Proportions are measured from each repository's own history; totals follow GitHub's per-repo accounting, which does not agree with it. The marks on the strip's baseline are the 136 days that carried more than ten commits — 79% of everything ever committed here landed on one of them.
A filter that ate every keystroke, icons that were 130 bytes of text, and a page nothing linked to — all invisible to a green build.
A 13-slide talk now plays in the browser as newsprint, as classic Lossless dark, or as glowing gradient glass.
Any branch, any commit — the deck you just pushed is the deck at lossless-decks.vercel.app.
The monogram fallback did its job and is now out of a job — the real icons arrived, recovered from a sibling repo and checked byte for byte.
Seven members promised a screen reader arrow-key navigation and none delivered it. Closing that took six more primitives — and the first tests this federation has ever had.
The first talk has an outline, and the site now has a written pattern for turning outlines into browser decks.
Three sites got redesigned as three complete directions each — nine working variants, twenty-seven direction-and-mode combinations, all off one shared content layer per site. gth-site turns a docuseries funnel into a clinical publication, an anatomical atlas, or a consultation, with an upgrade cart whose tiers are strict supersets so it can show you a delta instead of a new cart. dominocielo-site holds a 300-word family-office page at 300 words and makes brevity read as confidence. multiphasic-site got the README treatment its CEO's interest deserved. The pattern repeated three times, so it stopped being improvisation and became `generate-site-design-variants` — a 257-line skill, plus a `/design-variants` page on the splash so the workflow is legible to people who are not us.
Forty deployable units, fourteen READMEs, zero component design systems. We audited our own flagship against the advice we give clients.
The graph moved 23 nodes in five weeks. Underneath, a quarter of it was replaced — including a fix we proved, then stopped applying.
Ten byte-identical service configs folded onto a shared base — and the audit that proved it found three defects nobody was looking for.
Nobody had ever seen the eleven status pills next to each other. Put on one canvas, the promotion argument makes itself in about four seconds.
Nineteen units, 302 raw buttons, 824 net lines of CSS deleted. The buttons were the easy part — the loop got four revisions from its own engineers' reports and is now the thing worth keeping.
Twelve units in one evening, deviations from eight to one, and the gallery caught a contrast defect in the page that documents contrast.
The third primitive reached every member in a night. Three engineers independently proved one of them was keyboard-dead, then came back and adopted it once it worked.
Two projects share the name Archify. We picked one, vendored it the way we vendor Chroma, and it rejected our first eleven diagrams.
A roster photo looks legible and is wrong often enough to poison the whole list. Crop into full-res bands; never trust the overview.
On Linux, one GNU Stow command now links every Lossless skill into ~/.claude/skills, and one more takes them all back out.
The Lossless Group's slides site has a landing page, the brand's look, and a one-command dev environment.
A page for client toolkit recommendations, a quick-access rail so a client sees their own logo first, a header that collapses properly on a phone, and a repositories page built from four spellings of the same frontmatter key — one of them misspelt, and the only way seven tools were reachable at all. Also: a logo that returned HTTP 200 for a week without ever being an image.
The landing page now leads with the copy on the left and the five curated filters on the right, one row, no max-widths. Getting there took four small CSS changes and one genuinely instructive detour: a local server can be running, healthy, returning 200 — and still be serving the wrong build at an address your browser cannot reach. Both halves of that lie are worth writing down, because both were reported as success first.
Three corpora showed identical sources under three names. The data was right; the UI was answering a question it had stopped asking.
Three hundred words is the whole site. The interesting constraint was refusing to add any.
A 2008-shaped storefront becomes three 2026 candidates — sharing collections, tokens and a cart that shows the delta instead of a new cart.
Every default in resolve-path.ts was argued from a table nobody could re-run. The script that regenerates it is now committed — and it turns out the original count treated image embeds as navigational links, which shifted every percentage that used the corpus as a denominator. All six conclusions survive; only the digits moved.
The spike ran on 173 of 1,764 tools, and a tenth of a corpus is not a small version of it — it is a different one. Importing the rest turned five silent defects loud: wikilinks pointing at pages that were never built, two featured tags rendering 2 and 8 tools instead of 258 and 128, and every tag page opening on the unfiltered catalogue because a dead variable was holding the answer.
New site scaffolded for Multiphasic Corporation — a materials-state-control deep tech company — from a single five-slide deck. Rather than picking a direction on a mood board, we wrote the eight narrative beats once into markdown and had three agents build three complete sites against them concurrently: Phase Diagram (instrument readout), Lattice (the material itself), and Before the Beam (a two-column argument). Because the argument is held constant in the content layer, the comparison is purely about form. All three ship light, dark, and vibrant modes and six flare components, and the whole thing typechecks clean at 12 routes.
A dropdown with 112 options is a control you scroll, not one you use. Three ways in — and three silent bugs found on the way.
Search opened every file in the corpus. A manifest makes it one read — and Pagefind, built on that manifest, ranks what comes back.
The results panel looked like it was answering. It was showing the previous answer — confidently, for several seconds.
The site said one login and had nowhere to perform one. Now there is a sign-in, and an account page you can actually act on.
createPathResolver turns Obsidian path resolution into configuration instead of code — an index cascade that rescues the 28% of wikilinks written with no folder at all, a collision policy that refuses to guess, and 5µs per link so it never becomes the reason a build is slow.
The second stage of the lossless-site rebuild gets its own repo, and its architecture conversation gets written down before any code exists. Four explorations capture what is decided (Pagefind, Timeline, OpenPanel, Vitest and Playwright, no published token package) and — more valuably — five constraints discovered by reading the existing corpus rather than assuming: for_clients is curation and not a gate across 481 files, URL fragments never reach the server so anchor links cannot carry their own unfurl, Pagefind indexes at build time so 'live' and 'searchable' are different promises, no Lossless site implements theme modes at all despite three packages built for it, and the house already coined a name for the materialize-versus-JIT rule we kept rediscovering.
Pagefind lands in the toolkit spike, and with it the fix for a subtler problem: two Svelte islands that looked like one feature and behaved like two. A shared reactive atom makes the header bar and the catalogue explorer genuinely the same state, tags become clickable objects that fold the query into themselves, and a Playwright drive finally catches the class of bug that a passing build never will — starting with a one-argument mistake that silently killed hydration.
Train-Case is the storage form and Train Case is the display form, and for a few hours the toolkit confused the two badly enough to route people at /tags/Video%20Generators/. Fixing it meant giving the URL a single owner, teaching Pagefind to index the stored value rather than the rendered label, and lifting a decorative span out of a link so it could finally be clicked.
Two independent implementations of the Lossless Toolkit, built from the same guidance along opposite architectural axes, converged on five decisions nobody asked either of them to make — including the same filename for the content boundary. That agreement is worth more than the winner, and it is what the spec should be built from. The session closes with a handoff, an honest note that none of this code has been read by a human, and a request for a refactoring harness.
A portfolio archive put 45% of its documents in the unknown bucket and classified a cap table as product documentation. A shared text layer, a name-stripping classifier, and the legal extractor that was never built take that to 1%.
The deck analyst batches five slides per call and merges them, which destroys slide identity by design. A second agent keeps the slide as the unit: render, transcribe verbatim, emit typed content, one markdown file each — plus date resolution, deck lineage, and a renamer that puts a portfolio archive on convention.
Two collectors run over transcribed slides and isolate the parts a reader has to see: charts with their axes and legends intact, and any visual that communicates a concept. Decoration is named and skipped. A deck-level index makes the result reusable without opening a single slide document.
Three sets of people wanted time during the same conference week. The itinerary page stopped being a list of sessions and became an availability instrument.
The logo stopped animating itself into place on every page load — plus a person off the roster, a pronoun fix, and three scripts that had been 404ing in production the whole time.
Fourteen days after R2 was decided, the prefix still held zero objects. 886 files went up in 28 seconds and came back byte-identical.
Two years of triage was invisible. `corpora changes` renders it in 0.23 seconds, and refuses to invent a reason where there isn't one.
140 MB of PDFs became 79.6 MB, addressed by content hash and fetched on demand — and the text-layer guard rejected one file, which is the point.
Wired the new store into the app, opened it, and got a blank window. Drawing a header was reading 845 files.
The app looked like a stranger next to its siblings. Twice I fixed the wrong layer before looking at the screen.
Cal.diy is live on the lossless stack, with a calendar actually connected. Getting there meant discovering the tool we pointed readers at in July ships no Docker image at all — and then chasing an error message that blamed the user for a mistake we had made.
It opens a folder. You see your files. You click themes/lossless.css, change a value, and the document you are reading restyles under your hands — then it is still there tomorrow, because it lives in the workspace and not inside any one document. Tauri on NixOS, in about an hour.
A demo that shows three features tells you three things work. The one that shipped yesterday had quietly shrunk to a welcome note — and building a real one immediately exposed four features the renderer had been silently dropping, including citations, which vanished entirely.
Paste an API key, lend it to a project you're not on, and watch what it pays for. Borrowers spend it; they never see it.
Seven rows of the ledger wore the Astro scaffold's logo as if it were a brand, one mark was invalid XML that no browser would draw, and a white plate sat behind all of them on a page with only a dark mode.
Sonar returns the sources it really fetched on every call. We read none of them. 37% of a memo's citations were invented, and a third of the rest pointed at real pages with the wrong title.
The same two companies, rendered twice on purpose — one surface that only enthuses, one that shows the scorecard. The split turned out to run deeper than hiding a chip.
Every link to this site unfurled as a grey box. It now ships five share cards and gives each platform its own URL to get the right one.
Nine services for one search box — the heaviest bundle in the stack by tenfold. Getting it up meant discovering that Railway silently drops start-command arguments, which cost more time than the other eight services combined.
Vercel moved to Node 24 and the deploy broke — not on Node, but on three fonts this repo never declared and was borrowing from its parent.
Two GPs get one connector and 158 tools they can't tell apart — so the skill supplies the missing safety tiering. Then measuring the boards found three fields that look populated and aren't.
The ledger counted every line as code. Two thirds of them aren't — and the chart now says so, day by day, project by project.
Every project's own favicon, pulled from its own repo, now filters the feed by pseudomonorepo and points at the right changelog.
metafetch 0.1.7 was tagged, released, and attested. Obsidian's directory still read 0.1.6, because it reads the default branch and the default branch was six commits behind. Every repo in the family is now development = main = master.
`:::image-carousel` normalizes into one renderer-ready payload: alias collapsed, slides extracted, order resolved. Sequence variants sort chronologically by the ISO stamp in the filename — which works because the sort is stable, and which needed to be, because that stamp records when a batch was processed rather than when each screen was captured.
Markdown gives you one line where editorial practice has always used three. `$$` and `&&` bind an eyebrow and a subheading to the heading they sit against — and the table-of-contents outline that shipped a release ago can now tell a real section from an `###` buried in a callout.
161 assertions across every plugin and every diagram handler, zero new dependencies — plus a demo page whose numbers are computed by the real pipeline at build time. Writing them found a bug that had been shipping for months: a callout nested inside a callout was never transformed.
Metafetch was wrapping every tag in quotes. Annoying, but harmless. Chasing it turned up two bugs underneath that weren't harmless at all — one of which was quietly destroying inline tag arrays on every fetch.
A note about an arXiv paper keys its link as `arxiv:`, not `url:` — so metafetch couldn't see it at all. Now it shows you every URL in your frontmatter and lets you pick, comes back with the whole author list when the source is scholarly, and can stamp a stable code so the note is referenceable anywhere.
A skill library that publishes everything needs somewhere to put the things it can't. This session built that boundary — gitignored config read by instruction, an audit grep that has to come back empty, and a rule against injecting our conventions into repos we only have READ on — then shipped three skills through it and finally wrote down the frontmatter standard the tree had been running on tacitly for months.
Project tracking joins the stack — and an agent now files work items into it over the API. Getting there meant learning that a 200 response from Plane means the proxy is alive, not that Plane works.
Twenty-one entries that only existed in a git directory are now a readable, linkable surface. Getting there cost four bugs worth writing down, including a content collection that reported zero entries while building perfectly.
Some skills only work if they know who they're working for, which is exactly what a public repo can't hold. The answer is a gitignored config/private.json read at runtime by instruction rather than by templating, an example file that serves as the contract, and an audit grep that has to come back empty before you push. The CRM interface guidelines are the first skill built to that shape.
A stakeholder at the-water-foundation couldn't log in or create an account. The invitation had been sent, accepted by the server, and written to a log file instead of an inbox. Twenty's email driver defaults to a setting that silently discards mail, and our own runbook called SMTP optional.
Outline's database now dumps weekly to R2, proven by forcing a run and reading the archive back. The plan that created it also quietly proposed bumping every Postgres in the project to 18, which would have taken them all down.
Paste an Outline share link now and it stays on lossless.at instead of flipping to a Railway hostname mid-click. Alongside it: a spec for the one-connector-per-client MCP, and a working prototype of its Skills plane built with no new infrastructure.
Dates came out backwards and citations glued themselves to quotation marks. Both bugs traced to the same thing — we'd written the format down in the spec, then written it down again in the code, four separate times.
Seven fence handlers — YANG and JSON Schema render as trees, PlantUML encodes to a URL with no renderer at all, Vega-Lite parses for a client chart, Graphviz and Mermaid just claim their language. None add a dependency. The mechanism underneath ships knowing nothing: remarkCodeFences has an empty registry and you name the formats you want.
The spec sat in content-farm's context-v since May. It's a plugin now — paste a YouTube link, get a frontmattered note with a timestamped transcript, no server to stand up first.
lossless.at/<client>/<tool> now opens the right thing for every managed client, replacing hostnames nobody could read. The AI connector link had to stay ugly, and we found out why by measuring rather than assuming.
Reach's homebase went from one card to three. Getting there cost a phantom Docker tag and a Redlock deadlock that turns one bad boot into an infinite one — both now written into the runbook so the next client's Outline is a repeat, not a re-fight.
the-water-foundation went from zero backups to five verified weekly pg_dump crons landing in a per-client Cloudflare R2 bucket — proven by forcing live runs and reading the objects back, not assumed.
the-water-foundation now has a working Papermark VDR — the first in the stack. Getting there meant making an OSS repo build that imports 41 closed-source Enterprise modules it doesn't ship, then chasing a 404 through three middleware red herrings to the real culprit: the webhook host set equal to the app host.
The client portal leaves the public marketing repo for a private submodule, and the address model gets decided on purpose: lossless.at/<client>/<service>. The crux is that MCP's OAuth won't tolerate a naive subpath proxy.
palmer-ai.didi.sh now has a 'Your Knowledge Base' card pointing at the Outline instance — the one bookmarkable link per client keeps its promise as the stack grows.
Since May we deliberately parked TypeScript 6 and ESLint 10 as 'known open questions.' Today they landed — with the tsconfig migrated off deprecated options so the eventual TypeScript 7 jump is already paid for.
A researched dependency campaign swept nine plugin modules plus the umbrella: TypeScript 6 and ESLint 10 everywhere, twenty-two dead dependencies removed, a GPL relicense dodged, two marketplace blockers fixed — and the umbrella's build went from 3,910 errors to zero.
file-transporter jumps googleapis 158 → 173 without touching a line of code, sheds seven never-imported dependencies, and lands the TypeScript 6 / ESLint 10 toolchain.
filestarter is where new Lossless plugins are born, which means it's also where the dead fastify/MCP/zod dependencies kept being born. Purged at the source, with the toolchain bumped to TypeScript 6 + ESLint 10.
Image-gin's dev toolchain catches up in one pass — and Obsidian's own upgraded lint ruleset immediately starts earning its keep by pointing at the new declarative settings API.
The youngest plugin in the family was sitting on a detached HEAD with a 4-digit version Obsidian's marketplace would reject. One pass later: valid manifest, TypeScript 6, ESLint 10, zero dead dependencies — and its first changelog.
fastify, the MCP SDK, zod, and a mystery package literally named 'dev' were riding along from the starter template with zero imports. All gone, along with the Dependabot noise they generate. Toolchain bumped to TypeScript 6 + ESLint 10 in the same pass.
typed.js quietly went GPL-3.0 in v3. Instead of freezing on the old version, we rebuilt the typing animation in ~100 lines of our own TypeScript — and the plugin bundle shrank 18%.
Perplexed jumps @anthropic-ai/sdk 0.92 → 0.115 without a single code change, picks up Obsidian's 0.4 lint ruleset, and development catches up to the 0.3.0 release that had run ahead of it.
plunk-it's markdown-to-email pipeline rode marked from v9 to v18 — a span that rewrote the library's renderer API twice — and our code needed exactly one line changed. Minimal API surface pays off.
Perplexity streaming stopped working when Perplexity's API began enforcing CORS headers that block requests originating from app://obsidian.md. Both streaming call sites — the modal query flow and the directory-template batch runner — were using activeWindow.fetch, which runs in Electron's Chromium renderer and is fully CORS-constrained. Both are now replaced with Node.js https.request, which routes through the OS network stack and is never subject to CORS enforcement.
Perplexed now ships templates for profiling open specs (with five-way authority typing and three-tier adoption) and named market categories (with explicit financial-stage tiers for incumbents, challengers, and innovators). The third shipped thing was a max_tokens override we built after diagnosing why the first real spec profile stopped halfway through with no warning at all. The fourth was a rendering-discipline upgrade — strengthened mermaid rules plus new LaTeX rules — after diagnosing that the vault's concept-profile template had drifted from the bundled source and was generating diagrams with no rendering rules in scope at all.
A fifth shipped directory template aimed at analyst-grade market maps (Known Category and Thesis-Driven flavors) — the kind of document a partner pays a $150K analyst three days to produce.
The per-chunk idle-timeout discipline the legacy modal flow has used for two iterations now governs the directory-template flow too. A healthy slow stream completes naturally; a silently-stalled one fails in seconds instead of minutes.
The Gemini provider shipped yesterday as TypeScript that compiled. Today it became TypeScript that runs cleanly inside Obsidian: CORS-blocked fetch swapped for Obsidian's requestUrl (with bonus page-title + canonical-URL parsing), API-key auth moved from query string to header, default model switched to `gemini-flash-latest` so the free tier doesn't wall the first request, the modal got real CSS, the template seeder stopped crashing on its own previously-written files, network drops mid-stream now surface as plain-English notices instead of stack traces, three system-prompt textareas grew from 200px-wide cramped slots to full-width 3-line rows, and a long-standing `addClass` token bug that was silently breaking eight settings sections finally got cleared.
Shared guidance for the four Obsidian profile templates (mermaid discipline, citation enforcement, image-placement, research-framing) used to be duplicated across templates or hardcoded in TypeScript; today it moved into vault-visible `partials/` and `preambles/` folders that templates pull in by `{{include: name}}` and that the runtime auto-attaches to every Perplexity request. While checking the UI shipped, we discovered the Perplexed settings tab only ever rendered its first section — Perplexity — because a stray `activeDocument.createEl('textarea')` was throwing in this Obsidian version and aborting `display()` before Claude, Perplexica/Vane, LM Studio, and eight other sections could mount. One-line fix: switch eight call sites to `containerEl.createEl`.
Perplexed now ships a fourth AI provider: Gemini, with Google Search grounding. Gemini's `groundingSupports[]` carries per-segment attribution (text span → source URL) that survives intact, where Claude's `web_search_20260209` dynamic-filter sandbox drops it on the floor. Same modal UX as Ask Claude, same Citations footer shape so cite-wide hex substitution stays provider-agnostic. Two Gemini-specific quirks surfaced when we dissected a real response with curl: chunk URLs are short-lived `vertexaisearch.cloud.google.com` redirects that rot in ~30 days (the plugin resolves them to durable source URLs before writing), and the spec's `searchEntryPoint.renderedContent` is 5KB of inline-styled HTML that Obsidian's Markdown renderer can't display (replaced with a Markdown list of the queries, each linked to google.com/search).
Two days of marketplace-submission rounds, six dot-releases across three plugins, 86 Dependabot alerts triaged into nothing, one splash page that now visibly differentiates plugins you can install today from ones you can't yet. The Lossless plugin family has its first three public-directory listings, and the surrounding discipline is now codified everywhere it can be re-used.
Brings the plugin to clean against ObsidianReviewBot's recommended eslint config: applies all 73 sentence-case rewrites the bot demanded (brand names lowercased mid-string — to be restored after marketplace acceptance), refactors four streaming fetch() call sites to activeWindow.fetch so the no-restricted-globals rule is satisfied without a disable directive, drops async on a callback and on listTemplates that had no await expression, and drops six unused catch bindings to clear the optional warnings. Both the local eslint config and the bot-mirror recommended config now exit 0; tsc passes; production esbuild passes.
Ships the cf/cft codefence DSL, four directory templates (concept, vocabulary, source, toolkit), streaming + cleanup pipeline with image marker placement and fallback, anti-incumbent editorial stance, cite-wide-compatible sources footer, frontmatter run-stamps, Google Books URL harvesting for book sources, and a first-run seeder that drops shipped templates plus a README into the user's vault on plugin load.
Showing 30 of 529 — filter to see more from one project. Every entry lives on its project's page.