blot-git 0.5.0__tar.gz → 0.7.0__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.
- {blot_git-0.5.0 → blot_git-0.7.0}/.pre-commit-hooks.yaml +1 -1
- {blot_git-0.5.0 → blot_git-0.7.0}/CLAUDE.md +26 -11
- {blot_git-0.5.0 → blot_git-0.7.0}/PKG-INFO +12 -2
- {blot_git-0.5.0 → blot_git-0.7.0}/README.md +92 -1
- {blot_git-0.5.0 → blot_git-0.7.0}/action.yml +1 -1
- {blot_git-0.5.0 → blot_git-0.7.0}/bench/README.md +3 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/bench/RESULTS.md +50 -4
- {blot_git-0.5.0 → blot_git-0.7.0}/bench/cases.json +46 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/bench/labels/django-django.json +2 -2
- blot_git-0.7.0/bench/labels/expressjs-express.json +246 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/bench/labels/facebook-react.json +300 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/bench/labels/pallets-flask.json +2 -2
- blot_git-0.7.0/bench/labels/psf-requests.json +216 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/docs/PRD.md +31 -4
- {blot_git-0.5.0 → blot_git-0.7.0}/docs/PYPI.md +11 -1
- {blot_git-0.5.0 → blot_git-0.7.0}/pyproject.toml +1 -1
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/__init__.py +1 -1
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/bench.py +14 -2
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/checks/base.py +10 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/checks/removed_fix.py +35 -11
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/cli.py +107 -13
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/diff.py +25 -0
- blot_git-0.7.0/src/blot/fixmap.py +135 -0
- blot_git-0.7.0/src/blot/guard.py +177 -0
- blot_git-0.7.0/src/blot/hooks.py +279 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/index.py +16 -0
- blot_git-0.7.0/src/blot/mcp.py +383 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/trace.py +60 -23
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/why.py +28 -4
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_checks.py +143 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_cli.py +28 -0
- blot_git-0.7.0/tests/test_diff.py +80 -0
- blot_git-0.7.0/tests/test_guard.py +219 -0
- blot_git-0.7.0/tests/test_map.py +101 -0
- blot_git-0.7.0/tests/test_mcp.py +199 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_why_github.py +18 -0
- blot_git-0.5.0/bench/labels/expressjs-express.json +0 -96
- blot_git-0.5.0/bench/labels/psf-requests.json +0 -86
- blot_git-0.5.0/src/blot/hooks.py +0 -77
- blot_git-0.5.0/tests/test_diff.py +0 -39
- {blot_git-0.5.0 → blot_git-0.7.0}/.gitattributes +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/.github/ISSUE_TEMPLATE/bench_case.md +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/.github/ISSUE_TEMPLATE/check_idea.md +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/.github/pull_request_template.md +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/.github/workflows/blot.yml +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/.github/workflows/ci.yml +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/.github/workflows/release.yml +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/.gitignore +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/CONTRIBUTING.md +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/LICENSE +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/demo/demo.gif +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/demo/make_demo.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/blue/alert.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/blue/faces.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/blue/happy.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/blue/idle.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/blue/sad.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/blue/sleepy.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/blue/walk.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/blue/worried.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/build_sprites.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/mustard/alert.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/mustard/faces.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/mustard/happy.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/mustard/idle.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/mustard/sad.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/mustard/sleepy.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/mustard/walk.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/mustard/worried.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/orange/alert.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/orange/faces.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/orange/happy.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/orange/idle.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/orange/sad.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/orange/sleepy.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/orange/walk.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/orange/worried.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/sage/alert.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/sage/faces.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/sage/happy.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/sage/idle.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/sage/sad.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/sage/sleepy.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/sage/walk.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/assets/sora/sage/worried.svg +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/docs/EXPLAINER.md +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/docs/competitors.md +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/__main__.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/checks/__init__.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/checks/cochange.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/checks/readded_bug.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/classify.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/config.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/git.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/github.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/report.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/src/blot/store.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/conftest.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_action.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_bench.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_classify.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_config.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_git.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_hooks.py +0 -0
- {blot_git-0.5.0 → blot_git-0.7.0}/tests/test_index.py +0 -0
|
@@ -7,12 +7,14 @@ always changes with another (`forgot-related-file`). Mascot: Sora the slime. Own
|
|
|
7
7
|
|
|
8
8
|
## Layout
|
|
9
9
|
- `src/blot/`: stdlib-only on Python 3.11+ (3.10 also needs `tomli`; keep it that way)
|
|
10
|
-
- `cli.py` commands: index, check, why, install-hook,
|
|
10
|
+
- `cli.py` commands: index, check, why, map, mcp, install-hook, guard, install-agent-hook, install-mcp, bench.
|
|
11
|
+
`check`/`why`/`map`/`mcp` build/refresh the index themselves (`index.open_index`).
|
|
11
12
|
- `index.py` builds `.git/blot.db` (parallel `git log` slices for 2000+ commits); `store.py` SQLite schema v2
|
|
12
13
|
- `classify.py` fix severity 0/1/2 + refactor detection: most precision lives here
|
|
13
14
|
- `checks/` one file per check; `trace.py` blame that follows lines past refactor commits
|
|
14
|
-
- `
|
|
15
|
-
- `
|
|
15
|
+
- `fixmap.py` (`blot map`), `mcp.py` (stdio MCP server, both protocol eras; `install-mcp` in `hooks.py`)
|
|
16
|
+
- `guard.py` (Claude Code hook; installer in `hooks.py`), `why.py`, `github.py` (`index --github` via `gh api graphql`), `bench.py` (cases + `--replay N`)
|
|
17
|
+
- `bench/cases.json` scripted real-world cases (33/33); `bench/labels/` hand verdicts for every replay
|
|
16
18
|
warning on the 5 reference repos; `bench/RESULTS.md` replay numbers and precision
|
|
17
19
|
- `action.yml` GitHub Action; `.github/workflows/blot.yml` runs it on Blot's own PRs
|
|
18
20
|
- `assets/sora/build_sprites.py` and `assets/demo/make_demo.py` regenerate the mascot and demo GIF
|
|
@@ -22,7 +24,7 @@ always changes with another (`forgot-related-file`). Mascot: Sora the slime. Own
|
|
|
22
24
|
```bash
|
|
23
25
|
python -m venv .venv && .venv/Scripts/python -m pip install -e ".[dev]"
|
|
24
26
|
.venv/Scripts/python -m pytest -q
|
|
25
|
-
.venv/Scripts/blot bench --workdir <short path> # must stay
|
|
27
|
+
.venv/Scripts/blot bench --workdir <short path> # must stay 33/33
|
|
26
28
|
```
|
|
27
29
|
- Any change to `classify.py` or a check: on each of the 5 clones, checked out at the `head` in its
|
|
28
30
|
labels file, run `blot bench --replay 500 --labels bench/labels/<repo>.json --update-labels`. Keep a
|
|
@@ -50,11 +52,24 @@ python -m venv .venv && .venv/Scripts/python -m pip install -e ".[dev]"
|
|
|
50
52
|
- Writing code through bash heredocs mangles `\b`, `\n`, `\s`: use the Edit tool for regexes/strings.
|
|
51
53
|
- `gh api` paths: no leading slash (Git Bash rewrites it).
|
|
52
54
|
|
|
53
|
-
## State (2026-
|
|
54
|
-
- Released 0.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
55
|
+
## State (2026-11-14)
|
|
56
|
+
- Released 0.7.0: checks, `why`, `map`, `--github`, agent guard (`blot guard` / `install-agent-hook`),
|
|
57
|
+
MCP server (`blot mcp` / `install-mcp`, tools `explain_lines` and `load_bearing_lines`), bench/replay
|
|
58
|
+
with labels, parallel indexing, pre-commit framework (`rev: v0.7.0`), GitHub Action (`@v0.7.0`).
|
|
59
|
+
- Live-tested in Claude Code: the guard (2026-11-06: the agent cited fix 8efea0c it was undoing) and MCP
|
|
60
|
+
(2026-11-14: an agent called `load_bearing_lines` on its own and answered correctly).
|
|
61
|
+
Throwaway test folder: `Documentslask-guard-test` (guard hook + `.mcp.json`).
|
|
62
|
+
- `blot map` takes ~1.5 s on Flask app.py. `trace_lines(whole_file=True)` drops `-C` from the first blame so
|
|
63
|
+
map and why agree; checks still use the old path.
|
|
64
|
+
- Precision (2026-11-11, after #40): 59/108 useful (54.6%); Django 1.17 / 10 commits, others ≤ 0.6.
|
|
59
65
|
`forgot-related-file` has 0 useful warnings in 45 labelled; candidate to become opt-in.
|
|
60
|
-
-
|
|
66
|
+
- `--github` measured (2026-11-01): precision unchanged, +7 useful warnings, almost no demotions.
|
|
67
|
+
- Tried and dropped (2026-11-01): "fix word + PR number" as an offline fix rule: +85 warnings at 22%
|
|
68
|
+
useful. Don't retry it without a narrower signal (see bench/RESULTS.md).
|
|
69
|
+
- Known recall limit: tracing only passes commits classified as refactors, so a big non-refactor rewrite
|
|
70
|
+
hides older fixes (e.g. Flask `c2705ffd` hides fix #2935 in `ctx.py` `copy()`).
|
|
71
|
+
- Labels match on commit/check/path/cited commit, not line: when a rule moves a warning to another line,
|
|
72
|
+
re-check its label (#40 found two that way).
|
|
73
|
+
- Contributors: Sandro850 (#30, #38, #40). Open issues: #2–#4, #6 (bench cases), #33 (guard reports),
|
|
74
|
+
#36 (keyword-argument noise).
|
|
75
|
+
- Launch: dev.to post published 2026-10-24; the CVE sentence there should mention the withdrawn advisory.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: blot-git
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: A pre-commit check that has read your project's history: warns when a change removes a fix, re-adds a fixed bug, or forgets a file that always changes too.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Sym-jay/Blot
|
|
6
6
|
Project-URL: Issues, https://github.com/Sym-jay/Blot/issues
|
|
@@ -43,9 +43,19 @@ cd your-repo
|
|
|
43
43
|
blot index # optional: the first check indexes history itself
|
|
44
44
|
blot install-hook # adds Blot to your pre-commit hook
|
|
45
45
|
blot why src/app.py:42 # which commit explains this line, skipping refactors
|
|
46
|
+
blot map src/app.py # which lines of the file exist because of bug fixes
|
|
46
47
|
blot index --github # optional: issue/PR titles and labels via the gh CLI
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
**Guard coding agents.** `blot install-agent-hook` adds a Claude Code hook that runs the same checks on
|
|
51
|
+
an edit an agent is about to make, so the agent sees "this line exists because of fix 8efea0c" before
|
|
52
|
+
it "simplifies" it away. It warns by default; `--ask` makes Claude Code ask you first.
|
|
53
|
+
|
|
54
|
+
**Tell agents before they plan.** `blot mcp` is a stdio MCP server (standard library only) with two
|
|
55
|
+
read-only tools, `load_bearing_lines` (like `blot map`) and `explain_lines` (like `blot why`), so an agent
|
|
56
|
+
can look at a file's fixes before deciding what to change. `blot install-mcp` registers it in `.mcp.json`
|
|
57
|
+
for Claude Code; `--local` registers it just for you.
|
|
58
|
+
|
|
59
|
+
> **Status:** pre-alpha. All three checks work. Replayed on 500 recent commits each of Flask, requests, Express, Django and React, Blot warns on 0.6–9.5% of commits, and about half of its warnings (labelled by hand) are worth a reviewer's time; see the bench results on GitHub. Please report false alarms.
|
|
50
60
|
|
|
51
61
|
Meet Sora the slime, read the docs, or contribute on GitHub: https://github.com/Sym-jay/Blot
|
|
@@ -71,6 +71,97 @@ src/flask/app.py:355
|
|
|
71
71
|
|
|
72
72
|
`blot why FILE:LINE` (or `FILE:START-END`) finds the commit that explains a line, skipping commits that only moved it. Plain `git blame` stops at the refactor.
|
|
73
73
|
|
|
74
|
+
## Map a file's load-bearing lines
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
$ blot map src/flask/app.py
|
|
78
|
+
src/flask/app.py: 3 load-bearing lines in 2 ranges, from 2 fixes
|
|
79
|
+
|
|
80
|
+
355 | self_ref = weakref.ref(self)
|
|
81
|
+
8efea0c "Break reference cycle created by default in Flask instances." (2020-10-03)
|
|
82
|
+
traced past refactor 0ec7f71 "Split the App and Blueprint into Sansio and IO parts"
|
|
83
|
+
|
|
84
|
+
1061-1062 | rv = self.response_class()
|
|
85
|
+
| rv.allow.update(methods)
|
|
86
|
+
dbf55de "Fixed an issue where the default `OPTIONS` response was not exposing all valid methods in the `Allow` header." (2010-07-28, fixes #97)
|
|
87
|
+
traced past refactor 0ec7f71 "Split the App and Blueprint into Sansio and IO parts"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`blot map FILE` runs `why` over a whole file and keeps the lines whose explaining commit is a bug fix:
|
|
91
|
+
the lines `blot check` would warn about if you changed them. Consecutive lines from one fix are grouped.
|
|
92
|
+
`--full` prints the whole file with the fix commit in the gutter, `--format json` gives the ranges and
|
|
93
|
+
commits as JSON, `--rev` maps an older version, and `--min-severity 1` also counts routine fixes.
|
|
94
|
+
On Flask's 1,625-line `app.py` it takes about 1.5 s.
|
|
95
|
+
|
|
96
|
+
## Give agents the history before they edit (MCP)
|
|
97
|
+
|
|
98
|
+
`blot mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) server on stdio, so a coding
|
|
99
|
+
agent can ask about a file *before* it plans a change, instead of hearing about it from `blot guard` at
|
|
100
|
+
the last moment. It has two read-only tools:
|
|
101
|
+
|
|
102
|
+
| Tool | Same as | Arguments |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `load_bearing_lines` | `blot map FILE` | `path`, optional `rev`, `min_severity` |
|
|
105
|
+
| `explain_lines` | `blot why FILE:START-END` | `path`, `start`, optional `end`, `rev` |
|
|
106
|
+
|
|
107
|
+
Each returns one paragraph per commit: short sha, date, subject, linked issues, the lines it explains
|
|
108
|
+
and any refactor traced past. The same data comes back as JSON in `structuredContent`.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
blot install-mcp # adds `blot` to .mcp.json at the repo root and builds the index
|
|
112
|
+
blot install-mcp --local # or just for you, via `claude mcp add --scope local` (needs the claude CLI)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Claude Code asks each person once before it starts a server from `.mcp.json`. The entry names this
|
|
116
|
+
machine's Python by absolute path, like `install-agent-hook` does, so prefer `--local` if your team
|
|
117
|
+
commits `.mcp.json`. Other servers in the file are left alone; remove Blot's entry with
|
|
118
|
+
`blot install-mcp --uninstall` (add `--local` if you installed it that way). To register it by hand:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
claude mcp add --scope local --transport stdio blot -- python -m blot mcp
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The server works on the repository it is started in (or `blot --repo PATH mcp`). It speaks MCP
|
|
125
|
+
2026-07-28 and the earlier `initialize`-handshake versions (2024-11-05 to 2025-11-25), uses only the
|
|
126
|
+
standard library, writes nothing but protocol messages to stdout, and answers a bad path or an
|
|
127
|
+
untracked file with a tool error rather than exiting.
|
|
128
|
+
|
|
129
|
+
## Guard coding agents
|
|
130
|
+
|
|
131
|
+
Coding agents are the most likely to "clean up" a line they don't understand. `blot guard` is a
|
|
132
|
+
[Claude Code](https://code.claude.com/docs/en/hooks) `PreToolUse` hook on `Edit`, `Write` and `MultiEdit`:
|
|
133
|
+
it runs the `removed-fix` and `readded-bug` checks on the edit the agent is about to make, and hands any
|
|
134
|
+
finding to the agent as context:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
blot: 1 warning
|
|
138
|
+
|
|
139
|
+
! removed-fix src/flask/app.py:355
|
|
140
|
+
You're removing or changing a line that a bug fix added.
|
|
141
|
+
Check that the bug it fixed can't come back.
|
|
142
|
+
8efea0c "Break reference cycle created by default in Flask instances." (2020-10-03)
|
|
143
|
+
traced past refactor 0ec7f71 "Split the App and Blueprint into Sansio and IO parts"
|
|
144
|
+
Run `blot why FILE:LINE` for the full history before you change it.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
blot install-agent-hook # adds the hook to .claude/settings.json and builds the index
|
|
149
|
+
blot install-agent-hook --local # or .claude/settings.local.json, which isn't committed
|
|
150
|
+
blot install-agent-hook --ask # also ask you to approve edits that touch a fix
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The hook command names this machine's Python by absolute path (like `install-hook`), so prefer `--local`
|
|
154
|
+
if your team commits `.claude/settings.json`. Remove it with `blot install-agent-hook --uninstall`
|
|
155
|
+
(add `--local` if you installed it there); other hooks in the file are left alone.
|
|
156
|
+
|
|
157
|
+
By default the guard only warns: Claude Code adds the warning to the agent's context with the edit's
|
|
158
|
+
result, and the normal permission flow is unchanged. With `--ask`, Claude Code stops before the edit and
|
|
159
|
+
asks you, showing the warning. The guard prints nothing when it has nothing to say, stays quiet for
|
|
160
|
+
files outside a git repo or not tracked at HEAD, and never blocks an edit because of its own errors. It
|
|
161
|
+
doesn't build a first index (that can take a minute): `install-agent-hook` does, or run `blot index`.
|
|
162
|
+
After that it refreshes the index incrementally. On Flask a guarded edit takes about 450 ms.
|
|
163
|
+
`forgot-related-file` is left out: it had no useful warnings among the 45 labelled in replays.
|
|
164
|
+
|
|
74
165
|
## Issue and PR titles (optional)
|
|
75
166
|
|
|
76
167
|
```bash
|
|
@@ -91,7 +182,7 @@ jobs:
|
|
|
91
182
|
blot:
|
|
92
183
|
runs-on: ubuntu-latest
|
|
93
184
|
steps:
|
|
94
|
-
- uses: Sym-jay/Blot@v0.
|
|
185
|
+
- uses: Sym-jay/Blot@v0.7.0
|
|
95
186
|
```
|
|
96
187
|
|
|
97
188
|
The action checks out the pull request with its full history, installs Blot, runs `blot index` and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Run Blot on a pull request and show its findings as warning annotations.
|
|
2
2
|
# Usage (in a workflow triggered by pull_request):
|
|
3
|
-
# - uses: Sym-jay/Blot@v0.
|
|
3
|
+
# - uses: Sym-jay/Blot@v0.7.0
|
|
4
4
|
# See "Use in CI" in README.md.
|
|
5
5
|
name: Blot
|
|
6
6
|
description: History-aware checks on the commits of a pull request, shown as warning annotations
|
|
@@ -62,5 +62,8 @@ How to label:
|
|
|
62
62
|
|
|
63
63
|
Write a short `reason` either way; grouping noise by reason shows which rule to fix next.
|
|
64
64
|
|
|
65
|
+
Add `--github` to replay with issue and PR labels too, as after `blot index --github` (needs the
|
|
66
|
+
GitHub CLI logged in and a GitHub `origin` or `upstream` remote).
|
|
67
|
+
|
|
65
68
|
Target: **at most 1 warning per 10 commits**, and most of those should be worth reading.
|
|
66
69
|
See [RESULTS.md](RESULTS.md) for the latest numbers.
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Measured 2026-10-30 with `blot bench`, on Windows 11, Python 3.12.
|
|
4
4
|
|
|
5
|
-
## Cases:
|
|
5
|
+
## Cases: 33/33 correct
|
|
6
6
|
|
|
7
7
|
| Check | Cases | Precision | Recall |
|
|
8
8
|
|---|---|---|---|
|
|
9
9
|
| removed-fix | 24 (16 warn, 8 quiet) | 100% | 100% |
|
|
10
|
-
| readded-bug |
|
|
10
|
+
| readded-bug | 6 (4 warn, 2 quiet) | 100% | 100% |
|
|
11
11
|
| forgot-related-file | 3 (3 quiet) | n/a | n/a |
|
|
12
12
|
|
|
13
13
|
A quiet case now fails if its scripted edit can't be applied (it used to pass without running).
|
|
@@ -58,7 +58,23 @@ code from earlier fixes and CVEs.
|
|
|
58
58
|
| commits cited as fixes that weren't: `Fixed #21189: Cleaned up ...`, `Fixed #12882 - Moved ...`, `[DevTools] upgrade to Manifest V3`, `Change Flask.__init__ to accept ...`, error-message wording (6) | `Fixed #N:` / `Fixed #N -` ticket styles judged by their description; cleanups count as refactors; `[Tag]` prefixes and "change" subjects count as features; error messages are routine |
|
|
59
59
|
| docstrings with `backticks` or (asides) (1) | they count as prose |
|
|
60
60
|
|
|
61
|
-
###
|
|
61
|
+
### Since 0.6.0: real docstrings (#40, by @Sandro850)
|
|
62
|
+
|
|
63
|
+
`removed-fix` now parses the old Python file (`ast`, only when a warning is already on its way) and skips
|
|
64
|
+
lines inside real module, class and function docstrings. That removed one Django noise warning. It also
|
|
65
|
+
exposed two of **our** labels as wrong: in Flask `c2705ffd` and Django `ebcb13b3`, the warning used to point at
|
|
66
|
+
a docstring line, but the same change also edits the fix's code (Flask's `copy()` passing
|
|
67
|
+
`session=self.session`, from fix #2935; Django's `isinstance(normalized, (dict, str))` check, from the
|
|
68
|
+
CVE-2026-15307 fix). Now the warning points at the code, and both are relabelled useful.
|
|
69
|
+
|
|
70
|
+
| Repo | Before | After |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| pallets/flask | 3/4 (75%) | **4/4 (100%)** |
|
|
73
|
+
| django/django | 40/59 (68%) | **41/58 (71%)** |
|
|
74
|
+
| requests, Express, React | unchanged | unchanged |
|
|
75
|
+
| **All five** | 57/109 (52.3%) | **59/108 (54.6%)** |
|
|
76
|
+
|
|
77
|
+
### Noise still there (49 warnings)
|
|
62
78
|
|
|
63
79
|
| Pattern | Warnings | Why there's no rule yet |
|
|
64
80
|
|---|---|---|
|
|
@@ -67,13 +83,43 @@ code from earlier fixes and CVEs.
|
|
|
67
83
|
| new argument or keyword argument on a fix's call | 3 | adding an argument can change behaviour |
|
|
68
84
|
| formatter reflows with f-strings or joined lines (requests' Black commit) | 4 | dropping parentheses can change precedence |
|
|
69
85
|
| deleting a legacy module or feature in pieces (React) | 6 | intentional, but only the commit message says so |
|
|
70
|
-
| docstring lines that still read as code | 3 | needs to know it's inside a docstring, not just how the line looks |
|
|
71
86
|
| other: code moved to another file, fix behaviour kept while restructuring, generic re-added lines | 16 | case by case |
|
|
72
87
|
|
|
73
88
|
**`forgot-related-file` has shown no useful warning yet**: 0/45 before this release, 0/9 after. It
|
|
74
89
|
stays on, because the remaining warnings are rare (React only), but it is the first candidate to
|
|
75
90
|
become opt-in if real-world reports agree.
|
|
76
91
|
|
|
92
|
+
## Does `--github` help? (measured 2026-11-01)
|
|
93
|
+
|
|
94
|
+
`blot bench --replay 500 --github` reads issue and PR labels the way `blot index --github` does.
|
|
95
|
+
Flask, requests, Express and React track bugs on GitHub; Django's GitHub Issues are off (it uses
|
|
96
|
+
Trac), so it is unchanged. New warnings were labelled with the same rules.
|
|
97
|
+
|
|
98
|
+
| Repo | Without `--github` | With `--github` | Commits re-judged by labels |
|
|
99
|
+
|---|---|---|---|
|
|
100
|
+
| pallets/flask | 3/4 (75%) | 3/4 (75%) | 0 |
|
|
101
|
+
| psf/requests | 2/8 (25%) | **4/10 (40%)** | 15 upgraded |
|
|
102
|
+
| expressjs/express | 5/9 (56%) | 5/9 (56%) | 25 upgraded |
|
|
103
|
+
| facebook/react | 7/29 (24%) | **12/40 (30%)** | 154 upgraded, 4 demoted |
|
|
104
|
+
| **All five, with Django** | 57/109 (52.3%) | **64/122 (52.5%)** | |
|
|
105
|
+
|
|
106
|
+
**`--github` doesn't raise precision; it finds more useful warnings** (7 more, with 5 more noise).
|
|
107
|
+
Labels almost never demote a commit (4 of ~3,200 issues and PRs looked up). What they do is
|
|
108
|
+
upgrade commits like `ReactDOM: Fix missing form data ... (#28056)`, which Blot rates as unclear
|
|
109
|
+
without labels: "Fix" isn't at the start, and there's no "fixes #N" or strong defect word.
|
|
110
|
+
|
|
111
|
+
Caveat: labels are read as they are today, so a label added after a commit was made counts too.
|
|
112
|
+
|
|
113
|
+
### Tried and dropped: "a fix word plus a PR number" (2026-11-01)
|
|
114
|
+
|
|
115
|
+
Could a rule do offline what the `bug` label does? Rule tried: a fix word anywhere in the subject
|
|
116
|
+
plus an issue or PR number makes a commit a fix. It added **85 warnings** (React went from 0.60 to
|
|
117
|
+
1.95 per 10 commits), because almost every React and Express subject ends in its PR number. Of 46
|
|
118
|
+
labelled (all of requests' and Express's, 20 of React's), **10 were useful (22%)**. Most were
|
|
119
|
+
`fix(deps): …@^2.0.0` bumps, "Fix Flow type" commits and import lines. Overall precision would fall
|
|
120
|
+
from 52% to about 39%, so the rule was not kept. Its 46 labels stay in `labels/` for any narrower
|
|
121
|
+
attempt. A label written by a maintainer says more than a keyword can.
|
|
122
|
+
|
|
77
123
|
## Reproduce
|
|
78
124
|
|
|
79
125
|
```bash
|
|
@@ -408,5 +408,51 @@
|
|
|
408
408
|
"commit": "db8cb31"
|
|
409
409
|
},
|
|
410
410
|
"notes": "Changes load_dotenv's return behaviour that fix #2937 made consistent."
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"id": "requests-readd-verify-https-condition",
|
|
414
|
+
"repo": "https://github.com/psf/requests",
|
|
415
|
+
"at": "af66521381d9e6806ca80b7e297b704e6a4966c9",
|
|
416
|
+
"file": "requests/adapters.py",
|
|
417
|
+
"replace": [
|
|
418
|
+
" if url.lower().startswith('https'):",
|
|
419
|
+
" if url.lower().startswith('https') and verify:"
|
|
420
|
+
],
|
|
421
|
+
"expect": {
|
|
422
|
+
"check": "readded-bug",
|
|
423
|
+
"warn": true,
|
|
424
|
+
"commit": "af66521"
|
|
425
|
+
},
|
|
426
|
+
"notes": "Re-adds the condition removed by fix #4025; this mirrors the later 00c6ad58 simplification. See https://github.com/psf/requests/issues/4025."
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"id": "requests-quiet-black-auth-strip-formatting",
|
|
430
|
+
"repo": "https://github.com/psf/requests",
|
|
431
|
+
"at": "2d5517682b3b38547634d153cea43d48fbc8cdb5",
|
|
432
|
+
"file": "requests/sessions.py",
|
|
433
|
+
"replace": [
|
|
434
|
+
" if (not changed_scheme and old_parsed.port in default_port\n and new_parsed.port in default_port):",
|
|
435
|
+
" if (\n not changed_scheme\n and old_parsed.port in default_port\n and new_parsed.port in default_port\n ):"
|
|
436
|
+
],
|
|
437
|
+
"expect": {
|
|
438
|
+
"check": "readded-bug",
|
|
439
|
+
"warn": false
|
|
440
|
+
},
|
|
441
|
+
"notes": "Black's formatting change from 2a6f290b in should_strip_auth. Formatting only: must stay quiet."
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"id": "requests-quiet-per-host-proxy-feature",
|
|
445
|
+
"repo": "https://github.com/psf/requests",
|
|
446
|
+
"at": "48fe34447de02bfe9827321fafe3277741d77c38",
|
|
447
|
+
"file": "requests/adapters.py",
|
|
448
|
+
"replace": [
|
|
449
|
+
" u = urlparse(url.lower())\n proxy = proxies.get(u.scheme+'://'+u.hostname, proxies.get(u.scheme))",
|
|
450
|
+
" proxy = proxies.get(urlparse(url.lower()).scheme)"
|
|
451
|
+
],
|
|
452
|
+
"expect": {
|
|
453
|
+
"check": "readded-bug",
|
|
454
|
+
"warn": false
|
|
455
|
+
},
|
|
456
|
+
"notes": "Re-adds the exact line removed by 48fe3444. Although the commit says Fixes #2722, it implements the per-host proxy feature rather than fixing a regression. Blot classifies it with severity 1, so its removed line must not be indexed as a bug fix."
|
|
411
457
|
}
|
|
412
458
|
]
|
|
@@ -579,8 +579,8 @@
|
|
|
579
579
|
"path": "django/contrib/gis/gdal/raster/source.py",
|
|
580
580
|
"line": 37,
|
|
581
581
|
"cited": "f1949c1",
|
|
582
|
-
"verdict": "
|
|
583
|
-
"reason": "docstring"
|
|
582
|
+
"verdict": "useful",
|
|
583
|
+
"reason": "extends the isinstance check from the CVE-2026-15307 fix to bytes (was mislabelled: the old warning pointed at the docstring line above)"
|
|
584
584
|
},
|
|
585
585
|
{
|
|
586
586
|
"sha": "62ef9d40c",
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repo": "C:\\Users\\N Jayasankar\\AppData\\Local/Temp/wl/replay/express",
|
|
3
|
+
"head": "0dee2c52c7c873498f8ed7d8ee4ddd0a04d3ada8",
|
|
4
|
+
"labels": [
|
|
5
|
+
{
|
|
6
|
+
"sha": "a0276c6c9",
|
|
7
|
+
"subject": "Use mime-types for file to content type mapping",
|
|
8
|
+
"check": "removed-fix",
|
|
9
|
+
"path": "lib/response.js",
|
|
10
|
+
"line": 642,
|
|
11
|
+
"cited": "ae0b630",
|
|
12
|
+
"verdict": "useful",
|
|
13
|
+
"reason": "replaces the charset logic from fix #3303 with mime.contentType"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"sha": "490f1a173",
|
|
17
|
+
"subject": "lint: remove deprecated String.prototype.substr",
|
|
18
|
+
"check": "removed-fix",
|
|
19
|
+
"path": "lib/router/index.js",
|
|
20
|
+
"line": 291,
|
|
21
|
+
"cited": "99a369f",
|
|
22
|
+
"verdict": "noise",
|
|
23
|
+
"reason": "equivalent API swap (substr -> slice)"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"sha": "0def9bb65",
|
|
27
|
+
"subject": "Add \"root\" option to res.download",
|
|
28
|
+
"check": "removed-fix",
|
|
29
|
+
"path": "lib/response.js",
|
|
30
|
+
"line": 585,
|
|
31
|
+
"cited": "1150ca7",
|
|
32
|
+
"verdict": "useful",
|
|
33
|
+
"reason": "changes the path resolution that fix #2284 relied on"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"sha": "0867302dd",
|
|
37
|
+
"subject": "Prevent open redirect allow list bypass due to encodeurl",
|
|
38
|
+
"check": "removed-fix",
|
|
39
|
+
"path": "lib/response.js",
|
|
40
|
+
"line": 915,
|
|
41
|
+
"cited": "76eaa32",
|
|
42
|
+
"verdict": "useful",
|
|
43
|
+
"reason": "changes the URL encoding from fix #2897 (security follow-up)"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"sha": "89f198c6a",
|
|
47
|
+
"subject": "lib: use req.socket over deprecated req.connection (#6705)",
|
|
48
|
+
"check": "removed-fix",
|
|
49
|
+
"path": "lib/request.js",
|
|
50
|
+
"line": 285,
|
|
51
|
+
"cited": "11c74d7",
|
|
52
|
+
"verdict": "noise",
|
|
53
|
+
"reason": "equivalent API swap (req.connection -> req.socket)"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"sha": "89f198c6a",
|
|
57
|
+
"subject": "lib: use req.socket over deprecated req.connection (#6705)",
|
|
58
|
+
"check": "removed-fix",
|
|
59
|
+
"path": "lib/request.js",
|
|
60
|
+
"line": 290,
|
|
61
|
+
"cited": "20aa126",
|
|
62
|
+
"verdict": "noise",
|
|
63
|
+
"reason": "equivalent API swap (req.connection -> req.socket)"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"sha": "697547cde",
|
|
67
|
+
"subject": "Revert \"sec: security patch for CVE-2024-51999\"",
|
|
68
|
+
"check": "removed-fix",
|
|
69
|
+
"path": "lib/utils.js",
|
|
70
|
+
"line": 269,
|
|
71
|
+
"cited": "2f64f68",
|
|
72
|
+
"verdict": "useful",
|
|
73
|
+
"reason": "reverts the CVE-2024-51999 patch (deliberate; advisory later withdrawn)"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"sha": "697547cde",
|
|
77
|
+
"subject": "Revert \"sec: security patch for CVE-2024-51999\"",
|
|
78
|
+
"check": "readded-bug",
|
|
79
|
+
"path": "lib/utils.js",
|
|
80
|
+
"line": 269,
|
|
81
|
+
"cited": "2f64f68",
|
|
82
|
+
"verdict": "useful",
|
|
83
|
+
"reason": "reverts the CVE-2024-51999 patch (deliberate; advisory later withdrawn)"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"sha": "a08da78e6",
|
|
87
|
+
"subject": "deps: bump qs minimum to 6.15.2 (#7305)",
|
|
88
|
+
"check": "removed-fix",
|
|
89
|
+
"path": "package.json",
|
|
90
|
+
"line": 55,
|
|
91
|
+
"cited": "925a1df",
|
|
92
|
+
"verdict": "noise",
|
|
93
|
+
"reason": "raises the dependency floor past the fix's version, keeping the fix"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"sha": "9482b82d0",
|
|
97
|
+
"subject": "Invoke default with same arguments as types in res.format",
|
|
98
|
+
"check": "removed-fix",
|
|
99
|
+
"path": "lib/response.js",
|
|
100
|
+
"line": 700,
|
|
101
|
+
"cited": "1311f2a",
|
|
102
|
+
"verdict": "useful",
|
|
103
|
+
"reason": "changes how res.format calls the default handler from fix 1311f2a"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"sha": "b3906cbdd",
|
|
107
|
+
"subject": "fix(deps): serve-static@^2.1.0",
|
|
108
|
+
"check": "removed-fix",
|
|
109
|
+
"path": "package.json",
|
|
110
|
+
"line": 57,
|
|
111
|
+
"cited": "65b6206",
|
|
112
|
+
"verdict": "noise",
|
|
113
|
+
"reason": "dependency floor raised past the cited version"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"sha": "0c49926a9",
|
|
117
|
+
"subject": "fix(deps): send@^1.1.0",
|
|
118
|
+
"check": "removed-fix",
|
|
119
|
+
"path": "package.json",
|
|
120
|
+
"line": 56,
|
|
121
|
+
"cited": "ecf762f",
|
|
122
|
+
"verdict": "noise",
|
|
123
|
+
"reason": "dependency floor raised past the cited version"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"sha": "9f8589e31",
|
|
127
|
+
"subject": "cleanup: remove unnecessary require for global Buffer",
|
|
128
|
+
"check": "removed-fix",
|
|
129
|
+
"path": "lib/response.js",
|
|
130
|
+
"line": 15,
|
|
131
|
+
"cited": "c70197a",
|
|
132
|
+
"verdict": "noise",
|
|
133
|
+
"reason": "removes a require made unnecessary by Node's global Buffer"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"sha": "9f8589e31",
|
|
137
|
+
"subject": "cleanup: remove unnecessary require for global Buffer",
|
|
138
|
+
"check": "removed-fix",
|
|
139
|
+
"path": "lib/utils.js",
|
|
140
|
+
"line": 15,
|
|
141
|
+
"cited": "c70197a",
|
|
142
|
+
"verdict": "noise",
|
|
143
|
+
"reason": "removes a require made unnecessary by Node's global Buffer"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"sha": "d2de128a3",
|
|
147
|
+
"subject": "fix (deps): update deps (#6337)",
|
|
148
|
+
"check": "removed-fix",
|
|
149
|
+
"path": "package.json",
|
|
150
|
+
"line": 65,
|
|
151
|
+
"cited": "6a40af8",
|
|
152
|
+
"verdict": "noise",
|
|
153
|
+
"reason": "dependency range loosened, cited commit was a dev-deps bump"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"sha": "9e9714422",
|
|
157
|
+
"subject": "feat(deps): finalhandler@2.1.0 (#6373)",
|
|
158
|
+
"check": "removed-fix",
|
|
159
|
+
"path": "package.json",
|
|
160
|
+
"line": 45,
|
|
161
|
+
"cited": "accafc6",
|
|
162
|
+
"verdict": "noise",
|
|
163
|
+
"reason": "dependency floor raised past the cited version"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"sha": "f1a2dc884",
|
|
167
|
+
"subject": "deps: type-is@^2.0.1 (#6420)",
|
|
168
|
+
"check": "removed-fix",
|
|
169
|
+
"path": "package.json",
|
|
170
|
+
"line": 60,
|
|
171
|
+
"cited": "402e7f6",
|
|
172
|
+
"verdict": "noise",
|
|
173
|
+
"reason": "dependency floor raised past the cited version"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"sha": "00bb633ca",
|
|
177
|
+
"subject": "deps: qs@^6.14.1",
|
|
178
|
+
"check": "removed-fix",
|
|
179
|
+
"path": "package.json",
|
|
180
|
+
"line": 55,
|
|
181
|
+
"cited": "1e359f5",
|
|
182
|
+
"verdict": "noise",
|
|
183
|
+
"reason": "dependency floor raised past the cited version"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"sha": "f5c159b11",
|
|
187
|
+
"subject": "ci: build express with node.js v26 (#7218)",
|
|
188
|
+
"check": "removed-fix",
|
|
189
|
+
"path": "package.json",
|
|
190
|
+
"line": 75,
|
|
191
|
+
"cited": "d2de128",
|
|
192
|
+
"verdict": "noise",
|
|
193
|
+
"reason": "dev dependency bump; cited commit was a deps bump"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"sha": "cb19f0417",
|
|
197
|
+
"subject": "Upgrade `content-type` (#7234)",
|
|
198
|
+
"check": "removed-fix",
|
|
199
|
+
"path": "package.json",
|
|
200
|
+
"line": 38,
|
|
201
|
+
"cited": "d2de128",
|
|
202
|
+
"verdict": "noise",
|
|
203
|
+
"reason": "dependency bump; cited commit was a deps bump"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"sha": "59e205a57",
|
|
207
|
+
"subject": "Upgrade `content-disposition` (#7233)",
|
|
208
|
+
"check": "removed-fix",
|
|
209
|
+
"path": "package.json",
|
|
210
|
+
"line": 37,
|
|
211
|
+
"cited": "05f40f4",
|
|
212
|
+
"verdict": "noise",
|
|
213
|
+
"reason": "dependency bump past the cited version"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"sha": "9a34acf03",
|
|
217
|
+
"subject": "fix(res.send): preserve ETag generation with Transfer-Encoding (#7459)",
|
|
218
|
+
"check": "removed-fix",
|
|
219
|
+
"path": "lib/response.js",
|
|
220
|
+
"line": 168,
|
|
221
|
+
"cited": "18e5985",
|
|
222
|
+
"verdict": "useful",
|
|
223
|
+
"reason": "changes the Transfer-Encoding condition from fix #4893"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"sha": "9a34acf03",
|
|
227
|
+
"subject": "fix(res.send): preserve ETag generation with Transfer-Encoding (#7459)",
|
|
228
|
+
"check": "readded-bug",
|
|
229
|
+
"path": "lib/response.js",
|
|
230
|
+
"line": 166,
|
|
231
|
+
"cited": "18e5985",
|
|
232
|
+
"verdict": "useful",
|
|
233
|
+
"reason": "re-adds the condition that fix #4893 replaced"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"sha": "6d2df035a",
|
|
237
|
+
"subject": "build(deps-dev): bump supertest from 6.3.0 to 7.3.0 (#7494)",
|
|
238
|
+
"check": "removed-fix",
|
|
239
|
+
"path": "package.json",
|
|
240
|
+
"line": 79,
|
|
241
|
+
"cited": "d2de128",
|
|
242
|
+
"verdict": "noise",
|
|
243
|
+
"reason": "dev dependency bump; cited commit was a deps bump"
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
}
|