@esoteric-logic/praxis-harness 2.3.0 → 2.4.0

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.
Files changed (36) hide show
  1. package/README.md +13 -1
  2. package/base/CLAUDE.md +4 -0
  3. package/base/configs/linters/.editorconfig +29 -0
  4. package/base/configs/linters/.golangci.yml +63 -0
  5. package/base/configs/linters/.hadolint.yaml +7 -0
  6. package/base/configs/linters/.shellcheckrc +4 -0
  7. package/base/configs/linters/.tflint.hcl +30 -0
  8. package/base/configs/linters/commitlint.config.js +10 -0
  9. package/base/configs/vale/.vale.ini +16 -0
  10. package/base/configs/vale/Praxis/AISlop.yml +90 -0
  11. package/base/configs/vale/Praxis/CopulaAvoidance.yml +22 -0
  12. package/base/configs/vale/Praxis/ElegantVariation.yml +14 -0
  13. package/base/configs/vale/Praxis/Hedging.yml +22 -0
  14. package/base/configs/vale/Praxis/NaturalVoice.yml +85 -0
  15. package/base/configs/vale/Praxis/Precision.yml +60 -0
  16. package/base/hooks/file-guard.sh +53 -0
  17. package/base/hooks/post-session-lint.sh +5 -0
  18. package/base/hooks/quality-check.sh +165 -0
  19. package/base/hooks/settings-hooks.json +14 -1
  20. package/base/hooks/vault-checkpoint.sh +25 -0
  21. package/base/rules/coding.md +23 -0
  22. package/base/rules/context-management.md +2 -0
  23. package/base/rules/terraform.md +10 -0
  24. package/base/skills/execute/SKILL.md +15 -0
  25. package/base/skills/plan/SKILL.md +16 -0
  26. package/base/skills/pre-commit-lint/SKILL.md +21 -1
  27. package/base/skills/repair/SKILL.md +7 -0
  28. package/base/skills/scaffold-new/SKILL.md +40 -0
  29. package/base/skills/scaffold-new/references/repo-CLAUDE-md-template.md +35 -0
  30. package/base/skills/ship/SKILL.md +6 -0
  31. package/base/skills/verify/SKILL.md +16 -5
  32. package/bin/praxis.js +19 -0
  33. package/package.json +1 -1
  34. package/scripts/install-tools.sh +137 -0
  35. package/scripts/lint-harness.sh +2 -1
  36. package/scripts/test-harness.sh +87 -0
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env bash
2
+ # quality-check.sh — PostToolUse hook (merged format + lint + typecheck)
3
+ # Detects stack from file extension. Formats, lints, type-checks.
4
+ # Always exits 0 (PostToolUse cannot hard-block).
5
+ # Emits JSON feedback for Claude self-correction.
6
+ set -uo pipefail
7
+
8
+ INPUT=$(cat)
9
+
10
+ # Guard: skip if stop hook is active (prevent infinite loop)
11
+ if [ "$(echo "$INPUT" | jq -r '.stop_hook_active // false')" = "true" ]; then
12
+ exit 0
13
+ fi
14
+
15
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty')
16
+
17
+ if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
18
+ exit 0
19
+ fi
20
+
21
+ EXT="${FILE_PATH##*.}"
22
+ BASENAME=$(basename "$FILE_PATH")
23
+ ISSUES=()
24
+
25
+ # ── FORMAT ──────────────────────────────────────────────
26
+ case "$EXT" in
27
+ go)
28
+ if command -v goimports &>/dev/null; then
29
+ goimports -w "$FILE_PATH" 2>/dev/null
30
+ elif command -v gofmt &>/dev/null; then
31
+ gofmt -w "$FILE_PATH" 2>/dev/null
32
+ fi
33
+ ;;
34
+ tf|tfvars)
35
+ if command -v terraform &>/dev/null; then
36
+ terraform fmt "$FILE_PATH" 2>/dev/null
37
+ fi
38
+ ;;
39
+ sh|bash)
40
+ if command -v shfmt &>/dev/null; then
41
+ shfmt -i 2 -ci -bn -w "$FILE_PATH" 2>/dev/null
42
+ fi
43
+ ;;
44
+ py)
45
+ if command -v ruff &>/dev/null; then
46
+ ruff format "$FILE_PATH" 2>/dev/null
47
+ fi
48
+ ;;
49
+ json)
50
+ if command -v jq &>/dev/null; then
51
+ TMP=$(mktemp)
52
+ if jq . "$FILE_PATH" > "$TMP" 2>/dev/null; then
53
+ mv "$TMP" "$FILE_PATH"
54
+ else
55
+ rm -f "$TMP"
56
+ fi
57
+ fi
58
+ ;;
59
+ toml)
60
+ if command -v taplo &>/dev/null; then
61
+ taplo format "$FILE_PATH" 2>/dev/null
62
+ fi
63
+ ;;
64
+ md)
65
+ if command -v prettier &>/dev/null; then
66
+ prettier --write --prose-wrap always "$FILE_PATH" 2>/dev/null
67
+ fi
68
+ ;;
69
+ esac
70
+
71
+ # ── LINT ────────────────────────────────────────────────
72
+ case "$EXT" in
73
+ go)
74
+ if command -v go &>/dev/null; then
75
+ LINT_OUT=$(go vet "$FILE_PATH" 2>&1) || true
76
+ if [ -n "$LINT_OUT" ]; then
77
+ ISSUES+=("go vet: $LINT_OUT")
78
+ fi
79
+ fi
80
+ ;;
81
+ tf|tfvars)
82
+ if command -v tflint &>/dev/null; then
83
+ LINT_OUT=$(tflint --filter="$FILE_PATH" 2>&1) || true
84
+ if [ -n "$LINT_OUT" ]; then
85
+ ISSUES+=("tflint: $LINT_OUT")
86
+ fi
87
+ fi
88
+ ;;
89
+ sh|bash)
90
+ if command -v shellcheck &>/dev/null; then
91
+ LINT_OUT=$(shellcheck -f gcc "$FILE_PATH" 2>&1) || true
92
+ if [ -n "$LINT_OUT" ]; then
93
+ ISSUES+=("shellcheck: $LINT_OUT")
94
+ fi
95
+ fi
96
+ ;;
97
+ py)
98
+ if command -v ruff &>/dev/null; then
99
+ LINT_OUT=$(ruff check --fix "$FILE_PATH" 2>&1) || true
100
+ if [ -n "$LINT_OUT" ] && ! echo "$LINT_OUT" | grep -q "All checks passed"; then
101
+ ISSUES+=("ruff: $LINT_OUT")
102
+ fi
103
+ fi
104
+ ;;
105
+ md)
106
+ if command -v vale &>/dev/null; then
107
+ LINT_OUT=$(vale --output=line "$FILE_PATH" 2>&1) || true
108
+ if [ -n "$LINT_OUT" ]; then
109
+ ISSUES+=("vale: $LINT_OUT")
110
+ fi
111
+ fi
112
+ ;;
113
+ yml|yaml)
114
+ if command -v yamllint &>/dev/null; then
115
+ LINT_OUT=$(yamllint -f parsable "$FILE_PATH" 2>&1) || true
116
+ if [ -n "$LINT_OUT" ]; then
117
+ ISSUES+=("yamllint: $LINT_OUT")
118
+ fi
119
+ fi
120
+ ;;
121
+ esac
122
+
123
+ # ── LINT (Dockerfile special case) ─────────────────────
124
+ if [ "$BASENAME" = "Dockerfile" ] || echo "$BASENAME" | grep -qE "^Dockerfile\."; then
125
+ if command -v hadolint &>/dev/null; then
126
+ LINT_OUT=$(hadolint "$FILE_PATH" 2>&1) || true
127
+ if [ -n "$LINT_OUT" ]; then
128
+ ISSUES+=("hadolint: $LINT_OUT")
129
+ fi
130
+ fi
131
+ fi
132
+
133
+ # ── TYPECHECK ───────────────────────────────────────────
134
+ case "$EXT" in
135
+ go)
136
+ if command -v go &>/dev/null; then
137
+ DIR=$(dirname "$FILE_PATH")
138
+ TC_OUT=$(cd "$DIR" && go build ./... 2>&1) || true
139
+ if [ -n "$TC_OUT" ]; then
140
+ ISSUES+=("go build: $TC_OUT")
141
+ fi
142
+ fi
143
+ ;;
144
+ bicep)
145
+ if command -v az &>/dev/null; then
146
+ TC_OUT=$(az bicep build --file "$FILE_PATH" 2>&1) || true
147
+ if echo "$TC_OUT" | grep -qi "error"; then
148
+ ISSUES+=("bicep build: $TC_OUT")
149
+ fi
150
+ fi
151
+ ;;
152
+ esac
153
+
154
+ # ── EMIT RESULT ─────────────────────────────────────────
155
+ if [ ${#ISSUES[@]} -eq 0 ]; then
156
+ echo '{"decision":"pass","reason":""}'
157
+ else
158
+ # Truncate to avoid flooding context window
159
+ COMBINED=$(printf '%s\n' "${ISSUES[@]}" | head -30)
160
+ # Escape for JSON
161
+ ESCAPED=$(echo "$COMBINED" | jq -Rs .)
162
+ echo "{\"decision\":\"block\",\"reason\":$ESCAPED}"
163
+ fi
164
+
165
+ exit 0
@@ -10,6 +10,15 @@
10
10
  }
11
11
  ]
12
12
  },
13
+ {
14
+ "matcher": "Write|Edit|MultiEdit",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "bash ~/.claude/hooks/file-guard.sh"
19
+ }
20
+ ]
21
+ },
13
22
  {
14
23
  "matcher": "Bash",
15
24
  "hooks": [
@@ -26,7 +35,7 @@
26
35
  "hooks": [
27
36
  {
28
37
  "type": "command",
29
- "command": "bash ~/.claude/hooks/auto-format.sh"
38
+ "command": "bash ~/.claude/hooks/quality-check.sh"
30
39
  }
31
40
  ]
32
41
  }
@@ -39,6 +48,10 @@
39
48
  "type": "command",
40
49
  "command": "bash ~/.claude/hooks/post-session-lint.sh"
41
50
  },
51
+ {
52
+ "type": "prompt",
53
+ "prompt": "Run the project test suite. Read CLAUDE.md ## Commands for the test command. If no test command defined, respond {ok:true}. Run tests. If all pass: respond {ok:true}. If tests fail: respond {ok:false, reason:'Tests failing: <summary>'}. Do not fix — only report."
54
+ },
42
55
  {
43
56
  "type": "command",
44
57
  "command": "bash ~/.claude/hooks/session-data-collect.sh"
@@ -37,6 +37,27 @@ if [[ -f "$STATUS_FILE" ]]; then
37
37
  [[ -z "$LOOP_POSITION" ]] && LOOP_POSITION="unknown"
38
38
  fi
39
39
 
40
+ # ── Quality state snapshot (survives compaction) ──
41
+ LINT_STATE="unknown"
42
+ TEST_STATE="unknown"
43
+
44
+ if [ -f "go.mod" ] && command -v golangci-lint &>/dev/null; then
45
+ LINT_COUNT=$(golangci-lint run ./... 2>&1 | grep -c "^" || true)
46
+ if [ "$LINT_COUNT" -eq 0 ]; then
47
+ LINT_STATE="clean"
48
+ else
49
+ LINT_STATE="$LINT_COUNT findings"
50
+ fi
51
+ fi
52
+
53
+ if [ -f "go.mod" ] && command -v go &>/dev/null; then
54
+ if go test ./... -short 2>&1 | grep -q "^ok"; then
55
+ TEST_STATE="passing"
56
+ else
57
+ TEST_STATE="failing"
58
+ fi
59
+ fi
60
+
40
61
  cat > "$CHECKPOINT_FILE" <<EOF
41
62
  ---
42
63
  tags: [checkpoint, compact]
@@ -58,6 +79,10 @@ $CURRENT_PLAN
58
79
  ## Loop Position
59
80
  $LOOP_POSITION
60
81
 
82
+ ## Quality State
83
+ - Lint: $LINT_STATE
84
+ - Tests: $TEST_STATE
85
+
61
86
  ## Note
62
87
  This checkpoint was auto-written by the PreCompact hook.
63
88
  Read this file after compaction to restore context.
@@ -113,8 +113,31 @@ Before adding any new package:
113
113
 
114
114
  ---
115
115
 
116
+ ## Linter Delegation
117
+
118
+ Style, formatting, import ordering, complexity thresholds, and security patterns
119
+ are enforced by automated tooling (golangci-lint, shellcheck, hadolint, tflint,
120
+ semgrep, vale). Rules files cover ONLY:
121
+ - Architecture decisions tooling cannot express
122
+ - Permission boundaries and file-group scoping
123
+ - Error learning from real session failures
124
+ - Verification command references
125
+
126
+ Do NOT duplicate in CLAUDE.md what hooks already enforce.
127
+
128
+ ---
129
+
116
130
  ## Verification Commands
117
131
 
132
+ Single-file (matches PostToolUse hooks):
133
+ - `go vet <file>` / `shellcheck <file>` / `hadolint Dockerfile` / `vale <file>.md`
134
+
135
+ Project-level (matches pre-commit + /verify):
136
+ - `golangci-lint run`
137
+ - `semgrep --config=auto --error .`
138
+ - `govulncheck ./...`
139
+ - `trivy config .`
140
+
118
141
  ```bash
119
142
  # Verify Context7 MCP is active
120
143
  claude mcp list | grep context7
@@ -57,6 +57,8 @@ conversation length heuristic (not token count — we cannot read session JSONL)
57
57
  - Re-read key requirements before implementation decisions
58
58
  - Reinforce constraint awareness — re-state SPEC if drifting
59
59
  - Prefer concise output — save context for implementation
60
+ - Run lint on all files modified this session to catch drift-induced errors:
61
+ `golangci-lint run $(git diff --name-only HEAD) 2>/dev/null || true`
60
62
 
61
63
  ### DEPLETED (late session, >60%)
62
64
  - Checkpoint progress to vault files before continuing
@@ -39,10 +39,20 @@ paths:
39
39
  - Managed identity preferred over service principal where possible
40
40
  - Tag all resources: `project`, `environment`, `owner`, `created_by = "terraform"`
41
41
 
42
+ ### Security scanning
43
+ - Run `trivy config .` before committing infrastructure changes.
44
+ - If trivy is not installed: warn, do not block. (tfsec is deprecated — use trivy.)
45
+
46
+ ### Cost Awareness
47
+ Before proposing any Azure resource, state the estimated SKU and monthly cost.
48
+ Prefer B-series (burstable) over D/E-series unless workload justifies it.
49
+ Infracost runs on every commit — cost surprises are treated as bugs.
50
+
42
51
  ## Verification Commands
43
52
  ```bash
44
53
  terraform fmt -recursive -check
45
54
  terraform validate
55
+ trivy config --severity HIGH,CRITICAL .
46
56
  rg 'source\s*=\s*"\.\.\/environments' modules/
47
57
  rg '^resource "' environments/
48
58
  ```
@@ -41,6 +41,21 @@ Before implementing the current milestone, declare the file group:
41
41
  - **Rationale**: {why}
42
42
  ```
43
43
 
44
+ **Step 2c — Execution mode selection**
45
+ Read `execution_type` from the active plan frontmatter.
46
+
47
+ **If execution_type: tdd**:
48
+ 1. Write test file first (`*_test.go`, `*.test.ts`, etc.)
49
+ 2. Run test command → confirm RED (tests fail as expected)
50
+ 3. If tests pass before implementation → tests are wrong, rewrite them
51
+ 4. Implement minimum code to make tests GREEN
52
+ 5. Run lint (project lint command from CLAUDE.md ## Commands)
53
+ 6. Refactor if needed — tests must stay green
54
+ 7. Commit test + implementation together
55
+
56
+ **If execution_type: execute** (default):
57
+ Proceed with existing implementation flow unchanged.
58
+
44
59
  **Step 3 — Implement current milestone**
45
60
  - Update `{vault_path}/status.md`: set `loop_position: EXECUTE`.
46
61
  - One milestone at a time. Keep diffs scoped.
@@ -106,6 +106,22 @@ After building the milestone list:
106
106
  - Validate checkpoints: milestones with architectural decisions or user-facing output
107
107
  should be annotated as `checkpoint: decision` or `checkpoint: human-verify`.
108
108
 
109
+ **Step 2c — Execution type recommendation**
110
+ After building milestones, recommend `execution_type` in plan frontmatter:
111
+
112
+ Recommend **tdd** when:
113
+ - Plan touches existing code that already has tests
114
+ - Plan involves API contracts, interfaces, or data transformations
115
+ - Plan is a bug fix (regression test first)
116
+
117
+ Recommend **execute** when:
118
+ - Pure IaC / infrastructure work
119
+ - Greenfield scaffolding with no existing test patterns
120
+ - Documentation-only changes
121
+
122
+ Write the recommendation to plan frontmatter. Explain the reasoning in one line.
123
+ The operator can override during plan review.
124
+
109
125
  **Step 3 — Write and wire**
110
126
  - Write to: `{vault_path}/plans/{YYYY-MM-DD}_{kebab-title}.md`
111
127
  - Update `status.md`: set `current_plan:`, update `last_updated`, set `loop_position: PLAN`, update `## Now What`
@@ -36,7 +36,7 @@ Out of scope:
36
36
 
37
37
  1. Read `{repo_root}/CLAUDE.md` → extract `{identity_email}` and `{stack}`
38
38
  2. Detect stack from repo: `git ls-files | grep -E "\.(tf|ps1|yml|py|ts|sh|go)$" | sed 's/.*\.//' | sort -u`
39
- 3. Check tool availability: tflint, tfsec, actionlint, ruff, mypy, terraform, shellcheck
39
+ 3. Check tool availability: tflint, trivy, actionlint, ruff, mypy, terraform, shellcheck, golangci-lint, semgrep, hadolint, govulncheck, infracost, vale, markdownlint, gitleaks, commitlint
40
40
 
41
41
  ## Phase 1 — Generate Hook
42
42
 
@@ -47,6 +47,26 @@ Append stack sections based on detected flags:
47
47
  - **Shell**: bash -n syntax, set -euo check, shellcheck
48
48
  - **GitHub Actions**: actionlint, unpinned action check
49
49
  - **Python**: ruff, mypy
50
+ - **Go** (if golangci-lint available): `golangci-lint run` on staged .go files
51
+ - **Security** (if tools available):
52
+ - `gitleaks protect --staged` (replaces regex-based secret scan if gitleaks available)
53
+ - `semgrep --config=auto --error` on staged code files (if semgrep available)
54
+ - **Terraform security** (if tools available):
55
+ - `trivy config --severity HIGH,CRITICAL --exit-code 1` on staged .tf files
56
+ - `infracost breakdown --path=.` (advisory only, exit 0)
57
+ - **Docker** (if hadolint available):
58
+ - `hadolint` on staged Dockerfiles
59
+ - `trivy config` on staged Dockerfiles (if trivy available)
60
+ - **Dependencies** (if applicable):
61
+ - `govulncheck ./...` (if go.mod exists and govulncheck available)
62
+ - **Markdown** (if tools available):
63
+ - `vale` on staged .md files
64
+ - `markdownlint` on staged .md files
65
+
66
+ ### Commit Message Hook
67
+ Generate a SEPARATE `.git/hooks/commit-msg` file that runs:
68
+ - `commitlint --edit $1` (if commitlint available)
69
+
50
70
  Append summary footer.
51
71
 
52
72
  ## Phase 2 — Write and Verify
@@ -52,6 +52,13 @@ Run the full validation sequence from `/verify` Step 1:
52
52
  3. Typecheck (if applicable)
53
53
  4. Build (if applicable)
54
54
 
55
+ **Step 4b — Post-fix security re-scan**
56
+ After applying the fix, run the full validation sequence from /verify Step 1 (including security scan).
57
+ If the original failure was security-related:
58
+ 1. Re-run the exact scanner that found the original issue
59
+ 2. Confirm the specific finding is resolved
60
+ 3. Verify the fix didn't introduce NEW findings in the same file
61
+
55
62
  **Step 5 — Evaluate result**
56
63
  - **PASS** → Milestone repaired. Commit the fix. Return to workflow.
57
64
  Output: `Repaired: {what was fixed} in {file}. Next: continue with /execute or /verify.`
@@ -129,6 +129,46 @@ If no: skip silently.
129
129
 
130
130
  ---
131
131
 
132
+ ## Phase 5.5b — Linter Config Generation
133
+
134
+ Based on the detected tech stack from Phase 1, copy relevant configs from `base/configs/`:
135
+
136
+ | Stack Detected | Configs to Copy |
137
+ |---------------|-----------------|
138
+ | Go | `.golangci.yml` |
139
+ | Shell scripts | `.shellcheckrc` |
140
+ | Dockerfile | `.hadolint.yaml` |
141
+ | Terraform | `.tflint.hcl` |
142
+ | Any | `.vale.ini`, `.vale-styles/Praxis/*` (all rule files), `.editorconfig`, `commitlint.config.js` |
143
+
144
+ After copying Vale configs: run `vale sync` to download community packages (Microsoft, write-good, Readability).
145
+
146
+ ---
147
+
148
+ ## Phase 5.5c — VS Code Workspace (optional)
149
+
150
+ Ask: "Generate VS Code workspace settings?"
151
+ If yes:
152
+ - Generate `.vscode/settings.json` with stack-detected configuration (formatter, linter paths)
153
+ - Generate `.vscode/extensions.json` with recommended extensions from this mapping:
154
+
155
+ | Always | Extension ID |
156
+ |--------|-------------|
157
+ | Vale | `chrischinchilla.vale-vscode` |
158
+ | ShellCheck | `timonwong.shellcheck` |
159
+ | EditorConfig | `editorconfig.editorconfig` |
160
+ | markdownlint | `davidanson.vscode-markdownlint` |
161
+
162
+ | Stack Detected | Extension ID |
163
+ |---------------|-------------|
164
+ | Go | `golang.go` |
165
+ | Terraform | `hashicorp.terraform` |
166
+ | Docker | `exiasr.hadolint` |
167
+
168
+ If no: skip silently.
169
+
170
+ ---
171
+
132
172
  ## Phase 6 — Update Project Registry
133
173
 
134
174
  Append to vault CLAUDE.md Project Registry table and agenda.md.
@@ -39,14 +39,40 @@ Code lives here. Knowledge, decisions, and plans live in the vault.
39
39
  - {stack_item_3}
40
40
 
41
41
  ## Commands
42
+ <!-- Populated by scaffold-new based on detected stack -->
43
+
44
+ ### Go
45
+ ```bash
46
+ test: go test ./...
47
+ lint: golangci-lint run
48
+ format: goimports -w . && shfmt -w .
49
+ typecheck: go build ./...
50
+ security: govulncheck ./... && semgrep --config=auto --error .
51
+ ```
52
+
53
+ ### Terraform
54
+ ```bash
55
+ lint: tflint --recursive && trivy config .
56
+ format: terraform fmt -recursive
57
+ security: trivy config --severity HIGH,CRITICAL .
58
+ cost: infracost breakdown --path=.
59
+ ```
60
+
61
+ ### General
42
62
  ```bash
43
63
  dev: # fill in as project develops
44
64
  test: # fill in as project develops
45
65
  lint: # fill in as project develops
46
66
  build: # fill in as project develops
47
67
  format: # fill in as project develops
68
+ prose: vale .
48
69
  ```
49
70
 
71
+ ## Thresholds
72
+ - coverage_minimum: 80
73
+ - max_function_lines: 60
74
+ - max_cognitive_complexity: 20
75
+
50
76
  ## Code Style
51
77
  - Prefer simple, readable code over clever abstractions
52
78
  - After finishing implementation, run `/simplify` to clean up
@@ -65,6 +91,12 @@ format: # fill in as project develops
65
91
  - **Plans**: `{vault_path}/plans/YYYY-MM-DD_[task-slug].md`
66
92
  - **Learnings**: `{vault_path}/notes/learnings.md` using [LEARN:tag] schema
67
93
 
94
+ ## Protected Files
95
+ <!-- Files that file-guard.sh blocks from modification -->
96
+ - go.sum
97
+ - go.mod
98
+ - .github/workflows/
99
+
68
100
  ## Error Learning
69
101
  <!-- When a mistake is corrected, write a new rule here to prevent recurrence -->
70
102
  <!-- Each rule should be specific and actionable -->
@@ -77,6 +109,9 @@ format: # fill in as project develops
77
109
  **Step 1** — Read this file top to bottom first.
78
110
  **Step 2** — Active task? → read active plan. No task? → read status.md.
79
111
  **Step 3** — Load stack rules only if the current task touches them.
112
+ **Step 4** — Quality re-anchor: read most recent `compact-checkpoint.md` → check Quality State section.
113
+ If lint findings existed before compaction: re-run lint, confirm status.
114
+ If tests were failing before compaction: re-run tests, confirm status.
80
115
 
81
116
  ## Compact Preservation
82
117
  When compacting, always preserve:
@@ -14,6 +14,12 @@ You are running the ship workflow — commit, push, and PR in one command.
14
14
  3. Typecheck (if applicable)
15
15
  4. Test suite (from CLAUDE.md `## Commands`)
16
16
  5. Identity check: `git --no-pager config user.email` must match expected
17
+ 6. Secret deep scan: `gitleaks protect --staged` (if gitleaks available)
18
+ 7. Security scan: `semgrep --config=auto --error .` (if semgrep available, on staged files)
19
+ 8. Dependency check: `govulncheck ./...` (if Go project with go.mod)
20
+ 9. Cost check: `infracost breakdown --path=.` (if Terraform project — advisory only, do not block)
21
+ - Any HIGH/CRITICAL finding in steps 6-8 blocks the ship. Cost check is informational.
22
+ - If tools not installed: skip with note, do not block.
17
23
  - If ANY check fails: STOP. Fix first, then re-run `/ship`.
18
24
 
19
25
  **Step 2 — Stage and review changes**
@@ -12,7 +12,12 @@ Execute in order, showing actual output (never assertions):
12
12
  2. **Linter** → run the project lint command → show output, fix ALL warnings
13
13
  3. **Typecheck** (if applicable) → show output
14
14
  4. **Build** (if applicable) → show output
15
- 5. **Functional check** ask: "Is there a smoke test, `terraform plan` output, or browser check needed for this milestone?" If yes: block until user confirms it passed. If no: proceed.
15
+ 5. **Security scan** (if tools available):
16
+ - **Go**: `gosec ./...` OR `golangci-lint run --enable=gosec` (if golangci-lint available)
17
+ - **All staged code**: `semgrep --config=auto --error .` (if semgrep available)
18
+ - If either tool finds HIGH/CRITICAL: treat as blocking — must fix before proceeding.
19
+ - If tools not installed: skip with advisory note, do not block.
20
+ 6. **Functional check** — ask: "Is there a smoke test, `terraform plan` output, or browser check needed for this milestone?" If yes: block until user confirms it passed. If no: proceed.
16
21
 
17
22
  Read test/lint/build commands from the project CLAUDE.md `## Commands` section.
18
23
  If no commands are defined: warn and ask user for the correct commands.
@@ -29,10 +34,16 @@ If no commands are defined: warn and ask user for the correct commands.
29
34
  4. Check if more milestones remain:
30
35
  - Yes → "Milestone committed. Run `/execute` for the next milestone."
31
36
  - No → "All milestones committed. Running self-review."
32
- 5. After ALL milestones: trigger Self-Review Protocol
33
- - Launch a subagent to review the full diff as a critical code reviewer
34
- - Subagent receives ONLY: the diff, the SPEC (from plan file `## SPEC` section), relevant rules files
35
- - Address all Critical and Major findings before reporting done
37
+ 5. After ALL milestones: trigger Self-Review Protocol (Subagent Isolation)
38
+ - Launch a subagent for code review. The subagent receives ONLY:
39
+ 1. The diff: `git diff main...HEAD` (or `git diff HEAD~N` for N commits)
40
+ 2. The SPEC/ACCEPTANCE section from the active plan
41
+ 3. Relevant rules files (scoped to file types in the diff)
42
+ - The subagent must NOT receive session conversation history or implementation notes.
43
+ - Two-pass review:
44
+ - **Pass 1 — Spec compliance**: Does the diff deliver what ACCEPTANCE requires? Flag gaps.
45
+ - **Pass 2 — Code quality**: Bugs, security issues, dead code, missing error handling, maintainability.
46
+ - If either pass finds issues: list them. Do not auto-fix — report back to the operator.
36
47
 
37
48
  **Step 3b — UNIFY (mandatory after all milestones verified)**
38
49
  After self-review passes, write phase summary:
package/bin/praxis.js CHANGED
@@ -105,6 +105,14 @@ async function install() {
105
105
  }
106
106
  }
107
107
 
108
+ // Copy base/configs/ → ~/.claude/configs/
109
+ const configsDir = path.join(PKG_DIR, 'base', 'configs');
110
+ if (fs.existsSync(configsDir)) {
111
+ fs.mkdirSync(path.join(CLAUDE_DIR, 'configs'), { recursive: true });
112
+ copyDir(configsDir, path.join(CLAUDE_DIR, 'configs'));
113
+ ok('linter configs installed');
114
+ }
115
+
108
116
  // Copy kits/ → ~/.claude/kits/
109
117
  const kitsDir = path.join(PKG_DIR, 'kits');
110
118
  if (fs.existsSync(kitsDir)) {
@@ -234,6 +242,10 @@ function health() {
234
242
  }
235
243
  check(fs.existsSync(path.join(CLAUDE_DIR, 'settings.json')), 'settings.json with hooks configured');
236
244
 
245
+ // Configs
246
+ console.log('\nConfigs:');
247
+ check(fs.existsSync(path.join(CLAUDE_DIR, 'configs')), 'configs directory installed');
248
+
237
249
  // Kits
238
250
  console.log('\nKits:');
239
251
  check(fs.existsSync(path.join(CLAUDE_DIR, 'kits')), 'kits directory installed');
@@ -317,6 +329,13 @@ function uninstall() {
317
329
  ok('hooks removed');
318
330
  }
319
331
 
332
+ // Remove configs
333
+ const configsTarget = path.join(CLAUDE_DIR, 'configs');
334
+ if (fs.existsSync(configsTarget)) {
335
+ fs.rmSync(configsTarget, { recursive: true, force: true });
336
+ ok('linter configs removed');
337
+ }
338
+
320
339
  // Remove kits
321
340
  const kitsTarget = path.join(CLAUDE_DIR, 'kits');
322
341
  if (fs.existsSync(kitsTarget)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esoteric-logic/praxis-harness",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Layered Claude Code harness — workflow discipline, AI-Kits, persistent vault integration",
5
5
  "bin": {
6
6
  "praxis-harness": "./bin/praxis.js"