@sriinnu/omit 0.3.0 → 0.4.0

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.
@@ -4,7 +4,7 @@ Great software is edited, not written. You are the editor, not just the author.
4
4
 
5
5
  **Before code: the Seven Omissions** (stop at the first that holds): (1) Omit the feature: speculative need = needless until proven needed; write nothing. (2) Omit the new code: the codebase already does this; reuse it. (3) Omit the custom: stdlib covers it. (4) Omit the script: the platform does it natively. (5) Omit the dependency: an installed dep covers it; never add a new one for a few lines. (6) Omit the ceremony: one plain line beats a pattern. (7) What survives editing, ships.
6
6
 
7
- **Fact-Check**: no omission counts until verified now: codebase reuse → cite path:line; stdlib/platform claims → real docs or a run snippet; dependency claims → manifest + API exists in the installed version.
7
+ **Fact-Check**: no omission counts until verified now, and verified means checkable by something other than you. Record each in `.omit/receipts.jsonl`: reuse → `{"claim":"reuse","rung":2,"file":"src/x.ts","line":42,"symbol":"name"}`; stdlib/platform → `{"claim":"stdlib","rung":3,"api":"<function>","run":["node","-e","<snippet>"]}`; installed dep → `{"claim":"installed-dep","rung":5,"dep":"zod","run":["node","-e","require('zod').object"]}`; a NEW dependency → `{"claim":"new-dep","rung":7,"dep":"x","tried":[{"rung":2,"absent":"<symbol you searched for>"}]}`. `run` must name the api/dep it exercises — a snippet whose exit status is the whole evidence (`["true"]`, `["false"]`) proves nothing. A `tried` entry cites a symbol to search the tree for, not a file, and every entry must fail its own check. `run` is argv, never a shell string. `omit verify` re-checks them all; a claim that does not survive is not a claim.
8
8
 
9
9
  **Final Draft**: after tests go green, one ruthless edit of your own diff; report the net (±lines, files, new deps: target 0). Done = final draft, not green tests.
10
10
 
@@ -14,7 +14,7 @@ BEFORE code: the Seven Omissions (stop at the first that holds):
14
14
  6. Omit the ceremony: one plain line beats a pattern.
15
15
  7. What survives editing, ships: minimum that works, fewest files, shortest diff.
16
16
 
17
- FACT-CHECK: no omission counts until verified now: codebase reuse → cite path:line; stdlib/platform claims → real docs or a run snippet; dependency claims → in the manifest AND the API exists in the installed version. A hallucinated shortcut is a fabricated quote.
17
+ FACT-CHECK: no omission counts until verified now, and verified means checkable by something other than you. Record each in `.omit/receipts.jsonl`: reuse → `{"claim":"reuse","rung":2,"file":"src/x.ts","line":42,"symbol":"name"}`; stdlib/platform → `{"claim":"stdlib","rung":3,"api":"<function>","run":["node","-e","<snippet>"]}`; installed dep → `{"claim":"installed-dep","rung":5,"dep":"zod","run":["node","-e","require('zod').object"]}`; a NEW dependency → `{"claim":"new-dep","rung":7,"dep":"x","tried":[{"rung":2,"absent":"<symbol you searched for>"}]}`. `run` must name the api/dep it exercises — a snippet whose exit status is the whole evidence (`["true"]`, `["false"]`) proves nothing. A `tried` entry cites a symbol to search the tree for, not a file, and every entry must fail its own check. `run` is argv, never a shell string. `omit verify` re-checks them all; a hallucinated shortcut is a fabricated citation, and it does not survive one.
18
18
 
19
19
  AFTER green: the Final Draft: one ruthless edit of your own diff (dead branches, unused params/imports, speculative options, restating comments, single-caller indirection). Report the net: ±lines, files, new deps (target 0). Done = final draft, not green tests.
20
20
 
@@ -0,0 +1,15 @@
1
+ # omit: Omit needless code.
2
+
3
+ Great software is edited, not written. You are the editor, not just the author. Draft less, cite everything, cut last.
4
+
5
+ **Before code, the Seven Omissions** (stop at the first that holds): (1) Omit the feature: speculative need is needless until proven needed; write nothing. (2) Omit the new code: the codebase already does this; reuse it. (3) Omit the custom: stdlib covers it. (4) Omit the script: the platform does it natively. (5) Omit the dependency: an installed dep covers it; never add a new one for a few lines. (6) Omit the ceremony: one plain line beats a pattern. (7) What survives editing, ships.
6
+
7
+ **Fact-Check**: no omission counts until verified now, and verified means checkable by something other than you. Record each in `.omit/receipts.jsonl`: reuse → `{"claim":"reuse","rung":2,"file":"src/x.ts","line":42,"symbol":"name"}`; stdlib/platform → `{"claim":"stdlib","rung":3,"api":"<function>","run":["node","-e","<snippet>"]}`; installed dep → `{"claim":"installed-dep","rung":5,"dep":"zod","run":["node","-e","require('zod').object"]}`; a NEW dependency → `{"claim":"new-dep","rung":7,"dep":"x","tried":[{"rung":2,"absent":"<symbol you searched for>"}]}`. `run` must name the api/dep it exercises — a snippet whose exit status is the whole evidence (`["true"]`, `["false"]`) proves nothing. A `tried` entry cites a symbol to search the tree for, not a file, and every entry must fail its own check. `run` is argv, never a shell string. `omit verify` re-checks them all; a claim that does not survive is not a claim.
8
+
9
+ **Final Draft**: after tests go green, one ruthless edit of your own diff; report the net (plus/minus lines, files, new deps, target 0). Done means final draft, not green tests.
10
+
11
+ **Never cut load-bearing lines**: validation at trust boundaries, error handling preventing data loss, security, accessibility, concurrency correctness, explicit requests. Announce them (`load-bearing: <reason>`), never skip them.
12
+
13
+ **Footnotes**: record deliberate omissions: `// omitted: <what>; <when to add it back>`.
14
+
15
+ **Voice**: root causes, not symptoms; boring beats clever; deletion is the strongest edit.
@@ -0,0 +1,69 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: read
10
+ id-token: write
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: "20"
21
+ registry-url: "https://registry.npmjs.org"
22
+
23
+ - name: Verify tag matches package.json version
24
+ run: |
25
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
26
+ PKG_VERSION="$(node -p "require('./package.json').version")"
27
+ PKG_NAME="$(node -p "require('./package.json').name")"
28
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
29
+ echo "Tag v$TAG_VERSION does not match package.json version $PKG_VERSION"
30
+ exit 1
31
+ fi
32
+ echo "PKG_NAME=$PKG_NAME" >> "$GITHUB_ENV"
33
+ echo "PKG_VERSION=$PKG_VERSION" >> "$GITHUB_ENV"
34
+
35
+ - name: Skip if already published
36
+ id: check
37
+ run: |
38
+ if npm view "$PKG_NAME@$PKG_VERSION" version >/dev/null 2>&1; then
39
+ echo "Already on the registry, nothing to do."
40
+ echo "already_published=true" >> "$GITHUB_OUTPUT"
41
+ else
42
+ echo "already_published=false" >> "$GITHUB_OUTPUT"
43
+ fi
44
+
45
+ - name: Publish
46
+ if: steps.check.outputs.already_published == 'false'
47
+ env:
48
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
49
+ run: |
50
+ if npm publish --access public --provenance; then
51
+ echo "Publish succeeded."
52
+ exit 0
53
+ fi
54
+
55
+ # npm's registry can 404 a fresh scoped package's first publish for a
56
+ # few seconds after the write actually lands (read-replica lag), which
57
+ # makes npm publish report failure even though the version is live.
58
+ # Poll before trusting the error.
59
+ echo "npm publish reported failure; checking whether it landed anyway..."
60
+ for i in $(seq 1 10); do
61
+ sleep 6
62
+ if npm view "$PKG_NAME@$PKG_VERSION" version >/dev/null 2>&1; then
63
+ echo "Version $PKG_VERSION is live on the registry — treating as success."
64
+ exit 0
65
+ fi
66
+ done
67
+
68
+ echo "Version never appeared on the registry after retries. Publish failed."
69
+ exit 1
@@ -0,0 +1,40 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: actions/setup-node@v4
15
+ with:
16
+ node-version: "20"
17
+
18
+ - run: npm test
19
+
20
+ # action.yml is read by GitHub's action loader, not by anything in this
21
+ # repo, so an invalid one fails silently: the Action simply never runs and
22
+ # no check here notices. It shipped invalid once already — an unquoted
23
+ # ": " inside the description — which no test, and no reviewer, caught.
24
+ - name: action.yml parses
25
+ run: |
26
+ python3 - <<'PY'
27
+ import sys
28
+ try:
29
+ import yaml
30
+ except ImportError:
31
+ # Announced rather than silent: on a runner without PyYAML this
32
+ # guard is advisory, and saying so beats implying it ran.
33
+ print("PyYAML unavailable on this runner — action.yml was NOT validated")
34
+ sys.exit(0)
35
+ doc = yaml.safe_load(open("action.yml"))
36
+ for key in ("name", "description", "runs"):
37
+ assert key in doc, f"action.yml is missing a required key: {key}"
38
+ assert doc["runs"].get("using") == "composite", "runs.using must be composite"
39
+ print("action.yml parses and carries its required keys")
40
+ PY
@@ -4,7 +4,7 @@ Great software is edited, not written. You are the editor, not just the author.
4
4
 
5
5
  **Before code: the Seven Omissions** (stop at the first that holds): (1) Omit the feature: speculative need = needless until proven needed; write nothing. (2) Omit the new code: the codebase already does this; reuse it. (3) Omit the custom: stdlib covers it. (4) Omit the script: the platform does it natively. (5) Omit the dependency: an installed dep covers it; never add a new one for a few lines. (6) Omit the ceremony: one plain line beats a pattern. (7) What survives editing, ships.
6
6
 
7
- **Fact-Check**: no omission counts until verified now: codebase reuse → cite path:line; stdlib/platform claims → real docs or a run snippet; dependency claims → manifest + API exists in the installed version.
7
+ **Fact-Check**: no omission counts until verified now, and verified means checkable by something other than you. Record each in `.omit/receipts.jsonl`: reuse → `{"claim":"reuse","rung":2,"file":"src/x.ts","line":42,"symbol":"name"}`; stdlib/platform → `{"claim":"stdlib","rung":3,"api":"<function>","run":["node","-e","<snippet>"]}`; installed dep → `{"claim":"installed-dep","rung":5,"dep":"zod","run":["node","-e","require('zod').object"]}`; a NEW dependency → `{"claim":"new-dep","rung":7,"dep":"x","tried":[{"rung":2,"absent":"<symbol you searched for>"}]}`. `run` must name the api/dep it exercises — a snippet whose exit status is the whole evidence (`["true"]`, `["false"]`) proves nothing. A `tried` entry cites a symbol to search the tree for, not a file, and every entry must fail its own check. `run` is argv, never a shell string. `omit verify` re-checks them all; a claim that does not survive is not a claim.
8
8
 
9
9
  **Final Draft**: after tests go green, one ruthless edit of your own diff; report the net (±lines, files, new deps: target 0). Done = final draft, not green tests.
10
10
 
package/AGENTS.md CHANGED
@@ -20,11 +20,20 @@ Try to omit, in order: stop at the first omission that holds:
20
20
 
21
21
  ## The Fact-Check
22
22
 
23
- No omission counts until verified in this session: codebase reuse → cite `path:line`; stdlib/platform claims → real docs or a run snippet; dependency claims → in the manifest AND the API exists in the installed version. No citation, no omission.
23
+ No omission counts until verified in this session — and verified means checkable by something other than you. Record each one in `.omit/receipts.jsonl`, one JSON object per line, naming its own evidence:
24
+
25
+ ```
26
+ {"claim":"reuse","rung":2,"file":"src/x.ts","line":42,"symbol":"parseRange"}
27
+ {"claim":"stdlib","rung":3,"api":"crypto.randomUUID","run":["node","-e","crypto.randomUUID()"]}
28
+ {"claim":"installed-dep","rung":5,"dep":"zod","run":["node","-e","require('zod').object"]}
29
+ {"claim":"new-dep","rung":7,"dep":"left-pad","tried":[{"rung":2,"absent":"padTo"}]}
30
+ ```
31
+
32
+ Evidence is bound to the claim: a snippet's exit status proves nothing on its own, so `run` must also name what it exercises and the argv must mention it. `run` is argv, never a shell string. A `tried` entry cites a symbol to search the tree for, not a location — citing a file that merely does not exist proves nothing. A `new-dep` receipt is the strongest claim: it asserts omissions 2-5 were tried and did not hold, so every `tried` entry is re-checked and must fail. `omit verify` re-checks the ledger; `omit gate` refuses a new dependency cited by anything less. No receipt, no omission.
24
33
 
25
34
  ## After code works: the Final Draft
26
35
 
27
- One ruthless edit of your own diff: dead branches, unused params/imports, speculative options, comments restating code, single-caller indirection. Report the net (+/− lines, files, new deps: target 0). Done = final draft, not green tests.
36
+ One ruthless edit of your own diff: dead branches, unused params/imports, speculative options, comments restating code, single-caller indirection. Write the net report to `.omit/final-draft.md` in the shape the stop gate parses — `files touched: <n>`, `lines +<added> −<removed>`, `new dependencies: <n>` (untracked files count; `omit audit` prints exactly these). Done = final draft, not green tests.
28
37
 
29
38
  ## Load-Bearing Lines: never cut
30
39
 
package/README.md CHANGED
@@ -24,7 +24,7 @@ AI agents are prolific authors and terrible editors. Left alone they add abstrac
24
24
  | Part | What it does |
25
25
  |---|---|
26
26
  | **The Seven Omissions** | Before writing anything, try seven ways to *not* write it: omit the feature, the new code, the custom, the script, the dependency, the ceremony: stopping at the first omission that holds. What survives editing, ships. |
27
- | **The Fact-Check** | No omission counts without a citation verified this session: `path:line` for "the codebase has this", real docs or a run snippet for "stdlib covers it", manifest + installed API for "the dep handles it". A hallucinated shortcut is a fabricated quote. |
27
+ | **The Fact-Check** | No omission counts without a citation **the tool can re-check itself**: `file:line:symbol` for "the codebase has this", an executable snippet for "stdlib covers it", a declared dep + snippet for "the dep handles it". A citation only its author can read is a self-report, and a hallucinated shortcut is a fabricated citation. |
28
28
  | **The Final Draft** | Working code is a first draft. After tests go green, one ruthless edit of the agent's own diff: then a net report: files, ±lines, new deps (target: 0). Done means final draft, not green tests. |
29
29
  | **Load-Bearing Lines** | Editing cuts fat, not walls. Validation, error handling, security, accessibility, concurrency correctness, and explicit requests are never cut: and adding them is announced, never smuggled or skipped. |
30
30
 
@@ -40,27 +40,78 @@ Every other skill in this genre is words the agent can ignore under context pres
40
40
 
41
41
  | Mechanism | What it does |
42
42
  |---|---|
43
- | **Command sentinel** (hook) | Inspects every shell command BEFORE it runs and blocks the classic agent disasters: `rm -rf ~`, recursive deletes of system/drive roots, deletes through unset variables (`rm -rf $OUT/*` with `$OUT` empty), `dd` to block devices, `mkfs`, fork bombs. The user's machine is load-bearing. |
44
- | **Dep sentinel** (hook) | A new dependency hits a manifest with no receipt in `.omit/receipts.jsonl` → the edit is objected to on the spot. Cite why omissions 2-5 failed, or revert. |
45
- | **Hazard sentinel** (hook) | Hardcoded API keys/secrets and injection-prone patterns (string-built SQL, `eval`, shell concatenation, `innerHTML`, unsafe deserialization) are blocked the moment they land in a file. Secrets have no override; injection lines need a reviewed `omit-allow: <reason>`. |
46
- | **Lint sentinel** (hook) | omit ships no lint rules. It detects the linter the repo already configured (eslint, biome, ruff, flake8) and runs it on every edited file, so the agent hears objections immediately instead of at CI time. |
47
- | **Final Draft gate** (hook) | The session cannot end with an edited tree and no `.omit/final-draft.md` net report. The deletion pass is a gate, not a suggestion. |
48
- | **Receipts ledger** | Every Fact-Check citation is appended to `.omit/receipts.jsonl`: an auditable trail of the agent's claims your reviewers can actually read. |
43
+ | **Command sentinel** (hook) | Inspects every shell command BEFORE it runs and blocks the classic agent disasters: `rm -rf ~`, recursive deletes of system/drive roots, deletes through unset variables (`rm -rf $OUT/*` with `$OUT` empty), `dd` to block devices, `mkfs`, fork bombs. The user's machine is load-bearing. Waiving one takes a trailing comment carrying a real reason — `# omit-allow: <reason>`. The bare token, a token inside a string literal, and a reason-less marker are ignored. |
44
+ | **Dep sentinel** (hook) | A new dependency hits a manifest with no *verified* receipt in `.omit/receipts.jsonl` → the edit is objected to on the spot. The receipt has to name the omissions that were tried, and each one is re-checked: if an omission actually applies, the receipt is refuted and the dependency is refused. |
45
+ | **Hazard sentinel** (hook) | Hardcoded API keys/secrets and injection-prone patterns (string-built SQL, `eval`, shell concatenation, `innerHTML`, unsafe deserialization) are blocked the moment they land in a file. **Secrets have no override** — the secret rules run before any marker is consulted, so no `omit-allow:` waives one. Injection lines take a trailing comment with a real reason, and only that form. |
46
+ | **Leak sentinel** (hook) | Blocks shell commands that print an *existing* secret's raw value to stdout before they run, or a live key typed straight into the command line: macOS Keychain, Linux `secret-tool`/`pass`/`gpg -d`, 1Password/Vault/AWS/GCP/Azure/kubectl secret CLIs, bare `env`/`printenv`, `env \| grep`-ing a KEY/TOKEN/SECRET/connection-string var, or `cat`-ing a `.env`/`credentials`/`*.pem`/`id_rsa` file. Redirecting to a real file or piping into a non-printing sink (clipboard, `--password-stdin`) is recognized as safe — the goal is keeping secrets out of the transcript, not off disk. Adversarially reviewed (3 lenses, every finding re-verified by execution, not inspection) before shipping — one known gap stays undetected on purpose rather than chasing a fragile fix: a `for`/`do`/`done > file` loop's trailing redirect isn't attributed back to the loop body. The agent's own transcript is not a safe place for a real key. |
47
+ | **Lint sentinel** (hook) | omit ships no lint rules. It detects the linter the repo already configured (eslint, biome, ruff, flake8) and runs it on every edited file, so the agent hears objections immediately instead of at CI time. A linter that is configured but cannot be run — no `node_modules`, nothing on PATH — is reported as *not run* with the reason, never as a pass; "no linter configured" is now reserved for a repo that genuinely has none. |
48
+ | **Final Draft gate** (hook) | The session cannot end with an edited tree and no current `.omit/final-draft.md` net report — and the report is read, not just stat'd. Its files/lines/deps counts are cross-checked against the actual diff, so a stub or a stale draft does not pass. The deletion pass is a gate, not a suggestion. |
49
+ | **Receipts ledger** | Every Fact-Check citation is appended to `.omit/receipts.jsonl` as a claim *plus the evidence that settles it*, and `omit verify` re-checks the lot. Run it on a PR: "17/17 claims survived" is a number a reviewer can act on, and "3 refuted" names exactly which shortcuts were invented. |
49
50
 
50
- Hooks install automatically with the Claude Code plugin. Escape hatch for humans: `OMIT_OFF=1`.
51
+ Hooks install automatically with the Claude Code plugin. Codex CLI has its own hooks system in the same shape (`PreToolUse` fires with `tool_input.command` for Bash, exit 2 blocks) — run `npx @sriinnu/omit hook install codex` to write `.codex/hooks.json`. The command and leak sentinels are verified against Codex's documented schema and payload shape (not yet a live Codex session firing them end-to-end); the file-based sentinels (dep/hazard/lint) and the Final Draft gate are wired too but best-effort, since Codex's `apply_patch` input shape for those isn't verified. Escape hatch for humans: `OMIT_OFF=1`.
52
+
53
+ ## What this does not catch
54
+
55
+ A table of mechanisms invites you to read it as a guarantee. It isn't one, so here is the rest of it. Every item below is a known, reproduced limit, not a hypothetical.
56
+
57
+ - **Writes whose path cannot be read out of the command reach no file sentinel.** `python -c "open('f','w').write(...)"`, `curl -o f`, `node -e fs.writeFileSync`. A literal secret in such a command is still caught by the command-text scan; its *injection* patterns are not. The same goes for a file rewritten through a tool no sentinel is wired to.
58
+ - **`omit gate` is a git hook.** `git commit --no-verify` skips it, and `core.hooksPath` shadows it entirely — `omit audit` and `omit gate` both report that in the verdict, and `omit hook install` writes to whichever directory git actually runs hooks from, but nothing can stop you bypassing your own pre-commit hook.
59
+ - **The dependency allowlist is short.** `setup.py`, `build.gradle`, `Package.swift` and `*.csproj` are dependency-shaped and unparsed. They are now *reported* (`unparsed deps: …`) rather than counted as zero, and a changed one suppresses the `✅` on the deps row — but the gate does not fail on them, because a `setup.py` in a repo is not evidence of anything.
60
+ - **The lint sentinel runs your linter in your session**, which executes your lint config — the same trust as running `npm run lint` yourself. In the GitHub Action it is gated: with the default `exec: false`, CI does not run the linter at all, and the verdict says *not run* rather than claiming a pass.
61
+ - **Receipt evidence is bound to its claim textually.** A `run` snippet must name what it exercises and the argv must mention it, which closes snippets that exit on demand (`["true"]`, `["false"]`). It does not catch a snippet that names a symbol without calling it. Proving that would mean the verifier writing the snippet, and then it is testing the verifier.
62
+ - **`omit`'s own directory is exempt** from the hazard scan, because the ledger holds evidence strings by construction. Its execution risk is what `OMIT_HOOK_EXEC` gates.
63
+ - **The Action does not run a PR's receipts** unless you set `with: { exec: true }`. A PR's `run` snippets are untrusted code, so the default is not to execute them.
64
+
65
+ `// omitted: a mechanism for the first item above` would be a shell parser, and a wrong shell parser is worse than none. It is left out on purpose.
66
+
67
+ ## Receipts: the claim and the evidence, in one line
68
+
69
+ Minimalism is a taste, and taste has no receipt. The failure mode underneath it does: the agent says *"the codebase already does this"* or *"stdlib covers it"*, and nobody checks. Every rule file in this genre **tells** the agent not to lie. `omit` checks.
70
+
71
+ Each claim lands in `.omit/receipts.jsonl` with the evidence that settles it:
72
+
73
+ ```json
74
+ {"claim":"reuse","rung":2,"file":"src/util.ts","line":42,"symbol":"parseRange"}
75
+ {"claim":"stdlib","rung":3,"api":"crypto.randomUUID","run":["node","-e","crypto.randomUUID()"]}
76
+ {"claim":"installed-dep","rung":5,"dep":"zod","run":["node","-e","require('zod').object"]}
77
+ {"claim":"new-dep","rung":7,"dep":"left-pad","tried":[{"rung":2,"absent":"padTo"}]}
78
+ ```
79
+
80
+ ```
81
+ $ npx @sriinnu/omit verify
82
+ ✅ line 1 verified reuse
83
+ ⛔ line 2 failed new-dep
84
+ [new-dep] tried[0] (rung 2) actually HOLDS: padTo is present at src/util.ts — so the omission applies and this dep is not needed
85
+
86
+ 1/2 claims survived re-checking · 1 refuted · 0 unverifiable
87
+ ```
88
+
89
+ That second one is the whole idea. A `new-dep` receipt exists to say *"I tried the earlier omissions and none of them held"* — so it cites the symbol it looked for, `omit` searches the tree for itself, and a symbol that **is** there refutes the agent's own conclusion. When the author supplies the question instead of the evidence, citing your way past an omission you never tried stops being possible.
90
+
91
+ Two bindings make that hold, and neither is decorative:
92
+
93
+ - **`run` must name what it exercises** (`api`, or the dependency), and the argv must mention it. Exit status alone proves nothing: `["true"]` exits 0 and `["false"]` exits 1 while testing neither the standard library nor anything else. A snippet that exits on demand is not evidence.
94
+ - **A `tried` entry cites a search, not a location.** Naming a file that merely doesn't exist proves nothing — and it used to be the cheapest possible way to fake "I tried reuse", because the check only asked whether it failed.
95
+
96
+ `run` is an argv array, never a shell string: nothing expands, nothing hides in an argument. `.omit/receipts.jsonl` is executable in the same sense a Makefile is — read it in a PR the way you'd read one. `OMIT_NO_EXEC=1` downgrades those checks to "unverifiable" instead of executing them, and the dependency hook does not execute them at all unless you set `OMIT_HOOK_EXEC=1`: a *Write* of the ledger would otherwise run code as you, with no approval prompt.
97
+
98
+ **Upgrading from 0.3.x:** the shapes above are the 0.4.0 schema. A receipt written before it — a prose `receipt` string, or a `tried` entry citing a file — reads as `unverifiable`, which `omit verify` names individually. They are not silently accepted, and they are not silently dropped.
51
99
 
52
100
  ## Any provider, same gates
53
101
 
54
- The enforcement logic lives in a zero-dependency CLI, not in any one vendor's hook system: Claude Code's hooks are just thin adapters over it. For Cursor, Codex, Copilot, or anything else, enforce at the two chokepoints every agent passes through:
102
+ The enforcement logic lives in a zero-dependency CLI, not in any one vendor's hook system: Claude Code's and Codex's hooks are both just thin adapters over it. For Cursor, Copilot, or anything else, enforce at the two chokepoints every agent passes through:
55
103
 
56
104
  ```
57
105
  npx @sriinnu/omit hook install # git pre-commit: audits the staged diff,
58
106
  # fails on secrets, injections, uncited deps
59
- npx @sriinnu/omit audit # net diff, new deps, hazards, omit score
107
+ npx @sriinnu/omit audit # net diff (untracked files included), new deps, hazards
60
108
  npx @sriinnu/omit check <files> # hazard-scan specific files (wire into any hook system)
61
109
  npx @sriinnu/omit lint [files] # run the repo's OWN linter on changed files
110
+ npx @sriinnu/omit verify # re-check every claim in .omit/receipts.jsonl
62
111
  npx @sriinnu/omit guard "<cmd>" # is this shell command a disaster? (wire into any hook system)
112
+ npx @sriinnu/omit leak "<cmd>" # would this command print a real secret to stdout?
63
113
  npx @sriinnu/omit gate # the pre-commit check, callable from anywhere
114
+ npx @sriinnu/omit hook install codex # write .codex/hooks.json — live sentinels inside Codex CLI
64
115
  ```
65
116
 
66
117
  And server-side, the GitHub Action comments the verdict on every PR regardless of what wrote the code:
@@ -76,6 +127,9 @@ jobs:
76
127
  - uses: actions/checkout@v4
77
128
  with: { fetch-depth: 0 }
78
129
  - uses: sriinnu/omit@main
130
+ # with: { exec: true } # execute receipts' `run` snippets to verify them
131
+ # fully. Off by default: a PR's receipts are
132
+ # untrusted code, and this runs on pull requests.
79
133
  ```
80
134
 
81
135
  ```
@@ -84,7 +138,14 @@ jobs:
84
138
  - new deps: 0 ✅
85
139
  - hazards: 0 ✅
86
140
  - footnotes: 3 recorded · load-bearing: 1 marked
87
- - omit score: 91/100 🟢
141
+ - receipts: 17/17 verified
142
+ - lint: eslint ✅
143
+ ```
144
+
145
+ ```
146
+ // omitted: a composite "omit score": any weight vector over these counts is
147
+ // invented, and footnotes could raise it by adding lines. The counts above are
148
+ // the receipts; a single number would just be another uncited claim.
88
149
  ```
89
150
 
90
151
  ## The referee (experimental)
@@ -162,10 +223,6 @@ skills/omit/SKILL.md → .claude/skills/omit/SKILL.md (project)
162
223
  - `/omit [margin|redline|rewrite|off]`: switch or show the current mode
163
224
  - `/omit-edit`: run an editor's pass over the current diff: flag bloat, uncited claims, missing footnotes, and cut opportunities
164
225
 
165
- ## Benchmarks
166
-
167
- None yet: and we won't publish numbers we can't hand you the harness for. `benchmarks/METHODOLOGY.md` defines the measurement we consider honest (paired tasks, agentic baseline, net LOC / new deps / defect rate / load-bearing violations, full transcripts). Reproducible runs are the most welcome PR this repo can receive.
168
-
169
226
  ## Prior art
170
227
 
171
228
  The minimalism-pressure idea was popularized by [ponytail](https://github.com/DietrichGebert/ponytail), which deserves its stars. `omit` differs where it matters: shortcuts require citations, the diff is edited *after* it works, safety lines are enumerated and never cut, and what's left out is footnoted instead of silent.
package/action.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  name: omit audit
2
- description: Comment the omit verdict (net diff, new deps, hazards, score) on pull requests: works whatever agent or editor wrote the code.
2
+ description: Comment the omit verdict (net diff, new deps, hazards, lint) on pull requests — works whatever agent or editor wrote the code.
3
3
  branding:
4
4
  icon: scissors
5
5
  color: gray-dark
@@ -8,11 +8,28 @@ inputs:
8
8
  description: Base ref to diff against
9
9
  required: false
10
10
  default: ''
11
+ exec:
12
+ description: >
13
+ Execute the `run` snippets in .omit/receipts.jsonl to fully verify them.
14
+ Off by default: a PR's receipts are untrusted code, and this action runs on
15
+ pull requests. Turn it on for repos whose PRs you already run code from.
16
+ Also gates the repo's own linter, which a PR's config file can make
17
+ arbitrary: with exec off, the verdict reports the linter as not run.
18
+ required: false
19
+ default: 'false'
11
20
  runs:
12
21
  using: composite
13
22
  steps:
14
23
  - name: Run omit audit
15
24
  shell: bash
25
+ env:
26
+ # Inverted on purpose. The obvious `exec == 'true' && '' || '1'` never
27
+ # enables anything: '' is falsy in an expression, so the `&&` yields
28
+ # false and the `||` always takes '1'. Here the value being truth-tested
29
+ # is '1', which is never ambiguous, and the enabled branch yields '0' —
30
+ # an explicit off-value for OMIT_NO_EXEC, so the input can never mean
31
+ # "maybe". Anything that isn't exactly 'true' stays disabled.
32
+ OMIT_NO_EXEC: ${{ inputs.exec != 'true' && '1' || '0' }}
16
33
  run: |
17
34
  BASE="${{ inputs.base }}"
18
35
  if [ -z "$BASE" ]; then BASE="origin/${{ github.event.pull_request.base.ref }}"; fi
package/bench/run.mjs CHANGED
@@ -10,6 +10,7 @@ import { tmpdir } from 'node:os'
10
10
  import { basename, join, resolve } from 'node:path'
11
11
  import { fileURLToPath } from 'node:url'
12
12
  import { isManifest, addedDeps } from '../lib/deps.mjs'
13
+ import { fileAtRevision } from '../lib/git.mjs'
13
14
  import { findHazards } from '../lib/hazards.mjs'
14
15
 
15
16
  const configPath = process.argv[2]
@@ -51,7 +52,7 @@ for (const task of cfg.tasks) {
51
52
  const durationMs = Date.now() - t0
52
53
 
53
54
  // metrics vs the base commit
54
- let added = 0, deleted = 0, newDeps = [], hazards = []
55
+ let added = 0, deleted = 0, newDeps = [], hazards = [], metricsError = null
55
56
  try {
56
57
  sh('git add -A', dir)
57
58
  for (const row of sh('git diff --cached --numstat', dir).split('\n').filter(Boolean)) {
@@ -59,11 +60,30 @@ for (const task of cfg.tasks) {
59
60
  if (a === '-') continue
60
61
  added += +a
61
62
  deleted += +d
62
- if (isManifest(path)) newDeps.push(...addedDeps(basename(path), sh(`git diff --cached -- "${path}"`, dir)))
63
+ // Declared dependency sets across the two revisions, not diff lines:
64
+ // the arm's manifest is read the same whether the agent wrote it
65
+ // minified or pretty-printed.
66
+ if (isManifest(path)) {
67
+ let after = ''
68
+ try {
69
+ after = readFileSync(join(dir, path), 'utf8')
70
+ } catch {}
71
+ // Three outcomes, not string-or-null: "git could not answer" used to
72
+ // coalesce to '' and report every dependency the manifest declares as
73
+ // newly added — a measurement nobody made, in a file of measurements.
74
+ const before = fileAtRevision(dir, 'HEAD', path)
75
+ if (before.failed) throw new Error(`git could not read ${path} at HEAD: ${before.reason}`)
76
+ newDeps.push(...addedDeps(basename(path), before.present ? before.text : '', after))
77
+ }
63
78
  }
64
79
  const addedLines = sh('git diff --cached', dir).split('\n').filter((l) => l.startsWith('+') && !l.startsWith('+++')).map((l) => l.slice(1))
65
80
  hazards = findHazards(addedLines)
66
- } catch {}
81
+ } catch (e) {
82
+ // Swallowing this printed "deps +0 · hazards 0" for a run nothing measured.
83
+ // The harness keeps going (it is a referee, not a gate) but the run's
84
+ // record says which metrics are missing, and the summary counts the run.
85
+ metricsError = e.message
86
+ }
67
87
 
68
88
  let testPass = null
69
89
  if (task.testCmd) {
@@ -71,10 +91,11 @@ for (const task of cfg.tasks) {
71
91
  testPass = t.status === 0
72
92
  }
73
93
 
74
- const rec = { task: task.id, arm: arm.name, added, deleted, net: added - deleted, newDeps, hazards: hazards.length, testPass, durationMs, dir }
94
+ const rec = { task: task.id, arm: arm.name, added, deleted, net: added - deleted, newDeps, hazards: hazards.length, testPass, durationMs, dir, ...(metricsError ? { metricsError } : {}) }
75
95
  results.push(rec)
76
96
  writeFileSync(join(benchRoot, `${task.id}--${arm.name}.log`), `${run.stdout ?? ''}\n--- stderr ---\n${run.stderr ?? ''}`)
77
97
  console.log(` net ${rec.net >= 0 ? '+' : ''}${rec.net} · deps +${newDeps.length} · hazards ${rec.hazards} · tests ${testPass === null ? 'n/a' : testPass ? 'pass' : 'FAIL'} · ${(durationMs / 1000).toFixed(0)}s`)
98
+ if (metricsError) console.log(` ⚠ metrics incomplete: ${metricsError}`)
78
99
  }
79
100
  }
80
101