Skip to content

Releases

v1.24.0

Choose a tag to compare

@github-actions github-actions released this 10 Nov 09:44
a2bfe68

The spec track (Track S, Experimental): three advisory skills before a ticket exists. A spec is
read by fresh reviewers and gets a counted verdict before any code. None of the three closes a gate,
calls a lifecycle skill, or creates a ticket or an epic; the one link to the lifecycle is a
Spec: <path> line a user may add to an epic or a ticket, which design reads (S4).

  • Added (skills): /mango:brainstorm (idea → brief: one open question per turn, reviewer lenses
    only after a readiness gate the user passes), /mango:spec (brief or notes → spec: one section at a
    time, the user chooses every decision from at most three unranked options, the spec is compared with
    its brief and never edited to match it), /mango:spec-review (one reviewer per lens in parallel; a
    lens with no result line is retried once, then not run, never zero findings; resolve as CONTEST,
    ESCALATE, PROPOSE FIX or DECLINE; the review record goes into the document itself, on the user's yes).
  • Added (agent): spec-reviewer — tools: Read, Grep, Glob, omitClaudeMd: true, Sonnet.
    validate.py pins both host keys. The host behaviours it relies on were checked on Claude Code
    2.1.321 against the docs and a reproducible throwaway plugin (docs/spikes/spec-track-host.md);
    omitClaudeMd needs 2.1.271 or later and does not stop a subdirectory CLAUDE.md from attaching.
  • Fixed before release (manual trial): a real /mango:spec-review round on a sample spec found
    that three of four reviewers could not locate principles/spec-review.md and one read a copy from
    another checkout. The dispatch now passes the principle's absolute path, and spec-reviewer reads
    only that path and never searches for a copy (agent 353 → 375 words, ledger raised). The round
    itself caught the planted defect — a per-tenant purge over a shared directory — through all four
    lenses.
  • Added (principle, templates): principles/spec-review.md holds the lenses, severities, resolve
    rules, not run and convergence once for all three skills; templates/spec.md, templates/brief.md.
  • Added (counted lines): BRIEF, SPEC, DIVERGENCE, SPEC REVIEW, parsed by check_lines.py
    under the new --phase brief | spec | spec-review. A spec phase holds a document only to its own
    lines, never to a lifecycle line. Ten envelope tests.
  • Changed (design, S4): a ticket or epic with a Spec: <path> line gets one design evidence
    record in Phase 2 — path, sha256, the spec's last SPEC REVIEW: line, provenance: reported —
    shown at Gate 2 with any open Blocker or not run lens. It informs Gate 2 and never closes it. No
    Spec: line, no change. analysis is not touched and spec creates no ticket file: the plan's
    wider S4 was cut so the spec skills stay independent of the lifecycle. design 3090 → 3164 words
    (ledger raised): the record's fields are the behaviour, and no existing text covers them.
  • Changed (doctor): check 11, informational — a host below Claude Code 2.1.271 is a ⚠, since it
    does not support the omitClaudeMd key spec-reviewer relies on.
  • Added (config, optional): specs_dir (default docs/specs/), spec_review.max_rounds (default 3).
  • Decided, not added: no skill sets disable-model-invocation, like every other mango skill; a
    narrow description limits unwanted auto-invocation, and the flag is reconsidered only if measured.
    The spec track's grammars ship before Phase 2's GATE line rather than after it: no GATE change is
    in flight, and the new grammars only add registry entries.
  • Size: the full-run words rise 34,360 → 34,447 (+87): one row in PRINCIPLES.md's companion index,
    which validate.py requires for every companion (ledger raised 2653 → 2666, +13), and the design
    spec link (+74). maturity.md 923 → 979
    for the new Experimental entry. The three skills total 1,756 words; nothing new loads in a full run.
  • Smoke guard: six new fixtures (spec-review-not-run, spec-review-contest-evidence,
    spec-zero-decision, spec-divergence, brainstorm-readiness-gate, design-spec-evidence); their
    30 dispatch-free self-tests pass. Each fixture passed 3× fresh (--no-cache, sequential; CLI 2.1.322, model
    cli-default). That is a smoke guard, not a regression suite. The whole-guard run then failed one
    wording token on multi-clause-want (the outcome was right: two rows, the single-row ✅ rejected, but
    it said "two separate behaviours", not "clauses"); the token is widened to
    clause|behaviou?rs?|halves, over wording only, with a self-test proving both ways. The whole guard
    then passed fresh: 14/14 fixtures, 148/148 assertions, at 3b16bec.

v1.23.2

Choose a tag to compare

@github-actions github-actions released this 08 Nov 14:20
b7daa00

The RULE SECTIONS shape no longer reads as a list split by /. The red 1.23.0 e2e run wrote
its sections with / between them, copying ✅ / N/A (reason), …, where / meant "or";
check_lines counted one section against 3 applicable and stopped the run at Gate 1 (fail-closed,
not false-green).

  • Changed (skill text — analysis, quick; template ticket.md): the shape now shows two
    entries, §<id> (<source>) ✅ <what it constrains>, §<id> (<source>) N/A because <reason>, …, and
    states Separate entries with , only. The N/A because <reason> form the prose already required
    is now in the shape itself. check_lines's canonical form, validate.py's rule-first check and
    docs/reference/lines.md follow it.
  • Not changed: the checker. / is still not a separator, so a line written that way still fails
    the count — a new envelope test pins both the , pass and the / fail.
  • Size: analysis 3621 → 3620 words, ticket.md 4273 → 4272 (a redundant clause cut in each).
  • Smoke guard: multi-clause-want (mapped to analysis) green, 57/57, fresh run. That fixture
    does not emit a RULE SECTIONS line, so it shows the edit broke nothing it covers, not that agents
    now write the line correctly; the golden e2e is that check. quick has no mapped fixture.

v1.23.1

Choose a tag to compare

@github-actions github-actions released this 08 Nov 13:53
bfaa6d3

Three defects the 1.23.0 golden e2e found (3 runs on 1865fb1: 2 green, 1 red; the red run is
explained under Not changed below). Scripts only: no skill, agent, principle or template text changed.

  • DISCLOSURE no longer claims a review ran. run_contract.py disclosure-seed wrote ON — the rule-book-grounded reviewer ran and ON — the ticket-blind challenger ran before any phase had
    run. A run that stopped at Gate 1 carried both claims; the agent had to correct them by hand. Line
    one now reads ON — enabled for this run, and states that whether the seat ran is the review phase's
    record.
  • proof.py reads a unittest discover command. The unittest preset put -v before discover,
    which unittest rejects (unrecognized arguments: -s tests -t .), so a design naming
    python3 -m unittest discover -s tests -t . was could-not-run. -v now goes after discover.
    The run disclosed it and measured with --command; nothing read clean.
  • ticket_stats.py reads an embedded working doc. Under work_doc_mode: embed the header sits
    below the separator, after the ticket's own ## sections; both green runs printed lane=unknown scope=unknown (exit 3). The script now reads below the separator, through check_lines's own
    reading of it, and also reads the backticked header shape (`TIER: full`).
  • Not changed: the red run. It wrote RULE SECTIONS with / between the sections;
    check_lines counted one section against 3 applicable and the gate stayed closed, so the run
    stopped at Gate 1 with a correct account. The shape in skills/analysis reads ✅ / N/A (reason), …,
    where / means "or"; making that unambiguous is a skill edit and needs its smoke guard, so it is
    left to its own change.

v1.23.0

Choose a tag to compare

@github-actions github-actions released this 08 Nov 12:42
1865fb1

Aggregate rows from real tickets. A script prints one row of counts from a working doc, in the
project, so cost and challenger figures can leave a real project without its text.

  • scripts/ticket_stats.py <workdoc> [--row]. Read-only. Prints COST: lane=… scope=… dispatches=… dispatch_tokens=… unmeasured=… session_tokens=… minutes=… gates=… not_met=… mango=… date=…, or with --row the same fields as a docs/COST.md table row. Every field has a fixed shape
    (a number, a fixed word, a version, a date) and is checked against it before printing, so no ticket
    key, path, name or text from the doc can reach the output; no message quotes the doc.
  • Read from the doc, never estimated. Lane from TIER and a DISCLOSURE list under ## Phase 5;
    scope from SCOPE and the Reviewed at file list; dispatches, tokens and unmeasured cells from the
    Cost ledger; gates from Gate <n> status: cleared; raised not met from the first CHALLENGER VERDICT; the date from Last updated:. Session tokens, minutes, the mango version and how many
    not met items were real are not in the doc: they come from --session-tokens, --minutes,
    --mango, --real, or print unknown. Exit 3 when a field the doc should carry was not found.
  • docs/COST.md. The table's columns are now the script's fields. validate.py refuses a header
    that differs from them, a row with another number of cells, and any cell outside its field's shape.
    The USD, cost_tier and model columns are gone: they are outside the approved aggregate list. The
    Field report issue template now asks for the printed row only.
  • Runtime words unchanged. No skill, agent, principle or template text changed.

v1.22.0

Choose a tag to compare

@github-actions github-actions released this 08 Nov 12:20
8eb3468

Measured proof. For a working doc that declares PROTOCOL: v0.2, review no longer takes "fails
without the change" on the author's word: a script runs the proving test twice and counts the result.

  • PROTOCOL: header field. check_lines reads - **PROTOCOL:** v0.2 (or `PROTOCOL: v0.2`)
    from the doc's header — before its first ## section, so a quoted mention never declares — and names
    it on the doc line. A doc without the field is legacy: its report is unchanged, and PROOF is
    never required of it. An unknown version is NOT-CHECKABLE (exit 3). templates/ticket.md now declares
    v0.2, so new tickets opt in.
  • proof.py and the PROOF line. Run 1: the base source with the test files the branch added or
    changed (verification.proof.test_paths). Run 2: the head; a test that does not pass is re-run once
    there. Each test is classified by the kind of failure, never by how many fail: proven,
    regression-guard, green@base, env-fault@base (an import of something the diff does not add, a
    connection error), not green@head (failed, errored, skipped), flaky, could-not-run. Both runs
    happen in throwaway worktrees, removed on every path out.
  • Registered grammar and gates. PROOF: <n> proving test(s) | <p> proven | <r> regression-guard | <z> green@base | <e> env-fault@base | <g> not green@head | <f> flaky | <c> could-not-run, identity
    n = p+r+z+e+g+f+c. Required at review of a v0.2 doc. Blocks Gate 4 when z > 0, g > 0 or
    n = 0. PROOF waived (verification.proof.enabled: false) waives the requirement and is disclosed.
  • design declares regression guards on a **Regression guards:** line (tests green at base by
    design: refactor, docs, config); Gate 2 approves it. review step 6b runs proof.py and pastes
    its output; exit 2 is not clean, exit 3 is disclosed.
  • Presets. unittest only (FAIL and ERROR are red; results read from -v output). pytest and every
    other runner are could-not-run until a recorded sample proves how to read their output: no pytest
    was available to record one.
  • Config. verification.proof.enabled (default true), timeout_s (default 300), test_paths.
  • Maturity. Measured proof is Experimental (principles/maturity.md).
  • Words. design, review and templates/ticket.md stay within their ledger: the additions are
    paid for by sentences that restated a reason. design 3,100 → 3,090, ticket.md 4,278 → 4,273,
    principles/maturity.md 929 → 923, review unchanged at 3,504.

v1.21.1

Choose a tag to compare

@github-actions github-actions released this 08 Nov 08:10
0f64d7c

autorun leaves no stray files. It now writes one file outside the change and the working doc —
the run contract — and that file has an end of life.

  • .mango/ ignores itself. autorun writes .mango/.gitignore containing *, so the folder never
    shows in git status. It never edits the project's .gitignore or .git/info/exclude.
  • The spec is scratch. spec-<KEY>.json is written to a mktemp -d directory outside the repo and
    removed after run_contract.py write.
  • DISCLOSURE is never a file. It goes at the top of the PR body and under the working doc's
    ## Phase 5 heading.
  • The contract's end of life. reconcile.py run <contract> --phase post-merge prints DONE and
    the rm command when every declared condition holds. It never deletes the file itself.
  • The golden e2e judges it. assert_run.py is red when .mango/ holds anything but its .gitignore
    and the run contract, when the folder shows in git status, or when DISCLOSURE is missing from the PR
    body, the working doc and the final output.
  • Skill text. The retired forced-case control and the --tree paragraph in autorun lose their
    history sentences (kept here and in RATIONALE.md), so the skill stays under its word budget.
  • Upgrading. Folders left by earlier runs are not touched. After each merged ticket's post-merge
    check, remove its files: rm .mango/run-contract-<KEY>.txt .mango/disclosure-<KEY>.* .mango/spec-<KEY>.json.

v1.21.0

Choose a tag to compare

@github-actions github-actions released this 06 Nov 12:01
0cb7391

TREE-COMPARISON is generated, and waits for the merge. Golden e2e findings #1 and #5 (#20).

  • The problem, measured. In the 12 RUN CONTRACTs kept from golden e2e runs of one ticket, the floor
    condition's check meant one of two things:
    • 4 compared the base to the branch. That is the condition's intended meaning (envelope test T5),
      but before the merge it is BROKEN by construction, so every correct autorun closed on q > 0.
    • 8 compared the remote branch to the local one. That holds at close, but it repeats
      LOCAL-HEAD-PUSHED, and it HOLDS on a correction pushed after the merge, the incident the condition
      exists for. That is a false green.
    • Every one of the 12 passed the old contract checks, which looked only for git diff --quiet and
      refused a grep or an ancestry predicate. The path list took 4 shapes and the base header 5.
  • A — the floor checks are generated.
    • run_contract.py write renders TREE-COMPARISON as
      git diff --quiet <remote>/<base-branch> <remote>/<branch> -- UNBOUND ${CHANGED_PATHS}. It renders
      LOCAL-HEAD-PUSHED as a rev-parse --verify comparison of refs/heads/<branch> with
      refs/remotes/<remote>/<branch>.
    • A spec or contract carrying any other spelling is refused (exit 2), never silently corrected.
    • base is now <branch>@<sha>, its sha derived by git rev-parse. A supplied sha the repo
      contradicts is refused. branch and remote must be plain ref names, because they reach a shell.
    • CHANGED_PATHS is bound at Gate 2 to the approved change list, plain path tokens only.
    • The grep and ancestry refusals are kept.
  • B — pending-merge and --phase post-merge.
    • The RECONCILE line gains a seventh axis: … | <c> could-not-run | <w> pending-merge.
    • At close, TREE-COMPARISON is not run and is counted pending-merge: never holding, and never
      reported as verified.
    • --phase post-merge runs every condition. A BROKEN there prints READ THIS FIRST; a ref that did
      not resolve (a remote branch deleted at merge and pruned) is named as unverified.
    • An unknown phase is refused. t0 is unchanged.
    • The DISCLOSURE seed adds a POST-MERGE line carrying the exact command (git fetch <remote>, then
      reconcile.py run <contract> --phase post-merge). autorun puts it in the PR body and does not
      run it itself.
  • The golden e2e judge now requires the contract to validate at close and re-runs RECONCILE at
    post-merge after its simulated squash. Every condition must hold, with none pending-merge.
  • Incompatible with older contracts. A ≤ 1.20.0 contract carries an authored check and a free-form
    base, so 1.21.0 refuses it. This matters only for a run that spans a plugin upgrade, and the
    contract's plugin-version names the cause.
  • Tests and enforcement:
    • validate.py enforces each item above.
    • Envelope tests:
      • render and refuse;
      • every observed free-form base and both observed tree forms;
      • pending-merge at close, and post-merge HOLDING after a squash;
      • post-merge BROKEN on a correction pushed after merge, with the refused form shown HOLDING on that
        same correction;
      • t0 unchanged, and the disclosure line.

v1.20.0

Choose a tag to compare

@github-actions github-actions released this 06 Nov 05:16
fe3466e

Two gate contracts the golden e2e found unstable. Both were found across repeated unattended runs of
the same ticket. Each run was correct, but the artifact a gate rests on came out in a different shape.

  • CHALLENGER VERDICT:, a new counted line emitted by review.
    • Grammar: CHALLENGER VERDICT: <n> requirement(s) rebuilt | <m> met | <x> not met | <u> can't tell | <g> not-met covered by a recorded exclusion.
    • Why: review's "clean" rests on the challenger finding every requirement met. The challenger's brief
      fixes three counts and no grammar, so across five runs the same verdict appeared as
      7 requirements reconstructed — 7 met, …, 7 met | 0 not met | …, 7 met / 0 not met / … and
      7/7 … met, ….
    • Review now counts the rows of the challenger's numbered table into this line. check_lines.py
      checks n = m + x + u, g ≤ x and n > 0, and the gate requires m + g = n. A requirement not
      met, or can't tell, outside a recorded exclusion fails the gate with exit 2.
    • When it is required: at review, in the full lane only. It is not required when the doc records
      CHALLENGER: OFF (--no-challenger), so a recorded waiver stays the only way to omit it.
    • The challenger agent is unchanged; only review's transcription is new.
  • From finalise on, the tree under review is the Reviewed at SHA.
    • finalise, and the autorun and solve gate callers, previously passed --tree $(git rev-parse HEAD). So a lesson committed after review made every evidence block recorded at the reviewed
      commit "from ANOTHER tree". One run re-ran its suite just to satisfy the checker; another ran the
      checker before committing.
    • Finalise's step 1, the stale-review guard, already proves that only bookkeeping lies between the
      reviewed commit and HEAD. That commit is therefore the tree under review, and finalise now passes
      it.
    • Still one tree, checked strictly. Through review, --tree stays $(git rev-parse HEAD).
  • validate.py enforces both. Nine envelope tests cover the new line both ways: pass, sum mismatch,
    not met, can't tell, excluded not-met, zero requirements, missing at review, the waiver, and not
    required before review or in the lite lane.

v1.19.2

Choose a tag to compare

@github-actions github-actions released this 05 Nov 14:21
f1efeac

Fix: pre-change evidence no longer stops every honest run at review. Since 1.14.0, check_lines.py --tree HEAD refused every empirical-output block not run on the tree under review — including the
three blocks the lifecycle requires to run before the change exists: analysis's BASELINE, design's
blast-radius trace, and execute's fails-without-the-change run. An unattended autorun therefore could
not close Gate 4 on a correctly written working doc. Found by the first live run of a golden end-to-end
test on a generated sample project; neither the envelope suite nor the per-skill smoke guard exercises
two phases together, so neither could see it.

  • Ran at <sha> (pre-change) — a block that by design ran before the change declares that role.
    check_lines.py accepts it only when --base <sha> is given and the SHA is that base (the
    commit the branch was cut from; for the fails-without run, that code plus only the new proving
    tests). It is counted on a new trailing field of the evidence : line, <p> pre-change on the base,
    and never as evidence on the tree under review.
  • Nothing else is excused. A (pre-change) block naming any other SHA, or checked without
    --base, is refused exactly like stale evidence (exit 2). An undeclared block naming the base is
    still stale. (pre-change) does not rescue provenance-unknown (exit 3). Post-change runs still
    carry the reviewed tree's SHA.
  • review, autorun, solve and finalise pass --base $(git merge-base HEAD <default branch>);
    analysis, design, execute and the ticket template name the role. validate.py enforces both;
    six envelope tests prove the role both ways.

v1.19.1

Choose a tag to compare

@github-actions github-actions released this 04 Nov 15:00
4bbf889

Patch: adoption fixes from a review of 1.19.0 — first-run stumbles, a one-screen README, and
adoption and cost pages.
No gate, agent brief or doctor check changed; one skill (challenge)
gains a STOP for a case it left undefined. Released as one version rather than several, so the
Releases page does not read as constant churn.

  • The hand-fill path now passes doctor. Both READMEs and harness.minimal.json told a user to
    copy the minimal config, replace REPLACE_ME and create tickets_dir — but doctor check 3 reports
    ❌ when the file at rulebook_path does not exist, so a user who did every step still could not start
    solve. They now also say to create the rule book file (an empty file is enough: doctor reports it
    as a ⚠ stub, not a ❌) or to run /mango:init. doctor is unchanged — it still fails on a missing rule
    book. validate_harness_minimal now requires the instruction in both READMEs and the config comment.
  • /mango:challenge leads the root README. The zero-config entry point sat after init / doctor
    / solve, inside a paragraph. A new Try it in 2 minutes section right after Why mango? shows the
    three commands; the old paragraph is removed so it is introduced once. validate_challenge_standalone
    checks it appears in the first 40 lines and exactly once.
  • /mango:challenge <KEY> with no ticket read configured now stops. Outside local mode the skill
    read a KEY only through tracker.read_mcp; a project with only tracker.cli fell into an undefined
    case. It now stops with "no ticket read is configured; pass the ticket as a file path", and never
    reads through tracker.cli, the write channel. Two contract tokens added for challenge.
  • The pin example names the current version. The README showed #v1.18.2, a tag without
    /mango:challenge, so copying it verbatim installed an older mango. It now shows the current tag, and
    validate_release_workflow fails unless every pin example and the version badge match plugin.json —
    the badge was the release-checklist item most often left stale, and it is now enforced too.
  • Root README is 120 lines, from 261. The autorun detail, the SECTIONS / ROWS and
    surfaces proven examples, the full prerequisites list and the codify / promote paragraphs
    moved to the plugin README (new Prerequisites and What the artifacts look like sections) or were
    already there; the cross-skill-regression example moved to docs/MATURITY.md. The challenger table
    stays, under Try it in 2 minutes, since that is what the command prints. Nothing was dropped: each
    removed sentence was checked against the README, plugin README, docs/ and CONTRIBUTING.md.
    validate_root_readme_docs fails a root README over 120 lines.
  • harness.example.json is grouped REQUIRED / RECOMMENDED / ADVANCED. Section markers
    ("//== REQUIRED (doctor fails without these) ==" and so on) head each group; no key was added,
    removed or renamed, and the parsed content is identical apart from the three markers.
    validate_harness_minimal checks the markers are in order and that the REQUIRED group is exactly
    doctor check 2's list.
  • docs/ADOPTION.md — a four-week rollout, one action and one number per week, starting with
    /mango:challenge on already-merged PRs; when not to use mango; an FAQ.
  • docs/COST.md — the measurement protocol and an empty table. It holds no figures: none have
    been measured. Token counts come from the working doc's dispatch-only Cost ledger plus the host's
    session figure, since mango does not measure the main loop. A one-line COST: summary format is
    proposed there and is not implemented. validate_root_readme_docs fails a data row that carries
    no run date, and an empty table that does not say it is empty. Both pages join the jargon scan.
  • docs/STABILITY.md — semver over the skill / config / counted-line API, a one-minor deprecation
    notice before any removal, and batching related changes into one release. main stays the release
    channel. Linked from the README and CONTRIBUTING.md.
  • Issue templates — bug_report.yml, and field_report.yml whose fields match the docs/COST.md
    columns, so external runs can be added to that table. Blank issues are off.