@nurix/etna 0.18.3 → 0.18.5
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/data/harness-index.json +1 -1
- package/data/harnesses/code/harness.json +3 -3
- package/data/harnesses/code/rules/general.md +38 -36
- package/data/harnesses/hr/CLAUDE.md +7 -0
- package/data/harnesses/hr/harness.json +2 -2
- package/data/harnesses/hr/rules/code-eval-batcher.md +71 -0
- package/data/harnesses/hr/skills/evaluator/SKILL.md +208 -0
- package/data/harnesses/hr/skills/evaluator/criteria/code-quality.md +350 -0
- package/data/harnesses/hr/skills/evaluator/criteria/deployment-readiness.md +414 -0
- package/data/harnesses/hr/skills/evaluator/criteria/maintainability.md +445 -0
- package/data/harnesses/hr/skills/evaluator/criteria/mechanical-ambition.md +300 -0
- package/data/harnesses/hr/skills/evaluator/criteria/problem-substantiveness.md +88 -0
- package/data/harnesses/hr/skills/evaluator/criteria/testing.md +399 -0
- package/data/harnesses/hr/skills/evaluator/criteria/ux-mechanics.md +283 -0
- package/data/harnesses/hr/skills/evaluator/criteria/value-articulation.md +76 -0
- package/data/harnesses/hr/skills/evaluator/preflight.md +74 -0
- package/data/harnesses/hr/skills/evaluator/preflight.sh +100 -0
- package/data/harnesses/hr/skills/evaluator/validate_scorecard.py +98 -0
- package/package.json +1 -1
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# Mechanical Ambition — Extraction Plan
|
|
2
|
+
|
|
3
|
+
- **Sub-criterion:** 2.4 (Product Thinking)
|
|
4
|
+
- **Weight:** 12.5 pts
|
|
5
|
+
- **Contenders:** scc + Semgrep stub rule + git analytics + Tier-A build check + LLM README-vs-shipped diff (layered)
|
|
6
|
+
- **Source quote:** _"Genuinely shippable scope."_
|
|
7
|
+
|
|
8
|
+
This recipe measures the **mechanical** evidence of ambition (LoC, commits, delivery concentration, stubs, shipped-vs-promised ratio, Tier-A build) against the project's apparent size, and whether the built scope holds together. Bands are **absolute**. Gate this recipe owns: **`BROKEN`** (Tier-A build check fails outside `DEPS_NOT_INSTALLED`; caps **`mechanical_ambition ≤ 2`**).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Extraction recipe
|
|
13
|
+
|
|
14
|
+
### Signal 1 — LoC + complexity + language mix (scc)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
scc --format=json <local-repo-path> \
|
|
18
|
+
--exclude-dir node_modules,.venv,dist,build,.next,target \
|
|
19
|
+
> /tmp/scc.json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Parse:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Total source LOC
|
|
26
|
+
jq '[.[] | .Code] | add' /tmp/scc.json
|
|
27
|
+
|
|
28
|
+
# Language breakdown (top 3)
|
|
29
|
+
jq 'sort_by(-.Code) | .[0:3] | map({lang: .Name, code: .Code, files: .Count})' /tmp/scc.json
|
|
30
|
+
|
|
31
|
+
# Total complexity + complexity-per-file distribution
|
|
32
|
+
jq '[.[] | .Complexity] | add' /tmp/scc.json
|
|
33
|
+
jq '[.[] | .ComplexityAvg] | add / length' /tmp/scc.json
|
|
34
|
+
|
|
35
|
+
# COCOMO estimate (person-months)
|
|
36
|
+
jq '.[] | .EstimatedScheduleMonths' /tmp/scc.json | head -1
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Capture:
|
|
40
|
+
|
|
41
|
+
- `total_loc`, `total_files`
|
|
42
|
+
- `language_mix` (top 3 by LoC)
|
|
43
|
+
- `total_complexity`, `mean_complexity_per_file`
|
|
44
|
+
- `cocomo_estimated_months` (sanity check: is the built scope reasonable for the project's apparent size/duration?)
|
|
45
|
+
|
|
46
|
+
### Signal 2 — Stub / placeholder detection (Semgrep)
|
|
47
|
+
|
|
48
|
+
Create `/tmp/stubs.yml`:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
rules:
|
|
52
|
+
- id: stub-not-implemented
|
|
53
|
+
message: Stub / placeholder function body
|
|
54
|
+
severity: INFO
|
|
55
|
+
languages: [python, js, ts, go, java, rust]
|
|
56
|
+
pattern-either:
|
|
57
|
+
- pattern: raise NotImplementedError(...)
|
|
58
|
+
- pattern: throw new Error("not implemented")
|
|
59
|
+
- pattern: throw new Error("TODO")
|
|
60
|
+
- pattern: |
|
|
61
|
+
def $F(...):
|
|
62
|
+
pass
|
|
63
|
+
- pattern: |
|
|
64
|
+
def $F(...):
|
|
65
|
+
...
|
|
66
|
+
- pattern: |
|
|
67
|
+
func $F(...) { }
|
|
68
|
+
- pattern: |
|
|
69
|
+
return null // TODO
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Run:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
semgrep --config /tmp/stubs.yml --json <local-repo-path> > /tmp/stubs.json
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Parse:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
jq '[.results[] | {file: .path, line: .start.line, rule: .check_id}]' /tmp/stubs.json
|
|
82
|
+
jq '.results | length' /tmp/stubs.json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Capture: `stub_count`, `stub_per_kloc`, `stub_files` (distinct files with stubs).
|
|
86
|
+
|
|
87
|
+
### Signal 3 — Git analytics (cramming + scope)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
cd <local-repo-path>
|
|
91
|
+
|
|
92
|
+
# Commit count
|
|
93
|
+
git rev-list --count HEAD
|
|
94
|
+
|
|
95
|
+
# Contributor count
|
|
96
|
+
git shortlog -sn --all | wc -l
|
|
97
|
+
|
|
98
|
+
# Date range (days between first and last commit)
|
|
99
|
+
first=$(git log --reverse --format=%ct | head -1)
|
|
100
|
+
last=$(git log --format=%ct | head -1)
|
|
101
|
+
echo "$(( (last - first) / 86400 )) days"
|
|
102
|
+
|
|
103
|
+
# Last-day commit share (cramming signal)
|
|
104
|
+
total=$(git log --format=%ct | wc -l)
|
|
105
|
+
last_day_cutoff=$(( last - 86400 ))
|
|
106
|
+
last_day=$(git log --format=%ct | awk -v c=$last_day_cutoff '$1 >= c' | wc -l)
|
|
107
|
+
echo "Last-day commits: $last_day / $total"
|
|
108
|
+
|
|
109
|
+
# Commit size distribution — mean lines added per commit
|
|
110
|
+
git log --shortstat --pretty=format: | awk '/insertion/ {sum+=$4; n++} END {print (n?sum/n:0)}'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Capture:
|
|
114
|
+
|
|
115
|
+
- `commit_count`
|
|
116
|
+
- `contributor_count`
|
|
117
|
+
- `project_duration_days` (first commit → last commit)
|
|
118
|
+
- `last_day_commit_share` (ratio, 0–1)
|
|
119
|
+
- `mean_lines_per_commit`
|
|
120
|
+
- `cramming_suspected` (bool: `last_day_commit_share > 0.3`)
|
|
121
|
+
|
|
122
|
+
### Signal 4 — Tier-A build check (BROKEN gate trigger)
|
|
123
|
+
|
|
124
|
+
Dep-less syntax / parse / undefined-name checks. Runs whatever applies to the primary language:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
cd <local-repo-path>
|
|
128
|
+
|
|
129
|
+
# Python — syntax + undefined-name
|
|
130
|
+
if find . -maxdepth 4 -name '*.py' -not -path '*/node_modules/*' -not -path '*/.venv/*' | head -1 > /dev/null; then
|
|
131
|
+
# Syntax
|
|
132
|
+
find . -name '*.py' -not -path '*/.venv/*' -not -path '*/node_modules/*' \
|
|
133
|
+
-exec python -m py_compile {} \; 2>&1 | head -20 > /tmp/py-syntax.txt
|
|
134
|
+
# Undefined names (ruff rule F821)
|
|
135
|
+
ruff check --select F --output-format json . > /tmp/ruff.json 2>/dev/null || true
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# TypeScript — noEmit parse check
|
|
139
|
+
if [ -f tsconfig.json ]; then
|
|
140
|
+
npx --yes tsc --noEmit --skipLibCheck --pretty false 2>&1 | head -20 > /tmp/tsc.txt
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# Go — vet + gofmt
|
|
144
|
+
if [ -f go.mod ]; then
|
|
145
|
+
go mod download 2>&1 | head -5
|
|
146
|
+
go vet ./... 2>&1 | head -20 > /tmp/govet.txt
|
|
147
|
+
gofmt -l . 2>&1 | head -20 > /tmp/gofmt.txt
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# JavaScript — node --check on entrypoints
|
|
151
|
+
for f in $(jq -r '.main // empty' package.json 2>/dev/null) index.js src/index.js; do
|
|
152
|
+
[ -f "$f" ] && node --check "$f" 2>&1
|
|
153
|
+
done
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Aggregate `build_check_passed` bool — true if all applicable checks produced zero errors:
|
|
157
|
+
|
|
158
|
+
- Python: `py_compile` exit 0 AND ruff F-errors == 0
|
|
159
|
+
- TS: `tsc --noEmit --skipLibCheck` exit 0
|
|
160
|
+
- Go: `go vet` exit 0 AND `gofmt -l` empty
|
|
161
|
+
- JS: `node --check` exit 0
|
|
162
|
+
|
|
163
|
+
Capture: `build_check_passed` (bool), `build_errors_summary` (string — first 20 lines for LLM context), `build_checks_applied` (list of checks that ran).
|
|
164
|
+
|
|
165
|
+
**Escalation note:** if errors contain `Cannot find module` / `ModuleNotFoundError`, flag `DEPS_NOT_INSTALLED` — Tier-A can't distinguish "broken" from "needs install." Scorer may choose to escalate to Tier-B (shallow install) for such repos.
|
|
166
|
+
|
|
167
|
+
### Signal 5 — LLM README-vs-shipped diff
|
|
168
|
+
|
|
169
|
+
Assemble:
|
|
170
|
+
|
|
171
|
+
1. `README.md` full contents
|
|
172
|
+
2. `tree -L 2 <local-repo-path>` (top 2 levels)
|
|
173
|
+
3. `scc` language-mix output
|
|
174
|
+
4. `stub_count` and top 5 stub file paths
|
|
175
|
+
|
|
176
|
+
Score:
|
|
177
|
+
|
|
178
|
+
- Does the README promise N features? How many are visibly shipped?
|
|
179
|
+
- Is the built scope reasonable for the project's apparent size/duration?
|
|
180
|
+
- Are there obvious overreach signals (README talks about "microservices architecture" but repo is one file)?
|
|
181
|
+
- Are there obvious underreach signals (README claims "MVP" but repo has 5000 LOC and 20 stubs)?
|
|
182
|
+
|
|
183
|
+
Capture:
|
|
184
|
+
|
|
185
|
+
- `llm_promises_count` (features named in README)
|
|
186
|
+
- `llm_shipped_count` (features visibly implemented)
|
|
187
|
+
- `llm_shipped_ratio` = `llm_shipped_count ÷ llm_promises_count`
|
|
188
|
+
- `llm_scope_match_score` (0–5) — integrated "right-sized?" judgment
|
|
189
|
+
- `llm_notes` (one sentence, e.g. "README promises 4 features; 2 shipped + 2 stubbed under feature flags")
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Raw dumps (flat — files under `raw/` named `mechanical-ambition-*`)
|
|
194
|
+
|
|
195
|
+
Each signal writes its raw output to the flat `raw/` directory at the workspace root. Absent files = not extracted. Aggregates go to `scorecard.yaml` under `data.`.
|
|
196
|
+
|
|
197
|
+
| File | Source signal | Shape / note |
|
|
198
|
+
| --- | --- | --- |
|
|
199
|
+
| `raw/mechanical-ambition-scc.json` | Signal 1 | Raw `scc --format=json` output (per-language LoC, complexity, COCOMO estimate) |
|
|
200
|
+
| `raw/mechanical-ambition-stubs.json` | Signal 2 | Semgrep stub-rule findings: `[{file, line, rule, snippet}]`. Empty array when no stubs. |
|
|
201
|
+
| `raw/mechanical-ambition-git-analytics.json` | Signal 3 | `{commit_count, contributor_count, project_duration_days, first_commit, last_commit, last_day_commit_share, mean_lines_per_commit, cramming_suspected, commits_by_day: [{date, count}]}` |
|
|
202
|
+
| `raw/mechanical-ambition-tier-a-build.log` | Signal 4 | Combined output of checks that ran (py_compile, ruff -F, tsc --noEmit, go vet, gofmt, node --check). First 20 lines per tool. |
|
|
203
|
+
| `raw/mechanical-ambition-llm-read.md` | Signal 5 | README-vs-shipped diff: `{promises: [...], shipped: [...], shipped_ratio, scope_match_score, notable_gaps, one_sentence}` |
|
|
204
|
+
|
|
205
|
+
Additional short-lived intermediates: the `stubs.yml` semgrep rule config is a skill artifact — generate it under `$SKILL_DIR/criteria/mechanical-ambition-stubs.yml` (or inline on-the-fly), not in the repo workspace.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Aggregation → 0–5 band
|
|
210
|
+
|
|
211
|
+
Scorer integrates the 5 signals. Rough directionality:
|
|
212
|
+
|
|
213
|
+
| Signal | Pulls score UP | Pulls score DOWN |
|
|
214
|
+
| --- | --- | --- |
|
|
215
|
+
| `build_check_passed` | true | false → `BROKEN` gate fires |
|
|
216
|
+
| `commit_count` + `total_loc` | commits > 50, LoC > 1000 | both low → thin delivery, pulls score down |
|
|
217
|
+
| `stub_per_kloc` | < 0.5 | > 5 |
|
|
218
|
+
| `llm_shipped_ratio` | ≥ 0.8 | < 0.5 |
|
|
219
|
+
| `cramming_suspected` | false | true (last-day share > 0.3) |
|
|
220
|
+
| `cocomo_estimated_months` | matches apparent size/duration | wildly overshoots or undershoots |
|
|
221
|
+
|
|
222
|
+
**Banding (absolute):**
|
|
223
|
+
|
|
224
|
+
- **5** — Scope matches delivery (`llm_shipped_ratio ≥ 0.9`); `stub_per_kloc < 0.5`; build passes; delivery evenly spread; LLM ≥ 4
|
|
225
|
+
- **4** — Strong delivery, one visible gap (e.g. 3/4 features shipped, minor delivery-concentration spike)
|
|
226
|
+
- **3** — Most features shipped (0.6–0.8 ratio); 1–3 unfinished; stub density 0.5–2/kLoC; some delivery concentration
|
|
227
|
+
- **2** — Overreached visibly; shipped ratio 0.4–0.6; stubs 2–5/kLoC; **`BROKEN` gate fires here (cap at 2)**.
|
|
228
|
+
- **1** — README promises 4+ features, repo ships 1; stub density > 5/kLoC; delivery heavily concentrated; or gates firing AND low quality
|
|
229
|
+
- **0** — Suppressed (gates enforce; use only when repo is truly empty or broken)
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Gates
|
|
234
|
+
|
|
235
|
+
| Gate | Trigger | Cap |
|
|
236
|
+
| --- | --- | --- |
|
|
237
|
+
| `BROKEN` | `build_check_passed == false` (and not because of `DEPS_NOT_INSTALLED`) | `mechanical_ambition ≤ 2` |
|
|
238
|
+
|
|
239
|
+
Affects this sub-criterion only. Canonical gate list in [../SKILL.md](../SKILL.md).
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Fallback — signals missing
|
|
244
|
+
|
|
245
|
+
- scc doesn't support a language → LoC count may under-report; document in evidence.
|
|
246
|
+
- Semgrep stub rule misses project-specific stub patterns → LLM catches what regex missed; increase weight of Signal 5.
|
|
247
|
+
- Repo has < 5 commits → git analytics degrade (cramming, commit-rate meaningless); rely on Signals 1, 2, 5.
|
|
248
|
+
- `DEPS_NOT_INSTALLED` flag on Tier-A → `BROKEN` gate does NOT fire on this alone; scorer should either: (a) escalate to Tier-B, or (b) defer build-status judgment to the LLM's read of repo shape.
|
|
249
|
+
- README missing → Signal 5 is degraded; emit `llm_confidence: low`.
|
|
250
|
+
|
|
251
|
+
Annotate evidence with `"Signals used: [...]"`.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Output (what the scorer emits)
|
|
256
|
+
|
|
257
|
+
```yaml
|
|
258
|
+
sub_criterion: mechanical_ambition
|
|
259
|
+
score: 3
|
|
260
|
+
evidence: "1240 LOC TypeScript + 380 LOC Python; stub density 1.3/kLoC; git shows 42 commits over 6 days, last-day share 0.28 (borderline delivery concentration); Tier-A build passes; README promises 4 features, 2.5 shipped (dashboard + API done; scheduler + notifications stubbed). Overreached on scheduler."
|
|
261
|
+
data:
|
|
262
|
+
total_loc: 1620
|
|
263
|
+
total_files: 47
|
|
264
|
+
language_mix:
|
|
265
|
+
[
|
|
266
|
+
{ lang: TypeScript, code: 1240, files: 31 },
|
|
267
|
+
{ lang: Python, code: 380, files: 16 },
|
|
268
|
+
]
|
|
269
|
+
total_complexity: 142
|
|
270
|
+
mean_complexity_per_file: 3.0
|
|
271
|
+
cocomo_estimated_months: 0.4
|
|
272
|
+
stub_count: 9
|
|
273
|
+
stub_per_kloc: 1.3
|
|
274
|
+
stub_files: [src/scheduler.ts, src/notifications.ts, ...]
|
|
275
|
+
commit_count: 42
|
|
276
|
+
contributor_count: 3
|
|
277
|
+
project_duration_days: 6
|
|
278
|
+
last_day_commit_share: 0.28
|
|
279
|
+
mean_lines_per_commit: 38.5
|
|
280
|
+
cramming_suspected: false
|
|
281
|
+
build_check_passed: true
|
|
282
|
+
build_checks_applied: [tsc_noemit, ruff_F, py_compile]
|
|
283
|
+
build_errors_summary: ""
|
|
284
|
+
llm_promises_count: 4
|
|
285
|
+
llm_shipped_count: 2
|
|
286
|
+
llm_shipped_ratio: 0.5
|
|
287
|
+
llm_scope_match_score: 3
|
|
288
|
+
signals_used: [scc, semgrep_stubs, git_analytics, build_check, llm]
|
|
289
|
+
gates_triggered: []
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Caveats
|
|
295
|
+
|
|
296
|
+
- **Tier-A build check catches ~80% of breakage.** Import-resolution errors need deps installed. `DEPS_NOT_INSTALLED` flag suppresses the `BROKEN` gate when the check failed for install reasons.
|
|
297
|
+
- **Stub detection via Semgrep misses project-specific patterns.** A function that returns hardcoded fake data without any stub marker looks "shipped" but isn't. LLM Signal 5 compensates.
|
|
298
|
+
- **Delivery-concentration detection is heuristic.** Last-day share > 30% flags concentrated delivery but doesn't distinguish "final polish" from a single large drop. LLM evidence should adjudicate when the flag fires.
|
|
299
|
+
- **`llm_shipped_ratio` depends on how the README frames features.** Terse READMEs may under-count promises; florid READMEs may over-count. Evidence should show the promise list.
|
|
300
|
+
- **COCOMO is a sanity check, not a score driver.** `cocomo_estimated_months > 3` is a soft-overreach signal relative to the project's apparent size; `< 0.1` is a soft-underreach signal. Don't gate on it.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Problem Substantiveness — Extraction Plan
|
|
2
|
+
|
|
3
|
+
- **Sub-criterion:** 2.1 (Product Thinking)
|
|
4
|
+
- **Weight:** 12.5 pts
|
|
5
|
+
- **Source quote:** _"Is the problem clearly stated, non-trivial, with defined user/use-case?"_
|
|
6
|
+
|
|
7
|
+
This recipe scores whether the problem is substantively framed — a crisp, non-trivial problem statement with a defined user and use-case, versus a toy-shaped placeholder.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Extraction recipe
|
|
12
|
+
|
|
13
|
+
LLM-primary. The evaluator reads the top of the repo (README, any PRD/SPEC doc, sample data, one or two handler files) and answers: **Is this a substantive, well-defined problem, or a toy-shaped placeholder?**
|
|
14
|
+
|
|
15
|
+
### Signal 1 — Problem framing (LLM interpretive)
|
|
16
|
+
|
|
17
|
+
Feed the LLM the following context:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# README (truncated to first ~500 lines)
|
|
21
|
+
head -500 "$REPO/README.md" > /tmp/prob-sub-readme.md
|
|
22
|
+
|
|
23
|
+
# PRD / SPEC / DESIGN docs if present
|
|
24
|
+
find "$REPO" -maxdepth 3 -iname "PRD*.md" -o -iname "SPEC*.md" -o -iname "DESIGN*.md" \
|
|
25
|
+
-not -path '*/node_modules/*' -not -path '*/.venv/*' 2>/dev/null \
|
|
26
|
+
| xargs -I{} head -200 {} 2>/dev/null > /tmp/prob-sub-docs.md
|
|
27
|
+
|
|
28
|
+
# Sample data files — check provenance
|
|
29
|
+
find "$REPO" -maxdepth 4 \( -iname '*.csv' -o -iname '*.json' -o -iname 'sample*' -o -iname 'fixture*' -o -iname 'seed*' \) \
|
|
30
|
+
-not -path '*/node_modules/*' -not -path '*/.venv/*' 2>/dev/null | head -10 > /tmp/prob-sub-data-files.txt
|
|
31
|
+
|
|
32
|
+
# Top-level folder structure (reveals user/use-case intent)
|
|
33
|
+
tree -L 2 -I 'node_modules|.venv|.git|dist|build' "$REPO" 2>/dev/null | head -80 > /tmp/prob-sub-tree.txt
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Write the LLM read to `raw/problem-substantiveness-llm-read.md`. The LLM emits:
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
detected_domain: "internal dev tooling" | "customer-facing SaaS" | "infra automation" | "ops observability" | "data pipeline" | "agent/LLM tooling" | ...
|
|
40
|
+
problem_clarity_score: 0-5 # 5 = crisp problem statement with user + constraint + outcome named; 0 = no statement
|
|
41
|
+
target_user_inferred: "internal engineers" | "support agents" | "end customers" | "unclear" | ...
|
|
42
|
+
sample_data_provenance: "synthetic" | "redacted-real" | "production" | "none" | "unclear"
|
|
43
|
+
toy_pattern_detected: true | false # hello-world, generic TODO app, calculator, tic-tac-toe, etc.
|
|
44
|
+
one_sentence: "<LLM's one-line verdict on whether this is a substantive problem>"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Signal 2 — Proxies JSON
|
|
48
|
+
|
|
49
|
+
Aggregate the LLM output into `raw/problem-substantiveness-proxies.json`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"detected_domain": "...",
|
|
54
|
+
"problem_clarity_score": 0-5,
|
|
55
|
+
"target_user_inferred": "...",
|
|
56
|
+
"sample_data_provenance": "...",
|
|
57
|
+
"toy_pattern_detected": true|false
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Banding (absolute)
|
|
64
|
+
|
|
65
|
+
| Band | Criteria |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| **5** | Problem statement is crisp: user + constraint + outcome named. Domain is non-trivial (enterprise ops / a non-trivial real-world domain / novel agent workflow). Sample data is production-shaped. `problem_clarity_score: 5`, `toy_pattern_detected: false`. |
|
|
68
|
+
| **4** | Problem statement is substantive with one minor gap (user inferable but not named; outcome fuzzy). `problem_clarity_score: 4`, `toy_pattern_detected: false`. |
|
|
69
|
+
| **3** | Problem is stated but under-specified: missing user definition OR missing success criterion. `problem_clarity_score: 3`. |
|
|
70
|
+
| **2** | Problem statement is vague or generic. README is mostly boilerplate. `problem_clarity_score: 2`. |
|
|
71
|
+
| **1** | No problem statement, or README is an unmodified starter/template README. `problem_clarity_score ≤ 1`. |
|
|
72
|
+
| **0** | Empty README + no PRD/SPEC + toy pattern detected. |
|
|
73
|
+
|
|
74
|
+
**Toy-pattern auto-floor:** if `toy_pattern_detected == true` (hello-world, generic TODO, calculator, tic-tac-toe, etc.), cap the band at **2** regardless of other signals — a clear problem statement doesn't rescue a toy.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Raw dumps
|
|
79
|
+
|
|
80
|
+
- `raw/problem-substantiveness-llm-read.md` — LLM's full interpretive read with axis scores.
|
|
81
|
+
- `raw/problem-substantiveness-proxies.json` — aggregated JSON of signals (also embedded in `scorecard.yaml.problem_substantiveness.data`).
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Not in scope
|
|
86
|
+
|
|
87
|
+
- Judging whether the problem matches a specific organization's real priorities needs operational context — out of scope; this scores whether the problem is substantively framed, not whether it's the right problem for any given org to be solving.
|
|
88
|
+
- Judging the quality of the implementation — that's 1.1–1.4 + 2.4. This recipe only looks at framing.
|