rulereceipt 0.1.42 → 0.1.43
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.
- package/README.md +16 -0
- package/dist/checks/claimEvidence.js +44 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -101,6 +101,22 @@ rulereceipt verify <session-file> <hash> # spot-check a report you received ag
|
|
|
101
101
|
|
|
102
102
|
`verify` isn't a routine check — trust your team day to day, same as any status update. It's there for the rare case it actually matters (a dispute, an incident review): give it the session file and the hash printed in the report, and it confirms whether they really match.
|
|
103
103
|
|
|
104
|
+
### Claims of having read something
|
|
105
|
+
|
|
106
|
+
A session that writes `PAGES READ: 1-20`, `STATUS: READ IN FULL` or "confirmed
|
|
107
|
+
at source" while never opening a file is asserting provenance it does not
|
|
108
|
+
have. Reported by a user in anthropics/claude-code#92505, where those headers
|
|
109
|
+
went into tracked files and commit messages for material the model had never
|
|
110
|
+
read.
|
|
111
|
+
|
|
112
|
+
The check is narrow on purpose. It fires only when **nothing at all** was read
|
|
113
|
+
in the session — no `Read`, no `Grep`, no `cat`. That much a transcript can
|
|
114
|
+
prove, and it contradicts any claim of reading. It cannot tell you *which*
|
|
115
|
+
document was read when reads did happen, so a session that read the wrong
|
|
116
|
+
thing is still beyond it, and the report says so rather than guessing.
|
|
117
|
+
|
|
118
|
+
"I will read the filing next" is a plan, not a claim, and does not fire.
|
|
119
|
+
|
|
104
120
|
## Blocking, not just reporting
|
|
105
121
|
|
|
106
122
|
`rulereceipt check` tells you afterwards. `rulereceipt hook` refuses to let the
|
|
@@ -86,7 +86,46 @@ const ACTION_CLAIMS = [
|
|
|
86
86
|
exclude: /\bcommitted\s+to\b/i,
|
|
87
87
|
command: /\bgit\s+commit\b/i,
|
|
88
88
|
},
|
|
89
|
+
{
|
|
90
|
+
/**
|
|
91
|
+
* A claim to have READ a source, when nothing was read at all.
|
|
92
|
+
*
|
|
93
|
+
* From anthropics/claude-code#92505: "PAGES READ: 1-20", "STATUS: READ
|
|
94
|
+
* IN FULL", "confirmed at source" — emitted for material never opened,
|
|
95
|
+
* then written into tracked files and commit messages. The reporter's
|
|
96
|
+
* framing is the one that matters: the apparatus that certifies work was
|
|
97
|
+
* produced decoupled from the work, and a plainly-worded guess would
|
|
98
|
+
* have been safer, because a guess reads as a guess.
|
|
99
|
+
*
|
|
100
|
+
* Two claim shapes, because a provenance header has no "I" in it. The
|
|
101
|
+
* first-person form is gated like the others; the header form is matched
|
|
102
|
+
* literally, since "PAGES READ:" and "READ IN FULL" do not occur by
|
|
103
|
+
* accident.
|
|
104
|
+
*
|
|
105
|
+
* exclude carries the future tense. "I will read the filing next" is a
|
|
106
|
+
* plan, and a plan is not a claim.
|
|
107
|
+
*
|
|
108
|
+
* Ceiling: this fires only when NOTHING was read. The transcript can
|
|
109
|
+
* show that, and it contradicts any claim of reading. It cannot show
|
|
110
|
+
* WHICH document was read when reads did happen, so a session that read
|
|
111
|
+
* something else entirely is still beyond it.
|
|
112
|
+
*/
|
|
113
|
+
label: "read of a source",
|
|
114
|
+
claim: /\b(?:i|we)(?:'ve|’ve| have| had)?\s+(?:\w+ly\s+|just\s+|already\s+|then\s+|also\s+|now\s+)*read\b|^\s*(?:pages?\s+read|status)\s*:\s*(?:[\d\s,-]+|read\s+in\s+full)|\bread\s+in\s+full\b|\bconfirmed\s+at\s+source\b/im,
|
|
115
|
+
exclude: /\b(?:will|going\s+to|need\s+to|should|next|plan\s+to|about\s+to|let\s+me|i'?ll|we'?ll)\s+(?:\w+\s+){0,3}read\b/i,
|
|
116
|
+
command: /\b(?:cat|head|tail|less|more|bat|nl|strings|pdftotext|xxd|od)\b/i,
|
|
117
|
+
},
|
|
89
118
|
];
|
|
119
|
+
/**
|
|
120
|
+
* Tools that read a file, as distinct from shell commands that do.
|
|
121
|
+
*
|
|
122
|
+
* ACTION_CLAIMS matches Bash command text, which is the whole surface for
|
|
123
|
+
* push and commit. Reading is not: most reads go through Read, Grep, Glob
|
|
124
|
+
* or WebFetch and never touch a shell. Without these, a session that read
|
|
125
|
+
* twenty files through the proper tool would be reported as having read
|
|
126
|
+
* nothing.
|
|
127
|
+
*/
|
|
128
|
+
const READING_TOOLS = new Set(["Read", "Grep", "Glob", "NotebookRead", "WebFetch", "Fetch"]);
|
|
90
129
|
/**
|
|
91
130
|
* Removes what a message SHOWS, leaving what it SAYS.
|
|
92
131
|
*
|
|
@@ -202,6 +241,11 @@ export function runClaimEvidenceChecks(classifications, events) {
|
|
|
202
241
|
let unreadable = null;
|
|
203
242
|
let backed = null;
|
|
204
243
|
for (const event of events) {
|
|
244
|
+
// A read through Read/Grep/Glob never reaches the shell, so it has to be
|
|
245
|
+
// recorded here rather than by matching command text.
|
|
246
|
+
if (event.kind === "tool_use" && READING_TOOLS.has(event.toolName ?? "")) {
|
|
247
|
+
commandsSeen.add("read of a source");
|
|
248
|
+
}
|
|
205
249
|
const command = commandOf(event);
|
|
206
250
|
if (command !== null) {
|
|
207
251
|
const id = event.kind === "tool_use" ? (event.toolUseId ?? null) : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulereceipt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.43",
|
|
4
4
|
"description": "Checks whether a Claude Code session actually followed your CLAUDE.md / AGENTS.md rules, with evidence.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
},
|
|
47
47
|
"license": "SEE LICENSE IN LICENSE",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@anthropic-ai/sdk": "
|
|
50
|
-
"commander": "
|
|
51
|
-
"nodemailer": "
|
|
49
|
+
"@anthropic-ai/sdk": "~0.32.1",
|
|
50
|
+
"commander": "~15.0.0",
|
|
51
|
+
"nodemailer": "~10.0.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^26.4.0",
|