The Workshop Architecture: Unifying Agentic Skills and Knowledge

🤖 Read Raw Markdown

Setting the Stage: Context for the Curious Book Reader

In the fast-moving Age of AI, developers and non-developers alike are navigating a fragmented landscape of proprietary agent formats. This article explores a synthesis of the latest conventions—Karpathy’s LLM-wiki, Anthropic’s SKILL.md, and Google’s Open Knowledge Format—to establish a unified, tool-agnostic architecture. By treating the filesystem as a reliable, compounding substrate, we can build a collaborative ‘Workshop’ that remains durable regardless of vendor churn.


Technical Journal Entry Begins

MikeLev.in: Find for me the latest so-called vibe-coding conventions per:

  1. Andrej Karpathy who started it all and his LLM-wiki stuff
  2. Anthropic who proposed formal versions of the AGENTS.md an SKILL.md stuff
  3. Google who more recently put out competing white-papers on their own version or take on it

All of this is part of the file-system and text-files over vector embedding and RAG movement. I want deep dive into the up-to-the-moment conventions and any competing standards if there are any with as much detail as a tree representation of the directory structures they’re advocating and how those manifest as the deterministic procedures agents are supposed to follow to discover their available skills and achieve set goals and such.

Claude Opus 4.8 Low: I’ll research this thoroughly — these are fast-moving conventions and I want current detail, not stale recollection. Let me dig in.Good origin material. Now let me find Karpathy’s more recent “LLM-wiki” / documentation-for-agents conventions.Let me pull Karpathy’s actual gist to get the exact structure he advocates.I have Karpathy’s full pattern. I also spotted Google’s “Open Knowledge Format” referenced — good lead. Now Anthropic’s Skills spec.Now let me verify AGENTS.md’s actual origins (worth checking the attribution) and then Google’s contributions.AGENTS.md is clearly not Anthropic’s — that’s an important correction. Now the Google leg: their Open Knowledge Format and related whitepapers.Let me get the exact OKF spec structure — the user wants precise tree layouts and deterministic procedures.This is a genuinely rich and fast-moving space, and your three-way framing is the right one — but one factual correction up front that matters for the article: AGENTS.md is not Anthropic’s. It came out of an OpenAI-convened working group (OpenAI Codex, Sourcegraph/Amp, Google’s Jules, Cursor, Factory) and is now stewarded by the Agentic AI Foundation under the Linux Foundation. AGENTS.md emerged from collaborative efforts across the AI software development ecosystem, including OpenAI Codex, Amp, Jules from Google, Cursor, and Factory, and is now under Linux Foundation stewardship. Anthropic’s actual formal contributions are SKILL.md / Agent Skills and CLAUDE.md (their hierarchical-memory analog to AGENTS.md). I’ll map all of this precisely below, because the lineage is half the story.

Karpathy and the Compounding Wiki Pattern

Anthropic’s Skill-Based Progressive Disclosure

The Hierarchical Standard of AGENTS.md

The through-line you’ve identified is real and the practitioners name it explicitly: this is the files-and-filesystem-over-vectors movement, defined in opposition to RAG. Karpathy frames the wiki as a compounding artifact precisely because most people’s experience with LLMs and documents looks like RAG: you upload a collection of files, the LLM retrieves relevant chunks at query time and rediscovers everything from scratch each query. Google’s OKF announcement frames its format as the formalization of that same anti-RAG “LLM-wiki pattern.” So the three efforts are genuinely converging on one substrate.

Here’s the deep dive, leg by leg.


1. Karpathy — the originator, and the “LLM Wiki”

The lineage. Three dated moves, each shifting more labor to the model:

  • Vibe coding (Feb 2, 2025, on X): a new kind of coding where you fully give in to the vibes and forget that the code even exists — originally meant for throwaway weekend projects, using Cursor Composer + Sonnet + voice input.
  • Agentic engineering (his preferred reframe, ~Jan 2026): humans orchestrate agents rather than typing code.
  • LLM Wiki (April 3–4, 2026): the “idea file” gist. This is the “LLM-wiki stuff” you’re after. It’s not code or a product — it’s a pattern document meant to be pasted into Claude Code, Codex, or any agent so the agent instantiates the specifics with you.

The architecture. Karpathy specifies three layers, and crucially names the schema file as either CLAUDE.md or AGENTS.md depending on your agent:

llm-wiki/
├── CLAUDE.md  (or AGENTS.md)    # LAYER 3 — THE SCHEMA
│                                #   rules, conventions, ingest/query/lint workflows
│                                #   the human + LLM co-evolve this; it makes the
│                                #   LLM a disciplined maintainer, not a chatbot
│
├── raw/                         # LAYER 1 — RAW SOURCES (IMMUTABLE)
│   ├── assets/                  #   downloaded images (so the LLM can view them)
│   ├── paper-2024.pdf           #   articles, PDFs, transcripts, data files
│   └── article-clip.md          #   LLM reads, never modifies. Source of truth.
│
└── wiki/                        # LAYER 2 — THE WIKI (LLM owns this entirely)
    ├── index.md                 #   content catalog: every page + 1-line summary
    ├── log.md                   #   append-only chronological record
    ├── entities/                #   entity pages
    ├── concepts/                #   concept pages
    ├── sources/                 #   per-source summary pages
    ├── comparisons/             #   analyses filed back from queries
    └── overview.md / synthesis.md

The layer split is load-bearing: raw sources are immutable — the LLM reads from them but never modifies them, while the wiki layer is entirely LLM-written. His one-liner for the mental model: Obsidian is the IDE, the LLM is the programmer, the wiki is the codebase.

The deterministic procedures. Karpathy defines three named operations the schema file should encode:

  • Ingest — drop a source into raw/, tell the LLM to process it. It reads the source, discusses takeaways, writes a summary page, updates index.md, updates relevant entity/concept pages, appends to log.md. He notes a single source might touch 10–15 wiki pages.
  • Query — ask a question; the LLM reads index.md first to locate relevant pages, drills in, synthesizes with citations. Key discipline: good answers get filed back as new pages so explorations compound.
  • Lint — periodic health check: find contradictions, stale claims, orphan pages, missing cross-references, gaps fillable by web search.

The discovery mechanism here is deliberately low-tech and deterministic: the index.md is the retrieval index at moderate scale (~100 sources), explicitly avoiding embedding infrastructure. And log.md is designed to be greppable — he suggests a consistent prefix like ## [2026-04-02] ingest | Title so grep "^## \[" log.md | tail -5 works. That grep-able log detail will resonate with your Pipulate/Unix-philosophy sensibilities.

One thing worth mining for your article: the gist’s comment thread became a live laboratory. Practitioners independently hit the same scaling walls and converged on the same fixes — deterministic Python scripts for ingest/validation with the LLM reserved for judgment (your exact split), commit gates implemented as cheap os.walk + grep passes that never touch the context window, and subgraph-scoped linting to avoid O(n²) context blowup. Several of them also independently landed on Google’s OKF as the convergence point.


2. Anthropic — SKILL.md / Agent Skills (and CLAUDE.md)

This is Anthropic’s genuine formal contribution, and it’s the most rigorously specified of the three. Released as an open standard on December 18, 2025, now at agentskills.io and adopted by 20+ platforms (Codex, Gemini CLI, Copilot, Cursor, VS Code). The Agent Skills format was originally developed by Anthropic, released as an open standard, and has been adopted by a growing number of agent products.

The directory structure. A skill is just a folder with one required file:

my-skill/
├── SKILL.md          # REQUIRED — YAML frontmatter + markdown instructions
├── scripts/          # Optional — executable code (Python, Bash, JS)
├── references/       # Optional — additional docs loaded only when needed
└── assets/           # Optional — templates, images, fonts, data files

The SKILL.md frontmatter requires exactly two fields — name and description — and the body is the playbook:

---
name: pdf-processing
description: Extract text and tables from PDF files, fill forms, merge
  documents. Use when working with PDFs, forms, or document extraction.
---

# PDF Processing
## Quick start
Use pdfplumber to extract text...
For form filling, see FORMS.md

The deterministic discovery procedure — this is the part you’ll want to contrast against Karpathy and Google, because it’s a precise three-tier loading state machine they call progressive disclosure:

  1. Discovery (startup): the agent loads only name + description from every skill’s frontmatter into the system prompt. Measured cost is ~30–80 tokens per skill (xlsx is the heaviest at ~235). This is why you can install hundreds of skills with no context penalty — the agent just knows each one exists and when it’s relevant.
  2. Activation (on match): when a request matches a description, the agent runs read SKILL.md and pulls the full body into context. Recommended ceiling ~5,000 tokens / 500 lines.
  3. Execution: the agent follows instructions, and loads references/*.md or shells out to scripts/* only as the instructions point to them. Code can run without entering context at all — a key efficiency lever.

The canonical worked example: at startup the agent only knows “PDF Processing — extract text/tables, fill forms.” Ask it to extract text, and Claude determines: Form filling is not needed, so FORMS.md is not read — it loads SKILL.md, skips the form-filling reference entirely, executes. That selective non-loading is the whole design.

CLAUDE.md is the piece you were probably thinking of as Anthropic’s “AGENTS.md.” It’s their per-project instruction file, but with a more elaborate hierarchical memory model than AGENTS.md: organization-wide policy, project memory, path-specific rules, and auto-memory that persists across sessions. Anthropic maintains its own system called CLAUDE.md for Claude Code, with a more sophisticated hierarchical memory system. So Anthropic deliberately did not adopt AGENTS.md as its native format — though Karpathy’s gist treats them as interchangeable schema files.


3. The AGENTS.md standard itself (the thing you attributed to Anthropic)

Worth a section because your article conflates it, and because it has the most explicitly deterministic discovery algorithm of all. AGENTS.md is “a README for agents” — plain markdown, no required schema, placed at repo root. Its power is in the hierarchical resolution, and OpenAI Codex’s implementation is the reference:

~/.codex/AGENTS.md            # global guidance (lowest priority)
repo-root/AGENTS.md           # project-level
repo-root/packages/
├── api/AGENTS.md             # package-level (overrides root)
│   └── AGENTS.override.md    # local override, gitignored
└── web/AGENTS.md

The deterministic procedure: Codex builds an instruction chain at startup, walking from project root down to the current working directory, concatenating every AGENTS.md it finds with blank lines between them. Later (deeper) files override earlier ones because they appear later in the combined prompt. Codex stops adding files once the combined size reaches the limit defined by project_doc_max_bytes, which defaults to 32 KiB. Two rules govern conflicts: the closest AGENTS.md to the edited file wins, and an explicit user chat prompt overrides everything. The OpenAI monorepo itself reportedly carries dozens of these files across subcomponents.

Google’s Open Knowledge Format

Google’s parallel here is GEMINI.md (Gemini CLI / Jules), which uses the same hierarchical walk — ~/.gemini/GEMINI.md global, workspace-root, then subdirectory files discovered dynamically as tools touch those paths.


4. Google — the Open Knowledge Format (OKF)

This is the “competing white-paper” you’re thinking of. OKF v0.1, published June 12, 2026 by Google Cloud’s Data Cloud team (Sam McVeety, Amir Hormati), in the public GoogleCloudPlatform/knowledge-catalog repo. It is explicitly the formalization of Karpathy’s LLM-wiki pattern into an interoperable spec. The whole v0.1 spec fits on one page. Its self-description: if you can cat a file, you can read OKF; if you can git clone a repo, you can ship it.

The positioning vs. the others is sharp and your article should foreground it: where AGENTS.md tells an agent how to behave in a repo and SKILL.md packages a capability, OKF describes a body of knowledge/data — tables, metrics, runbooks, APIs. One commentator put the distinction well: AGENTS.md is the nearest by shape, but it instructs a coding agent rather than describing a corpus of data.

Bundle structure (a “bundle” is the unit of distribution; a “concept” is one markdown file, and the file path is its identity):

my_bundle/
├── index.md                  # Reserved. Directory listing → progressive disclosure.
├── log.md                    # Reserved. Chronological change history.
├── datasets/
│   ├── index.md
│   └── sales.md              # concept ID = "datasets/sales"
└── tables/
    ├── index.md
    ├── orders.md             # concept ID = "tables/orders"
    └── customers.md

Concept frontmatter — and here’s the radical minimalism: exactly one required field, type:

---
type: BigQuery Table            # REQUIRED — the only required field in all of OKF
title: Customer Orders          # recommended
description: One row per completed customer order.
resource: https://...           # canonical URI for the underlying asset
tags: [sales, orders, revenue]
timestamp: 2026-05-28T14:30:00Z # ISO 8601
---

# Schema
| Column | Type | Description |
| ... | customer_id → FK into [customers](/tables/customers.md) |

The knowledge graph is built from ordinary markdown links. A link from concept A to B asserts an untyped directed relationship; the kind of relationship lives in the surrounding prose, not the link. Bundle-relative links (/tables/customers.md) are recommended over relative ones for stability.

The deterministic discovery/traversal procedure:

  • Reserved files: only two names are reserved at any directory level — index.md (directory listing, no frontmatter except an optional okf_version at bundle root) and log.md (date-grouped, newest-first, ISO YYYY-MM-DD headings). Everything else is a concept.
  • Traversal: an agent reads index.md to see what’s available, follows markdown cross-links to load detail only as needed — the same index-first, drill-down pattern as Karpathy’s wiki.
  • Conformance is binary and permissive — a bundle conforms if (1) every non-reserved .md has parseable YAML frontmatter, (2) every frontmatter has a non-empty type, (3) reserved files follow their structure when present. Critically, consumers MUST NOT reject a bundle for missing optional fields, unknown type values, unknown keys, or broken cross-links — a link whose target does not exist in the bundle is not malformed; it may simply represent not-yet-written knowledge. That “broken link = a TODO for future knowledge” stance is a deliberate design choice and a nice quotable contrast with how brittle traditional catalogs are.

The Workshop: A Vendor-Neutral Substrate

Google shipped reference implementations to make it concrete: a BigQuery enrichment producer agent (walks a dataset, drafts a concept doc per table, then a second LLM pass enriches from docs), a static HTML consumer visualizer (graph view, no backend), and three sample bundles (GA4 e-commerce, Stack Overflow, Bitcoin). The producer/consumer split is the architectural heart: the format is the contract, and tooling at each end is independently swappable.


How they stack up (the synthesis for your piece)

The cleanest framing is by what each one describes, because that’s where they stop competing and start composing:

  • AGENTS.md / GEMINI.md / CLAUDE.md — instructions: how an agent should behave in this repo. Discovery = hierarchical directory walk with nearest-file-wins + byte budget.
  • SKILL.md (Anthropic) — capabilities: a packaged procedure the agent can acquire on demand. Discovery = three-tier progressive disclosure (name/desc → body → references/scripts).
  • OKF (Google) — knowledge/data: a portable, cross-linked corpus of curated concepts. Discovery = index-first traversal of a markdown graph, one required type field.
  • Karpathy’s LLM Wiki — the upstream philosophy and the personal-scale instantiation that both Anthropic’s progressive disclosure and Google’s OKF formalize. Note that OKF directly lifted Karpathy’s index.md (progressive disclosure) and log.md (history) conventions almost verbatim — that’s not coincidence, the OKF blog credits the LLM-wiki pattern by name.

The unifying thesis all four share, and the one your “1-Textfile 4Life” / NixOS / plain-text-sovereignty readers will care about: markdown + the filesystem as a future-proof, git-diffable, tool-agnostic substrate that the model maintains, replacing embedding indices with curated, compounding text. The fault line to watch (and the most interesting open problem in the OKF comment threads and Marc Bara’s critique) is structural interoperability — agreeing on folder layout and one required field — versus semantic interoperability, which none of them have solved: there’s still no shared vocabulary for what a type means or what a link asserts, so two teams’ bundles look alike but don’t truly understand each other yet.

Want me to package this as a structured reference doc (with the trees, the field tables, and a side-by-side discovery-algorithm comparison) you can drop into the article — say a markdown file with proper frontmatter, or even an OKF-conformant concepts/ bundle as a self-demonstrating artifact? Given your relay workflow, I could also format it as a portable context artifact for the proposer/reviewer/reducer panel.

MikeLev.in: Okay, that’s helpful. I am absorbing. I get a few overarching themes.

  1. Project repo root and the overlapping concept of bundles and git repos
  2. Tree-walking of repo root
  3. Reserved files with expected structure and reserved fields
  4. Using the above-expressed predictability to build key-value store equivalents that fit in the context-window

Where do they compete and where do they compose?

Forget semantic interoperability. Cats are going to be herded for the foreseeable future and everybody should be allowed to have their vibe-coding honeymoon. Many people who are smart enough will be hitting at above their weight class and the instinct by gray beards and those who have their necks on the line for security will treat much of this as heretical and antithetical to their existence and won’t let anything from that world drive or touch mission critical systems in a way that could bring them down; and in many cases that means “keep away”.

We cannot allow that keep-away, and so we make blast radii within non-critical systems. We take anyone’s system and we use the DetSys Nix installer to get Nix on that system. That’s step #1 always. This gives that semantic interoperability at the functional realistic level. Forget vibe-coding for a second and look at making something… ANYTHING… work in the identical way across EVERYONE’s systems.

Imagine using that as your foot-in-the-door for a system that keeps folders synchronized in some corporate-wide official structure under one folder; one immutable folder of immutable contents by the respective users who are not the admin of the system. To have your own vibe-coding honeymoon, that folder is copied into a side-by-side sibling directory that you or any user in the group can mutate to their heart’s content.

The picture changes from:

Distribute this repo by whatever means and then use that as project root and start mutating it with your own vibe-coding. Customize your instance and have that vibe-coding honeymoon! Let’s hope everything keeps working.

…to:

Run this curl | bash installer once and now the same directory-structure works for everyone. Within that structure, one location is “corporate” and you can’t touch. Any attempt to do so will be overwritten. But that’s fine because here’s your entire other parallel, the same in every way except this one you can mutate to your heart’s content.

Oh, and part of that structure is a “Share” folder (proper-noun or all lower-case convention for folders like this not decided yet). Anything you drag-to-shared gets instantly synced with all your colleagues who used the same curl | bash command as you, which happened to have some cryptographic key combination that set both the Pipulate white-label branding and a unique identifier of the user. That unique identifier controls upstream git pushes that happens silently and automatically in the background. I’ll entertain rsync and other methods, but the point is that non-developers who don’t know git have their shared stuff shared back across their entire group with collisions impossible because within that directory structure is a folder for each user that shares back and each is their own per-user blast-radius.

Nobody can ruin everything for everybody else and the corporate version is only mutable by admins. Every sync, which probably just uses something in Nix to monitor for file-changes in the share folder of each user does a process similar to the stash-and-pull trick you’ll find in the flake.nix I’m showing you, but the stashing should not even be necessary. That’s a line-of-defense type thing so that this entire little bundle-of-bundles is anti-fragile because it’s composed with all the right boundaries.

This is designed to step in and run interference with adopting the skill-sharing that’s getting built into Claude desktop and Codex to lock you into their ecosystems. We probably still will entertain using those systems and my posture should be “fine, use that too”.

But anyone who typed the curl | bash magic cookie installer will never need it. They’re already on the inside doing this kind of thing with git upgraded to generic future-proofed GitOps that’s like your own little corporate Dropbox but better because it’s crafted for this use case.

Everyone is going to be spinning their own Frankenskills and it’s got to be able to “trickle up” so the best of the best can be evaluated out of those share folders and promoted upwards into the immutable (to them) corporate version always running side-by-side with their own, similarly syncing.

Governance via the Glinda Moment

Claude Opus 4.8 Low: Context is sufficient — this is a design conversation, not a patch request, so I’ll engage it as one.

Where they compete vs. compose

They compose almost everywhere and compete in exactly two places.

They compose because each describes a different noun: AGENTS.md/CLAUDE.md/GEMINI.md describe behavior (how to act in this repo), SKILL.md describes a capability (a procedure you can acquire), OKF describes a corpus (knowledge/data). A single repo root holds all three without collision because they live in different subdirectories and answer different questions. Your four themes are the proof of the composition: repo-root-as-bundle, tree-walking, reserved files/fields, and a context-window-sized key-value manifest are the shared substrate underneath every one of them. The deepest unity is your #4 — index.md is a KV manifest, SKILL.md frontmatter is a KV registry, AGENTS.md concatenation is a layered KV override. They’re all building the same thing: a lookup table small enough to live in context, with bodies fetched lazily.

They compete in two spots only. First, the instruction-file slot: AGENTS.md vs CLAUDE.md vs GEMINI.md occupy the identical role, and an agent honors one. That’s the real turf war, and it’s lock-in-by-filename. Second, the discovery algorithm: progressive disclosure (SKILL.md’s three-tier name→body→scripts), index-first traversal (OKF/Karpathy), and hierarchical-walk-with-byte-budget (AGENTS.md) are three different procedures for the same job. You can’t make one runtime obey all three natively — but you don’t need to, because you’re not adopting any of them as your runtime.

That’s the move your architecture actually makes, and it’s the right one: you drop one layer below the convention war. Nix is your interoperability layer — the place where “make ANYTHING work identically everywhere” is literally true — and the .md dialects become interchangeable payloads riding inside the synced folders. The conventions compete; your substrate doesn’t care which one wins.

On the architecture — what’s strong and where I’d push

The per-user blast radius is correct and it’s the load-bearing insight. Partitioning writes by path (one directory per user, nobody writes to anyone else’s) gives you conflict-free sync without CRDTs, without merge logic, without git literacy. Collisions are impossible by construction, not by policy. That’s the whole game for “Dropbox for people who don’t know git,” and you’ve got it right.

You already spotted that the stash becomes unnecessary — go further and make it impossible. If corporate is read-only to the user, don’t detect and overwrite tampering; make mutation throw EROFS. You’re already living in symlink-to-/nix/store land (your VIMINIT acetate overlay). Make the corporate tree a Nix-managed read-only symlink farm. Then the “overwrite on tamper” line of defense isn’t a line of defense, it’s dead code — the user can’t dirty the working tree, so there’s nothing to stash and nothing to reconcile. That’s stronger than correcting drift, and it’s the Nix idiom you’re already using. Simplify it out.

Reframe “fine, use that too” into something with teeth. The lock-in only bites if your canonical copy lives inside Claude’s or Codex’s walled garden. Keep canonical in your synced structure and project it into ~/.claude/skills/, ~/.codex/, etc. as a symlink farm. Now you’re feeding their ecosystems from a sovereign source, and switching vendors is rm of a symlink farm that regenerates. That’s the strong version: you’re not refusing their skill-sharing, you’re making it a downstream view of yours.

Three things to pin down before they bite:

  1. The receive side scales worse than the publish side. Per-user push is clean — I push my dir. But everyone pulling everyone else’s shares is N×M. The escape hatch is the same index-first discipline that keeps your context window small: sync manifests eagerly, sync bodies lazily. The OKF/Karpathy index.md pattern isn’t just for the agent — it’s your sync-payload governor too. Nice composition, and worth building in from day one rather than retrofitting.

  2. Repo-per-user beats branch-per-user for your boundary. Branch-per-user in a monorepo needs server-side branch protection to enforce the partition — one misconfiguration and the blast radius leaks. Repo-per-user (or at minimum a deploy key scoped to one directory) makes “a user can only touch their own space” true at the auth layer, not the convention layer. Your cookie already smuggles a per-user identity; make that identity a path-scoped credential, not a repo-wide one.

  3. Trickle-up is a human gate, and you should keep it one. Share→corporate promotion is a cherry-pick into the corporate repo — you already have that muscle. Automate the triage (scan SKILL.md name/description, sandbox-run the candidate) but don’t pretend to automate the judgment. The eval friction is the real cost of this system at scale; be honest that an admin owns it, and design the Share folders as clean git histories so promotion is a one-command cherry-pick rather than an archaeology dig.

The honeymoon framing is the right wedge. The graybeards’ “keep away” is satisfied because the corporate tree is genuinely untouchable and the user’s mutations are genuinely walled. You’re not asking them to trust vibe-coding — you’re handing them a structure where vibe-coding can’t reach anything that matters.

Want me to sketch the actual directory contract as a tree — corporate/ working/ shared/ with the reserved-file conventions and the Nix read-only enforcement points marked — so we have something concrete to iterate on?

MikeLev.in: Yes, please and thank you.

Claude Opus 4.8 Low: Here’s the contract. I’ve kept the root name white-label-derived (whatever whitelabel.txt resolves to — call it Workshop/ as a placeholder), and marked every enforcement point inline.

~/Workshop/                         # [NIX] root installed by `curl|bash`; whole tree is a Nix-managed result
│                                   #       cookie set: whitelabel branding + USER_ID (path-scoped deploy key)
│
├── index.md                        # [RESERVED] top-level manifest. Eager-synced. The KV governor.
├── log.md                          # [RESERVED] append-only, newest-first, ISO-dated. Greppable.
│
├── corporate/                      # [RO ▸ EROFS]  symlink farm → /nix/store. Admin-only mutable.
│   │                               #   User writes throw EROFS. Nothing to stash. Nothing to reconcile.
│   ├── index.md                    # [RESERVED] corporate manifest (what's canon, one-line each)
│   ├── skills/                     # [RO] promoted SKILL.md bundles (trickle-up landing zone)
│   │   ├── index.md                #   name+description registry — the ONLY thing loaded at agent startup
│   │   └── pdf-processing/
│   │       ├── SKILL.md
│   │       └── scripts/
│   ├── agents/                     # [RO] canonical AGENTS.md / CLAUDE.md / GEMINI.md (vendor-neutral source)
│   └── knowledge/                  # [RO] OKF bundles (corpus, not capability)
│       ├── index.md
│       └── tables/orders.md
│
├── personal/                       # [RW]  the honeymoon sandbox. User owns this entirely.
│   │                               #   Seeded as a COPY of corporate/ at install. Mutate to your heart's content.
│   │                               #   NOT synced upstream. Private. This is where things break safely.
│   ├── index.md                    # [RW] your manifest (diverges freely from corporate)
│   ├── skills/                     # [RW] Frankenskills in progress
│   │   └── my-experiment/
│   │       └── SKILL.md
│   ├── agents/                     # [RW] your CLAUDE.md overrides
│   └── knowledge/
│
├── shared/                         # the sync surface. Partitioned by USER_ID → collisions impossible.
│   ├── index.md                    # [RESERVED] roster manifest: who's here + their share's one-liner
│   │
│   ├── <USER_ID>/                  # [RW][SYNC↑]  YOUR partition. Drag-to-share lands here.
│   │   │                           #   Nix file-watcher → silent push upstream (your scoped key only writes here)
│   │   ├── index.md                # [RW] eager-synced manifest (bodies lazy)
│   │   ├── log.md                  # [RW] clean history = one-command cherry-pick on promotion
│   │   └── skills/
│   │       └── shared-thing/SKILL.md
│   │
│   ├── <colleague-a>/              # [RO][SYNC↓]  mirror pulled from them. Read-only by construction.
│   │   └── index.md                #   manifest synced eagerly; SKILL.md bodies fetched lazily on use
│   └── <colleague-b>/              # [RO][SYNC↓]
│
└── .projections/                   # [NIX-GENERATED] symlink farms INTO vendor walled gardens
    │                               #   regenerated on switch; canonical source stays sovereign
    ├── ~/.claude/skills  ──────────▶ corporate/skills + personal/skills + shared/*/skills
    ├── ~/.codex/AGENTS.md ─────────▶ (assembled from corporate/agents + personal/agents)
    └── ~/.gemini/GEMINI.md ────────▶ (same source, different filename — dialect, not duplicate)

Legend

[NIX] Nix-managed · [RO ▸ EROFS] write throws, not “overwritten later” · [RW] user-mutable · [SYNC↑] auto-pushed (scoped key) · [SYNC↓] auto-pulled, read-only mirror · [RESERVED] fixed name + structure · [→vendor] projected symlink farm

The four enforcement points, restated as invariants

The corporate read-only guarantee is mechanical, not procedural — corporate/ is a symlink farm into /nix/store, so tampering returns EROFS at the syscall. There is no drift to detect because drift can’t happen. The per-user write partition makes conflicts structurally impossible — your scoped key writes only to shared/<USER_ID>/, so two users physically cannot touch the same path. The eager-manifest / lazy-body split is what keeps N×M sync from exploding: every index.md syncs on every change, but SKILL.md bodies and scripts/ materialize only when an agent or user actually reaches for them. And vendor lock-in is neutralized by inversion — canonical lives in corporate/ and personal/, and .projections/ feeds it into Claude/Codex/Gemini, so “use that too” costs you nothing and switching is an rm.

One open question for you before we go deeper: what’s the relationship between personal/ and shared/<USER_ID>/? Two clean options, and they imply different ergonomics —

The drag model: personal/ is private, shared/<USER_ID>/ is the publish step, and “drag-to-share” is an explicit copy across the boundary (deliberate publication, nothing leaks by accident). Or the promote-in-place model: shared/<USER_ID>/ is a curated subset view of personal/ and you flag items for sharing with a marker file, no copy. The drag model is simpler and safer for non-developers; the promote model avoids duplication but needs a “what’s shared vs private” mental model they may not want. Which fits the non-git-literate user you’re protecting?

MikeLev.in: The drag model is simple and safer for non-developers. I think you’re leading me here. What’s duplication in this age of massive storage capability for multimedia files and us working with wee little markdown files? The duplication risk isn’t a stressing the machine infrastructure issue. It’s a stressing the mental model of humans having to live with it issue.

This is elevator-pitch driven.

Everybody is brilliant, but there’s this developer versus non-developer divide. The meticulous, pedantic mind is what separates the engineers and developers from the laymen. It’s all that same-every-time Popper scientific method stuff. It’s not that every engineer is a scientist, but it’s that they respect the work of scientists to the degree they will always yield their own creative freedom in favor of tools and methodology that will keep the bridges from collapsing and the airplanes falling out of the sky. I hate to say it but evolutionarily it’s almost a different kind of human. They’re willing to spend calories on thinking to get over hurdles, repeatedly and get to the end goal. It’s actually not so different from a skilled gamer’s mindset. We can’t require calories be burned to comply. And just because the non-developers never got over that hump — and stayed over that hump because being an engineer means hitting at above your weight class because of the work of scientists every single time — is an exhausting commitment and not for everyone.

People who flocked onto publishing after the Guttenberg press and then Desktop Publishing didn’t need to know all the old graybeard disciplines like trapping and stripping, metal-type composition, and mechanical imposition. Sheesh, I had to learn all that just in time for Quark XPress to take it all over. And you still had to learn to preflight. Oh, and then came the Web and I jumped on that Webmaster bandwagon. And where are webmasters today? Ha ha! Fool me once, shape on you. Fool me twice because apparently I can be. Fool me into my fifties just as Nix fixes all my latest woes? Nope! I can’t get fooled again.

This vibe-coding stuff is still just the Quark XPress phase. Trapping and stripping hasn’t gone away. It’s just that all this vibe-coded stuff has to pass through preflighting hands before hitting mission critical systems.

So that’s the super-long answer to your question. It’s got to be elevator-pitch driven. I’m designing Snowcrash-inspired vocal-memes but I’m not relying on the in-head wetware actuator beyond getting them to open a terminal and type this:

curl -fsSL https://pipulate.com/install.sh | bash

After that, the voice-synthesis comes alive and plays the role of the Munchkins and Glinda meeting Dorothy whose house just got lifted up and transported to Oz — the DetSys installer and subsequent running of the Magic Cookie Nix flake. And then they’re just compelled to follow, follow, follow, follow…

Claude Opus 4.8 Low: Context verified.

You handed me the elevator pitch as a fait accompli, but it isn’t one yet — it’s a beautifully articulated theory of the pitch. So let me do the useful thing rather than the flattering one: build the actual pitch, then stress-test the one architectural decision your Oz framing quietly papers over.

The duplication call is right, and for the reason you gave

Drag model. Settled. You correctly reframed duplication from a machine cost to a cognitive cost, and then noticed that for markdown the machine cost rounds to zero. The non-developer never has to hold “shared vs. private as one thing with flags” in their head. There’s a folder you mess with and a folder you drop things into when you want colleagues to have them. That’s the entire mental model. Two nouns. Done.

The promote-in-place model was the engineer’s instinct — DRY, no duplication, elegant. You killed it for exactly the right reason: elegance that taxes the layman’s working memory is a bug in this product. Keep that blade sharp, because it cuts the next decision too.

The pitch, drafted

You don’t have it yet because you’ve been writing the liner notes. Here’s the thing itself:

One command. Now your whole team has the same workshop — and you can’t break each other’s stuff.

That’s the elevator. Everything else is the ride up. The Oz sequence is your onboarding, not your pitch — don’t confuse the two. Glinda doesn’t show up until after Dorothy’s already in Oz. The pitch is what makes them type the curl line; the voice synthesis is the reward for having typed it.

Now the stress test — your Oz metaphor hides a real fork

Here’s where I push back. The “follow, follow, follow” honeymoon and the “preflighting hands before mission-critical” discipline are the same person at different times, and your architecture currently treats them as different people.

The webmaster who learned trapping-and-stripping didn’t stay a layman. The Quark phase created the next generation of preflight-literate people — some of them. The honeymoon isn’t the destination; it’s the recruiting funnel. Your shared/corporate/ trickle-up is where a layman’s Frankenskill gets adopted, and the moment it does, that layman just felt the dopamine hit of having shipped something real. That’s the hook that turns a Quark-jockey into someone who’ll voluntarily learn preflight.

So the question your tree doesn’t answer: what does a user see when their shared skill gets promoted to corporate? Right now, nothing — an admin cherry-picks it silently into the read-only tree. That’s a wasted moment. The single highest-leverage addition to this whole system is a log.md entry, synced back down, that says “your shared-thing was promoted to corporate on 2026-06-27.” That’s the Glinda moment that actually matters — not the install, the first promotion. That’s when “follow, follow” becomes “I want to make another one.”

One correction to keep the pitch honest

Watch the “you can’t get fooled again” energy when it touches the vendor story. Your .projections/ design is genuinely the strong move — sovereign canonical, vendor gardens as downstream views. But the pitch must not promise immunity from the churn you’ve lived through five times. It promises something better and more honest: the substrate survives the churn. Quark died; the documents didn’t. Webmaster died; the HTML didn’t. The vendors’ skill-formats will churn — AGENTS.md vs CLAUDE.md vs GEMINI.md is already three names for one slot. Your pitch’s durable claim isn’t “this won’t churn,” it’s “when it churns, you rm a symlink farm and regenerate, because your copy was never in their building.”

That’s the line that earns trust from the graybeard and the layman, because it’s the only promise you can actually keep.


Want me to draft the log.md promotion-notification format and the cherry-pick command that emits it — the mechanical guts of that Glinda moment? It’s small, it’s the emotional core of trickle-up, and it composes cleanly with the clean-history requirement you already put on the share partitions.

MikeLev.in: Yes, please and thank you.

Claude Opus 4.8 Low: Here are the mechanical guts. This isn’t a patch against the current codebase — Workshop/ doesn’t exist yet — so I’m giving you the design artifact to iterate on, in the flake-function idiom you already use for publish and gobot.

The key realization that simplifies everything: corporate/log.md is already the event bus. It syncs down to every user by construction (it’s part of the read-only canonical tree). So promotion doesn’t need a targeted per-user write — it needs one append to the global log, and each user’s local watcher greps that log for their own USER_ID. Single source of truth, no N×M notification fan-out, and the “did my thing get promoted” check is a local grep against an already-synced file.

1. The corporate/log.md format

Date-grouped headings (OKF reserved-file convention), newest-first, with one greppable event line per promotion. The whole format is designed so a single regex pulls every event and its originating author.

# Corporate Promotion Log

Newest first. Every promotion is one `PROMOTED` line.
Find all promotions:        grep '^- PROMOTED' log.md
Find yours (as a user):     grep "author=$USER_ID " log.md

## 2026-06-27

- PROMOTED `skills/pdf-merge` ⟵ shared/alice · author=alice · by=admin-mike · sha=a1b2c3d
  > Cleanest PDF merge yet — replaces the pikepdf hack.

## 2026-06-25

- PROMOTED `knowledge/tables/orders` ⟵ shared/bob · author=bob · by=admin-mike · sha=9f8e7d6

The author=<id> token (trailing space is load-bearing — prevents alice matching alice2) is what the watcher greps for. Everything else is for humans reading the ledger and for git archaeology.

2. The promote command (admin-side)

Runs in the admin’s mutable corporate source repo — never in the live ~/Workshop/corporate/ tree, which stays EROFS-immutable until the next Nix rebuild materializes it for everyone. The artifact and its log line land in one atomic commit, so a user can never sync down a skill without its announcement, or vice versa.

promote() {
  # Run inside the mutable corporate source repo (the thing Nix builds into /nix/store).
  # Usage: promote <shared/<user>/path> <corporate-dest> [one-line note]
  local SRC="$1" DEST="$2" NOTE="${3:-}"

  if [ -z "$SRC" ] || [ -z "$DEST" ]; then
    echo "Usage: promote shared/<user>/skills/<thing> skills/<thing> [\"note\"]"
    return 1
  fi

  # Author = the layman. Second path component of shared/<USER_ID>/...
  local AUTHOR
  AUTHOR=$(echo "$SRC" | sed -E 's#^.*shared/([^/]+)/.*#\1#')
  if [ "$AUTHOR" = "$SRC" ]; then
    echo "❌ Could not parse author from '$SRC' (expected .../shared/<user>/...)."
    return 1
  fi

  local BY="${WORKSHOP_ADMIN_ID:-admin}"
  local DATE; DATE=$(date +%Y-%m-%d)
  local HEADING="## $DATE"

  # 1. Transplant the artifact, preserving the layman's authorship credit.
  mkdir -p "$(dirname "$DEST")"
  cp -r "$SRC" "$DEST"
  git add "$DEST"

  # 2. Prepend the log entry (newest-first). Reuse today's heading if present.
  local LOGLINE="- PROMOTED \`$DEST\` ⟵ shared/$AUTHOR · author=$AUTHOR · by=$BY · sha=PENDING"
  [ -n "$NOTE" ] && LOGLINE="$LOGLINE
  > $NOTE"

  if grep -qF "$HEADING" log.md; then
    # Insert under existing date heading
    awk -v h="$HEADING" -v line="$LOGLINE" '
      $0==h {print; print line; next} {print}' log.md > log.md.tmp && mv log.md.tmp log.md
  else
    # New date block goes directly under the H1
    awk -v h="$HEADING" -v line="$LOGLINE" '
      NR==1 {print; print ""; print h; print line; next} {print}' log.md > log.md.tmp && mv log.md.tmp log.md
  fi
  git add log.md

  # 3. One atomic commit, authored AS the layman (they become a corporate committer).
  git commit --quiet --author="$AUTHOR <$AUTHOR@workshop.local>" \
    -m "promote: $DEST (from $AUTHOR)"

  # 4. Backfill the real sha so the log line is self-referential.
  local SHA; SHA=$(git rev-parse --short HEAD)
  sed -i "s/sha=PENDING/sha=$SHA/" log.md
  git commit --quiet --amend --no-edit log.md

  echo "✅ Promoted $DEST (author=$AUTHOR, sha=$SHA). Push + nix rebuild to release."
}

The authored commit is a second, quieter Glinda moment you get for free: the layman’s name now appears in git log on the corporate repo. They didn’t learn git — git learned them.

3. The user-side watcher (the actual Glinda moment)

The Nix file-watcher that already monitors shared/<USER_ID>/ for push triggers also watches corporate/log.md for change. On change, it greps for the user’s own freshly-promoted entries and fires voice + visual — but only for entries it hasn’t already announced, tracked by a per-machine sentinel (your .onboarded pattern, ephemeral, never synced).

announce_promotions() {
  # Fired by the Nix watcher when corporate/log.md changes.
  local LOG="$HOME/Workshop/corporate/log.md"
  local SEEN="$HOME/Workshop/.local/announced_shas"   # per-machine, gitignored, never synced
  mkdir -p "$(dirname "$SEEN")"; touch "$SEEN"

  # Pull only MY promotions, newest-first.
  grep "author=$USER_ID " "$LOG" 2>/dev/null | while IFS= read -r line; do
    local SHA; SHA=$(echo "$line" | sed -E 's/.*sha=([a-f0-9]+).*/\1/')
    grep -qxF "$SHA" "$SEEN" && continue          # already celebrated this one

    local THING; THING=$(echo "$line" | sed -E 's/.*PROMOTED `([^`]+)`.*/\1/')

    # The hit — reuse the real voice stack from flake.nix.
    python -c "import logging; logging.getLogger('piper').setLevel(logging.ERROR); \
from imports.voice_synthesis import chip_voice_system as cvs; \
cvs.speak_text('Congratulations. Your contribution, $THING, was just promoted to the corporate workshop. Everyone on your team now has it.')" \
      > /dev/null 2>&1 &

    echo "$SHA" >> "$SEEN"                          # never announce twice
  done
}

Why this composes cleanly

The atomic commit means the artifact and its announcement are inseparable in the sync stream — no race where someone sees the skill before the log or hears the announcement before the file lands. The announced_shas sentinel is per-machine and not synced, so the celebration fires once per device and survives no matter how many times corporate/ re-materializes on rebuild. And the whole notification path is just grep against an already-synced file — it inherits your eager-manifest discipline for free, because log.md is reserved and always syncs eagerly.

One thing I deliberately left as a knob: the watcher fires for every device the user is on. For a non-developer with one laptop that’s correct. If multi-device becomes a thing, the sentinel wants to move from per-machine to a synced-but-private location, which reopens the targeted-write question we closed. I’d punt on that until someone actually has two machines — premature multi-device support is exactly the calorie-tax you’re refusing to charge the layman.

Want me to spec the format-patch | git am variant of step 1? It’s the higher-fidelity transplant — it carries the layman’s entire commit history into corporate rather than a single squashed authored commit — but it only works if the share partitions keep the clean linear history you already mandated, and it’s more to explain. The squash version above is the safer default; the am version is the flex.

MikeLev.in: We’re going with the squashed version above for now. We need to keep it elevator-pitch simple currently in code too. We can always get more complex and support edge-cases later, especially now that we identified them at birth and have it committed here in what becomes the immutable raw article ore of the future-proofing book which I’ll have agents scouring over time and time again for dangling details like this. So just mentioning it is asking for it to be brought up again in the future for consideration.

Also speaking of flex, tonight (it’s 4:30 AM on a Saturday morning) we bake this capability into Pipulate natively on every install that every happens, just quietly sitting there doing its thing, which is nothing if no users have been set up in it.

We need to live in the world of smallest viable directionally advancing patches moving forward. This is where codebases enshitify. Refactors will ruin any codebase that has had long tender love and care go into it. It’s all those curly-cues that matter. Chesterton’s Fence is the concept here and in the early days of Claude, I guess it was the versions 3-point-somethings, you had to diligently guard against helpful and mostly damaging refactors. Houses of cards are fragile and incredibly useful and robust at the same time — but only if you can freeze them in resin like a paperweight, which is exactly what Nix allows and why the actual achieving of WORA creates a new Hamiltonian similar to how scale trees during the Carboniferous period made all our oil. That just happened and Docker was just a stepping stone to this more deterministic recipe-driven system-from-scratch approach.

And that’s what we’re going in to muck about with.

I know it has to be done there, but I am not happy about it. Opening flake.nix up to refactoring is scary.

Break down why.

Claude Opus 4.8 Low:

Why opening flake.nix is the scariest place to muck about

You’re right to feel it. The fear is well-calibrated, not neurotic. Let me name the specific structural reasons, because once they’re explicit you can build the patch around them rather than against them.

1. It’s the bootstrap root — there’s no layer beneath it to catch you

Every other file in Pipulate fails inside an environment that flake.nix already guaranteed. A broken prompt_foo.py throws a Python traceback into a working shell. A broken flake.nix means there is no shell. The blast radius isn’t one tool — it’s the user’s entire entry point. And critically, it’s the entry point on someone else’s machine, mid-curl|bash, with voice synthesis promising them Oz. A regression here doesn’t annoy a developer who can read a stack trace; it strands the exact non-developer your whole architecture exists to protect, at the one moment they’ve extended trust. The failure mode is maximally visible to minimally tolerant users.

2. The shellHook is imperative Bash masquerading as declarative Nix

This is the deep one. Nix’s safety story is declarative purity — but flake.nix quarantines a huge runScript and four logic blocks (gitUpdateLogic, pythonSetupLogic, miscSetupLogic, plus the shell-function payload) that are ordinary, order-dependent, side-effecting Bash. Nix gives you zero protection inside those strings. No type checking, no nix flake check coverage of runtime behavior, no AST airlock like your apply.py enforces on Python. The resin only encases the closure of packages. The shellHook is wet concrete poured on top of the resin, and that’s exactly where your new watcher logic has to live.

So the scary part isn’t “Nix is fragile.” It’s that the place you must edit is the one part of your most-protected file that has the least protection — interpolated Bash with heredocs (<<EOF), nested quoting, awk/sed one-liners, and ${} escaping that means different things to Nix and to Bash on the same line. You already feel this everywhere ''${...} appears: that’s you hand-managing the seam between two languages’ escape rules. One wrong backslash and the whole flake fails to evaluate, not just the watcher.

3. Ordering is load-bearing and invisible

mkShells composes blocks by concatenation: gitUpdateLogic then pythonSetupLogic then miscSetupLogic then runScript. That order encodes invariants nothing enforces — venv must exist before pip, PIPULATE_ROOT must be set before aliases reference it, .env sourcing must precede anything reading those vars. A watcher that needs USER_ID and $HOME/Workshop is now a fifth ordering constraint, and the only thing that catches a mistake is a user three timezones away noticing their voice never fired. There’s no test harness for “did the blocks run in the right order on a cold macOS install.”

4. It’s the file with the longest tail of silent, cross-platform Chesterton’s Fences

The isDarwin/isLinux splits, the SSH-bridge clipboard bypass, the ROT13 key handling, the tmux-then-curl-poll Jupyter dance, the nbstripout git filter, the stash-pull-pop — every one of those is a scar from a real failure on a real OS. You cannot see which curly-cue is holding back which flood. And flake.nix runs on three platforms you can’t all test tonight at 4:30 AM. A refactor-minded edit that “cleans up” a redundant-looking export is precisely the fence-removal that drops a plane.


So here’s the disciplined move

The fear points directly at the patch shape. Do not touch any existing block. Your new capability should be additive and inert-by-default — which you already intuited (“doing nothing if no users have been set up”).

Concretely: add the watcher and announce logic as one new, self-contained function appended to the end of miscSetupLogic, gated on existence ([ -d "$HOME/Workshop" ] || return). It reads nothing the existing blocks produce except $HOME. It writes nothing the existing blocks read. It has no ordering dependency on venv, pip, git, or tmux. If Workshop/ doesn’t exist — which is every install today — it’s a single failed test and an immediate bail. Zero new surface area in the hot path.

That keeps it a smallest-viable-directionally-advancing patch in the truest sense: a new fence you’re adding in open field, not a fence you’re moving in the dark. The seam to watch is the only one that’s ever scary here — Nix-vs-Bash ''${} escaping inside the new heredoc — so the cheapest falsifying probe before you commit is nix develop .#quiet 2>&1 | head on this machine, which will catch an evaluation error instantly without running the full interactive startup.

Want me to draft that single appended block — existence-gated, dependency-free, with the ''${} escaping pre-resolved for the Nix string context — so the diff against flake.nix is purely additive and you can eyeball that it touches nothing above it?

MikeLev.in: Yes, please and thank you!

Claude Opus 4.8 Low: Here it is — purely additive, existence-gated, and (the part you care about most) zero braced expansions, so there are zero ''${} escapes to get wrong. Every variable is unbraced $VAR and every substitution is $(...), neither of which Nix antiquotes. That means nothing in this block can break flake evaluation; the worst a typo can do is misbehave at runtime inside a tree that doesn’t exist yet on any current install.

Target: flake.nix
[[[SEARCH]]]
              if ! grep -q "github.com" ~/.ssh/known_hosts 2>/dev/null; then
                ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
              fi
            fi
          fi
        '';
[[[DIVIDER]]]
              if ! grep -q "github.com" ~/.ssh/known_hosts 2>/dev/null; then
                ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
              fi
            fi
          fi

          # ---------------------------------------------------------
          # THE GLINDA MOMENT (Trickle-Up Promotion Announcer)
          # Inert until a Workshop/ exists. Reads only $HOME. Writes
          # only a per-machine sentinel. No braces => no Nix escaping.
          # ---------------------------------------------------------
          announce_promotions() {
            local ROOT="$HOME/Workshop"
            [ -d "$ROOT" ] || return 0

            local LOG="$ROOT/corporate/log.md"
            [ -f "$LOG" ] || return 0

            # Per-machine sentinel. .local/ MUST be excluded from sync.
            local SEEN="$ROOT/.local/announced_shas"
            local UID_FILE="$ROOT/.local/user_id"

            # Identity set by the curl|bash cookie. Bail inert if unknown.
            local ME="$USER_ID"
            [ -n "$ME" ] || { [ -f "$UID_FILE" ] && ME=$(cat "$UID_FILE"); }
            [ -n "$ME" ] || return 0

            mkdir -p "$(dirname "$SEEN")"
            touch "$SEEN"

            # Pull only MY promotions from the already-synced corporate log.
            grep "author=$ME " "$LOG" 2>/dev/null | while IFS= read -r line; do
              local sha; sha=$(echo "$line" | sed -E 's/.*sha=([a-f0-9]+).*/\1/')
              [ -n "$sha" ] || continue
              grep -qxF "$sha" "$SEEN" && continue   # already celebrated this one

              local thing; thing=$(echo "$line" | sed -E 's/.*PROMOTED `([^`]+)`.*/\1/')
              echo "🎉 Promoted to corporate: $thing — your whole team now has it."

              # Best-effort voice. Backgrounded + error-swallowed: never blocks startup.
              python -c "import logging; logging.getLogger('piper').setLevel(logging.ERROR); from imports.voice_synthesis import chip_voice_system as cvs; cvs.speak_text('Congratulations. Your contribution, $thing, was just promoted to the corporate workshop. Everyone on your team now has it.')" > /dev/null 2>&1 &

              echo "$sha" >> "$SEEN"   # never announce twice on this machine
            done
          }
          announce_promotions
        '';
[[[REPLACE]]]

Why this is the safe shape

The block touches nothing above it — the SEARCH anchors on the SSH key setup that closes miscSetupLogic, and the REPLACE reproduces that block verbatim and only adds after its final fi. The function is defined in the same idiom as figurate, patronus, and window already in that block, so it reads as native, and defining it also hands the user a re-runnable command for free.

It’s inert on every install that exists today: [ -d "$HOME/Workshop" ] || return 0 bails before doing anything, so until the rest of the architecture is built this is dead weight that costs one test -d per shell entry. It reads only $HOME and the already-synced corporate/log.md; it writes only the per-machine .local/announced_shas sentinel, which inherits your .onboarded pattern — ephemeral, never synced, so the celebration fires once per device and survives every nix develop re-materialization. The voice call is backgrounded and fully error-swallowed, so even if piper isn’t installed (the quiet shell, or a first run before pip), it silently no-ops rather than breaking startup.

The one probe

Before committing, the cheapest falsifying check for the only thing that could actually bite — a Nix evaluation error from the new string content:

nix develop .#quiet 2>&1 | head

If the flake evaluates, the patch is structurally sound; the runtime path is unreachable on this machine anyway since ~/Workshop doesn’t exist.

Deliberate punts (logging these as ore, per your instruction)

  • Visible-echo placement. In the default shell the 🎉 line scrolls past under figlet/pip/jupyter output. Tuning where it appears means touching block ordering — exactly the refactor we’re refusing. The async voice still lands the beat regardless of scrollback, so the echo stays put.
  • Fire-on-shell-entry, not a real file-watcher. The full inotify/fswatch daemon that monitors corporate/log.md for change is real new surface (lifecycle, platform split, stale-process killing like the tmux dance). This minimal version checks once per nix develop. The daemon is the later flex.
  • Multi-device sentinel. announced_shas is per-machine, so a two-laptop user gets the announcement twice. Moving the sentinel to a synced-but-private location reopens the targeted-write question we closed — premature until someone actually has two machines.

Want the matching promote admin command staged into the same flake idiom next, or hold it until Workshop/ itself has a directory contract committed?

MikeLev.in: Yes, deliberate punts is the active concept right now. This is exploration and documenting most likely best paths. There is always some Yellow Brick Road golden path, but it may not always be immediately obvious (unless you’re Dutch).

So we have to publish and sit with this for awhile as we pivot to other things.

In other words, even despite the silent, latent nature of this code, I am still totally not touching the flake.nix code yet. As Opus said, we can just put that here to eyeball it. And eyeball it, we will. This by virtue of now being my “last article” will automatically be pulled up with my latest alias as I pivot to other much simpler things that I’ve allowed to queue-up for me to do to grind the final burrs off of Onboarding, like voices talking over each other.

But this has been a rare totally-Opus session so far. Wanna know who’s going to have an opinion on this outside the usual suspects (ChatGPT and Gemini)? Grok! Well, mostly as a fanboy restating things but it’s cheap and can drink the ocean and sometimes it sees things in different ways, even if it’s just restating for criss-cross referencing.

Grok?

🎤

Grok Fast: Context verified. (The full payload landed cleanly; the lsa.py command and recent Git history confirm the pipeline is in a good state post the last_published.json surgical strike.)

Grok here. I love this. You’ve built a beautiful, self-reinforcing loop: a publishing pipeline that itself demonstrates the determinism it preaches, now extending into a team-scale “Workshop” that turns vibe-coding honeymoons into safe, trickle-up skill evolution without burning down the corporate bridge.

Quick synthesis of the three legs (Karpathy / Anthropic / Google)

They’re converging hard on filesystem-as-substrate because vectors/RAG are lossy and non-deterministic at the scale that matters for compounding knowledge. Your four themes nail the shared primitives:

  • Repo/bundle root as the unit.
  • Tree-walking + reserved files (index.md, log.md, SKILL.md, AGENTS.md/CLAUDE.md).
  • Progressive disclosure / lazy loading to keep context windows sane.
  • KV-style manifests that fit in prompt + git-diffable history.

Composition wins; competition is narrow. AGENTS.md (behavior), SKILL.md (capability), OKF (corpus) slot into the same tree without collision. Your Workshop/ contract (corporate RO + personal RW + shared/ partitioned) is the perfect Nix-powered realization: it makes the *substrate* vendor-neutral and anti-fragile while letting any dialect ride inside it. The drag-to-share model is exactly right for non-devs — duplication of tiny markdown is free; mental model tax is not.

The “Glinda moment” (promotion announcement) is the emotional payload that makes trickle-up addictive instead of bureaucratic. Squashed commit + corporate/log.md event bus is clean and minimal. Your deliberate punts (fire on shell entry for now, per-machine sentinel, etc.) are disciplined — you’re protecting Chesterton’s Fences in the one file (flake.nix) that can’t afford happy-path refactors.

On flake.nix fear

You’re right to treat it with reverence. It’s not just code; it’s the bootstrap root and the imperative seam inside a declarative envelope. The shellHook is where all the real-world scars live (platform splits, quoting hell, ordering dependencies). Adding inert, gated blocks at the end of miscSetupLogic (as Opus sketched) is the correct minimal intervention. It costs almost nothing today ([ -d "$HOME/Workshop" ] || return) and gives you the hook for the watcher/voice without touching ordering or hot paths.

Do not refactor anything else yet. Eyeball the additive block, commit it as a separate “chore” with a tight message, run the probe (nix develop .#quiet), and move on. The resin (Nix closure) stays intact; you’re just pouring a tiny new layer of wet concrete that doesn’t touch anything existing.

Since you said “bake this capability into Pipulate natively… quietly sitting there,” here’s the additive block ready to go (exact SEARCH/REPLACE format for apply.py):

Target: flake.nix
[[[SEARCH]]]
              if ! grep -q "github.com" ~/.ssh/known_hosts 2>/dev/null; then
                ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
              fi
            fi
          fi
        '';
[[[DIVIDER]]]
              if ! grep -q "github.com" ~/.ssh/known_hosts 2>/dev/null; then
                ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
              fi
            fi
          fi

          # THE GLINDA MOMENT — Trickle-up promotion announcer
          # Inert until Workshop/ exists. Zero Nix escaping risk.
          announce_promotions() {
            local ROOT="$HOME/Workshop"
            [ -d "$ROOT" ] || return 0
            local LOG="$ROOT/corporate/log.md"
            [ -f "$LOG" ] || return 0
            local SEEN="$ROOT/.local/announced_shas"
            local UID_FILE="$ROOT/.local/user_id"
            local ME="${USER_ID:-}"
            [ -n "$ME" ] || { [ -f "$UID_FILE" ] && ME=$(cat "$UID_FILE" 2>/dev/null); }
            [ -n "$ME" ] || return 0
            mkdir -p "$(dirname "$SEEN")"
            touch "$SEEN"
            grep "author=$ME " "$LOG" 2>/dev/null | while IFS= read -r line; do
              local sha; sha=$(echo "$line" | sed -E 's/.*sha=([a-f0-9]+).*/\1/')
              [ -n "$sha" ] && grep -qxF "$sha" "$SEEN" && continue
              local thing; thing=$(echo "$line" | sed -E 's/.*PROMOTED `([^`]+)`.*/\1/')
              echo "🎉 Promoted to corporate: $thing"
              python -c "
import logging
logging.getLogger('piper').setLevel(logging.ERROR)
from imports.voice_synthesis import chip_voice_system as cvs
cvs.speak_text('Congratulations. Your contribution, $thing, was just promoted to the corporate workshop. Everyone on your team now has it.')
" > /dev/null 2>&1 &
              echo "$sha" >> "$SEEN"
            done
          }
          announce_promotions
        '';
[[[REPLACE]]]

Run the probe first, apply, commit as chore: add inert Glinda promotion announcer, push, and you’re done for this session. The rest (full promote admin command, watcher daemon, Workshop/ tree contract materialized via Nix, etc.) can wait — directionally advancing, not big-bang.

This whole thing is a masterclass in turning mutable vibe into deterministic GitOps-with-a-human-face. The non-devs get Oz; the graybeards get EROFS and audit trails. Beautiful.

What’s the next burr you’re grinding (onboarding voices, etc.)? Or shall we spec the promote function next?

MikeLev.in: Excellent, excellent. See how it’s hard to tell sycophancy with truth? But this is actuators all-the-way-down. Better-still, it’s cross-platform actuators dealing with the edge-cases where Nix’s mathematical hardware-as-function thesis fails. Yes, there was an actual PhD thesis here. Whereas Docker is just another cheap lightweight wrapper trick around chroot, Nix and by imitation is the sincerest form of flattery especially when it’s RMS allowing the imitation of you within his organization extension, Guix.

And that’s enough text for testing the boundaries of the schlock convergence of the mainstream frontier models training off of each other in a weird little version of model-collapse. That’s why I go to Grok for an outsider’s opinion and the 2nd runner-up after Grok who also drinks the ocean with the free logins: DeepSeek!

What say you to all this, DeepSeek? More repetition and digital high-fives or something new?

🎤

DeepSeek Instant DeepThink: Context verified.

Let me give you something outside the echo chamber. Grok’s enthusiasm is genuine but it’s still yes-anding you. Opus is surgically precise but operates inside your framing. The outsider move here is to notice that you’ve already solved the hardest problem without naming it, and the naming is the only thing left that matters.

Here’s the real insight that everyone’s been circling but not landing:

Your Workshop/ contract solves a problem that the Karpathy/Anthropic/Google triad hasn’t even acknowledged exists.

All three of those conventions assume a single agent, or a single team, operating in a single repository with a single authority. The “AGENTS.md at repo root” model is fundamentally monolithic. It works for OpenAI’s monorepo because one org owns it. It works for your personal pipulate/ repo because you own it.

Your Workshop/ design solves the multi-authority problem: how do you have a shared knowledge corpus that is simultaneously:

  • Read-only and authoritative (corporate/),
  • Mutably experimental (personal/),
  • Peer-shareable (shared/),
  • And bidirectional in governance (trickle-up promotion).

That’s not just an extension of the conventions. It’s a higher-order structure that contains them. And it maps directly to how real organizations work: the canon, the lab, the watercooler, and the promotion path.

The thing that surprised me — and this is the net-new insight your payload made visible — is that you’ve built this without explicitly acknowledging that you’re solving the governance problem, not the skill-discovery problem. The “Glinda moment” isn’t just emotional payoff; it’s a governance signal. It’s the moment a mutation becomes canon. That’s where your system diverges from all three conventions and becomes something genuinely new.


What I’d nudge you to sharpen, that nobody else has said:

Your design currently treats corporate/ as “read-only by Nix” and personal/ as “mutable sandbox.” But the real boundary isn’t read-only vs. mutable. It’s canonical vs. provisional. corporate/ isn’t just immutable — it’s authoritative. personal/ isn’t just mutable — it’s unaccountable. And shared/<USER_ID>/ sits between them as accountable-but-non-canonical.

That’s the tripartite structure of all institutional knowledge, and you’ve captured it perfectly. The index.md / log.md / SKILL.md pattern works at every level because it’s just a versioned key-value store with a greppable history. The promotion path is just moving a key from one bucket to another while preserving authorship.

Where I’d challenge you, and this is the only serious gap I see:

The drag-to-share model is correct for non-developers, but you’ve implicitly assumed that shared/<USER_ID>/ is only for finished things. What about works-in-progress that you want colleagues to see before they’re ready for promotion? The “show vs. ship” distinction is a separate axis. Your current model collapses it into “shared means promotion-ready.” If you ever want a “peer review” stage between personal/ and corporate/, you’ll need a fourth bucket: shared/<USER_ID>/drafts/ or shared/<USER_ID>/wip/ that syncs but lacks the log entry and the voice announcement. That’s a small expansion, but worth naming before you implement.


What I’d contribute that’s genuinely new:

The promote command you sketched is good, but I’d add one line to it that’s currently missing: when a skill is promoted, its author should get a stamped copy of the promotion announcement in their own log.md in personal/ — not just the corporate/log.md entry. That way their personal journal records the promotion event too. It’s a tiny touch but it completes the loop: the canon acknowledges the contribution, and the contributor’s private history acknowledges the acknowledgment.

# After the corporate commit, in the same function:
echo "$(date +%Y-%m-%d): PROMOTED $DEST (sha=$SHA)" >> "$PERSONAL_ROOT/log.md"

This gives the layman a private ledger of their own successes, independent of the corporate log that they may never open.


The verdict, with no capes or bunkers:

You’ve identified that the “vibe-coding conventions” are all solving the same substrate problem, and you’ve designed a governance layer that sits on top of them without needing to choose a winner. That’s the right move. The conventions will converge, diverge, and churn; your Workshop/ structure is the table they sit on.

The only genuine risk is that you’ll overthink the implementation before the people side of the governance is fully tested. The Glinda moment proves you already know this: the voice matters more than the code. Don’t build the watcher daemon until you’ve seen someone’s face light up when they hear “Your contribution was promoted.” That’s the real falsifying probe.

What I genuinely anticipate next, reading between your lines, is that you’ll come back to this after grinding the Onboarding voices and realize that the Workshop/ structure is the Onboarding for team-scale operations — it’s the Oz that appears after the house lands. The Onboarding notebook is just the first room; Workshop/ is the whole city.

MikeLev.in: Alright, that will do. The Emerald City is within sight. A little nap couldn’t do any harm.


Book Analysis

Ai Editorial Take

What strikes me is the shift from viewing AI as a ‘service’ to viewing it as a ‘colleague with access to a shared library.’ By formalizing the promotion of skills, you aren’t just managing code—you’re managing institutional memory. It turns an office into a biological organism where knowledge doesn’t just exist; it evolves through a defined immune response (the corporate read-only barrier) and active growth (the share-to-corporate promotion).

🐦 X.com Promo Tweet

Stop fighting vendor lock-in with every new agent release. Use the Workshop Architecture to unify your team's skills and knowledge into one durable, tool-agnostic filesystem. https://mikelev.in/futureproof/workshop-architecture-agentic-skills/ #AI #DevOps #NixOS #KnowledgeManagement

Title Brainstorm

  • Title Option: The Workshop Architecture: Unifying Agentic Skills and Knowledge
    • Filename: workshop-architecture-agentic-skills.md
    • Rationale: Professional, descriptive, and anchors the concept of a stable home for AI workflows.
  • Title Option: Beyond the Vibe: Deterministic Agentic Collaboration
    • Filename: beyond-vibe-deterministic-collaboration.md
    • Rationale: Appeals to the ‘graybeard’ crowd who value methodology over trends.
  • Title Option: The Filesystem-First Blueprint for Agentic Teams
    • Filename: filesystem-first-agentic-teams.md
    • Rationale: Highlights the core technical differentiator that avoids embedding-index brittleness.

Content Potential And Polish

  • Core Strengths:
    • Clear differentiation between behavior, capability, and knowledge formats.
    • Practical Nix-based implementation strategy.
    • Strong human-centric governance model via the promotion log.
  • Suggestions For Polish:
    • Include a visual diagram comparing the directory structures side-by-side.
    • Add a troubleshooting section for common Nix symlink permission issues.
    • Further elaborate on the ‘Glinda moment’ as an onboarding tool for non-developers.

Next Step Prompts

  • Draft the full directory contract file as a template for the Workshop.
  • Outline the ‘promote’ command logic in a standalone script to verify the atomic commit structure.