Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ python -m venv .venv
source .venv/bin/activate # Windows (PowerShell): .venv\Scripts\Activate.ps1
# Windows (Git Bash): source .venv/Scripts/activate
pip install -e ".[dev]"
pytest # optional: all tests pass (126 at v0.3.0)
pytest # optional: all tests pass (129 at v0.4.0)
```

## 3. Create the demo repo
Expand Down Expand Up @@ -132,7 +132,7 @@ A fresh-clone check was run on the current candidate:
- **OS:** Windows 11 (Git Bash)
- **Python:** 3.11.9
- **Install:** `pip install -e ".[dev]"`
- **Tests:** all passing (126 at v0.3.0)
- **Tests:** all passing (129 at v0.4.0)
- **Demo:** `dtc init` / `dtc scan` / `dtc concepts` / `dtc explain "Billing Webhooks"`
all produced the expected output from a clean `git clone`.

Expand Down
36 changes: 36 additions & 0 deletions RELEASE_NOTES_v0.4.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# DevTime v0.4.0 - diff-aware claim impact

A pull request should tell you which verified claims it destabilizes. Now it
does. No cloud, no telemetry, no AI, no code execution - unchanged.

## dtc risk --diff now reports claim impact

```
Claim impact:
- billing-webhook-signature (previous status: SUPPORTED)
changed evidence: src/billing/stripe-webhook.ts
re-verify: dtc verify billing-webhook-signature
```

The rules:

- Only a claim's recorded evidence files count. A diff touching unrelated
files never flags a claim.
- Nothing is printed when no verified claim is affected. Advisory, never noisy.
- The output names the exact changed evidence and the exact command to
re-verify.

This connects the verification layer (v0.2, v0.3) to the risk layer: risk
review says "this change class needs review"; claim impact says "this specific
verified claim may no longer hold, check it."

## Notes

- 129 passing tests (3 new: affected claim reported with previous status and
changed evidence; unrelated diffs affect nothing; no verifications means no
impact output).
- No breaking changes. Risk output gains a section only when relevant.

## Names

- PyPI distribution: `devtime-ei`. Python import: `devtime`. CLI: `dtc`.
18 changes: 16 additions & 2 deletions VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ available in community edition"). The file name claims webhook handling; the
implementation is a disabled stub. `dtc verify` reports CONTRADICTED with both
sides and exact paths.

## Diff impact (v0.4)

`dtc risk --diff` now reports which verified claims a diff destabilizes:

```
Claim impact:
- billing-webhook-signature (previous status: SUPPORTED)
changed evidence: src/billing/stripe-webhook.ts
re-verify: dtc verify billing-webhook-signature
```

Only a claim's recorded evidence files count. A diff touching unrelated files
never flags a claim, and nothing is printed when no verified claim is affected.

## Trust model

- Deterministic and rule-driven. No AI, no network, no code execution.
Expand All @@ -94,6 +108,6 @@ sides and exact paths.
## Where this is going

Next candidates, in order: more built-in claims over well-covered domains
(webhook idempotency), more contradiction detectors, and diff-aware claim
staleness in `dtc risk`. User-defined
(webhook idempotency), more contradiction detectors, and machine-readable
claim impact in risk output. User-defined
claims come after built-in claims prove trustworthy on real repositories.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "devtime-ei"
version = "0.3.0"
version = "0.4.0"
description = "Local-first Engineering Intelligence for software repositories"
readme = "README.md"
requires-python = ">=3.11"
Expand Down Expand Up @@ -50,7 +50,7 @@ dev = [
Homepage = "https://github.com/Shakargy/devtime"
Repository = "https://github.com/Shakargy/devtime"
Issues = "https://github.com/Shakargy/devtime/issues"
"Release Notes" = "https://github.com/Shakargy/devtime/releases/tag/v0.3.0"
"Release Notes" = "https://github.com/Shakargy/devtime/releases/tag/v0.4.0"
Demo = "https://youtu.be/1Hiu3Y9J_SI"

[project.scripts]
Expand Down
4 changes: 2 additions & 2 deletions server.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
"source": "github"
},
"websiteUrl": "https://github.com/Shakargy/devtime",
"version": "0.3.0",
"version": "0.4.0",
"packages": [
{
"registryType": "pypi",
"identifier": "devtime-ei",
"version": "0.3.0",
"version": "0.4.0",
"transport": {
"type": "stdio"
}
Expand Down
2 changes: 1 addition & 1 deletion src/devtime/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""DevTime - local-first Engineering Intelligence for repository memory."""

__version__ = "0.3.0"
__version__ = "0.4.0"

# Version metadata (Builder Edition, Chapter 20).
EVIDENCE_MODEL = "2026.06.1"
Expand Down
18 changes: 18 additions & 0 deletions src/devtime/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,11 +347,29 @@ def risk(
conn = connection.connect()
try:
intelligence = repository.load_all_concepts(conn)
from devtime.intelligence.verification import claims_affected_by_paths

claim_impact = claims_affected_by_paths(conn, info.changed_files)
finally:
conn.close()

review = review_diff(info, intelligence)
console.print(render_risk_review(review), markup=False)

# v0.4.0: claim impact. A diff is not just risky in general - it can
# destabilize a previously verified claim. Only evidence files count;
# nothing is printed when no verified claim is affected.
if claim_impact:
console.print("\nClaim impact:", markup=False)
for item in claim_impact:
console.print(
f" - {item['claim_id']} (previous status: {item['previous_status']})",
markup=False,
)
for p in item["changed_evidence"]:
console.print(f" changed evidence: {p}", markup=False)
console.print(f" re-verify: {item['suggested_action']}", markup=False)

if review.state == STATE_REVIEW_FAILED:
raise typer.Exit(code=1)

Expand Down
32 changes: 32 additions & 0 deletions src/devtime/intelligence/verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,38 @@ def load_latest_verification(
)


def claims_affected_by_paths(
conn: sqlite3.Connection, changed_paths: list[str]
) -> list[dict]:
"""Claim impact for a set of changed paths (v0.4.0, diff integration).

For every claim with a stored verification, report it when a changed path
is one of its recorded evidence files. Only evidence files count: a diff
touching unrelated files never flags a claim. Advisory output - the caller
decides what to do with it.
"""
changed = set(changed_paths)
out: list[dict] = []
for slug in BUILTIN_CLAIMS:
latest = load_latest_verification(conn, slug)
if latest is None:
continue
result, fingerprints, created_at = latest
evidence_paths = {fp["path"] for fp in fingerprints}
hits = sorted(evidence_paths & changed)
if hits:
out.append(
{
"claim_id": slug,
"previous_status": result["status"],
"verified_at": created_at,
"changed_evidence": hits,
"suggested_action": f"dtc verify {slug}",
}
)
return out


def freshness_for(conn: sqlite3.Connection, slug: str) -> tuple[str, list[str]]:
"""Compare stored evidence fingerprints against current file hashes.

Expand Down
8 changes: 4 additions & 4 deletions tests/integration/test_evidence_precision.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@

# --- version ------------------------------------------------------------------

def test_version_is_release_0_3_0():
# v0.3.0 release: package metadata and __version__ agree on the release version.
def test_version_is_release_0_4_0():
# v0.4.0 release: package metadata and __version__ agree on the release version.
import importlib.metadata as m

assert devtime.__version__ == "0.3.0"
assert devtime.__version__ == "0.4.0"
# Distribution is published as "devtime-ei" (the name "devtime" is reserved on
# PyPI); the import package and the dtc command stay "devtime"/"dtc".
assert m.version("devtime-ei") == "0.3.0"
assert m.version("devtime-ei") == "0.4.0"


# --- P0 Authentication headline precision ------------------------------------
Expand Down
42 changes: 42 additions & 0 deletions tests/integration/test_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,45 @@ def test_verify_all_returns_both_claims(tmp_path, monkeypatch):
payload = json.loads(result.stdout)
ids = {r["claim_id"] for r in payload["results"]}
assert ids == {"billing-webhook-signature", "jwt-authentication"}


# --- diff-aware claim impact (v0.4.0) ---------------------------------------------

def test_claims_affected_by_changed_evidence(tmp_path, monkeypatch):
_repo(tmp_path, {"src/billing/stripe-webhook.ts": STRIPE_HANDLER})
_init_scan(tmp_path, monkeypatch)
conn = connection.connect()
try:
ver.save_verification(conn, ver.verify_claim(conn, "billing-webhook-signature"))
impact = ver.claims_affected_by_paths(conn, ["src/billing/stripe-webhook.ts"])
assert len(impact) == 1
item = impact[0]
assert item["claim_id"] == "billing-webhook-signature"
assert item["previous_status"] == ver.SUPPORTED
assert item["changed_evidence"] == ["src/billing/stripe-webhook.ts"]
assert "dtc verify" in item["suggested_action"]
finally:
conn.close()


def test_unrelated_diff_affects_no_claims(tmp_path, monkeypatch):
_repo(tmp_path, {"src/billing/stripe-webhook.ts": STRIPE_HANDLER})
_init_scan(tmp_path, monkeypatch)
conn = connection.connect()
try:
ver.save_verification(conn, ver.verify_claim(conn, "billing-webhook-signature"))
impact = ver.claims_affected_by_paths(conn, ["README.md", "src/util/other.ts"])
assert impact == []
finally:
conn.close()


def test_no_verifications_means_no_impact(tmp_path, monkeypatch):
_repo(tmp_path, {"src/billing/stripe-webhook.ts": STRIPE_HANDLER})
_init_scan(tmp_path, monkeypatch)
conn = connection.connect()
try:
impact = ver.claims_affected_by_paths(conn, ["src/billing/stripe-webhook.ts"])
assert impact == []
finally:
conn.close()
Loading