Skip to content

Effect catalog, pyright cross-check, and resolution fixes - #7

Merged
Raman369AI merged 4 commits into
mainfrom
resolution-checks-and-effects
Sep 27, 2026
Merged

Raman369AI merged 4 commits into
mainfrom
resolution-checks-and-effects

Conversation

@Raman369AI

Copy link
Copy Markdown
Owner

Summary

Three pieces, one commit each. They follow the resolver feasibility study, which recommended against delegating resolution to Jedi or pyright.

1. Resolution fixes (b775f43)

  • Unannotated factories: a factory with no return annotation types its result when every non-None return constructs a class (return Repo(), or return build() that does). Async and generator factories don't.
  • Aliases: a name assigned once from a function or attribute (alias = clean) resolves through that value as probably. Parameters, loop targets and reassigned names stay unknown.
  • Parse errors name the Python that parsed the file. Below 3.14 they add: "if this file uses newer syntax, run Threadline with Python 3.14".

2. Pyright cross-check in CI (fdbb5f1)

  • tests/pyright_crosscheck.py asks pyright's language server where a deterministic sample of calls goes.
  • It fails if pyright names a different project function for any call Threadline marks supported. Pyright having no answer is not a failure.
  • It reports, without failing, probably mismatches and calls pyright resolves where Threadline can't, as review leads.
  • CI runs it on this repo (Linux, 3.12) and on flask and requests in the public-repository job.
  • Pyright is pinned as a dev dependency beside axe-core; there's no runtime dependency.

3. Effect catalog (00a77aa)

  • threadline/effects.py maps known library calls to effect classes, with reads and writes separate:

    Effect Recognised
    DB read/write SQLAlchemy/SQLModel sessions and DB-API cursors; execute is classified by its statement (select / insert, update, delete)
    Network read/write requests, httpx, aiohttp, urllib, by HTTP verb
    File read/write open (by mode), pathlib, os, shutil, json/pickle
    Process subprocess, os.system, …
    Logging logging, loguru, structlog
    Raises raise statements
  • Certainty: identified library targets are definite. Untyped session/db/logger receivers match by name and are possible.

  • Propagation: effects travel through resolved project calls with their path. Probable or deferred calls (an unawaited coroutine, a generator) make them possible.

  • Summary: the method summary gains an Effects row, e.g. "DB write via update_user". Raises also lists exceptions from called project functions.

    • Clicking a direct effect highlights its lines; clicking an inherited one opens the function it comes through.
  • CLI: threadline method returns the records as effects.

  • Supporting analyzer changes: external calls record their library target. with X() as y: and dotted library factories (logging.getLogger(__name__)) now type their receivers.

Example (agent-kanban-pm, POST /tasks): Effects DB write, DB read; Raises HTTPException, PermissionError via create_task_record, TaskReferenceError via create_task_record.

Gate

Each change landed its semantic-corpus cases first, with contrasting checks, and each case failed before its fix. A new effects check kind covers every library family plus propagation, including a function built from pure helpers and a recursive pair that must have no effects.

The corpus now has 73 checks, 0 incorrect supported.

Testing

  • python -m pytest: 163 pass. python test_semantic_accuracy.py: 73 pass.
  • tests/browser_smoke.py in all four modes: all pass, including new Effects-row checks.
  • tests/browser_accessibility.py --browser chrome: all pass.
  • tests/pyright_crosscheck.py --project . --repo flask --repo requests: 0 disagreements on 563 supported and 561 probably calls.
  • tests/online_repo_smoke.py: all 7 pinned repositories pass.
  • mypy (CI scope): passes.
  • Effect pass on agent-kanban-pm: 0.04 s for 1,174 functions.

Limits (documented in docs/CAPABILITIES.md)

  • Only catalogued libraries are recognised; Django's ORM isn't catalogued yet.
  • A statement passed through a variable is DB access, with no direction.
  • Effects describe source, not executed behaviour.

🤖 Generated with Claude Code

Raman369AI and others added 4 commits September 26, 2026 22:19
- A factory without a return annotation types its result when every
  non-None return constructs a class, directly or through another factory
  (return Repo(), return build()). Async and generator factories do not.
- A name assigned once from a function or attribute (alias = clean)
  resolves through that value as a possible target; parameters, loop
  targets, and reassigned names stay unknown.
- A syntax error names the Python that parsed the file and, below the
  newest tested Python, suggests running Threadline with it.

Gate: the unannotated-factory expectation moves from unknown to possible,
with new contrasts for a lookup and an async factory; a new alias case
covers direct and chained aliases and a conditional reassignment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/pyright_crosscheck.py asks pyright's language server where a
deterministic sample of calls goes and fails when pyright names a
different project function for a call Threadline labels supported.
Possible-call mismatches and calls pyright resolves where Threadline
cannot are reported as review leads without failing.

CI runs it on this repository (Linux, Python 3.12) and on flask and
requests in the public-repository job. Pyright is pinned as a dev
dependency beside axe-core; Threadline has no runtime dependency on it.

Local result: 0 disagreements across 563 supported and 560 possible
calls in threadline, flask, and requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
threadline/effects.py maps known library calls to effect classes, with
reads and writes separate: SQLAlchemy/SQLModel sessions and DB-API
cursors (execute classified by its statement), requests/httpx/aiohttp
(by HTTP verb), open/pathlib/os/shutil/json (open by mode), subprocess,
and logging. Untyped session/db/logger receivers match by name as
possible. Effects and raised exceptions propagate through resolved
project calls with their path; probable or deferred calls make them
possible.

The method summary gains an Effects row ("DB write via update_user"),
and Raises lists exceptions from called project functions. threadline
method returns the records as `effects`.

Supporting analyzer changes: external calls record their library target;
`with X() as y` and dotted library factories (logging.getLogger) type
their receivers.

Gate: an `effects` check kind with 12 cases covering each library family
and propagation, including a function of pure helpers and a recursive
pair that must have no effects. Pyright cross-check, public-repository
validation, browser smoke, and accessibility checks pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A CI runner stalled headless Chrome at startup: DevTools never answered,
and cleanup then raised TimeoutExpired because Chrome ignored terminate,
hiding the real error. The browser smoke test now tries Chrome twice with
a fresh port and profile, logs the failed attempt, and force-kills a
browser that ignores terminate. The release smoke test gets the same
force-kill fallback for its server.

Checked with a fake Chrome that never opens DevTools and ignores SIGTERM:
two attempts, both killed, one clear RuntimeError, no leftover processes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Raman369AI
Raman369AI merged commit b12c0b0 into main Sep 27, 2026
14 checks passed
@Raman369AI
Raman369AI deleted the resolution-checks-and-effects branch September 27, 2026 04:04
@Raman369AI Raman369AI mentioned this pull request Sep 27, 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.

1 participant