Skip to content

Latest commit

 

History

History
326 lines (205 loc) · 107 KB

File metadata and controls

326 lines (205 loc) · 107 KB

Changelog

All notable changes to this project are documented here. The format follows Keep a Changelog 1.1.0 and the versioning policy described in the project README.

0.17.0 – 2026-07-23

Added

  • /orchestrate now emits a continuous, out-of-band per-issue milestone heartbeat over a multi-hour run (#48, ADR-0006). Between #49's terminal landed/opened-PR marker there was no within-ticket signal: an issue churning in fix round 3 for two hours was indistinguishable from steady progress, and a parked/failed issue left no durable marker at all — and the only in-flight view was the in-session progress tree, the exact place peeking has repeatedly killed the background run. Now a mechanical-tier reporter sub-agent posts a durable, machine-readable milestone comment on the issue at each lifecycle boundary the engine reaches — orchestrate: started #<n>, run <runId>, implementation green, verification cleared, fix round <k> (only when one runs), and parked #<n> (<reason>) when the issue dies — extending #49's single-source-of-truth grammar (format_started_marker / format_implementation_green_marker / format_verification_cleared_marker / format_fix_round_marker / format_parked_marker and the widened Marker verb domain + parse_landed_marker in scripts/orchestrate.py). The reporter is the second authorized outward writer, but strictly weaker than integrate: comment-only, never close/push/merge (it carries the same shared prohibition the workers do). started is posted only for an issue the preflight decided to dispatch — never for one skipped as already-landed / already-open / stale; the heartbeat is emitted in both merge and PR modes; reporting is best-effort (a failed or absent reporter never blocks, parks, or fails an issue); and each reporter dispatch is awaited so an issue's comments post in lifecycle order (which #50's status board reads back). A milestone verb carries an issue number, never a SHA, so it never parses as a landed marker — #49's four preflight verdicts are unchanged and no false already-landed skip occurs. skills/orchestrate/orchestrate.workflow.js gains the reporter agent, REPORTER_SCHEMA, the best-effort report wrapper, the milestone templates, and the wave-loop / buildAndVerify boundary dispatches; skills/orchestrate/SKILL.md documents the reporter lifecycle step, the milestone vocabulary, the two-authorized-writers operating-contract bullet, and out-of-band progress watching (the issue comment timeline / GitHub notifications, never the TUI); ADR-0006 records the decision and amends ADR-0005 §3 / ADR-0001 §7 by reference. tests/test_orchestrate*.py cover every milestone verb's round-trip, malformed-comment rejection, the no-milestone-parses-as-landed non-regression, the extended SKILL↔parser consistency binding, and the reporter engine structure, red-first. The parked reason is now sanitised at format time in both mirrored sources (sanitize_parked_reason in scripts/orchestrate.py, sanitizeReason in the engine): collapsed to one line, reduced to an allowlist — which drops : so a reason can never reconstruct an orchestrate: prefix and smuggle a second marker that would parse back as a landed verb (a false already-landed skip), drops () so it cannot break the single-line grammar #50 reads, and drops quotes/backticks/$ so it cannot inject into the reporter's gh command — and truncated to a short cap so no unbounded verifier detail is disclosed durably on a public issue. Every per-issue park now emits a durable parked heartbeat, not only the ones inside buildAndVerify: the prerequisite-cascade park (a blocked dependent) and the loud landed-marker-stale park both post one, so a maintainer watching the timeline never sees a prerequisite park followed by silence about the issues it cascades to; the token-budget park (dispatching a reporter after the budget floor is breached would contradict it) and the run-level integration-review / hotfix parks (no per-issue number to post on) stay silent by design.
  • /orchestrate is now interrupt-safe across a dead cross-session resume (#49, ADR-0005). A run killed mid-flight and blindly relaunched used to re-run the whole plan from scratch — Workflow resumeFromRunId is same-session-only, so a cross-session relaunch gets zero cache hits — re-implementing already-landed issues on fresh branches and landing redundant commits. Three mechanisms close this. (1) Durable landed-markers: at integrate time the integrate step posts a machine-readable comment on the issue — canonical orchestrate: landed <sha> on <branch>, run <runId> in merge mode, orchestrate: opened PR #<pr>, run <runId> in PR mode — with one source of truth (format_landed_marker / parse_landed_marker and the Marker dataclass in scripts/orchestrate.py), designed to extend to #48's milestone vocabulary. (2) Close-at-integrate (Decision D1): a merge-mode issue is now closed by the integrate step at integrate time — the moment it has both cleared independent verification and landed — not by the orchestrator at finalize; PR mode leaves the issue open. This is the one narrow exception to the sub-agents' ban on outward writes (the integrate step alone may post the marker and close), forced by the orchestrator being blocked on the single Workflow call mid-run and the engine having no I/O of its own. (3) Preflight idempotence guard: before dispatching an implementer, a cheap mechanical preflight sub-agent gathers each issue's durable state (gh issue view --comments, git merge-base --is-ancestor, any orchestrate PR's state), and a pure, unit-tested decision function (preflightDecision in lib/orchestrate/engine-helpers.mjs, mirrored byte-identically inline in skills/orchestrate/orchestrate.workflow.js) maps the facts to one of four verdicts — already-landed (skip, no implementer, marked landed so dependents proceed), landed-marker-stale (park loudly for a human, never rebuild from zero), already-open (benign PR-mode skip), or dispatch. So even a naive relaunch scoped to the same issues re-implements nothing that landed. skills/orchestrate/SKILL.md gains a Restarting a stopped run section stating Workflow resume is same-session-only and prescribing the cross-session restart (re-plan the remainder, launch fresh, lean on the guard — never blind-resume), corrects the old "resumable via its runId" advice, documents the preflight lifecycle step and the durable-marker + close-at-integrate behaviour, and carries the narrow-exception operating-contract bullet. scripts/orchestrate.py's report gains the already-landed / already-open (rendered as "already complete") and landed-marker-stale (rendered as a blocker) statuses. ADR-0005 records the decision and amends ADR-0001 §5/§7 by reference; the mandatory final integration review handles a cross-issue defect at run level and never reopens an already-closed issue. tests/test_orchestrate.py, tests/test_orchestrate_skill.py, and tests/test_orchestrate_workflow.py cover the marker round-trip and rejection, the four preflight verdicts (through node), the integrate marker+close structure gated to a real issue, the restart protocol, the SKILL↔parser consistency binding, and ADR-0005's existence and cross-reference.
  • /orchestrate status — a deterministic per-issue run board you can watch from outside the run (#50). The durable milestone and landed comments #48/#49 post are the only out-of-band progress signal, but reading them meant scrolling each issue's timeline by hand. A new status subcommand sits beside plan/redgreen/report in scripts/orchestrate.py as a pure stdin→stdout renderer — no network I/O in the Python — that folds those comments into one board, a row per issue in one of four states: queued, working (with the current phase), done (with the landed SHA), or parked (with the reason). All marker reading goes through the single-source-of-truth parse_landed_marker, so the board and the preflight guard never diverge on what a comment means. The board is scoped to a single run — the latest by default, --run <id> to pin an earlier one — so a restarted run's fresh markers don't blur with an older run's. skills/orchestrate/SKILL.md documents the gh issue list … | status one-liner and its watch variant (both driven with --state all so a landed-and-closed issue still renders a done row) and the first-page comment cap the reader honours. tests/test_orchestrate_status.py and tests/test_orchestrate_skill.py cover the four states, run scoping and the latest-run default, the queued fallback, and the SKILL↔renderer consistency binding, red-first.

Fixed

  • /orchestrate plan now reads dependency edges from an issue's Agent Brief comment, not only its body (#51). The comment above HARD_EDGE_KEYWORDS already promised the planner reads triage's inline **Depends on:** #N labels, but load_issues fed only entry["body"] to parse_dependencies — and triage posts the Agent Brief as a comment, so a hard dependency written only there was invisible and a coupled set collapsed into one unsafe wave (the #50→#48 reproduction planned [[48, 50]] instead of [[48], [50]]). load_issues now runs the same parse_dependencies discipline over the body and every Agent Brief comment — selected by the same AGENT_BRIEF_HEADING_RE heading anchor _has_agent_brief uses, so only genuine briefs qualify — and unions the results (_union_signals), the body's edge provenance winning on a shared number. Non-brief comments are never scanned: triage notes, milestone markers, and grammar-quoting discussion comments are full of other issues' numbers, and scanning them would fabricate edges (the same false-positive class #47 closed). Soft phrases, self-references, and (Related: #N) asides inside a brief follow the existing clause-boundary discipline unchanged — no hard edge — and the code comment above HARD_EDGE_KEYWORDS is corrected to name both sources. No new keywords and no brief-specific grammar: one shared parser, two sources, a union of results. _has_agent_brief now derives from the new _agent_brief_texts helper, giving one authoritative definition of "an Agent Brief comment." tests/test_orchestrate.py covers the #50/#48 reproduction planning as [[48], [50]], the non-brief-comment exclusion, the soft-phrase / self-reference / (Related: #N) cases inside a brief, the body∪brief union with body-wins provenance, a bare-string brief comment, and the unresolved-label warning reaching brief text — red-first.
  • /orchestrate status no longer lets a quoted marker poison another issue's board row (#54). #48's milestone grammar deliberately carries the issue number inside each marker (orchestrate: started #47, run <runId>) so attribution is verifiable, but #50's status consumer never checked it: load_status_universe attributed every orchestrate: marker in an issue's comments to that issue, so any comment that merely quotes the grammar — most sharply #48's own Agent Brief, which fences the templates in code — rendered a bogus row (a live #48 working started scoped to the placeholder run <runId>, demonstrated by the integration review that found this). parse_landed_marker is intentionally prose-tolerant, which was safe only because its prior consumer, preflight, has a second factor (landed SHAs must be ancestors of the default tip); status has none. A new _marker_attributable guard in the consumer/attribution layer restores that second factor without touching the single-source-of-truth parser: a milestone marker whose embedded number differs from the hosting issue is another issue's marker quoted here and is dropped, and a run_id still wearing its <placeholder> angle brackets (_PLACEHOLDER_RUN_ID_RE) is an unsubstituted template example — never a run a reporter posted — so it is dropped too, which also keeps a phantom <runId> run from becoming the default scope in _resolve_run and stops a quoted opened PR #12, run <runId> (number-less, so unreachable by number attribution) from marking its host done through the any-run terminal→done fallback. tests/test_orchestrate_status.py covers the #48-brief quoted payload, number attribution under a fully-substituted run id, and the placeholder terminal case, red-first; all existing status tests stay green.
  • /orchestrate's integrate step now structurally verifies a merge-mode landing reached disk, instead of trusting the agent not to strand it (#53). The #48 × #50 hotfix added a prompt-level guard forbidding the integrator from forcing a landing with git update-ref / git branch -f / git push . or by flipping core.bare, but that was advisory instruction to a mechanical-tier agent, with no positive post-condition check that the land actually reached disk and no defence when the repo was already corrupt at integrate time — a ref-only advance still looked "landed" while the working tree stayed at the pre-run commit, so the gates and the mandatory final re-run silently tested stale code. Now the integrator reports the raw post-land facts in INTEGRATE_SCHEMAbareRepo (git rev-parse --is-bare-repository), defaultCheckedOut, headSha (git rev-parse HEAD on the default after the fast-forward), featureSha (the feature-branch tip), and worktreeClean (git status --porcelain empty) — and a pure, unit-tested decision helper (landStrandedBlocker in lib/orchestrate/engine-helpers.mjs, mirrored byte-identically inline in skills/orchestrate/orchestrate.workflow.js) decides whether the land is sound: a bare repo or a default not checked out in the integrating worktree is refused before any sha comparison, and a missing sha, a HEAD that did not advance to the feature tip (a ref-only advance), or a dirty tree is a failed land. The engine — not the agent's own integrated flag — parks the issue with a specific blocker whenever those facts do not prove the tree advanced to the feature tip (merge mode only; PR mode lands nothing on the default), so a run can never record "landed" over stranded code. The merge-mode integrate prompt now instructs the integrator to gather and report those facts, and skills/orchestrate/SKILL.md documents the engine-side check. tests/test_orchestrate_workflow.py covers the schema fields, the merge-gated engine call and its park, the prompt requirement, and landStrandedBlocker's verdicts through node — red-first; the existing drift guard binds the two mirrored copies.
  • /orchestrate status no longer regresses a landed-and-closed issue to queued on a restarted run (#48 × #50 integration). The board defaults its scope to the newest run, but a cross-session restart (#49) re-runs the whole plan, so an issue that landed-and-closed in an earlier run carries only that run's markers. Scoping to the newest run rendered every such issue queued — silently defeating #50's own --state all amendment (which exists precisely so the board can show a done row) and wrongly answering the board's headline "was it done?". build_status in scripts/orchestrate.py now falls back, when an issue has no marker in the scoped run, to its latest terminal done marker from any run (landed / opened-pr, named by the new STATUS_TERMINAL_VERBS); a non-terminal earlier-run marker still does not carry over, so an issue merely started in an old run and untouched in this one stays queued. tests/test_orchestrate_status.py covers the restart universe red-first.
  • /orchestrate's integrate step is now barred from corrupting the repository to force a landing (#48 × #50 integration). A run fast-forwarded the default branch while it was checked out in the primary worktree and set core.bare = true to bypass git's checked-out-branch refusal, leaving the repo (bare), the default's working tree stranded at its pre-run commit (the landed code never reached disk), and no landed markers posted. The merge-mode integrate prompt in skills/orchestrate/orchestrate.workflow.js (mirrored in skills/orchestrate/SKILL.md) now requires the git merge --ff-only be run from the one worktree that actually holds the default branch — so ref and working tree advance together — and explicitly forbids setting core.bare, reconfiguring the repository, or force-advancing the default with git update-ref / git branch -f / git push ., parking a blocker instead when the default is not checked out where the agent runs. tests/test_orchestrate_workflow.py binds the guard structurally, red-first.
  • The doctor/init test fixtures no longer leak the git environment and corrupt the real repository under the pre-commit hook (#52). Git hooks export GIT_DIR (and, for index-touching hooks, GIT_INDEX_FILE / GIT_WORK_TREE) into everything they run, and those outrank a fixture's -C <tmpdir> targeting — so when the pre-commit tests hook launched pytest, the git_project fixture's own git init / add / commit ran against the developer's real .git, corrupting its index and (via a stray git init honouring the exported GIT_DIR) flipping core.bare = true; this was the upstream trigger of the integrate-time corruption fixed above (#48 × #50), and it had already forced --no-verify commits twice in one orchestrate run. A new tests/conftest.py autouse fixture now monkeypatch.delenvs GIT_DIR, GIT_INDEX_FILE, GIT_WORK_TREE, GIT_OBJECT_DIRECTORY, and GIT_COMMON_DIR from the process environment for every test, so every git subprocess beneath pytest — the fixtures' own and the ones scripts/doctor.py / scripts/init.py spawn when driven against temp projects — inherits a clean environment; the fixtures keep their -C <dir> targeting for ergonomics. A decoy-repo regression test in tests/test_doctor.py locks the isolation in: it seeds a throwaway repo, snapshots its .git/config and index bytes, exports the leak at the decoy exactly as the hook would, drives both a fixture-backed test and a doctor.main invocation (its own git) in a child pytest, and asserts the decoy is byte-for-byte unchanged — red before the scrub existed, green after. As defence-in-depth the tests hook entry in .pre-commit-config.yaml now scrubs the same variables with env -u … before pytest starts, so even a future test that bypasses conftest.py stays isolated. pre-commit run --all-files now completes with the working repo's index and config intact — no --no-verify needed.

0.16.1 – 2026-07-21

Changed

  • README.md drops the ### Repository layout section (the ASCII directory tree and its prose walkthrough). Both merely restated what ls shows directly — pure derivable duplication that only risked drifting out of sync as the tree grew — so the whole section is removed rather than maintained. The ## Development section now flows straight from the five-command gate into ### Authoring rules; no other section referenced the layout. The walkthrough's one non-derivable fact — the total skill count, which tests/test_commit_push_release_docs.py binds to reality — is preserved in the What you get intro, now stated as eight skills.

0.16.0 – 2026-07-21

Added

  • All eight skills now carry an argument-hint frontmatter field. Each skill's SKILL.md gains an argument-hint value describing the arguments the skill accepts, so a slash-command UI can show the expected arguments inline — the same convention commands/help.md already uses (argument-hint: [skill-name]). The values are grounded in each skill's own documented Arguments section: coder and init accept only the universal help gate ([help]); coding-standard [--update] [--dry-run] [help]; commit and push ["message"] [--yes] [help]; doctor [--yes] [help]; release [minor|major|X.Y.Z] [--no-build] [--yes] [help]; and orchestrate the full dial set ([scope] [--level=XS|S|M|L|XL] [--merge|--pr] [--plan|--dry-run] [--max-fix-rounds=N] [--max-lenses=N] [--yes] [help]). No skill behaviour changes; the field is metadata only. The audit and the full gate stay green.

Changed

  • README.md, CONTRIBUTING.md, and .gitignore reconciled to the Kntnt baseline via /doctor. The doctor run flagged drift against the project's own templates and baseline, resolved here without changing any substantive content. README.md is restructured to the canonical audience-layered shape — License/Latest-release badges under the title, a ## Description section with ### Key features / ### The problem / ### How this project helps, ## Requirements restored before ## Installation, the existing usage/architecture prose wrapped under ## Usage, a ## Questions, bugs, and feature requests section pointing to Discussions and Issues, the former collapsed Contributing split into ## Development (carrying the five-command gate) and ## How you can contribute, and a closing ## Changelog section keeping the domain-specific SemVer policy as a ### Versioning subsection — with all existing tables, code fences, and manual-page links preserved. CONTRIBUTING.md gains the ## Behaviour section from the template. .gitignore gains the missing baseline entries (*.py[cod], .venv/, *.egg-info/, dist/, build/, and an explicit .claude/settings.local.json).

0.15.0 – 2026-07-21

Added

  • /orchestrate verifier lenses now propose a fix direction (ADR-0004, decision 2). When a lens confirms a real finding it also proposes a remedy direction, so the fix agent starts from a diagnosis and a direction rather than re-deriving one — shortening each serial fix round, which is the wall-clock cost of the fix↔verify loop. The suggestion rides a new optional suggestedFix field on VERDICT_SCHEMA's finding object in skills/orchestrate/orchestrate.workflow.js; it is rendered separately and marked advisory in the fix prompt, alongside the finding's own title/detail that already reach it. Three guardrails bind the mechanism: (1) the verify prompt tells the lens to judge the finding real first, on its own merits, before proposing any fix, so the ease of a fix never softens (or inflates) the finding — preserving adversarial neutrality; (2) the fix prompt states the finding is authoritative and the suggestion advisory — verify it before following, pick a better fix if clearer, and bind the tests to the acceptance criteria, never to the suggestion; (3) the targeted re-verify (reverifyFindings) is unchanged and stays keyed to the finding, never the suggested solution, so a fixer who chose a better fix is not penalized (suggestedFix is deliberately not rendered there). tests/test_orchestrate_workflow.py covers the schema field's optionality, the judge-first verify wording, the advisory fix rendering, and the re-verify exclusion, red-first.

Changed

  • /orchestrate's --level ladder is re-pitched so verification rigor saturates at M (ADR-0004, decision 1). Field experience: verifiers found many defects → many serial fix iterations → long wall-clock, and the default M was rarely enough, so nearly every run was dialed up to L. Because issues run serially but the verifier panel runs in parallel (3 lenses ≈ the wall-clock of 1) and the fix-round cap is a ceiling not a schedule (a clean issue runs 0 rounds regardless), the wall-clock-cheapest way to park fewer issues is to catch everything in the one parallel panel and allow a second fix round — paying tokens, which are explicitly not the optimization target here. scripts/orchestrate.py's LEVEL_RANK changes from {XS:0, S:0, M:0, L:1, XL:2} to {XS:0, S:0, M:2, L:2, XL:2} (RIGOR_TIERS/LENSES_BY_RANK unchanged), so M/L/XL all sit at the top tier — 3 focused lenses, 2 fix rounds — while XS/S stay the lean fast lane (1 broad lens, 1 fix round). Rank 1 (2 lenses) is no longer any level's baseline; risk escalation is now only observable on an XS/S issue, since M+ already tops out. The illustrative model/effort ladder is re-pitched too — judgement climbs Opus·low → Opus·medium → Fable·medium → Fable·high → Fable·high (Fable as the judge from M, a deep-reasoning Opus·xhigh implementer at L, Fable-all-the-way at XL), the judge deliberately topping out at Fable·high not xhigh (Anthropic's Fable 5 guidance makes high the default and xhigh ≈ 2× the tokens for marginal review gain). The inviolable rigor floor, the escalate-only risk model, and the --max-lenses/--max-fix-rounds/--pr overrides are unchanged. skills/orchestrate/SKILL.md (both ladder tables, the honestly-rewritten cost model — default M now N × 7 + 3, the XS/S fast lane N × 4 + 3, per issue 5–9 vs 3–5 — and every per-level rigor mention), docs/man/orchestrate.md (the ambition-dial prose), and the engine's DEFAULT_LENSES/lensesFor comments are reconciled; ADR-0004 records the decision and cross-references ADR-0001 §4/§5 and ADR-0002 §4. tests/test_orchestrate.py and tests/test_orchestrate_skill.py are updated to the saturated ladder and two-tier cost model.

0.14.1 – 2026-07-20

Fixed

  • /orchestrate's planner no longer manufactures a false-positive Blocked by edge (and cycle) from a #N inside a None. (Related: #N) aside (#47). A /orchestrate --yes run failed at plan time with dependency cycle among issues [34, 36] even though both issues declared Blocked by: None — the planner's ## Blocked by branch ran ISSUE_REF_RE.findall over the whole section body and hard-edged every #N, including the sibling references in the parenthetical (Related: …) prose note that followed None. on the same line, so #34 → {35, 36} and #36 → {34, 35} produced a spurious #34↔#36 mutual block. scripts/orchestrate.py now peels non-directional asides out of the section before reading edges: a new NONDIRECTIONAL_ASIDE_RE matches a parenthetical opened by a non-directional cue ((Related: …), (Relates to …), (See …), (See also …)) — the (Related: colon form the existing SOFT_NOTE_RE deliberately does not cover, the gap triage flagged — and the ## Blocked by branch strips those asides from the section body before extracting #N edges, running title resolution, and gating the unresolved-region warning, so a - None. (Related: #A/#B) line resolves to zero edges while a genuine - Blocked by #N / - #N bullet beside it keeps its edge unchanged. The peeled references are recorded as soft notes (deduped, first-seen order) so the coupling stays visible after an unattended run. This is the mirror image of #34: that issue adds missing implicit edges; this removes a false one. tests/test_orchestrate.py covers the sentinel-with-aside case, the two verbatim real-world issue bodies, the (See …) aside, the aside-as-soft-note behaviour, the no-regression genuine bullet, the no-spurious-warning case, and the end-to-end build_waves no-cycle outcome.

0.14.0 – 2026-07-20

Added

  • /help adopts the echo-manpage mechanism from kntnt-wp-skillsdocs/man/*.md source, verbatim echo, help-gate parity, model: haiku (#45). scripts/help.py previously surfaced only each skill's intro paragraph, re-wrapped and column-aligned by hand — a missing-source bug, not a formatting one: /kntnt-code-skills:help release produced a paragraph, never a usage reference with real flags. The fix retrofits the reference model built for this in kntnt-wp-skills (its docs/design.md §12): one full manual page per skill at docs/man/<skill>.md (NAME, SYNOPSIS, DESCRIPTION, OPTIONS, EXAMPLES, FILES), added for all eight skills — coder, coding-standard, commit, doctor, init, orchestrate, push, release. scripts/help.py is now the echo-style renderer from kntnt-wp-skills, essentially unchanged (only its docstring's plugin name differs): it reads .claude-plugin/plugin.json and docs/man/*.md and prints them verbatim — Claude Code already renders GitHub-flavoured Markdown in the terminal, so no wrapping or alignment is computed here — no argument → the overview (the plugin blurb plus each manpage's NAME line); a skill name → that skill's manual page verbatim; anything else → the one-line unknown-skill error. commands/help.md now emits the rendered Markdown without the old outer triple-backtick fence (which broke the manpages' own tables and fenced SYNOPSIS blocks) and sets model: haiku, since the turn is a pure verbatim echo. Every one of the eight SKILL.md files gains a ## 0. Help gate step: on help/--help/-h it runs scripts/help.py <skill>, emits the result verbatim, and stops before anything else — so /release --help and /kntnt-code-skills:help release reach the same page. README.md's usage now links each skill's manual page instead of restating its flags (the /help bullet, a new "full option reference" pointer, and the former "Release-skill arguments" flag list, now a link to the three manpages). tests/test_help.py covers the renderer's overview/detail/unknown branches and main()'s dispatch against a synthetic plugin-root fixture; tests/test_help_docs_consistency.py binds the manpages to reality — every skill has one, every documented flag is grounded in that skill's own SKILL.md (and every flag SKILL.md defines is documented, so nothing real is silently dropped), every skill has a help gate, the overview carries every manpage's NAME line, commands/help.md runs on haiku without the outer fence, and every README docs/man/ link resolves.

  • general.md gains a language-agnostic Refactoring completeness rule (#35). Field incident: a three-caller function had two callers updated to a new promotion scope and the third stranded on the old behaviour, caught only at integration review — per-issue/PR review that only reads the diff structurally misses this, because a diff can be locally consistent within the files it touches while leaving the codebase globally inconsistent. The new ### Refactoring completeness rule in lib/coding-standard/general.md's Universal rules requires enumerating every caller (grep, find-references) before finishing whenever a change alters a shared symbol's contract, signature, or effective behaviour, and updating all of them in the same change; when bringing every caller up to date would genuinely ripple beyond the task at hand, the rule requires saying so explicitly in the summary or handoff rather than half-applying it. No structural change to lib/coding-standard/_index.md or scripts/scaffold.py — projects that have already run /coding-standard keep their existing agents.d/coding-standard/general.md snapshot until their next /coding-standard --update re-sync, per the standard's usual update model. Orchestrate-side enforcement (an implementer ripple report, a verifier consistency lens) is out of scope here — that is #36.

  • /orchestrate catches an incomplete shared-seam refactor earlier — implementer ripple report + verifier consistency lens (#36). Companion to #35: when an implementer changes a shared symbol's contract or effective behaviour but updates only some callers, the diff is locally consistent, and a per-issue verifier — bound to the diff and structurally unable to see an unchanged caller outside it — cannot catch the gap, so the class was previously caught only by the mandatory integration review, a full extra review + remediation cycle. Two prompt-text additions in skills/orchestrate/orchestrate.workflow.js, both single shared constants interpolated rather than copy-pasted (mirroring AGENT_CONSTRAINTS): a new RIPPLE_REPORT_INSTRUCTION, folded into the implement and fix prompts, tells the implementer that when a change alters a shared symbol's contract, signature, or effective behaviour it must enumerate the affected call sites and state under assumptions which it updated and which it did not; a new CONSISTENCY_LENS_INSTRUCTION, folded into every lens verify dispatches, tells the reviewer to also check that symbol's unchanged callers for consistency, not only the diff — scoped to fire only when the diff touches a shared, multi-caller symbol, so an ordinary diff pays nothing extra, and confined to the initial per-issue panel: the targeted fix-round/reconcile re-verifies and the mandatory integration review are unchanged, staying the backstop. skills/orchestrate/SKILL.md documents both: the operating contract's three-bucket handoff description (Assumptions & blockers) names the ripple report, and step 4's Verify description names the conditional consistency lens and its scope. tests/test_orchestrate_workflow.py and tests/test_orchestrate_skill.py cover the new constants' declaration, content, and interpolation (or deliberate exclusion) structurally, red-first.

  • php.md pins the declared PHP floor with PHPStan's phpVersion, and warns against reaching for PHPCompatibility (#43). The PHP module required declaring a PHP floor (Requires PHP header / composer.json) and using modern language features fully, but named nothing that checked the code against that floor — drift in either direction went unnoticed. The reflex tool for this job, PHPCompatibility, fails silently on modern PHP: its last stable release predates PHP 8.0, so it passes 8.x-only syntax with zero findings, a gate that looks green while enforcing nothing. php.md's PHP tooling section now extends the existing PHPStan bullet: pin phpVersion to the project's declared floor and keep the two in step — with phpVersion set, PHPStan reports any syntax newer than the floor as an ordinary, non-ignorable error that a baseline file cannot bury — and explicitly warns against PHPCompatibility's testVersion for this purpose, stating the reason. wordpress.md's WordPress-specific tooling section also gains the companion note on version_compare() bootstrap guards: once phpVersion is pinned, PHPStan constant-folds such a guard; treatPhpDocTypesAsCertain: false does not fix this, since that option governs PHPDoc-derived certainty rather than the phpVersion-derived narrowing at work, so the honest fix is a scoped @phpstan-ignore comment on the one guard line that defends against a host loading the plugin outside the activation path. Out of scope per triage: mandating a phpVersion range, any downstream CI wiring, and detecting an over-declared floor. tests/test_php_phpversion_floor.py guards both modules structurally, red-first.

  • php.md and wordpress.md name phpcs + WPCS as recommended tooling for WordPress projects (#39). phpcs previously appeared nowhere in the standard even though every Kntnt WordPress project ships it, so each project's phpcs.xml.dist was copy-pasted from an older project rather than written from the standard — the observed drift included a wrong 120-column line cap inherited from a stale config and warnings that never failed the build. Scope settled at triage: prose-only, no central ruleset package. php.md's PHP tooling section now names phpcs + WPCS alongside the other WordPress-specific tools it already points at the WordPress module for. wordpress.md's WordPress-specific tooling section states phpcs is recommended, not required — consistent with the proportionality direction of #40 — and a new phpcs / WPCS ruleset section carries three things in prose: the six sniffs a ruleset must exclude (Universal.Arrays.DisallowShortArraySyntax, WordPress.Files.FileName, WordPress.NamingConventions.PrefixAllGlobals, WordPress.PHP.YodaConditions, WordPress.Arrays.MultipleStatementAlignment, PSR2.Methods.FunctionClosingBrace) to encode the four deliberate WP-CS deviations plus the two sniffs that actively contradict the standard's own alignment and paragraphing rules; an explicit cannot-enforce list naming the comment-width rule and the no-vertical-alignment rule, so a green phpcs run is read as "conforms to the subset phpcs can see," never "conforms" outright; and the per-project ruleset posture — no central kntnt/coding-standard Composer package exists, the idea remaining a possible future issue. tests/test_coding_standard_phpcs_tooling.py guards all of the above structurally, red-first.

Changed

  • /orchestrate's planning flow now assesses implicit issue relationships beyond the explicit Blocked by edge graph (#34). A field run exposed two gaps the deterministic planner's hard-edge parsing cannot see, because it stays prose-blind by design: two same-wave issues rewriting the same seam produced an integrate-time park of already-verified work, and an issue naming a sibling issue's not-yet-built command ran before that sibling, leaving a dangling reference the docs-truthfulness gate had to hotfix by pulling the sibling's scope forward. skills/orchestrate/SKILL.md step 2 (Plan) now instructs the orchestrator's own read of every in-scope issue and brief to additionally assess, on top of the computed dependency graph: same-wave file/module overlap — serialize the colliding pair (soft-order or one-issue-per-slice) rather than let them run concurrently, reading the plan's soft_notes array as first-class input rather than an audit-trail note — and a cross-reference to another in-scope issue's deliverable (a command, symbol, or file it creates) with no explicit Blocked by edge, treated as a missing dependency: order the referrer after the provider, or flag the missing hard edge. Both adjustments are silent-but-visible, surfaced at the step-3 confirm gate (and in what --plan presents) alongside the reason for each, exactly like the existing no_brief flag — neither is a new confirmable decision. The "issues in one wave are independent" claim is now qualified to dependency-independence only, and the Cost and chunking subsection names both facets as reasons to serialize or reorder, recommending one-issue-per-slice for a tightly-coupled refactor. scripts/orchestrate.py plan is unchanged — this is prose-level LLM-side planning guidance only. tests/test_orchestrate_skill.py covers the new instructions, the soft_notes input, the confirm-gate surfacing, and the qualified wave-independence wording structurally.
  • general.md is now the single authoritative source for line-width rules; php.md's 120-column code cap is gone (#37). general.md's old Line wrapping rule was a single vague sentence ("Comments wrap at column 80. Code may go wider where it improves readability — see formatter settings per language.") that disagreed with php.md's own duplicate of the comment rule plus a hard 120-column cap on code, and neither mentioned trailing (end-of-line) comments. general.md now states three explicit rules: a comment standing alone on its own line never passes column 80 regardless of where it sits; a comment trailing at the end of a code line is never wrapped, however long; and code lines have no upper limit — removing the pressure to break a line for its own sake, not licence to let one sprawl, with the existing Motivated line breaks guidance (unchanged) as the actual trigger for breaking. It also records that rule 1 is enforced by review, not tooling, since no sniff can express a comment-specific width — the standard stays prose-only here by design (a possible executable half is future work, see #38/#39). php.md's 120-column cap and its duplicated comment-width sentence are both gone, replaced by a pointer to the general module's Line wrapping rules, so the rule has exactly one authoritative statement. The other language modules (typescript.md, javascript-vanilla.md, python.md, bash.md) were checked and carry no conflicting width rule — TypeScript's Biome Line width: 100 is an unrelated per-tool formatter default, not a restatement of the standard's own rule, and is left as-is. tests/test_coding_standard_line_width.py guards the three rules, the enforcement note, the surviving Motivated line breaks bullet, and the absence of the old cap/duplicate anywhere in the modules.
  • general.md's "no vertical alignment" rule now documents that no tooling enforces it. No sniff in the phpcs ecosystem can forbid vertical alignment of = / =>Generic.Formatting.MultipleStatementAlignment and WordPress.Arrays.MultipleStatementAlignment are one-directional and can only ever demand alignment, so a project's phpcs.xml.dist merely excludes them and aligned code passes the gate silently. Triage settled #38 as prose-only (no custom sniff, no kntnt/coding-standard package for now): the rule's own bullet in lib/coding-standard/general.md now states that no sniff enforces it, that review catches violations, and that excluding those sniffs is the expected, correct posture rather than a surprise. tests/test_general_alignment_enforcement_doc.py guards the wording and that this repository's own dogfooded agents.d/coding-standard/general.md never drifts from the source.
  • php.md's PHP tooling section states its own proportionality rule; pcov drops as a peer bullet (#40). The section mandated Pest, PHPStan, and pcov as unconditional peer bullets, contradicting general.md's own YAGNI-first philosophy — real projects already skip Pest on YAGNI grounds, teaching readers the list was decorative — and pcov additionally listed a PHP runtime extension (installed via PECL / the PHP build / container config) beside Composer-installable packages as though Composer could depend on it, which it cannot. Positions settled at triage: the list is now defaults-when-applicable, naming general.md's own TDD rule — automate every test that can meaningfully constrain behaviour at the lowest layer that does — as the deciding question for whether a project owes itself a test suite at all, and by extension Pest; pcov disappears as its own bullet and becomes a coverage clause folded into the Pest bullet (use pcov rather than Xdebug — roughly 10x faster because it only instruments line execution instead of running a full step debugger — and pcov is a runtime extension, not a Composer package); PHPStan stays unconditional, its rationale now stating its value is highest precisely where no test suite exists, because it is then the only automated check. general.md is unchanged — its TDD rule is referenced, not modified. tests/test_php_tooling_docs.py guards the defaults-when-applicable framing, the TDD-rule reference, pcov's removal as a peer bullet and survival as a coverage clause naming it a runtime extension, and PHPStan's continued unconditional bullet with its "only automated check" rationale, red-first.

Fixed

  • /orchestrate merge mode no longer parks verified work en masse on a stale branch base (#46). A merge-mode run integrates issues serially by fast-forwarding the default branch to each verified feature tip, but the implement prompt only said "work on a fresh branch off the current integration base" — resolved from inside the harness's Workflow worktree, that "current base" was the run-start scaffolding ref, pinned at run start and never advancing as issues landed. So every issue after the first forked from a stale base and, once the default moved under it, could no longer --ff-only; a doc-heavy batch that shares files (a common CHANGELOG.md, a shared module) degraded into a park generator. Three fixes in skills/orchestrate/orchestrate.workflow.js: (1) the implement prompt now creates the feature branch fresh off the up-to-date default tip with git checkout -B, mirroring integrationHotfix, so in the serial integrate-immediately design each issue forks from a base that already contains its landed predecessors and the fast-forward holds for real; (3) the integrate prompt no longer asserts the false "nothing landed between the build and now" invariant — it ties the fast-forward to the branch having been cut off the then-current tip; (2) a genuine rebase content conflict on a verified branch is no longer parked outright — a new bounded reconcile stage (reconcile rebases and resolves keeping both sides' concerns, reverifyReconcile re-verifies only that resolution as a single targeted agent, then it lands via the same linear fast-forward integrate step) repairs it, merge mode only, bounded by maxFixRounds, parking only when the re-verify blocks or the cap is hit. A new pure isReconcilableConflict(record) helper (mirrored byte-for-byte in lib/orchestrate/engine-helpers.mjs, drift-guarded) decides eligibility from a new conflict flag the integrator threads onto a parked record via INTEGRATE_SCHEMA. skills/orchestrate/SKILL.md step 4 documents the fresh-off-current-tip fork and the reconcile fallback. tests/test_orchestrate_workflow.py covers the git checkout -B fork, the corrected integrate invariant, the conflict schema field and its threading, the worktree-isolated reconcile and single read-only reverifyReconcile agents, the merge-gated wave-loop hook, and the bounded reconcile loop; the isReconcilableConflict behaviour is exercised through node.
  • lib/coding-standard/php.md mandated first-class callable syntax unconditionally, which is wrong for WordPress hook callbacks (#42). $this->method(...) builds a fresh Closure at the call site, and a Closure's hook id (_wp_filter_build_unique_id()) is tied to that unreachable instance, so no other code can ever remove_action() / remove_filter() against it — a real extensibility loss the standard never mentioned, while the array-callable form [ $this, 'method' ] stays removable and read as a violation. The PHP module's first-class-callable bullet now carries an explicit exception: the syntax stays the default for immediate-consumption callables (array_filter/array_map/usort-style), but not for add_action() / add_filter() callbacks, nor any registry where a callback must remain individually removable. lib/coding-standard/wordpress.md gains a new Hook registration callables section stating the same rule and rationale for hook registration, cross-referencing the PHP module rather than duplicating it, so a WordPress-focused reader meets the exception in context. tests/test_php_wordpress_hook_callback_exception.py guards both modules structurally.
  • lib/coding-standard/wordpress.md said nothing about plugin/theme header metadata, so it silently inherited an unsatisfiable comment-width rule (#41). The comment-width rule (#37) hard-wraps a standalone comment at column 80, but the plugin header docblock and the theme style.css header are not prose comments — get_file_data() parses them one field per line, with no continuation syntax, so wrapping a long Description: does not reformat it, it silently truncates it at the line break with no error or warning. WordPress's header-parsing regex also treats @ as an ordinary header-line prefix, on a par with *, #, and whitespace, so a linter annotation (@since, @phpstan-ignore) placed inside the block risks being parsed as, and shadowing, a real field. A new Plugin and theme headers section states the comment-width rule does not apply to the header block — the whole block, not only its Field: lines, per the triage-settled scope — that a field is never wrapped, with the truncation reason; warns against linter annotations, with the @-prefix-shadowing reason; and leaves column alignment of field values to WordPress convention, explicitly stating that the general module's =/=> alignment ban does not extend to header metadata. tests/test_wordpress_header_metadata_exemption.py guards all of the above structurally, red-first.
  • /release --yes now suppresses the first-run project-record confirmation too. Step 2's project-record confirmation was documented as "never skipped, even with --yes" — the only prompt in the whole plugin that survived --yes, defeating its fire-and-forget promise on a project's very first release. Under --yes the skill now proceeds straight on its best-detected record (version locations, archive decision) and reports what it detected and recorded as part of the run's output — the safety valve becomes visible reporting, not a blocking question. Without --yes, the confirmation behaves exactly as before. skills/release/SKILL.md's --yes argument description and step 7's confirmation-gate prose now state the uniform rule — --yes suppresses all interactive prompts, no exceptions — and docs/man/release.md (reachable from README.md's manpage link) documents it accordingly. tests/test_commit_push_release_docs.py guards that the carve-out wording is gone and the uniform rule and the reporting requirement are stated (#44).

0.13.0 – 2026-07-14

Added

  • New /commit skill — commit the working tree without pushing. The routine save operation, factored out as the innermost of the three release-workflow skills. It reconciles CHANGELOG.md's [Unreleased] section against the real changes since the last release (via lib/changelog.md), then stages and commits the current branch (via the new lib/commit.md) behind a single confirmation gate, and stops — no push, no version bump, no tag, no branch integration, no platform release. Unlike /push and /release, it does not stop when [Unreleased] comes out empty: a pure refactor, a formatting pass or a test-only change carries no user-facing changelog line yet is still worth committing, so /commit stops only when the working tree is genuinely clean. Its arguments mirror /push"message" for an exact commit message, --yes to skip the gate. It triggers on /commit or an obvious commit-without-push request in any language; commit and push and a bare push route to /push, and an ambiguous bare commit (which might mean a raw git commit) asks first.

Changed

  • /push and /release now share one commit spine with /commit. The stage-and-commit mechanic — the .gitignore safety net, git add -A && git commit, never bypassing commit hooks (--no-verify), and the commit-message default — was duplicated as prose in both /push and /release. It is now factored into lib/commit.md, the sibling of lib/changelog.md, and all three release-workflow skills reference it: /release supplies its own Release X.Y.Z: message and keeps its bump / rebase / promote ordering before the shared commit, so its behaviour is unchanged. As a side effect, /push's stop condition is corrected — it no longer stops when [Unreleased] is empty (which wrongly refused to push a non-changelog-worthy change), stopping only when there is nothing to commit and nothing unpushed. tests/test_commit_push_release_docs.py guards the shared spine: that lib/commit.md exists and all three skills reference it, that /commit never runs git push, and that the README's spelled-out skill counts track the actual skills/ directories.

0.12.0 – 2026-07-14

Added

  • /orchestrate decouples per-issue verification rigor from the --level dial (ADR-0003). The first large real run exposed a workflow the single dial could not express: a large batch of individually-trivial, homogeneous issues driven by a strong implementer, where an independent per-issue second opinion is N× overhead for near-zero risk. scripts/orchestrate.py plan gains --max-lenses=N, mirroring --max-fix-rounds exactly (rejected on a negative value, per-run only, never a policy default): a pure cap_lenses() post-step truncates each issue's level-and-risk-derived lenses panel to at most N after derive_rigor runs, so it only ever lowers a panel, never raises one. N ≥ 1 is a plain, floor-respecting cap; N = 0 is the deliberate amendment to ADR-0001 §5's inviolable per-issue-lens floor — it empties every panel outright — but when it meets an issue whose panel was plan-time risk-escalated (a Risk: marker or risk:* label), the panel still lands at 0 (the flag holds) and the plan's warnings names the issue for gate visibility rather than silently re-escalating it. In skills/orchestrate/orchestrate.workflow.js, lensesFor now distinguishes an explicitly empty lenses array (--max-lenses=0) from an absent one (which still falls back to DEFAULT_LENSES), and buildAndVerify reads an explicitly empty panel as "skip the per-issue verify stage — implement straight to integrate" without ever suppressing red-before-green or the mandatory integration review (not a "lens," so the name never touches it). The sole sanctioned automatic override is in-situ hazard escalation (ADR-0003 §5): a new pure shouldEscalateInSitu(report, panel) helper (mirrored in lib/orchestrate/engine-helpers.mjs, drift-guarded like the other extracted helpers) returns true only when the implementer's three-bucket report flags a genuine, previously-unseen danger surface via a new inSituHazard field and the panel is empty; on true, withInSituHazard folds that hazard into a one-lens DEFAULT_LENSES panel dispatched for that one issue only — plan-time hazards are never re-escalated here, and there is no mid-run pause either way. A new --pr flag is the explicit conservative partner of --merge: precedence is explicit flag > declared policy marker > the conservative PR default, --pr and --merge together is an error, and merge authority is never inferred. skills/orchestrate/SKILL.md documents both flags in §Arguments, the ADR-0003 §5 risk precedence and the --max-lenses=0 use-case (a large homogeneous-trivial batch) and non-use-case (a small trivial task reaches for a lighter tool) in the rigor-baseline section, the amended §5 floor prose, and decision-boundary-map rows for both flags noting merge authority is never inferred. README.md documents the two merge modi operandi (merge-policy: merge for solo-on-main vs. the PR default for multiple users, with --pr/--merge as the per-run exceptions and where the policy marker is recorded) and the --level=XL --max-lenses=0 --merge quick-orchestrating idiom alongside the retroactive /code-review <base> idiom (ADR-0003 §7). docs/adr/0001-orchestrate-control-model.md gains a back-reference noting §1/§5/§7 are amended by ADR-0003. tests/test_orchestrate.py, tests/test_orchestrate_workflow.py, and tests/test_orchestrate_skill.py cover the cap, the floor-breach-with-warning case, the empty-panel skip, the in-situ escalation helpers, and the SKILL.md prose structurally.

0.11.0 – 2026-07-14

Added

  • /orchestrate gains the --level ambition dial, and every sub-agent's model and reasoning effort now derives from it instead of inheriting the session tier. The first real runs were slow and expensive because the engine set no model or effort per agent, so even purely mechanical leaves ran on the strongest tier — this ends "everything runs on Opus" (ADR-0001 §1–§4). The dial has the fixed vocabulary XS | S | M | L | XL (default M). Because the engine is a deterministic Workflow script with no primitive to enumerate live models, the orchestrator (the session-model planning pass) resolves --level into a per-role (model, effort) against the harness's live model list and passes it to the engine as args.roles = { judgment, implementer, mechanical }; the engine stores no model-name table and only applies what it is handed. In skills/orchestrate/orchestrate.workflow.js a new pure roleTuning helper (tested source of truth in lib/orchestrate/engine-helpers.mjs, byte-identical inline copy drift-guarded) turns one role's resolution into an agent() opts fragment — only the fields the orchestrator set are copied, and an absent role or field yields an empty fragment, so the agent inherits the session model and a plan produced before the dial existed still runs unchanged. The engine reads config.roles (default {}) and spreads the fragment into each sub-agent: judgment onto verify / reverifyFindings / integrationReview, implementer onto implement / fix / integrationHotfix, and mechanical onto integrate and the teardown agent. skills/orchestrate/SKILL.md documents the dial in §Arguments and, in §Model and effort, the orchestrator's per-role derivation against the live model list, the illustrative non-durable ladder (ADR §4), and the two guardrails (only dispatchable models; judgment roles never below the session tier unless the maintainer dialed down). tests/test_orchestrate_workflow.py guards the exported/drift-guarded helper, the config.roles read, each role's spread, and the absence of any hardcoded model/effort literal, plus a behavioural node run of roleTuning over a table; tests/test_orchestrate_skill.py guards the §Arguments dial and the §Model-and-effort derivation, ladder, and guardrails structurally.
  • /orchestrate verification rigor is now derived from --level and each issue's risk, not set by hand. ADR-0001 §5–§6 couples the rigor baseline to the ambition dial and lets per-issue risk escalate it. scripts/orchestrate.py plan now accepts --level (XS|S|M|L|XL, default M) and --max-fix-rounds, and emits each issue's verifier panel as a lenses array plus a run-level maxFixRounds cap from a fixed, model-agnostic ladder: XS/S/M → 1 broad lens & 1 fix round, L → 2 focused lenses & 2 rounds, XL → 3 lenses & 2 rounds. An explicit Risk: high|medium|low marker in the issue body or Agent Brief — or an optional risk:* label — is read deterministically and escalates the rank escalate-only (the highest of level baseline and risk signal wins), so a Risk: high on an XS issue pulls just that issue to the XL tier; risk never lowers rigor below the level baseline, an inviolable floor holds at ≥ 1 lens and a fix-round floor of 1 (0 reachable only via an explicit --max-fix-rounds=0), and an explicit Risk: low contradicted by a hazard label is surfaced in the plan's warnings rather than silently applied. The lens count is settled in the helper; the engine's lensesFor consumes the array unchanged and the orchestrator tailors each lens's prose to the issue. tests/test_orchestrate.py covers the derivation, the escalate-only max, the floor, the --max-fix-rounds=0 route, and the disagreement warning.
  • /orchestrate gains the sliding implementer and its implementation-planning pass (ADR-0002). The implementer's mode now slides with --level — a mechanical executor of a pre-settled recipe at the low end, an autonomous problem-solver at the high end — driven by a per-issue implementation plan the orchestrator authors inside its existing one-time planning judgment (zero extra sub-agents per issue) whose detail scales inversely with the level (recipe at XS/S, balanced spec at M, goals + constraints at L, none at XL). skills/orchestrate/orchestrate.workflow.js consumes two new args fields — a per-issue issues[].plan string and a run-level implementerMode ∈ { execute, balanced, autonomous } marker — and a new inline planOverlay helper (mirrored byte-for-byte in lib/orchestrate/engine-helpers.mjs and drift-guarded) composes an additive "how" overlay for the implement prompt from the fixed mode framing (IMPLEMENTER_MODE_FRAMINGS) plus the plan text, each degrading independently to nothing when its field is absent, so a legacy or hand-launched run is byte-for-byte today's prompt. The framing is keyed by the explicit marker (hardened against Object.prototype key collisions) and applies to implement only; fix and integrationHotfix are unchanged. The Agent Brief stays the authoritative what and the tests bind to the acceptance criteria, never to the plan. tests/test_orchestrate_workflow.py guards the three framings, the mode selection, the plan interpolation, the untouched fix/integrationHotfix prompts, and the clean no-plan degradation.

Changed

  • /orchestrate separates --yes from merge authority (ADR-0001 §7). --yes now means "yes to the single pre-run confirmation gate, and nothing more" — it waives only the go/no-go and grants no authority to write to the default branch. Merge-vs-PR is a policy settled before the gate: merge authority comes from exactly two sources — a per-run --merge or a once-per-project/global merge policy — and absent both the plugin default is PR, so --yes alone opens one pull request per issue and never walks to main. skills/orchestrate/SKILL.md documents the separation in §Arguments and the operating contract, names where the merge policy is recorded and read (an orchestrate: merge-policy: merge|pr marker in the project's AGENTS.md/CLAUDE.md, read at plan time as prose, not a deterministic code path), restates the inviolable safety floor as per-element sub-bullets, and shows the merge-vs-PR decision surfaced (not re-decided) at the single gate. tests/test_orchestrate_skill.py guards the separation, the policy location, the safety-floor elements, and the gate wording structurally.
  • /orchestrate SKILL.md reconciled end-to-end with ADR-0001 and ADR-0002. Docs-only, no behaviour change — reconciling the skill's control surface with the mechanisms now shipped by #25/#26/#28/#29, never re-deciding them. §Arguments now presents the complete argument set — [scope], --level (carrying both the per-role model/effort and the rigor baseline), --merge, --max-fix-rounds (an override of the level-derived cap, the only route to 0 rounds), --plan/--dry-run, --yes (gate-only), and the budget cap (budgetFloor + the session token target) — each as ADR-0001 §8's decision-boundary map has it. A new decision-boundary map section reproduces that map: almost every decision is silent (autonomous), only three surface at the single gate (merge authority, cost cap, go/no-go), and the per-issue Risk: marker is the one optional lever. §Model and effort now documents the level-derived rigor baseline (panel size + fix-round cap), the escalate-only per-issue risk escalation, and the inviolable floor alongside the model/effort ladder (still marked illustrative and non-durable), plus the sliding implementer and the implementation-planning pass (the per-issue plan overlay and the run-level implementerMode framing). The step-3 cost estimate is now level-aware — the per-issue agent count scales with the level's panel size P (1/1/1/2/3) and fix-round cap F (1/1/1/2/2) as N × (P + F + 2), reducing to the lean N × 4 + 3 at the default M — instead of a fixed × 4, keeping the required-cap-above-~10-issues rule and the 8–10-issue slicing guidance. The Build step's launch args now enumerate implementerMode and each issue's plan alongside roles/lenses, and a stale "every sub-agent runs at the strong tier" claim is corrected to the level-derived tiers. tests/test_orchestrate_skill.py gains structural guards for the level-aware estimate, the decision-boundary map, the rigor baseline, and the sliding-implementer documentation.

0.10.0 – 2026-07-13

Added

  • /orchestrate confirm gate now sizes the run to its cost and its closing step confirms each issue individually. A 30-issue run with no up-front estimate and no cap blew the monthly spend limit mid-flight and lost the whole run, and right after closing a batch the issue list read stale, which misled the close confirmation. skills/orchestrate/SKILL.md step 3 gains a Cost and chunking subsection: an agent/token estimate pinned to the lean defaults (a single broad reviewer, one fix round) with a typical closed form agents ≈ N × 4 + 3 (N issues × the average per-issue cost, folding the default panel-size-1 × fix-rounds-1 over implement / verify / (fix + re-verify) / integrate), an honest per-issue range of 3–5 sub-agents (3 with no fix, 5 when the one fix round and its re-verify run) and a per-run overhead of ≈ 2 in the default PR mode (integration review + teardown), up to ≈ 5 in merge mode when a hotfix runs, a token order-of-magnitude per sub-agent, a recommendation to run large backlogs in slices of 8–10 issues so a stop loses at most a slice, and a required explicit cap for any run above ~10 issues — naming only the real engine levers (budgetFloor, now documented as a settable args knob with default 60000 tokens in step 4; the run's budget.total; a lower maxFixRounds; and slicing), not a nonexistent max-agents cap — a large run must not be confirmed uncapped. Step 5 gains a Confirming closures subsection: the orchestrator verifies each closure by querying that issue's own state with gh issue view <n> (its state / closed field), and explicitly does not trust a gh issue list re-query, whose endpoint is eventually consistent and reads stale immediately after a close. tests/test_orchestrate_skill.py guards the estimate formula, the lean-defaults wording, the slice size, the cap-above-threshold requirement, and the per-issue gh issue view closing rule structurally.
  • /orchestrate now always runs a mandatory adversarial integration review with a bounded, mode-aware hotfix loop. Per-issue reviews see one issue in isolation and structurally cannot catch a cross-issue defect — one issue's change silently weakening another's guarantee across the combined change set; on the real run only the integration review caught a genuine HIGH-severity defect of exactly this kind. In skills/orchestrate/orchestrate.workflow.js, after the wave loop and inside the teardown try (so any hotfix worktree is torn down too), whenever the run integrated at least one issue (verdicts.length > 0 — the only case a combined change set exists) a new integrationReview agent reviews it: one read-only adversarial reviewer given a per-issue verifier's full rigor (never a token smoke test), carrying the shared AGENT_CONSTRAINTS, returning the VERDICT_SCHEMA, and its clear decision routed through blockingFindings so a dead / not-clear / empty-findings review can never pass silently. The reviewer is mode-aware: in merge mode it reviews the combined diff now on the default branch; in the conservative default PR mode nothing landed on the default branch, so it reviews the union of the run's feature branches (verdict.branch) against the default branch — reviewing the default branch there would see nothing and falsely clear, the exact silent pass the review exists to kill in the default mode. A real finding drives a bounded hotfix + re-review rather than a mere report, in merge mode only: a new code-touching integrationHotfix agent (worktree-isolated per #14, bound by AGENT_CONSTRAINTS per #15) addresses only those findings test-first on a branch created fresh off the up-to-date default branch with git checkout -B (resetting any stale ref a prior run left), lands through the existing linear rebase-fast-forward integrate step, and the combined diff is re-reviewed — capped by a documented maxIntegrationRounds (default maxFixRounds, i.e. 1), with the hotfix branch tracked in builtBranches so teardown removes its worktree. In PR mode auto-hotfixing would contradict the leave-the-merge-to-you posture, so a finding is reported (parked with its specifics) for the human instead. A finding is never silently cleared or dropped in either mode, a clean pass logs a trace, and the run's return grows an integration field alongside the unchanged verdicts/parked. skills/orchestrate/SKILL.md step 5 describes the always-run, mode-aware review, and tests/test_orchestrate_workflow.py guards the reviewer (including the PR-mode branch union), the fresh-branch hotfix, the merge-mode gate, the cap, the placement, and the returned outcome structurally.

Fixed

  • /orchestrate left the Workflow harness's per-worktree scaffolding branches behind, so a run was not fully state-neutral. The Workflow harness names each per-worktree scaffolding branch worktree-<runId>-<n>, and once an agent checks out its own feature branch that ref is orphaned — #14's teardown removes the worktrees and preserves every feature-branch ref, but leaves these orphaned scaffolding branches to accumulate run after run, so the repository the run started from is not the one it leaves. skills/orchestrate/SKILL.md's Finalize step now documents a closing prune of exactly this run's scaffolding: the orchestrator, which knows the runId from the Workflow launch, lists this run's own branches with git branch --list "worktree-<runId>-*" and deletes each with git branch -D, confined to the exact runId so never a feature branch, an integration-hotfix branch, or any branch outside the run-scoped prefix — the one authorised branch delete the orchestrator makes (sub-agents and the worktree teardown are forbidden any), leaving #14's worktree teardown and its preservation of every feature-branch ref unchanged, and verifying the captured runId is non-empty first so a degenerate worktree--* glob skips rather than force-deletes every run's scaffolding. tests/test_orchestrate_skill.py guards the run-scoped prefix, the runId confinement, the scoped git branch -D, and the preserved #14 teardown structurally.
  • /orchestrate in the default PR mode built a dependent on a base missing its prerequisite's unmerged PR. Issue #20 parks a dependent whose in-scope prerequisite did not land, but "landed" means the prerequisite's code is on the base a dependent builds from — the default branch — and a successful integration puts it there only in --merge mode. In the conservative default PR mode integrate merely opens a pull request and merges nothing, yet skills/orchestrate/orchestrate.workflow.js still added the issue to the landed set on the successful-PR path, so a dependent was not parked and built off a default branch that did not contain its prerequisite's still-open PR — the same "incomplete base" #20 exists to prevent, still present in the default mode. The engine now adds to landed only in merge mode (if (merge) landed.add(...)), because only merge mode lands the change on the base; in PR mode nothing merges, so a dependent with an in-scope prerequisite is parked with a mode-aware reason that names the prerequisite and points the human at --merge (or waiting for its PR to merge), and — because a parked issue never lands — the skip cascades transitively to its own dependents, exactly as #20's does. Merge-mode behaviour (#20) is unchanged. skills/orchestrate/SKILL.md now documents the enforcement in the merge_required note and the wave-outcome paragraph, and tests/test_orchestrate_workflow.py guards the merge-gated landed.add and the mode-aware park reason structurally, plus a behavioural run of the real unlandedPrerequisites helper over one dependency graph in both modes showing merge mode builds the chain while PR mode parks the dependent and cascades.
  • /orchestrate never threaded the feature branch onto the per-issue record, so integration saw undefined. In skills/orchestrate/orchestrate.workflow.js the toRecord helper built the record buildAndVerify returns by copying gates/remaining_for_human/assumptions/blockers off the implementer result but NOT impl.branch — so every done or parked record carried branch: undefined. Downstream this broke two consumers that read the branch: integrate rendered "Rebase branch undefined onto…" / "Open a pull request for branch undefined", and — worse — the mandatory final integrationReview in the default PR mode builds its prompt from the union of the run's feature branches (verdict.branch), which then rendered an EMPTY list, so the run's headline cross-issue safeguard reviewed nothing and cleared silently — the exact silent pass that review exists to prevent. toRecord now sets branch: impl?.branch (optional-chained, so a null implementer parks cleanly rather than throwing), threading the real branch onto every record so integrate and integrationReview receive it. A behavioural test in tests/test_orchestrate_workflow.py extracts toRecord and runs its real logic through node, asserting a done record actually carries its branch.
  • /orchestrate treated the Agent Brief as mandatory, so an issue with none could not be built. The implement, verify, and reverifyFindings prompts in skills/orchestrate/orchestrate.workflow.js told the sub-agent to treat the "Agent Brief" comment as authoritative with the issue body only as context — but briefs are sometimes never posted (issues #12–#19 have none), leaving those issues without a contract. The prompts now state: if an Agent Brief comment exists it is authoritative, OTHERWISE the issue body and its acceptance criteria ARE the contract — so a brief-less issue is implemented and verified from its body + acceptance criteria without error. The planner (scripts/orchestrate.py) now parses each issue's optional comments field (a list of {"body": ...} objects, tolerating bare strings and an absent field) and flags issues lacking a brief: an Issue.no_brief boolean surfaced per issue in the plan's issues[] and as a convenience top-level issues_without_brief list, with every prior plan field preserved. Brief detection is anchored to a Markdown "Agent Brief" heading (^#{1,6}\s*Agent Brief, case-insensitive), not a bare substring, so a prose mention ("no Agent Brief was posted") does not falsely clear the flag. skills/orchestrate/SKILL.md step 2 fetches comments in the gh issue list command and documents that flagged issues are still built from their body + acceptance criteria; tests/test_orchestrate.py and tests/test_orchestrate_workflow.py cover the flag, the heading anchor, and the fallback wording.

Changed

  • The project's quality gate now enforces ruff format --check and mypy everywhere it runs. CI (.github/workflows/audit.yml) and pre-commit (.pre-commit-config.yaml) ran only scripts/audit.py + pytest, so code could drift on formatting or typing while the gate stayed green — the /orchestrate engine's sub-agents, which run the project's own gate, landed an unformatted test file for exactly this reason. Both now run the full five-command gate: uvx ruff check ., uvx ruff format --check ., uv run --with mypy --with pytest mypy scripts tests, uv run --with pytest pytest -q, and uv run scripts/audit.py — added as steps in the CI job and as local language: system hooks in pre-commit, alongside the existing audit + tests. CONTRIBUTING.md now names all five commands as the gate in one discoverable place so a human contributor and an autonomous agent (whose "discover the project's gate" step reads it) both enforce the same bar. As one-time compliance the seven previously-unformatted files (scripts/audit.py, scripts/doctor.py, scripts/init.py, scripts/scaffold.py, and their tests) were brought into ruff format compliance — a pure ruff format result, whitespace and style only, no behaviour change; mypy scripts tests was already clean.
  • /orchestrate verification is now lean by default. The combinatorial blow-up — issues × lenses × fix-rounds, each fix round re-running the whole verifier panel — produced roughly 247 sub-agents on a 30-issue run and exhausted the spend limit before anything integrated. In skills/orchestrate/orchestrate.workflow.js the default verifier panel (DEFAULT_LENSES) is now a single broad adversarial reviewer whose one lens folds correctness against intent and acceptance criteria, test quality, and any security or data-safety hazard the issue touches — instead of three separate lenses; the per-issue lenses override still lets planning raise a genuinely high-risk issue to 2–3 focused lenses. maxFixRounds now defaults to 1 (was 2). And a fix round no longer re-runs the full panel: the panel verifies once after green, then each fix round re-verifies only the fixed findings through a new single targeted reverifyFindings agent (a read-only reviewer bound by the shared AGENT_CONSTRAINTS, never a panel). For a default non-high-risk issue with one finding, worst-case verifier agents drop from 9 (a 3-lens panel re-run across the initial pass and two fix rounds) to 2 (one panel pass plus one targeted re-verify). skills/orchestrate/SKILL.md documents the single broad reviewer, the default cap of 1, and the fixed-findings re-verify, and tests/test_orchestrate_workflow.py guards each structurally.

Fixed

  • /orchestrate integrated in an end-of-run batch and could leave feature branches non-linear. In skills/orchestrate/orchestrate.workflow.js the wave loop built an entire wave concurrently (parallel(wave.map(...))) and only then integrated the green issues, so a green issue did not land until the whole wave finished and a mid-run stop (a spend-limit cut-off) lost every already-verified-but-not-yet-integrated issue; and the integrate prompt's rebase step did not forbid the inverse, so a branch could accrue Merge branch 'main' commits that later resisted a clean rebase. The engine now processes each wave's issues serially in issue-number order and integrates each verified-green issue the moment it goes green — before the next issue's build begins — so a stop leaves every issue landed so far durably on the default branch. The integrate merge path now rebases the feature branch onto the up-to-date default branch and fast-forwards ONLY, and explicitly forbids merging the default branch INTO the feature branch or creating a merge commit there, keeping integrated history linear. The budget-floor park-before-dispatch guard moves into the per-issue loop; skills/orchestrate/SKILL.md now describes serial per-issue dispatch with immediate linear integration, and tests/test_orchestrate_workflow.py guards the linear-integration clause, the per-issue immediate integration, and the preserved empty-plan and budget-floor behaviour structurally.

  • /orchestrate sub-agents could close, push, merge, or destructively reset shared state. A sub-agent auto-closed an issue mid-run (closing must be the orchestrator's call, made only after independent verification), and a git reset --hard <base> issued while an agent was on a feature branch silently reset that branch and discarded its commits. skills/orchestrate/orchestrate.workflow.js now defines the forbidding text once in a single AGENT_CONSTRAINTS constant — you must NOT close the issue, push to any remote, or merge into the default branch (those stay exclusively with the orchestrator and the integrate step), NEVER git reset --hard on a feature branch, and one safe clean-start recipe (abort any in-progress rebase/merge, git checkout -f <base>, discard only tracked working-tree changes with git checkout -- ., never delete untracked files) — and interpolates that same constant into the implement, fix, and verify prompts. integrate is deliberately excluded because merging/pushing is its job. skills/orchestrate/SKILL.md states the rule in its operating contract, and tests/test_orchestrate_workflow.py guards the single definition, the per-prompt reuse, integrate's exclusion, and the constant's content structurally.

  • /orchestrate sub-agents shared one working tree and left worktrees behind. In skills/orchestrate/orchestrate.workflow.js the fix agent ran without worktree isolation, so a fix and another agent (or the launcher) could share one working directory: one agent's uncommitted changes left the tree dirty and parked the next issue for a purely mechanical, non-conflict reason, and worktrees left checked out by an earlier run locked branches a later run then could not rebase. Every code-touching agent (implement and fix, and any future salvage/hotfix agent) now carries isolation: 'worktree'; the read-only verifiers need none and integrate stays un-isolated as the sole mutator of the real default branch. Because a worktree-isolated fix lands in a fresh worktree while its target branch is still checked out in the implementer's persisted one, the fix prompt first frees whichever other worktree still holds that branch (git worktree remove --force, which keeps the ref) before taking it over, so git's one-worktree-per-branch rule never breaks a fix round. The run now tracks the exact feature branches it built and, in a try/finally that fires on both the clean-completion and the parked/blocked paths, dispatches a non-isolated teardown agent that removes only those run-created worktrees with git worktree remove --force (which keeps the branch ref) followed by git worktree prune — never deleting a branch or resetting, so no branch ref is lost. tests/test_orchestrate_workflow.py guards the isolation and teardown constructs structurally.

  • /orchestrate engine could not launch. skills/orchestrate/orchestrate.workflow.js declared three top-level exports (meta, normalizeArgs, planIsEmpty), but the Workflow harness tolerates only a single leading export const meta and rejects any other top-level export/import with a SyntaxError at launch — so the skill's preferred execution path was dead on arrival. normalizeArgs and planIsEmpty are now plain internal consts, leaving export const meta as the only top-level export and no top-level import. Their logic is mirrored byte-for-byte in a new importable module lib/orchestrate/engine-helpers.mjs that is unit tested, and tests/test_orchestrate_workflow.py now guards the single-export constraint structurally as well. skills/orchestrate/SKILL.md documents the constraint.

0.9.0 – 2026-06-26

Added

  • /init skill — bootstraps a new project to the Kntnt baseline in one pass: git init, the AGENTS.md/CLAUDE.md skeleton (via kntnt-skills:agents-md --force), the coding standard scaffolded into agents.d/coding-standard/, a licence fetched by SPDX id, the README/CHANGELOG/CONTRIBUTING (and NOTICE under Apache) rendered from generic templates, and a stack-aware .gitignore, then optionally the first commit and the GitHub repository. The deterministic file work lives in scripts/init.py (gitignore, templates, and license commands), covered by tests/test_init.py.
  • /doctor skill — init's idempotent reconciler. Deterministic checks in scripts/doctor.py (git state, .gitignore coverage, the coding standard's home and sync, the licence/NOTICE pairing) emit JSON findings; a read-only Workflow (skills/doctor/doctor.workflow.js) checks whether AGENTS.md, the agents.d/ files, and the README still match the real code. It applies only the fixes you select (--yes applies all) and never commits. Covered by tests/test_doctor.py.
  • lib/templates/ — generic, tokenised README.md, CHANGELOG.md, CONTRIBUTING.md, and NOTICE that /init renders. The README embodies the audience-layered structure (Users → Extenders → Contributors) with the fixed boilerplate blocks; CONTRIBUTING carries a licence-adaptive inbound-licensing paragraph and a behavioural-expectations line.
  • lib/gitignore/ per-module fragmentsphp.txt, typescript.txt, python.txt, and wordpress-block.txt, composed onto base.txt and deduplicated when /init or /doctor build a .gitignore.
  • scaffold.py prerequisite closure — requesting an override module now pulls in what it builds on automatically (wordpress adds php; wordpress-block adds wordpress and typescript).
  • audit.py structural checks — skills carry valid frontmatter (name matches directory), the lib/gitignore/ fragments name real modules, and lib/templates/ is present.

Changed

  • The coding standard's scaffolded files are now plugin-owned, verbatim. scaffold.py no longer writes a private manifest.json; a project is "scaffolded" exactly when agents.d/coding-standard/ holds module files. Drift is the plain content diff between a file and a fresh regeneration — there is no "locally edited" state and no edit protection, and /coding-standard --update reconciles every difference to the canonical content. --force now only overrides the project-root sanity check. AGENTS.md References are backticked. coder and the coding-standard/README prose are updated to match.
  • /orchestrate reads the coding standard from agents.d/coding-standard/ instead of the stale monolithic docs/coding-standards.md; sub-agents read general.md plus the module(s) for the language or framework they touch, and absence is detected as a missing agents.d/coding-standard/ directory (remedied by /coding-standard, not coder).
  • lib/gitignore-base.txt moved to lib/gitignore/base.txt (the release, push, and README references follow).

Removed

  • The coding-standard manifest.json and its machinery — the stored/on-disk/fresh three-way hashing, the generatedWith version label, and the create-only "refuse to clobber" exit code 2. The plugin owns the files, so the bookkeeping is unnecessary.

0.8.3 – 2026-06-20

Changed

  • coder standard — the prose of general.md and typescript.md was tightened across nine small edits, dropping filler and redundancy (real, genuinely, a stray below and instead, "is written to run" → "runs", and the like) from the connective text around a handful of rules. Every normative rule, code sample, table, directory tree, and identifier is byte-for-byte unchanged; only wording was trimmed.

0.8.2 – 2026-06-20

Fixed

  • /kntnt-code-skills:help <skill-name> always rendered the overview instead of the named skill's detail. The command template passed the skill name to scripts/help.py through $1, but Claude Code's slash-command substitution numbers positional arguments from zero — $0 is the first argument and $1 the second — so with a single argument $1 expanded to empty and the script received no skill name, falling back to the overview. The template now uses $ARGUMENTS, which carries the whole argument string (here the lone skill name), so help <skill-name> renders that skill's detail.

0.8.1 – 2026-06-20

Changed

  • README.md prose was reworked to British English typographic conventions: every em-dash () became a spaced en-dash (), the serial (Oxford) commas were dropped from lists of three or more items (one deliberately retained where the items' own internal or would otherwise read ambiguously), and the two American -ize spellings (organized, authorizes) became British -ise (organised, authorises). Prose wording and meaning are unchanged — only mechanics.

Fixed

  • A stale cross-reference in README.md pointed at a section called How coder is organized; the actual heading is How the coding standard is organised. The reference now matches the heading.

0.8.0 – 2026-06-20

Added

  • New /coding-standard skill — materialises the coding standard into a project as files and keeps them in sync, as a deliberate, explicitly-invoked operation. On a fresh project it creates agents.d/coding-standard/<module>.md (one on-demand file per applicable module) plus a private manifest.json, wires a ## References pointer to each into AGENTS.md, and bridges CLAUDE.md with @AGENTS.md. On an already-scaffolded project a bare invocation investigates instead of overwriting — it prints a read-only drift report covering which modules the standard has updates for, which files have been edited locally, and which languages have been added to or dropped from the project since — and --update reconciles the files to the project's current profile, adding new modules and removing dropped ones (pruning their References too), while leaving locally edited files untouched unless --force. Drift is detected from a per-module content hash recorded in the manifest, so the report tells an upstream change apart from a local edit.

Changed

  • The coder skill now loads the standard as a lazy, context-driven bootstrap instead of profiling the project and reading every matching module up front. A trigger loads only SKILL.md plus the standard's router (lib/coding-standard/_index.md, kept resident); each topic module is then pulled in only when the working context proves its axis applies, under a standing rule — before writing code in a language whose module you have not loaded, load it first, if one exists. So general.md loads as soon as code work begins and a language or framework module loads the moment its markers surface, including mid-session as new axes appear; a language with no module (Go, Ruby, plain CSS) falls back to general.md plus that language's own recognised standard rather than stalling. When the project has already scaffolded the standard into agents.d/coding-standard/ (via /coding-standard), coder loads nothing at all from the plugin and defers to the project's own on-demand AGENTS.md References, which the harness already follows when a task needs them — that snapshot is authoritative by design until /coding-standard --update is run, so deferring is both leaner and more correct than re-reading the plugin's lib/ copies; the one exception is an axis added to the project since it was scaffolded, where coder pulls that single module from lib/ and hints at --update. No rules changed — only when each module is loaded.
  • Standalone-script guidance was refactored so it lives where it loads when needed. The packaging mechanics — env-based shebangs per language (#!/usr/bin/env php / bun / -S uv run --script; Bash already carried its own), command-style (bin/, no extension, chmod +x) versus internal packaging, and inline-pinned single-file dependencies — moved out of coder's bootstrap into the language modules plus a shared Standalone-script packaging section in general.md, so the rules load whenever the language is in play rather than only when a script's language is unpinned. The opinionated language-selection policy (which language to reach for when none is given) was dropped from the plugin as out of scope for a coding standard, and the separate skills/coder/standalone-scripts.md file was removed.
  • Markdown sources across the plugin (the coder standard modules and its SKILL.md, skills/push/SKILL.md, commands/help.md, lib/changelog.md, and skills/coder/templates/claude-md-template.md) were reflowed so each paragraph is a single physical line instead of being hard-wrapped at a fixed column width. The prose is byte-for-byte identical once whitespace is normalized — only the intra-paragraph line breaks were removed, and code blocks, tables, blockquotes, and YAML frontmatter were left untouched — so the files render cleanly in viewers that show hard breaks and future edits produce one-line-per-paragraph diffs.
  • The coding standard is now split across two skills. coder applies it while writing, refactoring, or reviewing code and never writes any files into the project; the new /coding-standard skill materialises and updates it. coder's former behaviour of offering to scaffold the standard mid-task is gone — putting the standard into a project is now always an explicit /coding-standard invocation. The standard is still written as on-demand, per-axis files so an agent reads only the modules a task needs the moment it sets out to write or change code, with override modules (WordPress over PHP, Gutenberg blocks over TypeScript) carrying a generated prerequisite-and-precedence header.
  • The standard's source modules moved from skills/coder/ to lib/coding-standard/, beside a new shared _index.md (module table, detection signals, canonical order and override relationships) that both skills load to profile a project. The scaffolding engine moved from skills/coder/bin/scaffold to scripts/scaffold.py and gained a tests/test_scaffold.py suite.
  • Scaffolded files now live together in agents.d/coding-standard/ — one <module>.md per module, the coding- filename prefix dropped because the directory is the namespace — instead of flat agents.d/coding-<module>.md files, isolating the plugin's footprint from other contributors to agents.d/. AGENTS.md References are now pure pointers; the prerequisite and precedence wiring an override module needs lives only in that module's generated header.
  • The coder standard modules were tightened for the on-demand model — roughly 15% fewer words overall, and about 25% in general.md — by dropping definitions every coding agent already knows (SOLID, Red/Green/Refactor) and examples that merely re-illustrate an unambiguous rule, while preserving every normative rule and all code samples that disambiguate one.
  • The plugin version now lives in .claude-plugin/plugin.json alone; the per-skill version frontmatter field (previously carried by coder) has been dropped, and scripts/audit.py no longer checks it.

Removed

  • skills/coder/templates/claude-md-template.md — the scaffolder now generates the CLAUDE.md bridge and a minimal AGENTS.md inline, so the starter template is no longer used.

Fixed

  • .claude-plugin/marketplace.json — the bundled plugin's source was the string shorthand "./", which a local /plugin marketplace add accepts but Claude Cowork's remote sync rejects: Cowork clones the repository server-side and requires each plugin's source to be an object whose source field is one of github, url, or git-subdir, so adding the repository as a marketplace failed with REMOTE_SYNC_FAILED. The entry now uses the github object form ({ "source": "github", "repo": "Kntnt/kntnt-code-skills" }), so the Cowork marketplace add syncs successfully while the local /plugin marketplace add keeps resolving as before.

0.7.0 – 2026-06-18

Added

  • /orchestrate plan output (scripts/orchestrate.py) — the deterministic plan now records dependency provenance and integration guidance: a dependency_edges list giving each derived edge with the keyword it came from, a soft_notes list that surfaces non-blocking mentions (Relates to, "touches the same files as …") without turning them into edges, and a merge_required flag with a human-readable merge_note, raised whenever the in-scope graph has any cross-issue edge so a coupled set is integrated in merge mode rather than branching dependents off bare main. The fields are additive — the five existing top-level plan keys are unchanged, so the Workflow engine that consumes the plan is unaffected. skills/orchestrate/SKILL.md documents the new merge_required signal.

Fixed

  • /orchestrate engine (skills/orchestrate/orchestrate.workflow.js) — the Workflow engine read its run configuration as if args were an object, but the harness delivers args as a JSON string, so every field was undefined: the wave loop ran zero iterations and the run returned an empty success in milliseconds with no agents — a silent no-op indistinguishable from a legitimately empty plan. The engine now normalizes args once at entry (tolerating both a JSON string and an already-parsed object) and routes every configuration read through the normalized object; a misdelivered or empty plan now emits a prominent warning and a non-success status instead of masquerading as a clean run.
  • /orchestrate planner (scripts/orchestrate.py) — dependencies written as inline prose or a bold label (e.g. **Depends on:** #44, Blocked by: #44, #45, or a label followed by a bullet list of #N) were invisible to the planner, which recognized only a ## Blocked by heading; a coupled issue set then collapsed into a single wave that built dependents before their prerequisites and raced parallel edits on a shared file. The planner now derives edges from labelled, directional forms (Blocked by, Depends on, Depends upon, Requires, Needs) anywhere in an issue body or agent brief, producing a correct multi-wave, dependency-ordered plan. Vague, non-directional mentions and self-references are deliberately not treated as edges.

0.6.0 – 2026-06-16

Added

  • coder standard — a Defensive coding rule in general.md. A guard is written only where a real, present condition needs it (an untrusted boundary, a documented platform quirk, a contract a caller can plausibly break); defensive code against states the surrounding invariants already rule out — redundant null checks, try/catch around calls that cannot throw, re-validation of already-validated data, dead else branches, fallbacks for a self-constructed dependency — is forbidden, and a warranted guard names the threat it defends against in its // topic sentence.
  • .claude/settings.json — a tracked, curated settings file carrying the recommended Bash(uv:*) permission for contributors, while .gitignore is narrowed (.claude/* plus !.claude/settings.json) so that one shared file is versioned and per-user .claude/ state (settings.local.json, and the like) stays ignored.

Changed

  • coder standard — the TDD rule in general.md strengthened to require a demonstrable RED step (a test seen failing before the code that satisfies it exists, never inferred after the fact) and to define test-automation scope: automate every test that can meaningfully constrain behaviour at the lowest layer that captures it, escalating to integration or end-to-end only where a unit test cannot, and reserve human verification for the irreducibly subjective.
  • scripts/release.pypromote now writes version headings with a single canonical en-dash separator instead of detecting and mirroring the file's existing dash; the heading parser still accepts en-dash, em-dash, or hyphen so existing changelogs keep resolving. CHANGELOG.md headings were normalized from em-dash to en-dash to match.

0.5.0 – 2026-06-16

Added

  • /orchestrate skill (skills/orchestrate/SKILL.md) — an away-from-keyboard, multi-agent build that turns a project's ready-for-agent issues (from the to-issuestriage pipeline, each with an agent brief and a Blocked by graph) into implemented, independently verified, integrated code. A deterministic helper (scripts/orchestrate.py, with a pytest suite in tests/) computes the dependency graph and concurrency waves, checks red-before-green commit ordering, and folds the sub-agents' verdicts into the final report; a Workflow-tool engine (skills/orchestrate/orchestrate.workflow.js) drives the per-issue lifecycle — implement (test-first, demonstrating the red), independently verify (adversarial, only what the gates cannot), integrate in dependency order — with the Agent tool (optionally /goal) as the portable fallback. Every sub-agent runs inside the interactive session (subscription pool, never headless claude -p), with the strong model and high effort spent on implementers and verifiers rather than routing; it excludes ready-for-human issues, scales verification to risk, caps the fix↔verify loop, and stops short of releasing — bump, tag, and platform release stay with /release. Auto-discovered by scripts/help.py, so /help lists it without further wiring.

0.4.0 – 2026-06-02

Added

  • /release and /push slash commands (skills) that automate the "bump, commit, tag, push, release" workflow across any project. /release reconciles CHANGELOG.md against the real changes since the last release, bumps the version per Semantic Versioning across every place it lives, integrates a feature branch into the main branch by rebase and fast-forward, commits, tags vX.Y.Z, pushes, and creates the platform release (GitHub) with notes drawn from the changelog and, when the project ships one, the built user archive. /push is the routine companion — it reconciles the changelog, commits, and pushes the current branch, without bumping, tagging, or releasing. Both are deliberately general-purpose (WordPress plugins, Laravel, Bun, React, Python, …) and gate every irreversible step behind a single confirmation, with a "when in doubt, ask" triggering posture.
  • lib/ — shared text resources that skills include: changelog.md, the changelog-reconciliation procedure used by both /release and /push, and gitignore-base.txt, a universal .gitignore baseline offered on first run when a project has none.
  • scripts/release.py — a standalone PEP 723 script run via uv that performs the deterministic CHANGELOG mechanics: promote (promote [Unreleased] to a dated version heading, open a fresh [Unreleased], maintain the reference-link block, and emit the release-note body with heading levels shifted up one) and extract (re-emit an existing version's body when resuming a partial release).

Changed

  • README.md restructured around three audiences (users, then builders, then contributors) and expanded to document the /release and /push skills, the planned forge support — GitHub today via gh; GitLab via glab and the Gitea/Forgejo family (including Codeberg) via tea, detected by the remote's host rather than a fixed domain — and the new lib/ and scripts/release.py. Versioning now names both governing standards (Semantic Versioning and Keep a Changelog 1.1.0).

0.3.0 – 2026-05-29

Added

  • /help slash command (/kntnt-code-skills:help [skill-name]) and its renderer scripts/help.py — a manpage-style overview of the plugin's skills, or details for one. The command is disabled for model invocation, so it runs only when typed; scripts/help.py renders the whole block from .claude-plugin/plugin.json and each skills/<name>/SKILL.md, so the help text can never drift from the actual skills. The renderer is a standalone PEP 723 script run via uv.

Changed

  • The coder skill's frontmatter description rewritten to English only and broadened — it now triggers on any code-shaped task in any language or framework, with the listed languages explicitly non-exhaustive. The skill still triggers on prompts in any language; only the examples are now English.
  • bin/scaffold reverted from a Bun/TypeScript script to a command-style Python script run via uv (#!/usr/bin/env -S uv run --script shebang, PEP 723 inline metadata, standard-library only). Behaviour-equivalent to the TypeScript version — same flags, exit codes, and CANONICAL_ORDER.
  • scripts/audit.py is now a standalone PEP 723 script run via uv rather than a python3 shebang script: PEP 723 metadata pins requires-python, the deprecated typing.Callable import moved to collections.abc, and the source is ruff-formatted. The pre-commit hook and the audit GitHub Actions job invoke it with uv run, and its CANONICAL_ORDER matcher now tolerates the annotated Python declaration in bin/scaffold.
  • README.md updated throughout to reflect the Python scaffolder, the uv-run helper scripts, and the new /help command.

0.2.1 – 2026-05-29

Added

  • LICENSE (Apache License 2.0) and NOTICE — the project's licence text and the copyright / attribution statement that accompanies redistributions.
  • CONTRIBUTING.md — contribution-scope guidance: which kinds of changes are welcomed, which want an issue first, and which are better kept in a fork.
  • CHANGELOG.md — this file, reconstructed for the three prior releases from their GitHub release notes.
  • .pre-commit-config.yaml and .github/workflows/audit.yml — the audit runs as a pre-commit hook locally and as a GitHub Actions job on every push and PR.
  • .github/ISSUE_TEMPLATE/bug.md — a structured bug-report template (which module, which language/framework, input, observed vs expected).
  • scripts/audit.py — standard-library audit script. Verifies that plugin.json is well-formed and its version matches the latest changelog heading, that the topic-module files and bin/scaffold's CANONICAL_ORDER agree, and that the coder skill's frontmatter version tracks plugin.json.

Changed

  • License changed from MIT to Apache 2.0. The license field in .claude-plugin/plugin.json was MIT but no licence file ever shipped; the project now declares Apache 2.0 with a full LICENSE, a NOTICE, and matching CONTRIBUTING.md guidance.
  • README.md refreshed and restructured to mirror the kntnt-text-skills layout — added File structure, Versioning, Authoring rules with an audit checklist, Requirements, License, and About sections.
  • CI actions bumped to their Node 24 majors — actions/checkout@v4@v6 and actions/setup-python@v5@v6 — clearing the Node 20 deprecation warning. Removed a dead *.skill stanza from .gitignore that documented output of a package_skill.py not present in this repo.

0.2.0 – 2026-05-29

Changed

  • Converted the repo into an installable Claude Code plugin and renamed it to kntnt-code-skills. The manifest moved to .claude-plugin/plugin.json, a single-plugin catalog was added at .claude-plugin/marketplace.json, and all skill files were reorganized under skills/coder/. The plugin is now installable with the two-step marketplace flow (/plugin marketplace add Kntnt/kntnt-code-skills then /plugin install kntnt-code-skills@kntnt-code-skills); verified with claude plugin validate ..
  • README.md install instructions and component table updated to the new layout, plus a --plugin-dir local-development path.

0.1.1 – 2026-05-28

Changed

  • Documentation fixes in SKILL.md. The flow, step 3 dropped its inline canonical-order list (which had gone stale, omitting python and bash) and now points to step 4 and bin/scaffold's CANONICAL_ORDER as the single source of truth. Adding a new module, step 4 corrected the rule: a new module must always be added to the canonical order, because bin/scaffold validates every --include against that list and rejects unknown modules; a module's position only matters when it has override relationships with an existing module. No behaviour changes.

0.1.0 – 2026-05-28

Added

  • Standalone scripts convention — when a script is requested with no language given by context, choose by preference order (TypeScript on Bun by default), and package by target directory: command-style in bin/ (no extension, shebang, executable) versus internal (keep extension, explicit invocation).
  • python.md (uv + PEP 723, ruff, mypy/pyright) and bash.md (GNU Bash 5+, set -euo pipefail, shellcheck) modules, wired into the router, the detection step, and the scaffolder.
  • bin/scaffold — the scaffolder, rewritten from Python into a command-style Bun/TypeScript script that dogfoods the new convention. Behaviour-equivalent to the old scripts/scaffold.py and strict-typechecked.

Changed

  • general.md — the "latest stable version" rule gained an escape clause for projects and dependencies that require an earlier version; standalone scripts added to the no-prefix-needed list.
  • typescript.md — documents that Bun strips types at runtime, so type safety needs a separate tsc --noEmit pass.