feat(effort): entry level on every skill next to its model pin (BDR-108)
- lib/effort-pins.txt (map) + lib/effort-pins.sh (idempotent re-apply) replace the hardcoded brainstorming/writing-plans loop; called after the last vendoring step of install-plugins.sh AND update-all.sh (the resync dropped the pins until the next make plugin) - design stack high uniform (last loaded wins), superpowers, agent-skills, 21st pack pinned from the map; skills-perso low, pdf-translate medium, site-motion high - doctrine: design stack loads paired with the first Read; one level per stack (CLAUDE.global.md, lib/effort-shift.md) - lib/effort-audit.py prints thinking coverage per scope (sub-agent records carry no thinking count on ~94 % of requests) - census map-driven + fixture suite lib/tests/effort-pins.test.sh; docs README/USAGE/CHANGELOG; contract + TODO plan
This commit is contained in:
@@ -1,5 +1,24 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
## 2026-09-29 — effort round: every skill carries a level next to its model pin (feature/effort-round)
|
||||||
|
User table: low fix-a-line/run-a-script · medium day-to-day · high refactor/resisting bug ·
|
||||||
|
xhigh architecture/audit before validation · max stuck. Approved 2026-09-29: design stack
|
||||||
|
high uniform, hotfix stays high, all vendored externals of the table, docs in the same branch.
|
||||||
|
Model pins stay aliases (latest of each tier is also the cheapest or same price); the
|
||||||
|
quality/price trade-off is tier × effort, never version.
|
||||||
|
- [ ] S1 `lib/effort-pins.txt` (map) + `lib/effort-pins.sh` (idempotent re-apply) replacing the
|
||||||
|
hardcoded brainstorming/writing-plans loop; called after the last vendoring step of
|
||||||
|
install-plugins.sh AND update-all.sh (resync dropped the pins until the next make plugin)
|
||||||
|
- [ ] S2 repo skills: skills-perso low, pdf-translate medium, site-motion high
|
||||||
|
- [ ] S3 tests: `lib/tests/effort-pins.test.sh` (fixture: insert, keep, replace, skip, reject)
|
||||||
|
+ effort-routing census map-driven + design-stack uniformity lock
|
||||||
|
- [ ] S4 `lib/effort-audit.py`: count records without output_tokens_details, print coverage
|
||||||
|
(sub-agent thinking was read as 0 on ~90 % of records: a gap, not a finding)
|
||||||
|
- [ ] S5 doctrine: Design work paired load + one level per stack (CLAUDE.global.md, lib/effort-shift.md)
|
||||||
|
- [ ] S6 docs: README effort section, USAGE niveau d'effort, CHANGELOG
|
||||||
|
- [ ] S7 contract + GATE 0 + fresh verifier + security gate, make test, shellcheck
|
||||||
|
- [ ] S8 registries (BDR-108, LRN, BLK resync, EVAL correction) after user approval; journal
|
||||||
|
|
||||||
## 2026-09-28 — effort tiering: session high, agent pins, skill levels, phase shifts (feature/effort-tiering)
|
## 2026-09-28 — effort tiering: session high, agent pins, skill levels, phase shifts (feature/effort-tiering)
|
||||||
Spec `docs/superpowers/specs/2026-09-28-effort-tiering-design.md`, plan
|
Spec `docs/superpowers/specs/2026-09-28-effort-tiering-design.md`, plan
|
||||||
`docs/superpowers/plans/2026-09-28-effort-tiering.md`. Approved 2026-09-28: session
|
`docs/superpowers/plans/2026-09-28-effort-tiering.md`. Approved 2026-09-28: session
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# CONTRACT — effort-round
|
||||||
|
- date: 2026-09-29 | flow: feat by hand (feature/* off develop) | branch: feature/effort-round
|
||||||
|
- status: active
|
||||||
|
|
||||||
|
## REQUEST (verbatim — IMMUTABLE)
|
||||||
|
> en se basant sur le meme tableau que la derniere fois [low: corriger une ligne, renommer un fichier, lancer un script · medium: le travail courant · high: un refactor, un bug qui resiste · xhigh: architecture, audit avant validation · max: quand une erreur coince, une erreur ne se rattrape pas, ou qu'on juge avoir besoin de beaucoup de reflexion], quand on a pin les orchestrateurs et leur sous agent a des efforts, j'aimerais que tu fasse une ronde de tout les skill et que tu mete un niveau d'effort en plus du model pin. D'ailleurs les model pin, c'est du par exemple Sonnet ou du Sonnet 5.5 (version du model pinned) ? Car il faudrait utiliser les versions qui vont bien avec la tache qu'ils ont a acomplir.
|
||||||
|
> [answered: model pins stay tier aliases; the latest version of a tier is also the cheapest or same-priced, the quality/price trade-off is tier × effort]
|
||||||
|
|
||||||
|
## CLARIFICATIONS
|
||||||
|
- User choices 2026-09-29 (AskUserQuestion): design stack high uniform; hotfix stays high; every vendored external of the proposed table gets a pin; README/USAGE docs in the same branch.
|
||||||
|
- Round result: 30 existing entry levels hold against the table; 3 repo skills had none (skills-perso low, pdf-translate medium, site-motion high); vendored externals get theirs from `lib/effort-pins.txt` re-applied by `lib/effort-pins.sh`; impeccable, graphify, find-docs, gstack, darwin-skill and the five shifters stay unpinned (machine-owned, BDR-107).
|
||||||
|
- Defect found in passing, fixed here: `update-all.sh` re-fetched the vendored skills but never re-applied the pins (lost until the next `make plugin`).
|
||||||
|
- Defect found in passing, surfaced not fixed: ~94 % of sub-agent usage records carry no `output_tokens_details`, so `lib/effort-audit.py` read zero thinking on sub-agents; the script now prints coverage and a CAVEAT; EVAL-037's "executors stay cheap" is a measurement gap (registry correction pending user approval).
|
||||||
|
- Frontmatter placement of the inserted `effort:` line (after `name:`, else before the closing `---`) has no harness effect; locked by the fixture suite only.
|
||||||
|
|
||||||
|
## ACCEPTANCE CRITERIA
|
||||||
|
1. Map + helper: `lib/effort-pins.sh` inserts, keeps, replaces (frontmatter only), skips a missing skill, is idempotent, rejects a bad level / traversal name / three-field line before writing, parses the real map.
|
||||||
|
CHECK: out=$(make test suite=lib/tests/effort-pins.test.sh 2>&1); echo "$out" | grep -q 'effort-pins: [0-9]* pass, 0 fail' || { echo "$out" | grep FAIL; exit 1; }; echo PINS_GREEN
|
||||||
|
EXPECT: PINS_GREEN
|
||||||
|
EVIDENCE: MET exit=0 marker-found :: PINS_GREEN
|
||||||
|
2. Census: the effort-routing suite is green and locks the three new repo levels, the map-driven vendored check, the design-stack single level, both re-apply call sites and the doctrine pointer.
|
||||||
|
CHECK: out=$(make test suite=lib/tests/effort-routing.test.sh 2>&1); echo "$out" | grep -q 'census: [0-9]* pass, 0 fail' || { echo "$out" | grep FAIL; exit 1; }; for k in skills-perso pdf-translate site-motion effort-pins.txt 'stack_levels' 'apply_effort_pins'; do grep -q "$k" lib/tests/effort-routing.test.sh || { echo "census lacks $k"; exit 1; }; done; echo CENSUS_GREEN
|
||||||
|
EXPECT: CENSUS_GREEN
|
||||||
|
EVIDENCE: MET exit=0 marker-found :: CENSUS_GREEN
|
||||||
|
3. Re-apply wired after the LAST vendoring step of both scripts, hardcoded loop gone: in install-plugins.sh the call follows the 21st pack staging block; in update-all.sh it follows the superpowers refresh.
|
||||||
|
CHECK: a=$(grep -n 'apply_effort_pins "$REPO"' install-plugins.sh | cut -d: -f1); b=$(grep -n 'rm -rf "$TFD_STAGE"' install-plugins.sh | tail -1 | cut -d: -f1); c=$(grep -n 'apply_effort_pins "$REPO"' update-all.sh | cut -d: -f1); d=$(grep -n 'vendor_pinned_skills superpowers refresh' update-all.sh | cut -d: -f1); [ "$(echo "$a" | wc -l)" -eq 1 ] && [ "$a" -gt "$b" ] && [ "$(echo "$c" | wc -l)" -eq 1 ] && [ "$c" -gt "$d" ] && ! grep -q 'for _s in brainstorming writing-plans' install-plugins.sh && bash -n install-plugins.sh && bash -n update-all.sh && echo RESYNC_OK
|
||||||
|
EXPECT: RESYNC_OK
|
||||||
|
EVIDENCE: MET exit=0 marker-found :: RESYNC_OK
|
||||||
|
4. Live tree: every map entry whose skill is vendored on this machine carries that level in its frontmatter (idempotent re-run applies 0).
|
||||||
|
CHECK: out=$(bash lib/effort-pins.sh 2>&1) && echo "$out" | grep -q ' 0 applied, [0-9]* already at level' && echo LIVE_AT_LEVEL
|
||||||
|
EXPECT: LIVE_AT_LEVEL
|
||||||
|
EVIDENCE: MET exit=0 marker-found :: LIVE_AT_LEVEL
|
||||||
|
5. Audit script: compiles, runs on a fixture with two records lacking `output_tokens_details` and one carrying it, reports 33 % coverage for that scope and the CAVEAT line (below 50 %).
|
||||||
|
CHECK: python3 -m py_compile lib/effort-audit.py && D=$(mktemp -d) && mkdir -p "$D/p" && printf '%s\n%s\n%s\n' '{"type":"assistant","message":{"id":"m1","model":"claude-sonnet-5-5","usage":{"input_tokens":1,"output_tokens":10}}}' '{"type":"assistant","message":{"id":"m2","model":"claude-sonnet-5-5","usage":{"input_tokens":1,"output_tokens":10}}}' '{"type":"assistant","message":{"id":"m3","model":"claude-sonnet-5-5","usage":{"input_tokens":1,"output_tokens":10,"output_tokens_details":{"thinking_tokens":4}}}}' > "$D/p/s.jsonl" && out=$(python3 lib/effort-audit.py "$D") && echo "$out" | grep -q 'thinking counted on 33% of them' && echo "$out" | grep -q 'CAVEAT: main' && echo AUDIT_OK
|
||||||
|
EXPECT: AUDIT_OK
|
||||||
|
EVIDENCE: MET exit=0 marker-found :: AUDIT_OK
|
||||||
|
6. Doctrine + docs: CLAUDE.global.md ≤ 320 lines with the paired-load line; README "## Effort routing"; USAGE "### Niveau d'effort"; CHANGELOG Added + Fixed entries; doctrine-citers census green.
|
||||||
|
CHECK: [ "$(wc -l < CLAUDE.global.md)" -le 320 ] && grep -q 'a lone Skill call applies no effort' CLAUDE.global.md && grep -q '^## Effort routing' README.md && grep -q "^### Niveau d'effort" USAGE.md && grep -q 'Effort round (BDR-108)' CHANGELOG.md && grep -q 'never re-applied the effort pins' CHANGELOG.md && make test suite=lib/tests/doctrine-citers.test.sh 2>&1 | grep -q 'FAIL=0' && echo DOCS_OK
|
||||||
|
EXPECT: DOCS_OK
|
||||||
|
EVIDENCE: MET exit=0 marker-found :: DOCS_OK
|
||||||
|
7. Health stack: shellcheck clean on the touched shell files and the Health Stack set; no-vacuous-locks green.
|
||||||
|
CHECK: shellcheck lib/effort-pins.sh lib/tests/effort-pins.test.sh lib/tests/effort-routing.test.sh install-plugins.sh update-all.sh *.sh hooks/*.sh lib/*.sh && make test suite=lib/tests/no-vacuous-locks.test.sh >/dev/null 2>&1 && echo LINT_OK
|
||||||
|
EXPECT: LINT_OK
|
||||||
|
EVIDENCE: MET exit=0 marker-found :: LINT_OK
|
||||||
|
|
||||||
|
## FILE SCOPE
|
||||||
|
- lib/effort-pins.txt, lib/effort-pins.sh (new); lib/tests/effort-pins.test.sh (new); lib/tests/effort-routing.test.sh
|
||||||
|
- install-plugins.sh, update-all.sh (re-apply call), lib/effort-audit.py (coverage)
|
||||||
|
- skills/skills-perso/SKILL.md, skills/pdf-translate/SKILL.md, skills/site-motion/SKILL.md (effort line)
|
||||||
|
- lib/effort-shift.md, CLAUDE.global.md (doctrine), README.md, USAGE.md, CHANGELOG.md
|
||||||
|
- .claude/tasks/TODO.md, .claude/tasks/contracts/ (this file)
|
||||||
@@ -7,6 +7,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- **Effort round (BDR-108)**: every skill carries an entry level next to its model pin. `lib/effort-pins.txt` (map) + `lib/effort-pins.sh` (idempotent re-apply after the last vendoring step of `install-plugins.sh` and `update-all.sh`) replace the hardcoded brainstorming/writing-plans loop and extend the pins to the design stack (high, one level per stack since the last loaded wins), superpowers, agent-skills and the 21st pack; `skills-perso` low, `pdf-translate` medium, `site-motion` high; doctrine: the design stack loads paired with the first Read (a lone Skill call applies nothing). Model pins stay tier aliases: the latest version of a tier is also the cheapest or same-priced, so the quality/price trade-off is tier × effort, never version. `lib/effort-audit.py` prints thinking coverage per scope (sub-agent records carry no thinking count on ~90 % of requests: EVAL-037's "executors stay cheap" was a measurement gap, not a finding).
|
||||||
- **Effort tiering (BDR-107)**: reasoning effort routed per role and per phase. Session default `high`; `effort:` pins on the 20 repo-authored agents; entry level on 28 tracked user-invoked skills plus the two vendored superpowers skills (re-applied by `install-plugins.sh` after resync); five shifter skills `effort-low` … `effort-max` loaded at phase boundaries per `lib/effort-shift.md`, always sent with the step's first tool call (a lone Skill call is a no-op on 2.1.283), with `max` at the verify-secure caps and ship-feature 4b; `/effort-max` as the turn-scoped relaunch lever; statusline shows the live level; session banner warns when `CLAUDE_CODE_EFFORT_LEVEL` silences the pins; census `lib/tests/effort-routing.test.sh`; transcript audit `lib/effort-audit.py`.
|
- **Effort tiering (BDR-107)**: reasoning effort routed per role and per phase. Session default `high`; `effort:` pins on the 20 repo-authored agents; entry level on 28 tracked user-invoked skills plus the two vendored superpowers skills (re-applied by `install-plugins.sh` after resync); five shifter skills `effort-low` … `effort-max` loaded at phase boundaries per `lib/effort-shift.md`, always sent with the step's first tool call (a lone Skill call is a no-op on 2.1.283), with `max` at the verify-secure caps and ship-feature 4b; `/effort-max` as the turn-scoped relaunch lever; statusline shows the live level; session banner warns when `CLAUDE_CODE_EFFORT_LEVEL` silences the pins; census `lib/tests/effort-routing.test.sh`; transcript audit `lib/effort-audit.py`.
|
||||||
- **Design gate asks the user to sign in to 21st instead of skipping it**:
|
- **Design gate asks the user to sign in to 21st instead of skipping it**:
|
||||||
`lib/design-tool-gate.sh` adds a three-state 21st auth predicate
|
`lib/design-tool-gate.sh` adds a three-state 21st auth predicate
|
||||||
@@ -462,6 +463,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|||||||
plugin cache or `claude plugin list`.
|
plugin cache or `claude plugin list`.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- `update-all.sh` re-fetched the vendored skills at every run but never re-applied the effort pins: brainstorming/writing-plans lost their xhigh until the next `make plugin` (BDR-107 gap, closed by `lib/effort-pins.sh`).
|
||||||
- **gitflow pre-commit blocked every commit with gitleaks 8.16** (Ubuntu's apt
|
- **gitflow pre-commit blocked every commit with gitleaks 8.16** (Ubuntu's apt
|
||||||
package): the hook ran `gitleaks git --staged`, a subcommand that exists from
|
package): the hook ran `gitleaks git --staged`, a subcommand that exists from
|
||||||
8.19 only, so the "unknown command" exit 1 read as a leak. The generator now
|
8.19 only, so the "unknown command" exit 1 read as a leak. The generator now
|
||||||
|
|||||||
@@ -283,6 +283,9 @@ design routing; the design-toolchain hook reinforces it.
|
|||||||
- Design system / brand → design-consultation first, then the build tools.
|
- Design system / brand → design-consultation first, then the build tools.
|
||||||
- Review / audit → design-review + emil-design-eng + design-motion-principles
|
- Review / audit → design-review + emil-design-eng + design-motion-principles
|
||||||
+ /impeccable audit|critique + `impeccable detect` floor.
|
+ /impeccable audit|critique + `impeccable detect` floor.
|
||||||
|
- Load the stack paired with the first Read of the target file, never
|
||||||
|
alone (a lone Skill call applies no effort, `lib/effort-shift.md`); every
|
||||||
|
member pins `high`, one level per stack (`lib/effort-pins.txt`).
|
||||||
Scope doubt → ask or default to Build, never silently skip. Gate: light
|
Scope doubt → ask or default to Build, never silently skip. Gate: light
|
||||||
skills run `~/.claude/lib/design-gate.md`, orchestrators plugin-check. 21st =
|
skills run `~/.claude/lib/design-gate.md`, orchestrators plugin-check. 21st =
|
||||||
CLI (`npm i -g @21st-dev/cli`, `21st login`), no MCP, no key; search free,
|
CLI (`npm i -g @21st-dev/cli`, `21st login`), no MCP, no key; search free,
|
||||||
|
|||||||
@@ -93,6 +93,23 @@ was split like `/feat` (reflection inline + gate, `hotfixer` executor) and so
|
|||||||
joins the gated group (13th); `/client-handover`'s nested skill-runner
|
joins the gated group (13th); `/client-handover`'s nested skill-runner
|
||||||
children are dispatched `model:"fable"` (they carry reflection).
|
children are dispatched `model:"fable"` (they carry reflection).
|
||||||
|
|
||||||
|
## Effort routing (BDR-107, BDR-108)
|
||||||
|
|
||||||
|
Second axis of the same table: how hard each phase thinks. Session default
|
||||||
|
`high`. Every typed agent carries an `effort:` pin next to its `model:` (low
|
||||||
|
appliers, medium executors, high judgment, xhigh challengers and gates; none
|
||||||
|
on haiku, which rejects the parameter). Every user-invoked skill carries an
|
||||||
|
entry level (`/status` low … `/ship-feature` xhigh); the vendored externals
|
||||||
|
(design stack, superpowers, agent-skills, 21st) get theirs from
|
||||||
|
`lib/effort-pins.txt`, re-applied by `lib/effort-pins.sh` after every
|
||||||
|
vendoring step. Orchestrators shift per phase through the `effort-low` …
|
||||||
|
`effort-max` skills (`lib/effort-shift.md`, always sent with another tool
|
||||||
|
call: a lone Skill call applies nothing). Model pins stay tier aliases
|
||||||
|
(`sonnet`, `opus`, `haiku`, `fable`): the latest version of a tier is also
|
||||||
|
the cheapest or same-priced, so the quality/price trade-off is tier × effort,
|
||||||
|
never version. Census `lib/tests/effort-routing.test.sh`; transcript audit
|
||||||
|
`python3 lib/effort-audit.py`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Install notes
|
## Install notes
|
||||||
|
|||||||
@@ -171,6 +171,20 @@ Tu veux...
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Niveau d'effort
|
||||||
|
|
||||||
|
Chaque commande démarre à un niveau de réflexion fixé dans son frontmatter
|
||||||
|
(`effort:`) : low pour la tenue de registre (`/status`, `/close`,
|
||||||
|
`/commit-change`), medium pour le courant (`/gitflow`, `/prune-memory`),
|
||||||
|
high pour un fix ou un refactor (`/feat`, `/hotfix`, `/bugfix`, `/refactor`,
|
||||||
|
audits avec fix), xhigh pour l'architecture et l'audit avant validation
|
||||||
|
(`/ship-feature`, `/onboard`, `/analyze`). Les orchestrateurs décalent
|
||||||
|
ensuite le niveau par phase (`lib/effort-shift.md`), et `/effort-max` tapé à
|
||||||
|
la main relance un tour bloqué au maximum. Les skills externes vendorés
|
||||||
|
(pile design, superpowers, 21st) reçoivent leur niveau de
|
||||||
|
`lib/effort-pins.txt`. Un skill chargé seul par Claude n'applique pas son
|
||||||
|
niveau : il doit partir avec un autre appel d'outil dans le même message.
|
||||||
|
|
||||||
## Les plugins — décision rapide
|
## Les plugins — décision rapide
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
+10
-10
@@ -934,16 +934,9 @@ for _ext_skill in "${EXT_SKILL_NAMES[@]}"; do
|
|||||||
done
|
done
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Effort tiering (BDR-107): the vendored brainstorming/writing-plans carry an
|
# Effort pins (BDR-107, BDR-108): every vendored external gets its entry
|
||||||
# effort pin upstream lacks; re-apply after every resync (census lock in
|
# level from lib/effort-pins.txt, re-applied ONCE after the last vendoring
|
||||||
# lib/tests/effort-routing.test.sh alarms if this ever stops working).
|
# step (the 21st pack, STEP 8.7) — see apply_effort_pins there.
|
||||||
for _s in brainstorming writing-plans; do
|
|
||||||
_f="$(cd "$(dirname "$0")" && pwd)/skills-external/$_s/SKILL.md"
|
|
||||||
if [ -f "$_f" ] && ! grep -q '^effort:' "$_f"; then
|
|
||||||
sed -i "0,/^name: $_s\$/s//&\neffort: xhigh/" "$_f"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
unset _s _f
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# STEP 8.5 — EXTERNAL SKILLS (npx skills add …)
|
# STEP 8.5 — EXTERNAL SKILLS (npx skills add …)
|
||||||
@@ -1062,6 +1055,13 @@ if command -v 21st &>/dev/null; then
|
|||||||
rm -rf "$TFD_STAGE"
|
rm -rf "$TFD_STAGE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Effort pins (BDR-107, BDR-108): the vendored externals carry no `effort:`
|
||||||
|
# upstream and every vendoring step above rewrites SKILL.md. Re-apply the
|
||||||
|
# entry levels from lib/effort-pins.txt once, after the LAST such step.
|
||||||
|
# shellcheck source=lib/effort-pins.sh disable=SC1091
|
||||||
|
source "$REPO/lib/effort-pins.sh"
|
||||||
|
apply_effort_pins "$REPO" || warn "effort pins: map lines rejected — fix lib/effort-pins.txt"
|
||||||
|
|
||||||
# Auth — detect, then offer login ONLY in an interactive TTY. A non-interactive
|
# Auth — detect, then offer login ONLY in an interactive TTY. A non-interactive
|
||||||
# run (CI / headless / re-run) must never open a browser or block on OAuth.
|
# run (CI / headless / re-run) must never open a browser or block on OAuth.
|
||||||
# Search and logo lookup are free; retrieving component code and 21st AI need
|
# Search and logo lookup are free; retrieving component code and 21st AI need
|
||||||
|
|||||||
+39
-14
@@ -10,18 +10,21 @@ import sys
|
|||||||
|
|
||||||
# Weights relative to input price.
|
# Weights relative to input price.
|
||||||
WEIGHTS = {"in": 1.0, "cc": 1.25, "cr": 0.1, "out": 5.0}
|
WEIGHTS = {"in": 1.0, "cc": 1.25, "cr": 0.1, "out": 5.0}
|
||||||
FIELDS = ("in", "cc", "cr", "out", "think")
|
FIELDS = ("in", "cc", "cr", "out", "think", "nodet")
|
||||||
|
|
||||||
|
|
||||||
def usage_row(usage):
|
def usage_row(usage):
|
||||||
"""Map one API usage block to the five counted fields."""
|
"""Map one API usage block to the counted fields. `nodet` marks a
|
||||||
details = usage.get("output_tokens_details") or {}
|
record whose usage carries no output_tokens_details at all: no thinking
|
||||||
|
count was recorded (most sub-agent records), so `think` understates."""
|
||||||
|
details = usage.get("output_tokens_details")
|
||||||
return {
|
return {
|
||||||
"in": usage.get("input_tokens", 0) or 0,
|
"in": usage.get("input_tokens", 0) or 0,
|
||||||
"cc": usage.get("cache_creation_input_tokens", 0) or 0,
|
"cc": usage.get("cache_creation_input_tokens", 0) or 0,
|
||||||
"cr": usage.get("cache_read_input_tokens", 0) or 0,
|
"cr": usage.get("cache_read_input_tokens", 0) or 0,
|
||||||
"out": usage.get("output_tokens", 0) or 0,
|
"out": usage.get("output_tokens", 0) or 0,
|
||||||
"think": details.get("thinking_tokens", 0) or 0,
|
"think": (details or {}).get("thinking_tokens", 0) or 0,
|
||||||
|
"nodet": 0 if details else 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -57,21 +60,27 @@ def weighted(counter):
|
|||||||
return sum(counter[f] * WEIGHTS[f] for f in WEIGHTS)
|
return sum(counter[f] * WEIGHTS[f] for f in WEIGHTS)
|
||||||
|
|
||||||
|
|
||||||
def report(agg):
|
def coverage(counter):
|
||||||
"""Print the per-key table, then the main/sub split and the thinking
|
"""Share of requests whose usage carries a thinking count."""
|
||||||
share."""
|
return 100 * (1 - counter["nodet"] / max(counter["msgs"], 1))
|
||||||
total = collections.Counter()
|
|
||||||
for counter in agg.values():
|
|
||||||
total.update(counter)
|
def print_rows(agg, total_w):
|
||||||
total_w = weighted(total) or 1
|
"""One line per (scope, model, effort), costliest first."""
|
||||||
print(f"{'scope':5} {'model':22} {'effort':7} {'msgs':>6} {'think/msg':>9} "
|
print(f"{'scope':5} {'model':22} {'effort':7} {'msgs':>6} {'think/msg':>9} "
|
||||||
f"{'think_tok':>10} {'out_tok':>10} {'cache_read':>12} {'%wcost':>7}")
|
f"{'think_tok':>10} {'out_tok':>10} {'cache_read':>12} {'%wcost':>7} "
|
||||||
|
f"{'%counted':>8}")
|
||||||
ranked = sorted(agg.items(), key=lambda kv: -weighted(kv[1]))
|
ranked = sorted(agg.items(), key=lambda kv: -weighted(kv[1]))
|
||||||
for (scope, model, effort), c in ranked:
|
for (scope, model, effort), c in ranked:
|
||||||
per_msg = c["think"] / max(c["msgs"], 1)
|
per_msg = c["think"] / max(c["msgs"], 1)
|
||||||
print(f"{scope:5} {model:22} {effort:7} {c['msgs']:6d} "
|
print(f"{scope:5} {model:22} {effort:7} {c['msgs']:6d} "
|
||||||
f"{per_msg:9.0f} {c['think']:10d} {c['out']:10d} "
|
f"{per_msg:9.0f} {c['think']:10d} {c['out']:10d} "
|
||||||
f"{c['cr']:12d} {100 * weighted(c) / total_w:6.1f}%")
|
f"{c['cr']:12d} {100 * weighted(c) / total_w:6.1f}% "
|
||||||
|
f"{coverage(c):7.0f}%")
|
||||||
|
|
||||||
|
|
||||||
|
def print_scopes(agg, total, total_w):
|
||||||
|
"""Main/sub split, thinking share and the coverage caveat."""
|
||||||
by_scope = collections.defaultdict(collections.Counter)
|
by_scope = collections.defaultdict(collections.Counter)
|
||||||
for (scope, _, _), c in agg.items():
|
for (scope, _, _), c in agg.items():
|
||||||
by_scope[scope].update(c)
|
by_scope[scope].update(c)
|
||||||
@@ -79,11 +88,27 @@ def report(agg):
|
|||||||
print(f" {scope:5} weighted-cost "
|
print(f" {scope:5} weighted-cost "
|
||||||
f"{100 * weighted(c) / total_w:5.1f}% thinking "
|
f"{100 * weighted(c) / total_w:5.1f}% thinking "
|
||||||
f"{100 * c['think'] / max(total['think'], 1):5.1f}% "
|
f"{100 * c['think'] / max(total['think'], 1):5.1f}% "
|
||||||
f"requests {c['msgs']}")
|
f"requests {c['msgs']} thinking counted on "
|
||||||
|
f"{coverage(c):.0f}% of them")
|
||||||
print(f" thinking = "
|
print(f" thinking = "
|
||||||
f"{100 * total['think'] * WEIGHTS['out'] / total_w:.1f}% "
|
f"{100 * total['think'] * WEIGHTS['out'] / total_w:.1f}% "
|
||||||
f"of weighted cost; cache reads = "
|
f"of weighted cost; cache reads = "
|
||||||
f"{100 * total['cr'] * WEIGHTS['cr'] / total_w:.1f}%")
|
f"{100 * total['cr'] * WEIGHTS['cr'] / total_w:.1f}%")
|
||||||
|
low = [s for s, c in by_scope.items() if coverage(c) < 50]
|
||||||
|
if low:
|
||||||
|
print(f" CAVEAT: {', '.join(low)} records mostly carry no thinking "
|
||||||
|
f"count — their think columns are a floor, not a measure")
|
||||||
|
|
||||||
|
|
||||||
|
def report(agg):
|
||||||
|
"""Print the per-key table, then the main/sub split and the thinking
|
||||||
|
share."""
|
||||||
|
total = collections.Counter()
|
||||||
|
for counter in agg.values():
|
||||||
|
total.update(counter)
|
||||||
|
total_w = weighted(total) or 1
|
||||||
|
print_rows(agg, total_w)
|
||||||
|
print_scopes(agg, total, total_w)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# lib/effort-pins.sh — re-apply the entry effort level on vendored skills
|
||||||
|
# (BDR-107 second axis, extended to every vendored external by BDR-108).
|
||||||
|
# Upstream copies carry no `effort:` and every vendoring step rewrites
|
||||||
|
# SKILL.md, so the level lives in lib/effort-pins.txt and this helper puts
|
||||||
|
# it back after the last vendoring step of install-plugins.sh and
|
||||||
|
# update-all.sh. Idempotent: same level → untouched, other level →
|
||||||
|
# replaced inside the frontmatter only, skill not vendored → skipped,
|
||||||
|
# malformed map line → rejected loudly, never applied. Placement inside the
|
||||||
|
# frontmatter has no effect on the harness, which reads the key anywhere.
|
||||||
|
#
|
||||||
|
# Usage: source it, then `apply_effort_pins [repo-root]`
|
||||||
|
# or standalone: bash lib/effort-pins.sh [repo-root]
|
||||||
|
# Exit 1 when at least one map line was rejected.
|
||||||
|
|
||||||
|
EFFORT_PINS_REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
EFFORT_PIN_LEVEL_RE='^(low|medium|high|xhigh|max)$'
|
||||||
|
EFFORT_PIN_NAME_RE='^[A-Za-z0-9][A-Za-z0-9._-]*$'
|
||||||
|
|
||||||
|
# Callers (install-plugins.sh, update-all.sh) define these; standalone
|
||||||
|
# runs get plain fallbacks.
|
||||||
|
declare -F ok >/dev/null || ok() { printf ' ok %s\n' "$*"; }
|
||||||
|
declare -F info >/dev/null || info() { printf ' info %s\n' "$*"; }
|
||||||
|
declare -F err >/dev/null || err() { printf ' ERR %s\n' "$*" >&2; }
|
||||||
|
|
||||||
|
# _effort_pin_current <skill-file> → prints the frontmatter effort, if any
|
||||||
|
_effort_pin_current() {
|
||||||
|
awk 'NR==1&&/^---$/{p=1;next} p&&/^---$/{exit} p' "$1" \
|
||||||
|
| sed -n 's/^effort: //p' | head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# _effort_pin_write <skill-file> <name> <level> — replace the frontmatter
|
||||||
|
# `effort:` line, or insert one after `name: <name>` (before the closing
|
||||||
|
# `---` when the frontmatter has no name line). Body lines never change.
|
||||||
|
_effort_pin_write() {
|
||||||
|
local file="$1" name="$2" level="$3"
|
||||||
|
awk -v n="$name" -v lvl="$level" '
|
||||||
|
NR==1 && /^---$/ { fm=1; print; next }
|
||||||
|
fm && /^---$/ {
|
||||||
|
if (!done) { print "effort: " lvl; done=1 }
|
||||||
|
fm=0; print; next
|
||||||
|
}
|
||||||
|
fm && /^effort: / { if (!done) { print "effort: " lvl; done=1 }; next }
|
||||||
|
fm && $0 == "name: " n { print; if (!done) { print "effort: " lvl; done=1 }; next }
|
||||||
|
{ print }
|
||||||
|
' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# apply_effort_pins [repo-root] — walk the map, pin every vendored skill
|
||||||
|
apply_effort_pins() {
|
||||||
|
local repo="${1:-$EFFORT_PINS_REPO}" map name level rest file
|
||||||
|
local applied=0 kept=0 rejected=0
|
||||||
|
map="$repo/lib/effort-pins.txt"
|
||||||
|
[ -f "$map" ] || { err "effort-pins: map missing: $map"; return 1; }
|
||||||
|
while read -r name level rest; do
|
||||||
|
case "$name" in ''|'#'*) continue ;; esac
|
||||||
|
if [ -n "$rest" ] || ! [[ "$name" =~ $EFFORT_PIN_NAME_RE ]] \
|
||||||
|
|| ! [[ "$level" =~ $EFFORT_PIN_LEVEL_RE ]]; then
|
||||||
|
err "effort-pins: rejected map line '$name $level $rest'"
|
||||||
|
rejected=$((rejected + 1)); continue
|
||||||
|
fi
|
||||||
|
file="$repo/skills-external/$name/SKILL.md"
|
||||||
|
[ -f "$file" ] || continue
|
||||||
|
if [ "$(_effort_pin_current "$file")" = "$level" ]; then
|
||||||
|
kept=$((kept + 1)); continue
|
||||||
|
fi
|
||||||
|
_effort_pin_write "$file" "$name" "$level" && applied=$((applied + 1))
|
||||||
|
done < "$map"
|
||||||
|
ok "effort-pins: $applied applied, $kept already at level"
|
||||||
|
[ "$rejected" -eq 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
||||||
|
apply_effort_pins "$@"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# lib/effort-pins.txt — entry effort level of the vendored skills
|
||||||
|
# (skills-external/<name>/SKILL.md). Upstream copies carry no `effort:` and
|
||||||
|
# every resync rewrites SKILL.md, so the pin lives here and
|
||||||
|
# lib/effort-pins.sh re-applies it after the last vendoring step of
|
||||||
|
# install-plugins.sh and update-all.sh. One line = `<skill> <level>`,
|
||||||
|
# level in low|medium|high|xhigh|max. The census
|
||||||
|
# lib/tests/effort-routing.test.sh checks every vendored file against this
|
||||||
|
# map. Rungs (BDR-107, BDR-108): low = fix a line, run a script · medium =
|
||||||
|
# day-to-day · high = refactor, resisting bug · xhigh = architecture, audit
|
||||||
|
# before validation · max = stuck.
|
||||||
|
#
|
||||||
|
# superpowers (obra/superpowers, plugins.lock.json "superpowers")
|
||||||
|
brainstorming xhigh
|
||||||
|
writing-plans xhigh
|
||||||
|
requesting-code-review xhigh
|
||||||
|
subagent-driven-development high
|
||||||
|
writing-skills high
|
||||||
|
test-driven-development medium
|
||||||
|
using-git-worktrees low
|
||||||
|
#
|
||||||
|
# agent-skills (addyosmani/agent-skills, plugins.lock.json "agent-skills")
|
||||||
|
deprecation-and-migration high
|
||||||
|
ci-cd-and-automation medium
|
||||||
|
observability-and-instrumentation medium
|
||||||
|
#
|
||||||
|
# design stack — ONE level for every member: these skills load stacked in a
|
||||||
|
# single UI build and the last loaded wins (lib/effort-shift.md), so two
|
||||||
|
# levels in the stack would make the effort depend on load order.
|
||||||
|
# skills/site-motion (repo-authored) pins the same level in its frontmatter.
|
||||||
|
frontend-design high
|
||||||
|
emil-design-eng high
|
||||||
|
design-motion-principles high
|
||||||
|
21st-ui-build high
|
||||||
|
scroll-world-storytelling high
|
||||||
|
build-threejs-scroll-worlds high
|
||||||
|
scroll-scrubbed-visual-sequence high
|
||||||
|
scroll-scrubbed-word-reveal high
|
||||||
|
scroll-progress-timeline high
|
||||||
|
#
|
||||||
|
# 21st pack (`21st skills install`): tooling low, generation high, critique xhigh
|
||||||
|
21st-cli-use low
|
||||||
|
21st-registry low
|
||||||
|
21st-design-sync low
|
||||||
|
21st-ai high
|
||||||
|
21st-ui-explore high
|
||||||
|
21st-ui-review xhigh
|
||||||
@@ -24,6 +24,11 @@ max (stuck error, judged need).
|
|||||||
Claude loads alone, such as `brainstorming` or `writing-plans`, applies
|
Claude loads alone, such as `brainstorming` or `writing-plans`, applies
|
||||||
nothing). Last loaded wins, both directions. The prompt cache survives a
|
nothing). Last loaded wins, both directions. The prompt cache survives a
|
||||||
shift.
|
shift.
|
||||||
|
- **Stacked skills share one level**: skills that load together in one
|
||||||
|
build (the design stack) all pin the same level, since the last loaded
|
||||||
|
wins. Vendored externals get their level from `lib/effort-pins.txt`,
|
||||||
|
re-applied by `lib/effort-pins.sh` after every vendoring step; repo
|
||||||
|
skills carry it in their frontmatter.
|
||||||
- Dispatched agents run on their own `effort:` pin, never on a shift.
|
- Dispatched agents run on their own `effort:` pin, never on a shift.
|
||||||
Unpinned agents inherit the level in force at dispatch.
|
Unpinned agents inherit the level in force at dispatch.
|
||||||
- Headless sessions (`-p`, `claude agents`, SDK) ignore skill-level effort:
|
- Headless sessions (`-p`, `claude agents`, SDK) ignore skill-level effort:
|
||||||
|
|||||||
Executable
+60
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# lib/tests/effort-pins.test.sh — lib/effort-pins.sh's apply_effort_pins():
|
||||||
|
# insert after `name:`, keep an equal level untouched, replace a different
|
||||||
|
# level inside the frontmatter only (a prose `effort:` in the body stays),
|
||||||
|
# skip a skill not vendored, insert before the closing `---` when the
|
||||||
|
# frontmatter has no name line, run idempotently, reject a bad level, a
|
||||||
|
# traversal name and a three-field line before writing anything, and
|
||||||
|
# parse the real map without error. All on a throwaway fixture repo.
|
||||||
|
set -u
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
LIB="$ROOT/lib/effort-pins.sh"
|
||||||
|
pass=0; fail=0
|
||||||
|
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); echo "PASS $1"
|
||||||
|
else fail=$((fail+1)); echo "FAIL $1: got[$2] want[$3]"; fi; }
|
||||||
|
fm_effort() { awk 'NR==1&&/^---$/{p=1;next} p&&/^---$/{exit} p' "$1" \
|
||||||
|
| sed -n 's/^effort: //p' | head -1; }
|
||||||
|
|
||||||
|
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
|
||||||
|
REPO="$WORK/repo"; EXT="$REPO/skills-external"
|
||||||
|
mkdir -p "$REPO/lib" "$EXT/alpha" "$EXT/beta" "$EXT/gamma" "$EXT/noname"
|
||||||
|
printf -- '---\nname: alpha\ndescription: a\n---\nbody\n' > "$EXT/alpha/SKILL.md"
|
||||||
|
printf -- '---\nname: beta\neffort: low\n---\nprose says effort: max here\n' > "$EXT/beta/SKILL.md"
|
||||||
|
printf -- '---\nname: gamma\neffort: low\n---\nbody\n' > "$EXT/gamma/SKILL.md"
|
||||||
|
printf -- '---\ndescription: no name line\n---\nbody\n' > "$EXT/noname/SKILL.md"
|
||||||
|
printf '# map\nalpha high\nbeta medium\ngamma low\nghost xhigh\nnoname low\n' > "$REPO/lib/effort-pins.txt"
|
||||||
|
gamma_before="$(cat "$EXT/gamma/SKILL.md")"
|
||||||
|
|
||||||
|
bash "$LIB" "$REPO" >/dev/null 2>&1; check T1-rc-clean "$?" 0
|
||||||
|
check T2-insert-after-name "$(sed -n '3p' "$EXT/alpha/SKILL.md")" "effort: high"
|
||||||
|
check T3-replace-in-frontmatter "$(fm_effort "$EXT/beta/SKILL.md")" "medium"
|
||||||
|
check T3b-body-prose-untouched "$(grep -c 'effort: max' "$EXT/beta/SKILL.md")" 1
|
||||||
|
check T3c-single-effort-line "$(grep -c '^effort:' "$EXT/beta/SKILL.md")" 1
|
||||||
|
check T4-equal-level-untouched "$(cat "$EXT/gamma/SKILL.md")" "$gamma_before"
|
||||||
|
check T5-missing-skill-skipped "$([ -e "$EXT/ghost" ] && echo created || echo absent)" absent
|
||||||
|
check T6-no-name-inserts-before-closing "$(sed -n '3p' "$EXT/noname/SKILL.md")" "effort: low"
|
||||||
|
check T6b-no-name-still-frontmatter "$(fm_effort "$EXT/noname/SKILL.md")" "low"
|
||||||
|
snap="$(cat "$EXT"/*/SKILL.md)"
|
||||||
|
bash "$LIB" "$REPO" >/dev/null 2>&1
|
||||||
|
check T7-idempotent "$(cat "$EXT"/*/SKILL.md)" "$snap"
|
||||||
|
check T7b-no-tmp-left "$(find "$EXT" -name '*.tmp' | wc -l)" 0
|
||||||
|
|
||||||
|
# rejections: nothing written, rc 1
|
||||||
|
for bad in 'alpha turbo' '../evil high' 'alpha high extra'; do
|
||||||
|
printf '%s\n' "$bad" > "$REPO/lib/effort-pins.txt"
|
||||||
|
out="$(bash "$LIB" "$REPO" 2>&1)"; rc=$?
|
||||||
|
check "T8-rejected[$bad]-rc" "$rc" 1
|
||||||
|
check "T8-rejected[$bad]-named" "$(printf '%s' "$out" | grep -c 'rejected map line')" 1
|
||||||
|
done
|
||||||
|
check T8b-tree-unchanged-after-rejections "$(cat "$EXT"/*/SKILL.md)" "$snap"
|
||||||
|
check T8c-no-evil-dir "$([ -e "$WORK/evil" ] && echo created || echo absent)" absent
|
||||||
|
|
||||||
|
# the real map parses: fixture repo with the real map and no vendored skill
|
||||||
|
mkdir -p "$WORK/real/lib" "$WORK/real/skills-external"
|
||||||
|
cp "$ROOT/lib/effort-pins.txt" "$WORK/real/lib/"
|
||||||
|
out="$(bash "$LIB" "$WORK/real" 2>&1)"; check T9-real-map-parses "$?" 0
|
||||||
|
check T9b-real-map-nothing-applied "$(printf '%s' "$out" | grep -c '0 applied, 0 already')" 1
|
||||||
|
check T10-missing-map-rc "$(bash "$LIB" "$WORK/nowhere" >/dev/null 2>&1; echo $?)" 1
|
||||||
|
|
||||||
|
echo "effort-pins: $pass pass, $fail fail"
|
||||||
|
[ "$fail" -eq 0 ]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# lib/tests/effort-routing.test.sh — census: effort tiering (BDR-107)
|
# lib/tests/effort-routing.test.sh — census: effort tiering (BDR-107)
|
||||||
# agent pins, skill entry levels, shifter skills, orchestrator wiring, settings.
|
# agent pins, skill entry levels, shifter skills, orchestrator wiring, settings.
|
||||||
# shellcheck disable=SC2015 # A && ok || ko is deliberate here: ok/ko never fail, so C never masks a true A
|
# shellcheck disable=SC2015,SC2016 # A && ok || ko is deliberate (ok/ko never fail); '$REPO' locks are literal source text
|
||||||
set -u
|
set -u
|
||||||
R="$(cd "$(dirname "$0")/../.." && pwd)"
|
R="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
pass=0; fail=0
|
pass=0; fail=0
|
||||||
@@ -46,15 +46,30 @@ for s in status commit-change release-candidate doc capitalize close reconcile d
|
|||||||
for s in gitflow prune-memory; do fm_has_effort "skills/$s/SKILL.md" medium; done
|
for s in gitflow prune-memory; do fm_has_effort "skills/$s/SKILL.md" medium; done
|
||||||
for s in feat hotfix bugfix refactor web-validate harden seo geo; do fm_has_effort "skills/$s/SKILL.md" high; done
|
for s in feat hotfix bugfix refactor web-validate harden seo geo; do fm_has_effort "skills/$s/SKILL.md" high; done
|
||||||
for s in ship-feature init-project onboard tour audit-delta analyze code-clean client-handover; do fm_has_effort "skills/$s/SKILL.md" xhigh; done
|
for s in ship-feature init-project onboard tour audit-delta analyze code-clean client-handover; do fm_has_effort "skills/$s/SKILL.md" xhigh; done
|
||||||
|
# BDR-108 round: the three repo skills that had no level
|
||||||
|
fm_has_effort "skills/skills-perso/SKILL.md" low
|
||||||
|
fm_has_effort "skills/pdf-translate/SKILL.md" medium
|
||||||
|
fm_has_effort "skills/site-motion/SKILL.md" high
|
||||||
|
|
||||||
# ── 9) vendored superpowers carry xhigh (spec D3). The files live in skills-external/ (gitignored,
|
# ── 9) vendored externals carry the level of lib/effort-pins.txt (BDR-108). The files live in
|
||||||
# machine-owned), so the durable artifact is the install-plugins.sh re-apply; the frontmatter
|
# skills-external/ (gitignored, machine-owned): the durable artifact is the map + the re-apply
|
||||||
# check skips VISIBLY when the skill is not vendored yet (fresh clone before make plugin).
|
# after the last vendoring step of install-plugins.sh AND update-all.sh; a skill not vendored
|
||||||
for s in brainstorming writing-plans; do
|
# yet SKIPs visibly (fresh clone before make plugin).
|
||||||
if [ -f "$R/skills-external/$s/SKILL.md" ]; then fm_has_effort "skills-external/$s/SKILL.md" xhigh
|
while read -r s lvl _; do
|
||||||
|
case "$s" in ''|'#'*) continue ;; esac
|
||||||
|
if [ -f "$R/skills-external/$s/SKILL.md" ]; then fm_has_effort "skills-external/$s/SKILL.md" "$lvl"
|
||||||
else printf 'SKIP skills-external/%s/SKILL.md not vendored yet (run make plugin)\n' "$s"; fi
|
else printf 'SKIP skills-external/%s/SKILL.md not vendored yet (run make plugin)\n' "$s"; fi
|
||||||
done
|
done < "$R/lib/effort-pins.txt"
|
||||||
has "install-plugins.sh" 'effort: xhigh'
|
has "lib/effort-pins.txt" 'brainstorming xhigh'; has "lib/effort-pins.txt" 'writing-plans xhigh'
|
||||||
|
has "install-plugins.sh" 'apply_effort_pins "$REPO"'; has "update-all.sh" 'apply_effort_pins "$REPO"'
|
||||||
|
lacks "install-plugins.sh" 'for _s in brainstorming writing-plans; do'
|
||||||
|
[ -x "$R/lib/effort-pins.sh" ] && ok || ko "lib/effort-pins.sh missing or not executable"
|
||||||
|
# 9b) design stack = ONE level (last loaded wins); site-motion (repo skill) pins the same one
|
||||||
|
stack_levels() { awk '/^# design stack/{f=1;next} f&&/^#$/{f=0} f&&!/^#/&&NF==2{print $2}' "$R/lib/effort-pins.txt" | sort -u; }
|
||||||
|
[ "$(stack_levels | wc -l)" -eq 1 ] && ok || ko "design stack must share ONE level in lib/effort-pins.txt (got: $(stack_levels | tr '\n' ' '))"
|
||||||
|
[ "$(stack_levels | wc -l)" -ge 1 ] && fm_has_effort "skills/site-motion/SKILL.md" "$(stack_levels | head -1)"
|
||||||
|
has "lib/effort-shift.md" 'Stacked skills share one level'
|
||||||
|
has "CLAUDE.global.md" 'lib/effort-pins.txt'
|
||||||
|
|
||||||
# ── 5) shifter skills + include (spec D4)
|
# ── 5) shifter skills + include (spec D4)
|
||||||
for l in low medium high xhigh max; do fm_has_effort "skills/effort-$l/SKILL.md" "$l"; has "skills/effort-$l/SKILL.md" "name: effort-$l"; done
|
for l in low medium high xhigh max; do fm_has_effort "skills/effort-$l/SKILL.md" "$l"; has "skills/effort-$l/SKILL.md" "name: effort-$l"; done
|
||||||
@@ -101,7 +116,7 @@ has "lib/effort-shift.md" 'Before any built-in or unpinned dispatch'
|
|||||||
has "lib/model-gate.md" 'built-ins inherit the effort in force'
|
has "lib/model-gate.md" 'built-ins inherit the effort in force'
|
||||||
has "skills/ship-feature/SKILL.md" 'effort-shift: error recovery'
|
has "skills/ship-feature/SKILL.md" 'effort-shift: error recovery'
|
||||||
for s in feat hotfix bugfix seo geo harden web-validate ship-feature init-project onboard code-clean audit-delta; do has "skills/$s/SKILL.md" 'effort-shift: own level before the challenge'; done
|
for s in feat hotfix bugfix seo geo harden web-validate ship-feature init-project onboard code-clean audit-delta; do has "skills/$s/SKILL.md" 'effort-shift: own level before the challenge'; done
|
||||||
has "install-plugins.sh" 'for _s in brainstorming writing-plans; do'
|
has "update-all.sh" 'source "$REPO/lib/effort-pins.sh"'
|
||||||
|
|
||||||
# ── summary (later tasks insert their locks ABOVE this line)
|
# ── summary (later tasks insert their locks ABOVE this line)
|
||||||
printf 'effort-routing census: %d pass, %d fail\n' "$pass" "$fail"
|
printf 'effort-routing census: %d pass, %d fail\n' "$pass" "$fail"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: pdf-translate
|
name: pdf-translate
|
||||||
|
effort: medium
|
||||||
description: Use when translating a PDF (especially OCR or image-based) to another language and producing faithful HTML output. Handles image extraction, layout preservation, contextual translation, and style-matched reconstruction. Triggers on "translate this PDF", "PDF en francais", "convert PDF to HTML translated", "traduire ce document".
|
description: Use when translating a PDF (especially OCR or image-based) to another language and producing faithful HTML output. Handles image extraction, layout preservation, contextual translation, and style-matched reconstruction. Triggers on "translate this PDF", "PDF en francais", "convert PDF to HTML translated", "traduire ce document".
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: site-motion
|
name: site-motion
|
||||||
|
effort: high
|
||||||
description: |
|
description: |
|
||||||
Site-level motion choreography: scroll engine choice, page-transition
|
Site-level motion choreography: scroll engine choice, page-transition
|
||||||
rules, and pin/scrub sequencing across a whole page or Astro route —
|
rules, and pin/scrub sequencing across a whole page or Astro route —
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: skills-perso
|
name: skills-perso
|
||||||
|
effort: low
|
||||||
description: |
|
description: |
|
||||||
List personal (user-created) skills from ~/.claude/skills/.
|
List personal (user-created) skills from ~/.claude/skills/.
|
||||||
Excludes framework/gstack skills and symlinked/external skills.
|
Excludes framework/gstack skills and symlinked/external skills.
|
||||||
|
|||||||
@@ -393,6 +393,14 @@ echo ""
|
|||||||
echo "── Updating superpowers skills (obra/superpowers)..."
|
echo "── Updating superpowers skills (obra/superpowers)..."
|
||||||
vendor_pinned_skills superpowers refresh
|
vendor_pinned_skills superpowers refresh
|
||||||
|
|
||||||
|
# Effort pins (BDR-107, BDR-108): every refresh above rewrites SKILL.md and
|
||||||
|
# drops the `effort:` line; put the entry levels of lib/effort-pins.txt back.
|
||||||
|
echo ""
|
||||||
|
echo "── Re-applying effort pins on the vendored skills..."
|
||||||
|
# shellcheck source=lib/effort-pins.sh disable=SC1091
|
||||||
|
source "$REPO/lib/effort-pins.sh"
|
||||||
|
apply_effort_pins "$REPO" || warn "effort pins: map lines rejected — fix lib/effort-pins.txt"
|
||||||
|
|
||||||
# ── Impeccable (design detector + skill + subagents) ──
|
# ── Impeccable (design detector + skill + subagents) ──
|
||||||
# Global scope: the installer writes through the ~/.claude/{skills,agents}
|
# Global scope: the installer writes through the ~/.claude/{skills,agents}
|
||||||
# symlinks straight into this repo (install-plugins.sh Step 8d explains why
|
# symlinks straight into this repo (install-plugins.sh Step 8d explains why
|
||||||
|
|||||||
Reference in New Issue
Block a user