207 Commits
Author SHA1 Message Date
Bastien Chanot 8d5d154c28 chore(memory): LRN-149 — background_tasks gates the turn-end signal 2026-09-10 03:03:22 +02:00
Bastien Chanot 2c0439a0a8 chore(memory): LRN-148 — pre-flight terminal test; LRN-147 mechanism too narrow 2026-09-03 02:22:24 +02:00
Bastien Chanot a627201bee chore(memory): LRN-147 — restored terminals never instrumented by OSC ext 2026-09-03 02:00:22 +02:00
Bastien Chanot 6aca40a810 chore(memory): BDR-087 + LRN-146 + BLK-020 — capitalize 2026-09-03 00:28:45 +02:00
Bastien Chanot 069a73338a chore(todo): reconcile 2026-09-01 — T4 gate ticked, T6 residuals corrected, Makefile item re-verified open 2026-09-01 16:54:15 +02:00
Bastien Chanot c4e6ef1e2b chore(memory): BLK-019 notify-attention bell silent (VS Code client default) 2026-09-01 16:30:39 +02:00
Bastien Chanot f08c3ab51c chore(memory): LRN-145 terminalSequence pattern + journal 2026-09-01 2026-09-01 15:32:51 +02:00
Bastien Chanot a51a65e1d5 chore(memory): LRN-143 index row — re-escape pipes (sed a-command unescaped them) 2026-08-26 23:01:16 +02:00
Bastien Chanot a15854aa87 chore(memory): EVAL-028 + LRN-143/144 + BDR-086 + journal — darwin run capitalized; TODO round-count corrected 2026-08-26 23:00:41 +02:00
Bastien Chanot 7f457f09fd docs(darwin): result card PNG 2026-08-26 23:00:41 +02:00
Bastien Chanot 12823181d1 chore(darwin): Phase 3 — optimization report + TODO T5/T6 ticked 2026-08-26 22:45:03 +02:00
Bastien Chanot af002ba235 chore(darwin): T3 baseline — 54 rows, mean 83.4, 13 candidates <80 2026-08-26 11:15:03 +02:00
Bastien Chanot afec610dc8 chore(darwin): T2 gate passed — prompts reused, dim8 on candidates, threshold 80 2026-08-26 10:43:47 +02:00
Bastien Chanot a871ce5acb feat(darwin): Phase 0.5 — test-prompts for 7 promptless skills + campaign plan 2026-08-25 20:23:46 +02:00
Bastien Chanot 7f16213456 chore(reconcile): TODO vs real — 3 open-but-done ticked, Makefile rescoped, C1 note corrected
Oracles: merge 5ec7bfa (user-writing-web-rules), BDR-065 Amendment +
LRN-138 in registry bodies, darwin-skill present + T6c green + make
test exit 0, Makefile :31 glob fixed / :57 profiles still 5/10,
seo-geo-deprescription merged 5488c48.
2026-08-25 20:12:40 +02:00
Bastien Chanot dab25636c8 chore(memory): BDR-085 + journal + TODO — user permanent rules integrated 2026-08-25 19:48:06 +02:00
Bastien Chanot b9aa9ba2e6 chore(memory): TODO — T4 verified, merge pending human gate 2026-08-24 14:19:08 +02:00
Bastien Chanot 543b0c811a feat(tour): multi-project parallel fan-out — one runner per repo
Two or more project paths dispatch one general-purpose runner per repo,
all in a single message, instead of processing repos one by one. The
runner inherits the session model — no pin, it carries tour's reflection
(fix decisions, convergence) — and every agent inside keeps its defined
tier (security-auditor sonnet, Phase B opus, doc-syncer sonnet). A dead
or mute runner becomes an explicit RUNNER FAILED summary row; the gated
capitalize offer stays in the main loop, never in a runner.

Bounded LRN-083 derogation recorded in BDR-084: the per-project fix loop
moves into its runner, but nothing a runner decides touches shared state
— independent repos, per-repo chore branches, branches left unmerged for
human review exactly as inline. Mechanics proven before building: nested
probe, 3 sub-agent windows all overlapping, 9.1s vs ~18s sequential.

Census §12: 6 locks, flip-tested. Single-project path unchanged.
2026-08-24 14:18:59 +02:00
Bastien Chanot 4ededc75ab chore(memory): TODO — plan tour-parallel (T1-T4) 2026-08-24 14:17:25 +02:00
Bastien Chanot 763d0022bf chore(memory): TODO — W9 human merge signal + Palier 3 trigger (won't-build-now) 2026-08-24 13:44:01 +02:00
Bastien Chanot 33f5356c82 feat(gates): wire GATE 0 into the four orchestrator skill restatements
The include is authoritative, but feat/bugfix/ship-feature/init-project
each restate the verify loop inline — an orchestrator following the
restatement alone would have skipped the floor. Each now carries the
GATE 0 bullet ahead of GATE 1 (4 new structure locks, flip-tested).
The contract-interview weight table stops promising a hotfix oracle
nothing executes: hotfix runs no floor, the hotfixer runs the suite
itself. CHANGELOG extended with the wiring + the RED result.
2026-08-24 13:36:32 +02:00
Bastien Chanot abb4ea7650 chore(memory): EVAL-027 — contract-gates behavioral RED 16/16 conformant 2026-08-24 13:26:39 +02:00
Bastien Chanot bfac4d4522 chore(memory): BDR-083 + LRN-141/142 + journal + CHANGELOG + TODO W0-W8
BDR-083 records what was taken from unlazy and, more usefully, what was
refused and why. LRN-141: an external skill's machinery encodes its threat
model, not yours — take the invariants, refuse the machinery. LRN-142:
structure locks are fixed-string, so reflowing a doctrine paragraph reds
them; fix the doc, not the lock.
2026-08-24 13:12:38 +02:00
Bastien Chanot 325962e080 chore(memory): BDR-082 + LRN-140 + journal + CHANGELOG + TODO C1 done (seo/geo de-prescription) 2026-08-02 17:28:07 +02:00
Bastien Chanot 9681b468e1 test(census): seo/geo agent⇄dispatcher contract locks (C1 P1, pre-reword)
71 locks, flip-proven (7 scratch mutations → 7 FAILs): judge verdict
grammar, FIX BUNDLE + READY-TO-APPLY sentinel, signals handoff, ALL
STEP headers (interiors included, conf#5), collect report, bundle item
fields parsed by L1 appliers, score labels (BDR-010/LRN-011), scoring
blocks, trajectory, envelope keys. Locks existing state — reword
commits must keep this green.

+ plan v3 (challenged 3 lenses FATAL/FATAL/CONCERNS + 1 confirmation
pass FATAL(9), every BLOCKER closed by a named change, §5bis record)
+ directive-language inventory annex (analyzer report).
2026-08-02 01:10:47 +02:00
Bastien Chanot 7047adfe77 chore(memory): reconcile TODO — opus5 branch merged (709cf9b), add Claude 5 follow-on chantiers C1-C4 2026-07-30 13:41:37 +02:00
Bastien Chanot 550b39043e chore(memory): BDR-081 + LRN-139 + journal + CHANGELOG + plan (opus5 tuning)
Capitalizes the Claude-5-family config recalibration: decision record,
trait-inversion learning (LRN-030 superseded premise, #80988 injections,
no-effort-hold trap), journal line, CHANGELOG Unreleased entries, and the
challenged plan (3 blind Opus 5 lenses, synthesis in §5bis).
2026-07-30 13:02:55 +02:00
Bastien Chanot 78a25aeb5e chore(memory): BDR-065 amendment (auto-purge coded) + LRN-138 + journal
BDR-065 delete-side now automated (lib/gitflow.sh _gitflow_purge_transient).
LRN-138: gitignore != delete for run-time artifacts read from disk — use
commit-during-run + auto-delete at the integration boundary. TODO checked,
journal line.
2026-07-22 15:12:28 +02:00
Bastien Chanot 1ef6e6e694 chore(memory): BDR-080 bug routing inversion + journal line 2026-07-21 01:15:06 +02:00
Bastien Chanot 33f9529b9e chore(memory): BLK-018 classifier-blocked finish span + v1.3.1 journal line 2026-07-21 00:27:39 +02:00
Bastien Chanot 533fcc841e chore(memory): journal — README rebuild session 2026-07-20 22:17:48 +02:00
Bastien Chanot dc90aae9bd Merge chore/purge-transient-docs into develop
# Conflicts:
#	.claude/tasks/TODO.md
2026-07-20 17:07:11 +02:00
Bastien Chanot e75ea79ae6 chore(tasks): reconcile 2026-07-20 — 4 stale claims corrected + pending-gates section
- seo/geo STATUS: H1+C1 were done (url-guard, sitemap verb) and the branch
  merged (92301fe) + shipped v1.2.0 — 'NEXT'/'nothing merged' lines stale
- ctx7 + opus-pin sections: 'NO merge' notes stale (8ee7d19, 17fbe51 both
  shipped v1.2.0)
- f1c9c474 transcript decision moot: auto-rotated (cleanupPeriodDays=7)
- new open items: 2 unmerged branches + Makefile help-text fix
2026-07-20 16:46:42 +02:00
Bastien Chanot 8008d8233c feat(profile): set symmetric on managed externals + MCPs (BDR-079)
- MANAGED_EXTERNALS (emil-design-eng, frontend-design,
  design-motion-principles, impeccable) + MANAGED_MCPS (magic):
  cmd_set now trims both when the profile does not list them —
  design leftovers no longer survive a 'set backend'
- cmd_set refactored to 4 symmetric trim helpers; nothing outside
  the MANAGED_* allowlists is ever auto-toggled (darwin-skill manual)
- enable_skill external: from-source fallback (ln -sf
  skills-external/<name>), mirrors toggle-external.sh
- stale usage() NOTE + SKILL.md updated to the both-ways reality
- hermetic test: 16 checks, fixture repo + fake claude shim (gstack
  on-demand, from-source, park/restore, magic add/remove, non-managed
  untouched); shellcheck + full make test green
2026-07-20 14:47:53 +02:00
Bastien Chanot b3a03fd974 chore(memory): journal — v1.2.0 cut + doc pass 2026-07-20 14:24:39 +02:00
Bastien Chanot b7026e4bda feat(ctx7): coverage extension — fast-libs single source + reminder hook + executor briefs (BDR-078)
- lib/fast-libs.sh: detect/cache-status verbs, JS+Python manifests,
  7-day cache freshness, LC_ALL=C sort — replaces 3 hardcoded lists
  (ship-feature 0c, init-project 5c, onboard 3.5)
- hooks/ctx7-reminder.sh: once-per-session UserPromptSubmit nudge when
  the project carries fast-libs and .ctx7-cache/ is missing/stale
- find-docs: before-writing-code trigger + cache-first rule; dist is
  machine-owned (gitignored) so the durable patch lives in
  install-plugins.sh STEP ctx7 (idempotent, grep-guarded)
- feater/bugfixer briefs: fast-lib docs rule (fresh cache read, else
  2-topic ctx7 fetch, else NOTES cache miss + proceed)
- tests: lib/tests/fast-libs.test.sh (11 checks); shellcheck + full
  make test green (review-guards 5/0)
2026-07-20 10:45:06 +02:00
Bastien Chanot 07253e093c feat(doctrine): W6 — prose sweep + BDR-077 + LRN-137 + plan execution notes
LRN-113 whole-surface sweep: client-handover x2 + commit-change prose
repointed to the two-mode reality; code-cleaner/status historic notes
kept (accurate). Memory: BDR-077 (full architecture), LRN-137
(mode-based re-tiering + fail-safe pin rule), journal. Plan carries
as-built EXECUTION NOTES.
2026-07-19 23:44:24 +02:00
Bastien Chanot 1c2d30dbf0 chore(tasks): model-tiering v2 — analysis + challenged plan v3 + TODO reconcile
Plan challenged by 3 blind lenses + 1 confirmation pass (1 BLOCKER closed
by fable-dispatch spike, 6 MAJORs + 8 MINORs fixed by named changes, 0
deferred). TODO: seo-geo-integrity 'UNMERGED' note was stale (92301fe
already in develop) — corrected.
2026-07-19 19:51:06 +02:00
Bastien Chanot 3eaf31ca09 chore(memory): BDR-076 + journal + TODO — opus-pin dispatched judgment agents 2026-07-19 17:38:58 +02:00
Bastien Chanot 727a41ad71 chore(memory): BDR-075 amendment (hotfix included) + journal — Option B + behavioral smoke 2026-07-18 23:08:53 +02:00
Bastien Chanot 2aa95636ee chore(memory): BDR-074 BDR-075 EVAL-026 LRN-136 — config-protection removal + plan-challenge phase 2026-07-17 22:54:12 +02:00
Bastien Chanot a391be4906 chore(memory): LRN-134 LRN-135 — capitalize 2026-07-17 20:16:50 +02:00
Bastien Chanot 0564afcb3c chore(memory): journal — content_quality shipped, both easy picks done 2026-07-17 19:07:10 +02:00
Bastien Chanot fb0b587240 chore(memory): journal — schema_gen shipped, gap-revisit note 2026-07-17 14:31:26 +02:00
Bastien Chanot f96206ff21 chore(memory): BDR-070..073 LRN-131..133 BLK-017 EVAL-025 — capitalize 2026-07-17 13:46:17 +02:00
Bastien Chanot d6b8edc8ea fix(seo): B1 KILLED — Common Crawl backlinks measured, not assumed
The plan said Common Crawl was the free backlink source and the 70/100 cap
was therefore mandatory. Measured before building, and both premises die.

HEAD against data.commoncrawl.org, live:
  cc-main-2026-feb-mar-apr-domain-edges.txt.gz    17.3 GB   gzipped
  cc-main-2026-feb-mar-apr-domain-ranks.txt.gz     2.3 GB
  cc-main-2026-feb-mar-apr-domain-vertices.txt.gz  879 MB

Finding one domain's inbound links means scanning the edges file end to end,
per audit. That is not slow, it is non-viable — and abusive toward a
nonprofit serving the data free.

Worse, the reference implementation everyone points at
(claude-seo scripts/commoncrawl_graph.py:169) does this:

    max_compressed_bytes = 500 * 1024 * 1024   # 500 MiB safety cap
    if total_downloaded > max_compressed_bytes: break

500 MiB of 17.3 GB is **2.9% of the edges file**, which is sorted by source
ID — so it reads an arbitrary slice of source domains and reports whatever
backlinks happened to be in it, as a backlink profile, capped at "70/100
health". Nothing in the output says 3%. That is a random sample wearing a
measurement's clothes: the exact failure class this branch exists to remove,
and I was one step from copying it.

B2 dies with B1: nothing left to cap.

CONSEQUENCE, and it is the point: I1's narrowed Off-page axis — brand
mentions only, backlinks + authority declared unauditable in §14 — is the
FINAL state, not a placeholder waiting for data. Corrected my own I1 text,
which pointed at Common Crawl as the "nearest free source": that sends a
future reader into a 17 GB dead end. The §14 line now records what was
measured and why no number beats a fabricated one.

Also corrects the B3 note, whose follow-on ("so Common Crawl is the only free
source") was wrong for the same reason. The only free viable backlink source
is Bing's GetUrlLinks — first-party only, never a competitor, and blocked on
the client's Bing account. That raises W2's value; it does not unblock it.

Verified: full suite green, seo-data 144 pass / 0 fail.
2026-07-17 13:17:18 +02:00
Bastien Chanot 7d6aa09faf feat(lib): H1 — url-guard, shell-injection + local-target refusal before curl
Prerequisite for C1, which is why this moved up from AXE 5. Today $DOMAIN is
typed by the operator and interpolated into ~10 curls (seo-analyzer.md:254+,
geo-analyzer.md:248+) — self-inflicted risk. The sitemap crawl changes the
threat model completely: URLs then come from the TARGET'S OWN SERVER, so a
remote file's bytes reach a shell.

The severe hazard is injection, not SSRF. Those curls quote with ", inside
which $ and backtick still execute, and ~/.claude/.env holds
GOOGLE_OAUTH_CLIENT_SECRET + CRUX_API_KEY. A <loc> of
`https://x/$(cat ${HOME}/.claude/.env)` reads the vault into a request. The
test suite asserts exactly that payload is refused.

Code, not prose: a markdown instruction does not stop an injection. Mirrors
the house pattern (fetch.sh:25 _label_safe) — whole-string allowlist, C
locale, POSIX case: newline-proof, locale-independent, no grep pitfall.
Allowlist over denylist per CLAUDE.md.

Covers: shell metacharacters; scheme (http/https only — no file:, gopher:);
literal loopback/private/link-local/metadata/.local; userinfo authority
confusion (https://trusted.com@127.0.0.1/ hits .0.0.1, not trusted.com).

NOT covered, stated in the header rather than left silent: DNS-level SSRF. A
public hostname resolving to a private address passes. Closing it needs
resolve-then-pin at the HTTP layer; shell curl cannot without a TOCTOU
window. Proportionate to the threat model — this runs on a workstation
auditing the operator's own client sites.

Wired at all three entry points: both agents' STEP 4 domain assignment, and
the W3 sameAs loop (whose URLs come from the audited repo, not the operator).
Refused sameAs rows report as REFUSED rather than vanish — neither dead nor
live, and an unguardable sameAs is itself a finding.

Note: writing the test file tripped the config-protection hook (test suite is
a guarded quality-gate). Used the documented one-shot sentinel with a reason
rather than working around the gate; it was consumed as designed.

Verified: 47 new assertions PASS / 0 FAIL, picked up by make test; full suite
green; shellcheck clean on lib/url-guard.sh (the sole remaining hit in the
health-stack glob is pre-existing, lib/gitflow-test.sh:242); guard dogfooded
against the real zenquality.fr domain (accepted) and the real exfil payload
(refused, exit 2).
2026-07-17 09:25:34 +02:00
Bastien Chanot 8b0c98c99a fix(geo): I3 — port NAP direction rule (LRN-032) into geo-analyzer spec
geo-analyzer owns JSON-LD NAP (ownership matrix, seo/SKILL.md:261) and can
rewrite it via G2 — AUTO tier, no confirmation (geo-analyzer.md:660). The
LRN-032 protection lived ONLY in the /seo dispatcher prompt
(seo/SKILL.md:339-343), so standalone /geo reconciled NAP with no canonical
and no anti-seed guard — the exact zenquality trap, writing into client
structured data.

Root cause: a safety invariant that depended on the caller. Fixed at the
layer that owns the data.

- Data integrity: NAP direction rule, caller-independent, binds G2/G6.
  Covers CREATE (LocalBusiness from scratch) not just rewrite — geo builds
  missing schemas, seo-analyzer's wording only covered rewrite.
- STEP 6 checklist: pointer at the line that triggers the action.

Absent canonical is already the safe default (no directional fix), so no
NAP collection step is needed in /geo — that would duplicate seo/SKILL.md
STEP 0 and risk drift.

Verified: make test 25+5+5 GREEN / 0 RED (incl. G3 strict-YAML frontmatter).
2026-07-16 16:06:30 +02:00
Bastien Chanot 56aa3c8a17 chore(memory): BDR-069 + LRN-130 + EVAL-024 — deny-list design pass
- BDR-069: keep broad Edit(**/.env.*), keep .env.example name (option A).
  Rename rejected (~30 refs); glob narrowing rejected (fails open on
  .env.production outside the Next.js convention).
- LRN-130: a deny glob is absolute — allow, `!` negation and PreToolUse
  hooks all fail to exempt it (permissions.md :33/:35/:361, verbatim).
  Only lever = the glob's own shape.
- EVAL-024: the pass shipped one unauthorized weakening (scope inversion +
  framework parochialism) on my own permission boundary, caught by the
  auto-mode classifier rather than self-caught. Reverted pre-commit. Also
  logs a false-positive automated review and a bad subagent glob claim.
2026-07-16 15:02:00 +02:00
Bastien Chanot 83eba36ac7 chore(memory): journal — v1.1.0 cut + v4.0.0 stale-tag watch-item 2026-07-16 14:06:58 +02:00