Files
claude/.claude/tasks/contracts/2026-09-25-default-profile-full-1254.md
T

12 KiB

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).