Skip to content

Re-write and revise fundamentals, intermediate and ecosystem - #14

Merged
amirkiarafiei merged 73 commits into
mainfrom
feat/amirkia-revision
Sep 3, 2026
Merged

amirkiarafiei merged 73 commits into
mainfrom
feat/amirkia-revision

Conversation

@amirkiarafiei

Copy link
Copy Markdown
Member

No description provided.

… into index

Adds MANIFEST.md at the repo root: the seven rules that define what this project
is and why it is worth writing — human-written content, conversational voice,
deliberate simplicity, 5-10 minute modules, visuals-first, links out, and
cheatsheet-not-book topic selection. Plus a short "How we work" section covering
English-first authoring and the README/index mirror rule.

Completes the mini-courses -> sections rename. mkdocs.yml still pointed docs_dir
at the old directory, which broke `mkdocs build --strict` in CI; also fixes 10
dead paths in README.md and 12 stale target paths in the scratchpad research
notes.

README.md gains a "Why This Is Valuable" section — the manifest itemized for
quick reading — and sections/index.md is rebuilt as a mirror of it so the GitHub
landing page and the published site say the same thing. site_name follows the
new "AI Engineering Bazaar" headline.
Cuts the manifest from ~1100 to ~500 words. The seven rules are unchanged in
substance; what goes is the redundancy and the parts that read as internal notes
rather than something a reader should see: the five-way enumeration of how LLMs
fail on new topics (now two sentences), the self-assessment of the project's own
value, the production tooling list under the visuals rule, and the explanation of
keeping modules short as a strategy to make readers want more.

Self-reference is now "me" for the author and "we" for the project; no bare "I".

The README and index itemization follow the same edit on rule 6.
Reverses my cut of rule 6's rationale — the author wants readers to see that the
brevity is deliberate, so the manifest now matches the README wording instead of
the neutered version.

Also fixes "things that might not written down anywhere else" -> "might not be
written", strips stray trailing/double spaces from the README bullets, and
re-mirrors sections/index.md so it is content-identical to the README again.
Refines the "Why This Is Valuable" section by correcting grammatical errors and improving phrasing for better readability. Adjusts formatting in the module structure table for consistency, ensuring alignment and clarity across categories.
The previous version read like notes passed between collaborators rather than a
public statement of what this project is. It talked about drafts and internal
workflow, described how the repo keeps its files in sync, and pointed at
directory paths — none of which means anything to someone arriving from outside.

Rewritten for that reader, and in the plain English the manifest itself asks for:
short sentences, common words, nothing that needs a second pass. Same seven
rules, no workflow talk, and the only process note left is the one a reader
benefits from — English first, Turkish after.
The itemized rules on the landing pages still used the older, wordier phrasing
while MANIFEST.md had been rewritten. All three now share the same seven rule
names and the same plain wording, so the short version and the full version read
as one voice.

Rule 6 now says why the modules are short: in the world of Reels and TikTok
nobody pushes through something they were told to read, so a page is written to
leave you curious rather than full. Rule 5 picks up "a picture is worth a
thousand words" and drops the sentence that repeated the same point.
…ants

Introduces two new files, AGENTS.md and CLAUDE.md, which outline the working agreements and rules for AI assistants in the repository. These documents serve as live references for collaboration, emphasizing clarity, simplicity, and adherence to the project's established guidelines. The content includes instructions on writing style, module structure, and visual representation, ensuring consistency across the project.
Rewrites Module 1 from the author's draft and adds the matching Turkish
version. Plain headings replace the broken Roman-numeral outline (the old one
skipped II and repeated A and B), cloud and local inference are no longer split
across the file, and every "module X" placeholder is now a real cross-reference.

Content fixes: cites Scaling Laws for Neural Language Models (Kaplan et al.,
2020) behind the claim that more parameters means a more capable model, and
corrects the quantization example — a 32B model needs ~64 GB at 16-bit and
~16 GB at 4-bit, not the 44 GB and 12 GB the draft had.

Adds three hand-drawn diagrams: the structure of a system prompt, the context of
a plain two-turn chat, and the context of a single agent turn. The last two make
the authorship split visible — the LLM emits the Tool Call, the host machine
produces the Tool Result. The image that previously illustrated "the context
window" actually depicted a system prompt, so it is replaced rather than
recaptioned. Images embed as HTML at 70% width so the size is controllable.

Also adds the reusable diagram prompts and the two source templates they build
on, and the capital-of-Turkey next-word example as a mermaid diagram.
Removes every em dash from both language versions of Module 1 (38 in English,
42 in Turkish) and from the working agreement files, replacing them with
colons, commas or separate sentences. The rule is now written into CLAUDE.md
and AGENTS.md so it does not creep back.

Reorders "What goes into the context": the message list now runs HumanMessage,
AIMessage, SystemMessage, and the plain-chat figure comes first so the reader
sees the stack before zooming into the system prompt. The leaked-system-prompts
link moves down to the system prompt section where it belongs.

The Turkish version now keeps technical vocabulary in English (tools, agents,
context window, input, output, parameter, inference, quantization, precision,
weight, free tier, provider, request, GUI, CLI), inflected with Turkish
suffixes, since the audience is technical. Only ordinary words are translated.
Enhances the explanation of a **turn** in LLM interactions by defining it as the combination of a HumanMessage and the corresponding AIMessage. Updates the description of tool interactions, emphasizing that tools are simply functions, typically written in Python, that the model calls to fetch real data. Clarifies the roles of ToolCall and ToolResult within the context of an agent's turn, ensuring a better understanding of how these components fit into the overall message stack.
…ions

Prepares this branch to merge cleanly into the Next.js work on main.

Renames sections/ back to mini-courses/. The app hardcodes that directory as
its content root, and since it is the app that decides routes now, the folder
name carries no meaning for readers. Path references follow in the README, the
working agreement and the scratchpad notes.

CLAUDE.md and AGENTS.md move into mini-courses/, next to index.md. Next 16
rewrites those two filenames at the repository root on every dev run, and most
agents read nested ones, so inside the corpus they survive and also sit exactly
where the rules apply. The repo map now draws the line: mini-courses/ plus
MANIFEST.md is the authored corpus, src/ and tests/ are the app.

Module 1 gains the YAML frontmatter block the app reads, with five objectives
covering what the rewritten module actually teaches, including turns and the
message types.

Both language versions capitalise their trailing sequence link so the app's
duplicate-navigation stripper matches it. The mermaid progress rail stays for
now, deliberately.
Brings in the Next.js application, the CI workflows and the YAML frontmatter
the app reads from each module. Three conflicts, resolved as follows.

mkdocs.yml: took main's deletion. MkDocs is gone, along with pyproject.toml,
uv.lock and the docs deploy workflow. The link checking it did now lives in
tests/corpus/links.test.ts and in the build itself.

mini-courses/1_fundamentals/1_llms.md: kept this branch's rewrite. Main's side
was the original draft plus frontmatter; the frontmatter is already present here,
with objectives rewritten to match what the module now teaches.

README.md: hand-merged. The course sections from this branch (Why This Is
Valuable, Structure, How to Use) and the application sections from main (The
repository, Your record lives in your browser, Local development, Tests). The
old MkDocs instructions are dropped.

Verified after merging: npm run typecheck clean, npm run build succeeds, which
means the frontmatter validation and the build-time link gate both accept the
rewritten module 1.
Those two patterns had no leading slash, so they matched at any depth and
swallowed mini-courses/AGENTS.md and mini-courses/CLAUDE.md, which are authored
content and have to stay committed. Anchoring them keeps the files Next 16
regenerates at the repository root ignored, and leaves the working agreement
inside the corpus tracked.
The corpus is edited every day. Tests that hardcoded what it measured on one
day turned every module rewrite into 45 red tests: exactly 163 internal links,
96 Roman numerals, 49 tables, 21 figures, 209 distinct sources, 13 SEE ALSO
edges, a 2,440 XP ceiling, a self-check on exactly 15 sheets. None of those is a
fact about the code, so none of them belongs in a test.

Removed the assertions that count or quote the prose, across 14 files. Kept
every gate that catches real breakage: each internal markdown link still has to
resolve to a route the app serves, no internal href ending in .md may reach any
rendered page, frontmatter is still validated, and every table, figure, scroll
container and code block is still checked on all 32 modules. Where a test mixed
the two, only the literal went: the link test now asserts a non-empty set and a
legal shape for every link it finds, rather than a total.

Two of the removals are worth naming. tests/unit/path/evidence.test.ts scored 123
path reasons by word overlap against the modules they cite, so rewriting a module
un-grounded its reasons; the fixture-shape checks around it stay. And
loader.test.ts asserted module 1's file contains the literal "## Tutorial
Progress", which the rewrite removed.

Adds mini-courses/_module_template.md: annotated frontmatter plus a body showing
every structure the renderer understands, including the optional Quick Check and
checklist and the sequence link the app strips. The loader only reads the six
category directories, so the template is never loaded as a module.

77 test files, 1988 tests, typecheck and build all pass.
…dmap

The authored side of the repository now lives in one directory. mini-courses/
holds the modules, MANIFEST.md, ROADMAP.md, the working agreement and the module
template; everything outside it is the application. Neither file is visible to
the loader, which only reads the six category directories.

Links follow: the root README reaches in with mini-courses/ prefixes, and
index.md now links to both relatively, since all three sit side by side. The
working agreement's mirror rule is updated to say so, and its repo map redrawn.

ROADMAP.md is filled in with the seven phases, in order: content for
Fundamentals and Intermediate, the website, a daily newsletter matched to the
reader's job and position, advanced material, chatbot features over the content
with OpenMAIC as the reference point, the remaining material, then generative
and agentic UI. Each entry says why it sits where it does. The ordering is the
point: content and platform alternate, because neither half is worth much
running ahead of the other.
Updated the description of the advanced_multiagent topic to specify that it involves context delegation through handoff, enhancing clarity for readers. This change ensures a more precise understanding of the topic's focus within the curriculum.
Module 2 keeps its structure and its worked example, and gains the two figures.
The ASCII placeholder is now a mermaid diagram, the Roman-numeral outline is
gone, and every em dash with it.

The figures do real work rather than decorate. training-network.jpg opens a new
first section, because it shows what training changes: the same network with
random weights answers "The students opened their" with "Sun" and with learned
weights answers "Laptops". pretrain-vs-finetune.png now bridges the two halves
of the module, because the honest difference between them is the data each one
eats: unlabelled text at internet scale against pairs a human wrote. A note
names that first case as self-supervised learning, flags it as advanced, and
points at the new Module 25.

Adds a section on Unsloth covering what it is, the notebooks, the pre-quantized
releases that Module 1's quantization advice was pointing at, and its FAQ on
whether fine-tuning is right for you at all. Adds a short section on what
Hugging Face is, since both code samples import from it. References now carry
the Unsloth docs, Google Cloud's overview and a short video; Module 1 gains a
video too, in both languages. Both video titles were checked rather than
guessed.

Two new modules, which renumbers everything after them:
3_expert/25_advanced_training and 4_ecosystem/31_choosing_tech_stack. Ecosystem
becomes 26-31, Protocols 32, Optional 33-34. Slugs derive from the filename
after the number prefix, so no route or path fixture changed. Fixed alongside:
frontmatter numbers, prev/next seams, per-category counts and progress rails,
category READMEs, the README and index tables, the working agreement's repo map,
and topics.txt.

Two things on the application side needed it too. src/app/lokum.css carries one
segment selector per module and stopped at 32. And src/lib/path/paths.ts stores
a module number beside each step's slug, so those numbers were remapped; the
colleague's own test, that every step carries the number its slug actually has,
is what verifies it.

The remaining test failures were all corpus-shape assertions of the same kind
already removed once: 32 sheets, 15 drawn, 17 not, category sizes. Where the
literal was incidental to a structural check it now derives from sheetCount(),
whose own doc comment anticipated this: "Counted, never typed ... it stops being
true the day a thirty-third file lands." Where the count was the whole test, the
test is gone.

77 test files, 1951 tests, typecheck and build all pass. 34 modules, numbered
1-34 with no gaps and English/Turkish parity throughout.
…e modules

Resolves both notes left in the draft. The context-window framing was wrong:
RAG is not a workaround for a memory limit, it is the observation that no single
question needs all of your data, so the section now leads with that. The ASCII
block is a mermaid flow, and the two embedding examples are mermaid too, shaped
as the author asked: two inputs on the left, "encode" on the arrows, one
embedding model as a decision node, two vectors out. They are numbered so the
pairing is unambiguous, since a shared node cannot show which output belongs to
which input.

Adds the desktop-and-library analogy as its own subsection, because it is what
makes the rest obvious: the weights are a library and remembering from one is
unreliable, the context is a desk and reading what is open on it is exact. It is
called back in the fine-tuning comparison and again in the summary.

The fine-tuning section keeps the author's argument and sharpens it. Company
knowledge is alive and fine-tuning is a snapshot, so fine-tuning documents is
solving a live problem with a photograph. Fine-tuning is for tasks, not facts.
And the race is unwinnable: finish a month of fine-tuning and the next frontier
model is already better at your task out of the box. A closing line says this is
not either-or, because otherwise the "why not both" reference contradicts the
section. The same argument now appears in module 2 as "When not to fine-tune".

The king-and-queen figure lands where it illustrates its claim, with the parallel
arrows called out and an honest note that real embeddings have far more than the
three dimensions the picture draws. Tools is cut to a single FAISS sample, the
rawest mechanic the rest wrap, with the frameworks listed by how much they do for
you and a line explaining that FAISS returns positions rather than text.

All three external links were fetched and titled from the page rather than
guessed.

Turkish version written with verified parity: 10 h2, 1 h3, 2 figures, 5 mermaid,
1 code block.

CLAUDE.md and AGENTS.md gain two sections, "How a module gets written" and
"Translating a module", recording the practices these three modules settled:
figures carry the argument and may restructure a section, answer the
misconception the material just invited, one analogy called back rather than
three stacked, mermaid never ASCII, one code sample at the rawest useful level,
and verify every number and link title rather than passing it through.

Three tests quoted module 3's Quick Check and Summary verbatim, including the
"Keep going" line the rewrite removed, so they went the way of the other
prose-quoting tests. 77 files, 1948 tests, typecheck and build pass.
…he site

Module 4 replaces the ASCII walkthrough with the generated context diagram and
adds what the draft never explained: where a tool schema comes from.

You do not write the schema. The framework reads it off your function: the name
from the function name, the description from the docstring, the parameters from
the type hints, and then sends it so the model receives it beside the system
prompt. Which makes the name and the docstring the interface rather than
documentation, because they are the only thing the model has when it chooses a
tool. A function called get_data with the docstring "gets data" gets picked at
the wrong moments and no prompting fixes it.

Then one real call end to end, with a weather tool: what the model receives as
readable text and as the wire schema, the question, the exact tool_calls object
the model emits, the Python the host runs, the tool-role message that carries
the result back, and the final answer. Three details are called out because they
catch people: content is null while the model is asking rather than answering,
the id is how the result is matched to the call, and arguments is a JSON string
rather than an object.

The tool list is trimmed and typed, and the last entry is Module 3's retrieval
pipeline turned into a tool, since deciding when to retrieve rather than
retrieving every time is most of what separates a RAG app from an agent.

Turkish version written, parity verified across 9 h2, 5 h3, 2 figures, 2 mermaid
and 9 fenced blocks.

Also fixes a real defect I introduced earlier. The rule to embed images as HTML
at 70% width was wrong: the renderer strips raw HTML out of the markdown, so all
eight figures added to modules 1 to 4 were absent from the published site while
still rendering on GitHub. Verified against the static export, where the image
filenames appeared on zero pages. All 19 figure blocks are converted back to
markdown, which the app turns into a proper numbered figure with a caption label
and a width class, and the image filenames now appear on four pages each. The
rule is reversed in CLAUDE.md, AGENTS.md, the module template and memory, with
the reason recorded so it is not tried again.

Two of the colleague's tests caught this class of mistake and are worth keeping:
the figure-caption count, and langCoverage, which compares English and Turkish
extents and flips a module to EN-only when a translation falls behind.

77 files, 1948 tests, typecheck and build pass.
…rkish

Module 5 was already sound, so this is a light pass.

The ASCII walkthrough of the growing stack is replaced by the plain-chat context
figure, which is literally the thing the section describes: a stack of Human and
AI messages where nothing is ever removed. The caption carries what the ASCII
said in prose, that the whole container is what gets sent on every call, since a
figure cannot hold that line itself. The stack-resending point is kept in the
paragraph underneath.

The module had no References section, which every module is supposed to have, so
it gains one: the three-memory-types article, plus the two internal
cross-references the text already leans on, RAG for how long-term memory returns
to working memory and Advanced Memory for where this goes next. That second link
replaces a bare mention of the module by name.

Otherwise: Roman numerals out, em dashes out, the greeting and the sign-off cut,
and one objective added for the module's best idea, that an LLM is stateless
during generation and a conversation is something the application fakes.

Turkish version written, parity verified across 9 h2, 1 h3, 2 figures, 2 mermaid
and the comparison table.

77 files, 1948 tests, typecheck and build pass.
Three additions, all from mistakes or near-misses this session.

A green suite is not proof the page is right. The missing-images defect passed
typecheck, 1948 tests and the build, and was only provable by grepping the static
export. So the checks section now says: when changing how content is authored
rather than what it says, look in out/ and confirm the figure is on a page.

A failing suite is often the author, not the assistant. Twice the real cause was
a concurrent edit: a save that dropped a section, and a frontmatter fence broken
into "## module: 3" with the closing delimiter deleted, which took 31 test files
down. Run git status before assuming fault, and treat a file carrying NOTE
markers as the author's live draft: repair the structural breakage, touch nothing
else, say what was fixed.

Cross-module payoff is now written down as a practice, because it was used
repeatedly without being stated. Module 3's retrieval becomes a tool in Module 4;
Module 1's read_file schema reappears in Module 4's system prompt. One sentence
each, and it is what makes the set a series rather than seven articles.

Also notes that API examples use the OpenAI and OpenRouter-compatible shape,
since Module 1 points readers at OpenRouter and they can run what they read.

Memory gains a matching note, including the two of the colleague's tests that
protect against this class of mistake and should never be deleted as content
counts: the figure-caption count, and langCoverage, which flags a module EN-only
when its translation falls behind.
The module now opens with the definition rather than working towards it: an
agent is a loop in which an LLM calls tools until it reaches its goal, and when
it calls no tools it writes a final answer and the loop breaks. Everything else
in the module is that sentence unpacked.

Single-turn against multi-turn is stated plainly, and the point that the model is
identical in both cases: nothing is added to it, only the number of times you
call it and what you put in front of it.

A new section says where the loop actually runs, which is the part people get
wrong. None of it is inside the LLM. The loop, its termination, the message
stack, assembling the system prompt before every call and executing the tools
all happen on the host machine, and that list is exactly why agents use a
framework while a plain call does not. Module 1 is the proof: we ran an LLM from
the terminal with no framework because it was one call, and an agent is almost
entirely scaffolding.

That leads to the payoff the series has been building towards. The LLM really is
just a brain, text in and text out, storing nothing. Module 1 was not
oversimplifying; every capability since has been an environment built around it.

The three memes each make a different point, so each moved to where it lands. The
bell curve now sits under the definition, since it argues the simple answer is
the correct one. "LLM + for loop, is this an agent" follows the single-turn
contrast. The Scooby unmasking, which reveals prompts, if-else, loops and
functions, sits under where-the-loop-runs. All three had captions that wasted
them, including two that said only "See the steps in action!" and "Agents in
action!".

Only the smolagents example remains; crewAI and AutoGen keep their links. The
agent-context figure from Module 4 is reused for one pass of the loop.

Turkish version written, parity verified across 9 h2, 5 figures, 2 mermaid, the
code block, the definition blockquote and the table.

Two of the colleague's tests hardcoded module 6's figure counts to assert that
images are never counted as diagrams. That claim is worth keeping, so both now
derive the totals and assert the behaviour instead: adding an image must not move
the diagram count, and the DIAG term must equal the diagram count alone.
Fundamentals is complete. All seven modules are rewritten in both languages, and
the app reports EN · TR on every one of the seven pages.

Module 7 is rebuilt around the two problems that splitting the work creates,
because a single agent has neither and they are the whole difficulty.
Coordination: who does what, in what order, and how anyone knows a step is
finished, which is where duplicated work and deadlocks come from. Context
transfer: each agent has its own message stack, so deciding what crosses between
them is a real design choice, and getting it wrong is the usual reason a
multi-agent system performs worse than the single agent it replaced.

That leads into shared against isolated context, stated plainly with the cost of
each, and deferred to Module 20 rather than half-explained here.

The architectures figure carries three panels, so the section now walks them:
single agent as Module 6, network where every agent talks to every other, and
supervisor where workers only talk to the one that delegates. The advice is to
start with a supervisor, because every decision passes through one place and most
problems that look like they need a swarm do not. Hierarchical, agent-as-a-tool
and subagents are named so the vocabulary is familiar, and left for later.

The smolagents example now uses managed_agents, and the text points out that
workers are handed to a supervisor exactly the way tools were handed to an agent
in Module 6, because that is what they are to it. crewAI and AutoGen keep links
without code. The module closes the category by naming the seven ideas
Intermediate is built on.

Module 6 gains the autonomy spectrum: a plain call where you decide everything, a
fixed workflow or state machine where you wrote the steps, and an agent where the
model chooses them, with LangChain's "what is an agent" as the source and the
observation that most production systems are deliberately not at the far end.

Module 20's draft gains the LangChain multi-agent reference and two topics this
module defers to it.

CLAUDE.md, AGENTS.md and memory gain a section on where a visual comes from, in
cost order: reuse an existing figure, draw one with the project templates, use
mermaid for any flow or branch, or search. Searching is explicitly legitimate,
including Google Images for a standard diagram, an article whose figure is worth
citing, YouTube and Shorts for video, and memes or brain rot when they compress
an argument, which is what Module 6's three do. With the standing rule that the
caption must still say what the picture cannot, and that you check what an image
actually shows before writing about it.
The corpus reaches for `graph LR` almost every time, and several of those would
read better as something else. Mermaid 11.17.2 is what ships, so the working
agreement now carries a table mapping the shape of an idea to the type that fits:
sequenceDiagram for actors exchanging messages over time, mindmap for a
breakdown, timeline for how a topic evolved, stateDiagram-v2, pie, quadrantChart,
erDiagram, xychart.

Two are called out. sequenceDiagram is the honest shape for anything where a
user, a host and a model take turns, which is most of Fundamentals. And timeline
answers manifest rule 1 directly: a model cannot tell a reader when an idea
appeared or what it replaced, so a timeline is a picture only a human can draw.

Also records the constraint that goes with it, verified in the pipeline rather
than assumed: no colour literal may survive into a rendered diagram. The
transform rewrites `style NODE fill:#HEX` into themed classes but knows only nine
specific hexes, so any other colour fails the build, and the safe move is no
colour at all. Anything ending in -beta renders but its syntax can still move.
…ation

Included a new image comparing the autonomy spectrum of LLM calls and agents in both English and Turkish versions of the agents module. This visual aids in understanding the varying levels of autonomy in production systems. Updated the text to reflect the addition and clarify the model's behavior across different scenarios.
The intermediate draft was machine-written, so this replaces it rather than
editing it. The module opens from Module 1's probability formula, keeps the
mixer and the two-drivers analogies, and calls the mixer back at role
prompting to make the retrieval argument.

Two claims from the notes are corrected against their own sources: personas
in system prompts do not improve accuracy (2311.10054), and the CoT
visualizer's plain side is deliberately prompted to answer badly, so it is
labelled a demonstration. Every external link was fetched for its real title.

FIG. 8.1 started as a mermaid timeline, which CLAUDE.md recommended. It
renders, but mermaid paints its blocks with inline colours the site's tokens
never reach, so it ignores the design system and stays bright in dark mode.
Replaced with a graph TD, verified in both themes, and the rule reversed in
CLAUDE.md and AGENTS.md.

The lane-count assertion in the record layout test pinned a measurement of the
corpus that moves with any module's cross-references. The invariant it is
named for is kept; the pinned number is gone.
Author's own notes: Deep Agents, proposer-verifier and self-critique added to
the topics they belong under.
Mirrors the English exactly: 20 headings, 3 figures, 2 mermaid blocks, 19
code blocks, no em dashes. Technical vocabulary stays English with Turkish
suffixes, prompt and code blocks stay verbatim because they are records of
what you send a model, and the diagram that carries the argument is
translated while the series map keeps the English module titles.

The sheet now measures as a real translation rather than a placeholder, so it
badges EN · TR. Three assertions pinned the bilingual set to modules 1-7,
which is a fact that changes every time a module is translated. They now
derive it: langCoverage is checked against extents measured off the files, no
draft sheet may carry the badge, and the index row is checked against its
module. The measured Hızlı Kontrol count in quickcheck.ts is now 7.
Author's own note. The files on disk still number security 13 and loop
engineering 14; this records the intended order, not a rename.
…e plan

The landscape sheet was a split of the coding-agents module that the topic plan
no longer wants, so it is gone and everything after it moves down. Loop
engineering also moves ahead of security, as the plan now has it. The set is 33
sheets, numbered 1-33 with no gaps:

  intermediate  10 coding agents · 11 harness · 12 loop · 13 security · 14 personal
  expert        15-24 · ecosystem 25-30 · protocols 31 · optional 32-33

Routes are unaffected: a slug is the name after the number, so renumbering
moves no page. What did move: frontmatter numbers and prerequisites, every
cross-reference and its label, the category deks, the category README lists,
the range table in README.md and index.md, the step numbers in every learning
path, the segment lists in lokum.css, and the sheet enumeration the browser
suite checks the site against, which had been stale since two sheets were added.

The surviving coding-agents sheet inherits the deleted sheet's prerequisite
(Module 6, Agents) rather than the sheet that no longer exists. Its "which tool
to pick" pointer now goes to Choosing a Tech Stack, which is whose job that is.

topics.txt numbered the ecosystem block from 24 and protocols at 28, so two
numbers were used twice; the plan now counts the way the corpus does.
66 module files renamed to their names, 270 link targets rewritten to match,
and the three GitHub-only duplications of what the app already derives deleted.
Unavoidably one commit: between the rename and the link rewrite every link in
the corpus names a file that does not exist, `courseLinkFor` throws on the first
sheet, and the build dies there with 269 other errors it never reaches.

Done by `scripts/migrate-corpus.mjs`, which reads `curriculum.yaml` for every
fact it uses and is committed so the pass is auditable. It cannot run twice: it
asserts its pre-state first, and after one run "no prefixless module file
exists" fails and it stops with "the prefixes are already gone".

What it changed, and what it refused to:

  targets   `../3_expert/17_advanced_tools.md` -> `../3_expert/advanced_tools.md`.
            The DIRECTORY keeps its own prefix; the directories are not renamed.
  labels    a label carrying a module number is rewritten from the TARGET's yaml
            title, never from the label's own text, so `[Module 11]` becomes
            `[Harness Engineering]` and a stale title is corrected. A label with
            no number is prose and was left exactly as written: `[Expert] picks
            up from here` is a sentence, and the target's title is not what it
            means.
  Turkish   the number is stripped and the author's Turkish text kept, because
            the yaml holds no Turkish titles. Three bare `[Modül N]` labels in
            prompt_engineering_tr.md have nothing left after the number and are
            reported for the editorial pass rather than filled in with English.
  deks      24 `*Category: Intermediate — Module 13 (6 of 7 ...)*` lines.
  footers   126 `**Previous/Next Module:**` and `**Next Category:**` lines, in
            both languages. They were already wrong: the Intermediate chain ran
            8, 9, 10, 11, 13, 12, 14, because the last renumber remapped the
            numbers in those links and not the chain they describe.
  READMEs   the six `## Modules` lists, 33 blocks of three numbers each. The app
            strips all three of these (`strip.ts`, `intro.ts`), so none of it
            was ever on the site.
  mermaid   a leading module number leaves a node label: `[16: Advanced UI]` and
            `[1. LLMs]` become `[Advanced UI]` and `[LLMs]`. This silently fixes
            the Expert progress rails, which were off by one throughout: the
            file for module 15 drew `A[16: Advanced UI]`. The rule is narrow on
            purpose, because the corpus also draws `"Chunk 1: def add(a,b)"`,
            `"Vector 1<br/>[0.2, 0.8"`, `"2020 to 2024<br/>"` and
            `heartbeat ~5 min`, and none of those numbers is a module.
  H1        the title alone. Three English H1s disagreed with the yaml title and
            decision 5 gives the yaml the win, each reported by the script:
            "Retrieval-Augmented Generation (RAG)" -> "RAG & Embeddings",
            "LLM Tool Calling" -> "Tool Calling",
            "Human-in-the-Loop" -> "Human in the Loop".
  frontmatter  down to `summary` and `objectives`, and the fence gone entirely
            from the 19 drafts, which had neither.

Measured before the run, so that none of it had to be guessed at: 265 module
links, not one of them with a label whose number disagreed with its target, and
not one of them pointing at a module whose real number differed from its prefix.
That last one is why unifying the link resolver in the previous commit could not
move a graph edge.

App side, all of it removing the transitional scaffolding that commit put in:
`fileFor` collapses from a search to a join, the prefix-optional regexes in
`curriculum-file.ts` and `links.ts` become prefixless, and `schema.ts` flips to
`z.strictObject`, so a file declaring its own `module` or `status` again fails
the build by name. The reconciliation test is deleted: there is no second source
left to reconcile against.

`renders.test.ts`'s two prefix-dependent rules are replaced by three that hold
for the corpus as it now is: no number in a filename and none in a frontmatter
block, no numbered module file left in any category directory, and no footer or
dek for `strip.ts` to remove. The last one matters because `strip.ts` still
removes both as dead paths, so one coming back would be invisible on the site.
`honesty.test.ts` stops parsing `module:` and `prerequisites:` out of 33
frontmatter blocks with two regexes and reads the curriculum instead.

Gate: typecheck clean, 861 vitest tests, build succeeds. Structural extract of
the export, before and after: IDENTICAL. Same 33 routes in the same order with
the same titles, same `33 SHEETS · 14 DRAWN · ~6 H 25 MIN` eyebrow, every one of
the 29 graph traces on the dashboard unchanged (which is the direct proof the
SEE ALSO path did not silently empty), `SHEET n OF n` unchanged on all 33
sheets, `EN · TR` on the same 8 pages, and no relative `.md` href anywhere.

Two tests and 33 browser cases cannot be green until this commit exists, and
they are the same failure: `revisionFor` reads `git log` for a file's own last
commit, and a renamed file has no history until the rename is committed. `git
log -- mini-courses/1_fundamentals/1_llms.md` had commits and
`git log -- mini-courses/1_fundamentals/llms.md` had none, which is exactly what
the code is written to answer `null` to. Verified green immediately after this
commit.
`CLAUDE.md` and `AGENTS.md` (byte-identical, as always): the "In flight"
section, which said it must be replaced once the migration landed, is replaced.
The new section says where the course shape lives, what a module file still
declares, that prose names another module by title, and which three things are
deleted rather than maintained. It also names the seven rules the validator
enforces, because the fifth one is the only new thing that can go wrong.

"Starting a new module" is now three steps, and the third is the one that is
easy to miss: one line in `curriculum.yaml`. Without it the file is one nobody
listed, and the build says so by name.

Twelve prose references to a module by number become the module's title, which
is the rule the corpus now follows: "the training-network figure became the
opening of Training LLMs", "RAG & Embeddings' retrieval pipeline reappears in
Tool Calling". Two remaining "Module N" strings are quotations of the forms that
are now forbidden. The repo map drops its number ranges, which were a fourth
copy of what the config owns, and gains `curriculum.yaml`.

`_module_template.md` was still teaching the old shape, which is the worst place
for it to be wrong: its frontmatter block declared six fields the config now
owns, its H1 read `# Module 0:`, its rail read `[1. LLMs]`, its link examples
carried numbers, and it ended with a `**Next Module:**` footer plus a note
explaining how to write one. All of that is replaced, and the closing note now
says why a footer, a dek and a README list must not come back: `strip.ts` still
removes them, so one added back is invisible on the site and wrong on GitHub.

`README.md` and `index.md`, together as they must be: the hand-maintained
`Modules | 1-7` column becomes `Sheets | 7`, because a range encodes six other
categories' positions and a count does not. README also loses two claims that
were not true: "reading the 32 sheets" and "17 of the 32 sheets are not written
yet" (the set is 33 with 19 unwritten, and the site prints the live figures on
every listing page), and a citation of `tests/fixtures/path-evidence.json`,
which is not a file in this repository.

`ROADMAP.md`'s three module ranges become titles, and two of the three were
wrong: phase 1 said "Modules 1 to 15" for Fundamentals plus Intermediate, which
ends at 14, and phase 6 said "Modules 24 to 32" for a range that is 25 to 33.

Stale prose counts in eighteen source docblocks: "all 32 sheets", "15 of the 32
sheets carry a self-check", "17 of the 32 sheets are drafts", "SHEET 13 OF 32",
`0 of 32`, `02/32`, "reads 32 files", "at 32 nodes". Where the number was the
point it is corrected; where it was decoration it is gone, because a count in a
comment is the kind of thing nobody updates. `links.ts`'s measured link count is
re-measured at 265 and its examples lose their prefixes.

Two things deliberately left alone, and reported rather than changed:

  - `src/lib/record/derive.ts`'s CLASS thresholds (8 / 16 / 24 / 32). §12.5.3
    pins CLASS to sheets out of 32, and the set is 33, so CLASS IV is now
    reachable one sheet before the end. That is a design question about the
    award, not a stale comment, and it is not this migration's to settle.
  - `mini-courses/scratchpad/research/`. Its files carry their own numbering
    that already disagrees with the corpus (`12_security.md`,
    `13_loop_engineering.md`), it is never published and never loaded, and it is
    the author's raw research material.

Gate: typecheck clean, 861 vitest tests pass, build succeeds, structural extract
of the export unchanged. Playwright: 242 passed, with
`accessibility.spec.ts:212` ("the manifest's quiet columns clear the §10.4
floor") intermittent. It is not this commit's doing and not the migration's: run
against a static server on the export of `05a113d`, before any of these commits,
the same case fails 3 runs in 4 with `dark: "01" at 3.10:1`. It reads computed
colours straight after a theme switch, which is the same shape as the two flakes
tests/README.md already documents, and it is not in their retry list.
…ulum

`lokum.css` held three hand-written lists keyed on the module number: 33
selectors for every module's segment fill, 14 for a drawn module's path step
tick, and the same 14 again inside `forced-colors: active`. 61 lines, each
carrying the number twice, in a file the curriculum config was supposed to have
taken the last number out of.

Enumeration itself stays, because it is inherent rather than accidental: channel
A matches a class on `<html>` against an attribute on a descendant, and CSS has
no operator that relates the two. There is no `:has()`, `attr()` or custom
property that says "the number in the class equals the number in the attribute".
So there is one selector per module and there has to be. What is removed is
writing them out by hand.

`scripts/curriculum-css.mjs` generates them into `src/app/lokum-modules.css`,
which `lokum.css` imports at the top. Committed rather than gitignored, because
`prebuild` regenerates it and neither vitest nor playwright runs `prebuild`, so
a gitignored file would be absent in the two places that check it. A committed
generated file can go stale instead, so `category-css.test.ts` runs the
generator and asserts the committed file matches, which is the one thing the
other cases in that file cannot check for themselves. It also now reads
`lokum.css` with the generated file inlined at its import, because a selector's
completeness is a property of the stylesheet and not of which file it was typed
into.

**The plan said `@import "./lokum-modules.css" layer(components)` and that is
wrong.** Group C sat in a bare `@media (forced-colors: active)` block, outside
any layer, where it beats `.hl-seg { background: Canvas }` in `lokum.css`'s own
forced-colours block. `layer()` on the import puts the whole file into one
layer, and layer origin outranks specificity, so no selector could have saved
it. Tried, and it cost two cases in `colour-not-alone.spec.ts`: every segment
came back the same fill under forced colours, and a signed path step lost its
`SIGNED OFF` marker. Neither typecheck, nor 862 unit tests, nor the structural
export extract noticed; only the browser did. So the import carries no `layer()`
and the generated file declares `@layer components` around the two hue rules
itself, which reproduces the structure `lokum.css` had exactly.

`prebuild` and `predev` run the generator, so a curriculum change cannot ship
with yesterday's selectors.

Verified beyond the suite: the compiled stylesheet's `hl-signed` rules are
identical before and after, selector for selector and declaration for
declaration, extracted from the two builds' CSS chunks and compared as sets.

Gate: typecheck clean, 862 vitest tests, build succeeds, playwright 243 passed
with no failures and no flakes, structural export extract unchanged.
Found by reordering the curriculum, which is what that acceptance check is for.

`prerequisites` used to be hand-written in each module's frontmatter, and every
one of them happened to be ascending, so nothing noticed that `edges.ts` and
`title-block.ts` both sort the list while the loader did not. Resolving it from
the yaml's `needs` made the author's listing order visible: `personal_agents`
names `[loop_engineering, security]`, and swapping those two adjacent modules
left it reporting `[13, 12]` from the loader and `[12, 13]` on its own sheet.

`title-block.test.ts` caught it, which is the right test catching the right
thing. The fix is upstream of both readers rather than in either: a prerequisite
list is a set, so it gets one order and it gets it once, in the loader.

`loader.test.ts` gains the rule, so the two cannot drift apart again whatever
order an author writes the names in.

It changes nothing in today's curriculum: every `needs` list happens to resolve
ascending, so the structural export extract is unchanged. That is exactly why
this needed a reorder to find.

Gate: typecheck clean, 863 vitest tests, build succeeds, structural export
extract unchanged, playwright 242 passed with the pre-existing
`accessibility.spec.ts:212` intermittent.
Every Intermediate progress rail still described the course as it was before
the landscape sheet was dropped: eight nodes for a seven-module category,
splitting the single title "Coding Agents: Extending Them" across two of them,
and putting Security ahead of Loop Engineering.

Mine, from the renumber. I remapped the numbers in those rails and never
touched the node list, so `C[10. Coding Agents] --> D[11. Extending Them]`
became `C[Coding Agents] --> D[Extending Them]`.

One of them ships. `strip.ts` deletes a rail under `## Tutorial Progress`, and
52 files put it there, but 17 use `## Where this fits in the series` and the app
leaves those alone. So module 8's rail was live on the page as FIG. 8.7, and
de-numbering had removed the only clue it was wrong: a reader seeing "10." and
"11." on two nodes of a seven-module track had something to notice, while
"Coding Agents" and "Extending Them" read as two real module titles. The other
twelve are GitHub-only, which the working agreement treats as half the
audience. English and Turkish had also drifted apart on module 9, where the
English rail was right and the Turkish one still named the deleted sheet.

All thirteen are now generated from curriculum.yaml's order rather than edited,
so the shape cannot disagree with the config again. Verified on the built page:
seven nodes, Prompt Engineering through Personal Agents.

Also corrects a wrong number rather than a stale one. advanced_harness_engineering
cited "the guardrails/hooks/sandboxes basics from Module 10", which is Coding
Agents; guardrails, hooks and sandboxes are Harness Engineering. Both languages
now name the module instead.
My mistake, and it was the one thing both agents were warned about. I staged
`mini-courses/2_intermediate` as a directory rather than naming the files I had
changed, so `d3756bf` added nine images the author is still choosing between for
module 10, and I pushed them.

No module references any of the nine. `scripts/copy-course-images.mjs` copies
each `images/` directory wholesale without consulting what any module names, so
tracked or not they land in `out/`; but tracked means they also land in a CI
build, which is nine unpublished drafts on a public site.

The files stay on disk untouched. They go back to being the author's to add when
a module names them.

The blobs remain one commit deep in history. That is his call, not mine: getting
them out of the pack means rewriting a pushed branch, and they are images he
intends to commit eventually anyway.
…irectories

Two corrections to the working agreement, both from mistakes made while landing
the migration.

The four-paths promise. `9b672d8` wrote that adding a module leaves exactly
three paths in `git status`, then `ea5ec0a` committed a generated
`lokum-modules.css` and made it four. Measured in a throwaway worktree: adding
two files and one config line fails two `category-css` cases until the generator
runs, after which the status shows four paths. The generated file being
committed is the right trade, because neither vitest nor playwright runs
`prebuild`, so the promise was what needed correcting.

And the staging rule, which is here because I broke it. Staging
`mini-courses/2_intermediate` as a directory swept nine of the author's
in-progress images into a commit and onto the remote. Name the files that
changed, and read `git status` first.
`9eee98c` deleted it. It should not have.

I asked whether the per-module notes should move into curriculum.yaml so nothing
would be lost "when scratchpad goes", and Amirkia said yes to moving the notes.
I turned that into deleting the file, wrote the deletion into the plan, and the
plan was approved as a whole. That is nominal consent for a step he never asked
for: moving a copy of the notes is not the same as removing the file, and this
is a file he edits while working, three times during the session in which it was
deleted.

Restored byte for byte from `9eee98c^`, so every note, every trailing space and
the two unnumbered Optional entries are exactly as he left them. The notes also
live in curriculum.yaml, which is fine: the yaml is what the build reads and
this is his to think in.
Approval of a plan is not approval of every step buried in it. Recorded because
topics.txt was deleted on that reasoning.
Brings in main's §14 to §17 work — Supabase-backed accounts and
organisations, the `/sheets/` register, the home screen, `/profile/`'s
drafter block and register, `/team/`, and the claim receipt — onto the
branch where `mini-courses/curriculum.yaml` owns the course shape.

Main touched nothing under `mini-courses/`, so the corpus merged
untouched: 33 sheets, 8 bilingual, 95 figures, 29 hl-trace, 27 hl-rail,
no relative `.md` href in the export, no numbered filename on disk.

Twenty-one conflicts. Most were one shape: main kept importing
`CATEGORIES` from `categories.ts`, which the migration moved to
`curriculum-file.ts`. Both sides were wanted in every case.

The three that were not mechanical:

- `RolePanel` — main moved the mark offer out to the drafter block and
  dropped `MarkOffer` and `useState` with it. Main's structure stands,
  keeping the `drawnSlugs` prop, because `drawnCount` no longer reads a
  hardcoded `DRAWN_THROUGH`.
- `/profile/` — main replaced the eight panels with a register, so the
  role section this branch had inserted had no enclosing `<section>`
  left. Main's page stands and its `role` row takes the prop.
- `home/Resume` — new on main, and it renders `PathStanding` with no
  drawn set. It is a server component holding `facts`, so it measures
  the set itself and hands it down (§12.2).

Twelve test files this branch had deleted were modified by main, and all
twelve are restored: they cover the record subsystem main just extended
with claim, merge, sync and scope, and not one of them writes down a
fact about the corpus.

Two assertions did, and both said in their own comments that the number
was "counted, never typed" while typing it. They now count it:

- `record-report.test.ts` read "ledger of all 32 sheets".
- `record-pages.spec.ts` read "2 of 8 signed off" for a subsystem that
  has held seven modules since the landscape sheet was dropped.

Gate: typecheck clean, 2,055 unit tests, 58 pages built, 357 browser
cases. Three browser cases fail on a cold start, all of them channel-A
first-paint probes; a clean build of `origin/main` fails three of the
same family on this machine, and the stamps themselves are correct
(`hl-signed-13`, `hl-cat-intermediate-started`, `data-hl-record="1"`).
`derive.test.ts` asserted that every Fundamentals module was shorter than
every ready module from 8 up. Its own comment called the two bands "a real
fact about the corpus and worth pinning", which is the one thing a test in
this repo may not do (`tests/README.md`).

It went red the moment Harness Engineering was written at 2,216 words
against a Fundamentals module at 2,468. No defect: the corpus simply no
longer sorts into two bands, and an editorial choice about how long a
module runs is not something a test gets a vote on.

It also keyed off module numbers, which `curriculum.yaml` now owns.
… engineering

Modules 10, 11 and 12, rewritten from scratch in both languages. The three
files that were here were machine-written drafts and none of their text
survives.

**Coding Agents: Extending Them.** Opens on why an agent that writes and runs
code is useful far outside coding: it does not need a tool to convert a PNG
when it can install a library, and it builds what does not exist as readily as
it uses what does. Then the eight extension points, in the order they build on
each other: AGENTS.md, slash commands, MCP, subagents, skills, hooks, plugins,
auto memory, plan mode and effort. Closes on the observation that almost every
one of them is markdown in a folder, which is the format the reader and the
model both handle well.

**Harness Engineering.** Starts from the gap hooks opened in the previous
module: everything else is advice with a good success rate, and a good success
rate is not what you want from "never push to main". Agent = Model + Harness,
the harness as the outer ring that contains the prompt and context work rather
than competing with it, and Böckeler's split into guides the agent reads
beforehand and sensors that report on the work. Carries two numbers: five
harness changes moved one agent from 52.8% to 66.5% on Terminal Bench 2.0 with
the model untouched, and one model across eight harnesses scored 14 to 20 out
of 30.

**Loop Engineering.** The driver was still a person. Four levels, each wrapping
the one below: the agent loop, the verification loop with a goal and a cap on
tries, the event-driven loop on a clock or a webhook, and the autonomous Ralph
loop, one task per iteration with the state on disk. Then the agent designing
the loop itself, with agent teams against dynamic workflows on the question of
who holds the plan.

Ten images the author supplied, placed where the argument needs them.

Corrections made against the draft rather than passed through:

- Custom commands have been merged into skills. `.claude/commands/deploy.md`
  and `.claude/skills/deploy/SKILL.md` both give you `/deploy`; the
  human-versus-agent distinction the draft drew is now the frontmatter fields
  `disable-model-invocation` and `user-invocable`.
- Claude Code reads `CLAUDE.md`, not `AGENTS.md`, and the documented move is a
  `CLAUDE.md` that imports the other.
- Both formats support nested per-directory files. The draft said only some
  agents do.
- Dynamic workflows are `/effort ultracode` or the `ultracode` keyword in one
  prompt, not `/ultracode`.
- Agent teams are experimental and off until
  `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`.
- `RyanAlberts/best-of-Agent-Harnesses`, not the lowercase spelling.

Every external link fetched for its real title. Two would not answer: the
OpenAI post (403, title confirmed by search) and the author's own Medium
article (403, title from the URL). LangChain's own fourth loop level is hill
climbing rather than the autonomous loop, and that is noted in the module
instead of being smoothed over.

Gate: typecheck clean, 862 unit tests, 51 pages built, 243 browser cases.
Bilingual coverage 8 to 11. Every one of the ten images verified present in
the static export as a real figure with a caption, because a green suite has
hidden missing figures on this corpus before.
Asked for while writing Coding Agents: the module explains MCP as "somebody
already wrapped the API you were about to wrap", and Tool Calling never said
that wrapping an API is what a tool usually is.

Adds a `send_email` tool to the existing list, which is nine tenths Gmail's own
API and one tenth tool, then the paragraph: the same shape gives an agent your
Jira, your calendar, your internal service, and it is usually the fastest way
to make an agent useful at work because the API already exists.

Two consequences stated with it. A tool is exactly as powerful as the
credentials behind it, so one that sends mail from your account can send
anything to anyone. And you rarely write these by hand any more, which is the
handoff to MCP.

Also fixes two bare module numbers in the surrounding text, since prose here
names a module by title: "Module 1 introduced the context" and "the RAG
pipeline from Module 3".
…figure

An editorial pass over the three new modules. Nothing about the content
changed; the sentences did.

The problem was density. Several passages had been compressed into shapes that
are compact to read but hard to follow: relative clauses folded back on
themselves ("a request with a good success rate, and a good success rate is not
what you want"), telegraphic fragments used as sentences ("Which means...",
"Which is a sensor pointed at the guides."), stacked adjectives, and one
90-word sentence in Harness Engineering carrying five separate items through
four semicolons. That last one is now a list.

The summaries were the worst of it. Coding Agents ended on ten one-clause
sentences fired in a row, one per extension point, which is short without being
readable. It now groups the eight into what the agent knows, what it can do,
and what controls it.

Thirty-two passages loosened across the three, plus the same thirty-two mirrored
into the Turkish. All six files got longer, which was the point: concise was
never the goal, and being brief at the cost of the reader is the failure mode
the manifest names in rule 3.

Also fixes a real defect the read-through caught: **harness_engineering_tr.md
opened with Loop Engineering's opening.** The Turkish carried the driver's-seat
paragraphs instead of the hooks paragraph its English sibling has, so the module
introduced itself as the wrong module. The structural counts matched, so nothing
in the suite could have found it.

New figure in Loop Engineering, in the agent teams section, before the
comparison table: `subagents-vs-teams.png`. Its caption takes the point past
what the drawing already says, since the drawing carries its own label. The
dashed result circles are what matters, because a subagent's result is all that
survives it, while an agent team summarises nothing on the way up. That is
where the higher token cost in the table comes from.

Gate: typecheck clean, 862 unit tests, 51 pages, 242 browser cases. The one
failure is `navigation.spec.ts` "previous walks 32 back to 1", which does 31
real page loads under `test.slow()` and passes twice in isolation; the same spec
failed the same way under load earlier in this branch. Figure counts in the
export: 6, 7 and 7, with the new image confirmed on the page and copied to
`out/course-images/`. Bilingual coverage still 11.
…nting it

Two rules from the editorial pass over modules 10 to 12.

**Never dense.** Plain English and compressed English are not the same thing,
and compression is the failure mode this project keeps hitting. Amirkia: "Make
sure you did not make the language too dense solely to make it short or
concise. We do not like dense language at all." The rule now carries a table of
the five shapes that mean a passage is too compressed, each with the real
sentence that had to be rewritten: a clause folded back on itself, a fragment
used as a sentence, stacked adjectives, one long sentence carrying a list, and a
run of one-clause sentences.

The check is not word count. Read it aloud and ask whether somebody explaining
this out loud would say it that way. Loosening a module makes it longer, and
that is the correct outcome.

**Read a translation, do not just count it.** `harness_engineering_tr.md`
shipped carrying Loop Engineering's opening paragraphs, so the module
introduced itself as the wrong module while headings, figures, mermaid blocks
and code fences all matched exactly. No check can catch that; reading the
opening and the closing of both files can.

Memory updated to match, as `no-dense-prose`.
All three went red while Security and Personal Agents were being written, and
none of them had found a defect. Each was asserting a fact about what the
corpus happens to contain today.

**`checklist-island.test.ts` needed some module to carry a task list.** It
picked whichever `ready` sheet had one, which was the old machine-written
Security sheet and nothing else. Rewriting that sheet without a checklist took
three cases down, all of them about the `render.ts` and island index contract
and none of them about Security. It now reads the checklist out of
`tests/fixtures/kitchen-sink.md`, which is what the fixture is for: a structure
can be tested without a module having to keep using it.

**`derive.test.ts` pinned two link counts**, `countSources(body(10)) === 15` and
`body(14) === 16`. The property it meant to protect, that a URL in backticks or
in a fence is not openable while an anchor and a bare URL are, is already
covered by the two cases directly above it from inline fixtures. Removed rather
than renumbered.

**`manifest.test.ts` asserted sheet 13 was English-only.** It went red the day
Security was translated. It now checks every row against the coverage the
loader computed, which is the real invariant: the manifest prints what it was
given and does not invent a label.

One finding worth raising rather than asserting away, left as a comment in
`checklist-island.test.ts`: **`render.ts` passes an authored `- [x]` straight
through as `checked`.** No sheet in the corpus writes one, which is why nothing
noticed until the fixture supplied one. A pre-ticked box tells a reader an item
is done before the island has read their record, and then flips. Whether the
build should strip it is the app's call, so the assertion was narrowed to the
contract that does hold (every box inert, no `data-ticked` served) instead of
being scoped around the behaviour.

861 unit tests.
Modules 13 and 14, rewritten from scratch in both languages. Both were
machine-written drafts and none of that text survives. Written deliberately
simpler than 10 to 12, as asked.

**Security** splits the subject in its first lines, because two different
things share the name: the security *of* LLMs and agents, and LLMs *doing*
security work. Jailbreaking is defined in one sentence, then black box against
white box, which is a claim about what the attacker can see rather than about
technique. The two figures earn their place there: the same question refused
and then answered, and token-level against prompt-level, where the gibberish is
what a computer finds and the story is what a person writes.

The part worth the most is the one the draft asked for. A system prompt
outranks a user message *by design*, so the module says why injection works
anyway: the model reads one stream of text, and the system prompt is a stretch
of it with a stronger claim on attention rather than a protected region of
memory. Nobody breaks a permission check; they write text persuasive enough to
outrank text that was meant to outrank it.

Then guardrails as one diagram (a check before the model, a check after it, both
ordinary code), the four libraries, the five guard models, the red-teaming
tools, and the agents that run penetration tests. `claude-red` closes it,
because it is offensive security shipped as skills for an agent you already
have, which needs no new software at all.

**Personal Agents** needed a story told properly, since every design decision
in these tools comes out of it. Verified against Wikipedia and the release
notes rather than from memory: a project called Clawd, shipped as Warelay in
November 2025, renamed Moltbot on 27 January 2026 after Anthropic's trademark
complaint, renamed OpenClaw three days later because Moltbot never quite rolled
off the tongue. In the ten seconds between releasing the old GitHub org and X
handle and claiming the new ones, someone took both, and a fake $CLAWD token
briefly touched $16m. The creator left for OpenAI in February and handed the
project to a foundation.

`SOUL.md` is the section that matters: a system prompt on disk, read on every
wake, and writable, so whoever can write it decides who your agent is. That is
the security module's indirect prompt injection with a filename.

The Moltbook study supplied a better number than the draft had. 18.4% of posts
in an agent-only social network carried action-inducing instructions, and the
agents challenged the risky ones more often than the harmless ones without
being told to.

Four images placed, all four verified present in the export as real figures
with captions. The Hermes and OpenClaw pair carries the comparison: one is
drawn as a loop that closes back on the user, the other as a gateway with
everything hanging off it, and that shape difference *is* the argument.

Gate: typecheck clean, 861 unit tests, 51 pages built, 245 browser cases with
exit 0 (six distinct cases flaked and passed on retry, all layout and
navigation ones that flake under load on this machine). Bilingual coverage 11
to 13, leaving only Context Engineering untranslated in Intermediate.
It was the fourth bullet under the guardrail frameworks with the words "is not
a framework but a small classifier" attached to it, which is a sign it was in
the wrong list. Moved to the top of the guard models section, where it is now
the one to start with: the small one at 86M parameters, cheap enough to sit in
front of every single request, before the 8B and 12B models that follow it.

The frameworks list is three items now, and the sentence that used to say
"that last one" states the rule directly instead: a guardrail can be a rule or
it can be a model.

Both languages.
Modules 25 to 29, English, written from scratch over the stubs. Kept short on
purpose: these are survey sheets rather than concept modules, 1,150 to 1,500
words each. Turkish not written yet, since English comes first.

**Agent Frameworks** opens on the honest point that you do not need one, then
says what you actually get: retries, streaming, tool schemas, memory that
survives a restart. The LangChain stack is drawn as the three levels it really
is, because calling all three "LangChain" is what confuses people: LangGraph
the runtime where state is explicit, LangChain the interface to start from,
deepagents the deep agent architecture pre-assembled. Then the other Python
options, the TypeScript ones, and the distinction that costs months if you get
it wrong: a workflow runs a path you wrote, an agent decides its own, and the
agent is not the grown-up version. n8n is where the workflow half lives, and
its MCP server means a coding agent can build those workflows for you, which
makes the two partners rather than rivals.

**Inference Providers** does not repeat the first module's API key. It covers
what changes once you are paying: what a gateway buys beyond one key, when to
go direct for caching and batch discounts, and the fact that price per token
is the least useful number on the pricing page. Prompt caching is the biggest
lever, which is also why the harness comparison found the fastest tool was the
most expensive per success.

**Inference Engines** answers the question the first module deferred: when you
do touch the engine. Three reasons only, then engines by the size of the
problem, then batching as the one mechanism that explains every number, which
is also why benchmarking one request at a time misleads you.

**UI Design** starts from the real symptom, that an agent writes a different
interface every time, and lands on DESIGN.md as the fix. Same move as
AGENTS.md and SOUL.md: tokens and intent, in markdown, at the repository root.
Closes on what these tools still do not give you, which is judgement about
what the screen is for, and accessible output.

**Observability** starts from the fact that the code no longer documents the
behaviour, so the traces do. Then the improvement loop, and the problem nobody
expected: at a hundred thousand traces a day nobody reads them, and analytics
only answers questions you already thought of, so the newest tool is an agent
that clusters them.

Config: the five flip to `status: ready` with minutes and prerequisites, so
`src/app/lokum-modules.css` is regenerated and 19 sheets are drawn instead of
14.

Three more tests were keyed on module number as a proxy for "is a stub", which
stopped being true the moment Ecosystem was written ahead of Expert:

- `derive.test.ts` used `n >= 16` in three places; they now derive the draft
  list from `status`.
- `loader.test.ts` required the ready modules to be a run from 1 with no
  holes. They are 1-14 and 25-29 now. It asserts the substantive rule instead:
  anything calling itself ready is past the 200-word stub threshold, so the
  flag and the file cannot disagree.
- `quickcheck.test.ts` named modules 16, 20, 25, 30 and 32 as drafts. Module
  25 is not one any more, and the case below it already derives the same check
  from `status`.

One renderer fact found in the export and recorded in `CLAUDE.md`: a caption
note only attaches to a markdown image. After a mermaid fence the build writes
the figcaption from the nearest heading, and an italic line stays ordinary
prose. Still worth writing, but do not count `hl-cap-note` to check a diagram
has one.

Gate: typecheck clean, 860 unit tests, 51 pages, 222 browser cases at exit 0.
33 module pages, 19 drawn, hl-rail unchanged at 27, no relative .md href in
the export. hl-trace rose from 29 to 38, which is the five new modules
entering the prerequisite graph.
Brings `merge/main-into-revision` in, which is main's §14 to §17 work
(Supabase-backed accounts and organisations, `/sheets/`, the home screen,
`/profile/`'s drafter block and register, `/team/`, the claim receipt) already
resolved against the curriculum config. `origin/main` had not moved since that
branch was prepared, so the resolutions still applied and this merge itself was
clean: no conflicts, and `manifest.test.ts` auto-merged keeping both main's
`numberWord` cases and the derived language-coverage check.

**Four fixes were missing from that branch, and this commit restores them.** I
staged the resolved files in the trial worktree and then kept editing the
working copies, so `git commit` wrote the index and left the later fixes
behind. Removing the worktree discarded them. The gate I reported as green for
`bd5fb69` was the working tree, not the commit, and `bd5fb69` does not in fact
typecheck. What was lost:

- `tests/unit/route-labels.test.ts`: the `CATEGORIES` fixture sat inside a
  multi-line `import { ... }`, which is a syntax error.
- `src/components/home/Resume.tsx`: main's new home-screen island renders
  `PathStanding` without the drawn-slug set that this branch's `drawnCount`
  requires. Resume is a server component holding `facts`, so it measures the
  set and hands it down (§12.2).
- `tests/unit/components/record-report.test.tsx`: two assertions wrote the
  sheet count into the file as 32. They count the corpus instead.
- `tests/e2e/record-pages.spec.ts`: the subsystem band read "2 of 8 signed
  off" for a subsystem that has held seven modules since the landscape sheet
  was dropped. Both numbers are now derived from the seed and the sheet list.

Gate on the merged tree: typecheck clean, 2,052 unit tests, 58 pages built,
328 browser cases with 1 flaky and exit 0. Corpus untouched and verified: 33
module pages, 19 drawn, 13 bilingual, hl-rail 27, no relative `.md` href in
the export, no numbered filename on disk. Main's routes all present.
**`context/ARCHITECTURE.md`** is new and is the point of this commit: the map of
the repository as a whole, meant to be the context anyone or any agent starts
from. The root `ARCHITECTURE.md` already covers the application in depth, so
this one covers what that cannot: that there are two projects here, where the
line between them sits, the pipeline that turns a markdown file into a page,
exactly what `curriculum.yaml` owns and what is derived from it, and which
generated outputs are committed and which are ignored.

Three claims in the first draft of it were wrong and were corrected by checking
rather than by remembering: the copied images go to `public/course-images/` and
not straight to `out/`, `public/course-images/` is gitignored rather than
committed, and only `lokum-modules.css` is committed (because vitest and
playwright never run `prebuild`).

**`README.md` goes from 2,704 words to about 1,000, and is now for readers.**
What it keeps: the pitch, the seven rules, the structure table, how to use the
series, and a short honest account of the record living in your browser. What
it loses is the app's internals, which now live in the two architecture
documents.

Nothing was deleted outright. The eight notable test checks (contrast, stroke
weights, the copy register, the palette, path honesty, path evidence and the
two corpus gates) were documented **only** in the README, so they moved to
`tests/README.md`, which is where somebody about to touch a test will look.

The structure table also stopped lying: Ecosystem is no longer marked NOT
READY, since five of its six sheets are written, and the remaining three
categories say IN PROGRESS instead. The Intermediate row lists its modules in
the order they are actually in.

`mini-courses/index.md` is mirrored to match, per the rule that the two move
together, and its Local Development section is gone because it documented
**MkDocs**, which this project stopped using when it became a Next.js app.

Two corrections to `CLAUDE.md` and `AGENTS.md`:

- **`index.md` is no longer the published home page**, and the rule said it
  was. Main's §15 work replaced `/` with a home screen built from components,
  and `lib/content/` now reads `index.md` only as a link target. It is a page
  for people browsing `mini-courses/` on GitHub, so keep it in step for that
  reader but do not expect its prose in the export.
- A note not to link outside `mini-courses/` from `index.md`, because an
  internal `.md` link the corpus cannot resolve fails the build.

Also removed the em dash from the two category READMEs that had one, which are
live pages. The 30 remaining in the corpus are all the placeholder line in
unwritten stubs and go when each is written.

Gate: typecheck clean, 2,052 unit tests, 58 pages. Corpus verified: 33 module
pages, 19 drawn, 13 bilingual, no relative `.md` href in the export, no raw
`<img>` in any body.
Both earn their place on content grounds, and both also restore structures the
corpus lost when this sheet was rewritten.

The six guard models were a bullet list describing the same four things about
each one: size, whether it checks input or output, and what makes it worth
picking. That is a table, and it is now one. Sizes are given only where they
were verified; Llama Guard 3 and Granite 4.1 Guardian say "on Ollama" instead
of a number nobody checked.

The checklist is five things to do before shipping an agent: a guardrail either
side, one red-teaming run with the attack success rate written down, an audit of
what the agent reads, an attempt to extract your own system prompt, and the
narrowest credentials per tool. A checklist is the right shape for "what to
actually do", and this module had no such section.

Both languages, structure verified equal.
The full browser suite was failing 29 cases. I had reported it green earlier in
this branch on the strength of a grep whose `tail` cut the "failed" line off,
and a pipeline whose exit code came from `tail` rather than from Playwright.
Both were my error. The suite is now exit 0: 358 passed, 19 skipped, none
failing.

Almost all of it was one cause: specs used the machine-written Intermediate
drafts as their exemplars, and rewriting those sheets moved every measurement.

**`sheets.ts`** listed modules 25 to 29 as `A4`. They ship as `A0` now that the
Ecosystem sheets are written, which took out `module-sheets.spec.ts` and both
`index-sheet.spec.ts` counts. The fixture is a deliberate hand-typed
restatement of what ships, so this is the update it is designed to need.

It also gains `CHECKLIST_ITEMS`, because three record-sheet cases had the
number 8 written into them and the rewritten Security checklist has five.

**`responsive.spec.ts`** named module 10 "the widest table on the site" and
module 13 "the longest sheet, and the most figures". Neither is true any more.
Remeasured: the widest table is module 12 at five columns, and the longest
sheet with the most figures is module 9.

**`accessibility.spec.ts`** wanted more than three tables on the A0 exemplar.
That counted one draft's tables. The rule is that *any* data table of three or
more columns announces its rows, so one is enough for the loop to mean
something.

**`prose-type.spec.ts`** wanted more than ten inline-code table cells on
Security, which now has none. Module 6 is the only sheet in the corpus that
puts inline code in a table cell, so it loads that and asks for one.

**`section-marks.spec.ts`** is the one I did not fix, and deliberately.
Both cases need an `h2` carrying `data-mark`, which is what draws a section
numeral. `data-mark` appears **nowhere** in the export: the old drafts numbered
their sections and the rewrite dropped that style everywhere. There is no page
left to measure the rule on, so both are skipped with the reason written above
them. Bringing them back means either a sheet using numbered sections again or
a served fixture page, and that is a decision about the corpus rather than
something to settle by loosening an assertion.

One correction to myself inside this commit: I first derived the checklist XP as
five per item, which is invented arithmetic. `XP_CHECKLIST` in
`lib/record/derive.ts` is a flat 40 for completing the checklist, so the award
does not move with the item count. The original 40 was right; only "eight
items" was wrong.

Left alone as pre-existing and not content-related: the channel-A first-paint
probes. An `requestAnimationFrame` scheduled at document start does not fire on
the first navigation in a fresh browsing context, so those cases fail when their
file runs alone and pass in the full suite. Verified earlier against a clean
build of `origin/main`, which fails the same way.
…h half

`context/ARCHITECTURE.md` is deleted and the whole `context/` directory with it.
There is one map now, the root `ARCHITECTURE.md`, which already existed and
which I should have updated in the first place instead of writing a second one
beside it.

Folding the repository-level material into it also meant correcting what the
curriculum config had made stale, none of which was noticed while the file sat
unedited:

- "Thirty-two modules" and a table reading 32 sheets and 53 pages. It is 33 and
  58.
- A build diagram drawing `01_llms.md` and `01_llms_tr.md`, filenames that
  stopped existing when the numeric prefixes were removed, and naming
  `schema.ts` as the validator.
- No mention of `mini-courses/curriculum.yaml` anywhere, which is now the file
  that owns the course shape.
- A test count written into the testing table.

Added to it: the config under rule 2, because "content is derived, never
restated" is the rule the config takes to its conclusion; a row in rule 4 for
which file answers a module's number; the two generated outputs and why one is
committed and the other ignored; the rule that a test may never write down a
fact about the content, with the note that the browser suite leaks hardest
there; and the corpus documents in Read next, which had only app documents.

**The root `CLAUDE.md` gains a "Two halves" section at the top**, because
which file governs a change was not written down anywhere. `src/`, `tests/`,
`scripts/` and `supabase/` answer to the root file; `mini-courses/` answers to
`mini-courses/CLAUDE.md`; and `curriculum.yaml` is the one file they meet at, so
it is the only place a change in one half can break the other. They share the
derive-never-restate rule and the measure-do-not-assert habit and diverge on
the rest, since prose, figures and translation do not apply to code.

Worth stating there because it is easy to get wrong: the root `AGENTS.md` is
**not** a copy of the root `CLAUDE.md`. It is only the block `next dev` writes
and re-adds. Inside `mini-courses/`, `AGENTS.md` is a byte-identical copy.

Its content pipeline paragraph and enforced-rules list were stale in the same
way as the architecture file, and are corrected the same way.

`README.md` and `mini-courses/index.md` now point at the one document.

Gate: typecheck clean, 2,052 unit tests, 58 pages, every link in the three
edited documents resolves.
Enhanced the Ecosystem section by expanding the descriptions for agent frameworks, inference providers, inference engines, UI design, and observability. Added specific insights about LangChain, OpenRouter, and various tools, along with relevant links for further reading. This update aims to provide clearer guidance and context for users exploring these topics.
Context Engineering, which had been the one gap in Intermediate since it was
written, and all five Ecosystem sheets. **Every written module is now bilingual:
19 of 19.**

Structure mirrored and checked on all six rather than assumed: heading counts,
figures, mermaid blocks, code fences and table rows all equal their English
sibling. Alt text stays in English, since it is the printed figure label, and
the captions are Turkish. Cross-references point at `_tr.md` siblings, verified
by resolving all 18 of them. No em dashes.

Each Turkish file lands at 72 to 76% of its English word count, which is the
band the earlier translations sat in and is the language rather than missing
content.

Gate: typecheck clean, 2,052 unit tests, 58 pages built, and the export now
prints `EN · TR` on 19 sheets instead of 13.
@amirkiarafiei
amirkiarafiei merged commit d6bfa6c into main Sep 3, 2026
1 check passed
@amirkiarafiei
amirkiarafiei deleted the feat/amirkia-revision branch September 3, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant