codeharness 0.20.0 → 0.21.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/dist/index.js +3102 -2881
- package/dist/modules/observability/index.d.ts +295 -0
- package/dist/modules/observability/index.js +366 -0
- package/package.json +6 -1
- package/patches/AGENTS.md +6 -1
- package/patches/observability/AGENTS.md +27 -0
- package/ralph/bridge.sh +8 -5
- package/ralph/drivers/claude-code.sh +2 -28
- package/ralph/lib/circuit_breaker.sh +1 -1
- package/ralph/ralph.sh +3 -2
- package/templates/Dockerfile.verify +8 -1
package/patches/AGENTS.md
CHANGED
|
@@ -28,7 +28,12 @@ Semgrep YAML rules for static analysis of observability gaps. Each `.yaml` file
|
|
|
28
28
|
- `function-no-debug-log.yaml` — Detects functions without debug/info logging (INFO)
|
|
29
29
|
- `error-path-no-log.yaml` — Detects error paths (throw/return err) without preceding log (WARNING)
|
|
30
30
|
|
|
31
|
-
**
|
|
31
|
+
**Test fixtures** (`.ts` files alongside rules, annotated with `// ruleid:` / `// ok:` comments):
|
|
32
|
+
- `catch-without-logging.ts` — Test cases for catch-without-logging rule
|
|
33
|
+
- `function-no-debug-log.ts` — Test cases for function-no-debug-log rule
|
|
34
|
+
- `error-path-no-log.ts` — Test cases for error-path-no-log rule
|
|
35
|
+
|
|
36
|
+
**Testing:** `semgrep --test patches/observability/` runs annotated test fixtures.
|
|
32
37
|
|
|
33
38
|
**Customization:** Edit YAML rules to add custom logger patterns (e.g., `logger.error(...)` for winston). Rules use `pattern-not` / `pattern-not-inside` to detect absence of logging.
|
|
34
39
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# patches/observability/ — Semgrep Rules for Observability Gap Detection
|
|
2
|
+
|
|
3
|
+
Standalone Semgrep YAML rules for static analysis of observability gaps. Each `.yaml` file is a complete Semgrep config — no build step, no TypeScript. Deleting a rule file removes that check.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
| File | Purpose | Severity |
|
|
8
|
+
|------|---------|----------|
|
|
9
|
+
| catch-without-logging.yaml | Detects catch blocks without error/warn logging | WARNING |
|
|
10
|
+
| function-no-debug-log.yaml | Detects functions without debug/info logging | INFO |
|
|
11
|
+
| error-path-no-log.yaml | Detects error paths (throw/return err) without preceding log | WARNING |
|
|
12
|
+
|
|
13
|
+
## Test Fixtures
|
|
14
|
+
|
|
15
|
+
| File | Purpose |
|
|
16
|
+
|------|---------|
|
|
17
|
+
| catch-without-logging.ts | Test cases for catch-without-logging rule (annotated with `// ruleid:` / `// ok:`) |
|
|
18
|
+
| function-no-debug-log.ts | Test cases for function-no-debug-log rule |
|
|
19
|
+
| error-path-no-log.ts | Test cases for error-path-no-log rule |
|
|
20
|
+
|
|
21
|
+
## Testing
|
|
22
|
+
|
|
23
|
+
Run `semgrep --test patches/observability/` to execute all test fixtures against their rules.
|
|
24
|
+
|
|
25
|
+
## Customization
|
|
26
|
+
|
|
27
|
+
Edit YAML rules to add custom logger patterns (e.g., `logger.error(...)` for winston). Rules use `pattern-not` / `pattern-not-inside` to detect absence of logging.
|
package/ralph/bridge.sh
CHANGED
|
@@ -99,9 +99,9 @@ fi
|
|
|
99
99
|
|
|
100
100
|
# ─── Sprint Status Parsing ────────────────────────────────────────────────
|
|
101
101
|
|
|
102
|
-
# Parse sprint status YAML into
|
|
102
|
+
# Parse sprint status YAML into a newline-delimited key=value store
|
|
103
103
|
# Maps story slug (e.g., "1-1-login-page") to status
|
|
104
|
-
|
|
104
|
+
SPRINT_STATUSES=""
|
|
105
105
|
|
|
106
106
|
parse_sprint_status() {
|
|
107
107
|
local file="$1"
|
|
@@ -133,7 +133,8 @@ parse_sprint_status() {
|
|
|
133
133
|
# Extract story number from slug: "1-1-login-page" -> "1.1"
|
|
134
134
|
if [[ "$key" =~ ^([0-9]+)-([0-9]+) ]]; then
|
|
135
135
|
local story_id="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
|
|
136
|
-
SPRINT_STATUSES
|
|
136
|
+
SPRINT_STATUSES="${SPRINT_STATUSES}${story_id}=${value}
|
|
137
|
+
"
|
|
137
138
|
fi
|
|
138
139
|
fi
|
|
139
140
|
fi
|
|
@@ -181,8 +182,10 @@ parse_epics() {
|
|
|
181
182
|
|
|
182
183
|
# Determine status from sprint status or default to pending
|
|
183
184
|
local status="pending"
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
local _sprint_val
|
|
186
|
+
_sprint_val=$(echo "$SPRINT_STATUSES" | grep "^${current_story_id}=" | head -1 | cut -d= -f2-)
|
|
187
|
+
if [[ -n "$_sprint_val" ]]; then
|
|
188
|
+
status=$(map_status "$_sprint_val")
|
|
186
189
|
fi
|
|
187
190
|
|
|
188
191
|
# Clean up description
|
|
@@ -79,10 +79,8 @@ driver_build_command() {
|
|
|
79
79
|
CLAUDE_CMD_ARGS+=("--plugin-dir" "$plugin_dir")
|
|
80
80
|
fi
|
|
81
81
|
|
|
82
|
-
# Output format
|
|
83
|
-
|
|
84
|
-
CLAUDE_CMD_ARGS+=("--output-format" "json")
|
|
85
|
-
fi
|
|
82
|
+
# Output format — always stream-json for real-time NDJSON output
|
|
83
|
+
CLAUDE_CMD_ARGS+=("--output-format" "stream-json" "--verbose" "--include-partial-messages")
|
|
86
84
|
|
|
87
85
|
# Allowed tools
|
|
88
86
|
if [[ -n "$CLAUDE_ALLOWED_TOOLS" ]]; then
|
|
@@ -123,30 +121,6 @@ driver_supports_live_output() {
|
|
|
123
121
|
return 0
|
|
124
122
|
}
|
|
125
123
|
|
|
126
|
-
# Prepare command arguments for live stream-json output
|
|
127
|
-
driver_prepare_live_command() {
|
|
128
|
-
LIVE_CMD_ARGS=()
|
|
129
|
-
local skip_next=false
|
|
130
|
-
|
|
131
|
-
for arg in "${CLAUDE_CMD_ARGS[@]}"; do
|
|
132
|
-
if [[ "$skip_next" == "true" ]]; then
|
|
133
|
-
LIVE_CMD_ARGS+=("stream-json")
|
|
134
|
-
skip_next=false
|
|
135
|
-
elif [[ "$arg" == "--output-format" ]]; then
|
|
136
|
-
LIVE_CMD_ARGS+=("$arg")
|
|
137
|
-
skip_next=true
|
|
138
|
-
else
|
|
139
|
-
LIVE_CMD_ARGS+=("$arg")
|
|
140
|
-
fi
|
|
141
|
-
done
|
|
142
|
-
|
|
143
|
-
if [[ "$skip_next" == "true" ]]; then
|
|
144
|
-
return 1
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
LIVE_CMD_ARGS+=("--verbose" "--include-partial-messages")
|
|
148
|
-
}
|
|
149
|
-
|
|
150
124
|
# Stream filter for raw Claude stream-json events
|
|
151
125
|
driver_stream_filter() {
|
|
152
126
|
echo '
|
package/ralph/ralph.sh
CHANGED
|
@@ -47,7 +47,7 @@ RATE_LIMIT_SLEEP=3600 # 1 hour
|
|
|
47
47
|
|
|
48
48
|
# Driver
|
|
49
49
|
PLATFORM_DRIVER="${PLATFORM_DRIVER:-claude-code}"
|
|
50
|
-
CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-json}"
|
|
50
|
+
CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-stream-json}"
|
|
51
51
|
CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS:-}"
|
|
52
52
|
CLAUDE_USE_CONTINUE="${CLAUDE_USE_CONTINUE:-false}" # Fresh context per iteration by default
|
|
53
53
|
|
|
@@ -829,7 +829,8 @@ execute_iteration() {
|
|
|
829
829
|
log_status "ERROR" "Claude API usage limit reached"
|
|
830
830
|
return 2
|
|
831
831
|
# Check for transient API errors (500, 529, overloaded) — don't count against story
|
|
832
|
-
|
|
832
|
+
# Status code patterns exclude decimal prefixes (e.g., cost_usd=0.503 ≠ HTTP 503)
|
|
833
|
+
elif grep -qiE 'Internal server error|api_error|overloaded|(^|[^0-9.])529([^0-9]|$)|(^|[^0-9.])503([^0-9]|$)' "$output_file" 2>/dev/null; then
|
|
833
834
|
log_status "WARN" "Transient API error (not story's fault) — will retry"
|
|
834
835
|
return 4
|
|
835
836
|
else
|
|
@@ -5,12 +5,19 @@ FROM node:20-slim
|
|
|
5
5
|
|
|
6
6
|
ARG TARBALL=package.tgz
|
|
7
7
|
|
|
8
|
-
# System utilities
|
|
8
|
+
# System utilities + Python for Semgrep
|
|
9
9
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
10
10
|
curl \
|
|
11
11
|
jq \
|
|
12
|
+
python3 \
|
|
13
|
+
python3-pip \
|
|
14
|
+
pipx \
|
|
12
15
|
&& rm -rf /var/lib/apt/lists/*
|
|
13
16
|
|
|
17
|
+
# Semgrep for static analysis verification
|
|
18
|
+
RUN pipx install semgrep && pipx ensurepath
|
|
19
|
+
ENV PATH="/root/.local/bin:${PATH}"
|
|
20
|
+
|
|
14
21
|
# Verification tools + Claude Code CLI
|
|
15
22
|
RUN npm install -g showboat @anthropic-ai/claude-code
|
|
16
23
|
|