Skip to content

Releases: renzrollon/interlock

v1.7.0

Choose a tag to compare

@renzrollon renzrollon released this 21 Oct 09:58

Added

  • A checkpoint advisor. After a clean artifact review, /interlock:spec spawns the read-only interlock:advisor agent once on a brief the CLI assembles (interlock advisor <change> --brief). The draft is validated and rendered by code (--check, exit 0 once the change resolves) to openspec/changes/<change>/advisor.md, and a consult that did not run is written down the same way. Nothing gates on the file and the advisor never writes decisions.md. interlock advisor --verdict --labels <dir> tallies the ten Spec Range eval cases' human labels against caps in interlock limits. The spec meter shows the advisor result, its questions, the consult spawn and a waiting on you line.
  • Compound notes. A review finding may carry an optional specGap: true; the domain and technical-lead reviewers set it for a requirement no spec stated. interlock note derive|add|match keep a table at docs/notes/compound.md, both adjudicators append to it (a failure is spoken as COMPOUND NOTES NOT WRITTEN, never an exit code), and the next spec's brief is shown the rows that name a file its tasks touch.
  • interlock evals triage --eval-dir <dir> joins grader types from the case definitions so the first judged graders are classified correctly.

Fixed

  • Two writers to docs/notes/compound.md no longer lose each other's rows. A ship review's adjudication and interlock note add on the same checkout each read the whole table and rewrote it, so a concurrent append was lost and an id could be handed out twice (12 concurrent note add processes landed 9 rows). Each append now reads, numbers and writes under an exclusive lock file, staging the table and renaming it into place. A lock still held past a short wait is spoken as COMPOUND NOTES NOT WRITTEN, never an exit code; a lock left by a writer that died is broken.
  • Eval triage no longer counts a grader the harness left out of the score. A trial grader written scored: false (a with-only indicator under --ablation with-without) is excluded, as the harness itself excludes it.
  • The CI eval jobs can run their cases. Both jobs now pass --trust-plugin, because without a terminal claude plugin eval refuses an untrusted directory before any case runs. They also grant the operator-gated tools their cases list (--allow-tools Bash for smoke, Bash Edit Write for the full job). Without the grant the harness withholds those tools, and no Spec Range case could pass check-ran or advisor-written. test/evals.test.mjs derives the grant from the cases and pins it.

Changed

  • The interlock advisor and interlock note handlers moved from bin/interlock to lib/advisor-cli.mjs (no behaviour change), and a test now fails any shipped file within 16 KiB of the plugin directory's 256 KiB per-file limit.

v1.6.3

Choose a tag to compare

@renzrollon renzrollon released this 19 Oct 07:58

Changed

  • A halt resume card no longer puts the band above the prompt. The band drew whenever a card of an open change was on disk, and a card stays there until its change is archived, so every session start in that repository showed it, each card as an absolute path, until the next Hide. The band now draws only for a fail or warn check, a preflight that could not run or be parsed, or an interrupted run spoken at that session start, and it never lists a card. /interlock-preflight still lists every card and /interlock-handoff still renders them; the preflight still records them in .claude/ship/preflight.json, and nothing marks, moves or deletes one.

v1.6.2

Choose a tag to compare

@renzrollon renzrollon released this 14 Oct 14:30

Fixed

  • A missing openspec CLI is named at the first command, with the install line. /interlock:spec, /interlock:bootstrap and /interlock:dispatch now run openspec --version first and, when it prints no version, stop before any exploration or writing. command not found is reported as a missing CLI with npm install -g @fission-ai/openspec@latest; any other error is reported as a CLI that is installed but does not run (usually Node.js older than 20.19.0), with its error and no install line. Dispatch no longer reads the missing CLI as an empty project and routes it to bootstrap. interlock doctor's no-CLI fix text spells that command (OPENSPEC_INSTALL in lib/doctor.mjs), and the doctor now warns when openspec is on PATH but its version probe does not run, instead of reporting ok. The session-start closer names /interlock:spec and /interlock:bootstrap alongside /interlock:ship only when an openspec or binaries check fails; a missing test profile or allowlist rule still holds back ship alone.

v1.6.1

Choose a tag to compare

@renzrollon renzrollon released this 14 Oct 05:44
  • chore: bump version to 1.6.1
  • fix(ship): recover relays and stop transport failures halting runs

v1.6.0

Choose a tag to compare

@renzrollon renzrollon released this 13 Oct 07:35

Added

  • A merge verdict and score, computed by the CLI (lib/merge-confidence.mjs). assessMergeConfidence takes the facts a review and a run already produce and returns MERGE, HUMAN or BLOCK, a score, and one entry per registered signal with its band (green, amber, red, unobserved), its flags, its evidence and its reason. The verdict is the worst band over the gating signals, and an unobserved signal ranks as amber. A flag lowers the score and never moves a band. The score sits inside the worst band's range, so a red never outscores an amber. Signals live in one frozen registry (SIGNALS, seventeen gating signals and the informational plan.fidelity) and flags in one published list (FLAGS); combineSignals is the formula and names no signal. The module is pure.

    • Every gating signal has a producer. conformance.living and spec.coverage were registered with no producer and read unobserved; both now read the facts their rules name (see below), so a clean, reviewed, verified change can read MERGE. A signal reads unobserved only because a fact about the change is missing, and its reason says which.
  • interlock merge-confidence [change] --changed <files...>. It reads the change's artifacts, its decision ledger, its delta-spec scenario count, drift and HEAD from the repository, and --review, --verify, --handoff, --pipeline and --coverage from files the caller names. --base <ref> adds the diff shape from git diff --numstat, and every path that diff reports joins the changed paths, so a path left out of --changed still reaches the risk classifier; a base that is HEAD or ahead of it reads the diff shape unobserved. It is offline and never calls gh, glab or the network. It exits 1 on BLOCK, or on anything but MERGE under --require-merge. HUMAN exits 0. An input it cannot read or parse also exits 1, with the reason on stderr and nothing on stdout, as gate does. The pipeline file and the coverage file are defined here and produced by later changes (E3, E8).

  • MERGE_BANDS in lib/limits.mjs and MERGE_ALLOWED_CLASSES in lib/risk.mjs. MERGE_BANDS holds the band ceilings and every threshold the verdict applies. It is a separate export beside LANE_CAPS, interlock limits prints one row per entry and per ceiling, --json publishes it as mergeBands, and the cap-authority sweep covers it. MERGE_ALLOWED_CLASSES holds the risk classes the merge verdict reads green. It sits beside CONTINUITY_ALLOWED_CLASSES and holds the same classes today, so the two policies can diverge without one moving the other.

  • The verify judge verdict carries typecheck: { ran, exitCode, passed }, or null when no type-check result was reported, in every context. A passing type check used to leave no trace, so "passed" could not be told from "never reported". No key is renamed and no halting set moves: a failed type check at the final gate still prints its non-blocking banner and does not halt.

  • Coverage figures the CLI computes (E8). A pure lcov reader gives a project total and changed-line coverage on canonical repository paths, whether the report wrote a file absolute, relative or with a leading ./; a record outside the repository is left out and counted.

    • interlock verify baseline [--report <lcov>] [--baseline <file>] records { schema, sha, ref, totalPct, generatedAt } for the current commit, and refuses (exit 1, previous baseline untouched) when the report has no project total. It writes to the working root's testing directory, never the state home.
    • interlock verify coverage --base <ref> [--report <lcov>] [--baseline <file>] [--compute-base] prints the coverage input merge-confidence --coverage reads and no band: configured, headTotalPct, baseTotalPct, baseSha, baseIsAncestor, diffPct, changedLines. A baseline is usable only when its commit is an ancestor of the head, which is read from git and never from an age. A profile with no coverage command prints configured: false; a declared report that is missing or not lcov exits 1 with nothing on stdout. --compute-base measures the merge base in a temporary worktree that is removed whether the command passed or not.
    • verify exec is unchanged. The brief's verify exec coverage --write-baseline was not built, because verify exec is a pass-through.
  • ## Always Human Review in REVIEW.md, and policy read from the base (E4). The section is parsed with the ## Do Not Report grammar and enforced only by the merge verdict; it never moves a finding or a gate.

    • --base <ref> on interlock review and interlock review-policy reads both path lists from the REVIEW.md committed at the ref and the prose from the working tree, and says when the two copies differ. A ref that names no commit exits 1 before a review is decided. Without it both commands read the working tree as before.
    • merge-confidence --base makes policy.humanPaths observable: amber, needing a person, for a changed path under a listed path, for any edit to REVIEW.md itself, and for a committed section that could not be read in full; green otherwise; unobserved when no base was given.
    • A threshold-shaped list item is now rejected in both path sections, as a threshold-shaped key already was.
    • startSha on the run manifest. A ship run records the commit it started on and reads the lists from it. A manifest written before this change has none and reads the working tree.
    • Two banners: POLICY DIFFERS FROM BASE when the working copy differs from the start commit's, and REVIEW BASE NOT PINNED when no start commit could be recorded. Both reach the close summary.
  • A domain review dimension (E5). A seventh rubric, skills/review-code/dimensions/domain.md, whose reviewer is handed the living requirements in force for the capabilities a change touches, quoted as data. Capabilities are selected from reliable spec-to-file edges and from the capabilities the change's own delta specs modify.

    • LIMITS.maxDomainSpecChars bounds the text at a requirement boundary and names what was left out. interlock limits prints it as domain spec text budget (chars), and --json carries it.
    • interlock domain-context [change] --changed <files...> [--base <ref>] [--json] serves the same block to the review skill and exits 0 on a completed report; a --base that names no commit exits 1.
    • A finding may carry requirement: openspec/specs/<cap>/spec.md#<heading>. A domain blocker with no citation is a problem; the CLI checks the citation against the spec at the base and reports a failed check without dropping the finding. "Blocker" is read as the gate reads it, so BLOCKER and blocker are reported too.
    • domain-skipped flag on review.ran when the dimension was skipped for want of a graph. It leaves the band to the rest of the record.
    • Two banners: DOMAIN REVIEW SKIPPED and DOMAIN REVIEW PARTIAL, each with its reason.
  • interlock spec-audit [--json] (E7). A non-gating report in the drift family of what each living capability states: requirements, scenarios without a GIVEN, failure and edge-case scenarios, bound and prohibition lines, numeric literals in requirement prose for a person to classify, and with a graph the broken file links and the coverable source no spec names. A measure it cannot take is stated with its reason and never printed as zero. It always exits 0. The suite pins the structural invariants of this repository's committed specs and never their counts.

  • docs/15-merge-confidence.md. The baseline recipe with the default-branch job, --compute-base and its limits, the always-human-review section and why lists are read from the base, and how to read a spec audit.

  • conformance.living: the handoff confirms the living requirements a change touches (E6). The signal compares the handoff's requirementsChecked and requirementsUnconfirmed with the number of entries in a living list the command counts itself; a requirementsRequired in the input is ignored. Green when every listed requirement was checked and none is unconfirmed, amber (needing more evidence) when any is unconfirmed, a count is missing or the counts differ, unobserved when the list has entries and no handoff was given.

    • interlock conformance --living [--base <ref>] lists, after the scenarios, the living requirements in force for the capabilities the change touches (L1..Ln, spec path, line and heading, never the body), selected, read from the base and bounded exactly as the domain review's text is, so the two cannot disagree. It exits 0 in every case; a --base naming no commit is the reason the list was not built. Without --living the output is unchanged.
    • The ship handoff asks about them under --conformance, and HANDOFF_RESULT_SCHEMA carries the optional counts requirementsChecked and requirementsUnconfirmed, recorded on the manifest and printed as a living requirements: row.
    • requirements-omitted flag when the size bound left requirements out. It lowers the score and never moves a band.
  • spec.coverage: is the source a change adds named in a spec? (E6) Amber, needing a fix, for a coverable source file the diff adds that no living spec and none of the change's delta specs names, and for a kept file whose only naming spec the diff changed or deleted. An edited file nothing ever named is the legacy-unspecced flag, with the repository's named-over-total figure. It reads spec text and git, never the graph, through specMentionedPaths in lib/spec-text.mjs (the rule the graph's extractor now shares) and lib/spec-coverage.mjs, so it reads the same in CI as on a developer's machine. Unobserved with no --base.

  • One gather for the command and the close (lib/merge-evidence.mjs). gatherMergeEvidence assembles the assessment's input for `inter...

Read more

v1.5.1

Choose a tag to compare

@renzrollon renzrollon released this 11 Oct 10:42

Changed

  • docs/08 is about Interlock's own harness. It was a survey of OpenClaw, Hermes Agent and DeepSeek Harness. It now maps the parts of the harness, what holds on each host, and what is left out on purpose; the other projects are one comparison table. The page no longer says lane isolation is closed on the Workflow host, which still halts with LANE BASE MISMATCH after the first fold.
  • The README says what the one network call sends. It said a push happens when INTERLOCK_NTFY_TOPIC is set. It now names what goes out (the close summary's first line, the change name and the run id; the change name at the spec checkpoint; the title and body of a notify you type), which server receives it, that the topic is the only thing in the request that identifies you, and that no request is made without one. It also says the plugin's ntfy topic option reaches the CLI through the session's environment, and that interlock doctor reads a model credential by name and never by value. The plugin directory holds a plugin that reads a value from the environment and posts to a server until a reviewer has read this.

Fixed

  • The session-start hook returns the forward itself. The classic.SessionStart hook in hooks/mod.mjs held the forward in a variable, awaited the preflight read, and returned the variable. The plugin directory's reader could not tell that return from a first message or a permission decision of the hook's own, and held the version. The hook now returns the forward call, continued once to read the preflight's report and hand back the value the chain resolved with. Nothing a session sees changes: the read still finishes before the event resolves, and a chain that rejects still skips it. test/mod/session-start.test.ts and test/spine/mod-pins.test.mjs cover it unchanged.

v1.5.0

Choose a tag to compare

@renzrollon renzrollon released this 11 Oct 08:11
  • chore: bump version to 1.5.0
  • feat(readme): improve and add interlock gif
  • Merge pull request #15 from renzrollon/fix/fit-ship-and-graph-to-compiled-repos
  • fix(ship,graph): bound rejected reports and say when a graph is partial
  • fix(ship,graph): fit ship runs and the graph to compiled-language repos

v1.4.2

Choose a tag to compare

@renzrollon renzrollon released this 09 Oct 16:24
  • chore: bump version to 1.4.2
  • fix(meter): follow wrapped ship relays and keep the live pane's tail in view

v1.4.1

Choose a tag to compare

@renzrollon renzrollon released this 09 Oct 08:40
  • chore: bump version to 1.4.1
  • fix(ship): correct the replay-cache key and the 1.4.0 resume note
  • feat(meter): draw the ship pane as a timeline with a time gutter

v1.4.0

Choose a tag to compare

@renzrollon renzrollon released this 09 Oct 03:13
  • chore: bump version to 1.4.0
  • feat(ship): show lanes as tasks and name every agent in the meter