crewforth 3.4.0 → 3.5.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +273 -0
- package/README.md +3 -2
- package/VERSION +1 -1
- package/adopt.sh +40 -5
- package/bin/cli.js +138 -2
- package/kit/AGENT_TEMPLATE.md +15 -0
- package/kit/CLAUDE.md +50 -18
- package/kit/agents/crew-backend-expert.md +4 -3
- package/kit/agents/crew-commit-agent.md +2 -0
- package/kit/agents/crew-database-expert.md +2 -1
- package/kit/agents/crew-devops-expert.md +1 -0
- package/kit/agents/crew-docs-agent.md +3 -1
- package/kit/agents/crew-frontend-expert.md +2 -0
- package/kit/agents/crew-performance-expert.md +7 -2
- package/kit/agents/crew-planner.md +15 -3
- package/kit/agents/crew-privacy-agent.md +2 -0
- package/kit/agents/crew-review-agent.md +8 -1
- package/kit/agents/crew-security-expert.md +4 -3
- package/kit/agents/crew-test-expert.md +6 -2
- package/kit/eval/doctor.sh +201 -1
- package/kit/eval/gate-report.sh +28 -0
- package/kit/eval/golden-holdout.txt +2 -0
- package/kit/eval/golden-routing.txt +17 -0
- package/kit/eval/lib/crew-map/contracts.mjs +77 -0
- package/kit/eval/lib/crew-map/crew-map.sh +2 -1
- package/kit/eval/lib/crew-map/detect.mjs +2 -1
- package/kit/eval/lib/crew-map/narrative.mjs +14 -3
- package/kit/eval/lib/crew-map/project.mjs +45 -6
- package/kit/eval/lib/crew-map/scan.mjs +153 -14
- package/kit/eval/lib/findings/findings.schema.json +11 -1
- package/kit/eval/lib/findings/findings.sh +24 -1
- package/kit/eval/lib/hook-inject.sh +159 -0
- package/kit/eval/lib/lockfiles.sh +71 -0
- package/kit/eval/lib/payload-check.sh +29 -0
- package/kit/eval/lib/preview.sh +3 -3
- package/kit/eval/lib/review-bench/bench.mjs +14 -2
- package/kit/eval/lib/review-prepass/prepass.mjs +32 -3
- package/kit/eval/lib/settings-json.awk +18 -0
- package/kit/eval/lib/statusline.sh +22 -5
- package/kit/eval/lib/todo.awk +167 -0
- package/kit/eval/lib/todo.sh +120 -0
- package/kit/eval/lib/untrusted-text.awk +38 -0
- package/kit/eval/scan-skill.sh +67 -20
- package/kit/eval/smoke-test.sh +1759 -41
- package/kit/eval/utilization.sh +77 -3
- package/kit/hooks/agent-outcome.sh +9 -1
- package/kit/hooks/board-sync.sh +49 -3
- package/kit/hooks/board.sh +8 -8
- package/kit/hooks/branch-fresh.sh +101 -0
- package/kit/hooks/floor-blocklist.txt +19 -1
- package/kit/hooks/guard-bash.sh +92 -8
- package/kit/hooks/guard-question.sh +2 -1
- package/kit/hooks/loop-guard.sh +445 -0
- package/kit/hooks/map-stale.sh +437 -0
- package/kit/hooks/pre-commit +48 -7
- package/kit/hooks/precompact-snapshot.sh +102 -6
- package/kit/hooks/prompt-approval.sh +50 -14
- package/kit/hooks/route-hint.sh +36 -7
- package/kit/hooks/secret-blocklist.txt +55 -1
- package/kit/hooks/session-guard.sh +45 -15
- package/kit/hooks/session-rehydrate.sh +34 -11
- package/kit/hooks/session-stats.sh +169 -8
- package/kit/hooks/session-update-check.sh +5 -1
- package/kit/hooks/skill-trust.sh +23 -14
- package/kit/hooks/turn-check.sh +190 -0
- package/kit/owned-blobs.tsv +4 -4
- package/kit/settings.json +64 -0
- package/kit/skills/a11y/SKILL.md +50 -3
- package/kit/skills/a11y/references/recipes.md +41 -0
- package/kit/skills/adr/SKILL.md +14 -2
- package/kit/skills/api-design/SKILL.md +6 -1
- package/kit/skills/api-design/references/idempotency.md +39 -0
- package/kit/skills/brainstorm/SKILL.md +4 -2
- package/kit/skills/brainstorm/references/asking.md +47 -0
- package/kit/skills/ci-pipeline/SKILL.md +11 -1
- package/kit/skills/ci-pipeline/references/agent-job.md +33 -0
- package/kit/skills/commit-message/SKILL.md +9 -3
- package/kit/skills/confidence-check/SKILL.md +9 -7
- package/kit/skills/confidence-check/references/source-check.md +37 -0
- package/kit/skills/crew-approve/SKILL.md +3 -0
- package/kit/skills/crew-audit/SKILL.md +63 -0
- package/kit/skills/crew-board/SKILL.md +2 -2
- package/kit/skills/crew-code-review/SKILL.md +27 -4
- package/kit/skills/crew-code-review/references/panel-mode.md +9 -9
- package/kit/skills/crew-code-review/references/simplify.md +28 -0
- package/kit/skills/crew-doctor/SKILL.md +3 -2
- package/kit/skills/crew-gates/SKILL.md +3 -0
- package/kit/skills/crew-gates/references/enforcers.md +30 -0
- package/kit/skills/crew-interview/SKILL.md +42 -0
- package/kit/skills/crew-map/SKILL.md +26 -6
- package/kit/skills/crew-plan/SKILL.md +10 -2
- package/kit/skills/crew-pr/SKILL.md +57 -0
- package/kit/skills/crew-pr/references/checks.md +35 -0
- package/kit/skills/crew-pr/references/threads.md +23 -0
- package/kit/skills/crew-review/SKILL.md +34 -1
- package/kit/skills/crew-ship/SKILL.md +5 -2
- package/kit/skills/crew-skill/SKILL.md +26 -1
- package/kit/skills/crew-todo/SKILL.md +31 -0
- package/kit/skills/crew-update/SKILL.md +4 -1
- package/kit/skills/db-migration/SKILL.md +15 -0
- package/kit/skills/db-migration/references/expand-contract.md +52 -0
- package/kit/skills/dependency-audit/SKILL.md +34 -1
- package/kit/skills/dependency-audit/scripts/record.sh +37 -0
- package/kit/skills/dependency-upgrade/SKILL.md +29 -1
- package/kit/skills/deploy/SKILL.md +18 -0
- package/kit/skills/deploy/references/topology-a.md +7 -0
- package/kit/skills/deploy/references/topology-b.md +2 -2
- package/kit/skills/docs-writer/SKILL.md +37 -2
- package/kit/skills/eval-grader/SKILL.md +9 -0
- package/kit/skills/eval-grader/references/method.md +26 -0
- package/kit/skills/frontend/SKILL.md +5 -2
- package/kit/skills/frontend/references/from-design.md +29 -0
- package/kit/skills/frontend/references/page-metadata.md +17 -0
- package/kit/skills/frontend/references/verify-contract.md +18 -1
- package/kit/skills/frontend/references/worst-data.md +35 -0
- package/kit/skills/frontend-design/SKILL.md +20 -4
- package/kit/skills/frontend-design/references/design-review.md +21 -0
- package/kit/skills/frontend-design/references/detection.md +44 -0
- package/kit/skills/frontend-design/references/motion.md +54 -0
- package/kit/skills/frontend-design/references/ui-copy.md +35 -0
- package/kit/skills/frontend-rn-expo/SKILL.md +3 -0
- package/kit/skills/handoff/SKILL.md +28 -3
- package/kit/skills/handoff/references/cold-reader.md +13 -6
- package/kit/skills/i18n-integrity/SKILL.md +3 -0
- package/kit/skills/iterate/SKILL.md +7 -2
- package/kit/skills/mcp-builder/SKILL.md +21 -4
- package/kit/skills/mcp-builder/references/building.md +74 -13
- package/kit/skills/mcp-builder/references/cli-for-agents.md +26 -0
- package/kit/skills/observability/SKILL.md +13 -1
- package/kit/skills/performance/SKILL.md +25 -1
- package/kit/skills/performance/references/bottlenecks.md +11 -0
- package/kit/skills/performance/references/web-vitals.md +31 -0
- package/kit/skills/red-team/SKILL.md +25 -3
- package/kit/skills/reflect/SKILL.md +7 -2
- package/kit/skills/release/SKILL.md +13 -0
- package/kit/skills/security-scan/SKILL.md +6 -2
- package/kit/skills/security-scan/references/ai-agents.md +16 -0
- package/kit/skills/security-scan/references/fronts.md +59 -2
- package/kit/skills/security-scan/references/reporting.md +13 -0
- package/kit/skills/security-scan/references/verify.md +9 -0
- package/kit/skills/sonarqube-check/SKILL.md +32 -2
- package/kit/skills/sonarqube-check/references/ratchet.md +32 -0
- package/kit/skills/spec-planning/SKILL.md +32 -8
- package/kit/skills/systematic-debugging/SKILL.md +15 -7
- package/kit/skills/systematic-debugging/references/techniques.md +5 -0
- package/kit/skills/teamboard/SKILL.md +3 -3
- package/kit/skills/testing/SKILL.md +18 -3
- package/kit/skills/testing/references/browser-headless.md +21 -0
- package/kit/skills/token-budget/SKILL.md +26 -5
- package/kit/skills/worktree/SKILL.md +13 -5
- package/kit/studio/server/lib/session.js +23 -2
- package/kit/studio/server/lib/worktree-changes.js +64 -0
- package/kit/studio/web/agent-spend.js +32 -0
- package/kit/studio/web/app.js +245 -236
- package/kit/studio/web/approvals.js +14 -13
- package/kit/studio/web/ask.js +10 -9
- package/kit/studio/web/canvas.js +56 -54
- package/kit/studio/web/chat.js +41 -41
- package/kit/studio/web/convo.js +33 -16
- package/kit/studio/web/dock.js +20 -19
- package/kit/studio/web/graph-plan.js +62 -22
- package/kit/studio/web/i18n-tr.js +760 -0
- package/kit/studio/web/i18n.js +79 -0
- package/kit/studio/web/index.html +1 -0
- package/kit/studio/web/inspect.js +27 -24
- package/kit/studio/web/list-plan.js +16 -15
- package/kit/studio/web/list.js +6 -5
- package/kit/studio/web/liveness.js +11 -9
- package/kit/studio/web/map-plan.js +86 -36
- package/kit/studio/web/map.js +85 -72
- package/kit/studio/web/models-plan.js +47 -42
- package/kit/studio/web/models.js +21 -20
- package/kit/studio/web/names.js +13 -10
- package/kit/studio/web/nav.js +34 -33
- package/kit/studio/web/newsession.js +25 -24
- package/kit/studio/web/style.css +2 -0
- package/kit/studio/web/theme.js +5 -2
- package/kit/studio/web/timeline-plan.js +19 -18
- package/kit/studio/web/timeline.js +23 -22
- package/kit/studio/web/usage.js +36 -32
- package/package.json +5 -4
- package/start.sh +11 -2
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# turn-check.sh — UserPromptSubmit + Stop: the files the main thread changed in a turn get the project's own fast
|
|
3
|
+
# check once, before the turn ends.
|
|
4
|
+
#
|
|
5
|
+
# WHY. A crew agent's work is checked when it stops (agent-outcome.sh runs its card's `verify`). The main thread's
|
|
6
|
+
# own edits, which the discipline allows for small changes, had nothing: the turn ended, the user read "done", and
|
|
7
|
+
# a lint error waited for the next commit or the next person. This closes that gap with the command the project
|
|
8
|
+
# already declared for it, and only that one.
|
|
9
|
+
#
|
|
10
|
+
# WHAT RUNS. The `Fast check (files):` line of ./CLAUDE.md (## Verify). `<file>` in it becomes the turn's changed files,
|
|
11
|
+
# each in single quotes; a command with no `<file>` runs as written. No line, the template's placeholder, or `none`:
|
|
12
|
+
# nothing runs, nothing is written, no process starts. The command is judged exactly as agent-outcome.sh judges a
|
|
13
|
+
# verify command before it runs it (_am_verify_ok in guard-agent-model.sh: permission rules, the shell gate, the
|
|
14
|
+
# mode); one it may not run is said to the user once per session and not run.
|
|
15
|
+
#
|
|
16
|
+
# WHICH FILES ARE THE TURN'S. At UserPromptSubmit a snapshot goes into the git directory (never committed): HEAD, the
|
|
17
|
+
# time, and the content id of every file that was already dirty. At Stop the turn's set is
|
|
18
|
+
# * a dirty file whose content differs from the snapshot (one dirty before the turn and untouched is not the
|
|
19
|
+
# turn's, it is earlier work);
|
|
20
|
+
# * the files of commits the turn made (commit, amend, cherry-pick, revert), as `git diff <snapshot HEAD> HEAD`;
|
|
21
|
+
# an amend therefore counts only what it changed. When the turn moved HEAD any other way (pull, merge, reset,
|
|
22
|
+
# checkout, rebase), the commits are not counted at all: someone else's code is not this turn's to check.
|
|
23
|
+
# Deleted files are left out. An empty set is silence.
|
|
24
|
+
#
|
|
25
|
+
# RED. Exit 2 with the end of the check's output, which Claude Code hands back to the model so the turn goes on to
|
|
26
|
+
# fix it. Once per turn: Claude Code marks the next Stop of the same turn `stop_hook_active`, and that Stop passes.
|
|
27
|
+
#
|
|
28
|
+
# MODES: CREW_TURN_CHECK=off nothing runs. CREW_TURN_CHECK_TIMEOUT seconds the check may take (default 120, at
|
|
29
|
+
# most 170, under the hook's own 180). NOT A GATE ON ITS OWN FAILURE: an error of this script is silence (exit 0);
|
|
30
|
+
# the only exit 2 is a check that ran and failed.
|
|
31
|
+
set -uo pipefail
|
|
32
|
+
case "${CREW_TURN_CHECK:-on}" in off|OFF|0|false) exit 0 ;; esac
|
|
33
|
+
trap 'exit 0' EXIT
|
|
34
|
+
export LC_ALL=C
|
|
35
|
+
IN=""; IFS= read -r -d '' -t "${CREW_STDIN_TIMEOUT:-2}" IN 2>/dev/null; [ "$?" -gt 128 ] && exit 0
|
|
36
|
+
|
|
37
|
+
_TC_HERE="${BASH_SOURCE%/*}"; [ "$_TC_HERE" = "${BASH_SOURCE}" ] && _TC_HERE=.
|
|
38
|
+
P="${CLAUDE_PROJECT_DIR:-$PWD}"; cd "$P" 2>/dev/null || exit 0
|
|
39
|
+
|
|
40
|
+
# The declared command, read with the builtin `read`: a project that declared none pays no process at all.
|
|
41
|
+
FC=""
|
|
42
|
+
if [ -f CLAUDE.md ]; then
|
|
43
|
+
while IFS= read -r _l || [ -n "$_l" ]; do
|
|
44
|
+
case "$_l" in "Fast check (files):"*) FC="${_l#Fast check (files):}"; break ;; esac
|
|
45
|
+
done < CLAUDE.md
|
|
46
|
+
fi
|
|
47
|
+
FC="${FC%$'\r'}"; FC="${FC#"${FC%%[![:space:]]*}"}"; FC="${FC%"${FC##*[![:space:]]}"}"
|
|
48
|
+
case "$FC" in '`'*'`') FC="${FC#\`}"; FC="${FC%\`}" ;; esac
|
|
49
|
+
case "$FC" in ''|'<'*|none|None|NONE) exit 0 ;; esac
|
|
50
|
+
|
|
51
|
+
_field(){ # $1 = key -> _F, the payload's string value (letters, digits, - and _ only), "" when absent
|
|
52
|
+
local r="${IN#*\"$1\"}"; _F=""
|
|
53
|
+
[ "$r" = "$IN" ] && return 0
|
|
54
|
+
r="${r#"${r%%[![:space:]:]*}"}"; r="${r#\"}"; _F="${r%%\"*}"
|
|
55
|
+
case "$_F" in *[!A-Za-z0-9_-]*) _F="" ;; esac
|
|
56
|
+
}
|
|
57
|
+
_field hook_event_name; EV="$_F"
|
|
58
|
+
_field session_id; SID="$_F"; [ -n "$SID" ] || exit 0
|
|
59
|
+
|
|
60
|
+
GD="$(git rev-parse --absolute-git-dir 2>/dev/null)" || exit 0
|
|
61
|
+
SNAPD="$GD/crewforth-turn"; SNAP="$SNAPD/$SID"
|
|
62
|
+
_now(){ if [ "${BASH_VERSINFO[0]}" -ge 5 ] || { [ "${BASH_VERSINFO[0]}" -eq 4 ] && [ "${BASH_VERSINFO[1]}" -ge 2 ]; }; then printf -v NOW '%(%s)T' -1; else NOW="$(date +%s)"; fi; }
|
|
63
|
+
|
|
64
|
+
# Dirty paths, NUL-separated, from `status -z` (a rename's old name is dropped; a deleted file is dropped).
|
|
65
|
+
_dirty(){ # -> DIRTY array
|
|
66
|
+
DIRTY=()
|
|
67
|
+
local rec st path skip=0
|
|
68
|
+
while IFS= read -r -d '' rec; do
|
|
69
|
+
if [ "$skip" = 1 ]; then skip=0; continue; fi
|
|
70
|
+
st="${rec:0:2}"; path="${rec:3}"
|
|
71
|
+
case "$st" in R*|C*) skip=1 ;; esac
|
|
72
|
+
case "$st" in *D*) continue ;; esac
|
|
73
|
+
[ -f "$path" ] && DIRTY+=("$path")
|
|
74
|
+
done < <(git -c core.fsmonitor= status --porcelain=v1 -z --untracked-files=all 2>/dev/null)
|
|
75
|
+
}
|
|
76
|
+
_hashes(){ # paths in "$@" -> HASH array, one content id per path (git hash-object, one process)
|
|
77
|
+
HASH=()
|
|
78
|
+
[ "$#" -gt 0 ] || return 0
|
|
79
|
+
local h
|
|
80
|
+
while IFS= read -r h; do HASH+=("$h"); done < <(printf '%s\n' "$@" | git hash-object --stdin-paths 2>/dev/null)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if [ "$EV" = UserPromptSubmit ]; then
|
|
84
|
+
HEAD="$(git rev-parse -q --verify HEAD 2>/dev/null)"
|
|
85
|
+
_dirty; _hashes ${DIRTY[@]+"${DIRTY[@]}"}; _now
|
|
86
|
+
mkdir -p "$SNAPD" 2>/dev/null || exit 0
|
|
87
|
+
{
|
|
88
|
+
printf 'head\t%s\ntime\t%s\n' "${HEAD:--}" "$NOW"
|
|
89
|
+
i=0; while [ "$i" -lt "${#DIRTY[@]}" ]; do printf 'f\t%s\t%s\n' "${HASH[$i]:--}" "${DIRTY[$i]}"; i=$((i+1)); done
|
|
90
|
+
} > "$SNAP.tmp" 2>/dev/null && mv -f "$SNAP.tmp" "$SNAP" 2>/dev/null
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
[ "$EV" = Stop ] || exit 0
|
|
95
|
+
case "$IN" in *'"stop_hook_active":true'*|*'"stop_hook_active": true'*) exit 0 ;; esac
|
|
96
|
+
[ -f "$SNAP" ] || exit 0
|
|
97
|
+
|
|
98
|
+
# ---- the turn's files -----------------------------------------------------------------------------------------
|
|
99
|
+
# No associative arrays: macOS ships bash 3.2. The snapshot's dirty files are one newline-framed string of
|
|
100
|
+
# "<content id> <path>" lines, and a lookup is one `case`.
|
|
101
|
+
SH="-"; ST=0; BEFORE=$'\n'
|
|
102
|
+
while IFS=$'\t' read -r k a b; do
|
|
103
|
+
case "$k" in head) SH="$a" ;; time) ST="$a" ;; f) BEFORE="$BEFORE$a $b"$'\n' ;; esac
|
|
104
|
+
done < "$SNAP"
|
|
105
|
+
rm -f "$SNAP" 2>/dev/null
|
|
106
|
+
case "$ST" in ''|*[!0-9]*) ST=0 ;; esac
|
|
107
|
+
|
|
108
|
+
SEEN=$'\n'; FILES=()
|
|
109
|
+
_add(){
|
|
110
|
+
[ -n "$1" ] && [ -f "$1" ] || return 0
|
|
111
|
+
case "$SEEN" in *$'\n'"$1"$'\n'*) return 0 ;; esac
|
|
112
|
+
SEEN="$SEEN$1"$'\n'; FILES+=("$1")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
_dirty; _hashes ${DIRTY[@]+"${DIRTY[@]}"}
|
|
116
|
+
i=0; while [ "$i" -lt "${#DIRTY[@]}" ]; do
|
|
117
|
+
p="${DIRTY[$i]}"
|
|
118
|
+
case "$BEFORE" in *$'\n'"${HASH[$i]:-x} $p"$'\n'*) ;; *) _add "$p" ;; esac
|
|
119
|
+
i=$((i+1))
|
|
120
|
+
done
|
|
121
|
+
|
|
122
|
+
HEAD="$(git rev-parse -q --verify HEAD 2>/dev/null)"
|
|
123
|
+
if [ -n "$HEAD" ] && [ "$SH" != - ] && [ "$HEAD" != "$SH" ]; then
|
|
124
|
+
# Only a turn whose every move of HEAD was a commit of its own counts its commits.
|
|
125
|
+
OURS=1; N=0
|
|
126
|
+
while IFS=$'\t' read -r sel subj; do
|
|
127
|
+
t="${sel#*@\{}"; t="${t%\}}"; case "$t" in *[!0-9]*|'') continue ;; esac
|
|
128
|
+
[ "$t" -ge "$ST" ] || break
|
|
129
|
+
N=$((N+1))
|
|
130
|
+
case "$subj" in "commit:"*|"commit (amend):"*|"commit (initial):"*|"cherry-pick:"*|"revert:"*) ;; *) OURS=0 ;; esac
|
|
131
|
+
done < <(git log -g --date=unix --format='%gd%x09%gs' -n 50 HEAD 2>/dev/null)
|
|
132
|
+
if [ "$OURS" = 1 ] && [ "$N" -gt 0 ]; then
|
|
133
|
+
while IFS= read -r -d '' p; do _add "$p"; done < <(git -c core.fsmonitor= diff --name-only -z --no-renames --diff-filter=d --no-ext-diff "$SH" "$HEAD" -- 2>/dev/null)
|
|
134
|
+
fi
|
|
135
|
+
fi
|
|
136
|
+
[ "${#FILES[@]}" -gt 0 ] || exit 0
|
|
137
|
+
|
|
138
|
+
# ---- the command ---------------------------------------------------------------------------------------------
|
|
139
|
+
CMD="$FC"
|
|
140
|
+
case "$FC" in *'<file>'*)
|
|
141
|
+
Q=""; n=0
|
|
142
|
+
for p in "${FILES[@]}"; do
|
|
143
|
+
case "$p" in *"'"*|*[\;\&\|\<\>\`\$]*|-*) continue ;; esac
|
|
144
|
+
Q="${Q:+$Q }'$p'"; n=$((n+1)); [ "$n" -ge 50 ] && break
|
|
145
|
+
done
|
|
146
|
+
[ -n "$Q" ] || exit 0
|
|
147
|
+
CMD="${FC//<file>/$Q}" ;;
|
|
148
|
+
esac
|
|
149
|
+
|
|
150
|
+
[ -f "$_TC_HERE/guard-agent-model.sh" ] || exit 0
|
|
151
|
+
. "$_TC_HERE/guard-agent-model.sh"
|
|
152
|
+
declare -F _am_verify_ok >/dev/null 2>&1 || exit 0
|
|
153
|
+
_am_state
|
|
154
|
+
_field permission_mode; PM="${_F:-default}"
|
|
155
|
+
if ! _am_verify_ok "$CMD" "$PM"; then
|
|
156
|
+
# Said once per session: a check that never runs must not look like one that passes.
|
|
157
|
+
if mkdir -p "$SNAPD" 2>/dev/null && [ ! -e "$SNAPD/$SID.said" ] && : > "$SNAPD/$SID.said" 2>/dev/null; then
|
|
158
|
+
W="${_AMVW//\\/\\\\}"; W="${W//\"/\\\"}"
|
|
159
|
+
printf '{"systemMessage":"crew: the fast check of this turn was not run: %s. CLAUDE.md declares it under Fast check (files)."}\n' "$W"
|
|
160
|
+
fi
|
|
161
|
+
exit 0
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
LIM="${CREW_TURN_CHECK_TIMEOUT:-120}"; case "$LIM" in ''|*[!0-9]*) LIM=120 ;; esac; [ "$LIM" -le 170 ] || LIM=170
|
|
165
|
+
OUTF="$SNAPD/$SID.out"; mkdir -p "$SNAPD" 2>/dev/null || exit 0
|
|
166
|
+
if command -v setsid >/dev/null 2>&1; then ( exec setsid bash -c "$CMD" ) > "$OUTF" 2>&1 </dev/null &
|
|
167
|
+
else set -m; ( exec bash -c "$CMD" ) > "$OUTF" 2>&1 </dev/null & set +m; fi
|
|
168
|
+
VPID=$!; W=0; RC=""
|
|
169
|
+
while kill -0 "$VPID" 2>/dev/null; do
|
|
170
|
+
if [ "$W" -ge "$LIM" ]; then
|
|
171
|
+
kill -TERM -- "-$VPID" 2>/dev/null; kill -TERM "$VPID" 2>/dev/null; sleep 1
|
|
172
|
+
kill -KILL -- "-$VPID" 2>/dev/null; kill -KILL "$VPID" 2>/dev/null; RC=timeout; break
|
|
173
|
+
fi
|
|
174
|
+
sleep 1; W=$((W+1))
|
|
175
|
+
done
|
|
176
|
+
if [ "$RC" = timeout ]; then
|
|
177
|
+
wait "$VPID" 2>/dev/null; rm -f "$OUTF" 2>/dev/null
|
|
178
|
+
printf '{"systemMessage":"crew: the fast check of this turn did not finish in %ss and was stopped; nothing is concluded from it."}\n' "$LIM"
|
|
179
|
+
exit 0
|
|
180
|
+
fi
|
|
181
|
+
wait "$VPID" 2>/dev/null; RC=$?
|
|
182
|
+
TAIL="$(tail -n 20 "$OUTF" 2>/dev/null | cut -c1-300)"; rm -f "$OUTF" 2>/dev/null
|
|
183
|
+
[ "$RC" = 0 ] && exit 0
|
|
184
|
+
{
|
|
185
|
+
printf 'The fast check this project declares failed on the files this turn changed (exit %s): %s\n' "$RC" "$CMD"
|
|
186
|
+
printf 'The end of its output:\n%s\n' "$TAIL"
|
|
187
|
+
printf 'Fix what it reports in those files, or say to the user why it stays red. This is said once per turn.\n'
|
|
188
|
+
} >&2
|
|
189
|
+
trap - EXIT
|
|
190
|
+
exit 2
|
package/kit/owned-blobs.tsv
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Generated by packaging/gen-legacy-blobs.sh from the final release tags v1.0.0..v3.
|
|
1
|
+
# Generated by packaging/gen-legacy-blobs.sh from the final release tags v1.0.0..v3.4.0 (below VERSION 3.5.0) — do not edit by hand.
|
|
2
2
|
# The flat files Crewforth owns: a copy whose bytes (CR aside) are one of these ids was never edited.
|
|
3
3
|
# install_path blob_sha1 first_tag last_tag
|
|
4
4
|
.claude/AGENT_TEMPLATE.md 075e2c26706c06f56af01652cd24c36918513bee v3.0.0 v3.0.4
|
|
5
5
|
.claude/AGENT_TEMPLATE.md 2c8223d811245e3627a3011bb64fb9e4b0bf3b94 v3.2.0 v3.2.1
|
|
6
6
|
.claude/AGENT_TEMPLATE.md 3fdc551d221ee3665b21ef8cc3563de861a9abe6 v1.4.0 v1.6.3
|
|
7
|
-
.claude/AGENT_TEMPLATE.md 59d3af63b0480696dec38136e124bd582066fb16 v3.3.0 v3.
|
|
7
|
+
.claude/AGENT_TEMPLATE.md 59d3af63b0480696dec38136e124bd582066fb16 v3.3.0 v3.4.0
|
|
8
8
|
.claude/AGENT_TEMPLATE.md 686fed306c873b45c245c77a95a8e6de6ee383d5 v1.7.0 v2.11.0
|
|
9
9
|
.claude/AGENT_TEMPLATE.md 7ab19417b6ff3ac732e2c894be17ffb3ec091f7d v3.1.0 v3.1.1
|
|
10
10
|
.claude/AGENT_TEMPLATE.md 981df9ea27b6f1c5ab5cb509d40875a07603858d v2.12.0 v2.13.0
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
.claude/DISCIPLINE.md b7e1066451a8590e0a2607ad0a6fe80b21599c53 v1.1.3 v1.1.8
|
|
31
31
|
.claude/DISCIPLINE.md b90a067ee323a08146862b889293f8feb49f8e7e v3.1.0 v3.1.1
|
|
32
32
|
.claude/DISCIPLINE.md bf062c2b51a9cf3cc4d0caac389c148cf6fdc977 v2.12.0 v2.12.0
|
|
33
|
-
.claude/DISCIPLINE.md c04f53866b84c54aa0e60089cb54cdc7c9aa8df1 v3.3.0 v3.
|
|
33
|
+
.claude/DISCIPLINE.md c04f53866b84c54aa0e60089cb54cdc7c9aa8df1 v3.3.0 v3.4.0
|
|
34
34
|
.claude/DISCIPLINE.md c693f1bd5832ce570058c6eedcacb5dff13d89e8 v1.10.0 v1.10.0
|
|
35
35
|
.claude/DISCIPLINE.md cf881a5229d85850194a98b8d0d0438a8eaaa9d6 v3.2.0 v3.2.1
|
|
36
36
|
.claude/DISCIPLINE.md d63a01a183c9a80d739c840a511d338dc2765820 v1.4.0 v1.4.4
|
|
@@ -47,4 +47,4 @@
|
|
|
47
47
|
.claude/README.md 97d4dd1f7f1695052cc1d06cc43f5bcdc603c989 v1.0.0 v1.0.4
|
|
48
48
|
.claude/README.md a3419891cfda7385564008e4befc1007bc5364cb v1.1.1 v1.7.0
|
|
49
49
|
.claude/README.md a3b76e0fa31267610d28b742c122a962f6db59c4 v3.0.1 v3.0.4
|
|
50
|
-
.claude/README.md b6c9b57a67d25b7e4781e4eb61ff9637e6abe5ef v3.1.0 v3.
|
|
50
|
+
.claude/README.md b6c9b57a67d25b7e4781e4eb61ff9637e6abe5ef v3.1.0 v3.4.0
|
package/kit/settings.json
CHANGED
|
@@ -152,6 +152,12 @@
|
|
|
152
152
|
"shell": "bash",
|
|
153
153
|
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/route-hint.sh",
|
|
154
154
|
"timeout": 60
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"type": "command",
|
|
158
|
+
"shell": "bash",
|
|
159
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/turn-check.sh",
|
|
160
|
+
"timeout": 30
|
|
155
161
|
}
|
|
156
162
|
]
|
|
157
163
|
}
|
|
@@ -170,6 +176,18 @@
|
|
|
170
176
|
"shell": "bash",
|
|
171
177
|
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/prompt-approval.sh",
|
|
172
178
|
"timeout": 60
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"type": "command",
|
|
182
|
+
"shell": "bash",
|
|
183
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/map-stale.sh",
|
|
184
|
+
"timeout": 30
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"type": "command",
|
|
188
|
+
"shell": "bash",
|
|
189
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/turn-check.sh",
|
|
190
|
+
"timeout": 180
|
|
173
191
|
}
|
|
174
192
|
]
|
|
175
193
|
}
|
|
@@ -242,6 +260,41 @@
|
|
|
242
260
|
"timeout": 60
|
|
243
261
|
}
|
|
244
262
|
]
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"matcher": "Bash|Edit|Write|MultiEdit|NotebookEdit",
|
|
266
|
+
"hooks": [
|
|
267
|
+
{
|
|
268
|
+
"type": "command",
|
|
269
|
+
"shell": "bash",
|
|
270
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/loop-guard.sh",
|
|
271
|
+
"timeout": 30
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"matcher": "Edit|Write|MultiEdit|NotebookEdit",
|
|
277
|
+
"hooks": [
|
|
278
|
+
{
|
|
279
|
+
"type": "command",
|
|
280
|
+
"shell": "bash",
|
|
281
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/map-stale.sh",
|
|
282
|
+
"timeout": 30
|
|
283
|
+
}
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
],
|
|
287
|
+
"PostToolUseFailure": [
|
|
288
|
+
{
|
|
289
|
+
"matcher": "Bash|Edit|Write|MultiEdit|NotebookEdit",
|
|
290
|
+
"hooks": [
|
|
291
|
+
{
|
|
292
|
+
"type": "command",
|
|
293
|
+
"shell": "bash",
|
|
294
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/loop-guard.sh",
|
|
295
|
+
"timeout": 30
|
|
296
|
+
}
|
|
297
|
+
]
|
|
245
298
|
}
|
|
246
299
|
],
|
|
247
300
|
"PreCompact": [
|
|
@@ -291,6 +344,17 @@
|
|
|
291
344
|
}
|
|
292
345
|
]
|
|
293
346
|
},
|
|
347
|
+
{
|
|
348
|
+
"matcher": "startup|resume",
|
|
349
|
+
"hooks": [
|
|
350
|
+
{
|
|
351
|
+
"type": "command",
|
|
352
|
+
"shell": "bash",
|
|
353
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/branch-fresh.sh",
|
|
354
|
+
"timeout": 30
|
|
355
|
+
}
|
|
356
|
+
]
|
|
357
|
+
},
|
|
294
358
|
{
|
|
295
359
|
"matcher": "startup",
|
|
296
360
|
"hooks": [
|
package/kit/skills/a11y/SKILL.md
CHANGED
|
@@ -8,9 +8,9 @@ description: |
|
|
|
8
8
|
# Accessibility (a11y)
|
|
9
9
|
|
|
10
10
|
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
11
|
-
Trigger phrases: "a11y", "accessibility", "WCAG", "screen reader", "keyboard navigation", "contrast", "ARIA", "keyboard only", "with a keyboard", "keyboard focus", "without a mouse", "focus ring", "tab order", "alt text", "color blind", "colour blind", "voiceover", "talkback", "reduced motion"
|
|
11
|
+
Trigger phrases: "a11y", "accessibility", "WCAG", "screen reader", "keyboard navigation", "contrast", "ARIA", "keyboard only", "with a keyboard", "keyboard focus", "without a mouse", "focus ring", "tab order", "alt text", "color blind", "colour blind", "voiceover", "talkback", "reduced motion", "target size", "focus not obscured", "focus is hidden", "WCAG 2.2"
|
|
12
12
|
|
|
13
|
-
Goal: make the interface usable by **everyone**, including keyboard, screen reader, and low vision. Baseline target: **WCAG 2.
|
|
13
|
+
Goal: make the interface usable by **everyone**, including keyboard, screen reader, and low vision. Baseline target: **WCAG 2.2 AA**.
|
|
14
14
|
Stack-agnostic (web/React/RN); do a web search when needed for framework-specific APIs.
|
|
15
15
|
|
|
16
16
|
## Checklist
|
|
@@ -21,9 +21,56 @@ Stack-agnostic (web/React/RN); do a web search when needed for framework-specifi
|
|
|
21
21
|
- [ ] **Alt text**: `alt` on meaningful images; `alt=""` on decorative images
|
|
22
22
|
- [ ] **Forms**: every input has a `label`; error message programmatically linked (`aria-describedby`)
|
|
23
23
|
- [ ] **ARIA**: only when needed; wrong ARIA is worse than no ARIA; role/name/state correct
|
|
24
|
-
- [ ] **Motion/animation**: respect `prefers-reduced-motion
|
|
24
|
+
- [ ] **Motion/animation**: respect `prefers-reduced-motion`; anything that moves for more than 5 s can be paused (2.2.2)
|
|
25
25
|
- [ ] **Language**: `<html lang>` correct (coordinate with i18n)
|
|
26
26
|
|
|
27
|
+
WCAG 2.2 added these; a 2.1 checklist misses all of them:
|
|
28
|
+
- [ ] **Focus not obscured** (2.4.11): a focused element is never fully hidden under a sticky header, footer or banner
|
|
29
|
+
- [ ] **Dragging** (2.5.7): every drag has a single-pointer alternative (buttons, a menu)
|
|
30
|
+
- [ ] **Target size** (2.5.8): pointer targets ≥ 24×24 CSS px, or spaced so a 24 px circle on each does not overlap another
|
|
31
|
+
- [ ] **Redundant entry** (3.3.7): information already given in the same flow is filled in or selectable, not asked again
|
|
32
|
+
- [ ] **Accessible authentication** (3.3.8): no memory or puzzle test to sign in without an alternative; paste and
|
|
33
|
+
password managers work
|
|
34
|
+
|
|
35
|
+
Commonly missed, all checkable in the markup:
|
|
36
|
+
- [ ] **Autocomplete** (1.3.5): personal-data inputs carry the right `autocomplete` token
|
|
37
|
+
- [ ] **No paste blocking** on any input, passwords and confirmations included
|
|
38
|
+
- [ ] **Submit stays enabled**: a disabled-until-valid button hides why; validate on submit and say what is wrong
|
|
39
|
+
- [ ] **Zoom and reflow** (1.4.4, 1.4.10): usable at 200% text and at 320 CSS px wide; no `maximum-scale=1` or
|
|
40
|
+
`user-scalable=no` in the viewport meta
|
|
41
|
+
- [ ] **Skip link** (2.4.1): the first focusable element jumps past repeated navigation to `main`
|
|
42
|
+
- [ ] **No flashing** (2.3.1): nothing flashes more than three times a second
|
|
43
|
+
- [ ] **Error summary**: a form that fails lists its errors in one focusable block that links to each field
|
|
44
|
+
- [ ] **No positive `tabindex`**: `0` and `-1` only; order comes from the DOM
|
|
45
|
+
|
|
46
|
+
**Criteria, not conventions.** A failed A or AA criterion, or a task a keyboard or screen-reader user cannot finish,
|
|
47
|
+
is a finding. AAA, an ARIA Authoring Practices pattern, or a 44 px target where 24 px passes is a recommendation:
|
|
48
|
+
say so, and never rank it with a finding.
|
|
49
|
+
|
|
50
|
+
## Before you finish: grep for these
|
|
51
|
+
| Pattern in the diff | Why it fails |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `outline: none` / `outline: 0` with no `:focus-visible` rule | focus becomes invisible (2.4.7) |
|
|
54
|
+
| `<div onClick` / `<span onClick` with no role and key handler | not reachable by keyboard (2.1.1) |
|
|
55
|
+
| `tabindex="1"` or higher | breaks the reading order (2.4.3) |
|
|
56
|
+
| `disabled={!isValid}` on a submit | hides why the form cannot be sent |
|
|
57
|
+
| a live region rendered together with its text | many readers announce nothing: render it empty first |
|
|
58
|
+
| `maximum-scale=1`, `user-scalable=no` | blocks zoom (1.4.4) |
|
|
59
|
+
| `aria-hidden="true"` on an element that holds something focusable | focus lands on nothing the reader can name |
|
|
60
|
+
| `<img` with no `alt` | no text alternative (1.1.1) |
|
|
61
|
+
| `onDrag`/`draggable` with no button or menu path | no single-pointer alternative (2.5.7) |
|
|
62
|
+
|
|
63
|
+
## Symptom → cause → fix
|
|
64
|
+
| Symptom | Usual cause | Fix |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| Reader says "button" and nothing else | icon-only button with no name | `aria-label`, or visually hidden text |
|
|
67
|
+
| Tab skips an element | a `div` handling clicks | a real `button` / `a` |
|
|
68
|
+
| Focus lost after a modal closes | focus not returned | return it to the element that opened the modal |
|
|
69
|
+
| An error appears and nothing is announced | message not tied to the field or a live region | `aria-describedby` on the field; a status region for the summary |
|
|
70
|
+
| Layout breaks at 200% zoom | fixed heights or widths in px | let text boxes grow; `min-width: 0` on flex and grid children |
|
|
71
|
+
|
|
72
|
+
Recipes for focus, forms and announcements (which channel says what, in order): `references/recipes.md`.
|
|
73
|
+
|
|
27
74
|
## How
|
|
28
75
|
1. **Start with semantics** — the right element solves 80%. Use `button` instead of `role="button"`+`div`.
|
|
29
76
|
2. **Navigate with the keyboard** — drop the mouse, run the whole flow with Tab/Shift-Tab/Enter/Escape; is focus visible and its order sensible.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Accessibility recipes: focus, forms, announcements
|
|
2
|
+
|
|
3
|
+
Loaded from the `a11y` skill when a change moves focus, validates a form or tells the user something happened.
|
|
4
|
+
Stack-neutral; the React Native and Flutter equivalents are named in the skill.
|
|
5
|
+
|
|
6
|
+
## Focus
|
|
7
|
+
- **Where focus goes after an action.** Opening a dialog: to its first meaningful control, or to the dialog itself
|
|
8
|
+
when it starts with text. Closing it: back to the control that opened it. Deleting the focused row: to the next
|
|
9
|
+
row, or the previous one when it was the last. A route change: to the new page's `h1` (`tabindex="-1"`), or to a
|
|
10
|
+
skip target, so the reader starts at the content and not at the top of the navigation.
|
|
11
|
+
- **Visible focus.** Style `:focus-visible`, never remove `outline` without a replacement of at least 2 px with 3:1
|
|
12
|
+
contrast against what surrounds it. A sticky header must not cover the focused element: set `scroll-padding-top`
|
|
13
|
+
to its height.
|
|
14
|
+
- **Traps.** Only a modal traps focus, and Escape always leaves it. `inert` on the background is the simplest trap
|
|
15
|
+
where it is supported.
|
|
16
|
+
|
|
17
|
+
## Forms
|
|
18
|
+
- Every field has a visible `label` tied by `for`/`id`; a placeholder is not a label.
|
|
19
|
+
- Validate on submit (and on blur for a field the user has left), not on every keystroke.
|
|
20
|
+
- On a failed submit: put a summary at the top that names how many errors there are and links to each field, move
|
|
21
|
+
focus to it, and tie each field's message to the field with `aria-describedby` and `aria-invalid="true"`.
|
|
22
|
+
- Keep the submit button enabled; a disabled button cannot be focused and does not say what is missing.
|
|
23
|
+
- Required fields say so in text, not only with a colour or an asterisk the reader skips.
|
|
24
|
+
- Known personal data carries its `autocomplete` token (`name`, `email`, `tel`, `street-address`, `one-time-code`,
|
|
25
|
+
`current-password`, `new-password`).
|
|
26
|
+
|
|
27
|
+
## Announcements: the cheapest channel that works
|
|
28
|
+
Use the first rung that fits; each one further down interrupts more.
|
|
29
|
+
1. **Move focus** when the user must act next (a dialog opened, an error summary).
|
|
30
|
+
2. **`aria-describedby`** when the message belongs to one control (a field hint, a field error).
|
|
31
|
+
3. **`role="status"`** (polite) for a result the user should hear without being interrupted: "Saved", "3 results".
|
|
32
|
+
4. **`role="alert"`** (assertive) only for something that cannot wait: a session about to expire, a failed payment.
|
|
33
|
+
|
|
34
|
+
Rules that make the live regions work:
|
|
35
|
+
- Render the region **empty** when the page loads and change its text later. A region that arrives together with its
|
|
36
|
+
text is often not announced.
|
|
37
|
+
- One polite region per page is usually enough; reuse it instead of adding one per component.
|
|
38
|
+
- Do not announce what focus already says. Moving focus to an error summary and also putting it in an alert reads it
|
|
39
|
+
twice.
|
|
40
|
+
- Toasts that disappear on their own need a status region and enough time to read (at least 5 s, longer for longer
|
|
41
|
+
text), or a way to bring them back.
|
package/kit/skills/adr/SKILL.md
CHANGED
|
@@ -34,7 +34,14 @@ every teammate's next session opening, and it travels even when the board lives
|
|
|
34
34
|
file stays the long form; the board entry is the part that has to reach people, so it must be readable on its
|
|
35
35
|
own — a title nobody can act on ("auth decided") is not a record.
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## Where it goes: the project's scheme first
|
|
38
|
+
Before writing, look for records that already exist: a decisions folder (`docs/adr/`, `doc/adr/`,
|
|
39
|
+
`docs/decisions/`, `Documentation/Decisions/`, `architecture/decisions/`), an `.adr-dir` file (adr-tools), or files
|
|
40
|
+
named like `ADR-004-…` or `0004-…`. Found: continue it, same folder, extension (.md, .rst, .adoc), numbering and
|
|
41
|
+
heading style, next number. Not found: `docs/adr/NNNN-short-title.md` below. Never start a second scheme beside one
|
|
42
|
+
that exists; two numbering lines make every "see ADR 12" ambiguous.
|
|
43
|
+
|
|
44
|
+
## Format (fallback: docs/adr/NNNN-short-title.md, ~1 page)
|
|
38
45
|
```
|
|
39
46
|
# NNNN. <Decision title>
|
|
40
47
|
Status: proposed | accepted | rejected | superseded (by NNNN)
|
|
@@ -46,11 +53,15 @@ What was decided (clear, one sentence + rationale)?
|
|
|
46
53
|
Pros / cons / accepted trade-offs.
|
|
47
54
|
## Alternatives considered
|
|
48
55
|
Why were they not chosen?
|
|
56
|
+
Verified against: <package>@<version> · <docs or source> · <date> — or "none" (no external fact relied on)
|
|
49
57
|
```
|
|
50
58
|
|
|
51
59
|
## Principles
|
|
52
60
|
- **Invariant:** a new decision marks the old ADR as `superseded`; an ADR is **never deleted** (decision history is preserved).
|
|
53
61
|
- Numbered and dated; keep it short.
|
|
62
|
+
- **A number a test enforces moves with its ADR.** When a test or gate pins a figure an ADR set (a cap, a count, a
|
|
63
|
+
budget), the change that moves the figure amends that ADR in the same commit; a figure that drifts from its record
|
|
64
|
+
leaves the next reader two answers.
|
|
54
65
|
|
|
55
66
|
## Deliberately bypassing a rule is a decision too
|
|
56
67
|
A gate the user knowingly overrode — a deferred DoD item, an accepted known gap, a trimmed scope — belongs here
|
|
@@ -67,7 +78,8 @@ Record it in the same file as any other decision, with the fields that make it r
|
|
|
67
78
|
```
|
|
68
79
|
|
|
69
80
|
`revisit` is the load-bearing field. A bypass with no condition attached is permanent by default, and nobody
|
|
70
|
-
chose permanence. See [[confidence-check]], which is where most bypasses surface.
|
|
81
|
+
chose permanence. See [[confidence-check]], which is where most bypasses surface. A recorded bypass is the user's decision, not a
|
|
82
|
+
deferral (`CLAUDE.md`, "No deferral").
|
|
71
83
|
|
|
72
84
|
## DoD
|
|
73
85
|
- Decision + rationale + rejected alternatives recorded; status current.
|
|
@@ -20,7 +20,8 @@ public API is a commitment; a breaking change is expensive. Stack-agnostic (REST
|
|
|
20
20
|
- [ ] A clear **versioning** strategy (URL `/v1` or header); a breaking change means a new version
|
|
21
21
|
- [ ] **Pagination/filtering/sorting** defined and consistent on large collections
|
|
22
22
|
- [ ] **Backward compatibility**: adding a field is additive; removing a field or changing its meaning is breaking → version
|
|
23
|
-
- [ ] **Idempotency** (for POST/payment-like cases) supported via a key when needed
|
|
23
|
+
- [ ] **Idempotency** (for POST/payment-like cases) supported via a key when needed: the key, its atomic claim and
|
|
24
|
+
the answers to a replay are in **`references/idempotency.md`**
|
|
24
25
|
- [ ] The contract is documented in **OpenAPI**; example request/response present (coordinate with `docs-writer`)
|
|
25
26
|
|
|
26
27
|
## How
|
|
@@ -42,6 +43,10 @@ public API is a commitment; a breaking change is expensive. Stack-agnostic (REST
|
|
|
42
43
|
| A new optional parameter | Add a required parameter |
|
|
43
44
|
| A new enum value (if the consumer is tolerant) | Change a type/meaning, change a status code |
|
|
44
45
|
|
|
46
|
+
**Observable behaviour is the contract,** not only the documented fields: someone depends on the error text, the
|
|
47
|
+
order of a list, the default page size and how long a call takes. Changing one of those is breaking for them; say
|
|
48
|
+
it in the changelog like a field change.
|
|
49
|
+
|
|
45
50
|
## Invariant rules
|
|
46
51
|
1. **A public API is a commitment** — a breaking change is not made silently; version + announcement.
|
|
47
52
|
2. **Consistency > local cleverness** — a single naming/error/pagination pattern across the whole API.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Idempotency keys
|
|
2
|
+
|
|
3
|
+
A retried POST that charges twice, ships twice or creates two rows is the bug this prevents. The key makes a retry
|
|
4
|
+
safe; the store behind it decides whether it really is.
|
|
5
|
+
|
|
6
|
+
## The key
|
|
7
|
+
- **From the intent, not the request.** The client sends `Idempotency-Key` (a UUID made once per user action, kept
|
|
8
|
+
across its retries), or the server derives one from what the action means (order id + operation). A key made per
|
|
9
|
+
HTTP attempt protects nothing: every retry is new.
|
|
10
|
+
- **Scoped to the caller.** Store it per account or API client, so two callers cannot collide or read each other's
|
|
11
|
+
result.
|
|
12
|
+
|
|
13
|
+
## Claiming it
|
|
14
|
+
- **Atomically.** Insert the key under a unique constraint (or an equivalent conditional write) before the side
|
|
15
|
+
effect runs. "Look it up, then insert" leaves a window where two retries both find nothing and both charge.
|
|
16
|
+
- **With a fingerprint of the payload.** Store a hash of the normalised body with the key.
|
|
17
|
+
|
|
18
|
+
## Answers
|
|
19
|
+
| The request | The answer |
|
|
20
|
+
|---|---|
|
|
21
|
+
| New key | Run it; store the outcome with the key |
|
|
22
|
+
| Same key, same payload, finished | The stored response, status code included, without running it again |
|
|
23
|
+
| Same key, different payload | `422` (or `409`): a client bug, never a silent replay of the first result |
|
|
24
|
+
| Same key, first attempt still running | `409` with `Retry-After`, or wait briefly, or `202` with a status URL; never a second run |
|
|
25
|
+
|
|
26
|
+
Three outcomes are stored: **succeeded** (replay it), **failed in a way the client can fix** (replay the 4xx), and
|
|
27
|
+
**failed before or during the side effect** (5xx, timeout): release the key, or mark it retryable, so the next
|
|
28
|
+
retry can run. Storing a 5xx as final turns a passing outage into a permanent failure for that action.
|
|
29
|
+
|
|
30
|
+
## Retention
|
|
31
|
+
Keep a key longer than the longest retry chain any client runs (its backoff schedule and the queue's redelivery
|
|
32
|
+
window), and say how long in the API docs. A key dropped after one hour is useless to a client that retries for a
|
|
33
|
+
day.
|
|
34
|
+
|
|
35
|
+
## Review checks
|
|
36
|
+
- The side effect and the key claim are in one transaction, or the key is claimed first and the effect is
|
|
37
|
+
conditional on it.
|
|
38
|
+
- A downstream call (payment provider, email) gets its own idempotency key, derived from ours.
|
|
39
|
+
- A test sends the same key twice in parallel and finds one effect.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: brainstorm
|
|
3
3
|
description: |
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Use when the ask is still fuzzy and no direction is chosen yet: "not sure what we want", several ways to go,
|
|
5
|
+
or a scope to settle before any plan is written.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Brainstorm — diverge before you converge
|
|
@@ -27,6 +27,8 @@ option into tasks). If scope is already clear, skip this and go straight to [[sp
|
|
|
27
27
|
4. **Converge — ask, don't guess.** Present the directions as **explicit numbered options** and let the user
|
|
28
28
|
choose (CLAUDE.md discipline: a decision point = multiple choice, never a prose either/or). Record the
|
|
29
29
|
chosen direction + the rejected ones with the one-line reason they lost.
|
|
30
|
+
How to put a question, what is worth asking and what to do when no answer comes:
|
|
31
|
+
`references/asking.md`; `/crew-interview` runs the whole round of questions as a command.
|
|
30
32
|
5. **Hand off.** The chosen direction + resolved constraints flow into `spec-planning`; a lasting
|
|
31
33
|
architectural choice made here is recorded with [[adr]].
|
|
32
34
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Asking the user — what is worth a question, and how to put it
|
|
2
|
+
|
|
3
|
+
The discipline says to ask at every decision point with `AskUserQuestion`. This file says what a decision point
|
|
4
|
+
is, and how to put one so the answer settles something.
|
|
5
|
+
|
|
6
|
+
## Facts are yours, decisions are theirs
|
|
7
|
+
- **Never ask for a fact the workspace, the docs or the web can answer**: the ORM, the test runner, the Node
|
|
8
|
+
version, where a file lives. Read it, then state it.
|
|
9
|
+
- If the options differ only in how the code does it, and a user would see the same behaviour either way, it is
|
|
10
|
+
not their decision. Pick, and say what you picked.
|
|
11
|
+
- A decision is theirs when it changes what someone sees or pays: scope, behaviour, data kept or shown, cost, risk,
|
|
12
|
+
a trade-off between two things they want.
|
|
13
|
+
|
|
14
|
+
## Is it worth asking?
|
|
15
|
+
A question earns its place when all three hold:
|
|
16
|
+
1. **The answer changes the outcome.** If every answer leads to the same next step, do not ask.
|
|
17
|
+
2. **It is about this work**, not a principle ("do you value maintainability?").
|
|
18
|
+
3. **It is not asked already.** Never ask again what was answered in this session or recorded in the project.
|
|
19
|
+
|
|
20
|
+
Where to look for the questions that matter: the empty state, the error state, two people at once, existing data
|
|
21
|
+
when the shape changes, and the default when nobody chooses. When two things the user said pull against each
|
|
22
|
+
other, quote both and ask which wins.
|
|
23
|
+
|
|
24
|
+
## Putting the question
|
|
25
|
+
- **One `AskUserQuestion` call, at most 4 questions, and only questions that do not depend on each other.** A
|
|
26
|
+
question whose options depend on another answer waits for the next call.
|
|
27
|
+
- **Options are behaviour, not mechanics.** "A saved draft survives closing the tab" beats "use localStorage".
|
|
28
|
+
- **Recommended option first**, marked as recommended, with a one-line reason. The user should be able to agree
|
|
29
|
+
in one click.
|
|
30
|
+
- A concrete case beats an abstract choice: "a refund on an order shipped in two parts" is answerable;
|
|
31
|
+
"refund granularity" is not.
|
|
32
|
+
|
|
33
|
+
## Confirming an inference
|
|
34
|
+
When you can infer the answer, do not ask an open question: state the inference and let the user confirm it.
|
|
35
|
+
The statement is the question: "Looks right" / "Off base" (and what to change). Read the answer four ways: a
|
|
36
|
+
yes, a no, a correction (take the correction, not your inference), or a new constraint (add it, then re-check
|
|
37
|
+
what it changes).
|
|
38
|
+
|
|
39
|
+
## When the answer does not come
|
|
40
|
+
- **A skipped question becomes a recorded assumption.** Write it down where the plan or brief keeps its
|
|
41
|
+
assumptions, so it can be revisited, and go on.
|
|
42
|
+
- **A whole round skipped means stop asking.** Proceed on the recorded assumptions and say so.
|
|
43
|
+
- **"I don't know"** means the question was framed wrong. Re-ask it from what the user would see, not from how it
|
|
44
|
+
is built.
|
|
45
|
+
- **No one can answer** (`auto` mode with no person attending, `/loop`, a scheduled or headless run): do not
|
|
46
|
+
interview. Take the recommended option for each open decision, record it as an assumption, and list the
|
|
47
|
+
assumptions at the top of what you hand over.
|
|
@@ -19,9 +19,19 @@ Trigger phrases: "ci", "pipeline", "github actions", "jenkins", "build pipeline"
|
|
|
19
19
|
|
|
20
20
|
## Principles
|
|
21
21
|
- **Deterministic:** dependencies pinned, cache keyed correctly; no "it worked on my machine".
|
|
22
|
-
- **
|
|
22
|
+
- **Actions and the token:** pin every third-party action to a full commit SHA with its version in a comment (a tag
|
|
23
|
+
moves when its owner moves it); `permissions: contents: read` at the top, raised per job only where a step writes;
|
|
24
|
+
`persist-credentials: false` on checkout unless a later step pushes; never put `${{ github.event.* }}` or a matrix
|
|
25
|
+
value inside `run:` — pass it through `env:` and quote it.
|
|
26
|
+
- **Secret management:** CI secret store; NO plaintext secrets in the repo/logs (overlaps with trace scan). Log
|
|
27
|
+
masking is a filter on exact strings: an encoded, split or transformed value prints in clear, so a secret never
|
|
28
|
+
reaches a step that echoes its environment. Prefer short-lived OIDC credentials over stored keys and ephemeral
|
|
29
|
+
runners over reused ones; a cache restored into a job is input, so key it on what produced it and never on a
|
|
30
|
+
value a fork controls.
|
|
23
31
|
- **PR gate:** quality gate + tests must pass; a red build is not merged.
|
|
24
32
|
- **Branch protection:** direct push to main is disabled; PR + review required.
|
|
33
|
+
- **An agent job in CI** (one that fixes, updates or triages with nobody watching): pinned, few tools, an isolated
|
|
34
|
+
copy, an allowlist for what comes back: **`references/agent-job.md`**.
|
|
25
35
|
|
|
26
36
|
## When a stage fails
|
|
27
37
|
1. **Fetch the failing job's log first** — `gh run view --log-failed`, `glab ci trace`, `jenkins-cli console <job>`,
|