chore(memory): skill-catalog prune — contract, plan r4, oracles, TODO, journal

This commit is contained in:
bastien
2026-09-28 11:49:02 +02:00
parent 4c86d6dc70
commit 729d71546f
8 changed files with 590 additions and 0 deletions
@@ -0,0 +1,375 @@
# PLAN — skill-catalog-prune (feat, ad-hoc dispatch) — r4 (after confirmation pass)
- r4 closes the confirmation pass (robustness FATAL(4)): BLOCKER 1 — the
helper lib skips any non-skill dir holding a nested SKILL.md
(browser-skills/, node_modules/, openclaw/) and `.git*`/`node_modules`
by name, fixture `other/deep/SKILL.md`; MAJOR 2 — the three existing
profile/toggle suites copy lib/gstack-removed.sh into their fixture
(E1b scope); MAJOR 3 — the lib removes a stale `dst` symlink (gstack
./setup plants `~/.claude/skills/gstack -> submodule`) and refuses a dst
that resolves inside src, fixture case added; MAJOR 4 — update-all.sh's
third copy of the block goes through the lib too (criterion 18); MINOR
5-8 — real restored count after the skip, policy message instead of the
setup hint, `len(x or '')` + stderr warn on fallback, E2 owns every
install-plugins.sh edit (E3 no longer touches it).
- date: 2026-09-28 | contract: contracts/2026-09-28-skill-catalog-prune-0554.md
- branch: feature/skill-catalog-prune
- executors: 4 feater (sonnet-pinned), parallel, disjoint file sets, same tree
- r3 closes: correctness MAJOR 1-3 / MINOR 4-9, robustness MAJOR 1-4 / MINOR
5-9, simplicity MAJOR 1 / MINOR 2-5. Named changes: whole-class gstack
helper links through one shared lib (E2), `GSTACK_REMOVED` denylist that
`gstack on` / `enable gstack` honor (E1b), `max` = union of every profile
(E1a), live `set full` as an oracle (criterion 16), census test with a
passing baseline then one mutant per invariant (E1a), description parser
reused from lib/skill-routing-census.py (E2), synced info line dropped,
stale r1 wording swept.
- r2: superset = `max`; full keeps the 11 redundant gstack (user rule: full
⊇ every other profile, max = everything); parked = make-pdf, diagram,
21st trio; 21st trio out of full/web/web-full/design; freeze/bin link.
## Ground truth (verified on the live tree, 2026-09-28)
- Catalog: 150 skills, 53.5k chars of descriptions; the harness lists only
~19k chars of them with a description (least-invoked skills lose theirs).
78 skills are name-only in the current session.
- gstack skills are symlinked per profile (BDR-030), `full` is the default
(BDR-101). `profile.sh apply` is ADDITIVE (enables only); `set` and `reset`
park what the profile does not list (`disable_gstack_not_in`,
`disable_externals_not_in`, `disable_plugins_not_in`) then apply. So a
name dropped from full leaves the live tree only at the next `set full`
or `reset`; nothing runs that automatically. `gstack on`
(`enable_all_gstack`) and `lib/toggle-external.sh enable gstack` move
EVERY `skills-disabled/gstack__*` back, with no denylist.
- gstack skills hardcode `~/.claude/skills/gstack/<path>` for shared
assets, but link.sh (and a duplicate block in install-plugins.sh
STEP 2) only create `gstack/bin` and `gstack/browse/dist`. Census of the
hardcoded paths (`grep -rhoE '(~|\$HOME)/\.claude/skills/gstack/[A-Za-z0-9_./-]+'
skills-external/gstack/*/SKILL.md`): bin/* (dozens), scripts/jargon-list.json,
ETHOS.md, browse/bin/remote-slug, browse/dist/browse, design/dist/design,
extension/, lib/diagram-render/dist/diagram-render.html, make-pdf/dist/pdf,
freeze/bin/check-freeze.sh, careful/bin/check-careful.sh, */sections/*.md
(plan-*-review, cso, office-hours, design-consultation, document-release),
review/checklist.md, review/specialists/*.md, and other skills' SKILL.md
(office-hours/SKILL.md, gstack-upgrade/SKILL.md). Everything but `bin` and
`browse/dist` is unreachable today: make-pdf returns MAKE_PDF_NOT_AVAILABLE,
diagram BUNDLE_MISSING, the careful/guard/freeze hooks exit 127 and never
fire (LRN-096 class), cso and plan-*-review cannot read their sections.
- A global `skills/gstack -> skills-external/gstack` symlink is forbidden
(link.sh comment): the top-level gstack SKILL.md then lists as a duplicate
skill. Skill discovery reads `~/.claude/skills/<name>/SKILL.md`; a
`SKILL.md` must therefore never sit at `~/.claude/skills/gstack/SKILL.md`,
and no `<skill>/SKILL.md` is linked below `gstack/` either (only their
non-SKILL.md children), so the helper tree exposes no skill file.
- gstack `ship` resolves its base from `origin/HEAD` on Gitea → main; it
diffs and PRs against main, skipping develop. `land-and-deploy` runs
`gh pr merge --squash --delete-branch` then waits for the deploy.
- security-guidance 2.0.0: SessionStart venv, UserPromptSubmit baseline,
PostToolUse regex, Stop = direct POST /v1/messages (opus-4-7, thinking
10000) on every turn that changed source, commit/push = Agent SDK review
up to 18 turns. Log 2026-09-22→28: 0 findings, 1 recorded false positive
(EVAL-024). `ENABLE_STOP_REVIEW=0` is the plugin's own switch.
- settings.json has NO top-level `env` key today (create it). It is the
live `~/.claude/settings.json`: validate JSON right after the edit.
- doctor.sh: `find -maxdepth 2` without `-L` → 34 skills; `grep
'^description:' | head -1` → block scalars count 0 chars. doctor.sh runs
under `set -euo pipefail`. `lib/skill-routing-census.py` already has a
tested `extract_description()` handling `|`/`>` blocks (hyphenated file
name → load with importlib, not `import`).
- Specialized profiles carry exactly ONE name full lacks: pr-review-toolkit
(audit.profile; plugin deliberately out of full, audit 2026-07-02 #12,
~2.2k tokens when enabled; MANAGED_PLUGINS so `set` toggles it).
- Already done live (user go): brightdata disabled (`false` in
settings.json), frontend-design@claude-plugins-official uninstalled
(entry removed). `lib/gstack-removed.sh` written by the orchestrator:
`GSTACK_REMOVED=(…9…)` + `gstack_is_removed <name>`.
## Approach
### E1a — profiles + census suite + profile docs
Files: lib/profiles/{full,dev,backend,web,web-full,design}.profile,
lib/profiles/max.profile (new), lib/tests/profile-census.test.sh (new),
skills/profile/SKILL.md, README.md, USAGE.md.
1. Remove the 9 `GSTACK_REMOVED` entries (`ship land-and-deploy setup-deploy
autoplan context-save learn careful guard design-shotgun`) from EVERY
profile that lists them (dev, backend, web, web-full, design, full).
Comment lines describing only them go too. Keep `freeze` and `unfreeze`.
2. `full.profile`: additionally remove the 5 parked entries `make-pdf
diagram 21st-ai 21st-ui-explore 21st-ui-review`. The 11 redundant gstack
(plan-ceo/design/devex-review, spec, review, retro, investigate, canary,
qa, open-gstack-browser, setup-browser-cookies) STAY. Rewrite `# DESC:`:
default profile; carries everything every other profile carries (user
rule 2026-09-28, one exception: pr-review-toolkit); no broken or
doctrine-breaking gstack (see lib/gstack-removed.sh); parked tools live
in `max`. The pr-review-toolkit comment block stays.
3. `web`, `web-full`, `design`: also remove the 21st trio lines.
4. New `lib/profiles/max.profile`: header `# DESC: Everything — full + the
parked tools (make-pdf, diagram, 21st-ai/ui-explore/ui-review) +
pr-review-toolkit; switch here when one of them is wanted` and the marker
line `# SUPERSET-OF: full`. Content = new full + the 5 parked names in
their original sections + `pr-review-toolkit plugin@claude-code-plugins`
(the audit.profile line). NEVER a `GSTACK_REMOVED` name. Invariant: max ⊇
union(every profile) − GSTACK_REMOVED.
5. `lib/tests/profile-census.test.sh` (hermetic, `set -u`, `check` helper
and `trap 'rm -rf "$WORK"' EXIT` like lib/tests/skill-routing-census.test.sh):
- Top of file: `source "$ROOT/lib/gstack-removed.sh"` (REMOVED comes
from the single source), `PARKED=(make-pdf diagram 21st-ai
21st-ui-explore 21st-ui-review)`, and `FULL_EXCEPTIONS=(pr-review-toolkit)`
with the reason on comment lines ABOVE the array (names only inside it:
contract criterion 15 parses the parentheses).
- ONE assertion function `census_check <profiles_dir>` that runs an
inline python3 heredoc taking the dir and the three lists as argv.
Entry = first whitespace token of a non-blank line whose first non-blank
char is not `#` (what `read_profile` links). It prints ONE reason code
per violation on stdout — `REMOVED_LISTED:<profile>:<name>`,
`NO_SUPERSET` / `MANY_SUPERSETS`, `SUPERSET_GAP:<name>` (full or a
parked/exception name missing from the `# SUPERSET-OF: full` profile),
`MAX_GAP:<name>` (a name some profile carries that max lacks),
`FULL_GAP:<name>` (a name a non-max profile carries that full lacks,
minus REMOVED, the 21st trio and FULL_EXCEPTIONS) — and returns 0 iff
no violation.
- Live part: `census_check "$ROOT/lib/profiles"` must return 0
(`check T1-live-clean`).
- Fixture part under `mktemp -d`: `baseline/` = a minimal profiles dir
(full.profile with `alpha`, `beta`; qa.profile with `alpha`;
max.profile with the marker + `alpha`, `beta`, `parked-x`; PARKED and
FULL_EXCEPTIONS overridden for the fixture through the argv lists).
The baseline MUST return 0 → print `FIXTURE_BASELINE_OK`. Then three
mutants, each a copy of baseline with ONE change, each MUST return
non-zero AND print its own code: `ship` added to qa.profile →
`REMOVED_LISTED:qa:ship` → `FIXTURE_REMOVED_DETECTED`; `beta` deleted
from max.profile → `SUPERSET_GAP:beta` → `FIXTURE_SUPERSET_DETECTED`;
`gamma` added to qa.profile only → `FULL_GAP:gamma` →
`FIXTURE_FULLGAP_DETECTED`. A detected mutant counts as a PASS of the
test (it is the positive control); a mutant that returns 0 is a FAIL.
- Summary `PASS=n FAIL=m`, rc 1 on any FAIL. Shell helpers ≤ 25 logic
lines; the python heredoc is one function per invariant.
6. Docs: skills/profile/SKILL.md table gains the `max` row ("Everything —
full + parked tools (make-pdf, diagram, 21st generation trio) +
pr-review-toolkit") and full's row reads "Default — everything the other
profiles carry, minus broken or doctrine-breaking gstack"; README line
175 and 357-360 list `max`; README line 179 drops `context-save,
context-restore` from its example list; USAGE line 166 lists `max`.
### E1b — denylist honored by every "gstack back" path
Files: lib/profile.sh, lib/toggle-external.sh, lib/tests/gstack-removed.test.sh (new),
lib/tests/{profile-default,profile-set-managed,toggle-external-repo-resolution}.test.sh
(fixture copy line only).
1. lib/profile.sh: `source "$(dirname "${BASH_SOURCE[0]}")/gstack-removed.sh"`
next to the other top-level definitions. `enable_all_gstack`: skip a
parked entry whose name `gstack_is_removed` (leave it parked, `info
"skipped (removed by policy, lib/gstack-removed.sh): $name"`).
`enable_skill` gstack branch: refuse a removed name with `warn` and
return 0 (a profile listing one is a census failure, not a crash).
2. lib/toggle-external.sh `enable gstack` loop (the `for entry in
"$DISABLED_DIR"/gstack__*` block): same skip, same source line
(`source "$(dirname "$0")/gstack-removed.sh"`; note toggle-external
resolves REPO from `$0`, keep that idiom).
3. `enable_all_gstack` echoes the REAL restored count on stdout (skipped
names excluded); `cmd_gstack on` prints that count, not the pre-computed
`parked_gstack_count` (profile.sh ~651-655). toggle-external.sh: when
the loop skipped ≥ 1 removed name and moved 0, print "only policy-removed
skills remain parked (lib/gstack-removed.sh)" and NOT the "re-run gstack
setup" hint (setup would relink the 9 removed skills).
4. The three existing suites copy only profile.sh / toggle-external.sh into
their fixture `lib/` and would die on the new `source`: add
`cp "$ROOT/lib/gstack-removed.sh" "$FX/lib/"` (same for `$SANDBOX/repo/lib/`)
in lib/tests/profile-default.test.sh (:27), lib/tests/profile-set-managed.test.sh
(:22), lib/tests/toggle-external-repo-resolution.test.sh (:19). No other
change to those suites.
5. `lib/tests/gstack-removed.test.sh`: fixture repo under mktemp (the
`PROFILE_REPO_OVERRIDE` / `TOGGLE_EXTERNAL_REPO_OVERRIDE` harness as
lib/tests/profile-default.test.sh and toggle-external-repo-resolution.test.sh
use it — read them first): `skills-disabled/gstack__ship` and
`gstack__browse` present → `profile.sh gstack on` restores browse only,
ship stays parked, output names the skip; same for `toggle-external.sh
enable gstack`; `gstack_is_removed` positive + negative. `PASS=n FAIL=m`.
### E2 — wiring: shared gstack helper links + doctor catalog stats
Files: lib/gstack-links.sh (new), link.sh, install-plugins.sh (STEP 2 helper
block, STEP 5 comment blocks, summary lines — E2 owns EVERY edit of this
file), update-all.sh (its helper-link block), lib/doctor-skills.sh (new),
doctor.sh, lib/tests/gstack-links.test.sh (new), lib/tests/doctor-skills.test.sh (new).
1. `lib/gstack-links.sh` — `link_gstack_helpers <src> <dst>` (split into
small helpers, each ≤ 25 logic lines, no `set -e`, fallback ok/warn/info
like lib/vendor-skills.sh):
a. Guard: if `$dst` is a symlink → `rm -f "$dst"` + info (gstack ./setup
plants `~/.claude/skills/gstack -> skills-external/gstack` when the
dir is absent; link.sh's old stale-link removal moves here). Then if
`realpath -m "$dst"` is inside `realpath "$src"` → warn, return 1
(never write into the submodule). `mkdir -p "$dst"`.
b. For every top-level entry E of `$src`, skip by name `.git*`,
`node_modules`, `SKILL.md`. If E is a dir holding its own `SKILL.md`
(a gstack skill) → `mkdir -p "$dst/E"` and `ln -sfn` each child of E
except `SKILL.md`. Else if E is a dir and `find -L "$src/E" -name
SKILL.md -print -quit` is non-empty (browser-skills/, openclaw/ …) →
skip with info (would expose a nested skill). Else (file, or a clean
non-skill dir such as bin, scripts, lib, design, extension, ETHOS.md)
→ `ln -sfn "$src/E" "$dst/E"`.
c. Idempotent (`ln -sfn`), removes nothing but the stale dst symlink,
echoes the number of links created THIS run on stdout (callers add it
to CHANGED) and prints one `ok` summary on stderr. Rationale comment:
the census above + why no SKILL.md is ever exposed under `<dst>`.
2. link.sh: replace the stale-global-link removal AND the `bin` /
`browse/dist` blocks (lines ~55-91) with `source "$REPO/lib/gstack-links.sh"`
+ one call `n=$(link_gstack_helpers "$REPO/skills-external/gstack"
"$CLAUDE/skills/gstack")` guarded by `[ -d "$REPO/skills-external/gstack" ]`,
`CHANGED=$((CHANGED + n))`; keep the "submodule not found" warning; the
comment says the helper tree replaces the hand-made links and exposes no
SKILL.md.
3. install-plugins.sh STEP 2, the duplicate `GSTACK_DST` block (lines
~375-390): replace with the same source + call (after ./setup, so the
lib's guard removes the global link setup may have planted).
update-all.sh, its helper-link block (~106-116, `ln -sf` without `-n`
→ would nest `src/bin/bin` on a re-run): same source + call, right after
the submodule update. STEP 5 comment blocks + summary lines: E2 does
them (moved from E3, see E3.2 text below — same wording).
4. `lib/doctor-skills.sh` — `skill_catalog_stats <skills_dir>`: prints
`<count> <desc_chars>` on stdout, ALWAYS exits 0 (prints `0 0` when the
dir is absent or python fails). Implementation: one `python3 -` call that
loads `lib/skill-routing-census.py` via `importlib.util.spec_from_file_location`
(hyphenated name), globs `<dir>/*/SKILL.md` (glob follows symlinks),
sums `len(extract_description(path) or '')` (the function takes a PATH
and returns None when there is no description). On any python failure
print `0 0` to stdout AND one `warn` line to stderr (doctor shows the
failure instead of a healthy-looking zero). No awk parser.
5. doctor.sh token block: replace the loop + `find` with
`read -r SKILL_COUNT SKILL_DESC_CHARS < <(skill_catalog_stats "$HOME/.claude/skills")`
after `source "$REPO/lib/doctor-skills.sh"`. Constants: DELETE the
gstack (2750), context7 (200) and graphifyy (300) lines — their skills
sit in `~/.claude/skills` and are counted by the stats (one comment line
says so); superpowers 800 → 1500 (~900 t session-start injection + ~600 t
of 15 descriptions, measured 2026-09-28); ui-ux-pro-max 400 → 670 (7
descriptions, 2 669 chars, measured 2026-09-28). The "measured ~11.4k
post-audit, LRN-088" note → "(re-measure after a catalog change;
LRN-088)". NO synced-bucket line (dropped: out of the request's scope;
noted as a TODO follow-up by the orchestrator).
6. Tests. `lib/tests/gstack-links.test.sh`: fixture src under mktemp with
`bin/x`, `ETHOS.md`, `SKILL.md`, `browse/{SKILL.md,dist/browse}`,
`review/{SKILL.md,checklist.md,specialists/a.md}`, `.git/HEAD`,
`other/deep/SKILL.md`, `node_modules/pkg/SKILL.md` → after
`link_gstack_helpers`, `dst/bin`, `dst/ETHOS.md`, `dst/browse/dist`,
`dst/review/checklist.md`, `dst/review/specialists` resolve;
`find -L dst -name SKILL.md` is EMPTY (so no `dst/SKILL.md`,
`dst/browse/SKILL.md`, `dst/other`, `dst/node_modules`), `dst/.git`
absent; second run echoes 0 and changes nothing (idempotent); a dst
that is a symlink to src → the symlink is removed, dst becomes a real
dir, and `find src -type l` stays EMPTY (nothing written into src); a
dst path inside src (`src/helpers`) → warn + rc 1, nothing created. `lib/tests/doctor-skills.test.sh`:
fixture skills dir with an inline description, a `|` block scalar, a
`>-` block, a SKILL.md with no description, a symlinked skill dir →
count and chars equal the hand-computed sum; absent dir → `0 0` rc 0.
`PASS=n FAIL=m` summaries.
### E3 — config + doctrine + docs
Files: settings.json, agents/plugin-advisor.md, CLAUDE.global.md,
skills/deploy/SKILL.md, CHANGELOG.md. (install-plugins.sh is E2's: the
E3.2 wording below is what E2 writes there.)
1. settings.json: CREATE the top-level key `"env": {"ENABLE_STOP_REVIEW": "0"}`
(it does not exist), keep the two enabledPlugins states already written
live. Immediately validate: `python3 -c 'import json;json.load(open("settings.json"))'`.
2. [DONE BY E2, wording kept here] install-plugins.sh STEP 5: after the ui-ux-pro-max block, a comment block
"frontend-design@claude-plugins-official — NEVER installed: byte-identical
to the skills-external copy Step 8b syncs from the example-skills cache;
uninstalled 2026-09-28 (skill-catalog prune)" and "brightdata-plugin@synced
— account-synced from claude.ai, kept `false` in settings.json: every skill
needs a Bright Data account and its bright-data-mcp skill orders WebFetch/
WebSearch replaced 'no exceptions' (would hijack /seo /geo /harden)".
Summary line "security-guidance — PreToolUse security hook (0 tokens)" →
"security-guidance — regex hints on Edit/Write + out-of-band LLM reviews
on commit/push (Stop review off via ENABLE_STOP_REVIEW=0; quota, not
context) [claude-code-plugins]". The frontend-design summary line stays
(the managed copy stays).
3. agents/plugin-advisor.md: the `security-guidance ↔ any` row → "Hooks +
out-of-band LLM reviews (agentic review on commit/push; Stop diff review
disabled by ENABLE_STOP_REVIEW=0). No context injection unless a regex
hits."; the "> security-guidance and rtk are ALWAYS ON (0 tokens)" note →
"> rtk is always on at 0 context tokens; security-guidance is always on
and costs quota out of band (LLM reviews), not context — both omitted
from the estimates".
4. CLAUDE.global.md Skill routing: "- Ship / PR → ship (ship-feature if
gstack off); deploy → deploy (runbook, the user runs it)" → "- Ship / PR →
ship-feature (never gstack ship: it takes `origin/HEAD` = main as base
and skips develop); deploy → deploy (runbook, the user runs it)". The
gstack-OFF line lists "(investigate, qa, review, health, retro,
office-hours…)". Design work "Review / audit" line: drop "+ 21st-ui-review"
and add, at the end of the 21st sentence in that section, "21st-ai /
ui-explore / ui-review are `max`-profile only." Keep every line ≤ 80 chars.
5. skills/deploy/SKILL.md table: drop the `/land-and-deploy` and
`/setup-deploy` rows; add one row "Merge a finished branch | `gitflow
finish` on an explicit human signal (skills/gitflow)".
6. CHANGELOG `[Unreleased]`: Removed (brightdata synced plugin disabled,
frontend-design official plugin uninstalled, 9 gstack skills out of every
profile with reasons, `GSTACK_REMOVED` denylist honored by `gstack on` /
`enable gstack`), Changed (full = everything the other profiles carry,
`max` = full + parked + pr-review-toolkit, 21st trio max-only,
security-guidance Stop review off, doctor constants), Fixed (gstack helper
tree: make-pdf, diagram, sections, jargon list, ETHOS, freeze hook now
fires — `/unfreeze` clears `~/.gstack/freeze-dir.txt`; doctor.sh
undercount; "0 tokens" claims; Ship/PR routing), Known residual (kept
gstack skills still carry upstream prose routing to /ship,
/land-and-deploy, /context-save, /autoplan, /design-shotgun; 21st-ui-build
and 21st-cli-use point to the max-only trio — a Skill call on a parked
name fails and the doctrine routing applies).
## Orchestrator steps after the executors
- Criterion 16 runs `bash lib/profile.sh set full` live (parks the 14 names,
re-applies full) and checks none of them resolves under `~/.claude/skills`.
- Post-merge (user): `make link` (helper tree) and `bash lib/profile.sh set
full` on any other machine.
## Edge cases
- A profile line may carry a trailing label column (`personal`, `external`,
`# gstack`); entries match on the first token only.
- `design.profile` `# GATE-BLOCK:` lines name frontend-design, ui-ux-pro-max,
emil-design-eng, design-html, design-motion-principles, design-review,
design-consultation, the 21st CLI, 21st-ui-build: none of the removed or
parked names → no gate edit (grep before editing).
- link.sh / install-plugins.sh run on machines without the gstack
submodule: the call is guarded by `[ -d skills-external/gstack ]`.
- `skill_catalog_stats` never breaks doctor.sh's `set -euo pipefail`: it
always exits 0 and always prints two integers.
- The helper tree never contains a `SKILL.md` at any depth: skill dirs
expose only their non-SKILL.md children, non-skill dirs with a nested
SKILL.md (browser-skills/, node_modules/, openclaw/) are skipped
(asserted by the gstack-links test and criterion 3).
- `~/.claude/skills/gstack` may be a symlink to the submodule (planted by
gstack ./setup on a fresh machine): the lib removes it before linking and
never writes when dst resolves inside src.
- Kept gstack skills still name removed/parked skills in their upstream
prose: documented as a known residual (CHANGELOG), not patched (machine-
owned submodule files).
## Tests
- lib/tests/profile-census.test.sh, gstack-removed.test.sh,
gstack-links.test.sh, doctor-skills.test.sh (new, hermetic; SUITES glob
picks `lib/tests/*.test.sh` up automatically).
- make test suite=lib/tests/doctrine-citers.test.sh (routing text changed);
existing profile-default / profile-set-managed / toggle-external suites
must stay green (profile.sh and toggle-external.sh changed).
- shellcheck link.sh doctor.sh install-plugins.sh update-all.sh lib/*.sh lib/tests/*.test.sh.
- Full `make test` by the orchestrator at the end (2 pre-existing T16a
gitleaks failures are known).
## Disposition (RELATED MEMORY, read-before)
- honors BDR-030 / BDR-101 — gstack via profiles, full default: every drop
is a profile edit; the live tree follows through `set full`.
- honors BDR-025 — GATE-BLOCK single source untouched.
- honors BDR-093 — 21st pack stays installed; its generation/review trio
leaves the four design-bearing profiles and lives in max; BDR amendment
noted in registries.
- honors BDR-023 — close alias untouched.
- honors BDR-080 — investigate stays explicit-only and stays in full/dev/
backend (user rule).
- honors BDR-095 — static deny beats `ask`: careful/guard removal loses no
live protection (their hooks never fired).
- honors LRN-088 — measured before cutting: the gain is routing quality and
no broken 100 KB body invoked, not listing chars.
- honors LRN-022 / BLK-005 — profiles audited with the skill change (census).
- honors LRN-096 — vacuous guard class: helper tree makes the freeze hook
real; careful/guard are removed rather than left vacuous.
- honors BDR-070 — no rival SEO tooling: brightdata seo-audit stays off.
- does NOT touch BDR-104 (MengTo pack) nor the 2026-07-05 frontend-design +
impeccable "both" decision (the managed copy stays).