pcina 0.1.1__tar.gz

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 (49) hide show
  1. pcina-0.1.1/.gitignore +13 -0
  2. pcina-0.1.1/LICENSE +21 -0
  3. pcina-0.1.1/PKG-INFO +156 -0
  4. pcina-0.1.1/README.md +129 -0
  5. pcina-0.1.1/docs/adr/ADR-001-tool-scope-engine.md +55 -0
  6. pcina-0.1.1/docs/adr/ADR-002-scoring-model.md +143 -0
  7. pcina-0.1.1/docs/agentic-loop-goal.md +80 -0
  8. pcina-0.1.1/docs/dev-tasks.md +80 -0
  9. pcina-0.1.1/docs/field-triage.md +55 -0
  10. pcina-0.1.1/pcina.toml +12 -0
  11. pcina-0.1.1/pyproject.toml +40 -0
  12. pcina-0.1.1/pytest.ini +3 -0
  13. pcina-0.1.1/src/pcina/__init__.py +12 -0
  14. pcina-0.1.1/src/pcina/analyzer.py +485 -0
  15. pcina-0.1.1/src/pcina/budget.py +66 -0
  16. pcina-0.1.1/src/pcina/cli.py +246 -0
  17. pcina-0.1.1/src/pcina/config.py +55 -0
  18. pcina-0.1.1/src/pcina/diff.py +82 -0
  19. pcina-0.1.1/src/pcina/files.py +50 -0
  20. pcina-0.1.1/src/pcina/finding.py +54 -0
  21. pcina-0.1.1/src/pcina/metrics/__init__.py +35 -0
  22. pcina-0.1.1/src/pcina/metrics/clever.py +36 -0
  23. pcina-0.1.1/src/pcina/metrics/complexity.py +39 -0
  24. pcina-0.1.1/src/pcina/metrics/deadcode.py +98 -0
  25. pcina-0.1.1/src/pcina/metrics/hardcode.py +37 -0
  26. pcina-0.1.1/src/pcina/metrics/nesting.py +40 -0
  27. pcina-0.1.1/src/pcina/metrics/paranoia.py +52 -0
  28. pcina-0.1.1/src/pcina/metrics/size.py +57 -0
  29. pcina-0.1.1/src/pcina/metrics/yagni.py +35 -0
  30. pcina-0.1.1/src/pcina/outputs/__init__.py +1 -0
  31. pcina-0.1.1/src/pcina/outputs/json_out.py +59 -0
  32. pcina-0.1.1/src/pcina/outputs/md.py +76 -0
  33. pcina-0.1.1/src/pcina/outputs/term.py +116 -0
  34. pcina-0.1.1/src/pcina/scoring.py +135 -0
  35. pcina-0.1.1/tests/__init__.py +0 -0
  36. pcina-0.1.1/tests/conftest.py +22 -0
  37. pcina-0.1.1/tests/fixtures/clean/__init__.py +1 -0
  38. pcina-0.1.1/tests/fixtures/clean/api.py +28 -0
  39. pcina-0.1.1/tests/fixtures/clean/core.py +49 -0
  40. pcina-0.1.1/tests/fixtures/neuroslop/__init__.py +0 -0
  41. pcina-0.1.1/tests/fixtures/neuroslop/api.py +126 -0
  42. pcina-0.1.1/tests/fixtures/neuroslop/core.py +34 -0
  43. pcina-0.1.1/tests/test_analyzer.py +66 -0
  44. pcina-0.1.1/tests/test_budgets.py +48 -0
  45. pcina-0.1.1/tests/test_cli.py +119 -0
  46. pcina-0.1.1/tests/test_field_regressions.py +147 -0
  47. pcina-0.1.1/tests/test_metrics.py +94 -0
  48. pcina-0.1.1/tests/test_outputs.py +69 -0
  49. pcina-0.1.1/tests/test_scoring.py +75 -0
pcina-0.1.1/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ # pcina development
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .coverage
13
+ pcina-baseline.json
pcina-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 alexkay28
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pcina-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.5
2
+ Name: pcina
3
+ Version: 0.1.1
4
+ Summary: Python Code-pattern Investigator & Nonsense Analyzer - KISS/YAGNI harness for AI-generated code
5
+ Project-URL: Homepage, https://github.com/AlexKay28/pcina
6
+ Project-URL: Repository, https://github.com/AlexKay28/pcina
7
+ Project-URL: Issues, https://github.com/AlexKay28/pcina/issues
8
+ Project-URL: Changelog, https://github.com/AlexKay28/pcina/blob/main/docs/agentic-loop-goal.md
9
+ Author: alexkay28
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-generated-code,ast,code-quality,dead-code,kiss,linter,static-analysis,yagni
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Quality Assurance
24
+ Classifier: Topic :: Software Development :: Testing
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+
28
+ # pcina
29
+
30
+ Automatic checker for **KISS/YAGNI paradigm alignment**. pcina parses your Python code
31
+ (pure `ast`, one pass) and rates it per aspect out of 10 — mechanizing the ethos of the
32
+ classic KISS/YAGNI advice ("write the minimum code a new reader understands in one
33
+ pass; build nothing speculative") into a single fast command. It targets the
34
+ over-defensive, over-abstracted, over-nested output that both AI agents and humans
35
+ produce — without importing anyone's opinion on formatting or typing.
36
+
37
+ ## Install
38
+
39
+ Pure stdlib. Python >= 3.10. Zero runtime dependencies.
40
+
41
+ ```bash
42
+ pip install pcina # from PyPI
43
+ # or
44
+ pipx install pcina # isolated CLI tool
45
+ # or straight from GitHub
46
+ pip install git+https://github.com/AlexKay28/pcina.git
47
+ ```
48
+
49
+ Use it from Python too — scores and findings are plain data:
50
+
51
+ ```python
52
+ from pcina.analyzer import analyze
53
+ from pcina.metrics import findings_for
54
+ from pcina.scoring import score
55
+
56
+ project = analyze(["src/"], [])
57
+ findings = findings_for(project)
58
+ print(score(project, findings)["overall"]) # e.g. 9.48
59
+ for f in findings:
60
+ if f.severity in ("warn", "error"):
61
+ print(f.severity, f.aspect, f"{f.file}:{f.line}", f.message)
62
+ ```
63
+
64
+
65
+ ## Quickstart
66
+
67
+ ```bash
68
+ pcina scan src/ # advisory report
69
+ pcina check src/ # budget gate: exit 1 on violations (CI)
70
+ pcina report --md src/ # markdown report (drops into an agent context)
71
+ pcina baseline --save src/ # snapshot findings
72
+ pcina baseline --compare src/ # delta vs snapshot
73
+ pcina scan --json src/ # stable pcina/1 JSON schema
74
+ pcina scan --diff main src/ # only findings on lines changed vs BASE (git)
75
+ pcina scan --exclude 'tests/*' src/
76
+ ```
77
+
78
+ ## The 8 aspects
79
+
80
+ | Aspect | Principle | Meaning |
81
+ |---|---|---|
82
+ | complexity | KISS | a reader should understand a function in one pass |
83
+ | paranoia | KISS | validate at trust boundaries, not inside internal helpers |
84
+ | nesting | KISS | flat beats nested — guard clauses, early return |
85
+ | hardcode | KISS | no magic values forcing the reader to reconstruct facts |
86
+ | clever | KISS | if a reader must mentally execute the line, rewrite it |
87
+ | deadcode | YAGNI | unused code was never (or no longer) needed |
88
+ | yagni | YAGNI | no speculative abstraction for hypothetical futures |
89
+ | size | KISS | one outcome per function; a docstring is the 30-second test |
90
+
91
+ ## Terminal output (real run)
92
+
93
+ `pcina scan src/` on pcina's own source:
94
+
95
+ ```
96
+ == pcina scan: src/ (22 files, 974 LLOC) ==
97
+
98
+ complexity : 9.9/10 paranoia : 10.0/10
99
+ nesting : 10.0/10 hardcode : 10.0/10
100
+ deadcode : 9.9/10 yagni : 10.0/10
101
+ clever : 10.0/10 size : 10.0/10
102
+
103
+ src/pcina/analyzer.py:365 [warn][nesting] nesting depth 5 - invert conditions, return early
104
+ fix: guard clauses: invert the outer conditions and return
105
+ src/pcina/cli.py:138 [warn][complexity] function _cmd_baseline complexity cc=15
106
+ fix: split the function or name complex conditions as predicates
107
+
108
+ statistics:
109
+ loc=1485 lloc=974 files=22 functions=107 classes=11
110
+ docstring_coverage=79% comment_ratio=2%
111
+
112
+ Your code has been rated at 9.82/10
113
+ ```
114
+
115
+ Finding lines are single-line, greppable: `file:line [sev][aspect] message`.
116
+
117
+ ## Known false positives (honest)
118
+
119
+ **Dynamic dispatch.** The deadcode aspect flags "no callers found" for functions wired
120
+ up dynamically — CLI entry points, plugin registries, `__all__` re-exports. Static
121
+ analysis cannot see those callers. **Verify before deleting anything** pcina marks
122
+ as a deletion candidate.
123
+
124
+ pcina already excludes from deadcode analysis: dunders (`__init__`, `__call__`, …),
125
+ contents of `__init__.py` (import surface), functions named `main`, and test files.
126
+ Typing imports used only in annotations, class bases and cross-module re-exports
127
+ count as uses — all verified on real-world repos (see `docs/field-triage.md`).
128
+
129
+ **Intentional import markers.** Two conventions from the wider tooling
130
+ ecosystem (Ruff/Vulture) that pcina honors:
131
+
132
+ ```python
133
+ from pkg import thing as thing # redundant alias = explicit re-export marker
134
+ import encodings.idna # noqa: F401 # noqa = intentional, do not report
135
+ ```
136
+
137
+
138
+ ## Exit codes (contract)
139
+
140
+ | Code | Meaning |
141
+ |---|---|
142
+ | 0 | clean — no budget violations |
143
+ | 1 | budget violation (`pcina check`) |
144
+ | 2 | error / CLI usage problem |
145
+
146
+ Scores themselves are advisory; `pcina check` fails only on explicit budget
147
+ violations, never on the rating.
148
+
149
+ ## Roadmap
150
+
151
+ v0.1.1 "real-world proof" cycle — install from GitHub, proven on foreign repos,
152
+ CI-gated: see [docs/agentic-loop-goal.md](docs/agentic-loop-goal.md).
153
+
154
+ ## License
155
+
156
+ MIT
pcina-0.1.1/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # pcina
2
+
3
+ Automatic checker for **KISS/YAGNI paradigm alignment**. pcina parses your Python code
4
+ (pure `ast`, one pass) and rates it per aspect out of 10 — mechanizing the ethos of the
5
+ classic KISS/YAGNI advice ("write the minimum code a new reader understands in one
6
+ pass; build nothing speculative") into a single fast command. It targets the
7
+ over-defensive, over-abstracted, over-nested output that both AI agents and humans
8
+ produce — without importing anyone's opinion on formatting or typing.
9
+
10
+ ## Install
11
+
12
+ Pure stdlib. Python >= 3.10. Zero runtime dependencies.
13
+
14
+ ```bash
15
+ pip install pcina # from PyPI
16
+ # or
17
+ pipx install pcina # isolated CLI tool
18
+ # or straight from GitHub
19
+ pip install git+https://github.com/AlexKay28/pcina.git
20
+ ```
21
+
22
+ Use it from Python too — scores and findings are plain data:
23
+
24
+ ```python
25
+ from pcina.analyzer import analyze
26
+ from pcina.metrics import findings_for
27
+ from pcina.scoring import score
28
+
29
+ project = analyze(["src/"], [])
30
+ findings = findings_for(project)
31
+ print(score(project, findings)["overall"]) # e.g. 9.48
32
+ for f in findings:
33
+ if f.severity in ("warn", "error"):
34
+ print(f.severity, f.aspect, f"{f.file}:{f.line}", f.message)
35
+ ```
36
+
37
+
38
+ ## Quickstart
39
+
40
+ ```bash
41
+ pcina scan src/ # advisory report
42
+ pcina check src/ # budget gate: exit 1 on violations (CI)
43
+ pcina report --md src/ # markdown report (drops into an agent context)
44
+ pcina baseline --save src/ # snapshot findings
45
+ pcina baseline --compare src/ # delta vs snapshot
46
+ pcina scan --json src/ # stable pcina/1 JSON schema
47
+ pcina scan --diff main src/ # only findings on lines changed vs BASE (git)
48
+ pcina scan --exclude 'tests/*' src/
49
+ ```
50
+
51
+ ## The 8 aspects
52
+
53
+ | Aspect | Principle | Meaning |
54
+ |---|---|---|
55
+ | complexity | KISS | a reader should understand a function in one pass |
56
+ | paranoia | KISS | validate at trust boundaries, not inside internal helpers |
57
+ | nesting | KISS | flat beats nested — guard clauses, early return |
58
+ | hardcode | KISS | no magic values forcing the reader to reconstruct facts |
59
+ | clever | KISS | if a reader must mentally execute the line, rewrite it |
60
+ | deadcode | YAGNI | unused code was never (or no longer) needed |
61
+ | yagni | YAGNI | no speculative abstraction for hypothetical futures |
62
+ | size | KISS | one outcome per function; a docstring is the 30-second test |
63
+
64
+ ## Terminal output (real run)
65
+
66
+ `pcina scan src/` on pcina's own source:
67
+
68
+ ```
69
+ == pcina scan: src/ (22 files, 974 LLOC) ==
70
+
71
+ complexity : 9.9/10 paranoia : 10.0/10
72
+ nesting : 10.0/10 hardcode : 10.0/10
73
+ deadcode : 9.9/10 yagni : 10.0/10
74
+ clever : 10.0/10 size : 10.0/10
75
+
76
+ src/pcina/analyzer.py:365 [warn][nesting] nesting depth 5 - invert conditions, return early
77
+ fix: guard clauses: invert the outer conditions and return
78
+ src/pcina/cli.py:138 [warn][complexity] function _cmd_baseline complexity cc=15
79
+ fix: split the function or name complex conditions as predicates
80
+
81
+ statistics:
82
+ loc=1485 lloc=974 files=22 functions=107 classes=11
83
+ docstring_coverage=79% comment_ratio=2%
84
+
85
+ Your code has been rated at 9.82/10
86
+ ```
87
+
88
+ Finding lines are single-line, greppable: `file:line [sev][aspect] message`.
89
+
90
+ ## Known false positives (honest)
91
+
92
+ **Dynamic dispatch.** The deadcode aspect flags "no callers found" for functions wired
93
+ up dynamically — CLI entry points, plugin registries, `__all__` re-exports. Static
94
+ analysis cannot see those callers. **Verify before deleting anything** pcina marks
95
+ as a deletion candidate.
96
+
97
+ pcina already excludes from deadcode analysis: dunders (`__init__`, `__call__`, …),
98
+ contents of `__init__.py` (import surface), functions named `main`, and test files.
99
+ Typing imports used only in annotations, class bases and cross-module re-exports
100
+ count as uses — all verified on real-world repos (see `docs/field-triage.md`).
101
+
102
+ **Intentional import markers.** Two conventions from the wider tooling
103
+ ecosystem (Ruff/Vulture) that pcina honors:
104
+
105
+ ```python
106
+ from pkg import thing as thing # redundant alias = explicit re-export marker
107
+ import encodings.idna # noqa: F401 # noqa = intentional, do not report
108
+ ```
109
+
110
+
111
+ ## Exit codes (contract)
112
+
113
+ | Code | Meaning |
114
+ |---|---|
115
+ | 0 | clean — no budget violations |
116
+ | 1 | budget violation (`pcina check`) |
117
+ | 2 | error / CLI usage problem |
118
+
119
+ Scores themselves are advisory; `pcina check` fails only on explicit budget
120
+ violations, never on the rating.
121
+
122
+ ## Roadmap
123
+
124
+ v0.1.1 "real-world proof" cycle — install from GitHub, proven on foreign repos,
125
+ CI-gated: see [docs/agentic-loop-goal.md](docs/agentic-loop-goal.md).
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,55 @@
1
+ # ADR-001: Tool scope, engine and integration model
2
+
3
+ - **Status:** Accepted (2026-10-30)
4
+ - **Deciders:** user (alexkay28), builder agent
5
+ - **Context:** AI agents write large volumes of over-defensive, over-abstracted, over-nested
6
+ code ("neuroslop"). Existing SOTA tools (Ruff, Radon, Vulture, Pylint, wemake-python-styleguide)
7
+ each emit per-rule findings but none answer *"how bloated is this code, per aspect, with one
8
+ fast command"* in a format both a human and an agent can act on. The user runs the
9
+ `alexkay/pythont-dry-code-devevelopment@1.1.0` skill, whose KISS/YAGNI smell taxonomy
10
+ (paranoid raises, deep nesting, magic values, speculative abstraction, shadowed names,
11
+ clever-code towers) pcina must mechanize.
12
+ - **Core mission (user-confirmed 2026-10-30):** pcina plays the role of an **automatic checker
13
+ for YAGNI and KISS paradigm alignment**. This is the backbone and the actual goal of the
14
+ library. Every metric, finding, score and budget must justify itself as enforcing KISS
15
+ (minimum code a new reader understands in one pass, flat over nested, no cleverness
16
+ towers, no magic values, no paranoia at non-boundary call sites) or YAGNI (nothing
17
+ speculative: no unused/dead code, no zero-caller abstractions, no "in case we need it
18
+ later" layers). Findings that cannot be traced to KISS or YAGNI do not belong in pcina;
19
+ general style/typing/lint coverage is explicitly out of scope (Ruff's job). Principle map:
20
+
21
+ | Aspect | Principle | Principle anchor |
22
+ |---|---|---|
23
+ | complexity | KISS | understands in one pass: limited branching per responsibility |
24
+ | paranoia | KISS | keep validation only at trust boundaries; internal paranoia is cognitive noise |
25
+ | nesting | KISS | flat is better than nested (guard clauses, early return) |
26
+ | hardcode | KISS | magic values force the reader to reconstruct domain facts |
27
+ | clever | KISS | if a reader must mentally execute the line, rewrite it |
28
+ | deadcode | YAGNI | unused code was never (or no longer) needed |
29
+ | yagni | YAGNI | speculative abstraction for hypothetical futures |
30
+ | size | KISS | one outcome per function; docs == the 30-second reconstruction test |
31
+ - **Decision:**
32
+ 1. pcina is a **single pure-stdlib `ast` analyzer** (one parse per file, one visitor pass)
33
+ with **zero runtime dependencies**. Ruff/Vulture are optional future backends, never core.
34
+ 2. Metric families (v1, all included): `complexity`, `paranoia`, `nesting`, `hardcode`,
35
+ `deadcode`, `yagni`, `clever`, `size` (docs/params/LOC). Findings attach to code
36
+ **sections** (module → class → function) and are advisory review candidates.
37
+ 3. CLI: `scan` (report), `check` (budget gate, exit 1 on violation), `report`
38
+ (markdown), `baseline` (snapshot/delta). `--diff BASE` filters to changed lines.
39
+ 4. Three output formats: human terminal (color), `--json` (stable schema v1, agent
40
+ target), `--md` (drops into an agent context per skill step 6).
41
+ 5. Every report ends with the **pylint-style rating line, per-aspect scores and a
42
+ statistics block** (spec in ADR-002).
43
+ 6. Validation heuristics distinguish **trust boundaries** (entry points, IO, config,
44
+ `__init__`, decorated endpoints, CLI `main`) from internal code; repeated defensive
45
+ validation inside internal helpers is flagged, boundary validation is not.
46
+ 7. Acceptance: pcina must rate its own source ≥ 9.0/10 and produce zero `error` findings
47
+ on itself (self-application gate), zero warn/error on the `clean/` fixture, and the
48
+ expected finding set on the `neuroslop/` fixture.
49
+ - **Consequences:** we own all semantics (no Radon dependency, which is largely
50
+ unmaintained) at the cost of writing our own reference scanner for dead-code. The tool
51
+ stays installable everywhere via stdlib only, and fast (target: 100k LOC < 5 s,
52
+ parallel parse via `concurrent.futures`).
53
+ - **Alternatives rejected:** wrapping external tools (slow, dependency-heavy, unsuitable
54
+ for inline agent loops); Ruff-plugin form (Ruff cannot express aggregate per-aspect
55
+ scoring and section grouping the user requires).
@@ -0,0 +1,143 @@
1
+ # ADR-002: Scoring model and statistics calculation (internal_spec)
2
+
3
+ - **Status:** Accepted (2026-10-30)
4
+ - **Deciders:** user (alexkay28), builder agent
5
+ - **Context:** the user requires a pylint-like evaluation experience:
6
+ an overall rating (`Your code has been rated at 9.48/10`), per-aspect scores, and a
7
+ statistics block — but Pylint's own score mixes unrelated message categories and
8
+ penalizes convention noise heavily. Semgrep's lesson: output must be plain,
9
+ greppable, line-oriented so both humans and shell/awk-ish consumers can use it.
10
+ - **Decision:** the model below is the **normative internal specification**. Scores are
11
+ advisory signals, never CI gates (gates are budget violations, see ADR-001).
12
+
13
+ ## 1. Base quantities
14
+
15
+ | Quantity | Definition (normative) |
16
+ |---|---|
17
+ | `LOC` | physical non-blank lines of the analyzed sources |
18
+ | `LLOC` | logical lines: count of `ast.stmt` nodes, excluding `FunctionDef`/`AsyncFunctionDef`/`ClassDef` **signature lines only** (their bodies count). Comments and docstrings excluded |
19
+ | `N_FUN`, `N_CLS` | counts of analyzed function/class definitions |
20
+ | `DOC_COVERED` | functions/classes/modules with a non-empty docstring |
21
+
22
+ ## 2. Severity and finding weights
23
+
24
+ Finding severity (per ADR-001 metric table):
25
+
26
+ | Severity | Meaning | Base weight `w` |
27
+ |---|---|---:|
28
+ | `error` | near-certain defect or strong bloat (bare except, shadowed module import, unused-export) | 3.0 |
29
+ | `warn` | smell needing review | 1.0 |
30
+ | `info` | advisory observation | 0.2 |
31
+
32
+ ## 3. Aspect scores
33
+
34
+ Every finding belongs to exactly one aspect: `complexity`, `paranoia`, `nesting`,
35
+ `hardcode`, `deadcode`, `yagni`, `clever`, `size`.
36
+
37
+ For aspect `a` over scope with `LLOC` statements:
38
+
39
+ ```
40
+ P_a = Σ (w(severity_i) * aspect_penalty_mult[a] * fix_loc_mult_i) for findings i in a
41
+ score_a = clamp(0, 10 - K * P_a / max(LLOC, 50))
42
+ ```
43
+
44
+ - `K = 10` (so weighted penalty equal to LLOC ⇒ score 0; pylint's "10 - …" shape).
45
+ - `max(LLOC, 50)`: small files must not produce wild scores from 2 findings.
46
+ - `fix_loc_mult_i` = 1.0 (flat for v1; reserved for severity scaling by lines touched).
47
+ - `aspect_penalty_mult` — because aspects differ in natural frequency:
48
+
49
+ | Aspect | mult | Rationale |
50
+ |---|---:|---|
51
+ | complexity | 1.5 | one bad function is a real comprehension cost |
52
+ | paranoia | 1.0 | frequent; most common AI smell |
53
+ | nesting | 1.5 | strong readability damage |
54
+ | hardcode | 0.5 | mostly cosmetic |
55
+ | deadcode | 1.5 | deleted code is pure win |
56
+ | yagni | 1.0 | speculative layers |
57
+ | clever | 1.0 | localized cleverness |
58
+ | size | 0.5 | docs/params are softer signals |
59
+
60
+ These multipliers are software calibration constants: they live in
61
+ `pcina/scoring.py` as a data table, are printed via `pcina report --show-weights`,
62
+ and must never be duplicated elsewhere.
63
+
64
+ ## 4. Overall rating
65
+
66
+ ```
67
+ P = Σ P_a over all aspects
68
+ rating = clamp(0, 10 - 10 * P / max(LLOC, 50)) # one decimal, pylint-style line
69
+ ```
70
+
71
+ The overall rating is **not** a quality verdict (per skill: tool scores guide review).
72
+ `pcina check` never fails on the rating — only on explicit budget violations.
73
+
74
+ ## 5. Statistics block (normative fields)
75
+
76
+ Report must always end with (term and `--md` formats; same fields under `statistics`
77
+ in JSON):
78
+
79
+ ```
80
+ statistics:
81
+ loc=520 lloc=231 files=9 functions=47 classes=8
82
+ docstring_coverage=72% comment_ratio=11%
83
+ avg_cc=4.2 max_cc=17 p75_cc=6
84
+ raises=31 try_except=12 bare_excepts=0
85
+ dead_candidates=5 yagni_candidates=3 magic_values=14
86
+ ```
87
+
88
+ Definitions:
89
+
90
+ - `avg_cc`, `max_cc`, `p75_cc`: cyclomatic complexity over functions (radon CC semantics:
91
+ +1 per `if/elif/for/while/except/with/assert/comprehension/bool-op`).
92
+ - `comment_ratio` = comment lines / LOC (as `%`).
93
+ - `docstring_coverage` = documented defs+modules / total defs+modules (as `%`).
94
+ - Counters (`raises`, …) = raw pattern occurrences as defined per metric module.
95
+
96
+ ## 6. Human output template (term format)
97
+
98
+ ```
99
+ == pcina scan: src/ (9 files, 231 LLOC) ==
100
+
101
+ complexity : 9.3/10 paranoia : 4.2/10
102
+ nesting : 8.8/10 hardcode : 6.1/10
103
+ deadcode : 9.9/10 yagni : 9.0/10
104
+ clever : 9.7/10 size : 9.5/10
105
+
106
+ src/api.py:42 [warn][paranoia] 6 'raise' guards in internal helper 'load_user_args' — validation belongs to trust boundary
107
+ fix: keep one boundary check in caller; delete redundant guards
108
+ ...
109
+
110
+ statistics:
111
+ loc=... lloc=... (see §5)
112
+
113
+ Your code has been rated at 8.42/10
114
+ ```
115
+
116
+ - Finding lines are single-line `file:line [sev][aspect] message` — greppable,
117
+ `awk`/`grep`-friendly (`grep '\[warn\]'`), mirroring semgrep's usability lesson.
118
+ - The rating line is machine-matchable: `^Your code has been rated at ([0-9.]+)/10$`.
119
+ - With `--compare-baseline` the rating line gains `(previous: X.XX/10, +Δ)`, pylint-style.
120
+
121
+ ## 7. JSON schema v1 (frozen at v1.0.0)
122
+
123
+ ```json
124
+ {
125
+ "schema": "pcina/1",
126
+ "scope": {"roots": ["src"], "files": 9, "loc": 520, "lloc": 231},
127
+ "scores": {"overall": 8.42, "aspects": {"complexity": 9.3, "...": 0}},
128
+ "statistics": {"functions": 47, "...": 0},
129
+ "findings": [
130
+ {"aspect": "paranoia", "severity": "warn", "file": "src/api.py", "line": 42,
131
+ "section": "api:load_user_args", "metric": "raise-guard-burst",
132
+ "message": "...", "fix": "..."}
133
+ ],
134
+ "budget_violations": []
135
+ }
136
+ ```
137
+
138
+ Additive-only changes allowed within schema version; renames require `pcina/2`.
139
+
140
+ - **Consequences:** deterministic, testable scores (snapshot tests on fixtures);
141
+ calibration is one data table; small-file floor of 50 LLOC prevents noise spikes.
142
+ Risk: multipliers are judgment calls — mitigated by fixture calibration tests
143
+ (clean fixture must rate ≥ 9.0, neuroslop fixture ≤ 6.0).
@@ -0,0 +1,80 @@
1
+ # Agentic loop goal — v0.1.1 "real-world proof" cycle
2
+
3
+ - **Status:** Active (2026-10-30)
4
+ - **Loop:** `/loop 5m [investigate - plan - todo - implement - check - apply pcina on itself - check - fix - log]`
5
+ - **Session protocol (unchanged):** plan in main session, spawn subagent "developers"
6
+ with strict file ownership and normative ADRs as contracts; main session keeps
7
+ headroom for common sense and verification only.
8
+
9
+ ## Vision (why this cycle exists)
10
+
11
+ v0.1 closed the self-application loop: 8 KISS/YAGNI metric families, scoring
12
+ (ADR-002), CLI with scan/check/report/baseline, 42 tests green, self-rated
13
+ **9.82/10 with zero errors**. A tool that scores itself is credible; a tool nobody
14
+ else can install and run is a diary entry. This cycle upgrades pcina from a
15
+ verified prototype to a **shippable, externally-consumable tool proven on code it
16
+ did not write** — without breaking its own backbone rule: every change must
17
+ enforce KISS or YAGNI (ADR-001 principle map).
18
+
19
+ ## Goal statement (v0.1.1)
20
+
21
+ Any developer (or AI agent) must be able to `pipx install pcina` (or from GitHub),
22
+ run `pcina scan` on an arbitrary Python repo in one command, get per-aspect
23
+ scores with actionable findings, and gate CI with `pcina check` — and pcina's
24
+ own self-score must stay >= 9.0 with zero errors.
25
+
26
+ ## Success criteria (all must be true at cycle end)
27
+
28
+ 1. **README.md exists** at repo root: what/why (anti-"neuroslop" KISS/YAGNI checker),
29
+ install, quickstart, aspect table (from ADR-001), exit-code contract, output
30
+ format samples (term + json + md). Written for someone who has never seen an ADR.
31
+ 2. **Runs clean on foreign code:** `pcina scan` (and `--json`) executes without
32
+ crash on >= 3 real external repos (e.g. local clones chosen by size/variety —
33
+ must include one repo that is deliberately over-engineered). Triage every crash
34
+ and every obviously-bogus finding found this way; either fix the metric or
35
+ document the exclusion — no silent ignoring.
36
+ 3. **CI gate:** a GitHub Actions workflow (pure stdlib, no pip deps) running pytest +
37
+ `pcina check src/` with budgets configured so the gate fails on error findings.
38
+ The repo's own main branch must pass it.
39
+ 4. **False-positive budget shrinks:** the two bug classes found by self-scan
40
+ (decorators, `__all__`) get regression tests in `tests/`; any new FP found on
41
+ foreign code gets a test too. "Known FP list" documented in README honestly.
42
+ 5. **Self-discipline maintained:** pcina self-score >= 9.0, zero errors, pytest green,
43
+ `pyproject.toml` version bumped to 0.1.1, everything pushed to GitHub `main`.
44
+
45
+ ## Non-goals (say no in this cycle)
46
+
47
+ - No new metric families (beyond the 8) — reach comes before depth.
48
+ - No Ruff/Vulture backends, no LLM integration, no config language growth.
49
+ - No refactoring round for the accepted warns in analyzer/diff/files/cli
50
+ (tracked, but roadmap belongs to v0.2), unless a foreign-code crash forces it.
51
+
52
+ ## Out-of-scope guard (from ADR-001, restated)
53
+
54
+ Findings that cannot be traced to KISS or YAGNI do not belong in pcina. General
55
+ style/typing/lint coverage stays out — Ruff's job.
56
+
57
+ ## Suggested developer split (subagents with file ownership)
58
+
59
+ - **dev-R:** `README.md` (+ nothing else). Input: both ADRs + `--help` output.
60
+ - **dev-F (field testing):** no src ownership — drives `pcina scan --json` on 3+
61
+ foreign repos, writes `docs/field-triage.md` with crash log + FP triage. Fixes
62
+ assigned back to owners.
63
+ - **dev-Ci:** `.github/workflows/ci.yml` + minimal `pcina.toml` budgets for src/.
64
+ - **dev-M (metric fixes):** `src/pcina/metrics/*` + `tests/` regression tests
65
+ for every FP dev-F reports.
66
+ - Owners of analyzer/scoring/cli stay as in docs/dev-tasks.md for crash fixes.
67
+
68
+ ## Cycle log
69
+
70
+ - 2026-10-30 (main session I): goal defined after v0.1 verification closed (9.82/10 self-score).
71
+ - 2026-10-30 (main session II): COMPLETE — all 5 success criteria met:
72
+ 1. README.md shipped (dev-R subagent; install via git+ URL, real output sample, honest FP section).
73
+ 2. Field runs: requests (9.55), flask (9.68), poetry (9.59) — zero crashes, valid pcina/1 JSON; poetry (over-engineered target) shows ~6x findings density.
74
+ 3. CI gate: DESOPED by user decision (2026-10-30) — "we don't need ci now". The blocker was account-level Actions scheduling on a private repo (no runner assigned, 3s, no logs — billing/quota side). The `pcina.toml` baseline budget gate stays as a LOCAL gate (`pcina check src/` -> exit 0); workflow was removed. Re-add CI in a later cycle when the repo goes public or billing is sorted.
75
+ 4. Five FP classes fixed (annotations, class bases, cross-module re-exports, `__init__.py` surface + latent INIT_BASENAME bug, noqa markers) — each has a regression test in tests/test_field_regressions.py (52 tests total, incl. over-suppression guard and self-score floor >= 9.0). Industry conventions adopted after web research: Ruff F401 (redundant alias, ignore-init-module-imports), Vulture (#216 annotation refs, noqa, confidence laddering).
76
+ 5. Version 0.1.1, pushed to origin/main; pytest 52 passed; self-scan 9.80/10 zero errors; `pcina check` exit 0.
77
+
78
+ Every loop iteration appends one line to this file under "Cycle log"
79
+ (timestamp, what was investigated/implemented, self-score, verdict).
80
+ Full triage: docs/field-triage.md.