codebyplan 1.13.43 → 1.13.45
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/dist/cli.js +5079 -1556
- package/package.json +1 -1
- package/templates/agents/cbp-task-check.md +1 -3
- package/templates/agents/cbp-task-planner.md +8 -6
- package/templates/github-workflows/publish.yml +93 -21
- package/templates/hooks/cbp-auto-test-hooks.sh +1 -0
- package/templates/hooks/cbp-e2e-spec-patterns.sh +100 -0
- package/templates/hooks/cbp-lint-format-on-edit.sh +1 -0
- package/templates/hooks/cbp-maestro-yaml-validate.sh +1 -0
- package/templates/hooks/cbp-pre-commit-quality-gate.sh +1 -0
- package/templates/hooks/cbp-statusline.sh +0 -0
- package/templates/hooks/cbp-subagent-statusline.sh +0 -0
- package/templates/hooks/cbp-test-coverage-gate.sh +1 -0
- package/templates/hooks/cbp-test-hooks.sh +1 -0
- package/templates/hooks/hooks.json +4 -0
- package/templates/hooks/verify-parity.sh +20 -0
- package/templates/rules/parallel-waves.md +8 -3
- package/templates/rules/scope-vocabulary.md +4 -3
- package/templates/settings.project.base.json +22 -0
- package/templates/skills/cbp-build-cc-claude-file/SKILL.md +11 -1
- package/templates/skills/cbp-build-cc-claude-file/scripts/validate-claude-file.sh +72 -0
- package/templates/skills/cbp-build-cc-mode/SKILL.md +12 -16
- package/templates/skills/cbp-build-cc-rule/SKILL.md +11 -1
- package/templates/skills/cbp-build-cc-rule/scripts/validate-rule.sh +69 -0
- package/templates/skills/cbp-build-cc-settings/SKILL.md +2 -2
- package/templates/skills/cbp-build-cc-settings/scripts/validate-settings.sh +67 -0
- package/templates/skills/cbp-checkpoint-create/SKILL.md +12 -4
- package/templates/skills/cbp-checkpoint-end/SKILL.md +19 -11
- package/templates/skills/cbp-git-commit/SKILL.md +10 -12
- package/templates/skills/cbp-git-worktree-create/SKILL.md +7 -48
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +23 -40
- package/templates/skills/cbp-map-architecture/SKILL.md +1 -0
- package/templates/skills/cbp-merge-main/SKILL.md +21 -26
- package/templates/skills/cbp-refresh-arch-map/SKILL.md +1 -0
- package/templates/skills/cbp-round-check/SKILL.md +37 -36
- package/templates/skills/cbp-round-execute/SKILL.md +9 -3
- package/templates/skills/cbp-session-end/SKILL.md +27 -47
- package/templates/skills/cbp-session-start/SKILL.md +35 -51
- package/templates/skills/cbp-standalone-task-start/SKILL.md +10 -19
- package/templates/skills/cbp-supabase-migrate/SKILL.md +24 -27
- package/templates/skills/cbp-task-start/SKILL.md +9 -21
- package/templates/skills/cbp-task-testing/SKILL.md +18 -10
package/package.json
CHANGED
|
@@ -82,9 +82,7 @@ Review all QA items across all rounds:
|
|
|
82
82
|
- **Auto items**: Verify all passed (build, lint, types, tests)
|
|
83
83
|
- **Default items**: Verify all resolved (pass or skipped with reason)
|
|
84
84
|
|
|
85
|
-
**E2E
|
|
86
|
-
|
|
87
|
-
**Committed-screenshot check**: For any round where `round.context.e2e_eligible[]` is non-empty, verify `round.context.e2e_gallery[]` is non-empty. Refuse a READY verdict when it is empty — verdict text: "E2E ran but produced zero committed screenshots — open a fix round per `rules/e2e-mandatory.md` § Committed-Screenshot Enforcement." Sole exception: when `vscode-test` is the ONLY eligible framework, an empty `e2e_gallery[]` is allowed (SD-3, behavior-only extensions).
|
|
85
|
+
**E2E deterministic gate**: For each round where `round.context.e2e_eligible[]` is non-empty, run `codebyplan e2e verify-round --round-id <round_id> --task-id <task_id>`. Exit 0 = pass. Exit 1 = hard-fail — refuse a READY verdict and surface the stdout JSON's `failed_checks[]` verbatim in the verdict text. The CLI deterministically evaluates all three e2e hard-fails that were previously judged manually: `e2e_eligible_skipped` (eligible framework with no specialist output and no valid skip reason), `zero_assertion_run` (`passed === 0 && skipped > 0` on a path touching `files_changed` — "E2E spec authored but assertions did not execute (skip-gated)"), and `empty_gallery` (eligible UI-touching run with zero committed screenshots, per `rules/e2e-mandatory.md` § Committed-Screenshot Enforcement; the sole vscode-test-only exception is honored by the CLI). On any exit-1, route to a fix round per `rules/e2e-mandatory.md`.
|
|
88
86
|
|
|
89
87
|
List any pending or failed items. Determine if they are blockers.
|
|
90
88
|
|
|
@@ -552,13 +552,15 @@ plan.waves:
|
|
|
552
552
|
|
|
553
553
|
If `files_to_modify[]` contains ≤5 files across a single app, skip decomposition and emit a single wave or omit `waves[]` entirely (single-wave default in `round-execute` handles this gracefully).
|
|
554
554
|
|
|
555
|
-
**Verification
|
|
555
|
+
**Verification** before finalising waves — invariants I (disjoint files), II (acyclic `depends_on` DAG), and III (3–15 files per wave, with the small-plan lower-bound exemption) are deterministic set/graph checks; run the validator instead of self-checking them in prose:
|
|
556
556
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
- [
|
|
557
|
+
```bash
|
|
558
|
+
printf '%s' "$PLAN_JSON" | codebyplan validate-waves --json
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
(`$PLAN_JSON` is the `{ "waves": [...] }` structure; pass a file path as the first argument instead of stdin if preferred.) Exit 0 = invariants I–III satisfied. Exit non-zero = one or more violations — the `--json` `violations[]` array names the failing invariant (`I`/`II`/`III`) and offending wave/file; fix the decomposition and re-run before emitting the plan. The validator does NOT check invariant IV (UI skill preloads) — that remains a manual step:
|
|
562
|
+
|
|
563
|
+
- [ ] UI-bearing waves have `frontend-design` + `frontend-a11y` in `skill_preloads[]` (invariant IV — not covered by `validate-waves`)
|
|
562
564
|
|
|
563
565
|
### Phase 6: Build Context Summary
|
|
564
566
|
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
#
|
|
3
3
|
# This workflow publishes the codebyplan npm package on every merge to main
|
|
4
4
|
# where the committed package.json version exceeds the version currently on npm.
|
|
5
|
-
#
|
|
6
|
-
#
|
|
5
|
+
# It also auto-publishes prerelease versions (e.g. 1.14.0-beta.1) from feat/**
|
|
6
|
+
# branches to a scoped dist-tag (e.g. --tag beta) — see the beta channel docs
|
|
7
|
+
# for the full workflow. No release PR, no conventional-commit parsing — the
|
|
8
|
+
# version committed in the feat branch is the version that ships.
|
|
7
9
|
#
|
|
8
10
|
# Two values a consuming repo must adjust:
|
|
9
11
|
# 1. paths: — set to the package directory whose package.json drives versioning
|
|
10
12
|
# (current value: 'packages/codebyplan-package/**')
|
|
11
13
|
# 2. npm view <package-name> — replace `codebyplan` with the actual package name
|
|
12
|
-
# in the "Check version vs published"
|
|
14
|
+
# in the "Check version vs published" and exact-version check steps
|
|
13
15
|
#
|
|
14
16
|
# Everything else (OIDC auth, pnpm 10.12.4, Node 20, build:npm → publish) is
|
|
15
17
|
# intentionally generic and works as-is for any single-package npm publish.
|
|
@@ -20,6 +22,7 @@ on:
|
|
|
20
22
|
push:
|
|
21
23
|
branches:
|
|
22
24
|
- main
|
|
25
|
+
- "feat/**"
|
|
23
26
|
paths:
|
|
24
27
|
- "packages/codebyplan-package/**"
|
|
25
28
|
workflow_dispatch:
|
|
@@ -40,6 +43,7 @@ jobs:
|
|
|
40
43
|
outputs:
|
|
41
44
|
should_publish: ${{ steps.check.outputs.should_publish }}
|
|
42
45
|
version: ${{ steps.check.outputs.version }}
|
|
46
|
+
dist_tag: ${{ steps.check.outputs.dist_tag }}
|
|
43
47
|
steps:
|
|
44
48
|
- name: Checkout
|
|
45
49
|
uses: actions/checkout@v4
|
|
@@ -54,27 +58,80 @@ jobs:
|
|
|
54
58
|
fi
|
|
55
59
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
56
60
|
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
# no hyphen, so any '-' marks a pre-release.
|
|
61
|
+
# Compute dist-tag from version:
|
|
62
|
+
# prerelease (contains hyphen) → extract id between '-' and first '.'
|
|
63
|
+
# e.g. 1.14.0-beta.1 → "beta"; 1.14.0-rc.1 → "rc"
|
|
64
|
+
# stable → "latest"
|
|
62
65
|
case "$VERSION" in
|
|
63
66
|
*-*)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
PRE_ID="${VERSION#*-}"
|
|
68
|
+
DIST_TAG="${PRE_ID%%.*}"
|
|
69
|
+
;;
|
|
70
|
+
*)
|
|
71
|
+
DIST_TAG="latest"
|
|
67
72
|
;;
|
|
68
73
|
esac
|
|
74
|
+
echo "dist_tag=${DIST_TAG}" >> "$GITHUB_OUTPUT"
|
|
69
75
|
|
|
70
|
-
#
|
|
76
|
+
# ── workflow_dispatch ──────────────────────────────────────────────
|
|
77
|
+
# Manual trigger: always publish (recovery / forced re-publish path).
|
|
78
|
+
# Uses the computed dist_tag so manual prerelease publishes are tagged
|
|
79
|
+
# correctly.
|
|
71
80
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
72
|
-
echo "Manual dispatch —
|
|
81
|
+
echo "Manual dispatch — will publish (dist-tag: ${DIST_TAG})."
|
|
73
82
|
echo "should_publish=true" >> "$GITHUB_OUTPUT"
|
|
74
83
|
exit 0
|
|
75
84
|
fi
|
|
76
85
|
|
|
77
|
-
#
|
|
86
|
+
# ── feat/** push ───────────────────────────────────────────────────
|
|
87
|
+
# C4 guard: three conditions must hold before publishing from feat/**:
|
|
88
|
+
# (1) prerelease-required: stable versions on feat/** are skipped.
|
|
89
|
+
# (2) exact-version check: if this exact version is already on npm, skip.
|
|
90
|
+
# (3) path filter: already enforced by on.push.paths above.
|
|
91
|
+
case "${{ github.ref }}" in
|
|
92
|
+
refs/heads/feat/*)
|
|
93
|
+
case "$VERSION" in
|
|
94
|
+
*-*)
|
|
95
|
+
# Prerelease — check if this exact version already exists on npm.
|
|
96
|
+
EXACT_OUT=$(npm view "codebyplan@${VERSION}" version 2>&1)
|
|
97
|
+
EXACT_EXIT=$?
|
|
98
|
+
if [ "$EXACT_EXIT" -eq 0 ]; then
|
|
99
|
+
echo "VERSION=${VERSION} already published — skipping (exact-version guard)."
|
|
100
|
+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
|
|
101
|
+
exit 0
|
|
102
|
+
fi
|
|
103
|
+
if echo "$EXACT_OUT" | grep -q "E404"; then
|
|
104
|
+
echo "VERSION=${VERSION} not yet published — will publish (dist-tag: ${DIST_TAG})."
|
|
105
|
+
echo "should_publish=true" >> "$GITHUB_OUTPUT"
|
|
106
|
+
exit 0
|
|
107
|
+
fi
|
|
108
|
+
# Non-E404 failure: transient registry error — abort to avoid
|
|
109
|
+
# a duplicate publish in a degraded state.
|
|
110
|
+
echo "ERROR: npm view codebyplan@${VERSION} failed (exit ${EXACT_EXIT}): ${EXACT_OUT}"
|
|
111
|
+
exit 1
|
|
112
|
+
;;
|
|
113
|
+
*)
|
|
114
|
+
# C4 prerelease-required gate: stable versions on feat/** skip.
|
|
115
|
+
echo "VERSION=${VERSION} is not a prerelease — feat/** branches require a prerelease version (e.g. run 'codebyplan bump --prerelease beta'). Skipping."
|
|
116
|
+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
|
|
117
|
+
exit 0
|
|
118
|
+
;;
|
|
119
|
+
esac
|
|
120
|
+
;;
|
|
121
|
+
esac
|
|
122
|
+
|
|
123
|
+
# ── main push ──────────────────────────────────────────────────────
|
|
124
|
+
# Prerelease versions must never auto-publish to latest from main.
|
|
125
|
+
# The version core (X.Y.Z) has no hyphen, so any '-' marks a prerelease.
|
|
126
|
+
case "$VERSION" in
|
|
127
|
+
*-*)
|
|
128
|
+
echo "VERSION=${VERSION} is a pre-release — skipping auto-publish on main (use a feat/** branch or workflow_dispatch)."
|
|
129
|
+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
|
|
130
|
+
exit 0
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
|
|
134
|
+
# Stable on main: compare committed version against the npm registry.
|
|
78
135
|
# Distinguish "never published" (E404 → first publish) from a transient
|
|
79
136
|
# registry/network error (abort rather than blindly publish).
|
|
80
137
|
NPM_OUT=$(npm view codebyplan version 2>&1)
|
|
@@ -88,7 +145,9 @@ jobs:
|
|
|
88
145
|
echo "ERROR: npm view failed (exit ${NPM_EXIT}): ${NPM_OUT}"
|
|
89
146
|
exit 1
|
|
90
147
|
fi
|
|
91
|
-
|
|
148
|
+
# Extract the first semver-looking line so a registry deprecation/advisory
|
|
149
|
+
# notice in NPM_OUT cannot corrupt the sort -V comparison below.
|
|
150
|
+
PUBLISHED=$(printf '%s\n' "$NPM_OUT" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
|
|
92
151
|
|
|
93
152
|
# sort -V: version-aware sort. If VERSION sorts AFTER PUBLISHED, it is greater.
|
|
94
153
|
GREATER=$(printf '%s\n%s\n' "$PUBLISHED" "$VERSION" | sort -V | tail -n1)
|
|
@@ -147,7 +206,7 @@ jobs:
|
|
|
147
206
|
working-directory: packages/codebyplan-package
|
|
148
207
|
run: |
|
|
149
208
|
echo "dry_run=true — skipping actual publish."
|
|
150
|
-
echo "Would publish: codebyplan@${{ needs.check-version.outputs.version }}"
|
|
209
|
+
echo "Would publish: codebyplan@${{ needs.check-version.outputs.version }} (dist-tag: ${{ needs.check-version.outputs.dist_tag }})"
|
|
151
210
|
|
|
152
211
|
# No NODE_AUTH_TOKEN: npm >= 11.5.1 exchanges the GitHub OIDC token for a
|
|
153
212
|
# short-lived publish token. Requires a Trusted Publisher configured for
|
|
@@ -158,10 +217,14 @@ jobs:
|
|
|
158
217
|
# source repository visibility: private" otherwise). This works for both
|
|
159
218
|
# public and private repos. If your source repo is PUBLIC and you want
|
|
160
219
|
# supply-chain attestation, add `--provenance` to the command below.
|
|
220
|
+
#
|
|
221
|
+
# --tag: routes stable publishes to "latest" and prerelease publishes to
|
|
222
|
+
# the prerelease id (e.g. "beta", "rc"). C1 guarantee: betas never land on
|
|
223
|
+
# "latest" because dist_tag is computed from the version string.
|
|
161
224
|
- name: Publish to npm
|
|
162
225
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.dry_run != true }}
|
|
163
226
|
working-directory: packages/codebyplan-package
|
|
164
|
-
run: npm publish --access public
|
|
227
|
+
run: npm publish --access public --tag ${{ needs.check-version.outputs.dist_tag }}
|
|
165
228
|
|
|
166
229
|
tag-and-release:
|
|
167
230
|
name: Tag + GitHub release
|
|
@@ -199,9 +262,18 @@ jobs:
|
|
|
199
262
|
if gh release view "${TAG}" > /dev/null 2>&1; then
|
|
200
263
|
echo "GitHub release ${TAG} already exists — skipping."
|
|
201
264
|
else
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
265
|
+
# C1 guard: prerelease versions get --prerelease (NOT --latest);
|
|
266
|
+
# stable versions get --latest (existing behaviour preserved).
|
|
267
|
+
if echo "${VERSION}" | grep -q "-"; then
|
|
268
|
+
gh release create "${TAG}" \
|
|
269
|
+
--title "codebyplan v${VERSION}" \
|
|
270
|
+
--notes "codebyplan v${VERSION} — published to npm: https://www.npmjs.com/package/codebyplan/v/${VERSION} (install: npm install -g codebyplan@${VERSION})" \
|
|
271
|
+
--prerelease
|
|
272
|
+
else
|
|
273
|
+
gh release create "${TAG}" \
|
|
274
|
+
--title "codebyplan v${VERSION}" \
|
|
275
|
+
--notes "codebyplan v${VERSION} — published to npm: https://www.npmjs.com/package/codebyplan/v/${VERSION} (install: npm install -g codebyplan@${VERSION})" \
|
|
276
|
+
--latest
|
|
277
|
+
fi
|
|
206
278
|
echo "Created GitHub release ${TAG}."
|
|
207
279
|
fi
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# @scope: org-shared
|
|
3
|
+
# @event: PreToolUse
|
|
4
|
+
# @matcher: Edit|Write|MultiEdit
|
|
5
|
+
# Guard spec files against two patterns banned by rules/e2e-mandatory.md and
|
|
6
|
+
# context/testing/e2e.md:
|
|
7
|
+
#
|
|
8
|
+
# 1. In-spec env skip gates — test.skip(!process.env.X, ...) bypasses preflight,
|
|
9
|
+
# produces zero-assertion runs, and hides missing env vars behind a green check.
|
|
10
|
+
# (rules/e2e-mandatory.md § No In-Spec Env-Skip Gate)
|
|
11
|
+
#
|
|
12
|
+
# 2. waitForLoadState("networkidle") locators — networkidle is deprecated in
|
|
13
|
+
# Playwright and causes flaky / hanging tests; use load or domcontentloaded
|
|
14
|
+
# instead. (context/testing/e2e.md § locator hygiene)
|
|
15
|
+
#
|
|
16
|
+
# Only fires on spec files: file_path ending in .spec.ts OR containing /e2e/
|
|
17
|
+
# or starting with e2e/. All other file paths exit 0 immediately.
|
|
18
|
+
#
|
|
19
|
+
# Exit codes:
|
|
20
|
+
# 0 — no violation (or not a spec file)
|
|
21
|
+
# 2 — violation found; message on stderr
|
|
22
|
+
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
INPUT_JSON="$(cat)"
|
|
26
|
+
TOOL_NAME="$(echo "$INPUT_JSON" | jq -r '.tool_name // empty')"
|
|
27
|
+
FILE_PATH="$(echo "$INPUT_JSON" | jq -r '.tool_input.file_path // empty')"
|
|
28
|
+
|
|
29
|
+
# ------------------------------------------------------------------
|
|
30
|
+
# Fast-path: only enforce on spec / e2e files
|
|
31
|
+
# ------------------------------------------------------------------
|
|
32
|
+
is_spec=0
|
|
33
|
+
case "$FILE_PATH" in
|
|
34
|
+
*.spec.ts) is_spec=1 ;;
|
|
35
|
+
*/e2e/*) is_spec=1 ;;
|
|
36
|
+
e2e/*) is_spec=1 ;;
|
|
37
|
+
esac
|
|
38
|
+
|
|
39
|
+
[ "$is_spec" -eq 0 ] && exit 0
|
|
40
|
+
|
|
41
|
+
# ------------------------------------------------------------------
|
|
42
|
+
# Extract content depending on tool type
|
|
43
|
+
# ------------------------------------------------------------------
|
|
44
|
+
NEW_CONTENT=""
|
|
45
|
+
if [ "$TOOL_NAME" = "Write" ]; then
|
|
46
|
+
NEW_CONTENT="$(echo "$INPUT_JSON" | jq -r '.tool_input.content // empty')"
|
|
47
|
+
elif [ "$TOOL_NAME" = "Edit" ]; then
|
|
48
|
+
NEW_CONTENT="$(echo "$INPUT_JSON" | jq -r '.tool_input.new_string // empty')"
|
|
49
|
+
elif [ "$TOOL_NAME" = "MultiEdit" ]; then
|
|
50
|
+
NEW_CONTENT="$(echo "$INPUT_JSON" | jq -r '[.tool_input.edits[].new_string // ""] | join("\n")')"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
[ -z "$NEW_CONTENT" ] && exit 0
|
|
54
|
+
|
|
55
|
+
# ------------------------------------------------------------------
|
|
56
|
+
# Check 1: in-spec env-skip gate (test.skip with process.env)
|
|
57
|
+
# Pattern: test.skip( followed by optional whitespace and optional !
|
|
58
|
+
# then process.env
|
|
59
|
+
# ------------------------------------------------------------------
|
|
60
|
+
if echo "$NEW_CONTENT" | grep -qE 'test\.skip\([[:space:]]*!?process\.env'; then
|
|
61
|
+
cat >&2 <<'EOF'
|
|
62
|
+
[hook] cbp-e2e-spec-patterns: in-spec env skip gate detected
|
|
63
|
+
|
|
64
|
+
Pattern: test.skip(!process.env.X, ...) or test.skip(process.env.X, ...)
|
|
65
|
+
|
|
66
|
+
This pattern is banned by rules/e2e-mandatory.md § "No In-Spec Env-Skip Gate":
|
|
67
|
+
"Spec files MUST NOT contain in-spec env skip gates such as
|
|
68
|
+
test.skip(!process.env.X, ...). They bypass preflight, produce
|
|
69
|
+
zero-assertion runs, and hide missing env vars behind a green check."
|
|
70
|
+
|
|
71
|
+
The only valid mechanism for env-conditional skipping is pre-flight
|
|
72
|
+
(context/testing/e2e.md Step 6.5.1). Configure missing env vars in
|
|
73
|
+
.codebyplan/e2e.json credential_vars and let the preflight block the run.
|
|
74
|
+
|
|
75
|
+
EOF
|
|
76
|
+
exit 2
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# ------------------------------------------------------------------
|
|
80
|
+
# Check 2: waitForLoadState("networkidle") locator
|
|
81
|
+
# networkidle is deprecated and causes flaky / hanging tests.
|
|
82
|
+
# ------------------------------------------------------------------
|
|
83
|
+
if echo "$NEW_CONTENT" | grep -qE 'waitForLoadState\(["'"'"']networkidle["'"'"']\)'; then
|
|
84
|
+
cat >&2 <<'EOF'
|
|
85
|
+
[hook] cbp-e2e-spec-patterns: waitForLoadState("networkidle") detected
|
|
86
|
+
|
|
87
|
+
This locator is banned by context/testing/e2e.md locator hygiene rules.
|
|
88
|
+
"networkidle" is deprecated in Playwright and causes flaky or hanging tests.
|
|
89
|
+
|
|
90
|
+
Use one of the stable alternatives:
|
|
91
|
+
await page.waitForLoadState("load"); // waits for load event
|
|
92
|
+
await page.waitForLoadState("domcontentloaded"); // waits for DOM ready
|
|
93
|
+
await page.waitForURL("..."); // waits for navigation
|
|
94
|
+
await expect(locator).toBeVisible(); // waits for element
|
|
95
|
+
|
|
96
|
+
EOF
|
|
97
|
+
exit 2
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
exit 0
|
|
File without changes
|
|
File without changes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# @scope: org-shared
|
|
2
3
|
# @hook: NOT-A-HOOK (test suite for plugin hooks; invoked by cbp-auto-test-hooks.sh)
|
|
3
4
|
# Purpose: Test suite for plugin's shipped hooks. Invoked by cbp-auto-test-hooks.sh whenever a
|
|
4
5
|
# plugin hook file is edited. Not a PreToolUse/PostToolUse/Notification hook itself —
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# @scope: org-shared
|
|
3
|
+
# @event: SessionStart
|
|
4
|
+
# Scope + sibling-parity sweep at session start. Calls `codebyplan claude verify-parity --warn-only`.
|
|
5
|
+
# Fires ONLY in the templates source monorepo (self-guards on templates/ presence); no-op for consumers.
|
|
6
|
+
# Always exits 0 — warn-mode, non-blocking.
|
|
7
|
+
|
|
8
|
+
set -u
|
|
9
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
10
|
+
[ -z "$REPO_ROOT" ] && exit 0
|
|
11
|
+
[ -d "$REPO_ROOT/packages/codebyplan-package/templates" ] || exit 0
|
|
12
|
+
|
|
13
|
+
# Prefer the installed binary (fast); fall back to npx for fresh checkouts /
|
|
14
|
+
# environments where the workspace bin is not on PATH (matches cbp-mcp-round-sync.sh).
|
|
15
|
+
if command -v codebyplan >/dev/null 2>&1; then
|
|
16
|
+
codebyplan claude verify-parity --warn-only 2>&1 || true
|
|
17
|
+
else
|
|
18
|
+
npx codebyplan claude verify-parity --warn-only 2>&1 || true
|
|
19
|
+
fi
|
|
20
|
+
exit 0
|
|
@@ -12,7 +12,7 @@ Authoritative expansion of `cbp-task-planner` Phase 5.6. The planner reads this
|
|
|
12
12
|
|
|
13
13
|
## Wave Schema
|
|
14
14
|
|
|
15
|
-
Each entry in `plan.waves[]` carries these fields (source: `.claude/agents/cbp-task-planner.md
|
|
15
|
+
Each entry in `plan.waves[]` carries these fields (source: `.claude/agents/cbp-task-planner.md` Phase 5.6 "Output" block):
|
|
16
16
|
|
|
17
17
|
```yaml
|
|
18
18
|
- name: string # short identifier, e.g. "web-ui", "backend", "db"
|
|
@@ -34,7 +34,7 @@ Each entry in `plan.waves[]` carries these fields (source: `.claude/agents/cbp-t
|
|
|
34
34
|
- Above 15: apply the proximity-split algorithm below.
|
|
35
35
|
- Sole exception — trivially small plans are exempt from the lower bound: a plan with fewer than 3 total files uses one single wave, and a single-app plan with ≤5 total files MAY skip decomposition entirely (one wave, or `waves[]` omitted — see `cbp-task-planner` Phase 5.6). Zero waves (omitted `waves[]`) trivially satisfies this invariant.
|
|
36
36
|
|
|
37
|
-
**(IV) UI skill preloads** — for each wave whose `files[]` contains UI-bearing paths (`*.tsx`, `*.jsx`, `*.scss`, etc.), add `"frontend-design"` and `"frontend-a11y"` to `skill_preloads[]` in that order (source: `.claude/agents/cbp-task-planner.md
|
|
37
|
+
**(IV) UI skill preloads** — for each wave whose `files[]` contains UI-bearing paths (`*.tsx`, `*.jsx`, `*.scss`, etc.), add `"frontend-design"` and `"frontend-a11y"` to `skill_preloads[]` in that order (source: `.claude/agents/cbp-task-planner.md` Phase 5.6 step "Populate `skill_preloads[]`").
|
|
38
38
|
|
|
39
39
|
## Proximity-Split Algorithm
|
|
40
40
|
|
|
@@ -52,8 +52,13 @@ When a wave would exceed 15 files, split it in priority order:
|
|
|
52
52
|
|
|
53
53
|
When no cross-app or cross-concern independence is found, the planner emits one wave covering all files. The 15-file cap applies to this single wave — if the total exceeds 15, apply the proximity-split algorithm even though the original decomposition was a single wave.
|
|
54
54
|
|
|
55
|
+
## Enforcement
|
|
56
|
+
|
|
57
|
+
Invariants I (disjoint files), II (acyclic `depends_on` DAG), and III (3–15 files per wave, with the small-plan lower-bound exemption) are enforced deterministically by `codebyplan validate-waves [--json]` (`packages/codebyplan-package/src/lib/validate-waves.ts`). The planner runs it at Phase 5.6 against the `{ "waves": [...] }` structure (file-path or stdin input); a non-zero exit lists the violated invariant + offending wave/file. Invariant IV (UI skill preloads) is out of the validator's scope and stays a planner manual step.
|
|
58
|
+
|
|
55
59
|
## Cross-References
|
|
56
60
|
|
|
57
|
-
- `agents/cbp-task-planner.md` Phase 5.6 — consumer of this rule; steps 1–6 and verification
|
|
61
|
+
- `agents/cbp-task-planner.md` Phase 5.6 — consumer of this rule; steps 1–6 and the `validate-waves` verification call.
|
|
62
|
+
- `packages/codebyplan-package/src/lib/validate-waves.ts` — deterministic enforcement of invariants I–III.
|
|
58
63
|
- `agents/cbp-round-executor.md` Step 2.6 — wave-mode skill preloads.
|
|
59
64
|
- `skills/cbp-round-execute/SKILL.md` Step 3 — per-wave executor dispatch.
|
|
@@ -4,7 +4,7 @@ scope: org-shared
|
|
|
4
4
|
|
|
5
5
|
# Scope Vocabulary
|
|
6
6
|
|
|
7
|
-
Canonical scope-marker enum for `.claude/` files. Every CBP-managed agent, skill, rule, and hook script must declare exactly one `scope:` value from the enum below. The marker is enforced by
|
|
7
|
+
Canonical scope-marker enum for `.claude/` files. Every CBP-managed agent, skill, rule, and hook script must declare exactly one `scope:` value from the enum below. The marker is enforced by four validators (cross-linked at the bottom of this file).
|
|
8
8
|
|
|
9
9
|
## Enum values
|
|
10
10
|
|
|
@@ -57,8 +57,9 @@ Used by `.sh` hook scripts and `.sh` tooling under `.claude/`. Place the marker
|
|
|
57
57
|
|
|
58
58
|
## Enforcement
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
Four validators enforce this enum. A file missing the marker (or carrying a non-enum value) fails validation:
|
|
61
61
|
|
|
62
|
-
- `.claude/hooks/validate-structure-scope.sh` — warns
|
|
62
|
+
- `.claude/hooks/validate-structure-scope.sh` — warns when a `.claude/` file being edited (PreToolUse `Edit|Write`) lacks a scope marker; sourced by `validate-structure.sh`; it is NOT a session-start sweep.
|
|
63
|
+
- `codebyplan claude verify-parity` — sweeps all `.claude/{rules,skills,agents,hooks}` for missing/non-enum scope markers AND runs the sibling-parity check against `templates/`; invoked automatically at session start via `.claude/hooks/verify-parity.sh` (monorepo only); run manually with `--json` for CI.
|
|
63
64
|
- `.claude/skills/cbp-build-cc-agent/scripts/validate-agent.sh` — blocks agent authoring when `scope:` is absent.
|
|
64
65
|
- `.claude/skills/cbp-build-cc-skill/scripts/validate-skill.sh` — blocks skill authoring when `scope:` is absent.
|
|
@@ -84,6 +84,8 @@
|
|
|
84
84
|
"Bash(npx codebyplan upgrade-auth:*)",
|
|
85
85
|
"Bash(codebyplan config:*)",
|
|
86
86
|
"Bash(npx codebyplan config:*)",
|
|
87
|
+
"Bash(codebyplan doctor:*)",
|
|
88
|
+
"Bash(npx codebyplan doctor:*)",
|
|
87
89
|
"Bash(codebyplan branch:*)",
|
|
88
90
|
"Bash(npx codebyplan branch:*)",
|
|
89
91
|
"Bash(codebyplan ship:*)",
|
|
@@ -178,12 +180,20 @@
|
|
|
178
180
|
"mcp__codebyplan__update_eslint_repo_config",
|
|
179
181
|
"mcp__codebyplan__update_server_config",
|
|
180
182
|
"mcp__codebyplan__update_task_template",
|
|
183
|
+
"Bash(codebyplan check:*)",
|
|
184
|
+
"Bash(npx codebyplan check:*)",
|
|
185
|
+
"Bash(codebyplan session:*)",
|
|
186
|
+
"Bash(npx codebyplan session:*)",
|
|
187
|
+
"Bash(codebyplan supabase:*)",
|
|
188
|
+
"Bash(npx codebyplan supabase:*)",
|
|
181
189
|
"Bash(codebyplan whoami:*)",
|
|
182
190
|
"Bash(npx codebyplan whoami:*)",
|
|
183
191
|
"Bash(codebyplan resolve-worktree:*)",
|
|
184
192
|
"Bash(npx codebyplan resolve-worktree:*)",
|
|
185
193
|
"Bash(codebyplan version-status:*)",
|
|
186
194
|
"Bash(npx codebyplan version-status:*)",
|
|
195
|
+
"Bash(codebyplan worktree:*)",
|
|
196
|
+
"Bash(npx codebyplan worktree:*)",
|
|
187
197
|
"Bash(codebyplan statusline:*)",
|
|
188
198
|
"Bash(npx codebyplan statusline:*)",
|
|
189
199
|
"Bash(codebyplan ports:*)",
|
|
@@ -214,6 +224,18 @@
|
|
|
214
224
|
"Bash(npx codebyplan bump:*)",
|
|
215
225
|
"Bash(codebyplan scaffold-publish-workflow:*)",
|
|
216
226
|
"Bash(npx codebyplan scaffold-publish-workflow:*)",
|
|
227
|
+
"Bash(codebyplan claude verify-parity:*)",
|
|
228
|
+
"Bash(npx codebyplan claude verify-parity:*)",
|
|
229
|
+
"Bash(codebyplan slug:*)",
|
|
230
|
+
"Bash(npx codebyplan slug:*)",
|
|
231
|
+
"Bash(codebyplan commit:*)",
|
|
232
|
+
"Bash(npx codebyplan commit:*)",
|
|
233
|
+
"Bash(codebyplan migration-collisions:*)",
|
|
234
|
+
"Bash(npx codebyplan migration-collisions:*)",
|
|
235
|
+
"Bash(codebyplan validate-waves:*)",
|
|
236
|
+
"Bash(npx codebyplan validate-waves:*)",
|
|
237
|
+
"Bash(codebyplan e2e:*)",
|
|
238
|
+
"Bash(npx codebyplan e2e:*)",
|
|
217
239
|
"Bash(codebyplan arch-map:*)",
|
|
218
240
|
"Bash(npx codebyplan arch-map:*)"
|
|
219
241
|
]
|
|
@@ -170,7 +170,17 @@ Other teams' CLAUDE.md files in the ancestor tree get picked up by default. Excl
|
|
|
170
170
|
|
|
171
171
|
Managed policy CLAUDE.md cannot be excluded.
|
|
172
172
|
|
|
173
|
-
### Step 9 —
|
|
173
|
+
### Step 9 — Validate
|
|
174
|
+
|
|
175
|
+
Run the validator:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
bash "${CLAUDE_SKILL_DIR}/scripts/validate-claude-file.sh" "{path-to-claude-file}"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
It checks: at least one H1 heading present, `@path` imports all resolve relative to the file's directory (or repo root), line count (warns at 200, errors at 400), warns if `scope:` marker appears in frontmatter, warns if filename is neither `CLAUDE.md` nor `CLAUDE.local.md`.
|
|
182
|
+
|
|
183
|
+
### Step 10 — Verify with `/memory`
|
|
174
184
|
|
|
175
185
|
Run `/memory` to confirm the file is loaded. The list shows all CLAUDE.md, CLAUDE.local.md, and rules files in effect.
|
|
176
186
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# @scope: org-shared
|
|
3
|
+
# Validate a CLAUDE.md or CLAUDE.local.md file.
|
|
4
|
+
# Usage: validate-claude-file.sh <path-to-claude-file>
|
|
5
|
+
# Exit 0 = valid, exit 1 = invalid (errors printed to stderr).
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
FILE="${1:?Usage: validate-claude-file.sh <path-to-claude-file>}"
|
|
10
|
+
|
|
11
|
+
if [ ! -f "$FILE" ]; then
|
|
12
|
+
echo "ERROR: file not found: $FILE" >&2
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
errors=0
|
|
17
|
+
err() { echo " - $1" >&2; errors=$((errors + 1)); }
|
|
18
|
+
|
|
19
|
+
# Filename check — warn if not a known CLAUDE.md variant
|
|
20
|
+
fname=$(basename "$FILE")
|
|
21
|
+
if [ "$fname" != "CLAUDE.md" ] && [ "$fname" != "CLAUDE.local.md" ]; then
|
|
22
|
+
echo " WARN: unexpected filename '$fname' — expected CLAUDE.md or CLAUDE.local.md" >&2
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# At least one H1 heading must be present
|
|
26
|
+
if ! grep -qE '^# ' "$FILE"; then
|
|
27
|
+
err "missing H1 heading"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Line count thresholds
|
|
31
|
+
lines=$(wc -l < "$FILE")
|
|
32
|
+
if [ "$lines" -ge 400 ]; then
|
|
33
|
+
err "file is $lines lines (hard limit 400; split with @path imports or nested CLAUDE.md files)"
|
|
34
|
+
elif [ "$lines" -ge 200 ]; then
|
|
35
|
+
echo " WARN: file is $lines lines (recommended max 200; consider splitting)" >&2
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# @path import resolution — each @path token must resolve to an existing file.
|
|
39
|
+
# Tries both: relative to the file's directory, and relative to cwd (repo root).
|
|
40
|
+
filedir=$(dirname "$FILE")
|
|
41
|
+
while IFS= read -r imp; do
|
|
42
|
+
[ -z "$imp" ] && continue
|
|
43
|
+
ipath="${imp#@}"
|
|
44
|
+
if [ "${ipath:0:1}" = "/" ]; then
|
|
45
|
+
# absolute path
|
|
46
|
+
if [ ! -f "$ipath" ]; then
|
|
47
|
+
err "@import path '$ipath' does not exist"
|
|
48
|
+
fi
|
|
49
|
+
elif [ -f "$filedir/$ipath" ]; then
|
|
50
|
+
: # ok — resolves relative to the file's directory
|
|
51
|
+
elif [ -f "$ipath" ]; then
|
|
52
|
+
: # ok — resolves relative to cwd (repo root)
|
|
53
|
+
else
|
|
54
|
+
err "@import path '$ipath' does not exist (tried: $filedir/$ipath, $ipath)"
|
|
55
|
+
fi
|
|
56
|
+
done < <(grep -oE '^[[:space:]]*@[^[:space:]@]+' "$FILE" 2>/dev/null | sed 's/^[[:space:]]*//' 2>/dev/null || true)
|
|
57
|
+
|
|
58
|
+
# Warn if YAML frontmatter contains a scope: marker (CLAUDE.md is not a rule/skill/agent file)
|
|
59
|
+
if head -1 "$FILE" | grep -qE '^---[[:space:]]*$'; then
|
|
60
|
+
fm=$(awk '/^---[[:space:]]*$/{n++; next} n==1{print} n==2{exit}' "$FILE")
|
|
61
|
+
if grep -qE '^scope:[[:space:]]*' <<< "$fm"; then
|
|
62
|
+
echo " WARN: CLAUDE.md has a scope: frontmatter field — scope markers belong in rule/skill/agent files, not CLAUDE.md" >&2
|
|
63
|
+
fi
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
if [ "$errors" -gt 0 ]; then
|
|
67
|
+
echo "validation FAILED ($errors issue(s)) for $FILE" >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
echo "validation OK: $FILE"
|
|
72
|
+
exit 0
|