Run C1 of manual-push mode (BDR-111/BDR-112).
- lib/gitflow.sh: `gitflow.sh push-mode` prints auto | manual | invalid
(rc 0; an invalid value is named on stderr). It is the one reader a
skill may call: the bare `git config … gitflow.*` read is denied to
Claude since run B. Ignores GITFLOW_NO_PUSH by design (documented).
- skills/capitalize/SKILL.md STEP 5C: the explicit `git push origin
develop` is gone — `finish` has pushed develop itself since BDR-095,
mode-aware since run A. 5C is now three separate read-only calls
(finish; push-mode; `git rev-list --count origin/develop..develop`)
and prose outcomes keyed on the real ahead count: pushed / manual push
mode, you push / not on origin / push FAILED / invalid value named,
plus a finish-failure outcome (merge vs delete rc distinguished).
STEP 6 closing lines and the recap carry every outcome; the
`--no-push` line reads the branch's own ahead count ("this disk only"
only when true). Invariant: no `git push` inside any Bash call; the
user hints are prose.
- skills/close/SKILL.md, lib/gitflow-aiguillage.md: "push" claims
qualified "in auto-push mode".
- lib/gitflow-test.sh T11b: six cases for the verb (default, true,
false, non-boolean with stderr + rc 0, corrupt config, usage).
Polish items from the gates are listed in TODO.md (C1 polish).
Hardening after the security gate on a2ac018 (3 MEDIUM, all closed and
re-measured):
- dir tokens are deduplicated and capped: more than 20 distinct cd/-C
targets in one command denies before any git fork (20000 tokens: 0.15 s
against the 10 s hook timeout that used to turn a flood into an allow)
- a git or cd failure while reading gitflow.autopush denies instead of
reading as auto (git absent, usage error, unenterable dir); the key
being unset is the only "auto" answer; the decision is recorded only
after one candidate was evaluated cleanly, else the EXIT trap denies
- cd/pushd/-C targets that follow a quote or backtick (bash -c '…') are
extracted; quote characters are excluded from unquoted tokens
User decision (contract, gated): both fail-closed cases also fire in
auto mode on such pathological commands; silence in auto mode holds for
every ordinary push. Header limits list the residual misses (quotes or
backslashes inside a token, cumulative relative cd, unparseable payload)
backed by the soft_deny rule. Tests: 71 checks (T48–T50b added).
Run B of manual-push mode (BDR-111). With `gitflow.autopush false`
nothing stops Claude from typing `git push` itself: the `ask` tier is
inert under auto mode. This adds the mechanical block the user chose.
- hooks/push-guard.sh (PreToolUse, matcher Bash|Monitor, timeout 10):
detects a push in the command text (strict, quote-stripped loose and
alias patterns; backslash-newline folded in bash, BSD sed/grep only),
reads gitflow.autopush in the payload cwd and in every literal -C/cd
dir the command names (global config counts outside a repo), denies
with the documented JSON form and a reason that tells the user to run
the command with `!`. Unparseable value = manual (fail closed); once
a push is detected an EXIT trap emits a static deny on any internal
error. jq missing = one stderr warning, allow (sibling-hook policy).
- settings.json: hook wiring; 18 deny entries closing the write forms
of the human-only toggle (any `git … config` spelling, section
removal/rename, `-c`, config env overrides, direct edits of git config
files); one soft_deny on pushing in manual mode in any form, with no
per-turn clearance; the routing-around rule names hook refusals.
- hooks/session-start.sh: `🔒 push : manual (autopush=false) — ! git push`
banner line when the key reads false (padding in bytes).
- lib/tests/push-guard.test.sh: 61 checks (push forms, over-blocks,
invalid value, global key, fail-closed trap, no-jq, wiring, banner).
Known limits are listed in the hook header; the soft_deny rule is the
backstop. Run C (skills that push on their own) and run D (fail-closed
readers everywhere) follow. Do not enable manual mode at work before C.
`gitflow.autopush false` (human-set git config) now means "nothing is
pushed" end to end, not only in the post-commit/post-merge hooks:
- lib/gitflow.sh: `_gitflow_push_off` is the single reader of
GITFLOW_NO_PUSH / gitflow.autopush for the lib's push sites; `start`
and `finish` stop pushing in manual mode. `gitflow_delete` checks out
the base that contains the branch and drops a lagging upstream before
`git branch -d` (LRN-161: `-d` judges against the upstream when set).
Skipped remote deletes say `left in place`; `_gitflow_sync_base`
replaces the silent `pull --ff-only || true` and warns when a base is
behind origin and cannot fast-forward.
- hooks/unpushed-guard.sh: manual mode is silent at Stop and gives one
`ℹ manual push mode:` line at SessionStart counting every local
branch; an unparseable value is named and treated as auto.
- CLAUDE.global.md: manual-push mode doctrine, "ahead = defect" scoped
to auto mode.
- Tests: gitflow-test T18m block (T18m0, T18i-T18o, 7 cases),
unpushed-guard T10-T16.
Follow-ups (TODO.md): run B push-guard hook + settings deny widening +
banner; run C skills that push on their own (/close STEP 5C, …).
Do not enable manual mode on the work machine before B and C land.
The suite and seven libs assumed a GNU userland: `cmd | grep -q` under
pipefail (grep exits at the first match, the producer takes SIGPIPE,
rc 141 → 15 false "merged into" FAILs in gitflow-test), `sed -i` with no
suffix, `wc -l` padding compared as a string, `stat -c`, `touch -d`,
`realpath -m` (gstack-links refusal never fired), bare `timeout` off the
sanitized PATH (design gate READY BUT UNVERIFIED), `grep -oP` (update-all
emptied the plugin list). Thirteen suites were red on this machine.
Portable forms on the native userland of both OS: producer captured out
of the pipeline, `sed -i.bak` in tests and a temp-sibling `sed_profile`
on the user dotfile, `tr -d ' '`, python3 perms, `touch -t`, a
`realpath -m` emulation that refuses `..`, perl `alarm` for the 15 s
bound, `sed -n` token extraction. Regression tests: gstack-links T4b,
doctrine-citers Alphabet/Alpha flip, profile-set-managed T18 (failing CLI
no longer aborts `set`), new portability-census suite over the tracked
shell files. Linux run deferred (see TODO).
INT/TERM trap removes the mktemp sibling and exits 130 (traps restored,
never EXIT); re-read message honest and reached by a stubbed test; rejected
map line printed through printf %q; suite guards mktemp -d and skips the
read-only case under root. Cases T13b, T15, T15b, T16.
Last map line without newline read; unclosed frontmatter skipped with an
err; level re-read after write, mismatch counted as failed; mktemp + cp -p
+ mv, temp removed on failure; rc 1 on any rejected or failed entry.
Cases T11-T14 in the fixture suite; contract criteria 8-9.
The 21st pack refresh (update-all 7.4) rewrites every 21st-* SKILL.md after
the superpowers refresh; the re-apply now sits after it, the census locks
the order in both scripts. Contract: criterion 3 anchor, shellcheck
directive authorized, tracked design-motion-principles copy gated.
- 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
Ubuntu's gitleaks 8.16 package has no git subcommand, so the hook's
"unknown command" exit 1 blocked every commit as a leak. Probe
gitleaks git --help once, fall back to protect --staged; regenerate the
installed hooks. T16c simulates a missing binary with a /usr/bin symlink
farm minus gitleaks instead of a shorter PATH.
skip_kind matched SKIP_SUBSTRINGS as plain substrings, so 'xit(' hit
exit(, SystemExit( and process.exit(, and 'fit(' hit model.fit( and
profit(, flagging FLOOR SKIP on ordinary test-file lines (BLK-023). The
four bare identifiers (xit, fit, xdescribe, fdescribe) now match through
SKIP_IDENT_RE with an identifier-boundary lookbehind; the dotted and
decorator forms stay substrings. Flip-test fixtures cover the false
positive (RED before, GREEN after) and the three focus/skip calls.
Every superpowers-prefixed skill call in ship-feature, init-project, tour,
deploy, audit-delta, plugin-advisor and lib/analyze-before-plan now names
the vendored skill directly. finishing-a-development-branch is described
as the upstream skill this config does not vendor (gitflow finish is the
integration path). CLAUDE.global.md Skill routing maps the four
non-vendored skills the vendored text still references. settings.json
loses the plugin key and its marketplace block; README, USAGE,
plugin-advisor and the profile skill describe superpowers as vendored
skills, always on, zero plugin cost. CHANGELOG entry with a known
residual.
plugins.lock.json gains a superpowers entry (obra/superpowers @ 5bf4e78,
path skills, per-skill file lists, always_on) that lib/vendor-skills.sh
fetches byte-for-byte: brainstorming, writing-plans,
subagent-driven-development, test-driven-development,
requesting-code-review, using-git-worktrees, writing-skills. install-plugins
STEP 8e vendors it, update-all refreshes it at the pin, link.sh links the
seven, .gitignore ignores them. The plugin is no longer installed or
protected: its 8 other skills duplicated personal flows and its
SessionStart injection cost ~900 tokens per start, clear and compact.
detect_superpowers is one file test on the linked skill; doctor and
session-start stop charging the injection. doctor-vendored gains an
always_on class (third lock column) so always-on externals are
link-checked instead of reported parked.
The design gate checked the 21st CLI with command -v only, so an
installed-but-signed-out CLI read as READY and every 21st step failed
downstream. tool_active now probes 21st whoami through a three-state
function: signed in (TWENTYFIRST_TOKEN or API_KEY_21ST set, or 'Logged in
as'), signed out (exact 'Not logged in'), unknown (rc != 0, timeout,
unexpected output). Signed out is a new verdict, SIGN-IN REQUIRED, exit
12: design-gate.md tells the orchestrator to ask the user to run
! 21st login, end the turn, re-run the gate on their reply, and to skip
21st only on an explicit 'proceed without 21st', never silently. Unknown
surfaces as exit 11 with the whoami diagnostic and a CLI-runtime remedy,
so a node/PATH failure can never loop on a sign-in prompt. INCOMPLETE
still wins. Hermetic suite lib/tests/design-tool-gate.test.sh (stub CLI,
fixture repo through DESIGN_GATE_REPO_OVERRIDE) covers every state.
gstack skills hardcode ~/.claude/skills/gstack/<path> for 83 shared assets
(bin, scripts/jargon-list.json, ETHOS.md, */sections, review/specialists,
make-pdf/dist, lib/diagram-render/dist, freeze/bin...) but only bin and
browse/dist were linked: make-pdf and diagram failed on every run, cso and
plan-*-review could not read their sections, the freeze hook exited 127.
lib/gstack-links.sh links every top-level entry except SKILL.md, skips
non-skill dirs holding a nested SKILL.md (browser-skills, openclaw,
node_modules), removes the global symlink gstack ./setup plants and refuses
a destination inside the submodule. link.sh, install-plugins.sh and
update-all.sh all call it (three hand-copied blocks gone).
doctor.sh counted 34 skills (find without -L) and zero chars for block
scalar descriptions; lib/doctor-skills.sh reuses the census parser and
counts through the symlinks. Plugin constants re-based on measured values;
install-plugins.sh notes why frontend-design@claude-plugins-official and
brightdata-plugin@synced stay off and describes security-guidance truthfully.
Nine gstack skills leave every profile (ship is trunk-based on Gitea,
land-and-deploy auto-merges and deploys, setup-deploy, autoplan reads
paths that do not exist here, context-save has no restore, learn is an
unused parallel store, careful and guard hooks never fired, design-shotgun
needs an absent OpenAI key). lib/gstack-removed.sh is the single denylist;
profile.sh gstack on and toggle-external.sh enable gstack skip it.
full now carries everything every other profile carries (user rule), minus
the parked make-pdf, diagram and 21st-ai/ui-explore/ui-review, which live
in the new max profile together with pr-review-toolkit. The 21st trio also
leaves web, web-full and design (redundant with impeccable + ui-ux-pro-max).
lib/tests/profile-census.test.sh asserts the invariants live and on a
baseline fixture plus one mutant per invariant; gstack-removed.test.sh
covers both restore paths.
lib/doctor-vendored.sh check_vendored_skills: every curl-pinned lock entry
has its files under skills-external/ (list, dict, single-path shapes),
every link.sh EXTERNAL_SKILLS name is symlinked into ~/.claude/skills when
the active profile lists it, parked names reported not failed, hints make
plugin / make link. Lock shape-validated (warn, never a traceback), profile
and item names allowlisted before becoming paths. Suite: 11 cases.
Two security-gate LOW notes closed on user ask: the SAFE guard uses
re.fullmatch so a trailing newline is rejected; commit (40 hex), source
(github.com owner/repo) and path (SAFE class, no traversal) are validated
before any URL is built, INVALID marker names the field. Suite 12 cases.
Personal skill distilling the MengTo motion pack invariants (LRN-141):
gates first (reduced motion renders final states, content visible without
JS, compositor-only, offscreen pause), one smooth-scroll engine with the
Lenis/ScrollTrigger sync, Astro ClientRouter lifecycle, numbered recipes
(reveal, scrub, sticky stack, video and image scrub, TreeWalker split,
progressive blur, marquee, WebGL budgets), upstream pitfalls, checklist.
Routed into the Build UI chain of CLAUDE.global.md and lib/design-gate.md.
lib/vendor-skills.sh: vendor_pinned_skills <lock-key> [refresh], list or
dict lock shapes, lock read via python argv, traversal and charset guard
on lock values, VENDOR_BASE_URL honoured only as file:// (hermetic suite),
per-file tmp+mv, refresh skips a skill never installed. install-plugins.sh
Step 8e and update-all.sh 7.3 call it for agent-skills and mengto-skills.
Vendored at a965851: scroll-world-storytelling, build-threejs-scroll-worlds
(+5 references), scroll-scrubbed-visual-sequence, scroll-scrubbed-word-
reveal, scroll-progress-timeline; text files only. Registered in link.sh,
.gitignore, toggle-external, profile.sh and the design/web/web-full/full
profiles, which also list site-motion (personal). Suite: 8 cases.
Security-gate MEDIUM: a self-service floor-guard: allow <reason> neutralised
the detector in the same commit. User chose strict: the tool prints WAIVED,
the contract authorizes, the verifier counts the rest as gaps. BDR-102
amendment.
Top 10 description pairs, WARN >= 0.50, FAIL >= 0.75, fixture flip-test
with a positive control and a sensitivity re-run (2-doc corpora are
degenerate, LRN-172). Baseline 2026-09-27: 120 skills, max 0.52
(careful ~ guard). Adapted from agent-skills evals Tier 2.
observability-and-instrumentation, deprecation-and-migration,
ci-cd-and-automation from addyosmani/agent-skills 2686b620, curl'd into
skills-external/<name>/ by install-plugins.sh Step 8e (tmp+mv), refreshed
by update-all.sh 7.3, symlinked by link.sh, registered in toggle-external,
profile.sh and the full/backend/dev profiles. Pin read from the lock via
argv, never hardcoded. Case 2 of the 6-repo review, BDR-102.
`scrape`, `skillify` (Browser + dogfooding) and `diagram`, `make-pdf`
(Docs + translation) join the default profile, user go. The rest of the
BDR-017 exclusion list (ios-*, connect-chrome duplicate of
open-gstack-browser, gstack-internal tooling) stays out; enable it per
session with `profile apply` when needed.