Skip to content

v0.58.0

Choose a tag to compare

@github-actions github-actions released this 19 Sep 15:59
· 132 commits to main since this release

[0.58.0] - 2026-09-20

Added

  • The DRI accepts the SPEC, and nothing irreversible lands without that acceptance.
    hm spec_machine approve [--exempt] stamps a content-hash-bound approval into the machine
    SPEC (every authored field hashed, tooling fields excluded); approval-status reports one of a
    nine-row state table. schema_version: 3 requires an irreversible_decisions list
    (IRR-NNN, five categories, source: spec|execute). spec is now a judgment-gated autopilot
    stage whose gate is derived from the SPEC, not the stage's claim; /hm:spec ends on
    "Approve this SPEC and end interview"; /hm:execute appends newly discovered irreversible
    decisions (invalidating the approval); wrapup asks the DRI before landing, and wrapup_land,
    worktree task-land and worktree finalize success refuse with hold: lines — finalize
    checks every worktree before merging any and finds SPECs from the branch diff. Pre-v3 SPECs
    pass with a notice. The stamp records the flow; it does not prove a human. The hold fails
    closed: a branch whose SPECs git cannot list holds, a task-land whose worktree is gone checks
    the branch through a throwaway checkout, and a checkout's own spec.dir cannot point the
    gate away from its SPECs (the base's dir is searched first; absolute or .. values fall
    back to specs/).

  • The proposal backlog gets a reader, and wrapup says so where you can see it.
    .claude/memory/pending-proposals.md had one writer (wrapup Step 5.3) and no reader — 27 open
    proposals, the oldest from 2026-05-17. hm proposals list|count [--open|--triaged] and
    hm proposals summary (open-only, {"open": N, "oldest": date|null}) read it; an absent file
    is an empty backlog, not an error. Wrapup's main loop now calls summary at the head of
    Steps 6 → 7.6 and prints one line at five or more open proposals — not in Step 5.3, which runs
    inside stage-delegate whose output never reaches the operator. A failure of the call prints
    one line and never halts (a pre-release plugin answers unknown module).

  • High-recurrence failures reach every retrieving stage regardless of vocabulary.
    hm memory_retrieve adds a labelled high-recurrence section: the top --count-floor N
    (default 3) [fail:*] entries by recurrence, with their own additive byte budget, so the
    lexical section is byte-identical with the floor on or off. Excerpts keep the newest dated
    blocks, so a later correction is what the reader sees. Measured: the topic "repair round broke
    something new while the suite stayed green" returned (no entries matched) before (297 B) and
    now surfaces the three most-recurring failures, including fix-introduced-defect-passes-all-gates
    (count:13) that it paraphrases (2649 B); worktree finalize stash merge grows 8661 → 11013 B.

  • /hm:execute loads failure bodies, through hm memory_retrieve, instead of a
    failures.md head-skim plus a slug rg — the one stage that writes code was the only one
    reading slugs without bodies.

  • project-knowledge skill — capture code-absent project facts in the shared wiki
    (SPEC-mission-context-loop S0).

    • The gap. Code-absent knowledge the DRI holds had no capture path: how an external
      system behaves, an ops quirk, a correction of something previously believed. Claude Code's
      default files "remember X" in machine-local auto-memory, which is invisible to git, Cursor,
      Codex and memory_retrieve.
    • Capture. When the DRI explicitly asks to remember a fact, or corrects a recorded one,
      the skill searches first and writes a [wiki:fact] entry via the existing hm memory_md upsert-wiki. It reuses only a [wiki:fact] slug, and checks a new slug with the Grep
      tool so it does not overwrite a fact the search missed. A correction replaces the entry and
      ends with Supersedes: <old claim> (first recorded <date>), carrying the original capture
      date forward.
    • What it refuses. It never records an inferred fact. It surfaces the CLI's stderr on
      failure instead of falling back to auto-memory. Personal preferences stay in auto-memory.
    • Always-loaded pointer. A pointer of at most 300 characters in CLAUDE.md, AGENTS.md
      and .cursor/rules/harness.mdc routes shared facts to the wiki even when the skill does not
      trigger. The Codex copy names the skill file, because Codex starts skills only on mention.
    • wrapup 5.1 gains a 5.1.0 search-before-write step: it reuses a matching non-fact
      slug and never reuses a [wiki:fact] slug, so a task summary cannot overwrite a DRI's
      fact.
    • Templates. The wiki templates document the fact category and the Supersedes: line,
      and /hm:help lists the skill.
    • Withdrawal (pre-registered). 28 days (UTC dates, inclusive) from the first v* release
      that ships it, scripts/measure_wiki_fact_window.py counts [wiki:fact] subjects: ≥5 → S1
      (subject files) becomes a candidate task; 0 → the skill and pointers are removed. It is
      anchored as this repo's wiki_fact_entries_28d outcome and refuses to measure before the
      window closes.
  • hm world assume add and content-fingerprint evidence locators. The assumption layer had
    no way in — only observe/resolve on existing records — so depends_on and assumption
    revisits had nothing to point at. add creates a record (refusing an existing id or
    conflict); --locator <path:A-B> on add/observe cites up to 40 lines of the checkout by
    whitespace-normalised sha256 (never a commit SHA — wrapup records before its commit and
    task-land squashes), refused at record time if the citation does not exist. hm world gap --json gains assumptions, stale_evidence, moved_evidence and needs_revalidation;
    hm world status is unchanged and never reads cited files. Wrapup 5.7's assumption question
    now lists stale assumptions first and offers "new — record an assumption" (four options at
    most), showing the exact add arguments and asking once more before writing; the
    intent-layer skill lists the verb. add/observe/resolve now hold a per-file
    RMW lock (observe/resolve wrote unlocked before). Limitation: a locator detects drift in
    cited code, not in claims about external tools (Claude Code, Codex, agy) — probe-backed
    checks are a follow-up.

Changed

  • review_telemetry wall_time_ms is optional (null = not measured, never 0). The
    review stage's own paragraph forbade interpolating it while the schema required it, so the first
    emit of every round failed; the doc and PRIVACY.md now state the same contract. /hm:plan
    states that stage_agent_ledger --barrier-index takes an integer (type=int), as /hm:execute
    already did, at every comprehension depth.

  • hm world gap --json gains a withdrawal block (intent-layer-ops): the intent layer's
    own kill criterion ("10 wrapups, no observed objective, no fired revisit → remove the layer")
    was an uncounted skeleton comment; it is now measured from git history (filled_at, the
    committer date of the oldest commit whose .claude/intent.yaml is filled in) and base-root
    stage-spans.jsonl hm:wrapup start events. A count that cannot be taken is null with a
    reason, never a disguised 0. status --json is unchanged — the block lives in gap only.
    Wrapup Step 5.7 prints one sentence when due is true, reusing the gap --json call the
    outcome-measure question already makes (zero new Bash calls).

  • Measured outcome evidence is now hash-referenced, not argv-copied: hm world outcome measure used to write the full command argv (300–700 chars for some dogfood commands) into
    every evidence row; it now writes auto: measure#<definition_hash[:12]> @ <sha> exit=0 cwd=<base|checkout>. The hash still pins which measure definition produced the number by
    equality; the argv is recoverable via git show <sha>:.claude/intent.yaml only when
    intent.yaml was committed and clean at that sha. Existing rows are untouched.

  • Sourced 10 of the 13 unsourced plan-stage step-sensitivity classes (src/harness_maker/step_sensitivity.py):
    a blind Codex classification against an external evidence bundle, reconciled with Claude's
    own class-supporting citations under explicit ADR-003 §5 rules, gives each upgraded row a
    falsifier and tracked excerpt instead of an inherited-from-a-neighbour guess. 3 rows stay
    unsourced where the two readings disagreed with no resolving citation. unsourced_step_share
    moves 47.1% (40/85) → 35.3% (30/85); objective SOURCE-PLAN-STEPS's ≤31.8% target was missed
    (a valid outcome under ADR-005 — the approved target of 20% is out of reach for a
    plan-stage-only task). Registry, MATRIX-native-redundancy.md rows/census and CLAUDE.md's
    count all stay in agreement; render output is unchanged.

Fixed

  • memory_retrieve reads where memory_md writes. Without --memory-dir the reader now
    resolves the base root with memory_md._memory_dir. Before, a stage whose cwd was a task
    worktree read the branch copy of wiki.md/failures.md, which never holds entries captured
    mid-task (they are written to the base and folded only at land). An explicit --memory-dir
    is unchanged, and the high-recurrence count floor reads the same resolved root — so
    /hm:execute's warm tier, which runs from the task worktree, now sees base-captured failures.
    tests/unit/test_memory_retrieve_root.py fails loudly if the default is lost.

Boundary tests (advisory)

Layer 1 of PLAN-test-fidelity-gap. Pure parser checks; advisory only.

Status: ✅ PASS

============================= test session starts ==============================
platform linux -- Python 3.12.14, pytest-9.0.3, pluggy-1.6.0
rootdir: /home/runner/work/harness-maker/harness-maker
configfile: pyproject.toml
plugins: respx-0.23.1, hypothesis-6.155.7, asyncio-1.3.0, anyio-4.13.0, xdist-3.8.0
asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collected 50 items

tests/integration/test_boundary_codex_toml.py ............               [ 24%]
tests/integration/test_boundary_cursor_mdc.py .......                    [ 38%]
tests/integration/test_boundary_harness_yaml.py ........                 [ 54%]
tests/integration/test_boundary_hooks_json.py .........                  [ 72%]
tests/integration/test_boundary_meta.py .......                          [ 86%]
tests/integration/test_boundary_settings_json.py .......                 [100%]

============================== 50 passed in 4.84s ==============================