Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
},
"metadata": {
"description": "Torque Loop — a single-plugin marketplace.",
"version": "0.2.0"
"version": "0.3.0"
},
"plugins": [
{
"name": "ratchet",
"source": "./",
"description": "Torque Loop: turns ambiguous work into shipped artifacts through target locking, adversarial validation, patching, and state serialization, plus a bounded /ratchet:evolve loop.",
"version": "0.2.0",
"version": "0.3.0",
"author": {
"name": "Danny Gillespie"
},
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ratchet",
"displayName": "Torque Loop",
"version": "0.2.0",
"version": "0.3.0",
"description": "Torque Loop turns ambiguous work into shipped artifacts through target locking, adversarial validation, patching, and state serialization — plus a bounded /ratchet:evolve loop. A stateful advancement engine, not a prompt library. Not affiliated with Anthropic.",
"author": {
"name": "Danny Gillespie"
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "torque-loop",
"version": "0.2.0",
"version": "0.3.0",
"description": "Torque Loop evolves ambiguous work into shipped, tested, serialized artifacts through evidence-gated loops.",
"author": {
"name": "Danny Gillespie",
Expand Down
57 changes: 56 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,74 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.3.0] - 2026-07-03 — Seam Gate

Where 0.2 made the loop **require proof**, 0.3 asks whether the proof is about the
thing you are actually shipping. A proxy evaluation can produce the right-looking
number and still point at the wrong decision, so a production `KEEP` now needs
evidence from the exact ship seam — and remediated defects can finally be cleared,
waived, or superseded through the CLI instead of blocking confidence forever.

**No proof → no keep** (0.2). **Wrong proof → no ship** (0.3).

### Added

- **Defect lifecycle** — `ratchet defect resolve | reopen | waive | supersede`,
plus `list` and `get`. `resolve` requires `--evidence`; `waive` requires
`--owner` and `--reason`; `supersede` links the replacement with `--by`. Each
transition is logged per-defect and mirrored into the QA ledger. This is the
mutation 0.2 lacked: a defect could be born but never cleared, so remediated
work stayed confidence-blocking forever.
- **Seam-fidelity metadata on evolution events** — a verification can now record
`evidenceType`, `method`, `independentFromBuilderMethod`, `testedSeam`,
`shipSeam`, `seamMatch`, and `proxyWarning`.
- **Seam gate** — a production-code (`mode: code`) `KEEP` is rejected unless the
evidence seam is an exact match for the ship seam, or a named human waiver is
supplied. Verification that repeats the builder's own method is rejected as not
independent.
- **`ratchet retract <id>`** — retract an artifact whose claim became false or
obsolete (`--reason`, `--superseded-by`). Provenance is preserved
(`keptForProvenance`), and a retracted artifact's holes stop draining confidence.
- **`ratchet git status-refs`** — base-qualified git status: every ahead/behind
count names the ref it was measured against — never a bare "ahead of main".
- **`ratchet doctor cold-start`** — scans for stale steering that would start the
next session in the wrong world. Generic ratchet-state checks always run;
project operator surfaces (goal files, decision sheets) are an opt-in adapter
via `.ratchet/cold-start.json`. No workspace path is hardcoded, and a
declared-but-unimplemented check warns rather than silently passing. See
`templates/cold-start.example.json`.
- **`REVERTED_AND_LEARNED`** — a first-class successful evolve outcome for a
mutation reverted after verification that still left a reusable lesson. Evolve
status counts it distinctly: corrected knowledge, no bad code kept.
- Codex install metadata: `.codex-plugin/plugin.json` plus a repo-local
`.agents/plugins/marketplace.json`, so the plugin can be registered and installed by
Codex CLI and surfaced in the Codex app.

### Changed

- Confidence scoring now treats `waived` and `superseded` defects as terminal
(like `resolved` / `closed`) — a change proven necessary by a failing test
before it was made. `resolved` handling was already correct and was left
untouched.
- The terminal-defect predicate is centralized in `scoring.isDefectOpen` and
consumed by the scorer, the state summary, and the QA ledger, so a cleared
defect can never read as open on one surface while draining on another.
- The `ratchet` CLI gained a real `--key value` flag parser for subcommands that
carry values (the router previously treated every `--flag` as boolean).
- Package metadata, `ratchet doctor`, and plugin-shape tests now cover both Claude Code
and Codex manifests.
- npm packaging includes the Codex marketplace file explicitly as
`.agents/plugins/marketplace.json`, without packaging the whole `.agents` tree.
- Repo snapshots now surface `.agents` and `.codex-plugin` alongside the existing
plugin-critical dot directories.
- Bumped package, both plugin manifests, and the marketplace manifest to `0.3.0`.

### Fixed

- Remediated defects could never be cleared through the CLI, so they blocked
confidence permanently. They can now be resolved, waived, or superseded.
- A production `KEEP` could be justified by a proxy evaluation that measured a
different seam than the one it ships on. The seam gate now blocks that.

## [0.2.0] - 2026-07-03 — Proof Gate

Expand Down Expand Up @@ -103,6 +157,7 @@ Initial public release.
- Single-plugin marketplace manifest so the repo installs directly as a Claude Code plugin.
- Zero-dependency smoke test suites for the state engine and the evolution helpers.

[Unreleased]: https://github.com/TheLucidTech/torque-loop/compare/v0.2.0...HEAD
[Unreleased]: https://github.com/TheLucidTech/torque-loop/compare/v0.3.0...HEAD
[0.3.0]: https://github.com/TheLucidTech/torque-loop/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/TheLucidTech/torque-loop/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/TheLucidTech/torque-loop/releases/tag/v0.1.0
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,41 @@ refused at write time — the loop cannot record progress it did not prove.

---

## Why seam fidelity matters

Torque Loop does not only ask *"was this tested?"* It asks *"was this tested at the seam
you are about to ship?"* A proxy evaluation can produce the right-looking number and still
point at the wrong decision.

In one real session, a proposed replay-only recall-router gate looked like a **+21.4%**
improvement in a fixture-shortlist eval (`apply_strategy` over a force-included lexical
shortlist). A live-seam eval against the actual ship path (`rerank_candidates` over cosine
recall, with no forced gold) showed the opposite: the gate was a **regression**. The flag
was reverted, **no code shipped**, and the router stayed as-is.

That is a successful loop — the outcome Torque Loop now records as `REVERTED_AND_LEARNED`.

> **No proof → no keep.** (v0.2 — the proof gate)
> **Wrong proof → no ship.** (v0.3 — the seam gate)

The seam gate is why a production-code `KEEP` in `/ratchet:evolve` must declare an exact
ship-seam match (or a named human waiver), and why verification that merely repeats the
builder's own search method is rejected as not independent.

### v0.3 state & quality verbs (`ratchet` CLI)

| Command | Purpose |
| --- | --- |
| `ratchet defect resolve <id> --evidence "<proof>"` | Clear a defect — proof required. |
| `ratchet defect waive <id> --owner <name> --reason "<why>"` | Accept the risk; stop the confidence drain. |
| `ratchet defect supersede <id> --by <artifact-id>` | Replace a defect with newer work. |
| `ratchet defect reopen <id> --reason "<why>"` | A resolved defect regressed. |
| `ratchet retract <id> --reason "<why>" [--superseded-by <id>]` | Retract a false/obsolete artifact (provenance kept). |
| `ratchet git status-refs` | Ahead/behind vs every base ref — each one named. |
| `ratchet doctor cold-start` | Scan for stale steering (opt-in surfaces via `.ratchet/cold-start.json`). |

---

## How it works

```
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@torque-loop/plugin",
"version": "0.2.0",
"version": "0.3.0",
"description": "Torque Loop — a Claude Code and Codex plugin that evolves ambiguity into shipped, tested, serialized artifacts through evidence-gated loops.",
"license": "MIT",
"author": "Danny Gillespie",
Expand Down
82 changes: 81 additions & 1 deletion src/artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,88 @@ function addDefect(cwd, item, { alsoLedger = true } = {}) {
status: record.status,
foundAt: now,
}).item;
// Link the state defect to its ledger mirror so lifecycle transitions can
// keep both surfaces honest instead of letting the ledger silently drift.
record.ledgerId = ledgerRecord.id;
state.saveState(cwd, s);
}
return { state: record, ledger: ledgerRecord };
}

module.exports = { addArtifact, addDefect };
// Move a defect through its lifecycle: open/patched/reopened → resolved | waived
// | superseded, or resolved → reopened. This is the mutation the CLI lacked in
// 0.2: a defect could be born but never cleared, so remediated work stayed
// confidence-blocking forever. The scorer already honors terminal statuses
// (scoring.isDefectOpen); this is what finally lets a defect *reach* one.
function transitionDefect(cwd, id, toStatus, meta = {}) {
if (!schemas.DEFECT_STATUSES.includes(toStatus)) {
throw new Error(`unknown defect status "${toStatus}". valid: ${schemas.DEFECT_STATUSES.join(', ')}`);
}
const s = state.loadState(cwd);
const d = (s.defects || []).find((x) => x.id === id);
if (!d) throw new Error(`no defect with id "${id}"`);
const now = schemas.nowIso();
const from = d.status || 'open';

d.status = toStatus;
d.log = Array.isArray(d.log) ? d.log : [];
d.log.push({ at: now, from, to: toStatus, note: meta.note || '' });

// Stamp the fields each transition owns; clear stale ones on reopen.
if (toStatus === 'resolved') {
d.resolvedAt = now;
if (meta.evidence) d.evidence = meta.evidence;
}
if (toStatus === 'reopened') {
d.resolvedAt = null;
d.reopenReason = meta.reason || '';
}
if (toStatus === 'waived') {
d.waivedBy = meta.owner || '';
d.waiveReason = meta.reason || '';
}
if (toStatus === 'superseded') {
d.supersededBy = meta.by || '';
}

s.dirty = true;
s.history.push({ id: state.makeId('hist'), at: now, event: `defect.${toStatus}`, note: `${id}: ${from} → ${toStatus}` });
state.saveState(cwd, s);

// Keep the QA ledger mirror in step. Best-effort: a defect added before the
// link existed has no mirror to sync, and a ledger hiccup must never strand a
// state transition that already succeeded.
if (d.ledgerId) {
try {
require('./ledger').upsert(cwd, 'defects', { id: d.ledgerId, status: toStatus });
} catch (_e) {
/* ledger sync is best-effort */
}
}
return d;
}

// Retract an artifact whose claim turned out false or obsolete. Provenance is
// preserved (keptForProvenance) — the record stays in history, but its status
// flips to `retracted` so it stops steering cold sessions and its holes stop
// draining confidence. This is the move the T2.3 re-scope doc needed when its
// central premise ("no endpoint exists") was disproven by the live seam.
function retractArtifact(cwd, id, { reason = '', supersededBy = '' } = {}) {
const s = state.loadState(cwd);
const a = (s.artifacts || []).find((x) => x.id === id);
if (!a) throw new Error(`no artifact with id "${id}"`);
const now = schemas.nowIso();
a.status = 'retracted';
a.retracted = { at: now, reason, supersededBy, keptForProvenance: true };
s.dirty = true;
s.history.push({
id: state.makeId('hist'),
at: now,
event: 'artifact.retracted',
note: `${id}: ${reason}${supersededBy ? ` → superseded by ${supersededBy}` : ''}`,
});
state.saveState(cwd, s);
return a;
}

module.exports = { addArtifact, addDefect, transitionDefect, retractArtifact };
Loading
Loading