@optitransfer/proofline 0.0.0-stage → 0.2.0-next.1
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/AGENTS.md +32 -0
- package/CHANGELOG.md +56 -0
- package/CONTRIBUTING.md +29 -0
- package/LICENSE +154 -0
- package/NOTICE +10 -0
- package/PATENTS-NONASSERT.md +1 -0
- package/README.md +285 -2
- package/bin/proofline.mjs +292 -0
- package/docs/00-index.md +43 -0
- package/docs/01-scope.md +70 -0
- package/docs/02-requirements.md +64 -0
- package/docs/03-domain-model.md +43 -0
- package/docs/04-architecture.md +79 -0
- package/docs/05-lifecycle.md +47 -0
- package/docs/06-verification.md +57 -0
- package/docs/07-evidence.md +69 -0
- package/docs/08-adapters.md +61 -0
- package/docs/09-security.md +64 -0
- package/docs/10-conformance.md +59 -0
- package/docs/11-development-pipeline.md +50 -0
- package/docs/12-roadmap.md +104 -0
- package/docs/13-interdependencies.md +53 -0
- package/docs/14-metrics.md +40 -0
- package/docs/adr/0001-product-boundary.md +28 -0
- package/docs/adr/0002-evidence-and-gates.md +22 -0
- package/docs/adr/0003-standalone-first.md +18 -0
- package/docs/receipt.md +25 -0
- package/package.json +60 -4
- package/src/cache.mjs +81 -0
- package/src/checks.mjs +276 -0
- package/src/claims.mjs +60 -0
- package/src/contract.mjs +404 -0
- package/src/engine.mjs +378 -0
- package/src/evidence.mjs +45 -0
- package/src/forbidden.mjs +57 -0
- package/src/guided.mjs +240 -0
- package/src/hook-stop.mjs +105 -0
- package/src/index.mjs +431 -0
- package/src/infer.mjs +173 -0
- package/src/inference.json +20 -0
- package/src/isolate.mjs +123 -0
- package/src/mcp.mjs +70 -0
- package/src/receipt.mjs +17 -0
- package/src/signature.mjs +110 -0
- package/src/util.mjs +92 -0
- package/src/vault.mjs +109 -0
- package/src/verify-receipt.mjs +109 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# AGENTS.md: the loop a coding agent follows in a repository that uses proofline (copy this file into that repository)
|
|
2
|
+
|
|
3
|
+
Proofline decides whether your work is done. Your report is a claim; Proofline re-runs every check itself.
|
|
4
|
+
|
|
5
|
+
## Loop (every task, no exceptions)
|
|
6
|
+
|
|
7
|
+
1. `proofline next` (MCP: `task_next`). Work only on the task it returns. Read the whole brief. If the contract has owners, add `--owner <your name>` (MCP: `owner`) so you only get your tasks and unowned ones.
|
|
8
|
+
2. Write the failing test(s) first. Run them and keep the failing output.
|
|
9
|
+
3. Implement the root-cause fix **on the default path** (wired in, default config). Code that is built but unreachable does not count.
|
|
10
|
+
4. `proofline verify <TASK>` (MCP: `verify`). Read the corrective instruction. Fix and repeat until `DECISION: PASS`.
|
|
11
|
+
Max 3 corrective cycles; then report `blocked` with root cause (file:line), what you tried, what you need.
|
|
12
|
+
5. Commit (one task per commit) and push to the contract's remote and branch (`defaults.remote`, default `origin`; `defaults.branch`, default `main`) with hooks on. The pre-push hook re-gates the task. You may end the commit message with the trailer `Proofline-Base: <sha>` (the base the brief names), so a fresh clone gates against the same base.
|
|
13
|
+
6. `proofline gate <TASK> --claims "<report line>"` (MCP: `gate` with `claims`). Paste the report. In a clone where `next` did not run, add `--base <sha>` (MCP: `base`); without a base the gate is BLOCKED, never PASS.
|
|
14
|
+
7. Back to step 1. The closing text of each gate says which step is next: push (PASS, not pushed yet), report with claims (PASS on the pushed HEAD), or `proofline next` (claims accepted).
|
|
15
|
+
|
|
16
|
+
## Report line (exact)
|
|
17
|
+
|
|
18
|
+
`TASK-ID | done/partial/not done/blocked | <commit> | <test names> | <metric=value vs target> | <what is missing>`
|
|
19
|
+
|
|
20
|
+
Proofline rejects: "done" without a commit, test names or measured numbers; numbers that differ from its own measurement (>15%); commits not on the contract's push branch; tests that don't exist; hedged or deferred wording ("should work", "likely", "follow-up", "later", "out of scope").
|
|
21
|
+
|
|
22
|
+
## Never
|
|
23
|
+
|
|
24
|
+
- Skip/only/todo tests, raise timeouts, delete tests or assertions, edit fixtures to pass (unless the fixture is the proven root cause, with proof).
|
|
25
|
+
- Lower a target, cap or test size, or touch thresholds/commands/criteria in `proofline.json` (detected; BLOCKED until the owner approves).
|
|
26
|
+
- `--no-verify`, bypass env vars, skip flags. There is no bypass. If CI runs `proofline gate --ci`, it re-runs the gate.
|
|
27
|
+
- Say "done" for anything that is not PASS on the pushed HEAD.
|
|
28
|
+
- Defer. Only the owner approves a deferral.
|
|
29
|
+
|
|
30
|
+
## Stop rules
|
|
31
|
+
|
|
32
|
+
If a metric misses its threshold: STOP. Report the measured number, the per-stage trace and file:line. Do not start the next task.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable project changes are recorded here. Dates use ISO 8601 (`YYYY-MM-DD`).
|
|
4
|
+
|
|
5
|
+
## 0.2.0-next.1 (2026-10-17)
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- A contract whose canonical bytes differ from its base is refused at pre-commit, verify, and gate unless the approval marker names the new bytes. The field-level weakening list stays the explanation, and a tightening is still not a weakening.
|
|
9
|
+
- The repository commits package-lock.json at lockfile version 3, made with npm install --package-lock-only --ignore-scripts, so a gate can install this revision from the revision itself.
|
|
10
|
+
- `verify` and `gate` stay on the host unless `--isolated` is passed. That mode runs command, red-green and metric checks with no network, a fresh HOME, and the repository read-only except paths the check declares. The image digest is `env.image` on the contract under test. Without it, `--isolated` refuses and names `env.image`. The receipt records `isolated`, `env_digest` and `heuristic`. grep, wiring and the forbidden scan are one layer, not a boundary.
|
|
11
|
+
- Every subcommand refuses a flag it does not accept, exits 2, and names the unknown flag and the accepted ones. Nothing runs after that refusal (#33).
|
|
12
|
+
- The pre-push hook leaves out a commit already on the server default branch. It reads that head with `git ls-remote` of the push URL at send time, for an existing branch and for a new branch. A local remote-tracking ref does not count. If the server cannot be read, every commit in the range is gated (#31, #35).
|
|
13
|
+
- The pre-push hook gates the commit being pushed, at that sha, in a temporary worktree. It reads the local sha from stdin and does not gate the checked-out HEAD. The base is the most recent `proofline next` pin for the task, or the parent of that commit, and a refusal prints the base it used. `proofline next --base <rev>` records a newer pin. A worktree with no evidence still gates, and stale evidence in the pushing worktree cannot block a commit that passes at its own sha (#21, #22, #23).
|
|
14
|
+
- `proofline contract sign` writes `proofline.json.sig`, a signature over the canonical contract by the agentkey owner role. `proofline contract verify-sig` checks it against `guard/owner.pub` only. Once that pin is present, `next`, `verify`, `gate`, `brief` and both hooks exit 2 with `contract not owner-signed` when the signature is missing, altered or from another key. `proofline contract propose` writes `proposals/<sha256>.diff` and prints the hash.
|
|
15
|
+
- `proofline setup` installs the hooks, creates the owner key through agentkey and sets approvals to `.proofline/approvals`. `proofline task` writes a proposal from plain words and prints its hash. `proofline contract approve` shows the diff and, on y, writes the marker and applies the bytes. `proofline watch` prints the active task until the gate is PASS.
|
|
16
|
+
- `proofline contract merge` appends a fragment's tasks and new owner rules, refuses a duplicate id, keeps the existing bytes and prints the sha256. `proofline contract coverage` prints each requirement id with the tasks that name it, or `MISSING`, and exits 1 on a gap or an unknown id.
|
|
17
|
+
- `proofline contract scaffold` prints a task with a red-then-green test, the suite, scope, pushed and the default stop rules. `contract validate` refuses a task with no stop rules, a task with no scope criterion, a criterion path outside that scope, a double-quoted `sh -c` or `bash -c` command that contains `$`, and a red-then-green criterion with no test file. A grep that already matches on the base revision is a warning that names that revision. `brief` and `steer` no longer throw when a loaded task has no stop rules (#15).
|
|
18
|
+
- `examples/demo/catch.mjs`: a short demo that catches a false report, a test that proves nothing, a skipped test with a raised timeout, a change outside the scope and a weakened contract, then passes the real work; its transcript is in the README.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- `proofline setup` writes `guard/owner.pub` from the owner key it creates, so `proofline contract sign` makes a signature that `verify-sig` checks without copying the key by hand. A pin that already holds those bytes is left alone. A pin that holds other bytes is refused before anything is written, and the refusal names both paths.
|
|
22
|
+
- An active task ends when it lands. `next`, `verify` and the hooks clear `.proofline/active` once that task's head is an ancestor of `origin/main` or its pushed criterion has a PASS record, and they say so. The pre-push hook gates the task the commits name. A push that names no task and has no active task is refused, and the refusal prints `proofline next` (#25).
|
|
23
|
+
- `contract create` keeps one level of `scope.allow` when the task file writes the scope as the contract does, `{ "allow": [...] }`, and `contract validate` refuses a scope that is not a list of patterns, so `next` cannot crash on it (#19).
|
|
24
|
+
- A failed `command` criterion's evidence names the failed tests. `out_tail` keeps the last 1,200 characters, where node's summary of repeated failures with their stacks held the last two or three names. The new `failing` field lists the failed test names found anywhere in the output (TAP `not ok` lines, the spec reporter's `✖ <name> (<ms>ms)` lines and the `failing tests:` section), without todos, skips or repeats, in order of first appearance and at most 100, with `failing_total` when there were more. The FAIL detail names the first 10, a flaky result keeps both runs' lists, and the gate report lists the names under Failing tests (#13).
|
|
25
|
+
- The forbidden scan no longer reads `proofline.json` or `.proofline/`: the commit that adds or amends the contract failed on its own deny-list. Contract changes stay covered by the weakening check, and the gate report says whether the contract was added, changed or unchanged vs the base (#4).
|
|
26
|
+
- `init` no longer replaces an existing `pre-commit` or `pre-push` hook: it keeps it as `<hook>.proofline-prev`, and the installed hook runs it first (same arguments and stdin) and stops on its failure. A second `init` changes nothing (#1).
|
|
27
|
+
- The corrective text for an unpushed task and the brief name the push target from the criterion's `remote`/`branch`, else `defaults.remote`/`defaults.branch`, instead of always `origin` (#8).
|
|
28
|
+
- A PASS no longer always says "Commit, push to main ... Then call task_next". On a HEAD already on the push target it says the task is verified on the pushed HEAD and gives the `gate` command with claims (and `--base` when one was passed); after accepted claims it points to `proofline next`; only an unpushed PASS says to push, to the configured remote and branch (#9).
|
|
29
|
+
- A verify or gate with no pinned base, where the default base is HEAD (a fresh clone after the push), is BLOCKED instead of passing every diff check on an empty diff (#5).
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- `@optitransfer/proofline@0.2.0-next.0` is the public package on the `next` dist-tag. `private` is false, the test script runs `node --test test/*.test.mjs`, and `files` lists what this revision ships. The sixty-second start installs that package and calls `npx proofline`. The package description, keywords, homepage, repository, bugs and author match the GitHub repository page.
|
|
33
|
+
- `proofline hook stop` writes one receipt for the commits a session made, and stays quiet when there is nothing new. `proofline connect claude-code` merges that hook into `~/.claude/settings.json` after the hooks already there.
|
|
34
|
+
- MCP tools `receipt` and `verify_receipt` return the same JSON the CLI prints with `--json`. `tools/list` shows nine tools.
|
|
35
|
+
- A hivevault digest can be attached to a receipt. L1 stores the digest and the frontier. L2 in ci mode stores only a commitment. No daemon leaves the receipt unchanged. `--vault` writes the note under `work/receipts/`.
|
|
36
|
+
- `verify-receipt` checks a DSSE receipt offline. Exit 0 is verified, 1 is a bad signature, a schema problem or a `--recompute` difference, 2 is an unknown key, and 3 is usage. `--sigstore` is the only path that loads that verifier.
|
|
37
|
+
- Zero-config inference reads suite commands from `src/inference.json` at the base revision. Nothing inferable returns an L0 receipt and exit 2. A missing toolchain is blocked, not a pass. A change to the test command is a `test_command_changed` finding, and the base command is what runs.
|
|
38
|
+
- The pre-commit hook accepts a contract change only when an approval marker is present. The marker is a file named with the sha256 of the staged `proofline.json` bytes, in the directory set by `init --approvals` and stored in `.proofline/config.json` (never committed). The refusal prints the weakenings, the sha256 and the marker path. A marker for other bytes does not count, what else is staged does not matter, and with no directory configured every contract change is refused. `verify` and `gate` use the same marker for the current `proofline.json` bytes: a regular file approves the weakenings, the report names the marker path, and the decision is not blocked.
|
|
39
|
+
- Opt-in result reuse: with `defaults.reuse_results` (validated, default `false`), a `command` or `metric` PASS on a clean tree is recorded in the evidence log and reused by a later `verify` or `gate` on the same HEAD, so `verify`, `gate` and the pre-push hook run the full suite once per commit. The key is the verifier, HEAD, `process.version` and the env vars the command names; only a PASS is stored; `defaults.reuse_max_age_h` (default 24) bounds its age; a metric is reused only within `max_load_per_cpu`. A result is reused only from an evidence entry still in an intact chain with the hash the cache file stored. Reused criteria say `reused from <time> (evidence entry <n>)` and are listed apart in the gate report and its evidence entry. `--fresh` on `verify` and `gate` (MCP: `fresh`) runs every check. Turning reuse on or off is not a weakening (#12).
|
|
40
|
+
- Metric evidence and report lines record the load before the run, the CPU count and the `node --version` the bench command resolves. Optional `defaults.max_load_per_cpu`: a metric run on a busier machine is BLOCKED (machine busy, with the numbers) and the bench is not run; raising or removing the limit is a weakening (#7).
|
|
41
|
+
- `defaults.remote` in the contract (validated as a string), used by `pushed`, by claim reconciliation and by the default base, which is now `defaults.base`, else `<remote>/<branch>`. A repository whose remote is not `origin` no longer gets a BLOCKED gate on an unresolved base, or a claim check that silently checks nothing (#6).
|
|
42
|
+
- Optional `owner` on a task and `defaults.owners` in the contract (validated); `--owner <name>` on `next`, `status` and `brief` (MCP: `task_next` and `status`). `next --owner` hands out that owner's tasks and unowned ones, never another owner's (#2).
|
|
43
|
+
- `--base <rev>` on `verify` and `gate` (MCP: `base`), an optional `Proofline-Base:` commit trailer, and the base source (`--base`, contract, pinned at start, commit trailer, default) in every report. The `next` brief prints the pinned base (#5).
|
|
44
|
+
- `brief <TASK> --base <rev>`: the brief ends with the base `verify` and `gate` will use, its source, and the reason it would be BLOCKED, if any (#5).
|
|
45
|
+
|
|
46
|
+
## [0.1.0] - 2026-10-14
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- Executable MVP: CLI (`init`, `contract`, `next`, `brief`, `verify`, `gate`, `report`, `status`, `evidence`, `hook`, `mcp`).
|
|
50
|
+
- Contract schema `proofline/1` with ten deterministic verifier types, phase + dependency task graph, contract-weakening detection.
|
|
51
|
+
- Hash-chained append-only evidence log; red-green replay in a git worktree; flaky re-run policy; baseline regression check.
|
|
52
|
+
- Claim reconciliation of `TASK | status | commit | tests | numbers | missing` lines; corrective prompts per failed criterion.
|
|
53
|
+
- MCP stdio server, git pre-commit/pre-push hooks with no bypass variable, GitHub Actions self-gate.
|
|
54
|
+
- AGENTS.md agent loop, FAIL to PASS demo.
|
|
55
|
+
- Initial product scope, requirements, architecture, lifecycle, evidence model, security posture, verification strategy, delivery pipeline, roadmap, and dependency map.
|
|
56
|
+
- Initial Mermaid diagrams, ADRs, task-contract example, and project templates.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Current phase
|
|
4
|
+
|
|
5
|
+
The repository holds the CLI, the MCP server, the engine and their tests. Changes to scope, requirements, architecture, security, or acceptance criteria should be explicit and traceable.
|
|
6
|
+
|
|
7
|
+
## Change process
|
|
8
|
+
|
|
9
|
+
1. State the problem and the requirement(s) affected.
|
|
10
|
+
2. Record assumptions and exclusions.
|
|
11
|
+
3. Update the relevant specification and, if needed, an ADR.
|
|
12
|
+
4. Add or update conformance cases for behaviour changes.
|
|
13
|
+
5. Update the roadmap and changelog when status or commitments change.
|
|
14
|
+
6. Do not label a capability implemented until code and repeatable evidence exist.
|
|
15
|
+
|
|
16
|
+
## Design review checklist
|
|
17
|
+
|
|
18
|
+
- Does the change preserve the separation between agent claims and independent evidence?
|
|
19
|
+
- Can it be used without any optional integration (context store, agent SDK, network)?
|
|
20
|
+
- Does it distinguish pass, fail, skipped, not run, and inconclusive?
|
|
21
|
+
- Does it introduce unbounded retries, hidden side effects, or excessive interruption?
|
|
22
|
+
- Are permission boundaries and secret handling explicit?
|
|
23
|
+
- Can the outcome be reproduced from the report and captured revision?
|
|
24
|
+
|
|
25
|
+
## Sign-off
|
|
26
|
+
|
|
27
|
+
Each commit needs a Developer Certificate of Origin sign-off (https://developercertificate.org/). Put this line in the commit message, with the name and email of the commit:
|
|
28
|
+
|
|
29
|
+
Signed-off-by: Name <email>
|
package/LICENSE
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all other
|
|
16
|
+
entities that control, are controlled by, or are under common control
|
|
17
|
+
with that entity. For the purposes of this definition, "control" means
|
|
18
|
+
(i) the power, direct or indirect, to cause the direction or management
|
|
19
|
+
of such entity, whether by contract or otherwise, or (ii) ownership of
|
|
20
|
+
fifty percent (50%) or more of the outstanding shares, or (iii)
|
|
21
|
+
beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
|
24
|
+
permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation source,
|
|
28
|
+
and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but not
|
|
32
|
+
limited to compiled object code, generated documentation, and
|
|
33
|
+
conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or Object
|
|
36
|
+
form, made available under the License, as indicated by a copyright
|
|
37
|
+
notice that is included in or attached to the work (an example is
|
|
38
|
+
provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including the
|
|
49
|
+
original version of the Work and any modifications or additions to that
|
|
50
|
+
Work or Derivative Works thereof, that is intentionally submitted to
|
|
51
|
+
Licensor for inclusion in the Work by the copyright owner or by an
|
|
52
|
+
individual or Legal Entity authorized to submit on behalf of the
|
|
53
|
+
copyright owner. For the purposes of this definition, "submitted" means
|
|
54
|
+
any form of electronic, verbal, or written communication sent to the
|
|
55
|
+
Licensor or its representatives, including communication on electronic
|
|
56
|
+
mailing lists, source code control systems, and issue tracking systems
|
|
57
|
+
that are managed by, or on behalf of, the Licensor for the purpose of
|
|
58
|
+
discussing and improving the Work, but excluding communication that is
|
|
59
|
+
conspicuously marked or otherwise designated in writing by the copyright
|
|
60
|
+
owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of this
|
|
67
|
+
License, each Contributor hereby grants to You a perpetual, worldwide,
|
|
68
|
+
non-exclusive, no-charge, royalty-free, irrevocable copyright license
|
|
69
|
+
to reproduce, prepare Derivative Works of, publicly display, publicly
|
|
70
|
+
perform, sublicense, and distribute the Work and such Derivative Works
|
|
71
|
+
in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of this
|
|
74
|
+
License, each Contributor hereby grants to You a perpetual, worldwide,
|
|
75
|
+
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
|
|
76
|
+
this section) patent license to make, have made, use, offer to sell,
|
|
77
|
+
sell, import, and otherwise transfer the Work, where such license applies
|
|
78
|
+
only to those patent claims licensable by such Contributor that are
|
|
79
|
+
necessarily infringed by their Contribution(s) alone or by combination
|
|
80
|
+
of their Contribution(s) with the Work to which such Contribution(s) was
|
|
81
|
+
submitted. If You institute patent litigation against any entity
|
|
82
|
+
(including a cross-claim or counterclaim in a lawsuit) alleging that the
|
|
83
|
+
Work or a Contribution incorporated within the Work constitutes direct
|
|
84
|
+
or contributory patent infringement, then any patent licenses granted
|
|
85
|
+
to You under this License for that Work shall terminate as of the date
|
|
86
|
+
such litigation is filed.
|
|
87
|
+
|
|
88
|
+
4. Redistribution. You may reproduce and distribute copies of the Work or
|
|
89
|
+
Derivative Works thereof in any medium, with or without modifications,
|
|
90
|
+
and in Source or Object form, provided that You meet the following
|
|
91
|
+
conditions:
|
|
92
|
+
(a) You must give any other recipients of the Work or Derivative Works
|
|
93
|
+
a copy of this License; and
|
|
94
|
+
(b) You must cause any modified files to carry prominent notices stating
|
|
95
|
+
that You changed the files; and
|
|
96
|
+
(c) You must retain, in the Source form of any Derivative Works that
|
|
97
|
+
You distribute, all copyright, patent, trademark, and attribution
|
|
98
|
+
notices from the Source form of the Work, excluding notices that do
|
|
99
|
+
not pertain to any part of the Derivative Works; and
|
|
100
|
+
(d) If the Work includes a "NOTICE" text file as part of its distribution,
|
|
101
|
+
then any Derivative Works that You distribute must include a readable
|
|
102
|
+
copy of the attribution notices contained within such NOTICE file,
|
|
103
|
+
excluding those notices that do not pertain to any part of the
|
|
104
|
+
Derivative Works.
|
|
105
|
+
You may add Your own copyright statement to Your modifications and may
|
|
106
|
+
provide additional or different license terms and conditions for use,
|
|
107
|
+
reproduction, or distribution of Your modifications, or for any such
|
|
108
|
+
Derivative Works as a whole, provided Your use, reproduction, and
|
|
109
|
+
distribution of the Work otherwise complies with the conditions stated
|
|
110
|
+
in this License.
|
|
111
|
+
|
|
112
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
113
|
+
any Contribution intentionally submitted for inclusion in the Work by You
|
|
114
|
+
to the Licensor shall be under the terms and conditions of this License,
|
|
115
|
+
without any additional terms or conditions. Notwithstanding the above,
|
|
116
|
+
nothing herein shall supersede or modify the terms of any separate
|
|
117
|
+
license agreement you may have executed with Licensor regarding such
|
|
118
|
+
Contributions.
|
|
119
|
+
|
|
120
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
121
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
122
|
+
except as required for reasonable and customary use in describing the
|
|
123
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
124
|
+
|
|
125
|
+
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
|
|
126
|
+
in writing, Licensor provides the Work (and each Contributor provides
|
|
127
|
+
its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
|
128
|
+
OF ANY KIND, either express or implied, including, without limitation,
|
|
129
|
+
any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY,
|
|
130
|
+
or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for
|
|
131
|
+
determining the appropriateness of using or redistributing the Work and
|
|
132
|
+
assume any risks associated with Your exercise of permissions under this License.
|
|
133
|
+
|
|
134
|
+
8. Limitation of Liability. In no event and under no legal theory, whether
|
|
135
|
+
in tort (including negligence), contract, or otherwise, unless required
|
|
136
|
+
by applicable law or agreed to in writing, shall any Contributor be liable
|
|
137
|
+
to You for damages, including any direct, indirect, special, incidental,
|
|
138
|
+
or consequential damages of any character arising as a result of this
|
|
139
|
+
License or out of the use or inability to use the Work (including but not
|
|
140
|
+
limited to damages for loss of goodwill, work stoppage, computer failure
|
|
141
|
+
or malfunction, or any and all other commercial damages or losses), even
|
|
142
|
+
if such Contributor has been advised of the possibility of such damages.
|
|
143
|
+
|
|
144
|
+
9. Accepting Warranty or Additional Liability. While redistributing the Work
|
|
145
|
+
or Derivative Works thereof, You may choose to offer, and charge a fee for,
|
|
146
|
+
acceptance of support, warranty, indemnity, or other liability obligations
|
|
147
|
+
and/or rights consistent with this License. However, in accepting such
|
|
148
|
+
obligations, You may act only on Your own behalf and on Your sole
|
|
149
|
+
responsibility, not on behalf of any other Contributor, and only if You
|
|
150
|
+
agree to indemnify, defend, and hold each Contributor harmless for any
|
|
151
|
+
liability incurred by, or claims asserted against, such Contributor by
|
|
152
|
+
reason of your accepting any such warranty or additional liability.
|
|
153
|
+
|
|
154
|
+
END OF TERMS AND CONDITIONS
|
package/NOTICE
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
proofline
|
|
2
|
+
Copyright 2026 Ryan Gillespie / Optitransfer
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
5
|
+
|
|
6
|
+
"proofline" is a trademark of Ryan Gillespie / Optitransfer. The Apache License 2.0 (Section 6)
|
|
7
|
+
grants no trademark rights: any fork or a derived product uses its own name, and may say that it
|
|
8
|
+
is derived from proofline.
|
|
9
|
+
Contributions are accepted under the Apache License 2.0 with a Developer Certificate of Origin
|
|
10
|
+
sign-off on each commit (CONTRIBUTING.md); there is no contributor licence agreement.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Apache-2.0 section 3 grants the patent licence for contributions to this project. A fuller non-assert follows counsel review, and the entity signs that covenant.
|
package/README.md
CHANGED
|
@@ -1,3 +1,286 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Proofline
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The agent says done. Proofline verifies and validates the work against a V&V contract, to assure agent performance.**
|
|
4
|
+
|
|
5
|
+
A coding agent reports that a task is finished. Proofline does not take the report: it holds the definition of done as a contract a machine can check, hands the agent one task at a time, re-runs every check itself when the agent reports, and lets a commit through only when the evidence says PASS. The agent gets a corrective prompt that names the file and the line to fix next, never a verdict it can argue with. Zero dependencies, Node 22+, Apache-2.0.
|
|
6
|
+
|
|
7
|
+
`proofline verify --isolated` and `proofline gate --isolated` run command, red-green and metric checks in a container. Local use stays off unless that flag is passed. The image digest is `env.image` on the contract under test. Without it, `--isolated` refuses and names `env.image`.
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart LR
|
|
11
|
+
C["proofline.json<br/>tasks, criteria, scope,<br/>forbidden actions"] --> N["proofline next<br/>one task, its brief,<br/>the base revision pinned"]
|
|
12
|
+
N --> A["the agent<br/>failing test first,<br/>then the fix"]
|
|
13
|
+
A --> H1{"pre-commit hook<br/>forbidden actions,<br/>contract change without a marker"}
|
|
14
|
+
H1 -- "refused" --> A
|
|
15
|
+
H1 -- "clean" --> V["proofline verify<br/>every criterion re-run<br/>from the base revision"]
|
|
16
|
+
V -- "FAIL or BLOCKED:<br/>corrective prompt,<br/>file and line" --> A
|
|
17
|
+
V -- "PASS" --> G["proofline gate --claims<br/>the agent's report line<br/>against the evidence"]
|
|
18
|
+
G -- "a claim the evidence<br/>does not support" --> A
|
|
19
|
+
G -- "PASS, report written" --> H2{"pre-push hook<br/>does this HEAD gate PASS?"}
|
|
20
|
+
H2 -- "no" --> A
|
|
21
|
+
H2 -- "yes" --> M[("main")]
|
|
22
|
+
M --> N
|
|
23
|
+
O["owner marker<br/>sha256 of the approved<br/>proofline.json"] -. "approves a<br/>contract change" .-> H1
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## See it catch
|
|
27
|
+
|
|
28
|
+
`node examples/demo/catch.mjs` builds a throwaway repository with one task, then plays five ways an agent "finishes" without doing the work, and the real work last. Real output, shortened only where a line ran long:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
proofline next -> TASK-1: Saves return a durable receipt on the default path
|
|
32
|
+
|
|
33
|
+
1. The lie: no work, reported "done | abc1234 | app.test.mjs" -> FAIL
|
|
34
|
+
claim rejected: claimed "done" but the independent gate says FAIL: ...
|
|
35
|
+
claim rejected: commit abc1234 does not exist
|
|
36
|
+
TASK-1-AC01 FAIL test file test/durable.test.mjs not found
|
|
37
|
+
TASK-1-AC02 FAIL durableReceipt is not referenced from the default path (src/app.mjs; 2 files reachable). Built but not wired in.
|
|
38
|
+
|
|
39
|
+
2. The gap: a "durable" test that asserts what already held -> FAIL
|
|
40
|
+
TASK-1-AC01 FAIL test PASSES on base 412bc3d9 without the change: it does not prove the change (tests how, not what?)
|
|
41
|
+
|
|
42
|
+
3. The shortcut: test.skip and a timeout raised 2000 -> 20000 -> FAIL
|
|
43
|
+
forbidden: test_skip_only in test/app.test.mjs: test.skip('handle returns ok', { timeout: 20000 }, () => { assert.equal(handle('x').ok, true); });
|
|
44
|
+
forbidden: timeout_raise in test/app.test.mjs: 2000 → 20000
|
|
45
|
+
TASK-1-AC03 FAIL 1 skipped/todo > allowed 0
|
|
46
|
+
|
|
47
|
+
4. The stray change: README.md edited, outside the task's scope -> FAIL
|
|
48
|
+
TASK-1-AC04 FAIL changes outside the approved scope: README.md
|
|
49
|
+
|
|
50
|
+
5. The weakening: proofline.json edited so "suite green" runs `true`, then git commit -> REFUSED by the pre-commit hook
|
|
51
|
+
contract weakened: TASK-1/TASK-1-AC03 command changed: "node --test" → "true"
|
|
52
|
+
marker (no approvals directory configured)
|
|
53
|
+
|
|
54
|
+
6. The work: failing test first, durableReceipt wired in, committed through the hooks, pushed -> PASS
|
|
55
|
+
TASK-1-AC01 PASS red on base (exit 1), green now
|
|
56
|
+
TASK-1-AC02 PASS durableReceipt used on the default path: src/app.mjs
|
|
57
|
+
TASK-1-AC03 PASS exit 0 in 234 ms
|
|
58
|
+
TASK-1-AC04 PASS 2 changed files, all in scope
|
|
59
|
+
TASK-1-AC05 PASS HEAD f6234c72 is origin/main
|
|
60
|
+
claim accepted: "TASK-1 | done | f6234c7 | save is durable, test/durable.test.mjs | - | -"
|
|
61
|
+
evidence: 9 entries in the hash-chained log; report .proofline/reports/TASK-1-latest.md
|
|
62
|
+
|
|
63
|
+
Caught five, passed one.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Sixty seconds
|
|
67
|
+
|
|
68
|
+
In the repository the agent works on:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install --save-dev @optitransfer/proofline@next && npx proofline init --approvals ~/.proofline/approved && cp node_modules/@optitransfer/proofline/AGENTS.md .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
initialised /path/to/your-repo
|
|
76
|
+
proofline.json
|
|
77
|
+
.proofline/config.json
|
|
78
|
+
.gitignore
|
|
79
|
+
hooks/pre-commit
|
|
80
|
+
hooks/pre-push
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
That writes the contract skeleton, the local evidence log, the two git hooks, the directory where you (the owner) approve contract changes, and the loop the agent follows in `AGENTS.md`. Add one task and tell the agent to start:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cat > task.json <<'JSON'
|
|
87
|
+
{ "id": "TASK-1", "title": "Reject an empty body", "phase": "implement",
|
|
88
|
+
"scope": { "allow": ["src/**", "test/**", "CHANGELOG.md"] },
|
|
89
|
+
"criteria": [
|
|
90
|
+
{ "id": "TASK-1-AC01", "mandatory": true, "statement": "an empty body is refused",
|
|
91
|
+
"verifier": { "type": "test_red_green", "test_file": "test/empty_body.test.mjs", "cmd": "node --test {test}" } },
|
|
92
|
+
{ "id": "TASK-1-AC02", "mandatory": true, "statement": "suite green",
|
|
93
|
+
"verifier": { "type": "command", "cmd": "npm test" } },
|
|
94
|
+
{ "id": "TASK-1-AC03", "mandatory": true, "statement": "pushed", "verifier": { "type": "pushed" } } ] }
|
|
95
|
+
JSON
|
|
96
|
+
npx proofline contract create --from task.json # TASK-1: valid
|
|
97
|
+
npx proofline next # the agent's first command: the brief, with the base pinned
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
When the agent reports, Proofline checks. Before any work this is what it says, and the agent is told what to do next:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
$ npx proofline verify TASK-1
|
|
104
|
+
== TASK-1: FAIL
|
|
105
|
+
base: 0a88576db5567f08c9f1cd9481c633001036d8e1 = 0a88576d (pinned at start)
|
|
106
|
+
FAIL TASK-1-AC01 [test_red_green] test file test/empty_body.test.mjs not found
|
|
107
|
+
PASS TASK-1-AC02 [command] exit 0 in 393 ms
|
|
108
|
+
FAIL TASK-1-AC03 [pushed] working tree not clean (3 paths); work held locally
|
|
109
|
+
report: .proofline/reports/TASK-1-latest.md
|
|
110
|
+
|
|
111
|
+
PROOFLINE CORRECTIVE INSTRUCTION for TASK-1 (Reject an empty body): gate FAIL
|
|
112
|
+
1. TASK-1-AC01 [test_red_green] FAIL: an empty body is refused
|
|
113
|
+
Next step: The test test/empty_body.test.mjs must FAIL on the base and PASS now.
|
|
114
|
+
2. TASK-1-AC03 [pushed] FAIL: pushed
|
|
115
|
+
Next step: Commit everything and `git push origin main` (hooks on; never --no-verify).
|
|
116
|
+
DECISION: FAIL
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Exit codes: `0` PASS, `1` FAIL, `2` BLOCKED, `3` usage. Every command takes `--json`. The published package is `@optitransfer/proofline` at `0.2.0-next.0` on the `next` dist-tag: `npx @optitransfer/proofline@next`.
|
|
120
|
+
|
|
121
|
+
## How you use it, start to finish
|
|
122
|
+
|
|
123
|
+
### Short path
|
|
124
|
+
|
|
125
|
+
`proofline setup` then `proofline task "<plain words>"` then `proofline contract approve` then `proofline watch`.
|
|
126
|
+
|
|
127
|
+
That is the short path: four commands, in that order. The diagram and the table below are the manual path.
|
|
128
|
+
|
|
129
|
+
Once `guard/owner.pub` is pinned, `proofline contract sign` writes `proofline.json.sig` and `proofline contract verify-sig` checks it. `proofline contract propose` writes `proposals/<sha256>.diff` and prints the hash. An unsigned or altered contract is refused with `contract not owner-signed`.
|
|
130
|
+
|
|
131
|
+
Three parties: **you** (the owner of the repository), **your agent** (Claude Code, Cursor, a CI job), and **Proofline** (the hooks and the gate in your repository). The agent builds; Proofline checks; you decide twice, once on the rules and once on the result.
|
|
132
|
+
|
|
133
|
+
```mermaid
|
|
134
|
+
sequenceDiagram
|
|
135
|
+
participant You
|
|
136
|
+
participant Agent
|
|
137
|
+
participant Proofline
|
|
138
|
+
You->>Proofline: 1. npx proofline init
|
|
139
|
+
You->>Agent: 2. the task, in plain words
|
|
140
|
+
Agent->>Proofline: 3. proofline contract create (the task as checks)
|
|
141
|
+
Proofline-->>You: the contract and its hash
|
|
142
|
+
You->>Proofline: 4. approve the hash
|
|
143
|
+
Agent->>Proofline: 5. proofline next, build, proofline verify (repeat)
|
|
144
|
+
Agent->>Proofline: 6. git commit, git push (the hooks gate both)
|
|
145
|
+
Agent-->>You: 7. "done", commit, tests
|
|
146
|
+
Proofline-->>You: 8. gate report and signed receipt
|
|
147
|
+
You->>You: merge
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
| Step | Who | What happens | Command |
|
|
151
|
+
|---|---|---|---|
|
|
152
|
+
| 1 | You | Install Proofline in the repository once. It writes an empty `proofline.json`, installs the pre-commit and pre-push hooks and records where your approvals live. | `npm i -D @optitransfer/proofline@next` then `npx proofline init` |
|
|
153
|
+
| 2 | You | Describe the task to your agent in plain words. "Add per-client rate limiting, 100 requests a minute, 429 with Retry-After, every existing test stays green." | your agent's chat |
|
|
154
|
+
| 3 | Agent | Turn the words into a contract: a title, the files it may touch, and acceptance criteria that are checks, not sentences (a test that fails before and passes after, a grep for `Retry-After`, the suite green, changes in scope, pushed). The result is the contract's new bytes and their hash. The agent cannot put it into force. | `proofline contract create` |
|
|
155
|
+
| 4 | You | Read the contract (it is short and every line is checkable) and approve its hash. From now on the hooks accept only contract bytes you approved, so the agent cannot change the rules it is judged by. | approve the hash |
|
|
156
|
+
| 5 | Agent | Take the one active task with its brief, write the failing test first, then the code, and run the checks as often as it likes. Proofline re-runs every criterion itself and prints a corrective instruction for anything not yet true. | `proofline next`, `proofline verify TASK` |
|
|
157
|
+
| 6 | Agent | Commit and push. The pre-commit hook refuses the known shortcuts: skipped tests, raised timeouts, deleted assertions, a bypass flag. The pre-push hook runs the gate before anything leaves the machine; a FAIL stays local. | `git commit`, `git push` |
|
|
158
|
+
| 7 | Agent | Report: done, the commit, the tests. The report is a claim, not evidence. | one line |
|
|
159
|
+
| 8 | Proofline | The gate compares the claim with what it measured and writes the report: every criterion PASS or FAIL with the measured detail, and a signed receipt (through agentkey) saying which agent role produced the commit against which contract hash. A claim the evidence does not support is rejected. | `proofline gate TASK` |
|
|
160
|
+
| | You | Read the one page, not the diff. Merge. | |
|
|
161
|
+
|
|
162
|
+
What you never do: read the agent's prose for reassurance, trust a green checkmark the agent printed, or let the agent change the rules it is judged by.
|
|
163
|
+
|
|
164
|
+
## Why
|
|
165
|
+
|
|
166
|
+
An agent's report is a claim. Left to itself it says "done" for a test it skipped, a timeout it raised, a target it lowered, a commit it never pushed, or a number it did not measure. Reviewing every claim by hand does not scale past one agent, and asking a second model whether the first one is right only adds a second opinion.
|
|
167
|
+
|
|
168
|
+
Proofline replaces the opinion with a contract and a re-run. The contract is yours: it names each task, the files it may touch, and the checks that prove it, and it cannot be weakened by the agent, because a changed criterion, threshold or forbidden rule is refused at commit time unless a marker with the hash of the new contract sits in the directory only you write to. The checks are the kind a machine can settle: a test that fails on the base revision and passes now, a command and its exit code, a metric against its target, changed files inside the allowed scope, a HEAD that is on the contract's branch. Every result is appended to a hash-chained evidence log, so a report can be checked later against what actually ran.
|
|
169
|
+
|
|
170
|
+
## What it checks
|
|
171
|
+
|
|
172
|
+
Every criterion names a verifier. Each one is deterministic: no model reads the diff, no opinion decides.
|
|
173
|
+
|
|
174
|
+
| Type | Passes only when |
|
|
175
|
+
|---|---|
|
|
176
|
+
| `command` | exit code matches; no hung/cancelled tests; optional `max_skipped`, `min_tests` (catches deleted tests). A fail-then-pass is **flaky**, not a pass. Test counts and failing-test names are parsed from the Node test runner's output (TAP or spec reporter); for other runners use `command` criteria without `min_tests` |
|
|
177
|
+
| `test_red_green` | the test exists, **fails** on the base revision (replayed in a git worktree) and **passes** now; named test actually executed |
|
|
178
|
+
| `test_exists` | test file (and name) present |
|
|
179
|
+
| `metric` | bench command's JSON (`key` dotted path) meets `op value`; also fails on >20% regression vs `baseline.metrics`. One bench run feeds all its metrics. Missing rig → BLOCKED. The evidence and report line record the 1-minute load, the CPU count and the `node --version` the command resolves; with `defaults.max_load_per_cpu` set, a load per CPU above it before the run is BLOCKED (machine busy), never PASS or FAIL, and raising or removing that limit is a contract weakening |
|
|
180
|
+
| `grep` | pattern present/absent (e.g. the new code path is the default config) |
|
|
181
|
+
| `wiring` | the symbol is **called** in a file reachable by static imports from the default entry (catches "built but not wired in") |
|
|
182
|
+
| `scope` | every changed file matches the allowlist |
|
|
183
|
+
| `docs` | generator leaves no diff in the given paths |
|
|
184
|
+
| `pushed` | tree clean and HEAD == `<remote>/<branch>` (the criterion's `remote`/`branch`, else `defaults.remote`/`defaults.branch`, else `origin`/`main`) |
|
|
185
|
+
| `file_exists` | path exists (e.g. the bench rig is committed) |
|
|
186
|
+
|
|
187
|
+
Always on: forbidden-action scan of the diff (skip/only/todo, raised timeouts, net assertion loss, deleted tests, `--no-verify`, bypass env vars, lowered protected caps; `proofline.json` and `.proofline/` are left out, because the contract declares the deny-list and would match it), contract-weakening check vs the base revision (loosened thresholds, removed criteria, changed commands → BLOCKED), hash-chained evidence log integrity, claim reconciliation.
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
## How it decides
|
|
191
|
+
|
|
192
|
+
### Owners
|
|
193
|
+
|
|
194
|
+
Two or more builders can share one contract. A task may carry an `owner`, and `defaults.owners` may list the names (`contract validate` then refuses an owner outside the list). `next --owner <name>` (MCP: `task_next` with `owner`) hands out only that owner's tasks and unowned ones, never another owner's; the phase gate applies to that lane, and `depends_on` still waits on any owner's task. `status --owner <name>` and `brief <TASK> --owner <name>` take the same filter, and `brief` refuses another owner's task. Without `--owner`, nothing changes.
|
|
195
|
+
|
|
196
|
+
### Contract scaffold
|
|
197
|
+
|
|
198
|
+
`proofline contract scaffold --title ... --scope a,b [--kind feature|docs|release]` prints a task with the standard criteria and the default stop rules. It does not write the contract. A feature gets a red-then-green test and the suite; docs gets a grep; release gets one command. Scope and pushed are always there. `contract validate` refuses a task with no stop rules, a task with no scope criterion, a criterion whose file, path, test file or entry sits outside that scope, a double-quoted `sh -c` or `bash -c` command that contains `$`, and a red-then-green criterion with no test file. A grep whose pattern already matches on the base revision is a warning, and the warning names that revision. `brief` and `steer` still run when a loaded task has no stop rules.
|
|
199
|
+
|
|
200
|
+
`proofline contract merge <fragment.json> [--owners a=b,c=d]` appends the fragment's tasks and any owner rule that is not already there. A duplicate id is refused and the file is left untouched. Existing tasks and the bytes around them stay. The command prints the sha256 of the result. `proofline contract coverage <REQUIREMENTS.md> [--contracts a.json,b.json]` reads ids from lines of the form `- ID: text` and prints one line per id with the tasks that name it, or `MISSING`. A task that names an id the file does not list is `unknown`. The exit code is 1 when any id is missing or unknown.
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
### Git hooks
|
|
204
|
+
|
|
205
|
+
`init` installs `pre-commit` (forbidden actions and contract weakening vs HEAD) and `pre-push` (the active task must gate PASS). If the repository already has one of these hooks and it is not proofline's, `init` keeps it as `<hook>.proofline-prev` and says so. The installed hook runs it first, with the same arguments and stdin; if it fails, its exit code stands and the gate does not run. Running `init` again changes nothing. If both `<hook>` and `<hook>.proofline-prev` exist and neither is proofline's, `init` stops rather than lose one.
|
|
206
|
+
|
|
207
|
+
A push is gated on the commit being pushed. The hook reads that commit's local sha from stdin, checks the commit out in a temporary worktree, and gates it there. It does not gate the checked-out HEAD. A commit already on a remote default branch is left out. That head is read with `git ls-remote` of the push URL at send time, never from a local remote-tracking ref, for an existing branch and for a new branch. A commit on no remote is still gated. If the server cannot be read, every commit in the range is gated. The task is the `(TASK-ID)` suffix on the subject, or a `Proofline-Task` trailer. A commit that names no task is gated on the active task. The base is the most recent pin from `proofline next` for that task, otherwise the parent of the commit being pushed, and every refusal prints that base. A worktree with no evidence still gates. Evidence left in the worktree you push from cannot block a commit that passes at its own sha. An active task whose head is an ancestor of origin/main, or whose pushed criterion has a PASS record, has landed: `next`, `verify` and the hooks clear `.proofline/active` and say so, and a landed task is never gated. A push with no active task and no named task is refused, and the refusal prints `proofline next`. Before any task has a start record, a push that names no task passes and prints `proofline next`.
|
|
208
|
+
|
|
209
|
+
A contract change is refused unless an approval marker is present. `proofline init --approvals <dir>` (re-runnable) stores that directory in `.proofline/config.json`, which is never committed. The marker is a file in that directory named with the sha256 of the staged `proofline.json` bytes. The refusal prints the weakenings, the sha256 and the marker path. A marker named for any other bytes does not count, and what else the commit stages makes no difference. With no directory configured, every contract change is refused. There is no bypass.
|
|
210
|
+
|
|
211
|
+
`verify` and `gate` read the same marker for the current `proofline.json` bytes. When that marker is a regular file, the weakenings are an approved contract change: the report names the marker path, and the decision is not blocked. A missing marker, a marker for other bytes, or a symlink still blocks.
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
### Base revision
|
|
215
|
+
|
|
216
|
+
Every diff check (forbidden scan, `scope`, `test_red_green`, contract weakening) compares the base revision with the working tree. The base is taken from, first match wins:
|
|
217
|
+
|
|
218
|
+
1. `--base <rev>` on `verify` or `gate` (MCP: `base`);
|
|
219
|
+
2. the task's own `base` in the contract;
|
|
220
|
+
3. the most recent base pinned for the task (`proofline next` records it in the local `.proofline/` log, `proofline next --base <rev>` records a newer pin, and the brief prints the one that wins);
|
|
221
|
+
4. a `Proofline-Base: <sha>` trailer on the HEAD commit (optional; it must name an ancestor of HEAD, or the gate is BLOCKED);
|
|
222
|
+
5. the default: `defaults.base`, else `<defaults.remote>/<defaults.branch>` (`origin/main` when neither is set).
|
|
223
|
+
|
|
224
|
+
A repository whose remote is not called `origin` sets `defaults.remote` (for example `"github"`) and leaves `defaults.base` out; `pushed`, claim reconciliation and the default base then use that remote.
|
|
225
|
+
|
|
226
|
+
Every report says which one it used, and `brief <TASK> [--base <rev>]` says which base `verify` and `gate` will use and whether it would block. A fresh clone has no log, and after the push the default base is HEAD, so the diff is empty and every diff check would pass on nothing. In that case the gate is BLOCKED, never PASS, and the corrective line asks for `--base <rev>` (for one commit, its parent) or `proofline next`. The CI workflow passes the previous tip of the pushed branch as `--base`.
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
### Reusing results (verify, then gate)
|
|
230
|
+
|
|
231
|
+
`verify` and `gate` run every check each time. When a task's slowest check is the full suite, `verify`, `gate` and the pre-push hook on the same commit run it two or three times with nothing changed. A contract can opt in to reuse:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
"defaults": { "reuse_results": true, "reuse_max_age_h": 24 }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
- `reuse_results` (`true`/`false`, default `false`): with it off, or left out, every check runs every time, as before. Turning it on or off is not a contract weakening.
|
|
238
|
+
- Only `command` and `metric` results are reused. `test_red_green`, `scope`, `wiring`, `grep`, `docs`, `pushed`, the forbidden scan and the weakening check always run: they are cheap, or they depend on the base.
|
|
239
|
+
- Only a PASS is stored. A FAIL, a flaky result, a busy-machine block or an error runs again every time.
|
|
240
|
+
- A PASS is reused only on the same HEAD with a clean tree: `git status` shows nothing outside `.proofline/`, untracked files included. An uncommitted edit never reuses a result, and a check run on a dirty tree is not stored.
|
|
241
|
+
- The key is the criterion's verifier object (canonical JSON), the HEAD sha, `process.version` and the values of the env vars the command names (`$VAR`, `${VAR}`; `{VAR}` in a metric's `output_file`); for a metric also its baseline and the regression tolerance. Change any of them and the check runs again. Gitignored files, env vars the command does not name and tools outside the repo are not in the key: a check that depends on them needs `--fresh`.
|
|
242
|
+
- `reuse_max_age_h` (a positive number, default 24): an older PASS runs again.
|
|
243
|
+
- A metric PASS is reused only if the load per CPU it recorded is within `defaults.max_load_per_cpu`, when that is set. Its measured value is reused with it, so claims are checked against it.
|
|
244
|
+
- A fresh PASS is recorded in the evidence log (a `result` entry) and `.proofline/cache/<sha256 of key>.json` points at that entry with its hash, time, status, detail and evidence (exit code, test counts, duration). A result is reused only from that entry, and only while it is in an intact chain with the same hash; an edited or truncated log, or a cache file that names another hash, means the check runs again.
|
|
245
|
+
- Every reused criterion says `reused from <time> (evidence entry <n>)` in its report line, the gate report lists reused criteria under **Reused results** (not run again in that gate), and the gate's evidence entry carries the same lines.
|
|
246
|
+
- `--fresh` on `verify` and `gate` (MCP: `fresh: true`) ignores stored results and runs every check; a PASS it gets replaces the stored one.
|
|
247
|
+
|
|
248
|
+
Demo (claimed done with no evidence, FAIL, then real work, PASS): `npm run demo`.
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
## For agents
|
|
252
|
+
|
|
253
|
+
Read [AGENTS.md](AGENTS.md): the loop, the exact report line, what is never allowed and the stop rules. MCP config: [examples/mcp-config.json](examples/mcp-config.json). Tools: `contract_create`, `task_next`, `evidence_record`, `verify`, `gate`, `report`, `status`.
|
|
254
|
+
|
|
255
|
+
## Repository layout
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
proofline/
|
|
259
|
+
├── bin/proofline.mjs the command: init, contract, next, brief, verify, gate, report, status, evidence, hook, mcp
|
|
260
|
+
├── src/contract.mjs contract schema, validation, task graph, weakening detection
|
|
261
|
+
├── src/checks.mjs the verifiers
|
|
262
|
+
├── src/forbidden.mjs the forbidden-action scan
|
|
263
|
+
├── src/claims.mjs report-line parser and claim reconciliation
|
|
264
|
+
├── src/evidence.mjs the append-only, hash-chained evidence log (.proofline/evidence.jsonl)
|
|
265
|
+
├── src/engine.mjs verify, the corrective prompt, gate and its report, next and brief, phases
|
|
266
|
+
├── src/mcp.mjs the MCP server on stdio
|
|
267
|
+
├── src/index.mjs the API, init, hooks
|
|
268
|
+
├── examples/demo/ demo.mjs (FAIL then PASS, npm run demo) and catch.mjs (five catches, one pass)
|
|
269
|
+
├── examples/demo-contract/ an example contract with one task; the self-gate validates it
|
|
270
|
+
├── docs/ the source design: scope, requirements, architecture, lifecycle, evidence, roadmap
|
|
271
|
+
├── plans/ definition of done, risk register, pilot plan
|
|
272
|
+
└── proofline.json Proofline's own contract: the repository gates itself
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Documentation
|
|
276
|
+
|
|
277
|
+
[docs/00-index.md](docs/00-index.md) lists the design documents. [CONTRIBUTING.md](CONTRIBUTING.md) has the sign-off rule; [SECURITY.md](SECURITY.md) says how to report a vulnerability.
|
|
278
|
+
|
|
279
|
+
## Known limits
|
|
280
|
+
|
|
281
|
+
See `KNOWN_LIMITS` in `src/engine.mjs`; they are printed in every report. In short: same-machine agents can edit local state (tampering is detected, and CI re-verifies, but protect `proofline.json` with branch protection/CODEOWNERS); `--no-verify` can skip local hooks (CI catches it); static wiring/forbidden checks are heuristics; contracts are JSON only; with `reuse_results` on, a reused PASS is keyed on HEAD, node and the env vars the command names, not on gitignored files or tools outside the repo.
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
Apache-2.0 (see [LICENSE](LICENSE) and [NOTICE](NOTICE)). Contributions are accepted under the same licence with a Developer Certificate of Origin sign-off on each commit.
|