Nine Keyboard Widgets That Were Never Implemented
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.
2,645 commits · +1,370,774 lines across this project and its sub-projects
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.
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.
Three corpora showed identical sources under three names. The data was right; the UI was answering a question it had stopped asking.
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.
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.
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.
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.
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.
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.
Tenancy stops being a hierarchy nobody's collaborations fit: organization, workspace and project become three labels on one flat table, with no parent_id and no inheritance. Membership removal never cascades, and the API says out loud what a removal did not do. Adding an email that has no didi account now sends an invite from no-reply@didi.sh and attaches the membership when it's redeemed.
We built a viz for the knowledge graph, lost it to a cleaned temp folder, and rebuilt it properly. Plus: local extraction, debugged.
Type Lossless Flavored Markdown on the left, watch a styled document appear on the right, and invent brand-new syntax without touching a line of the renderer. That loop now runs. Getting there meant measuring what lfm actually emits instead of what we assumed — which killed one of our own assumptions, confirmed a decision we had already made for the wrong reason, and surfaced a real defect in lfm's browser story.
The first draft of the editor runs. Someone who is not the machine that wrote it opened the app, typed, and watched a document appear. That is the rung no test suite can climb, and it is the only one that decides whether any of the green checkmarks below it meant anything.
A human said the warning callout wasn't there. It was — the markup was perfect and had been all along. What was missing was every CSS rule meant to style it, silently discarded by the browser because we wrote Svelte syntax in a plain stylesheet. Here is the bug, the second one hiding behind it, and the two gates that make neither of them possible again.
Every context-v document across ~40 repos swept for frontmatter conformity, then re-indexed into Chroma and pushed to the cloud.
A day-long PM grilling session turned 'polyglot markdown that recreates Pages and Keynote' into something sharper: a document that keeps its workings. flave enters ai-labs as a submodule carrying a 24,000-word master spec, 20 decisions resolved on the record, and a deliberately tiny build order. The splash now rolls up eight children and the product highlights finally name didi.sh.
Production was serving a build from August 3rd. Every deploy since had failed at the same line, in all six frontend Dockerfiles, for the same reason — and nothing was watching.
An all-day PM grilling session turned a loose idea — 'polyglot markdown, but it recreates Pages and Keynote' — into a 24,000-word master spec with 20 decisions resolved on the record. The thesis moved twice under questioning and landed somewhere better than it started: flave is a document that keeps its workings. This entry initializes the repo that will build it.
flave's GitHub Pages splash ships with a deliberately divergent identity: graphite ground, Space Grotesk over Instrument Sans, zero-radius ledger rows, registration-mark ornament, and a misregistered display type that reads as an almost-aligned print run. The palette is the product's thesis — the clearance ramp runs cool to hot as exposure increases — and the hero renders one real paragraph at four clearance levels so a reader understands the model before reading a word of prose.
A client's research corpora vanished from the workspace. The data was fine, the database was fine, the service was healthy and logging nothing. Two counters found it: a single unanswerable message had taken the whole request channel down until restart.
Thirty-five design tokens had been verified by arithmetic and never once rendered. We built the page that renders them, and it found three bugs in itself and three in the theme before it had been open a minute.
The token layer got a surface last week. The other half of the design system — seventeen members' own components — still had none. Now each member ships its own component library, at its own address, with a live contract audit attached.
The surface has said 'Corpora Curator' on screen since early July. Underneath, every file, class and federation remote still said strategy — a word that was only ever one value out of five. The code now says what the thing is.
'No corpora yet' showed up twice in one week from two unrelated causes. The first was a dead NATS subject. The second was one guessed word, filtering a list nobody ever asked to have filtered.
Three apps had independently converged on the same source-file model without noticing. The first blueprint fixes the canonical field set before a third vocabulary hardens.
Approving 80 sources is not one sitting's work. The surface now autosaves, shows its progress, opens sources without waiting on a browser, and makes a pasted link name itself.
Previews fetched a page, showed it, and threw it away. Now they file it. Found a bug on the way that was quietly stripping `internal_only` from real client sources.
A knowledge graph over the codebase found eleven micro-frontend mount files differing by a single line each, 250 lines nothing imports, and a task runner that was never installed. All of it is gone — 466 lines lighter, 19 packages building, 1,494 files typechecking clean.
Ask a hard question about a micro-frontend architecture and you sometimes learn the answer is 'it renders invisible text and tells you everything is fine.' We measured that failure in a browser, then gave every design token a floor so it can't happen.
Adds an `approving_sources` stage to the native app's flow machine, between defining the deal and generating the memo, plus the surface that fills it: approve / deny-with-reason / re-search / preview / add-by-paste, backed by SearXNG candidate discovery. Curation is frontloaded because a memo written against an unconstrained corpus cannot be fixed by approving sources afterwards. Also wires six new routes through the Rust dispatcher, which is an allowlist — new sidecar endpoints are invisible to the webview until they are added to it.
Every gate in the pipeline asked whether a URL resolves; none asked whether it was approved. That single missing predicate is why the researcher, enricher, and validator have been fighting each other for months — a fabricated URL that happens to be live passes every reachability check ever written. This lands the membership predicate in both cleanup gates, no-ops citation enrichment in codified mode, strips out-of-set sources from fact corrections, adds an LLM-free SearXNG candidate seeder, and exposes six sidecar endpoints for the approval surface. Also fixes a pre-existing bug that was carrying every dropped URL into the final draft.
Two operator-reported curator bugs, fixed the same afternoon they surfaced: comma-separated tag entries now become separate chips instead of one fused mega-tag, and 'Fetch full content' no longer overwrites the title (and bib) you typed — it fills only empty fields and, when a page can't be read, says so instead of silently resetting the title to the URL. Plus the search-queue feature earns a splash highlight.
The metadata parser was reading only blog/OpenGraph keys, so scholarly articles — whose data hides under Highwire/Dublin-Core/PRISM tags — arrived with no date and a weak publisher, and the operator hand-filled them. Now it detects the source KIND (academic-paper / article / company-landing / web-page), routes to the right parser profile, and resolves every field across all conventions. A Springer article now fills its authors, publisher, and date automatically.
augment-it goes from zero automated tests to 43 across the whole corpora-builder chain — identity, tenancy, transport, canonical CRUD, corpus files, curator state, chat, alignment, and a full backend-chain integration test — spanning two repos and two languages. On the way it caught a real reconnect bug and two live data drifts that no human had flagged.
augment-it gets its first automated tests: Vitest wired, nine tests green across the transport, the workspace registry, and didi's corpora slab. The headline property test — 'no invoke silently vanishes' — failed first exactly as the registry predicted, and the failure was real: the client reconnect chain died on any refused connection, a live mechanism behind the zombie-session and lost-corpus symptoms. Found by the test, fixed in the transport, now guarded forever.
One domain, two client orgs: workspace-service learns the org↔workspace mapping the identity spec always designed, every capability frame gets server-side tenant enforcement, and reach-edu's Stephenie Tesoro becomes the first client user who isn't us.
The strategies collection is rebuilt from the ground up: eight Scroll-UI pitch decks generated from audited, verification-hardened pitch narratives — every number carrying a canonical hex-code citation that renders as ordered [n] markers with hover popovers and a paginated Sources slide. Deck slugs now match the reach-edu corpus strategy slugs; the June drafts are archived, not lost. Presentation mode grew up too: auto-hiding header, full-viewport slides, and typography sized for funder-age eyes on 16:9 screens.
Org→org edges land in the canonical layer — parent, child, or peer, with a typed flavor and free-text human context — and organizations get their first tags (Initiative, Program, Funder), all workable from the Org Workbench card.
A misclicked ➕ put alpha.school on Princeton and the only undo was a raw database write. Now every list entry and identity property on the org and person cards carries hover-revealed ✎ and × — patch it, or remove it with a one-line confirm.
The search-results rail shipped at dinner and got used in anger by ten. Every rough edge the operator hit — dismissal, concurrency, overflow, a broken drag, an open API-credit tab — was fixed and merged before midnight.
The first real workbench session filed seven issues; the three in today's scope ship together: person cards list their corpus entries instead of a bare count, pulse streams gain a user-facing name and an in-place kind/name editor, and a bio page on another org's site can be promoted to an affiliation in three clicks.
The fourth fix of the day: AdditiveList's fallback display becomes host+path instead of hostname alone, so a blog_index stream at gatesfoundation.org/<path> stops masquerading as the root domain and same-domain entries stop being identical twins. With it, all four issues filed from today's workbench sessions are fixed, shipped, and closed — same day.
The Gates Foundation's Deputy Director rendered as a bare UUID: her row was born from the crawlbase/event-CSV import scripts, which wrote full_name and never name — the field every person read selects. All person reads now coalesce name ?? full_name, fixing 136 of 1232 persons at once; the operator-run backfill makes it durable on data.
The workbench could only find orgs that already exist. '+ New organization' opens a gated create: scored candidates from every match signal surface first, picking one just opens it, and creating is an explicit choice past the gate — with the new org's domain seeded so it's matchable from birth.
The workbench gets its missing first move: a sticky left column listing every org the workspace client can see, filterable by name, sorted by corpus count ascending — so the organizations that could and should have more corpus content surface at the top, zero-corpus in red, one click from their card.
The workbench's 🤖 era begins: one organization.crawl capability (Sonnet + Anthropic server-side web search) crawls for relevant identity links, pulse streams, or team members, steered by a per-workspace operator-editable relevance brief — proven live against Aspen in 65 seconds with the brief visibly shaping what came back.
Agent searches run minutes; the operator no longer does. Every 🤖 now returns instantly, the search lands as a card in a persistent right rail, and the card signals when candidates arrive — expand, accept, dismiss.
The service floor under the new Augment-from-DB flow is in and proven over raw NATS: the org card and people-reveal reads, a registry-resolved generic search fire (SearXNG free default, Exa as the newest paid peer), and alias/domain matching in org autocomplete — all before a single pixel of the two new microfrontends exists.
The first of the flow's two new microfrontends: pick 'Augment from DB' at the front door, autocomplete to a canonical org (names, aliases, or domains), and work its card — identity/social links, pulse streams, corpus items — each list additive in place. Plus the verb the spec missed: organization.streams.add, proven live against The Aspen Institute's blog.
The flow's second microfrontend: every 🔍 on the org card opens a paired search rail with the term in an always-editable bar, providers a chip away (SearXNG preselected, Exa/Tavily/SerpApi as peers), and every result row one ➕ from landing on the launching entity's list — with the card refetching the instant it does.
Spec steps 6 and 7 become chrome: every person RELATEd to an org listed with role and relevance, expandable to nested identity links with their own ➕ and 🔍, and an inline add-person where the operator only resolves the human — the affiliation edge and its observation materialize from person.affiliate with the org pre-bound.
Spec step 8 ships as a mode of search-and-add: a per-stream Scan button on the org card fires the official-blog machinery with the stream URL as the authoritative index, dedups against content_items, and badges what the corpus already holds — proven live against The Aspen Institute's blog, flag-flip and all. All five phases of the spec are now implemented.
The ai-labs splash was rolling up 4 children while the tree had grown to 7 — corpora-builder, id-didi-sh, and the whole studies shelf were invisible. The sync script now understands three container shapes (apps/, client-sites/, and studies' direct children), and the rollup carries 525 context-v files from 20 sources.
The tree grew for two and a half months while the corpus stood still. Tonight the discovery walk ran again: 16 new context-v sources opted in, 3 relocated repos reconciled, 1,001 files re-collated, the Chroma collections re-embedded, and the splash rebuilt so every new repo shows on the corpus page.
A new spec inverts the kit's build order: instead of the Chroma-first memory system the June specs planned, the MVP is a plugin — the context-vigilance skill, a scaffolder, and a five-command surface (/cv:init, /cv:new, /cv:kickoff, /cv:prep, /cv:implement) installable in one step. Chroma becomes an upgrade adopters grow into via /cv:init chroma.
Corpus building keeps reappearing inside every ai-labs product — so it gets a repo of its own, scaffolded with context-v, changelog, and a splash slot.
Steps 9 and 10 of the humain-vc unlock build order: augment-it's single-tenant instance is live at augment.didi.sh, on Railway instead of the originally-prepped DigitalOcean droplet — a platform switch made after the droplet's real numbers didn't match the plan, and validated against Railway's actual multi-service project model.
Step 6 of the humain-vc unlock build order: Aniel adds a link, Michael's screen updates without a refresh, and vice versa. The resolver now broadcasts after every domain/source mutation commits; the curator surface refetches on the ones that touch its active view.
Step 7 of the humain-vc unlock build order: a single-tenant deploy now shows a real sign-in wall instead of a half-broken shell to anyone without a didi.sh session. Building it surfaced a genuine architecture gap — an anonymous WS upgrade against a required instance is rejected before any session frame ships — closed with a small, unauthenticated config endpoint.
Step 8 of the humain-vc unlock build order: the chat rail is now didi, it can file a captured link under an existing thesis by name in one turn, and it has an actual face — a character headshot composited with the shell's own brand gradient. Deliberately scoped to augment-it alone; the cross-service didi vision has its own exploration doc and its own timeline.
Build-Order step 5, finished: humain-vc now defaults to 'thesis' on its own (no more typing it every session), and a new domain.retype capability moved the one pre-existing domain — consumer-immunology — from strategy to thesis, DB and filesystem both. Riding along: a real back button in Corpora Curator's header, and a directory-existence bug caught mid-migration and fixed before it shipped broken.
The 'Flows' popdown moved from the header's right side to sit where the numbered bubble strip lives, and now actually switches which flow is active — 'Improve a CSV' (the original 6-step pipeline, with strategyCurator un-spliced back out of it), 'Build Corpora' (1 step), and a third, 'Augment a CSV of Event Attendees' (2 steps), added minutes later at zero architecture cost. FlowWidget's bubble count is no longer a fixed constant; it resizes to whatever the active flow's rotation actually is.
record-db-resolver only ever knew organizations — feeding it a CSV of FreedomFest speakers wrote a person into the organizations table and minted a nonsense opportunity. The fix is a new remote, person-db-resolver: match-or-create a person, then independently match-or-create their org and RELATE the affiliation with a role. Person and org are separate decisions the operator can make in either order, together or alone — not a coupled 1:1.
person-db-resolver shipped a few hours ago wired end to end — but the name field couldn't be typed into, and the observation form refused to save unless you filled in a predicate nobody was prompted for. Both are fixed. Improve a CSV of People is now basic-functioning: pick a record, edit the name, match or create the person, match or create the org, drop a note.
Every check on the canonical layer so far meant writing a disposable Node script, running it once, deleting it. Now there's an MCP server wired straight into SurrealDB Cloud, and a skill that carries the schema knowledge and the verification discipline. First real run: tonight's FreedomFest 2026 batch is tagged correctly across all 65 people and 40 orgs — but only 1 of those 65 people actually got RELATEd to an organization.
The first augment-it flow that starts from SurrealDB instead of a CSV has its rating half working — export the FreedomFest 2026 affiliations as a spreadsheet, rate relevance offline, reimport, and write the rating back onto the affiliations edge. Getting from spec to a working screen surfaced two real architecture gaps (person-enrichment's worklist was coupled to a different person shape than it looked; the rating resolver needed its own escape hatch when an operator lands on the wrong record set) and a lesson about not surgically restarting one process in a --parallel dev stack. More to go — the link/corpus editing half isn't in this screen yet.
The v0.1.0.0 rating loop shipped tonight, then got real feedback the moment it was actually used: two screens for one task was the wrong shape. Reversed course — apps/affiliation-rating-resolver now does what record-db-resolver and person-db-resolver already do for their entities: view the record, edit it in place. Five new capabilities later, the operator worked through 9 real FreedomFest 2026 affiliations end to end — real relevance ratings, real notes, real canonical links, real corpus content — and every byte of it verified correct directly against SurrealDB.
The hub was public by design, back when the site was mostly a friendly hello. Now that it's carrying real fundraise decks for a specific stakeholder, it needed a door. So it has one — the lightest one that still actually works.
The slot-rating matrix that already drove Play-UI work on other dididecks sites is now one click from every strategy card. While wiring it up, two long-standing shell chrome bugs surfaced — the pagination pill overlapping the header, and unreadable dark-mode colors — and got fixed at the root.
The first Play-UI build for any reach-edu-hub deck: eleven static, presentation-ready slides for Rural Income Boosts through Apprenticeship Degrees, plus a script that turns them into a PDF, a PPTX, and a Google-Slides-ready deck on demand. Two real rendering bugs in the shell's /play surface got found and fixed along the way.
We bought didi.sh and decided what converges on it: exactly two shared planes — a central identity service (one account, SSO via a .didi.sh cookie) and a shared agent named didi that loads skills per task. The identity service is specced to implementation depth, on an Elixir/Phoenix stack, with augment-it and its two waiting client teams as the first consumer.
augment-it becomes the didi.sh identity service's first consumer: the workspace WS upgrade now verifies the didi_session cookie locally (jose + JWKS, EdDSA-only), attaches didi_id to the session, and keeps the legacy continuity token working — proven end-to-end against local dev, id service to Docker container.
The header gains a didi.sh identity badge that carries across every micro-app: server-verified didi_id state straight from the workspace session frame, and a sign-in popover that completes the whole magic-link flow without leaving augment-it — the headless GTM contract, working in dev.
The verified didi.sh identity now rides beside every capability invoke into the domain services, stamping created_by/updated_by on domains, sources, and source_usages rows in SurrealDB and created_by into the corpus frontmatter content-ingest writes — no consumers yet, just the wire proven end to end against local dev.
A header-level dropdown (the Lossless 'jumbo popdown' convention, ported from astro-knots into the shell's Svelte header) replaces the memorized-:3017-port habit with a click: 'Flows' -> 'Build Corpora' navigates straight to strategy-curator, full-screen, no CSV-augmentation steps trailing it. No new persisted state, no auth interaction — a plain navigate action, same mechanism the chat rail and pack-runner already use to reach non-rotation remotes.
Strategy Curator is now Corpora Curator, on-screen: the app stopped being strategy-specific the day humain-vc needed the identical shape for theses. Two fixes riding along — the curator was silently stuck on a stale workspace after switching from the shell's header, and the domain type it writes into (previously a hardcoded 'strategy' constant) is now a plain text field the operator sets per corpus, no code change required to start a 'thesis' or anything else.
Each of the six Strategy decks now ships a one-pager: a self-contained, print-ready brief that a funder can read in sixty seconds. A new 1-Pager pill appears at the top-right of every strategy card on /strategies — click it, read the brief in-browser, and download the PDF in one step.
Workforce Development ships as the seventh Strategy deck. And every strategy deck — new and old — just went through its first real citation audit: seven parallel research agents fetched primary sources for every stat, wired the real ones into the citation system, and caught several numbers that were flat wrong.
id-didi-sh exists: the repo for the didi.sh identity service, scaffolded with the tree's universal directories, a README that carries the contract and the increment plan, and a CLAUDE.md that marks this as the Lossless tree's polyglot (Elixir) exception.
Increment 1 ships the same day as the scaffold: a Phoenix app with the full identity schema, an Ed25519 keypair behind a JWKS endpoint, and the whole magic-link → didi_session cookie → /api/me → refresh → logout loop — 19 tests green and every step proven against the live server by a curl script.
The repo gets its GitHub Pages presence: an Astro splash dressed as an identity document — guilloche engraving, a SPECIMEN ID card as the hero object, stamp chrome — pitching one login to fast-track DD-ready materials, with the three services near-copied from ai-labs/splash and a CTA pointed at id.didi.sh.
The walking skeleton left the laptop: id-didi-sh runs on Fly.io in lax with the libSQL file on a mounted volume, secrets set without ever being displayed, JWKS serving the production Ed25519 key at id-didi-sh.fly.dev — and a TLS cert issued for id.didi.sh, two DNS records away from the real domain.
A didi.sh ID now carries alt emails: a magic link to any alias authenticates the same didi_id. Normalized from the start (a user_emails table, globally unique across primaries and aliases) instead of merge-chained after the fact — the fullstack-vc lesson, applied preemptively.
Steps 1 and 2 of the humain-vc unlock close on production: a hand-rolled Resend adapter over Req delivers real sign-in emails from the deployed service, orgs and memberships exist with mix tasks to seed them, and production carries Michael's full identity — plus two infrastructure bugs found and fixed along the way.
DNS validated, the Let's Encrypt cert issued, and the operator clicked a production magic link end to end: an email from no-reply@didi.sh, the scanner-proof /access confirm page at https://id.didi.sh, and a didi_session cookie scoped to .didi.sh. The platform's one-login promise works in production, on the canonical URL, same day the domain was bought.
All 10 services swapped the deprecated monolithic nats package for the scoped @nats-io/* v3 client — 27 files, one codec rewrite, zero wire-protocol changes — and every service round-tripped live on the rebuilt stack before the branch moved on.
Pick a strategy, gather sources for it, and fully control each one: fetch full content, retry past anti-bot pages, edit the bibliographic fields, rename the file, attach a PDF you downloaded yourself, tag it — all writing straight to the corpus filesystem and a SurrealDB registry that never decouples. The landing-page-vs-PDF problem finally has an answer.
Fetch a source and it now fills in its own author(s), publisher, and publication date, straight from Jina's structured metadata — authors as an array, all editable, all written to both the file and the registry. Plus a green save-pulse on every field, and a fix so renamed files actually stay connected.
reach-edu-hub becomes the shell's fourth consumer, joining chroma, humain, and lossless. The onboarding also produced two reusable ideas the shell doesn't have yet: deck collections (a menu layer above single-deck convergence) and a token-driven deck-primitives system — both prototyped in reach as lift candidates. The pass also surfaced one concrete shell limitation: the slot registry is keyed by variant slug alone, so sibling decks can't reuse a variant name.
reach-edu-hub now consumes the shared @dididecks/shell instead of its own hand-rolled deck chrome: the three decks moved onto the shell's /scroll/[deck]/[variant] routing (with redirects from the old URLs), every section gained slot annotations, and the shell's TOC + in-deck ranking light up. On top of that we added a new layer above single-deck convergence — deck collections (Stories / Automations / Strategies) with their own menus — and shipped the first Strategy deck end-to-end: Rural Income Boosts through Apprenticeship Degrees, built on a new token-driven, mode-aware section system.
The Strategies collection grows from one deck to three. Improving Literacy & Numeracy in Working Adults makes the grant case that the two skills most tightly tied to wages and promotions can be lifted — and measured — inside the Apprenticeship Degree. NCAD Forge proposes a shared, cross-member agentic product lab where apprenticeship domain experts build the software the degree actually needs. Both are web-researched Scroll-UI drafts built on the token-driven deck-primitives system, so light/dark/vibrant work without per-element variant classes, and both are framed for grant-seeking philanthropy rather than a fundraise.
What started as an empty menu is now a six-deck portfolio of web-researched strategy decks, each authored as a narrative and designed into a Scroll-UI deck on the shared token-driven system. Today added the last two — Reverse Engineer Available Funding and Opinionated Program Design: Frontier Job Demand — and promoted Strategies into the hub's top navigation so the whole collection is one click from anywhere.
The Improving Literacy & Numeracy strategy deck (ln-v1) gains its first sourced, clickable citation: a verbatim Lumina Foundation pull-quote on slide 07, where the deck argues for embedding foundational skills into the work. Lumina is a major Reach funder, and the quote — 'integrating foundational literacy, numeracy and digital skills directly into workforce training rather than treating them as separate requirements' — has the funder's own voice seconding the deck's core strategy.
augment-it grew in two eras that never met: the row-store records and the SurrealDB canonical orgs. A new per-record surface bridges them — match each record to a canonical organization (or create one) and land its web presence as additive, deduped facts. First iteration works out of the box; a little UI polish before it ships.
The resolver now stamps each match straight back onto the source record (so it survives a crash and exports back to the client's CSV), and lets the operator rename the canonical org's name/slug without ever breaking the bond — the immutable id holds, old slugs become aliases.
Confirmed working in-app: you can now correct a canonical organization's name and slug after research, and the match to the original record holds. The bond is the immutable id; the slug is just its editable face, and every match rides back onto the source row.
Resolving a record now mints an opportunity: a first-class, client-scoped entity 1:1 with the source record and many:1 to the canonical org. Three pipeline rows for Accelerate the Future become three opportunities on one org — none merged, none lost. This is also where the client's CRM data finally lives.
528 markdown files on a laptop and 129 organizations in the cloud database knew nothing about each other. Tonight a reconcile pass joins them: every fetched URL becomes a content_items row that records who published it, who it's about, and (soon) everyone it mentions — 500 rows, zero leakage, idempotent and re-runnable.
A VC client runs their fund + CRM on Decile Hub. Tonight augment-it learns to talk to it — a skill that codifies the contract and a TypeScript MCP server that exposes it as agent tools, both grounded in Decile's own OpenAPI spec rather than guesswork. It's the first connector in the per-client seam we've been building toward.
The relationship between people and organizations is a lie at the data model level. How careers work: people sit on boards, advise, change jobs, keep a past CFO title. Tonight the single org slot becomes an open-ended stack of affiliation cards — each with its own free-text role written straight onto the graph edge, each able to find-or-create its org. Start typing a name and existing orgs autocomplete from canonical (so you pick 'Institute for Humane Studies' once, not retype it on every attendee).
Last night the canonical layer landed in SurrealDB; tonight the operator-facing surface that turns sparse rows into rich ones starts running in the browser. The new `apps/person-enrichment/` remote is a *pulse-surface* — a per-event UI where the operator iterates one attendee at a time and fills as many dimensions as one Google search surfaces: name (paste a full name and it splits surname + first_name; or edit either field directly), additional emails, personal links (LinkedIn, X, Substack, GitHub — kind auto-inferred from URL), personal corpus (the LLM/RAG ingest target — articles, podcasts, interviews), an organization (find-or-create by slug), org links, and org corpus. Every input commits on Enter; every save announces the doc-table AND relational-table writes it caused; corpus URLs get a shared UUID across the `content_items` relational row and any number of `personal_corpus` / `org_corpus` array entries, with in-browser verification confirming the cross-doc id matches; orgs auto-detect on the next attendee from the same domain so the operator stops typing 'Stand Together Foundation' fourteen times. Two new context-v docs — the spec naming the pulse pattern, the issue file capturing the query lenses we'll want once `has_personal_link` observations accumulate. 14 of 177 reach-edu attendees enriched in real use this evening before the operator paused for a safety commit.
A real consulting engagement walked in asking for a curated NYC dinner list, and the org-first frame augment-it has been living in didn't fit. Tonight: ~250 LinkedIn profiles captured via Crawlbase land as flat CSV + full-shape JSONL (skeletons flagged for retry, no fields silently dropped), ~200 LinkedIn 'Save to PDF' downloads get slug-renamed via pdftotext extraction, a browser snippet writes a forward manifest so we never need to reconcile by hand again, the deep-profile DOM extractor catches up to LinkedIn's late-2025 hashed-class layout (pronouns / followers / connections / website / cover photo / robust headline classifier), and three composition scripts join everything into a tag-grouped markdown briefing and a brand-themed HTML export. The exploration sitting alongside the code names what just happened structurally: org-first and people-first are two pivots over the same org↔people join, and a canonical layer (the LinkedIn pipeline) belongs cleanly separated from a proprietary layer (per-engagement commentary). Sub-scale (~30K enumerable venture entities) is the differentiator the big CRMs structurally can't occupy.
Yesterday's [[Joined-People-UI-and-the-Network-First-Pivot]] exploration ended with a clear conclusion: the filesystem-as-substrate posture that has carried augment-it this far stops working the moment we try to blend canonical entity data (LinkedIn truth, refreshable, shareable across clients) with proprietary per-engagement commentary. Tonight that posture changes. SurrealDB Cloud is live on `main/main`, wired through the Node SDK with a working connector, seeded with the 882 humain-vc LinkedIn-network persons from last night's tagged-briefing pipeline AND the 177 reach-edu attendees from a Stand Together event held last month — two clients sharing one canonical schema with not a single row of leakage between them, courtesy of a `client_access` array materialized on every entity. Plus the spec for the UI that closes the loop — a per-event enrichment surface where the operator turns 177 email-only sparse rows into named persons + properly-tagged organizations one row at a time. The fact log (`observations` table) captures every claim with provenance — who said what, when, on behalf of which client — so the next time we refresh from LinkedIn or pull a new attendee list, nothing overwrites an operator's hand-curated note. The bus that left yesterday with a network-first pivot is back tonight with the data substrate to make the pivot real.
The last six days surfaced enough orchestrator-level work to log: a new agent-skill that codifies the end-to-end procedure for onboarding a VC firm (setup-new-firm), two CLI scripts for capturing DocSend decks programmatically, three issue-resolution write-ups naming real pipeline gaps the recent runs exposed (competitive research not flowing into prose, missing exit-comp agent, unsolicited investor verdicts), one new spec for per-deal analyst focal points, and a small skills-directory rename (skills/ → agent-skills/) to match the parent repo's convention. Also adds a changelog-scope discipline to CLAUDE.md so firm-private content stays out of the orchestrator's log.
Tonight started with a small symptom — three rows in the Sort & Filter Lens on the v10 record set showing `corpus 0` despite per-funder corpus directories full of files on disk: hewlett-foundation (2 files), howard-schultz-foundation (6), kellogg-foundation (6). The yesterday-night issue file had named four others (sobrato, stand-together, cohen, todd-fisher) and proposed a per-client `corpus-overrides.yaml` as the defense-in-depth fix. Hard-refresh resolved those four cleanly — but exposed these three. A second NATS probe confirmed the backend was returning the correct counts (2 / 6 / 6) end-to-end for the new symptoms too. The actual bug was in the wire, not the join: `corpus.list_for_record` had no `CAPABILITY_TIMEOUTS_MS` entry so dispatch fell through to the 5000ms default, the content-ingest handler is a serial `for await` loop, and each call walked every funder directory under `clients/<id>/corpus/` (~40 dirs × ~300 .md files). With 96 visible rows firing 96 parallel requests on view load, late ones in the burst timed out and the lens swallowed the error silently — race timing explained why sobrato et al. recovered post-refresh and hewlett/schultz/kellogg didn't. The operator's framing: *'shouldn't there be a pretend join where the column in the records sheet for corpus references a folder name/relative path?'* The records sheet already populates `corpus_funder_slug` per row. Wiring that into `listForRecord` as the primary join key dissolved THREE things at once: the bug (operator's explicit assertion routes around every lineage edge case), the timeout race (per-request walk went from 300 files to ≤13), and the planned `corpus-overrides.yaml` proposal (the cell IS the override surface — no new file format needed). Lineage stays as the fallback for rows without a slug. While the door was open we noticed something else: Jina's response body has a `Published Time:` preamble line on every capture, and we'd been writing it to the body but never lifting it into frontmatter. The sort/filter UIs had no first-class authored-date field. A six-line addition to `jina.ts` parses the preamble + a `liftPublishedAt` helper in `corpus.ts` promotes it between `fetched_at:` and `record_id:` on both writers. A sibling backfill script rescued 244 dates from existing files (some going back to Wikipedia article creation dates from 2004 — the corpus carries deep history we couldn't see). Also tonight: 226 hand-curated captures across 20 funder directories + 86 inbox triages from the operator's v10 hand-search rhythm, including the first `manual-local-pdf` convention (operator drops a ResearchGate PDF from a local download; sidecar .md flags the missing canonical URL for later replacement). The slug-join means each of those 20 new funder dirs surfaces on its row's chip the moment the dir exists, no record_uuid plumbing required.
augment-it was filing all its work under one tenant (reach-edu) and the slab that told the chat what `client_id` to use literally hardcoded the string. Tonight that ends. The shell grows a workspace switcher in the top-right of the header that lists every directory found under `clients/` (today: humain-vc, reach-edu), and clicking one persists the choice to localStorage, broadcasts it via `augment-it:workspace-changed`, and tells the workspace-service which `client_id` is process-wide active. The chat slab now reads the operator's pick from `ctx.client_id` (forwarded on every chat turn) and falls back to the server's process-wide active value — the hardcoded reach-edu string is gone. The load-bearing piece is the env-var seam: `clients/<slug>/.env` gets loaded into a frozen, in-memory map keyed by slug at workspace-service boot. We deliberately do NOT merge into `process.env` because the workspace-service runs in one process and serves all tenants — `process.env` would bleed Decile keys from humain-vc into a reach-edu chat turn. The connector-config seam (LLM / search / CRM / MCP / storage) the spec calls for lands in step 2; tonight surfaces the raw env, hides nothing, and gives Decile a clean home to slot into next.
27 canonical slide types — problem, why-now, fund-thesis, lighthouse-customers, blue-ocean-strategy — each with the names they go by in the wild, the tags they belong to, and a soft sequencing graph. The first layer of a deck-scheming substrate that lets DidiDecks recognize a slide by its purpose, no matter what the original deck called it.
Three sibling reference skills land under MemoPop's context-v/agent-skills/, each codifying a slice of the analytical structure a venture investor expects to see in an investment memo: how to classify competitors (stage rings × competitor types, with a noisewashing-vs-genuine-threat test), how to size the prize (SAM/SOM/TAM × 8-step penetration grid for revenue and EBITDA), and how to test the path to it (sustain/improve/plateau/reduce scenarios against MoM/YoY growth at the company's actual unit of sale). A companion exploration proposes the grill-me checkpoint — a per-section orchestrator pause where the user, not the model, picks the synthesis frame. All four artifacts also cp'd into the root lossless-agent-skills repo at context-v/skills/, where they sit as untracked files awaiting a separate commit in that submodule.
The chat surface (`apps/chat/`) has been mechanically functional for weeks — Anthropic SDK in `services/prompt-runner/src/chat-turn.ts`, four-cache-eligible system slabs assembled in `services/workspace/src/chat.ts`, three-mode tool dispatch (chat_answer / chat_propose / chat_invoke), Svelte transcript with accept-proposal cards — but it knew about three verbs (`prompt.draft`, `prompt.improve`, `prompt.apply`) and one context dimension (`record_set_id`). For the funder content corpus workflow the operator is actually in, that meant the chat had nothing to offer. This commit ships the first non-prompt verb: `/inbox <url>` recognized by the model, dispatched as `corpus.inbox.add`, writes to `clients/reach-edu/corpus/inbox/` with the extended frontmatter contract from the Corpus-Inbox-Capture-and-Triage spec. Claude is the default and was the default; what changed is the chat now has something useful to do with the API key it already had. Companion spec `Chat-Context-Awareness-Architecture.md` lands alongside, naming the three v0.0.1 design holes the inbox verb exposed (hand-written verb roster, hardcoded active_client_id in the context slab, ActiveView covers only half the microfrontends) and defining a three-layer migration plan (workspace as context broker → slab-assembly contract → verb registry) that each layer ships independently. Path B per the user's pick — ship the verb tonight, spec the architecture once it's working.
The Lossless Group becomes its own client. Unlike Calmstorm / Chroma / Humain / Reach — all of which recreated a management-supplied deck — lossless-decks starts from *nothing visual* and generates the deck from per-slide markdown concept files. That workflow earned the corpus a new `narratives/slides-content/` convention (then promptly relocated to per-client-site `context-v/` once we saw the concepts were engagement-specific), four slide concepts authored from Michael's running notes, and an Astro scaffold consuming `@dididecks/shell` that renders the four slides at `/scroll/firm/v1/`. Building it surfaced a separate structural debt: the shell shipped a `--ddd-chrome-*` token system whose defaults were calmstorm-flavored hardcoded white/grey/black RGB values. On lossless-decks's dark mode the floating section indicator rendered as a light-on-dark capsule because the shell's defaults were fighting the consumer's brand. Fixed by rewriting every chrome color and font reference to resolve against the consumer's functional tokens (`--color-surface`, `--color-text`, `--color-border`, `--font-body`, `--font-mono`) — system-tier references functional, never hardcoded, per the discipline the new `setup-new-dddecks-workspace` skill encodes. Same day: SlidePaginator extracted as a standalone shell component with type-to-jump, positioned left of the mode toggle. Same day earlier: high-severity devalue DoS (CVE-2026-42570) patched in deck-shell + splash via astro 6.3.1 → 6.4.4 bump.
Alpha JWC Ventures becomes the sixth firm wired into the orchestrator under the established io/<firm>/ private-submodule pattern. This is a structural addition only — registering the submodule path in .gitmodules and pinning the initial commit. The firm-private content (brand config, customized 9-section 7Cs outline, first-deal setup) lives in the alpha-jwc-secure repo and is documented in its own changelog there, per the CLAUDE.md scope rule.
A new TechBio investor (humain.vc) became the third client deck. The full Fund I deck — 29 slots, three competing design variants — landed end-to-end against a shell consumption contract. But the onboarding also surfaced a structural drift: six deck-generic primitives (scroll-snap + keyboard nav, per-slide shell, mode toggle + runtime, registry-derivation lib, build-status matrix) had been built inside chroma-decks before the @dididecks/shell carve-out and never lifted, so every new client risked re-implementing or copy-pasting. All six are now in the shell, humain consumes them via clean @dididecks/shell imports, and a three-mode theme architecture bug that made the mode toggle a visible no-op got named, fixed, and codified as a per-client invariant. Chroma's own migration off its local copies is deferred until humain validates the lifted versions in browser.
After this morning's onboarding flurry we kept going. Chroma — the older sibling that originally carried every deck-generic primitive locally before @dididecks/shell existed — now consumes the shell for five of those primitives via thin shim re-exports. Net effect: 781 lines of duplicated implementation deleted in chroma, 119 lines of shim added, ~25 downstream importers untouched. Then a small but instructive correction: humain's landing was showing the lightweight SlideStatusMatrix where it should have been showing the rich audit-rated DeckMatrix that Chroma's landing already uses. The rich version was in the shell the whole time. Now both clients land on the same matrix UI.
Three things shipped in one short session against the live :3002 surface. First, the per-record field renderer in Record Collector stopped silently hiding structured values — arrays and objects now serialize through the same path as the CSV export (`logic/format.ts`, mirroring `download.ts:csvEscape`), and any empty value (scalar `''`, `null`, `undefined`, `[]`, `{}`) renders a muted italic `(empty)` placeholder so the operator can confirm the prior enrichment pass populated the field before kicking off the next one. The 11px muted JSON style bumped to a readable 12px, normal weight, on the field background. Second, the single `Augment This Set →` button became a small `Augment this Set` header with two stacked CTAs — `Run a Prompt →` (navigates to `promptTemplateManager`) and `Run a Bundle / Packs →` (navigates to `packRunner`) — so the user picks the divergence at the Record Collector surface instead of landing on a default and toggling in-slot. The set-header also restacked vertically so long versioned filenames (`2026-06-05_Master-Pipeline-Tracker--Active-Pipeline_v8.csv`) wrap above the CTA panel instead of squeezing it offscreen. Third, when the operator surfaced that the sidebar treats five sequentially-uploaded variants of the same external tracker (`…_v4.csv` through `…_v8.csv`) as five unrelated peers, that produced `context-v/specs/Record-Set-Family-Grouping.md` (v0.0.0.1, Draft) — a spec that separates internal-lineage families (already in `promoted_from`) from external-variant families (new `variant_family_id` field), names five new workspace capabilities, defines the suggestion-only filename-stem heuristic, and locks that the Augment-this-Set CTAs target the active leaf, never the family.
The spec dropped earlier today (`context-v/specs/Record-Set-Family-Grouping.md`, v0.0.0.1) implemented end-to-end against the live :3002 surface. RecordSet picked up the two new fields (`variant_family_id`, `variant_family_label`); the row-store grew a top-level `variant_families` slot plus five mutators (create / update / add / remove / dissolve) and one read-only heuristic (`suggest_variant_family`) that normalizes a filename stem — strips date prefix, `_vN` suffix, extension — and matches against existing sets with the same stem and ≤ 3-column schema delta. The workspace WS bridge registered the six new capabilities. Browser-side: `apps/record-collector/src/logic/family.ts` walks every leaf back through `promoted_from` to assemble lineage chains, groups leaves by `variant_family_id`, sorts every list — groups, members, archived ancestors — newest-on-top per the user's mid-implementation correction (spec edited to match: §Sidebar rendering now reads 'Newest-first is uniform across the sidebar'). `RecordSetsList.svelte` renders a collapsible family card with chevron header, member count, optional generation count, two inline action icons (✎ rename, × dissolve), and an 'Earlier generations (N archived)' sub-section under any leaf with promoted predecessors. Default sidebar query excludes archived sets at the top level. `App.svelte` calls `record_set.suggest_variant_family` after every ingest; if a match comes back AND the user hasn't dismissed that stem before (sticky per-stem in localStorage), a non-blocking 'Looks like a variant of X (N existing sets) — link as family?' prompt appears under the ingest status with Link / Dismiss buttons. Backwards-compatible — every new field defaults `undefined`; pre-existing stores deserialize without a migration. Existing `…_v4`–`…_v8` sets render as solo cards until the next ingest fires the suggestion.
Three coupled drops in the same session. First, the URL auto-detector + clickable rendering plan dropped earlier today implemented end-to-end in Record Collector — `apps/record-collector/src/logic/format.ts` grew a `FieldShape` discriminated union (`empty | scalar | scalar_url | url_list | json`) that picks by VALUE-shape never field-name, plus a `UrlEntry` extractor that surfaces `url + chip (from pack_id) + label (from label or display_name)` from `Array<{ url, ... }>` payloads. The structured-value branch in `App.svelte` switches on `shape.kind`: `url_list` renders as a vertical flex of clickable `<a target="_blank" rel="noopener noreferrer">` entries showing hostname+path (truncated at 60 chars with full URL in `title=`) so the operator can verify links resolve without leaving Record Collector. Auxiliary metadata (display_name, confidence, source_metadata, response_id, accepted_at, ...) drops from the rendered view but stays on the row and round-trips through CSV. Read-only by design; the per-entry remove affordance lives in a sibling plan, not this one. The keyed-each was unkeyed mid-implementation when `(entry.url)` was identified as a Svelte 5 duplicate-key crash risk against any row whose URL array has repeats — for read-only lists, positional iteration is the right call. Second, a Response Reviewer drill-through audit on the live response-store revealed 1,982 responses with 1,109 (56%) from the three Entity-Pulse OfficialPulse packs at a combined 3-accept rate — `official-blog-pack` 99.5% reject, `official-social-posts-pack` 100% reject, `official-pressrelease-pack` 100% reject — confirming the operator's 'tons of junk links' read of the surface. Domain breakdown showed 65% of pressrelease responses come from `news.google.com` (the google-news-rss connector with weak entity-name binding); 45% of social-posts responses come from `youtube.com` (weakly bound). The packs with strong identity binding — wikipedia (matches article title to entity), linkedin (returns entity's own page) — reject at the normal 73-82% rate for a triage workflow. Third, the operator's response to the junk-volume reframed the next-feature direction entirely: STOP triaging URLs one-at-a-time in Response Reviewer; START treating those URLs as a seed list for a Jina.ai content-ingestion pipeline that writes deduped markdown per funder; build a per-client content corpus that powers cross-funder fundraising-strategy synthesis and per-funder outreach customization. That direction surfaced the broader ai-labs architectural question the operator named explicitly — when does the single-operator-on-localhost posture stop scaling, what stack do we reach for, and how do we architect today so the move is cheap when it comes? — and produced `context-v/explorations/Per-Client-Privacy-and-the-Path-Off-Local.md` (v0.0.0.1, Draft) mapping five candidate stacks (defer-everything, per-client Railway single-tenant, multi-tenant SaaS shape, hybrid posture, managed BaaS) across five axes (repo topology, storage substrate, identity/auth, multi-tenant data model, sensitivity constraints), naming six decision-forcing functions, and five architecture choices that cost almost nothing today but preserve cheap optionality for every path-flip. Operator signed off on Path D — hybrid posture — and committed to start local: `lossless-group/augment-reach-edu` created as a private repo (default README); `clients/reach-edu` registered as a git submodule pointing at it. The reach-edu corpus + operational data + Jina ingest pipeline land in that submodule in follow-up sessions; the augment-it parent stays on the docker-local posture until one of the named forcing functions actually fires.
An end-of-day shipping note that is not about success. The reach-edu funder content corpus workflow was supposed to land tonight against the 96-row Master-Pipeline-Tracker. Final state: 75 markdown files written across 15 funder subdirectories under `clients/reach-edu/corpus/`. 81 of 96 funder records have zero corpus content. The operator's framing at session-end: 'this is all fucking bullshit, nothing is working the way it should, I was only able to process like 1/3 of the plausible records.' This changelog is the honest version. The goals spec landed (Funder-Content-Corpus-Workflow.md, eight rules + six-step workflow), the implementation rebuild against it landed in code (content-ingest service, Content Reader UI, pack honors operator curation, dispatch refuses invalid URLs, fire_id stamping), and 25 broken-URL rows were force-repaired across 5 promotion generations. But the end-to-end validation — fix URLs, re-fire entity-blog, evaluate fresh data — never happened because the session consumed itself patching display-layer symptoms of yesterday's broken-pack-broken-URL data instead of producing fresh clean data via a re-fire. The honest accounting: substantial code shipped, partial data landed, root causes mostly unfixed, eight named failure modes documented in `context-v/issues/Funder-Corpus-First-Session-Failed-Most-Records-Unprocessable.md` for morning-self to attack as a punch list.
After a long arc that re-spec'd the bundle/pack flow from scratch and ended a stack of architectural overreach, augment-it now has a working per-record surface where the operator picks ONE record, fires ONE connector (Firecrawl scan, Firecrawl + Haiku agent, or SerpApi `site:` search), watches the candidate URLs render inline, picks one (or pastes/edits a custom URL), and moves on. Acceptances land in an array column so an entity with multiple canonical paths (Arthur Blank Foundation's /news/ + /blogs/, Ascendium's /newsroom + /our-stories) carries all of them. A save bar at top AND bottom of the records list invokes the existing `record_set.promote` capability with confirmation, the new version auto-becomes the active set, and a post-promote nav surface offers Enhanced Records (download), Augment (next pass), or stay. The same session also produced three new context-v documents that explain the rearchitecture, audit the v3 → v4 → v5 chain to clear promote of a wrongly-pinned bug, and lock the row-count-stable-across-versions invariant. Record Collector got decomposed into proper components with a descending-by-created_at sort and a per-card CSV download button. The user ran the full loop end-to-end on the 96-row pipeline tracker, accepted 98 URLs across 45 rows, and promoted v5 → v6 cleanly.
First implementation step of the Entity-Pulse-Bundle spec lands as a working two-stage pack (find-index → extract) plus a generic ISO-8601 normalizer that solves date extraction across heterogeneous web sources. Smoke-tested against reach.edu: 5 real blog posts pulled end-to-end with ISO publish dates + computed age_days in 4.4s. SerpApi + Firecrawl wired as new connectors; new NATS subject `pack.entity_pulse.requested`; standalone CLI driver for one-off fires and future Agent Chat integration.
The reframe that took shape in late May lands as code: search-provider choice is now an architectural axis of the stack, not a Tavily-vs-anything-else swap. SearXNG arrives as a self-hosted container peer to Tavily, with its own connector under a new connector registry; the common-seven social packs flip their default to SearXNG (no key needed); Tavily stays wired in as the peer for content-RAG packs that need it; and a `provider_override` parameter threads from the response-reviewer's per-record buttons through workspace and social-search so the user can fire any pack through either provider on any row without touching the pack definition. The pre-flight surface for per-row iteration is now in place — the iteration loop itself still pending.
What started as live demo-prep — patching a sticky Fire button, wiring a dead 'Do another round' affordance, adding a navigation to where results land — became a verdict: augment-it's affordances hide, die, mismatch, and (a newly emerging fourth shape) misname. Rather than patching one more thing, the session pivoted to spec mode. The result is a whole-shell UX coherence audit with eight locked decisions, twelve evidence items, four surfaces audited, and three open architectural questions; four sibling context-v stubs (per-remote in-app API docs, initial user experience, the dual-identity blueprint, the auth-patterns-following-Astro-Knots blueprint); and a substantive pickup doc so the next session can resume cold. No code shipped from the spec arc itself — everything held at the sign-off gate — except three small forward-affordance fixes that landed alongside as a separate fix commit and are recorded in the spec's evidence log.
We thought the spec called for two patches and a rename. Halfway through the second patch the architecture broke in our face — a Split view with a working ✎/⊞ toggle but a stubbornly empty pane next to it — and we realized the toggle didn't belong inside either remote, it belonged to the slot. The fix turned into a shell-level concept that pays off in every layout mode.
Citation assembly was rewriting section files. Section files now stay the durable source of truth and keep their semantic IDs.
Over the last two days the response-reviewer microfrontend at localhost:3005 went from a basic review UI to the triage surface for every pack augment-it will ever run. The structured-output extension that landed on 5/25 was the seam; what followed was a fast sequence of commits — many attempts, two scraps, real-data smokes — that turned the remote into a per-row, per-pack triage cockpit with inline correction (URL), human-supply (display_name), and entity-name cleanup (the row's identity column) all riding the same response.set_structured subject. Two emergent-requirements rounds also got captured into the Packs-and-Bundles blueprint so future packs across the Lossless family inherit the discipline. The SearXNG-substrate decision is filed as an issue but deliberately not yet executed — the remote is good enough that the next session can start somewhere new.
Ten commits between 2026-05-25 17:08 and 2026-05-26 02:53 carry augment-it through a complete arc: a tidy-and-status-sweep across the existing context-v tree, a new exploration (Entity-Profile-Augmentation-Workflow) that surfaced a two-tier abstraction, a forked blueprint (Packs-and-Bundles-Pattern) that locked the contracts, a new shared package (@augment-it/shared-ui with the first reusable component — ConfidencePill), a structured-output extension to the response-store schema, a new federated remote (pack-runner at :3009), a new backend microservice (social-search) with six (then seven) packs and Tavily as the v1 connector, a row-store write-back surface (row.fields.socials), a paired-authoring shell rewire that took pack-runner out of the rotation and made it a peer to prompt-template-manager, a complete by-record triage view in response-reviewer (:3005) with inline URL / display_name / entity-name editing, two design pivots that got documented honestly (the profiles.<source> column scrap, the Run-entity plan deferred to its own arc), and emergent-requirements promoted into the blueprint as a §Triage Surface UX Requirements section for the rest of the Lossless family. The system that exists at the end of these two days is recognizably the same shape as the system at the start and recognizably a different product.
Three reproducible citation-assembly bugs squashed.
augment-it now has a chat panel that doesn't just execute on your fundraising-pipeline records — it helps you author the prompt that drives the enrichment. You state a goal in your own words; the chat drafts a prompt template against a sample of your record set's columns and renders it inline. You react to the draft conversationally, refining it across a turn or two. When it's right, you click run, and the same chat surface narrates the enrichment job's progress and checks at the end that the columns the prompt promised actually got populated. The structural answer to bulk-AI-enrichment-gone-haywire is conversational gating: draft, improve, then apply explicitly. This entry walks through what shipped, how it works, and what v0.0.2 brings in.
augment-it now has a checkpoint surface between enrichment rounds. You finish a pass of LLM-driven enrichment, open Enhanced Records, see every record in one place with its accumulated columns, and click Promote — the system snapshots the current state into a new canonical record set under a deterministic, versioned name, archives the predecessors, and gives you a clear success message with a one-click path back to the start of the arc to author the next round. record_uuid follows each record across every promotion so a single conceptual record stays traceable from original CSV through every round of refinement. This is the loop the walking skeleton has been building toward: enrich, triage, checkpoint, re-enrich on a cleaner base.
augment-it is a multi-tenant pipeline tool. Different clients will upload spreadsheets with wildly different columns. The code cannot have opinions about specific field names. Today we ripped out every place where it did: no hardcoded reserved-field lists, no field-name-aware special rendering, no schema decisions imposed on tenant data. The cell renderer is type-driven (scalar → text, object/array → JSON), the canonical-schema union is presence-driven (any key with values is a column), the promote-fold is type-driven (arrays merge, objects merge, scalars overwrite). One rule everywhere: whatever a tenant puts in row.fields stays in row.fields and ends up in the promoted record. This entry shows the messy middle — what we got wrong, how the user pushed back twice, and what the corrected discipline reads like.
Yesterday's v0.0.10 run made a problem legible: 33 hand-curated URLs went into the writer, 7 came out cited in the prose, 26 sat untouched. The fix wanted to be a new agent, and the first instinct was to name it 'Citation Diversity Enricher' and slot it between the writer and the existing 'cite' node. Today's planning work landed somewhere different — the existing cite node runs upstream of the writer (not after), the new agent's job is genuinely orthogonal to enrichment, and the right name is Citation Coverage Promoter. No code shipped; a plan got corrected before it generated a confused agent.
augment-it is growing two new pipeline stages: a request-reviewer that shows you exactly what is about to be sent to the model, and a response-reviewer that lets you triage what came back. This entry is the service layer beneath both — the no-UI plumbing. Its load-bearing idea is small and strict: there is now exactly one function that assembles a model request, so the request you preview is byte-identical to the request that fires. Alongside it, a new response-store service turns every fired response from a throwaway cell into a first-class object you can inspect and flag. The two frontends that sit on top are their own story — coming next.
The shell's tiling Deck now shows the whole enrichment pipeline. request-reviewer and response-reviewer join record-collector and prompt-template-manager as federated remotes — scaffolded, wired into the shell, connecting to the workspace, and tiling in pipeline order. The panels are deliberately placeholders: this entry is the federation wiring, not the review UIs. But for the first time the Deck reads end to end — collect, author, pre-flight, post-flight — in one window.
This morning, Request Reviewer and Response Reviewer were empty placeholder panels. Tonight they work. Request Reviewer shows the resolved request — the prompt with its {{tokens}} filled from a real record row — lets you pick the model, see the literal JSON body, and fire it. Response Reviewer steps through what came back and lets you flag or accept each response. And the boundary between the two is now strict: the request belongs to Request Reviewer, the response to Response Reviewer, and authoring stays in Prompt Templates — which lost its run button entirely. Four stages, four remotes, one gate each.
Last night's commit shipped Request Reviewer + Response Reviewer as code that typechecked. Tonight they survived a 207-record enrichment run against a real CSV with the user as the human in the loop — and every place the loop was thin, this round repaired. The Save / Apply ergonomics on Prompt Templates, the firing indicator and Cancel button and coverage strip on Request Reviewer, the per-row 90-second timeout and run-level cancellation in prompt-runner, the autosave-on-blur on Response Reviewer's edit textarea (after the first run lost 75 manually-typed URLs), helpful_links as a side-channel field on every row, a needs-human triage flag, an icon action row with instant CSS tooltips, per-bucket counts on the filter chips, a fire-time blank-row filter at csv ingest, and the spec for the next checkpoint — enhanced-records-list and the promotion loop — captured in context-v as the build target for the next session.
For months MemoPop's citation validator quietly skipped its URL accessibility check on almost every citation — because a date-format warning was enough to disable it. Two small changes put it back online, and upgrade the underlying check from a HEAD-only 'did this URL respond' to a GET-with-body-sniff that catches soft-404s and paywall stubs the prior pass had been rubber-stamping as valid sources.
Three things shipped today that together put the human analyst back in the middle of the citation pipeline: URL-keyed validation so the validator finally sees every source (not just the 6 that survived a per-file numbering collision), a Sources.md codified-mode loader that lets the writer compose only from analyst-approved URLs, and a new aggregate_sources agent that halts the pipeline mid-run with a curated draft so the analyst can rank and prune before any prose gets written.
Phase 1 of the augment-it rewrite landed today. Three containers come up cleanly with one command: a NATS message bus, a thin Workspace Service that owns no domain data, and a row-store microservice that owns its own rows. The shape of every future service is set; the wire is now visibly a microservices wire instead of point-to-point HTTP. The in-app chat surface has something real to plug into.
Phases 2 through 5 of the walking skeleton landed in a single session. Five containers running, the two real fundraise spreadsheets ingested, cells editable from a Svelte 5 frontend that consumes the workspace singleton, and the architecture demo's strongest claim — 'adding a new service is cheap' — has gone from slide to docker-compose diff. The 27th service made its first appearance as xlsx-ingest, with zero changes to row-store.
augment-it now has a real Module Federation host. The shell at :3000 mounts the record-collector remote at :3002, and the architecture has a federation boundary for the first time. Getting there meant fighting three subtle Svelte-5-plus-MF interactions that the official docs gloss over: shared-singleton factories that wouldn't register, $effect_orphan errors from cross-runtime mounts, and component-scoped CSS that quietly disappears across federation chunk boundaries. Each one has a fix worth keeping.
augment-it now does the thing it is named for. Two new microservices land — prompt-store holds prompt templates, prompt-runner makes the LLM calls — and together they close the augmentation loop: write a prompt with {{column}} placeholders, run it per-row against a record set, get back a derived record set with the model's answers as a new column. The proof-of-life was a web-search-backed prompt that turned a column of organisation names into a column of website URLs. This is the first time augment-it has called an LLM.
Yesterday's question was 'how do I see it?' — the enrichment loop worked but only from a smoke script. Now it has a UI. prompt-template-manager is augment-it's second federated remote: a prompt editor with a live {{token}} strip, a bind check that turns green or red against whichever record set you pick, and a run control that streams progress. It mounts as the second tab in the shell. The five-phase walking skeleton from the plan is complete — you can author a prompt, run it, and watch a derived record set appear, all without touching a terminal.
augment-it had its colours hardcoded as hex scattered across five CSS surfaces. Now it has a theme system: a shared @augment-it/theme package with one theme.css holding a two-tier token architecture and three mode blocks, a mode-switcher, and a sun/moon/star toggle in the shell chrome. Light, dark, and vibrant all work across both federated remotes — switched by one data-mode attribute on a shared <html>. The Astro Knots theme blueprint, adapted for a non-Astro, non-Tailwind, federated app.
augment-it's shell stops taking turns. Where it used to mount one federated frontend at a time and destroy it to show another, it now mounts several at once and tiles them like a desktop window manager. Three layout modes: a peek-deck where the focused app sits at 90% with its neighbours peeking from the edges and hover-expanding, a co-existence split where two apps share the viewport behind a draggable seam, and plain full-width. Layout state persists per-user behind a seam the shared-auth store will back later.
ai-labs grew a fourth active child (augment-it) and three of memopop-ai's apps started shipping their own changelogs, but the splash didn't know it. This round teaches the rollup to recurse into pseudomonorepo children's `apps/*`, adds a dedicated `/studies/` landing page that reads each study's `.gitmodules` and README at build time, surfaces augment-it in every count and card on the homepage, and wires OpenPanel analytics the same way the other splash sites do it.
Earlier this month an exploration in this tree argued that every AI product under ai-labs needs its own auth story but none of them is at the scale that justifies Auth0/Clerk pricing. The doc proposed ~800 lines of owned auth, a libSQL-backed session model, OAuth via the small `arctic` library, an opaque cross-app `lossless_id`, and a stub seam for future rollup. It estimated three sessions of work plus a package extraction. Tonight the first real install landed — in chroma-decks, greenfield, with the four 'Session-2 additions' from the spec baked in from day one. The pattern is validated against a real-world app, the cross-app organization-naming convention is locked into the exploration as binding policy for every AI product in this tree, and the next AI product that needs auth (memopop-ai, augment-it) installs from a 6-phase template that took ~3 hours to produce on a greenfield codebase.
A++.2 closed: `/play/[slot].astro` now consumes `<DeckOverlay--Play-UI>`, replacing the inline calmstorm-port chrome bars with the themable `<DeckChrome>` floating capsule and lighting up the in-play `<SlideRankPill>` via the section-wrap adapter. Phase 1 of the TOC-redesign plan also shipped: the audits storage migrated from one-rating-per-(deck, variant, slot) (schema 1) to up-to-two-ratings-per-row keyed by surface (schema 2 — `{scroll?, play?}`), with an auto-migrating loader, a one-shot CLI migrator, and the chroma audits file rewritten in place against its 18-row backlog. And the workflow-shaped TOC the founder asked for — variants as columns, slots as rows, paired scroll-rating + play-rating chips per cell, per-column shippability rollup, per-row drift indicator — is now a written plan ready for execution.
Mid-afternoon detour from the dididecks TOC redesign: the calmstorm-decks fundraise deck was stuck in an auth loop — submit `staycalm`, land on a page once, then every subsequent navigation bounced back to `/access`. Server logs said `[middleware] /play/variant/v2: no cookie → /access` even though DevTools showed `cs_session` sitting in the browser cookie jar with all the right attributes. The HAR export looked like the browser wasn't sending cookies at all. The real culprit, after three rounds of red herrings: `/play/variant/[variant].astro` and `/play/section/[slot].astro` both had `export const prerender = true`. In Astro 6 with `output: "server"`, prerendered routes serve through a code path where the middleware's request context doesn't surface the inbound `Cookie` header — so the cookie was being set correctly and sent correctly, but the middleware was structurally blind to it. Worse, in production those routes would be static HTML files served directly by the Vercel CDN, **bypassing the auth gate entirely**. Flipping both to `prerender = false` makes every request to a gated route flow through the middleware properly, in dev and prod. The auth hardening done before discovering the prerender issue (explicit `Set-Cookie` headers on manually-constructed redirect Responses in verify / logout / link redemption) stays — it removes ambiguity from a part of the stack that has bitten this codebase before.
After the morning's TOC matrix work and the calmstorm auth detour, the founder pulled up calmstorm-decks' /play surface and named what was missing from the shell side: the rich top-and-bottom border chrome that exposes section title, status rating buttons, variant cycle pill, audit link, keyboard hints, and variant switches. Today's third chunk lands that as `<DeckFrame--Play-UI>` — a new full-chrome component sitting alongside the existing floating `<DeckChrome>` capsule. The bigger move tucked inside it: a new orthogonal dimension on top of the shell's existing `surface` axis — `intent: "workflow" | "presentation"` — that gates which affordances render. Workflow chrome is the VC-firm / Lossless-Group authoring view (variant cycle pill, inline status, audit link, variant switches, 1-4 status keyboard). Presentation chrome is the end-user / LP view (exit, section title, counter, clean nav hints — no workflow controls at all). One URL parameter (`?intent=presentation`) swaps the whole thing. Two follow-ups baked in: an Astro scoped-CSS bug that was silently breaking the `C` chrome-toggle key, and a UX iteration that replaced the row of N-1 "Switch to {variant}" buttons with a single "Variant ▾" popover menu navigable by click or arrow keys.
Three in-browser review passes on chroma's index produced four DeckMatrix refinements and one plan revision. The matrix the morning shipped was structurally right but visually noisy and wrong on two specific decisions — the per-surface rollup rendered as five separate count pills wrapping onto two lines per surface (ten pills per column for a 4-variant deck); the column-level OPEN link buried the scroll-vs-play surface distinction into one button that dumped readers into 'the deck' without saying which surface; and a `getStaticPaths`-free variant with zero slots was vacuously satisfying the Ready badge's 'no blockers found' check. Fixing those produced one larger consequence: the per-variant `/toc/[deck]/[variant]/` route, originally slated for deprecation-to-redirect in Phase 6 of the redesign plan, gets to stay alive — the deck-level matrix and the per-variant TOC serve different workflow needs (cross-variant comparison vs. land-in-one-variant), and the variant-name in each column header now links to the latter as the variant's landing surface.
Chroma's home page already had a reviewer audit panel — the four-tile row above the matrix counting variants, slide files, people, companies — but the People and Companies tiles pointed at /data/team/ and /data/investors/ which were never actual routes. The audit pages themselves existed in calmstorm-decks (as /data-assets/companies and /data-assets/people) but had never been ported to the shell. This entry closes both gaps. Two new shell-injected routes — /data-assets/companies and /data-assets/people — discover .md files via consumer-root Vite globs that catch both calmstorm's single-firm data layout (data/firms/{firm}/portfolio|team/) and chroma's multi-investor layout (data/investors/{firm}/portfolio/, flat data/team/) without configuration. A new <DeckStatsPanel> shell component replaces chroma's local AssetsDataPanel, doing its own count math from registries + globs so the figures always match the audit pages. The work also surfaced three Astro-pipeline traps worth documenting: JSDoc terminators inside path strings, esbuild misclassifying inline block comments + slash-heavy regex literals as unterminated regex, and Astro's <Image> taking down a 373-row page over one corrupted SVG in the consumer's data tree.
DidiDecks has shipped two client engagements so far (calmstorm-decks, chroma-decks) and a third is in flight. Both production sites carry sensitive fundraise materials: portfolio company graphs, named advisors, deal flow that's not for the open web. Calmstorm had an owned-auth gate from day one. Chroma did not — `output: 'static'`, no DB, no middleware, no /access. Tonight that changed: chroma got the full calmstorm pattern installed greenfield (with four exploration-named additions baked in), the shell picked up two latent prerender-bypass fixes that benefit every consumer, and the deck-level organization-naming convention got locked into the parent ai-labs exploration. The pattern is now ready to install into every future client engagement with one well-shaped plan file.
The 'Open in Obsidian' button was silently doing nothing. Turns out `open -a Obsidian /some/folder` is a no-op unless that folder *is* a vault — so we taught the app to walk up to the nearest `.obsidian/` and route through `obsidian://` URIs instead. While we were in there, the deal-workspace header that hosted that button got a long-overdue redesign.
After Phase A+ shipped `@dididecks/shell` to its first cross-client functional state on 2026-05-12, the next four days were spent giving the shell its bones: a calmstorm-grade themable chrome (with the `--ddd-chrome-*` CSS-custom-property contract), a `<SlideCanvas>` + `/print` PDF-export route, the `<DeckOverlay--Scroll-UI>` / `<DeckOverlay--Play-UI>` paired-suffix discipline for any UI-mode-specific composition, a `context-v/sitemap/` living map that turns the shell's component+route surface into agent-readable mini-specs, and a status-discipline framework codified at the skill level that swept all nine plans into accurate state-of-record. None of this is shippable code-as-product on its own; together it's the architectural ledger that makes the next iteration tractable.
The changelog and context-v list pages now carry a Modified / Created / Published / Title chip-group plus a direction toggle, with per-page localStorage persistence and server-pre-sorted defaults so the static HTML reads correctly with JS off. Single follow-up remains for full skill conformance: the /llms.txt + /llms-full.txt endpoints.
Two endpoint files, three markdown templates, one small provenance helper, and a `<link rel="alternate" type="text/markdown">` in BaseLayout — agentic crawlers can now ingest the entire pseudomonorepo (parent + three children, 11 changelog entries, 29 context-v notes) in one fetch instead of crawling 44 HTML pages.
Chat-preview is the surface most readers will see the splash through first — iMessage, WhatsApp, Slack, Discord all crop unfurls to a square-ish landscape, and the old 1.78:1 banner crop weirdly. The new 890×760 Messages card (real JPEG, no ImageKit content-negotiation surprise) fits that crop natively and is now what every page emits in its og:image sextet.
A small Astro site at splash/ that ships to GitHub Pages on push to main, renders the repo's changelog/ and context-v/ alongside curated copy about the six module-federated microfrontends.
A long day inside calmstorm-decks surfaced four patterns that are now ready to migrate up the tree and seed every other client-site: a unified two-axis navigation grammar across Scroll and Play modes, a two-route discipline that splits the design contract from the component catalog, a typed component registry as single source of truth for both stakeholders and the embedded chat-composer, and a `ContentFit + swappable info-display` mechanic that ends the per-slide font-and-margin tuning war. None of these are calmstorm-specific.
DidiDecks just gained its first shared frontend module. `@dididecks/shell` — an Astro integration that wraps a client-site with shared chrome — boots inside chroma-decks today, injects a TOC route and a slide-ranking API, and operationalizes the Phase 1 → Phase 2 boundary of the deck-iteration workflow with a one-click `scaffold per-slide file` button. Each client-site stays a standalone repo deploying to its own Vercel project; only the shell travels. Along the way we learned why a workspace dep can't cross a `.npmrc` line that says `ignore-workspace=true` — a lesson the eventual publish solves cleanly.
Phase A shipped the integration that proved the architecture works. Phase A+ — landed the same day — closes the three concrete gaps the founder named seconds after first opening the smoke-test build. The shell now ships a global nav that auto-mounts on every shell-injected route, a floating `<SlideRankPill>` that lets a founder rank slots from inside a scroll deck via IntersectionObserver, and a `/play/[deck]/[variant]/[slot]` runtime with keyboard nav (← / → / Space / Home / End / F / C / T / Esc) plus a friendly empty-slot placeholder. Two seed per-slide files in chroma — a faithful cover recreation and a redesigned ask — prove the rank → decompose → recreate loop end-to-end. The whole release moves the shell from `0.0.1` to `0.1.0-rc.0`. Workspace-link mode throughout; Phase A.7 publish still queued.
Two related moves on the same day: the slide-deck work that was mis-parented under astro-knots became a first-class child of ai-labs (new repo, new specs, two client engagements re-attached as nested submodules), and ai-labs itself grew a splash page — three-mode + two-tier tokens, mono-forward bench aesthetic, rolled-up changelog + context-v across all three active children.
Yesterday the splash was scaffolded assuming everything lived under `/ai-labs/`. But the GitHub repo is `lossless-ai-labs`. Without correcting the base path, Pages would have 404'd every internal link, image, sitemap entry, and OG URL on first deploy. This entry fixes that mismatch and adds the `.github/workflows/pages.yml` that bootstraps Pages on push to `master`.
The skill says every splash ships search-by-default and I had quietly demoted it to a follow-up. Caught. Pagefind is now wired: header popover with `/` shortcut, full `/search/` page, three filter facets (kind / from / tag), and the UI tokenized to the splash's three-mode contract. 38 detail pages indexed across ai-labs and its three children.
Promoted DidiDecks from `ai-labs/context-v/specs/` folder to a first-class child of the ai-labs pseudomonorepo — its own GitHub repo, its own three-tier branches, its own context-v/ + changelog/, its own splash, and the two driving client engagements re-parented from astro-knots.
Pipeline & Automation lands as an 11-slide scroll deck — the Lossless side of the partnership, mapped to the Build Smarter pillar. While building it we found `/story` v1 baseline never responded to the mode toggle, so we retrofitted that too. Both decks now read clean in light, dark, and vibrant; the section-indicator pill stops glowing as a white artifact on dark backgrounds.
Saturday the splash started speaking llms.txt to LLMs. Tonight it learned to speak sitemap.xml to search engines, robots.txt to crawlers, and a 1024×1024 JPG to share-preview bots. The three audiences each get a machine-readable handhold pointed at the same corpus.
The memopop-site splash now serves the same machine-readable discovery surface that landed in context-vigilance-kit over the past two days, ported with the adaptations memopop needs — two content collections instead of one, provenance via `from` instead of `source_repo_slug`, and no publish gate to mirror. LLM crawlers get `/llms.txt` + `/llms-full.txt`, search engines get `sitemap-index.xml` + `robots.txt`, and the head of every page carries explicit `rel="sitemap"` and `rel="alternate" type="text/markdown"` hints.
Context Vigilance's splash now phones home to OpenPanel like its siblings — production-only, clientId-from-env, no secrets in the browser. The interesting part wasn't this wiring; it was figuring out why the other three splashes had been silently 401-ing for a day.
The splash now serves the llms.txt standard at /llms.txt (a 132 KB link index, grouped by source repo) and /llms-full.txt (the full 5.7 MB corpus concatenated as raw markdown). LLM crawlers and agentic tools that follow the convention can ingest 460 hand-authored context-v documents in one fetch.
One day across ai-labs: a third study repo lands, the investment-memo orchestrator stops pretending to be a sibling and folds into memopop-ai where it has always belonged, memopop gains a /find-team Firecrawl command, and a new context-vigilance-kit submodule starts collating context-v/ files across the whole tree.
Context Vigilance gets its public face: a dark-default engineer's-clipboard splash that walks readers through Hook → Practice → Proof → Invitation, opens with a Google-style 'what context do you need?' prompt over the 587-doc corpus, and ships to GitHub Pages on every push.
A new Firecrawl-driven pipeline walks any organization's website, finds the team page, extracts a structured roster, and back-fills missing socials by name. Phase 2 of Team-and-People-Metadata-Ingestion ships.
memopop-site catches up to the astro-knots/splash conventions that crystallized after this site was originally built. The marketing prose stays; everything underneath gets rebuilt around two-tier tokens, a three-mode (light · dark · vibrant) toggle, a monorepo-aware discovery layer that auto-surfaces new apps, a brand-new /context-v/ archive alongside /changelog/, build-time Pagefind search across both, and a custom MemoMark logo.
Corrected vibrant mode implementation to be dark-based (not light-based) with multi-color gradients, glassmorphic surfaces, and neon borders. Light and vibrant modes now visually distinct.
From a placeholder home to a working hub in one session — two scroll-deck story variants, Tailwind wired to all three modes, a sticky Header with a scroll-deck variant cycler, and a homepage that finally says what the partnership is for.
After last entry's end-to-end build, today is the second pass — the tab now reads Reach goes Lossless, the Header carries a circular favicon, the index has an editorial right-bump and a 70vw content band, a three-column cross-credit Footer ported from fullstack-vc lands at the bottom, and the mode toggle finally lights up in dark and vibrant.
From an empty Astro starter to a deployed splash page in a single session. Dark Lossless redesign, a click-to-expand GUI callout, and — the centerpiece — a permissive content-collection renderer that turns four scattered project-level changelogs into one unified timeline.
Six steam-punk character portraits — Financial-Analyst, Researcher, Writer, Editor, Fact-Checker, Scorecard-Generator — now sit above the JobView's 3-column grid during a memo run. The character whose stage is active glows with a slow breathing border; the rest stay visible but quiet. A live caption under the active character names the work in progress, swapping as the underlying milestones advance. The cast is authored in a single human-editable markdown file (`src/lib/characters.md`) — frontmatter holds names, stage mappings, default captions, and forward-compat agent-precise captions for v2. Mike retunes the wording without touching code.
First changelog at the monorepo level. Catching up the eight days that took memopop-native from `bun create tauri-app` to a fullscreen client driving the orchestrator's HTTP API end-to-end.
End-to-end integration of the desktop client with the orchestrator's FastAPI sidecar across four phases: pytest coverage on the server, a Rust transport layer with lazy spawn and /healthz poll, a TypeScript Transport implementation with SSE event subscription, and a Generate flow that streams milestones live into the UI.
The sidecar gains structured milestone events extracted from agent prints, and a missing scaffold step that was sending firm-scoped runs to legacy output/.
First contact with the desktop client surfaced four ways the orchestrator-as-a-service could go sideways. All four are closed in this pass.
A new brand_fetch module drives a Claude tool-use loop over a firm's homepage, and two endpoints expose it. The HTTP surface keeps growing; agents stay untouched.
First post-memo onboarding step. Click 'Make this look like your firm →' in the JobView's success banner (or 'Brand setup →' in Settings), paste the firm's website URL, watch Claude tool-use its way through the homepage and a couple of linked pages, then review every field on a confirmation form before anything saves to disk. ~15s wall time, ~$0.05–0.15 per fetch via Haiku 4.5. Brand managers stay happy because every Claude guess — colors, fonts, logos, taglines — is editable with live swatches and logo previews before save. Output merges into io/{firm}/configs/brand-{firm}-config.yaml, deep-merging with the firm-creation scaffold so company.conventional_name is never clobbered.
The investment memo orchestrator has been a 30-agent Python CLI for months — you type a company name, wait 15 to 45 minutes, and a fully cited 50-file deal memo lands in `output/`. It just works. Now it also speaks HTTP.
Stood up apps/memopop-native — a Tauri 2 desktop shell wrapping the investment-memo-orchestrator's CLIs — inside the existing memopop-ai Bun workspace. Verified prerequisites (Xcode CLI, Rust, Bun), updated Rust to current stable, scaffolded via bun create tauri-app with TypeScript + Svelte, brought the frontend stack to current within two safe waves (minor + coupled-major), added the four Tauri 2 plugins we'll need (shell, dialog, store, fs). Window opens, IPC works, baseline is clean.
First real feature in memopop-native: a Settings screen that picks the orchestrator repo path and the active firm, persisted across launches. Beneath the small UI, this lands the architectural seam — a Transport interface, a LocalTransport implementation backed by a single Rust api_dispatch command, and the first route (GET /firms) wired end-to-end. Every future action and query rides on this same rail; the eventual swap to a hosted Python server is now ~1 day of frontend work, not a rewrite.
Built out the full onboarding user journey end-to-end against the Onboarding spec. Home view stops being Settings — it's now an outline gallery (or, on first launch, a single Browse-for-orchestrator panel). New users see comprehensive sample outlines first (the value), and only get charged onboarding taxes when they click 'Try this on a company →'. Firm creation is a small modal that snake_cases the input, scaffolds io/{slug}/configs/brand-{slug}-config.yaml with company.conventional_name only, and idempotently appends /io/*/ to the orchestrator's .gitignore. Deal capture (URL, optional name, deck, mode) collects inputs and confirms — the actual memo-generation runner is the next spec. New backend routes: GET /outlines, GET /outlines/{id}, POST /actions/create-firm. Auto mode session, no testing run yet.
New guided CLI application that walks users through memo generation, export, and iteration without requiring knowledge of individual commands, flags, or file paths.
The Table of Contents was consistently missing from final memo output despite the `toc_generator` agent running successfully mid-pipeline. Two separate ordering bugs caused the TOC to either never generate or get destroyed before finalization.
Introduces a three-step LLM-powered fact-check pipeline (extract → verify → correct) and a per-section comprehensive source catalog. The mechanical-only fact-checker is upgraded to use Perplexity Sonar Pro for independent claim verification and Claude for surgical corrections.
Strengthens entity disambiguation to prevent research agents from conflating similarly-named organizations. Adds both improved prompt language and a mechanical post-synthesis override that forces correct company identity regardless of what the LLM returns.
New pipeline agent that mechanically enforces correct markdown citation spacing — no LLM, pure regex. Fixes the recurring problem of clumped, duplicated, and improperly spaced citations that affect Obsidian rendering, HTML/PDF exports, and readability.
New pipeline agent and standalone CLI tool that distills the full investment memo into a single-page visual summary. Serves as both a standalone deal teaser and a cover sheet for the full memo export.
Consolidated all final draft file resolution into `src/final_draft.py` and bumped the canonical prefix from `6` to `7`. Previously, three agents each had their own inline glob logic to locate the final draft — now they all call shared utility functions.
New standalone CLI for running the table generator agent outside the full pipeline. Scans memo sections and structured state data for tabular data opportunities, generates markdown tables, and inserts them into section files.
Rewrote the deck screenshot classification, naming, and injection system to fix two core problems: (1) Claude Vision was misclassifying slides because it only had 7 vague categories to choose from, and (2) the injection agent was spamming sections with duplicate, poorly-placed im…
Two new agents that discover, evaluate, and classify competitors using Perplexity Sonar Pro. The system splits competitive analysis into two phases with opposing cognitive biases: the researcher uses **completion bias** to find as many candidates as possible, while the evaluator…
A complete standalone module for parsing market research PDFs (McKinsey, CB Insights, PitchBook, Gartner, etc.) into markdown with properly formatted Obsidian-style citations.
Fixes to the citation pipeline addressing two critical issues: (1) the writer agent's regex only matched numeric citation keys (`[^1]`) and missed alphanumeric keys (`[^deck]`), causing citation loss during polishing; and (2) the citation enrichment agent was appending duplicate…
Established the production-grade memopop-ai monorepo with Bun workspaces, Astro marketing site, SvelteKit web app skeleton, and shared Tailwind configuration.
Critical fixes to the citation pipeline that were causing citation definitions (the source references at the bottom of files) to be lost during processing.
This release fixes an issue where the 12Ps Scorecard Summary section (section 08) would contain LLM "excuses" instead of actual scorecard data.
This release refactors final draft operations into a single centralized module (`src/final_draft.py`) and updates the artifact numbering scheme.
When resuming a workflow with `cli/resume_from_interruption.py` for firm-scoped deals, the writer agent would use legacy `output/` paths instead of firm-scoped `io/{firm}/deals/{deal}/outputs/` paths.
Fixed a critical issue where Perplexity was returning garbage/meta-commentary for non-standard section names like "Opening" and "Organization" in the 12Ps outline.
This release adds automatic 12Ps scorecard integration to the memo workflow. Previously, the scorecard evaluator generated detailed scores in `5-scorecard/12Ps-scorecard.
This release adds explicit wrong-entity exclusion to the disambiguation system. Deal configurations can now specify a `disambiguation` array of URLs/domains that belong to **different companies** with similar names.
This release completes the firm-scoped IO system, enabling private, firm-specific configurations, inputs, outputs, and exports. The system allows each firm (e.g.
Added a comprehensive scorecard evaluation system that scores completed investment memos against a structured 12-dimension framework (12Ps: Persona, Pain, Proposition, Problem, Possibility, Positioning, People, Process, Product, Potential, Progress, Plan).
Began major refactor to reorganize the flat `data/` and `output/` directories into a hierarchical `io/` directory organized by firm and deal. This enables private git submodules for firm-specific data while keeping the orchestrator open-source.
Completed the firm-scoped IO path resolution system, enabling deals to be stored in `io/{firm}/deals/{deal}/` with automatic fallback to the legacy `data/` and `output/` directories. All agents now pass the `firm` parameter through the workflow.
Added a focused set of CLI tools to tighten the memo workflow end-to-end:
A memo for Sava, the YC trust-administration startup, confidently reported a $19M Series A led by Balderton — except that round belonged to Sava Technologies, a biosensor spinout. The data was real and well-cited, just about a different company.
Three CLI tools each had their own way of stitching a memo back together, so citation numbers collided and the table of contents went stale. Now there is one assembler, and everything else delegates to it.
Three new CLI tools let you repair a finished memo without regenerating it: add citations to prose you already like, hyperlink every organization it mentions, or score the whole thing and see the breakdown.
Added explicit "Venture Capital mindset" framing to all memo-writing agent prompts to ensure generated content focuses on opportunity and upside rather than defaulting to Private Equity-style skepticism.
Created a comprehensive outline for evaluating LP commitments to emerging manager VC funds, integrating Hypernova Capital's proprietary 12-dimension GP evaluation framework directly into the memo generation system.
Implemented the Scorecard Agent and CLI tool to automatically generate Hypernova Capital's 12-dimension scorecards for emerging manager fund commitment memos. This completes the scorecard system introduced in changelog 2025-11-27_01.
Introduced a new multi-agent dataroom analyzer system for processing investment datarooms, plus improvements to trademark handling and export versioning.
Implemented Phase 2 of the Dataroom Analyzer: the Competitive Extractor. This extracts and synthesizes competitive landscape data from battlecard PDFs and outputs structured analysis as separate, numbered artifact files for better manageability.
Added two new extractors to the Dataroom Analyzer: Cap Table Extractor and Financial Extractor. These complete the core Phase 2 extractors for investment dataroom analysis.
Added the Traction Extractor to complete Phase 2 extractors (4 of 5). This extractor pulls customer metrics, revenue data, pipeline information, and partnerships from pitch decks and other traction-related documents.
Added the Team Extractor, completing all Phase 2 extractors (5/5). The dataroom analyzer now extracts comprehensive structured data from investment datarooms.
Added a new TOC Generator agent that automatically creates a Table of Contents with working anchor links for the final investment memo. The TOC includes main sections (h2) and subsections (h3) that function in both HTML and PDF exports.
Added the Synthesizer module to cross-reference data across all extractors, detect conflicts, identify data gaps, and generate a unified view of company metrics with confidence scores.
Major refactoring to organize 38 scattered Python files and shell scripts into logical directories, improving developer experience and project maintainability.
Implemented checkpoint-based resume functionality to recover from workflow interruptions without losing progress or re-running expensive API calls. System now detects completed agents from saved artifacts and resumes execution from the last successful checkpoint.
Enhanced PDF export quality with proper logo rendering, Unicode symbol support, and GHCP Partners brand configuration. Fixed critical rendering issues that caused logo distortion and citation symbols to display incorrectly in WeasyPrint-generated PDFs.
Implemented comprehensive prompt engineering fixes to eliminate LLM fabrication of pricing, revenue, traction, and financial metrics in investment memos.
Integrated Perplexity's `@source` syntax into the research workflow to ensure high-quality data from premium sources like @crunchbase, @pitchbook, @statista, and @cbinsights.
Implemented the fact-checker agent to prevent hallucinated metrics and claims in investment memos. This agent verifies all factual claims against research sources, catching fabricated revenue figures, pricing data, customer counts, and other metrics before final memo assembly.
Complete implementation of image-based PDF deck analysis using Claude's Vision API with intelligent batch processing. This solves the recurring issue of Docsend-exported decks that contain only images with no extractable text.
Complete integration of Perplexity Sonar Pro for section-specific research WITH citations as a core part of the memo generation pipeline. This fundamentally changes the architecture from "write then add citations" to "research with citations then polish.
Complete refactor of the enrichment pipeline to process memo sections individually rather than as a single monolithic document. This eliminates API timeout issues and enables proper citation management with Perplexity Sonar Pro.
Major architectural improvements to prevent API timeouts through section-by-section processing, plus comprehensive brand expansion with MaC VC assets and SVG logo support.
This session focused on building and refining a comprehensive export/conversion system for investment memos, enabling professional PDF generation with Hypernova Capital branding.
Implemented a comprehensive multi-brand export system that allows managing and exporting memos for multiple VC firm clients from a single installation.
Solved a critical PDF rendering issue where page backgrounds didn't extend to all edges and content lacked proper per-page spacing. Discovered WeasyPrint's `@page background-color` property enables edge-to-edge colored backgrounds while maintaining proper content margins on every…
Implemented automatic citation consolidation in HTML exports to match Obsidian's footnote behavior. When Perplexity AI cites the same source multiple times using `[^2]` throughout the markdown, Pandoc was incorrectly converting each instance into a separate numbered footnote, res…
Four enrichment agents now run after the draft is finished — links, visualizations, socials, citations — turning plain prose into a memo with 19 organization hyperlinks and footnotes that actually resolve in Obsidian.
Historical entry — Core Multi-Agent System, Templates & Style Guides, CLI Interface.
Five specialized agents under a LangGraph supervisor, wired to live web search — the Week 1 proof of concept wrote a real investment memo on Aalo Atomics and scored 7.5 out of 10 against its own validator.
Citations finally work. A new enrichment agent slots in between the writer and the validator, and every run now leaves a 16-file artifact trail you can audit step by step.
The pitch deck now goes in first: a new agent reads the PDF before any web research runs, because proprietary beats whatever the internet knows. Separate templates for direct versus fund investments landed alongside it.
Before writing the next line of code in the record-collector submodule, we wrote down what it actually has to do — input shapes, output contract, edge cases, and where it sits in the broader workshop.
A morning of issue-triage turned the next month of augment-it work into five named threads — and named the loose ends honestly.
Five planning docs that had been sitting at the repo root got consolidated into specs/. A small move, but it set the precedent for where future specs would land.
A 3,064-line documentation drop that gave augment-it a written voice — the kind of artifacts a new collaborator can read end-to-end before touching code.
The Card component was duplicated across two microfrontends. Tanuj lifted it into packages/ui as RecordCard, the first non-trivial component to live in the shared package — and the proof point for how cross-MFE components will work going forward.
The bolt.new monolith had served its purpose. We tore the build system down to studs and rebuilt on a turborepo + pnpm + Docker base ready to host federated microfrontends.
Tanuj brought up a minimal-but-real module-federation scaffold — a host shell that loads two remote microfrontends from the same monorepo, each exposing a Card component, all sharing a single ui package.
The two placeholder microfrontends got promoted out of the parent repo and into three named, independently-versioned submodules — the first concrete instance of the federation contract.
Two months after the Bolt monolith hit working-demo state, we tried to peel record-collection off into its own frontend repo. Two days, three commits, one DB swap (turso out, Supabase in), then we set it down to think harder.
A late-January progress update with embedded GIF demos from the pre-restart Augmenter App: API options and editor, response list, user profiles, highlighter, and grouping by customer.
Before there was a federation, there was one big React app: Bolt-vibed, Supabase-backed, three LLM providers wired in, every screen in the workshop already shipping as a component. This is where we found out what the workshop wanted to be.