Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .claude/skills/blog-next/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ reviewer verdict), a rebuilt plan section, or a HOLD.
practitioners disagree, the strongest counter-argument. Not "summarize this."
4. **Mine our real code** (this repo, `~/dev/elital`) for first-hand material.
Sanitize: shapes and lessons yes; prompts, model IDs, proprietary numbers no.
5. Verify every statistic against its source. Zero fabricated clients, stats,
5. Verify every statistic against its source - **and every mechanism too.** How
a tool behaves is a claim, not context; it just reads as reasoning, so it
gets waved through where a number would be challenged. Fetch the README or
the release notes. Zero fabricated clients, stats,
quotes or personas - `.okf/content/claims-canon.md` records "Sarah" as banned.
6. Internal links per `blog-pipeline.md` STEP 3b.

Expand Down
43 changes: 42 additions & 1 deletion .claude/skills/blog-write/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,36 @@ correction lands in one place:**
- `docs/90-99-content-strategy/strategy-analysis/90.11-voice-guide.md` - voice.
- `docs/90-99-content-strategy/strategy-analysis/90.10-icp-primary-website-target.md` - the reader.
- `.okf/content/claims-canon.md` - what you are allowed to say. A number with no
in-repo source is a defect, not a detail.
in-repo source is a defect, not a detail, and so is a mechanism.

## The claim rule that catches the errors gates cannot

Fetch the source before you assert how something works. Not after, and not from
memory - training memory returns a confident sentence whether or not it is true,
which is the failure it cannot warn you about.

Reach for the instrument that matches the claim: **`context7`** for how a
library behaves (it reads the project's own docs and names real classes and
methods), **`WebSearch` then `WebFetch` the primary source** for a study or
statistic - press coverage of a study is not the study - and **NotebookLM** when
you need to interrogate a body of sources. `blog-pipeline.md` STEP 4f has the
full routing.

**Search for the rebuttal, not the confirmation.** Query for criticism and
failed replications of what you are about to assert. Searching for support
returns support; that is what it is for.

**This applies hardest when you are EDITING.** Deleting a bad claim and writing
a replacement feels like cleanup, so the replacement gets less scrutiny than
anything you write from scratch - and it is a fresh assertion carrying the same
burden. Claims-canon records the case: the same author wrote the same Propshaft
sentence twice on one day, wrong when it was framed as a fix, right when it was
framed as writing, because only the second one triggered "go read the README".

The mechanical gates cannot help you here. A ratchet counts invented SHAPES; a
wrong explanation has no shape. If a sentence says how something behaves and you
did not open its documentation in this session, either open it or soften the
sentence until it stops being a claim.

## What you need before drafting

Expand Down Expand Up @@ -69,6 +98,8 @@ paraphrase it from memory - open it.
The gates that most often get skipped, named here so they are not:

- **STEP 4e self-critique** (`reflexion-reflect`) before the critic panel.
- **STEP 4f claim sourcing** - name the URL you opened for every claim about how
an external thing behaves. No gate covers this one.
- **STEP 5a anti-AI pass** before the review loop, not after.
- **STEP 5b slop gate: `slop >= 8/10`.** This is the blog scale, 0-10, higher is
better. The course's `Slop <= 25` is a different scale in the other direction -
Expand Down Expand Up @@ -98,6 +129,16 @@ criteria scored, and the scores written into the commit message.

Feature branch, commit, `gh pr create` with the evidence. Never push to master.

**Do NOT wait for CI on a content-only PR** (Paul 2026-08-22). Prose and
frontmatter cannot move the app build, so local gates decide and you merge as
soon as they are green.

One thing does not come free: `content/**` triggers the link checker, and
internal links are exactly what a post adds. Run **`bin/rake test:links`**
locally in place of the wait. If you skip it, say so in the handback - the
master push-run still crawls, so a broken link becomes a fix-forward defect
rather than a caught one.

**End the handback with the local review link** - `http://localhost:<port>/blog/<slug>/`.
One dev server per session, never 1313:

Expand Down
17 changes: 17 additions & 0 deletions .okf/build/ci-gates.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,23 @@ A CSS/test/docs-only PR does not trigger it. Non-PR events carry no `paths` filt

Caveat: with native `paths`, a filtered-out PR reports NO check (not a passing one). Fine unless `link_check` becomes a *required* status check - then add a skip-reporting companion job.

## Content PRs do not wait for CI - but they DO owe the link check (2026-08-22)

Docs/instruction/tooling PRs merge on local gates because no CI fires on them.
Blog CONTENT was folded into the same no-wait rule by Paul on 2026-08-22, and
the two cases are NOT symmetric: `content/**` is a trigger path above, so a
content PR does fire this workflow, and internal links are precisely what a new
post adds.

The rule therefore carries an obligation rather than a blanket exemption: run
**`bin/rake test:links`** locally in place of the wait - it is the same task the
job runs, over a production build. Skipping both is allowed only when stated out
loud, because the `push: branches: [master]` trigger still crawls after merge,
which converts a pre-merge catch into a fix-forward defect.

Reading "content-only merges on local gates" as "content-only has no link gate"
is the misreading to guard against; the gate moved from CI to your terminal.

# Visual regression is NOT a CI gate today - but the historical blocker is gone

A CI screenshot job (`quick_test` + `bin/qtest`) was built and removed in PR #386. At the time the divergence was unfixable: baselines were captured on the then-Alpine/musl docker image while CI runs Ubuntu (glibc), and text rendered differently enough that measured divergence ran **3-28%** (mobile code blocks 0.28, plain content pages up to 0.21) - far above any tolerance that still catches a real regression.
Expand Down
43 changes: 43 additions & 0 deletions .okf/content/claims-canon.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,49 @@ hardcoded its own copy instead of reading the param.
shipped in structured data on ~1,147 URLs for years with nothing backing it; the
real figure was 9. Before publishing a number, name where it comes from.

**A MECHANISM is a claim too, and it is the more dangerous kind.** A number
announces itself as something to check. An explanation of how a thing works
reads as reasoning, so it gets waved through - which makes a wrong mechanism
outlive a wrong number. Verify it against the primary source (the README, the
release notes, the code) exactly as you would a figure, and prefer linking that
source over asserting the mechanism unlinked.

**A REPLACEMENT IS A NEW CLAIM (2026-08-22).** When you delete an unsourced
claim, whatever you write in its place is not a fix - it is a fresh assertion,
and it needs the same source you just demanded of the thing you removed. This is
the failure mode de-fabrication has all of its own: the hole gets filled with
less scrutiny than the claim that was in it, because the edit feels like
cleanup rather than authorship.

Demonstrated the same day, by the same author, on the same sentence. An agent
removed an unsourced Propshaft timing figure and wrote that precompilation
"stops being a build step that scales with your asset count" - false; every
asset is still walked, fingerprinted and copied. Hours later the same claim went
into a blog post, and *that* time the Propshaft README was fetched first and the
sentence came out right. Same fact, same author, same day: sourced when it was
framed as writing, unsourced when it was framed as fixing.

**Use an instrument, and point it at the rebuttal.** `context7` for how a
library behaves; `WebSearch` then `WebFetch` the PRIMARY source for a study
(press coverage of a study is not the study, and the caveats travel with the
number); NotebookLM for a body of sources. Then query for the criticism and the
failed replications rather than the supporting case - searching for support
returns support. Doing that on 2026-08-22 surfaced a randomised trial that
complicated the claim being written and improved it: METR measured experienced
developers 19% SLOWER with AI while they estimated themselves 20% faster, a
~40-point gap between measurement and belief among experts working on their own
code.

**Correctness is not greppable, and no ratchet here claims otherwise.** Two
candidate markers for wrong-mechanism claims were measured and rejected on
2026-08-22 - an anonymous-company pattern (10 hits, mostly legitimate: "we
measured a mid-size Rails API app") and an absolute-claim pattern (185 hits,
almost entirely ordinary prose: "remote work is no longer a perk"). A gate
noisier than the defect it guards is worse than no gate. What catches a wrong
mechanism is a reader with domain knowledge and a brief that makes disagreement
their job - see [fabrication-ratchet](/content/fabrication-ratchet.md) for what
the mechanical gate does and does not cover.

**Prefer a linked claim over a bigger unlinked one.** "4.8/5 on Clutch" with the
profile linked beats "4.8/5 by 32 clients" unlinked. The link is the proof, and
it cannot go stale the way a transcribed count does.
Expand Down
19 changes: 19 additions & 0 deletions .okf/content/fabrication-ratchet.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ recognise a fabrication - it only has to notice the count going up.
authors. That is a TEST-scoping call and NOT editorial absolution - those
posts are still published on our domain.

# What this gate does NOT catch

It counts INVENTED shapes. It cannot tell you whether a claim is TRUE.

A wrong mechanism has no shape - it is a well-formed sentence using the right
vocabulary, and it usually points in roughly the right direction while getting
the reason wrong. Two candidate markers were measured and rejected on
2026-08-22: an anonymous-company pattern returned 10 hits mostly legitimate
("we measured a mid-size Rails API app (60 controllers...)"), and an
absolute-claim pattern returned 185 hits that were almost entirely ordinary
prose ("remote work is no longer a perk", "eliminates Rails boot time"). Either
one would cry wolf until people stopped reading the output.

So a green run means "no invented-client shapes", not "the post is accurate".
Correctness is caught by a reader with domain knowledge and a brief that makes
disagreement their job - and by the sourcing rules in
[claims-canon](/content/claims-canon.md), especially **a replacement is a new
claim**, which is the rule this gate's own creation violated.

# Prioritise by impressions, never by indignation

When clearing survivors, rank by live GSC impressions. The first sweep
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Session start: always read `@docs/workflows/flow-router.md` (rules live in `CLAU

**Async-first communication (SOP for ALL agents):** written, discoverable artifacts are the default for every decision, finding, status change, and handoff — sync interaction is the exception and its outcome gets written back same-day. Full rules + canonical surfaces table: `.agents/skills/async-first-communication/SKILL.md`. A task is not done until its state is readable asynchronously. **Cold-start surface: `STATUS.md` at repo root** (goals + WIP + blockers, links only) — a session that changed what's in flight updates it in the same commit/PR, or the session is not done.

**Four eyes on every stage, author != verifier (SOP for ALL agents):** the point is agents collaborating to find the TRUTH — a single agent cannot find its own blind spot, because it checks the thing it MEANT to build. Every change is **written by one sub-agent and verified by a different one**; the author never produces the evidence for its own claim. The review lands before the artifact leaves the workshop: a plan is peer-reviewed BEFORE the user is asked, a diff BEFORE commit, a finding is reproduced before it is reported, a measurement re-derived before it is quoted. Brief reviewers with the goal and the artifact, never your conclusions; ask for measurements, not verdicts. Routing: internal sub-agents for every per-stage review, `/codex:review` ONCE at the final verify before merge for USER-FACING changes (it is slow — never in the inner loop); docs-only and instruction-layer changes ship on internal review + local gates — no CI fires on them (path filters) and none is waited for (Paul 2026-08-22); findings apply as a follow-up. No reviewer tooling in this runtime? Fall back agent → external → peer session → human, and mark the change UNREVIEWED if none is reachable. Full protocol: the `jt-delivery:contract` skill (plugin `jt-delivery@jetthoughts`); repo bindings: `docs/workflows/autonomous-delivery-prompt.md`.
**Four eyes on every stage, author != verifier (SOP for ALL agents):** the point is agents collaborating to find the TRUTH — a single agent cannot find its own blind spot, because it checks the thing it MEANT to build. Every change is **written by one sub-agent and verified by a different one**; the author never produces the evidence for its own claim. The review lands before the artifact leaves the workshop: a plan is peer-reviewed BEFORE the user is asked, a diff BEFORE commit, a finding is reproduced before it is reported, a measurement re-derived before it is quoted. Brief reviewers with the goal and the artifact, never your conclusions; ask for measurements, not verdicts. Routing: internal sub-agents for every per-stage review, `/codex:review` ONCE at the final verify before merge for USER-FACING changes (it is slow — never in the inner loop); docs-only and instruction-layer changes ship on internal review + local gates — no CI fires on them (path filters) and none is waited for (Paul 2026-08-22); findings apply as a follow-up. **Blog CONTENT merges the same way** (Paul 2026-08-22) — but unlike docs, `content/**` DOES fire the link checker, so run `bin/rake test:links` locally in place of the wait, or state that you did not. No reviewer tooling in this runtime? Fall back agent → external → peer session → human, and mark the change UNREVIEWED if none is reachable. Full protocol: the `jt-delivery:contract` skill (plugin `jt-delivery@jetthoughts`); repo bindings: `docs/workflows/autonomous-delivery-prompt.md`.

**Session memory (ruflo-first, SOP for ALL agents):** search ruflo memory (`mcp__ruflo__memory_search`, MCP path) before non-obvious decisions; store decisions/corrections via `mcp__ruflo__memory_store` as they land. Details + prefix conventions: `CLAUDE.md` §Memory.

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Operational knowledge lives in `.okf/` (`build/`, `content/`, `design/`, `workfl
- **Session-random dev-server port (Paul 2026-08-17)**: `PORT=$((20000 + RANDOM % 20000)) bin/dev`, once per session; never assume 1313; never kill a server you didn't start.
- **Reflection trigger**: Paul reports "code is bad" / "over-engineered" → HALT, 5-Why, fix config, then proceed.
- **Self-critique on voice-sensitive content (BLOCKING)**: any LinkedIn/blog/marketing draft runs `reflexion-reflect` BEFORE first handback; after pushback on the same draft, escalate to `reflexion-critique`. Solo iteration misses pattern-level tells (5 corrections on a 0/10-scored post, 2026-05-11).
- **Content-only changes SKIP the visual suites (Paul 2026-07-31)**: markdown prose/frontmatter only (no themes/layouts/CSS/body HTML) → `bin/hugo-build` + rendered scroll gate; NOT qtest/test/dtest. The moment the diff touches a template/stylesheet/body HTML, the visual gate applies in full — check the actual diff, not the intent. Docs/instruction-only PRs also skip the app-build CI (publish.yml paths-ignore, 2026-08-21). **Docs/instruction/tooling-only PRs merge on local gates — do NOT wait for CI** (Paul 2026-08-22, on #575): this includes non-build scripts (e.g. `bin/agent-bootstrap`) that cannot change the built site; CI polling before merge is only for diffs that can alter the app build.
- **Content-only changes SKIP the visual suites (Paul 2026-07-31)**: markdown prose/frontmatter only (no themes/layouts/CSS/body HTML) → `bin/hugo-build` + rendered scroll gate; NOT qtest/test/dtest. The moment the diff touches a template/stylesheet/body HTML, the visual gate applies in full — check the actual diff, not the intent. Docs/instruction-only PRs also skip the app-build CI (publish.yml paths-ignore, 2026-08-21). **Docs/instruction/tooling-only PRs merge on local gates — do NOT wait for CI** (Paul 2026-08-22, on #575): this includes non-build scripts (e.g. `bin/agent-bootstrap`) that cannot change the built site; CI polling before merge is only for diffs that can alter the app build. **Extended to BLOG CONTENT (Paul 2026-08-22)**: a prose/frontmatter-only diff under `content/` merges on local gates too — no CI wait. One caveat that is not optional: `content/**` DOES trigger the link checker (`.github/workflows/link-check.yml` paths), and internal links are exactly what a post adds, so run **`bin/rake test:links`** locally before merging instead. If you skip it, say so — the master push-run still crawls, and a broken internal link is then a fix-forward defect rather than a caught one.
- **Visual regression gate (BLOCKING for HTML/CSS/template changes)**: `bin/qtest --changed` before EVERY commit; the full `bin/test` + `bin/dtest` pair ONCE at PR prep — a PR must never open without the dtest leg (CI is Linux). Failures are commit blockers: fix, or confirm intentional AND update both `macos/` + `linux/` baselines in the same commit. Baselines compare against git HEAD, not the working tree — identical difference_level across runs means a stale committed baseline (`md5 -q` the file to confirm), not flaky rendering. Full procedure incl. Linux re-record: `.okf/build/test-gates.md`. (Evidence: the 2026-04-30 35-failure incident — 15 commits with no visual gate; the suite caught a real `.fl-button` regression.)
- **Feature-branch + PR for ALL changes (BLOCKING — Paul 2026-08-17)**: never push to master, any change type. Branch → related commits → gates green → `gh pr create` (body: summary + per-commit description + visual evidence). **One sprint = ONE PR** (Paul 2026-04-30), **capped at ~500 changed lines of CODE** (Paul 2026-08-21; docs, `.okf/` logs, binaries/baselines don't count — docs-only may batch bigger); an oversized code sprint splits into sequential PRs, merge N before opening N+1. `gh pr merge --auto` does NOT queue here — it merges immediately; poll `gh pr checks` and merge without it.
**Master moved → REBASE, never merge it in** (Paul 2026-08-20): tag a backup ref, `git rebase origin/master` (`--autostash` if unstaged changes block; never a bare `git stash`), `git push --force-with-lease`. Branch cut from a since-squash-merged branch replays its own merged commits — don't fight it: `git reset --hard origin/master` + cherry-pick your commits; always cut from `origin/master`. `.okf/log.md` conflicts on essentially every parallel PR — resolution is near-always keep-both.
Expand Down
Loading
Loading