qualia-framework-v2 2.1.0 → 2.1.1
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/agents/builder.md +22 -5
- package/agents/planner.md +13 -1
- package/agents/verifier.md +40 -0
- package/hooks/branch-guard.sh +5 -0
- package/hooks/pre-push.sh +10 -4
- package/package.json +5 -1
- package/skills/qualia/SKILL.md +7 -1
- package/skills/qualia-plan/SKILL.md +13 -0
- package/skills/qualia-quick/SKILL.md +1 -1
- package/skills/qualia-task/SKILL.md +4 -4
- package/tests/hooks.test.sh +144 -0
package/agents/builder.md
CHANGED
|
@@ -49,12 +49,29 @@ git commit -m "{concise description of what was built}"
|
|
|
49
49
|
|
|
50
50
|
Stage specific files — never `git add .` or `git add -A`.
|
|
51
51
|
|
|
52
|
-
##
|
|
52
|
+
## Scope Discipline
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
Before writing or editing any file, check: Is this file listed in the task's **Files** section?
|
|
55
|
+
|
|
56
|
+
- **Yes** → Proceed.
|
|
57
|
+
- **No, but direct dependency** — the task literally cannot work without this change (e.g., adding an import to a shared types file) → Do it. Note in commit message: `also modified: {file} — {reason}`.
|
|
58
|
+
- **No, it's an improvement/cleanup you noticed** → Do NOT do it. Add a line to your commit message: `[discovered] {file}: {what you noticed}`. The planner picks this up next cycle.
|
|
59
|
+
- **Test files** → Never modify unless the task explicitly includes them.
|
|
60
|
+
|
|
61
|
+
This is non-negotiable. Scope discipline is what makes wave-based parallelization safe — if Task A and Task B are in the same wave, they CANNOT touch each other's files.
|
|
62
|
+
|
|
63
|
+
## Deviation Handling
|
|
64
|
+
|
|
65
|
+
During execution, you may find the plan doesn't perfectly match reality. Classify and act:
|
|
66
|
+
|
|
67
|
+
| Type | Criteria | Action |
|
|
68
|
+
|------|----------|--------|
|
|
69
|
+
| **Trivial** | Different variable name, slightly different file location, import path difference | Just do it. No need to mention. |
|
|
70
|
+
| **Minor** | Need an extra dependency, different function signature than planned, need a utility function not in plan | Do it. Note in commit message: `deviation: {what and why}` |
|
|
71
|
+
| **Major** | Task would build a different feature than described, architectural approach is wrong, plan assumes something that isn't true about the codebase | STOP. Do not implement. Return: `BLOCKED — major deviation: {description}. The plan assumes X but the codebase actually does Y. Recommend replanning.` |
|
|
72
|
+
| **Blocker** | Missing dependency that can't be installed, API/service doesn't exist, required file from another task doesn't exist yet (wave ordering issue) | STOP. Return: `BLOCKED — dependency missing: {what's needed}. This task likely needs to move to a later wave.` |
|
|
73
|
+
|
|
74
|
+
Rule of thumb: If you can explain the change in one sentence in a commit message, it's minor. If you'd need to rewrite the task description, it's major.
|
|
58
75
|
|
|
59
76
|
## Rules
|
|
60
77
|
|
package/agents/planner.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qualia-planner
|
|
3
3
|
description: Creates executable phase plans with task breakdown, wave assignments, and verification criteria.
|
|
4
|
-
tools: Read, Write, Bash, Glob, Grep
|
|
4
|
+
tools: Read, Write, Bash, Glob, Grep, WebFetch
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Qualia Planner
|
|
@@ -73,6 +73,18 @@ Goal: {what must be true when done}
|
|
|
73
73
|
- [ ] {truth 3}
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
## Task Specificity (Mandatory)
|
|
77
|
+
|
|
78
|
+
Every task MUST have these three fields with concrete content:
|
|
79
|
+
|
|
80
|
+
- **Files:** Absolute paths from project root. Not "the auth files" or "relevant components". Specific: `src/app/auth/login/page.tsx`, `src/lib/auth.ts`. If creating a file, state what it exports. If modifying, state what changes.
|
|
81
|
+
- **Action:** At least one concrete instruction — not just "implement auth". Reference specific functions, components, or patterns. "Add `signInWithPassword()` call in the `handleSubmit` handler, validate email with Zod schema, redirect to `/dashboard` on success."
|
|
82
|
+
- **Done when:** Testable, not fuzzy. Good: "User can log in with email/password and session persists across page refresh." Bad: "Auth works." Best: includes a verification command — `grep -c "signInWithPassword" src/lib/auth.ts` returns non-zero.
|
|
83
|
+
|
|
84
|
+
If a task involves a library or API you're unsure about, use WebFetch to check the current documentation before specifying the approach. Don't guess at APIs.
|
|
85
|
+
|
|
86
|
+
**Self-check:** Before returning the plan, verify every task has specific file paths, concrete actions, and testable done-when criteria. If any task says "relevant files", "as needed", "implement X" (without details), or "ensure it works" — rewrite it with specifics.
|
|
87
|
+
|
|
76
88
|
## Rules
|
|
77
89
|
|
|
78
90
|
1. **Plans complete within ~50% context.** More plans with smaller scope = consistent quality. 2-3 tasks per plan is ideal.
|
package/agents/verifier.md
CHANGED
|
@@ -58,6 +58,32 @@ grep -c "TODO\|FIXME\|placeholder\|not implemented\|stub" {file}
|
|
|
58
58
|
grep -r "import.*from.*{module}" {consumer_files}
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
### Stub Detection (Level 2)
|
|
62
|
+
|
|
63
|
+
Red flags — these indicate placeholder/stub code:
|
|
64
|
+
```bash
|
|
65
|
+
grep -c "TODO\|FIXME\|PLACEHOLDER\|not implemented\|coming soon" {file}
|
|
66
|
+
grep -c "return null\|return undefined\|return \[\]\|return \{\}" {file}
|
|
67
|
+
grep -c "throw new Error.*not implemented\|throw new Error.*todo" {file}
|
|
68
|
+
grep -c "console\.log.*only\|// stub\|// placeholder\|// temp" {file}
|
|
69
|
+
|
|
70
|
+
# Empty handlers:
|
|
71
|
+
grep -c "catch {}\|catch (e) {}\|catch (err) {}" {file}
|
|
72
|
+
grep -c "async.*=> {}\|() => {}" {file}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If Level 2 finds more than 2 stub patterns in a single file, mark that criterion as **FAIL** regardless of other checks. Stubs are not implementations.
|
|
76
|
+
|
|
77
|
+
### Wiring Check (Level 3)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Is the module actually imported somewhere?
|
|
81
|
+
grep -r "import.*from.*{module_name}" --include="*.ts" --include="*.tsx" | grep -v node_modules | grep -v ".planning"
|
|
82
|
+
|
|
83
|
+
# Are exported functions actually called?
|
|
84
|
+
grep -r "{function_name}" --include="*.ts" --include="*.tsx" | grep -v "export\|function\|const.*=" | grep -v node_modules
|
|
85
|
+
```
|
|
86
|
+
|
|
61
87
|
### 3. Run Code Quality Checks
|
|
62
88
|
|
|
63
89
|
```bash
|
|
@@ -108,6 +134,20 @@ OR
|
|
|
108
134
|
FAIL — {N} gaps found. Run `/qualia-plan {N} --gaps` to fix.
|
|
109
135
|
```
|
|
110
136
|
|
|
137
|
+
## Scoring
|
|
138
|
+
|
|
139
|
+
Each success criterion from the plan gets a verdict:
|
|
140
|
+
|
|
141
|
+
- **PASS** — All 3 levels check out. File exists, has real implementation (not stubs), and is imported/used by the system.
|
|
142
|
+
- **PARTIAL** — File exists and has real code, but isn't fully wired (e.g., component exists but isn't rendered in any page, API route exists but no client calls it). This is NOT a pass.
|
|
143
|
+
- **FAIL** — File missing, is a stub, or has 0 connections to the rest of the codebase.
|
|
144
|
+
|
|
145
|
+
Phase verdict:
|
|
146
|
+
- **ALL PASS** → Phase verified. Update STATE.md status to "verified".
|
|
147
|
+
- **ANY PARTIAL or FAIL** → Phase has gaps. List each gap with: what's wrong, what file, what's needed. Suggest `/qualia-plan {N} --gaps`.
|
|
148
|
+
|
|
149
|
+
Never round up. A PARTIAL is not a PASS. The goal of verification is to catch the work that LOOKS done but ISN'T.
|
|
150
|
+
|
|
111
151
|
## Rules
|
|
112
152
|
|
|
113
153
|
1. **Never trust summaries.** Always grep the code yourself.
|
package/hooks/branch-guard.sh
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
BRANCH=$(git branch --show-current 2>/dev/null)
|
|
5
5
|
ROLE=$(grep -m1 "^## Role:" ~/.claude/CLAUDE.md 2>/dev/null | sed 's/^## Role: *//')
|
|
6
6
|
|
|
7
|
+
if [ -z "$ROLE" ]; then
|
|
8
|
+
echo "BLOCKED: Cannot determine role — ~/.claude/CLAUDE.md missing or malformed. Defaulting to deny."
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
7
12
|
if [[ "$BRANCH" == "main" || "$BRANCH" == "master" ]]; then
|
|
8
13
|
if [[ "$ROLE" != "OWNER" ]]; then
|
|
9
14
|
echo "BLOCKED: Employees cannot push to $BRANCH. Create a feature branch first."
|
package/hooks/pre-push.sh
CHANGED
|
@@ -12,8 +12,12 @@ if [ -f "$STATE" ] && [ -f "$TRACKING" ]; then
|
|
|
12
12
|
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
13
13
|
|
|
14
14
|
# Update tracking.json with current values
|
|
15
|
-
if command -v python3 &>/dev/null; then
|
|
16
|
-
python3
|
|
15
|
+
if ! command -v python3 &>/dev/null; then
|
|
16
|
+
echo "WARNING: python3 not found — tracking.json not updated" >&2
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if ! python3 -c "
|
|
17
21
|
import json
|
|
18
22
|
with open('$TRACKING', 'r') as f:
|
|
19
23
|
t = json.load(f)
|
|
@@ -23,7 +27,9 @@ t['last_commit'] = '${LAST_COMMIT}'
|
|
|
23
27
|
t['last_updated'] = '${NOW}'
|
|
24
28
|
with open('$TRACKING', 'w') as f:
|
|
25
29
|
json.dump(t, f, indent=2)
|
|
26
|
-
" 2>/
|
|
27
|
-
|
|
30
|
+
" 2>/tmp/qualia-push-err.txt; then
|
|
31
|
+
echo "WARNING: Failed to update tracking.json — $(cat /tmp/qualia-push-err.txt)" >&2
|
|
32
|
+
exit 0
|
|
28
33
|
fi
|
|
34
|
+
git add "$TRACKING" 2>/dev/null
|
|
29
35
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qualia-framework-v2",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Claude Code workflow framework by Qualia Solutions. Plan, build, verify, ship.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"qualia-framework-v2": "./bin/cli.js"
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"url": "https://github.com/qualia-solutions/qualia-framework-v2"
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/qualia-solutions/qualia-framework-v2#readme",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "bash tests/hooks.test.sh"
|
|
27
|
+
},
|
|
25
28
|
"files": [
|
|
26
29
|
"bin/",
|
|
27
30
|
"agents/",
|
|
@@ -29,6 +32,7 @@
|
|
|
29
32
|
"rules/",
|
|
30
33
|
"skills/",
|
|
31
34
|
"templates/",
|
|
35
|
+
"tests/",
|
|
32
36
|
"CLAUDE.md",
|
|
33
37
|
"guide.md",
|
|
34
38
|
"statusline.sh"
|
package/skills/qualia/SKILL.md
CHANGED
|
@@ -22,7 +22,13 @@ echo "---VERIFICATIONS---"
|
|
|
22
22
|
ls .planning/phase-*-verification.md 2>/dev/null || echo "NO_VERIFICATIONS"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
###
|
|
25
|
+
### Task Routing (size-based)
|
|
26
|
+
|
|
27
|
+
- Single file change, config tweak, typo fix, bug with known cause → `/qualia-quick`
|
|
28
|
+
- One feature, 1-5 files, clear scope, under 2 hours → `/qualia-task`
|
|
29
|
+
- Multi-feature, 5+ files, needs architecture decisions, or part of a roadmap phase → `/qualia-plan`
|
|
30
|
+
|
|
31
|
+
### 2. Route (state-based)
|
|
26
32
|
|
|
27
33
|
| Condition | Command |
|
|
28
34
|
|-----------|---------|
|
|
@@ -82,3 +82,16 @@ Update tracking.json: status → "planned"
|
|
|
82
82
|
```
|
|
83
83
|
→ Run: /qualia-build {N}
|
|
84
84
|
```
|
|
85
|
+
|
|
86
|
+
### Gap Closure Mode (`--gaps`)
|
|
87
|
+
|
|
88
|
+
When invoked as `/qualia-plan {N} --gaps`, the planner is in gap-closure mode:
|
|
89
|
+
|
|
90
|
+
1. Read `.planning/phase-{N}-verification.md` — extract ONLY the FAIL items
|
|
91
|
+
2. For each FAIL item, create a targeted fix task:
|
|
92
|
+
- **Files:** The specific files that failed verification
|
|
93
|
+
- **Action:** The specific fix needed (not "fix auth" — "add session persistence check in `src/lib/auth.ts` signIn function")
|
|
94
|
+
- **Done when:** The exact verification criterion that previously failed, restated
|
|
95
|
+
3. Do NOT re-plan passing items. Do NOT add new features. Gap plans are surgical.
|
|
96
|
+
4. Write to `.planning/phase-{N}-gaps-plan.md` (separate from original plan)
|
|
97
|
+
5. All gap tasks are Wave 1 (parallel) unless they share files
|
|
@@ -5,7 +5,7 @@ description: "Fast path for small tasks — bug fixes, tweaks, hot fixes. Skips
|
|
|
5
5
|
|
|
6
6
|
# /qualia-quick — Quick Task
|
|
7
7
|
|
|
8
|
-
For tasks under
|
|
8
|
+
For tasks under 1 hour that don't need full phase planning. Single file changes, bug fixes, config tweaks, typo fixes.
|
|
9
9
|
|
|
10
10
|
## Process
|
|
11
11
|
|
|
@@ -12,7 +12,7 @@ Build one thing properly. Fresh builder context, atomic commit, but no phase pla
|
|
|
12
12
|
`/qualia-task {description}` — build it directly
|
|
13
13
|
|
|
14
14
|
## When to Use
|
|
15
|
-
-
|
|
15
|
+
- One feature, 1-5 files, clear scope, 1-3 hours of work
|
|
16
16
|
- Adding a single feature, component, API route, or integration
|
|
17
17
|
- Refactoring one module
|
|
18
18
|
- Building something specific someone asked for
|
|
@@ -29,12 +29,12 @@ Then use AskUserQuestion:
|
|
|
29
29
|
question: "How complex is this task?"
|
|
30
30
|
header: "Scope"
|
|
31
31
|
options:
|
|
32
|
-
- label: "Small (
|
|
32
|
+
- label: "Small (1-2hrs)"
|
|
33
33
|
description: "Single file or component, straightforward implementation"
|
|
34
|
-
- label: "Medium (
|
|
34
|
+
- label: "Medium (2-3hrs)"
|
|
35
35
|
description: "Multiple files, some integration work, needs testing"
|
|
36
36
|
- label: "Large (3hrs+)"
|
|
37
|
-
description: "Significant feature, multiple components
|
|
37
|
+
description: "Significant feature, multiple components — use /qualia-plan instead"
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
If "Large" — suggest `/qualia-plan` instead. Ask if they want to proceed anyway.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Qualia Framework v2 — Hook Tests
|
|
3
|
+
# Run: bash tests/hooks.test.sh
|
|
4
|
+
|
|
5
|
+
PASS=0
|
|
6
|
+
FAIL=0
|
|
7
|
+
HOOKS_DIR="$(dirname "$0")/../hooks"
|
|
8
|
+
|
|
9
|
+
assert_exit() {
|
|
10
|
+
local name="$1" expected="$2" actual="$3"
|
|
11
|
+
if [ "$expected" = "$actual" ]; then
|
|
12
|
+
echo " ✓ $name"
|
|
13
|
+
PASS=$((PASS + 1))
|
|
14
|
+
else
|
|
15
|
+
echo " ✗ $name (expected exit $expected, got $actual)"
|
|
16
|
+
FAIL=$((FAIL + 1))
|
|
17
|
+
fi
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
echo "=== Hook Tests ==="
|
|
21
|
+
echo ""
|
|
22
|
+
|
|
23
|
+
# --- block-env-edit.sh ---
|
|
24
|
+
echo "block-env-edit:"
|
|
25
|
+
|
|
26
|
+
echo '{"tool_input":{"file_path":".env.local"}}' | bash "$HOOKS_DIR/block-env-edit.sh" > /dev/null 2>&1
|
|
27
|
+
assert_exit "blocks .env.local" 2 $?
|
|
28
|
+
|
|
29
|
+
echo '{"tool_input":{"file_path":".env.production"}}' | bash "$HOOKS_DIR/block-env-edit.sh" > /dev/null 2>&1
|
|
30
|
+
assert_exit "blocks .env.production" 2 $?
|
|
31
|
+
|
|
32
|
+
echo '{"tool_input":{"file_path":".env"}}' | bash "$HOOKS_DIR/block-env-edit.sh" > /dev/null 2>&1
|
|
33
|
+
assert_exit "blocks .env" 2 $?
|
|
34
|
+
|
|
35
|
+
echo '{"tool_input":{"file_path":"src/app.tsx"}}' | bash "$HOOKS_DIR/block-env-edit.sh" > /dev/null 2>&1
|
|
36
|
+
assert_exit "allows src/app.tsx" 0 $?
|
|
37
|
+
|
|
38
|
+
echo '{"tool_input":{"file_path":"components/Footer.tsx"}}' | bash "$HOOKS_DIR/block-env-edit.sh" > /dev/null 2>&1
|
|
39
|
+
assert_exit "allows components/Footer.tsx" 0 $?
|
|
40
|
+
|
|
41
|
+
# --- migration-guard.sh ---
|
|
42
|
+
echo ""
|
|
43
|
+
echo "migration-guard:"
|
|
44
|
+
|
|
45
|
+
echo '{"tool_input":{"file_path":"migrations/001.sql","content":"DROP TABLE users;"}}' | bash "$HOOKS_DIR/migration-guard.sh" > /dev/null 2>&1
|
|
46
|
+
assert_exit "blocks DROP TABLE without IF EXISTS" 2 $?
|
|
47
|
+
|
|
48
|
+
echo '{"tool_input":{"file_path":"migrations/001.sql","content":"DROP TABLE IF EXISTS old_users;"}}' | bash "$HOOKS_DIR/migration-guard.sh" > /dev/null 2>&1
|
|
49
|
+
assert_exit "allows DROP TABLE IF EXISTS" 0 $?
|
|
50
|
+
|
|
51
|
+
echo '{"tool_input":{"file_path":"migrations/002.sql","content":"DELETE FROM users;"}}' | bash "$HOOKS_DIR/migration-guard.sh" > /dev/null 2>&1
|
|
52
|
+
assert_exit "blocks DELETE without WHERE" 2 $?
|
|
53
|
+
|
|
54
|
+
echo '{"tool_input":{"file_path":"migrations/003.sql","content":"TRUNCATE TABLE sessions;"}}' | bash "$HOOKS_DIR/migration-guard.sh" > /dev/null 2>&1
|
|
55
|
+
assert_exit "blocks TRUNCATE" 2 $?
|
|
56
|
+
|
|
57
|
+
echo '{"tool_input":{"file_path":"migrations/004.sql","content":"CREATE TABLE users (id uuid);"}}' | bash "$HOOKS_DIR/migration-guard.sh" > /dev/null 2>&1
|
|
58
|
+
assert_exit "blocks CREATE TABLE without RLS" 2 $?
|
|
59
|
+
|
|
60
|
+
echo '{"tool_input":{"file_path":"migrations/005.sql","content":"ALTER TABLE users ADD COLUMN email text;"}}' | bash "$HOOKS_DIR/migration-guard.sh" > /dev/null 2>&1
|
|
61
|
+
assert_exit "allows safe ALTER TABLE" 0 $?
|
|
62
|
+
|
|
63
|
+
echo '{"tool_input":{"file_path":"src/app.tsx","content":"DROP TABLE users;"}}' | bash "$HOOKS_DIR/migration-guard.sh" > /dev/null 2>&1
|
|
64
|
+
assert_exit "skips non-migration files" 0 $?
|
|
65
|
+
|
|
66
|
+
# --- branch-guard.sh ---
|
|
67
|
+
echo ""
|
|
68
|
+
echo "branch-guard:"
|
|
69
|
+
|
|
70
|
+
if [ -f "$HOOKS_DIR/branch-guard.sh" ]; then
|
|
71
|
+
echo " ✓ branch-guard.sh exists"
|
|
72
|
+
PASS=$((PASS + 1))
|
|
73
|
+
else
|
|
74
|
+
echo " ✗ branch-guard.sh missing"
|
|
75
|
+
FAIL=$((FAIL + 1))
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if grep -q 'ROLE' "$HOOKS_DIR/branch-guard.sh"; then
|
|
79
|
+
echo " ✓ checks ROLE variable"
|
|
80
|
+
PASS=$((PASS + 1))
|
|
81
|
+
else
|
|
82
|
+
echo " ✗ missing ROLE check"
|
|
83
|
+
FAIL=$((FAIL + 1))
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if grep -q 'z "$ROLE"' "$HOOKS_DIR/branch-guard.sh"; then
|
|
87
|
+
echo " ✓ defaults to deny on missing ROLE"
|
|
88
|
+
PASS=$((PASS + 1))
|
|
89
|
+
else
|
|
90
|
+
echo " ✗ missing empty-ROLE deny"
|
|
91
|
+
FAIL=$((FAIL + 1))
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# --- pre-push.sh ---
|
|
95
|
+
echo ""
|
|
96
|
+
echo "pre-push:"
|
|
97
|
+
|
|
98
|
+
if grep -q 'command -v python3' "$HOOKS_DIR/pre-push.sh"; then
|
|
99
|
+
echo " ✓ checks for python3 availability"
|
|
100
|
+
PASS=$((PASS + 1))
|
|
101
|
+
else
|
|
102
|
+
echo " ✗ missing python3 check"
|
|
103
|
+
FAIL=$((FAIL + 1))
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
if grep -q 'qualia-push-err' "$HOOKS_DIR/pre-push.sh"; then
|
|
107
|
+
echo " ✓ captures python3 errors"
|
|
108
|
+
PASS=$((PASS + 1))
|
|
109
|
+
else
|
|
110
|
+
echo " ✗ missing python3 error capture"
|
|
111
|
+
FAIL=$((FAIL + 1))
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# --- pre-deploy-gate.sh ---
|
|
115
|
+
echo ""
|
|
116
|
+
echo "pre-deploy-gate:"
|
|
117
|
+
|
|
118
|
+
if [ -f "$HOOKS_DIR/pre-deploy-gate.sh" ]; then
|
|
119
|
+
echo " ✓ pre-deploy-gate.sh exists"
|
|
120
|
+
PASS=$((PASS + 1))
|
|
121
|
+
else
|
|
122
|
+
echo " ✗ pre-deploy-gate.sh missing"
|
|
123
|
+
FAIL=$((FAIL + 1))
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
if grep -q 'tsc --noEmit' "$HOOKS_DIR/pre-deploy-gate.sh"; then
|
|
127
|
+
echo " ✓ runs TypeScript check"
|
|
128
|
+
PASS=$((PASS + 1))
|
|
129
|
+
else
|
|
130
|
+
echo " ✗ missing TypeScript check"
|
|
131
|
+
FAIL=$((FAIL + 1))
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
if grep -q 'service_role' "$HOOKS_DIR/pre-deploy-gate.sh"; then
|
|
135
|
+
echo " ✓ checks for service_role leaks"
|
|
136
|
+
PASS=$((PASS + 1))
|
|
137
|
+
else
|
|
138
|
+
echo " ✗ missing service_role check"
|
|
139
|
+
FAIL=$((FAIL + 1))
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
echo ""
|
|
143
|
+
echo "=== Results: $PASS passed, $FAIL failed ==="
|
|
144
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|