Skip to content

feat: vera/addEffect call-graph effect propagation (#222 Phase F3)#723

Merged
aallan merged 2 commits into
mainfrom
feat/222-phase-f3-add-effect
Jun 11, 2026
Merged

feat: vera/addEffect call-graph effect propagation (#222 Phase F3)#723
aallan merged 2 commits into
mainfrom
feat/222-phase-f3-add-effect

Conversation

@aallan

@aallan aallan commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

Phase F3 of the issue 222 skill layer — vera/addEffect, the multi-site workflow that completes the arc.

Per the Phase F design comment:

  • Request {uri, fn, effect} computes the transitive-caller closure by inverting the Phase B call walker (direct_callee_names): adding an effect to a function invalidates every transitive caller's row, so the whole closure rewrites in one candidate through the proposeEdit pipeline — all-or-nothing, never a half-propagated document.
  • Per-function rewrite by span (facts verified against the parser: PureEffect.span covers exactly pure; EffectSet.span includes the brackets): effects(pure)effects(<E>); effects(<A>)effects(<A, E>) appending after the original source verbatim; functions already naming the effect are skipped. Effect identity is the base name before type arguments, so State<Int> is not added next to State<Bool>.
  • Response carries rewritten (declaration order). A row state already satisfied everywhere short-circuits to the documented no-op shape (applied: false, ok: true, proof_delta: null, rewritten: []) without touching the verifier.
  • Deliberate bounds (documented in the module docstring): handler-unaware propagation (a caller handling the effect in handle[E] is still rewritten — bounding at handlers is noted future work); single-file (module-qualified calls never propagate across the file boundary); declared-but-unused effects verified legal empirically, so add-then-use agent ordering works.

Tests (13 new; 4,312 total)

  • Closure goldens: diamond in declaration order, leaf-only, unknown-fn None, recursion appears once.
  • Rewrite goldens: pure→singleton, source-preserving append (<IO, State<Int>><IO, State<Int>, Async>), already-present None, base-name identity.
  • The F3 pin: diamond propagation applies one multi-site candidate — four rows rewritten, the bystander's effects(pure) untouched.
  • Mixed rows (append + replace + skip-already-satisfied), the no-op shape pinned exactly, and both refusal paths.

Release prep (v0.0.168) + issue-closing bookkeeping

Version across all 6 tracked sites, CHANGELOG + compare links, ROADMAP Phase F row deleted, HISTORY rows for v0.0.166–v0.0.168 (Stage 12 table), TESTING/README counts, module map (lsp/ → 1389 lines), site assets.

The three skill-layer methods (proposeEdit v0.0.166, strengthenContract v0.0.167, addEffect this PR) complete the Phase F design; with Phases A–E (v0.0.161–v0.0.165) this finishes the issue's full arc.

Closes #222

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Phase F3 "addEffect" workflow to propagate and apply effect edits across call graphs.
  • Tests

    • Expanded LSP workflow tests with a new Phase F3 suite; total tests now 4,312 across 34 files.
  • Chores

    • Bumped project version to v0.0.168.
  • Documentation

    • Updated CHANGELOG, HISTORY, README, ROADMAP and TESTING to reflect the release and counts.

The multi-site workflow completing the skill layer: {uri, fn, effect}
computes the transitive-caller closure over the Phase B call walker,
rewrites every affected effects(...) clause by span (pure -> <E>;
<A> -> <A, E> appending after the original source verbatim; functions
already naming the effect skipped, identity = base name before type
args), and runs ONE candidate through the proposeEdit pipeline. The
response lists rewritten functions in declaration order; an
already-satisfied row state short-circuits to the documented no-op
shape. Handler-unaware by design; single-file by design.

13 new tests: closure goldens (diamond order, leaf, unknown, recursion
once), rewrite goldens (pure->singleton, source-preserving append,
already-present, base-name identity), diamond propagation with
bystander untouched, mixed append/replace, the no-op shape, refusal
paths. Release prep for v0.0.168; ROADMAP Phase F row removed;
HISTORY rows for v0.0.166-168.

Co-Authored-By: Claude <[email protected]>
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b3ebf021-4f94-48c1-969b-15e95dba30e7

📥 Commits

Reviewing files that changed from the base of the PR and between 7902a0b and d9e3251.

📒 Files selected for processing (1)
  • HISTORY.md

📝 Walkthrough

Walkthrough

Phase F3 vera/addEffect is a multi-site effect-propagation workflow for the LSP server. It computes transitive callers via call-graph closure, rewrites affected functions' effect rows using idempotent span-based splicing, and applies edits through the proposeEdit gate. This release includes comprehensive test coverage and updates all project metadata to 0.0.168.

Changes

Phase F3 addEffect workflow implementation and release

Layer / File(s) Summary
Release versioning and documentation
CHANGELOG.md, HISTORY.md, README.md, ROADMAP.md, TESTING.md, pyproject.toml, vera/__init__.py
Version bumped to 0.0.168 with Phase F3 feature documented in CHANGELOG and HISTORY. Test count updated from 4,299 to 4,312. Completed backlog item #222 removed from ROADMAP. Project status and test metadata synchronised across all files.
LSP handler registration and test imports
vera/lsp/server.py, tests/test_lsp.py
vera/addEffect custom LSP method handler registered with parameter validation (uri, fn, non-empty effect) and ValueError-to-JsonRpcInvalidParams error mapping. Workflow helpers and Phase F3 test section imported into test module.
Workflow specification and implementation helpers
vera/lsp/workflows.py
Docstring specification of vera/addEffect contract and transitive-caller semantics. Import of direct_callee_names for call-graph construction. Helpers for computing transitive_callers closure in declaration order and effect_row_rewrite for idempotent span-based effect-set mutations.
Core add_effect workflow orchestration
vera/lsp/workflows.py
Main add_effect function that acquires analysis lock, derives affected transitive callers via closure, builds span-based rewrites per caller, short-circuits on no-op, applies rewrites in reverse offset order, delegates to apply_propose_edit for verification and application, and returns annotated response with rewritten function list.
Test fixtures and comprehensive feature validation
tests/test_lsp.py
_fn and DIAMOND fixtures for test program construction. TestTransitiveCallers validates closure computation including diamond-graph ordering and recursive function handling. TestEffectRowRewrite validates pure→singleton conversion, set append, no-op idempotence, and type-argument identity matching. TestAddEffect validates multi-node propagation, mixed-row handling, no-op response, unknown-function and unanalysed-document error cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • aallan/vera#721: The new vera/addEffect workflow applies its rewrites via the Phase F1 proposeEdit/apply_propose_edit verification gate implemented in this PR.
  • aallan/vera#717: The new vera/addEffect workflow uses direct_callee_names from vera/obligations/cache.py, which was introduced by the Phase B incremental invalidation changes in this PR.

Suggested labels

compiler, tests, docs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature: implementing the vera/addEffect workflow for call-graph effect propagation as Phase F3 of issue #222.
Linked Issues check ✅ Passed The PR implements the addEffect LSP feature for Phase F3. The linked issue #222 calls for LSP server features; this PR delivers one specific workflow component (addEffect) that integrates with the existing compiler pipeline as required.
Out of Scope Changes check ✅ Passed All changes are tightly scoped: implementation of the vera/addEffect workflow, supporting test coverage, version bumps, and documentation updates aligned with Phase F3. No unrelated refactoring or infrastructure changes are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/222-phase-f3-add-effect

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.52941% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.90%. Comparing base (6ee74b6) to head (d9e3251).

Files with missing lines Patch % Lines
vera/lsp/server.py 25.00% 9 Missing ⚠️
vera/lsp/workflows.py 92.95% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #723      +/-   ##
==========================================
- Coverage   90.93%   90.90%   -0.03%     
==========================================
  Files          70       70              
  Lines       24772    24854      +82     
  Branches      292      292              
==========================================
+ Hits        22526    22594      +68     
- Misses       2239     2253      +14     
  Partials        7        7              
Flag Coverage Δ
javascript 61.40% <ø> (ø)
python 94.54% <83.52%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_lsp.py (1)

1093-1145: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add handler-level tests for vera/addEffect boundary behaviour.

Line 1097 onwards validates the workflow directly, but the new protocol path in vera/lsp/server.py (Line 262-280) is not exercised. Please add in-process handler tests for method registration, blank-effect refusal, and ValueErrorJsonRpcInvalidParams mapping.

Suggested test shape
+class TestAddEffectHandler:
+    def test_blank_effect_refused_as_invalid_params(self) -> None:
+        from vera.lsp.server import create_server
+        server = create_server()
+        protocol = server.protocol
+        fm = protocol.fm if hasattr(protocol, "fm") else server.feature_manager
+        handler = fm.features["vera/addEffect"]
+        with pytest.raises(JsonRpcInvalidParams, match="non-empty effect"):
+            handler({"uri": URI, "fn": "f", "effect": "   "})
+
+    def test_workflow_valueerror_mapped_to_invalid_params(self) -> None:
+        from vera.lsp.server import create_server
+        server = create_server()
+        protocol = server.protocol
+        fm = protocol.fm if hasattr(protocol, "fm") else server.feature_manager
+        handler = fm.features["vera/addEffect"]
+        with pytest.raises(JsonRpcInvalidParams):
+            handler({"uri": URI, "fn": "ghost", "effect": "Async"})

As per coding guidelines, tests/**/*.py reviews should flag missing edge cases for new compiler features.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_lsp.py` around lines 1093 - 1145, Add in-process tests that
exercise the LSP protocol path for the "vera/addEffect" handler (not just the
helper add_effect()); instantiate the real LSP server used in vera/lsp/server.py
(via your existing _server helper), then invoke the registered method entrypoint
for "vera/addEffect" (call it through the server's method-dispatch API or
directly via the server's registered handler map) to assert three behaviors: the
method is actually registered, a blank/empty effect parameter is rejected with a
JSON-RPC invalid-params response, and any ValueError thrown by the underlying
logic is translated into JsonRpcInvalidParams; reuse symbols from this test file
(URI, _server, _FakeServer, add_effect) to set up requests and to compare
outcomes.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@HISTORY.md`:
- Line 315: Update the v0.0.168 HISTORY entry to explicitly mention that the
workflow rewrites effects(...) clauses as part of Phase F3: locate the entry for
"v0.0.168" and amend the sentence about transitive-caller closure and the single
verified multi-site edit to include wording like "and rewrites effects(...)
clauses" (or equivalent) so the line states closure + a single verified
multi-site edit that rewrites effects(...), matching the CHANGELOG/release
contract.

In `@README.md`:
- Line 207: The README release count ("Vera is in **active development** at
v0.0.168 — ... 168 releases") is out of sync with the HISTORY.md footer (which
reports 165 tagged releases); update the mismatched value so both locations
match exactly: either correct the README phrase "168 releases" to the exact
tagged-release total from HISTORY.md or update HISTORY.md's footer to the
correct total after verifying tag count (use git tag --list to confirm), and
ensure both the README string and the HISTORY.md footer are identical.

---

Outside diff comments:
In `@tests/test_lsp.py`:
- Around line 1093-1145: Add in-process tests that exercise the LSP protocol
path for the "vera/addEffect" handler (not just the helper add_effect());
instantiate the real LSP server used in vera/lsp/server.py (via your existing
_server helper), then invoke the registered method entrypoint for
"vera/addEffect" (call it through the server's method-dispatch API or directly
via the server's registered handler map) to assert three behaviors: the method
is actually registered, a blank/empty effect parameter is rejected with a
JSON-RPC invalid-params response, and any ValueError thrown by the underlying
logic is translated into JsonRpcInvalidParams; reuse symbols from this test file
(URI, _server, _FakeServer, add_effect) to set up requests and to compare
outcomes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 714a12a4-07f9-412d-aca1-edef1abf3447

📥 Commits

Reviewing files that changed from the base of the PR and between 6ee74b6 and 7902a0b.

⛔ Files ignored due to path filters (5)
  • docs/index.html is excluded by !docs/**
  • docs/index.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (10)
  • CHANGELOG.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • TESTING.md
  • pyproject.toml
  • tests/test_lsp.py
  • vera/__init__.py
  • vera/lsp/server.py
  • vera/lsp/workflows.py

Comment thread HISTORY.md Outdated
Comment thread README.md
…count footer (#723 round 1)

The HISTORY footer roll-up (165 tagged releases) had not tracked the
F1/F2/F3 README bumps; now 168 in both locations.

Skip-changelog: docs-only amendments to rows added in this same unreleased PR
@aallan aallan merged commit 8c0a0e1 into main Jun 11, 2026
27 checks passed
@aallan aallan deleted the feat/222-phase-f3-add-effect branch June 11, 2026 17:04
@aallan aallan mentioned this pull request Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LSP server

1 participant