feat(classify-signals): classification signal extraction + classify_signals method (#5) - #10
Conversation
…classify_signals method (#5) Add the signals/ package (interface, visibility, caller, naming, docstring extractors + adapter fan-out) and analysis/inference.py (astroid caller-count index), wire the classify_signals JSON-RPC method, and flip the capability flag to true. Extractors reconstructed from documented gaze-py behavior (gaze-py source unavailable); the engine is deliberately not lifted. Section 6 fixture scenarios are realized as tmp_path-built trees within the test modules rather than committed tests/fixtures/signals/ files.
…dedup, and adapter branch-coverage tests (code review)
Record the code-review gate marker in the OpenSpec change tasks after the review council's unanimous (9/9) approval. Assisted-by: claude-opus Generated with AI assistance (claude-opus)
jflowers
left a comment
There was a problem hiding this comment.
Note: Could not post as APPROVE (GitHub blocks self-approval / insufficient permissions). Posted as COMMENT instead. Original council verdict: APPROVE.
Council Verdict: APPROVE
Reviewers: The Adversary, The Architect, The Guard, The Tester, The Operator, The Curator, The Envoy, The Herald, The Scribe (9/9 discovered invoked, 0 absent)
Iterations: 1 (initial REQUEST CHANGES → APPROVE after fixes)
The Adversary (APPROVE)
- Resolved: astroid 16 MiB cap-bypass (MEDIUM) via in-project name-gated inference + finally clear_cache; non-degrade exception → -32603 (LOW) via broadened per-call except.
- LOW/advisory (non-blocking): method-name collision residual; stale docstring inference.py:26-27; dev-only ast-serialize/librt transitive dep (maintainer confirm).
The Architect (APPROVE — alignment 9/10)
- Resolved HIGH provenance self-contradiction: "lifted" vs "reconstructed" now consistent per origin (detector/complexity lifted #4; signals/*+_routing reconstructed #5; adapter/inference original; engine not lifted).
The Guard (APPROVE)
- Resolved HIGH provenance drift; LOW _routing mislabel. Report-only: verify reconstructed weights vs canonical gaze-py (escalate to CRITICAL if any diverge — none changed here). LOW wording residual (design.md:90).
The Tester (APPROVE)
- Resolved MEDIUM degradation-branch coverage (S-6) + MEDIUM zero-signal protocol test (S-7); inference.py 90→94%. LOW residual: oversized-file test stubs the cap.
The Operator (APPROVE)
- Resolved 3 MEDIUM: stderr diagnostics on all degrade paths; broadened per-call catch; inference scoping + finally-clear + honest docs. CI gates intact, astroid pinned. 2 LOW micro-opts.
The Curator (APPROVE)
- Resolved: AGENTS ASCII tree; README naming "source"; _routing parenthetical. LOW cosmetic: signals/ ordering differs README vs AGENTS.
The Envoy (APPROVE)
- Resolved 3 LOW: spelling (behaviour/recognised); provenance wording.
The Herald (APPROVE)
- Resolved 2 LOW: provenance verbs; README _routing suffix.
The Scribe (APPROVE)
- Resolved: provenance, ASCII connectors, naming "source", design.md claims verified. LOW deferred: proposal/design "New files" omit _routing.py/_types.py.
LOW findings: ~10 across personas (summarized above); none blocking.
Report-only items for maintainer:
- Verify the 5 reconstructed extractor weights vs canonical gaze-py source (interface 30; visibility ±10; caller 5/15/25; naming +15/-10; docstring 15). No weight changed/lowered on this branch; escalate to CRITICAL if any later diverges.
- Per-file copyright-header form on reconstructed code + NOTICE "lifted files" line (NOTICE out of this branch's diff; accurate for genuinely-lifted #4 files) — licensing/legal judgment.
- Confirm dev-only transitive deps ast-serialize/librt (pulled by mypy) + intended mypy version; runtime footprint unaffected.
Note: tasks.md checkboxes/review markers + proposal fixture list were intentionally left untouched per the author's direction (by-design /uf.unleash progress tracking).
This review was generated by /review-council (AI-assisted).
…ion (review council) Change "lifted/verbatim" to "reconstructed from documented gaze-py behavior" for the signal extractors, which were reconstructed from the issue's documented behavior rather than copied. Fix AGENTS.md analysis/ tree connectors and README naming.py comment. Correct design.md on-disk-resolution and degradation claims to match inference.py.
…dings Scope astroid inference to in-project defined names to prevent cap-bypassing transitive parsing, and clear the MANAGER cache in a finally block. Broaden the per-call-site catch so an uninferable call is omitted rather than failing the whole request. Emit stderr diagnostics on all degrade paths. Add degradation-branch tests (outer degrade, parametrized per-file exceptions, Uninferable skip, non-degrade exception) and protocol non-empty/source/weight assertions; fix minor docstring spelling.
Review Council — Formal Review (what I would have said as a formal review)
Council Verdict: APPROVE — 9/9 discovered divisor reviewers APPROVE The remediation is committed in this push:
Gates green after fixes: 416 tests pass, coverage 96.43% (gate 85%), Per-reviewer verdicts
What was fixed this iteration
No circular findings were encountered. Report-only items for the maintainer (cannot be resolved by review)
Non-blocking LOW follow-ups (optional)
Note on scopeThe This review was generated by /uf.review-council (AI-assisted). |
PR Cost Report: #10Session: Propose change with artifactsID:
Timeline: 2026-08-29 17:17:33 — 2026-08-31 15:51:13 Child sessions: 19 — additional cost: $48.06 Child session breakdown
Session: Skip Gaze Python reviewID:
Timeline: 2026-08-31 15:52:49 — 2026-08-31 18:15:15 Child sessions: 18 — additional cost: $27.85 Child session breakdown
Grand Total: $586.05 across 2 session tree(s) (39 sessions) |
Summary
Implements the Gaze
classify_signalsanalyzer capability (issue #5). Snake Eyes now emits raw classification signals that Gaze's Go core scores — it does not classify or score itself.src/snake_eyes/signals/), reconstructed from documented gaze-py behavior (upstream source unavailable), weights pinned as source-of-truth literals:interface(30),visibility(+10/-10),caller_count(0→none, 1-4→5, 5→15, 20→25),naming_convention(+15/-10),docstring(15).analysis/inference.py):build_caller_index/CallerIndex/count_callersover astroid, callees matched by resolved defining file path (robust tosrc/layouts).signals/adapter.pyextract_signals) + JSON-RPCclassify_signalsserver method;initializecapability flag flipped totrue.engine.pydeliberately NOT lifted (avoids scoring drift; Gaze owns scoring).How to Test
All green: 409 passed, 96.19% coverage, ruff + mypy clean.
How to Demo
initializereportscapabilities.classify_signals: true;classify_signalsreturns asignals[]array of{function, package, side_effect_type, source, weight, reasoning}.Key Files Changed
38 files, +1975/-29.
src/snake_eyes/signals/(__init__,_routing,_types,adapter,interface,visibility,caller,naming,docstring);src/snake_eyes/analysis/inference.py.src/snake_eyes/server.py(_classify_signalshandler + dispatch),src/snake_eyes/protocol.py(capability flag).pyproject.toml,uv.lock(astroid>=3.0,<4).README.md,AGENTS.md.test_signals_*,test_inference*,test_classify_signals_method) + 3 updated (test_discover_method,test_protocol,test_server).openspec/changes/classify-signals/(proposal, design, tasks, 4 specs).Known Issues
Accepted-LOW review-council findings (non-blocking; sweep at archive):
tasks.md§6 fixtures are realized astmp_path-built trees inside the test modules rather than a committedtests/fixtures/signals/directory.NOTICE/README.AGENTS.mdproject-structure tree has minor ASCII-glyph misalignment.CoverageWarning(module-not-measured) from the-m snake_eyesdeterminism subprocess (cosmetic).MANAGER.clear_cache()and implicitly validated).This PR was generated by /uf.finale (AI-assisted).