186 lines
12 KiB
Markdown
186 lines
12 KiB
Markdown
# CONTRACT — default-profile-full
|
|
- date: 2026-09-25 | flow: feat | branch: feature/default-profile-full
|
|
- status: active
|
|
|
|
## REQUEST (verbatim — IMMUTABLE)
|
|
|
|
User message:
|
|
> il faudrait retirer l'API de magic 21st comme on en a plus besoin vu qu'on utilise le cli maintenant. egalement, il faudrait mettre un profil par defaut, quand aucunprofil n'est selectionne il faudrait que ca soit le full qui est actif
|
|
|
|
/feat $ARGUMENTS:
|
|
> Profil par défaut = full : quand aucun profil n'est sélectionné (`.active-profile` absent, vide ou "none", après `reset`), c'est le profil `full` qui est actif (statusline, `profile current`, `gstack off`, `reset`). Même lot : retirer les résidus de l'API Magic 21st (commentaires/docs périmés MAGIC_API_KEY / magic MCP) puisque le CLI `21st` a remplacé le MCP.
|
|
|
|
## CLARIFICATIONS
|
|
|
|
Pass A: none — request complete (outcome, scope and constraints derivable).
|
|
Ground truth found before pass B: the magic MCP wiring is already gone from
|
|
the live code (BDR-093, 2026-09-22): no `mcpServers` entry in `~/.claude.json`,
|
|
no `claude()` wrapper in `~/.bashrc`, `MANAGED_MCPS` empty. What remains is
|
|
prose: stale comments in `lib/profile.sh` (incl. a wrong `usage()` NOTE
|
|
claiming `set` toggles "the magic MCP"), `.env.example`, `.gitleaks.toml`,
|
|
`install-plugins.sh`, `plugins.lock.json`, `lib/tests/profile-set-managed.test.sh`,
|
|
`README.md` — plus ONE live `MAGIC_API_KEY=` line still in `~/.claude/.env`
|
|
(count only; value never read).
|
|
|
|
Pass B [gated 2026-09-25] — 4 questions, all answered:
|
|
Q: `reset` semantics now that `full` is the default?
|
|
A: `reset` = `set full` (exclusive): the 20 gstack skills `full` does not list
|
|
are parked, the 5 design skills of the 21st pack + full's plugins/externals
|
|
are enabled. Label and state coincide (LRN-020).
|
|
Q: Fresh install (`make plugin`) — apply the default profile when none is selected?
|
|
A: Yes. install-plugins.sh ends by applying the default profile
|
|
(`bash lib/profile.sh reset`) when `.active-profile` is absent, empty or
|
|
reads `none`; an existing selection is left alone. Adds install-plugins.sh
|
|
to the executor scope (6 files — /feat cap of 5 exceeded by one 15-line
|
|
guarded block; accepted, not escalated to /ship-feature).
|
|
Q: README depth for the magic-MCP mentions?
|
|
A: One sentence of history: the 21st section is renamed "21st.dev CLI", keeps
|
|
one sentence ("replaces the former magic MCP, same endpoint, no key"),
|
|
drops the MCP-era risk / API-key / bashrc-wrapper paragraphs (that wrapper
|
|
no longer exists in `~/.bashrc`). CHANGELOG untouched.
|
|
Q: Delete the leftover `MAGIC_API_KEY=` line in `~/.claude/.env`?
|
|
A: Yes — done by the orchestrator (targeted `sed -i` on that one line, count
|
|
before 1 / after 0; value never read).
|
|
|
|
## ACCEPTANCE CRITERIA
|
|
|
|
1. `lib/profile.sh` declares the default profile ONCE, `DEFAULT_PROFILE="full"`,
|
|
and `hooks/statusline.sh` derives its fallback from that constant (reads it
|
|
from the lib; a literal `full` may exist there only as the unreadable-lib
|
|
fallback).
|
|
CHECK: grep -q '^DEFAULT_PROFILE="full"' lib/profile.sh && grep -q 'DEFAULT_PROFILE' hooks/statusline.sh && echo CONST_OK
|
|
EXPECT: CONST_OK
|
|
EVIDENCE: MET exit=0 marker-found :: CONST_OK
|
|
|
|
2. [revised after challenge, 2026-09-25] `profile.sh current` is label-driven:
|
|
it names `$(active_profile)` as its FIRST WORD in every state and never
|
|
infers the profile from the parked-gstack count. With `.active-profile`
|
|
absent, empty or `none` the line contains `default — not applied yet`;
|
|
with a cache naming a profile it contains `% match`; with a cache naming
|
|
a profile that has no `.profile` file it contains `unknown profile`.
|
|
Right after `reset` on a clean tree (no gstack linked, nothing parked —
|
|
how a real tree looks under BDR-030) the line contains `100% match` and
|
|
NOT `not applied`. No output claims "all gstack skills enabled".
|
|
(judgement + suite criterion 6, tests T1/T4/T5/T6/T7)
|
|
|
|
3. `profile.sh gstack off` with `.active-profile` absent, empty, or reading
|
|
`none` exits 0 and trims gstack to the default profile's list (no
|
|
"no active profile" error for those three states). A cache naming a profile
|
|
whose file does not exist still errors (rc 1).
|
|
(judgement + suite criterion 6, tests T2/T2b/T3/T4)
|
|
|
|
4. `profile.sh reset` lands on the default profile: it writes `full` to
|
|
`.active-profile`, and the resulting skill/plugin/external state is what
|
|
the gated answer below specifies. The literal `write_active "none"` no
|
|
longer exists; nothing in `lib/profile.sh` writes `none` to the cache.
|
|
CHECK: ! grep -q 'write_active "none"' lib/profile.sh && ! grep -qE 'write_active +none' lib/profile.sh && echo NONE_GONE
|
|
EXPECT: NONE_GONE
|
|
EVIDENCE: MET exit=0 marker-found :: NONE_GONE
|
|
|
|
5. `hooks/statusline.sh` prints `profile: full` when `.active-profile` is
|
|
absent, empty or reads `none`, and `profile: <name>` when the cache names a
|
|
profile.
|
|
(judgement + suite criterion 6, tests T8/T9/T10/T11)
|
|
|
|
6. New hermetic suite `lib/tests/profile-default.test.sh` (fixture repo via
|
|
`PROFILE_REPO_OVERRIDE` + `TOGGLE_EXTERNAL_REPO_OVERRIDE`, fake `claude` on
|
|
PATH, same harness as `profile-set-managed.test.sh`) passes and covers
|
|
criteria 2, 3, 4, 5.
|
|
CHECK: bash lib/tests/profile-default.test.sh 2>&1 | tail -1 | grep -qE '^PASS=[0-9]+ FAIL=0$' && echo DEFAULT_SUITE_OK
|
|
EXPECT: DEFAULT_SUITE_OK
|
|
EVIDENCE: MET exit=0 marker-found :: DEFAULT_SUITE_OK
|
|
|
|
7. The existing managed-set suite stays green.
|
|
CHECK: bash lib/tests/profile-set-managed.test.sh 2>&1 | tail -1 | grep -qE '^PASS=[0-9]+ FAIL=0$' && echo MANAGED_SUITE_OK
|
|
EXPECT: MANAGED_SUITE_OK
|
|
EVIDENCE: MET exit=0 marker-found :: MANAGED_SUITE_OK
|
|
|
|
8. Help and docs in scope describe the new behaviour: `usage()` + the header
|
|
block of `lib/profile.sh`, `skills/profile/SKILL.md` (reset line, output
|
|
policy, failure-mode row about `current` saying `none`), and the Makefile
|
|
`profile-reset` help string name the default profile and no longer describe
|
|
`reset` as "re-enable all gstack skills" nor `current` as returning `none`.
|
|
CHECK: grep -qi 'default profile' skills/profile/SKILL.md && grep -qi 'default' Makefile && ! grep -q 'Re-enable all gstack skills (undo any profile set)' Makefile && ! grep -q '`current` says `none`' skills/profile/SKILL.md && echo DOCS_OK
|
|
EXPECT: DOCS_OK
|
|
EVIDENCE: MET exit=0 marker-found :: DOCS_OK
|
|
|
|
9. Magic 21st residue is gone from the tracked tree outside history and
|
|
registries: no `MAGIC_API_KEY`, `@21st-dev/magic`, or the word `magic` as a
|
|
standalone token (the MCP) in any tracked file except `CHANGELOG.md`,
|
|
`.claude/**`, `lib/tests/fixtures/**` (frozen snapshots) and
|
|
`lib/project-archetypes/**` ("magic numbers" is prose) and, [gated
|
|
2026-09-25] exactly ONE line of `README.md` (the history sentence the
|
|
user chose to keep). Positive control: the same search on `develop`
|
|
still trips.
|
|
CHECK: git grep -qiI -e 'MAGIC_API_KEY' develop -- . ':!CHANGELOG.md' ':!.claude' ':!lib/tests/fixtures' || exit 1; git grep -iIl -e 'MAGIC_API_KEY' -e '@21st-dev/magic' -- . ':!CHANGELOG.md' ':!.claude' ':!lib/tests/fixtures' | grep -q . && exit 1; git grep -iIl -e '[^a-zA-Z]magic[^a-zA-Z-]' -- . ':!CHANGELOG.md' ':!.claude' ':!lib/tests/fixtures' ':!lib/project-archetypes' ':!README.md' | grep -q . && exit 1; [ "$(grep -ci 'magic' README.md)" -eq 1 ] && echo RESIDUE_GONE
|
|
EXPECT: RESIDUE_GONE
|
|
EVIDENCE: MET exit=0 marker-found :: RESIDUE_GONE
|
|
|
|
10. shellcheck clean on every touched shell file.
|
|
CHECK: shellcheck lib/profile.sh hooks/statusline.sh lib/tests/profile-default.test.sh lib/tests/profile-set-managed.test.sh install-plugins.sh lib/toggle-external.sh >/dev/null 2>&1 && echo SHELLCHECK_OK
|
|
EXPECT: SHELLCHECK_OK
|
|
EVIDENCE: MET exit=0 marker-found :: SHELLCHECK_OK
|
|
|
|
11. (judgement) No new dependency. LRN-020 honoured: `full` names the real
|
|
default profile, `reset` applies it and a fresh install applies it when
|
|
nothing is selected, so no label denotes "nothing applied"; the
|
|
parenthetical after the name says whether the profile is applied
|
|
(`% match`) or merely in force (`default — not applied yet`). The
|
|
cross-profile "best guess" scan is replaced by the match of the labelled
|
|
profile only (one profile scored instead of ten).
|
|
|
|
12. [gated 2026-09-25, revised after challenge] `install-plugins.sh` applies
|
|
the default profile at the very end of the install (a Step 11 AFTER the
|
|
Step 10 `link.sh` refresh, BEFORE the Install Summary) by calling
|
|
`bash "$REPO/lib/profile.sh" reset` ONLY when `.active-profile` is
|
|
absent, empty or reads `none`; the call is guarded (`|| warn …`, the
|
|
installer runs under `set -e`) so a failure never hides the Summary; an
|
|
existing selection is re-applied with `bash "$REPO/lib/profile.sh" set
|
|
"$SEL"` (same guard) because Step 2 re-parks gstack and Step 10's
|
|
`link.sh` re-links the design externals on every run — the label never
|
|
changes, its state comes back; a missing `lib/profile.sh`
|
|
warns and skips. Step 8.7 no longer parks the 21st pack unconditionally
|
|
(that block is removed: a re-run must never re-park what the selected
|
|
profile or the user enabled); its comments and the Summary line say the
|
|
design skills follow the profile and the publishing skills stay on
|
|
demand, with no hard-coded counts. The installer itself is NEVER
|
|
executed during this run (side effects: npm, claude plugin) —
|
|
`bash -n` + shellcheck only.
|
|
CHECK: r=$(grep -n 'lib/profile.sh" reset' install-plugins.sh | head -1 | cut -d: -f1); l=$(grep -n 'bash "$REPO/link.sh"' install-plugins.sh | head -1 | cut -d: -f1); s=$(grep -n 'Install Summary' install-plugins.sh | head -1 | cut -d: -f1); [ -n "$r" ] && [ -n "$l" ] && [ -n "$s" ] && [ "$l" -lt "$r" ] && [ "$r" -lt "$s" ] && grep -q 'active-profile' install-plugins.sh && grep -A1 'lib/profile.sh" reset' install-plugins.sh | grep -q '|| *warn' && grep -A1 'lib/profile.sh" set "$SEL"' install-plugins.sh | grep -q '|| *warn' && ! grep -q 'toggle-external.sh" disable 21st' install-plugins.sh && bash -n install-plugins.sh && echo INSTALL_DEFAULT_OK
|
|
EXPECT: INSTALL_DEFAULT_OK
|
|
EVIDENCE: MET exit=0 marker-found :: INSTALL_DEFAULT_OK
|
|
|
|
13. [revised after challenge] The citers of the old `current`/`reset`
|
|
semantics outside the profile files are patched in the same diff:
|
|
`agents/plugin-advisor.md` no longer expects `"custom"` from `current`
|
|
nor states that `reset` leaves plugin state untouched; the
|
|
`lib/toggle-external.sh` header names `reset` as the way back to the
|
|
default profile. `cmd_gstack on` and SKILL.md no longer claim to
|
|
"(re-)enable ALL gstack".
|
|
CHECK: ! grep -q 'or "custom"' agents/plugin-advisor.md && ! grep -q 'Plugin state is NOT touched by reset' agents/plugin-advisor.md && grep -qi 'default profile' lib/toggle-external.sh && ! grep -q 'all gstack skills already enabled' lib/profile.sh && ! grep -q 'all gstack enabled' lib/profile.sh && ! grep -qi 're-enable ALL gstack' skills/profile/SKILL.md && echo CITERS_OK
|
|
EXPECT: CITERS_OK
|
|
EVIDENCE: MET exit=0 marker-found :: CITERS_OK
|
|
|
|
## FILE SCOPE
|
|
|
|
Executor (feater) — the feature:
|
|
- lib/profile.sh (DEFAULT_PROFILE, active_profile(), reset, current, gstack off, usage, header)
|
|
- hooks/statusline.sh (fallback = default profile)
|
|
- lib/tests/profile-default.test.sh (new)
|
|
- skills/profile/SKILL.md (docs)
|
|
- Makefile (profile-reset help string)
|
|
- install-plugins.sh ([gated 2026-09-25] final default-profile step + 8.7 comment + summary line)
|
|
- agents/plugin-advisor.md ([revised after challenge] two citers of `current`/`reset` semantics)
|
|
- lib/toggle-external.sh ([revised after challenge] one header-comment citer of `reset`)
|
|
|
|
Orchestrator, committed BEFORE dispatch as `chore(21st): drop magic MCP residue`
|
|
(prose only, no logic — the executor's diff starts after it):
|
|
- .env.example, .gitleaks.toml, install-plugins.sh (Step 8.7 header comment),
|
|
plugins.lock.json (21st note), lib/profile.sh (comments + usage NOTE),
|
|
lib/tests/profile-set-managed.test.sh (header comment), README.md
|
|
(21st section + MCP-secret section wording).
|
|
|
|
Out of scope: CHANGELOG.md history, `.claude/**` registries (append-only),
|
|
`lib/tests/fixtures/**` snapshots, `lib/profiles/*.profile` contents, the
|
|
gstack submodule, `~/.claude/.env` (user's file — gated question).
|