@nurix/etna 0.18.4 → 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.
@@ -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.