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
/orchestratenow emits a continuous, out-of-band per-issue milestone heartbeat over a multi-hour run (#48, ADR-0006). Between #49's terminallanded/opened-PRmarker 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-tierreportersub-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), andparked #<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_markerand the widenedMarkerverb domain +parse_landed_markerinscripts/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).startedis posted only for an issue the preflight decided to dispatch — never for one skipped asalready-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 alandedmarker — #49's four preflight verdicts are unchanged and no falsealready-landedskip occurs.skills/orchestrate/orchestrate.workflow.jsgains thereporteragent,REPORTER_SCHEMA, the best-effortreportwrapper, the milestone templates, and the wave-loop /buildAndVerifyboundary dispatches;skills/orchestrate/SKILL.mddocuments 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*.pycover 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. Theparkedreason is now sanitised at format time in both mirrored sources (sanitize_parked_reasoninscripts/orchestrate.py,sanitizeReasonin the engine): collapsed to one line, reduced to an allowlist — which drops:so a reason can never reconstruct anorchestrate:prefix and smuggle a second marker that would parse back as alandedverb (a falsealready-landedskip), drops()so it cannot break the single-line grammar #50 reads, and drops quotes/backticks/$so it cannot inject into the reporter'sghcommand — 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 durableparkedheartbeat, not only the ones insidebuildAndVerify: the prerequisite-cascade park (a blocked dependent) and the loudlanded-marker-stalepark 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./orchestrateis 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 — WorkflowresumeFromRunIdis 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 — canonicalorchestrate: 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_markerand theMarkerdataclass inscripts/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 (preflightDecisioninlib/orchestrate/engine-helpers.mjs, mirrored byte-identically inline inskills/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), ordispatch. So even a naive relaunch scoped to the same issues re-implements nothing that landed.skills/orchestrate/SKILL.mdgains 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 itsrunId" 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 thealready-landed/already-open(rendered as "already complete") andlanded-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, andtests/test_orchestrate_workflow.pycover 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 newstatussubcommand sits besideplan/redgreen/reportinscripts/orchestrate.pyas 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), orparked(with the reason). All marker reading goes through the single-source-of-truthparse_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.mddocuments thegh issue list … | statusone-liner and itswatchvariant (both driven with--state allso a landed-and-closed issue still renders a done row) and the first-page comment cap the reader honours.tests/test_orchestrate_status.pyandtests/test_orchestrate_skill.pycover the four states, run scoping and the latest-run default, the queued fallback, and the SKILL↔renderer consistency binding, red-first.
/orchestrate plannow reads dependency edges from an issue's Agent Brief comment, not only its body (#51). The comment aboveHARD_EDGE_KEYWORDSalready promised the planner reads triage's inline**Depends on:** #Nlabels, butload_issuesfed onlyentry["body"]toparse_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_issuesnow runs the sameparse_dependenciesdiscipline over the body and every Agent Brief comment — selected by the sameAGENT_BRIEF_HEADING_REheading anchor_has_agent_briefuses, 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 aboveHARD_EDGE_KEYWORDSis corrected to name both sources. No new keywords and no brief-specific grammar: one shared parser, two sources, a union of results._has_agent_briefnow derives from the new_agent_brief_textshelper, giving one authoritative definition of "an Agent Brief comment."tests/test_orchestrate.pycovers 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 statusno 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'sstatusconsumer never checked it:load_status_universeattributed everyorchestrate: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 startedscoped to the placeholder run<runId>, demonstrated by the integration review that found this).parse_landed_markeris intentionally prose-tolerant, which was safe only because its prior consumer, preflight, has a second factor (landedSHAs must be ancestors of the default tip);statushas none. A new_marker_attributableguard in the consumer/attribution layer restores that second factor without touching the single-source-of-truth parser: a milestone marker whose embeddednumberdiffers from the hosting issue is another issue's marker quoted here and is dropped, and arun_idstill 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_runand stops a quotedopened PR #12, run <runId>(number-less, so unreachable by number attribution) from marking its hostdonethrough the any-run terminal→donefallback.tests/test_orchestrate_status.pycovers 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 withgit update-ref/git branch -f/git push .or by flippingcore.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 inINTEGRATE_SCHEMA—bareRepo(git rev-parse --is-bare-repository),defaultCheckedOut,headSha(git rev-parse HEADon the default after the fast-forward),featureSha(the feature-branch tip), andworktreeClean(git status --porcelainempty) — and a pure, unit-tested decision helper (landStrandedBlockerinlib/orchestrate/engine-helpers.mjs, mirrored byte-identically inline inskills/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, aHEADthat 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 ownintegratedflag — 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, andskills/orchestrate/SKILL.mddocuments the engine-side check.tests/test_orchestrate_workflow.pycovers the schema fields, the merge-gated engine call and its park, the prompt requirement, andlandStrandedBlocker's verdicts through node — red-first; the existing drift guard binds the two mirrored copies./orchestrate statusno longer regresses a landed-and-closed issue toqueuedon 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 issuequeued— silently defeating #50's own--state allamendment (which exists precisely so the board can show a done row) and wrongly answering the board's headline "was it done?".build_statusinscripts/orchestrate.pynow falls back, when an issue has no marker in the scoped run, to its latest terminaldonemarker from any run (landed/opened-pr, named by the newSTATUS_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 staysqueued.tests/test_orchestrate_status.pycovers 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 setcore.bare = trueto 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 nolandedmarkers posted. The merge-mode integrate prompt inskills/orchestrate/orchestrate.workflow.js(mirrored inskills/orchestrate/SKILL.md) now requires thegit merge --ff-onlybe run from the one worktree that actually holds the default branch — so ref and working tree advance together — and explicitly forbids settingcore.bare, reconfiguring the repository, or force-advancing the default withgit 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.pybinds the guard structurally, red-first.- The
doctor/inittest fixtures no longer leak the git environment and corrupt the real repository under the pre-commit hook (#52). Git hooks exportGIT_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-committestshook launched pytest, thegit_projectfixture's owngit init/add/commitran against the developer's real.git, corrupting its index and (via a straygit inithonouring the exportedGIT_DIR) flippingcore.bare = true; this was the upstream trigger of the integrate-time corruption fixed above (#48 × #50), and it had already forced--no-verifycommits twice in oneorchestraterun. A newtests/conftest.pyautouse fixture nowmonkeypatch.delenvsGIT_DIR,GIT_INDEX_FILE,GIT_WORK_TREE,GIT_OBJECT_DIRECTORY, andGIT_COMMON_DIRfrom the process environment for every test, so every git subprocess beneath pytest — the fixtures' own and the onesscripts/doctor.py/scripts/init.pyspawn when driven against temp projects — inherits a clean environment; the fixtures keep their-C <dir>targeting for ergonomics. A decoy-repo regression test intests/test_doctor.pylocks the isolation in: it seeds a throwaway repo, snapshots its.git/configand index bytes, exports the leak at the decoy exactly as the hook would, drives both a fixture-backed test and adoctor.maininvocation (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 thetestshookentryin.pre-commit-config.yamlnow scrubs the same variables withenv -u …before pytest starts, so even a future test that bypassesconftest.pystays isolated.pre-commit run --all-filesnow completes with the working repo's index and config intact — no--no-verifyneeded.
0.16.1 – 2026-07-21
README.mddrops the### Repository layoutsection (the ASCII directory tree and its prose walkthrough). Both merely restated whatlsshows 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## Developmentsection 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, whichtests/test_commit_push_release_docs.pybinds to reality — is preserved in the What you get intro, now stated as eight skills.
0.16.0 – 2026-07-21
- All eight skills now carry an
argument-hintfrontmatter field. Each skill'sSKILL.mdgains anargument-hintvalue describing the arguments the skill accepts, so a slash-command UI can show the expected arguments inline — the same conventioncommands/help.mdalready uses (argument-hint: [skill-name]). The values are grounded in each skill's own documented Arguments section:coderandinitaccept only the universal help gate ([help]);coding-standard[--update] [--dry-run] [help];commitandpush["message"] [--yes] [help];doctor[--yes] [help];release[minor|major|X.Y.Z] [--no-build] [--yes] [help]; andorchestratethe 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.
README.md,CONTRIBUTING.md, and.gitignorereconciled 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.mdis restructured to the canonical audience-layered shape — License/Latest-release badges under the title, a## Descriptionsection with### Key features/### The problem/### How this project helps,## Requirementsrestored before## Installation, the existing usage/architecture prose wrapped under## Usage, a## Questions, bugs, and feature requestssection 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## Changelogsection keeping the domain-specific SemVer policy as a### Versioningsubsection — with all existing tables, code fences, and manual-page links preserved.CONTRIBUTING.mdgains the## Behavioursection from the template..gitignoregains the missing baseline entries (*.py[cod],.venv/,*.egg-info/,dist/,build/, and an explicit.claude/settings.local.json).
0.15.0 – 2026-07-21
/orchestrateverifier 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 optionalsuggestedFixfield onVERDICT_SCHEMA's finding object inskills/orchestrate/orchestrate.workflow.js; it is rendered separately and marked advisory in thefixprompt, alongside the finding's owntitle/detailthat already reach it. Three guardrails bind the mechanism: (1) theverifyprompt 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) thefixprompt 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 (suggestedFixis deliberately not rendered there).tests/test_orchestrate_workflow.pycovers the schema field's optionality, the judge-firstverifywording, the advisoryfixrendering, and the re-verify exclusion, red-first.
/orchestrate's--levelladder is re-pitched so verification rigor saturates atM(ADR-0004, decision 1). Field experience: verifiers found many defects → many serial fix iterations → long wall-clock, and the defaultMwas rarely enough, so nearly every run was dialed up toL. 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'sLEVEL_RANKchanges 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_RANKunchanged), soM/L/XLall sit at the top tier — 3 focused lenses, 2 fix rounds — whileXS/Sstay 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 anXS/Sissue, sinceM+ 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 fromM, a deep-reasoning Opus·xhigh implementer atL, Fable-all-the-way atXL), the judge deliberately topping out at Fable·high notxhigh(Anthropic's Fable 5 guidance makeshighthe default andxhigh≈ 2× the tokens for marginal review gain). The inviolable rigor floor, the escalate-only risk model, and the--max-lenses/--max-fix-rounds/--proverrides are unchanged.skills/orchestrate/SKILL.md(both ladder tables, the honestly-rewritten cost model — defaultMnowN × 7 + 3, theXS/Sfast laneN × 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'sDEFAULT_LENSES/lensesForcomments are reconciled; ADR-0004 records the decision and cross-references ADR-0001 §4/§5 and ADR-0002 §4.tests/test_orchestrate.pyandtests/test_orchestrate_skill.pyare updated to the saturated ladder and two-tier cost model.
0.14.1 – 2026-07-20
/orchestrate's planner no longer manufactures a false-positiveBlocked byedge (and cycle) from a#Ninside aNone. (Related: #N)aside (#47). A/orchestrate --yesrun failed at plan time withdependency cycle among issues [34, 36]even though both issues declaredBlocked by: None— the planner's## Blocked bybranch ranISSUE_REF_RE.findallover the whole section body and hard-edged every#N, including the sibling references in the parenthetical(Related: …)prose note that followedNone.on the same line, so#34 → {35, 36}and#36 → {34, 35}produced a spurious #34↔#36 mutual block.scripts/orchestrate.pynow peels non-directional asides out of the section before reading edges: a newNONDIRECTIONAL_ASIDE_REmatches a parenthetical opened by a non-directional cue ((Related: …),(Relates to …),(See …),(See also …)) — the(Related:colon form the existingSOFT_NOTE_REdeliberately does not cover, the gap triage flagged — and the## Blocked bybranch strips those asides from the section body before extracting#Nedges, 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/- #Nbullet 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.pycovers 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-endbuild_wavesno-cycle outcome.
0.14.0 – 2026-07-20
-
/helpadopts the echo-manpage mechanism fromkntnt-wp-skills—docs/man/*.mdsource, verbatim echo, help-gate parity,model: haiku(#45).scripts/help.pypreviously 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 releaseproduced a paragraph, never a usage reference with real flags. The fix retrofits the reference model built for this inkntnt-wp-skills(itsdocs/design.md§12): one full manual page per skill atdocs/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.pyis now the echo-style renderer fromkntnt-wp-skills, essentially unchanged (only its docstring's plugin name differs): it reads.claude-plugin/plugin.jsonanddocs/man/*.mdand 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.mdnow emits the rendered Markdown without the old outer triple-backtick fence (which broke the manpages' own tables and fenced SYNOPSIS blocks) and setsmodel: haiku, since the turn is a pure verbatim echo. Every one of the eightSKILL.mdfiles gains a## 0. Help gatestep: onhelp/--help/-hit runsscripts/help.py <skill>, emits the result verbatim, and stops before anything else — so/release --helpand/kntnt-code-skills:help releasereach the same page.README.md's usage now links each skill's manual page instead of restating its flags (the/helpbullet, a new "full option reference" pointer, and the former "Release-skill arguments" flag list, now a link to the three manpages).tests/test_help.pycovers the renderer's overview/detail/unknown branches andmain()'s dispatch against a synthetic plugin-root fixture;tests/test_help_docs_consistency.pybinds the manpages to reality — every skill has one, every documented flag is grounded in that skill's ownSKILL.md(and every flagSKILL.mddefines is documented, so nothing real is silently dropped), every skill has a help gate, the overview carries every manpage's NAME line,commands/help.mdruns on haiku without the outer fence, and every READMEdocs/man/link resolves. -
general.mdgains 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 completenessrule inlib/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 tolib/coding-standard/_index.mdorscripts/scaffold.py— projects that have already run/coding-standardkeep their existingagents.d/coding-standard/general.mdsnapshot until their next/coding-standard --updatere-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. -
/orchestratecatches 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 inskills/orchestrate/orchestrate.workflow.js, both single shared constants interpolated rather than copy-pasted (mirroringAGENT_CONSTRAINTS): a newRIPPLE_REPORT_INSTRUCTION, folded into theimplementandfixprompts, 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 underassumptionswhich it updated and which it did not; a newCONSISTENCY_LENS_INSTRUCTION, folded into every lensverifydispatches, 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.mddocuments 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.pyandtests/test_orchestrate_skill.pycover the new constants' declaration, content, and interpolation (or deliberate exclusion) structurally, red-first. -
php.mdpins the declared PHP floor with PHPStan'sphpVersion, and warns against reaching for PHPCompatibility (#43). The PHP module required declaring a PHP floor (Requires PHPheader /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: pinphpVersionto the project's declared floor and keep the two in step — withphpVersionset, 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'stestVersionfor this purpose, stating the reason.wordpress.md's WordPress-specific tooling section also gains the companion note onversion_compare()bootstrap guards: oncephpVersionis pinned, PHPStan constant-folds such a guard;treatPhpDocTypesAsCertain: falsedoes not fix this, since that option governs PHPDoc-derived certainty rather than thephpVersion-derived narrowing at work, so the honest fix is a scoped@phpstan-ignorecomment on the one guard line that defends against a host loading the plugin outside the activation path. Out of scope per triage: mandating aphpVersionrange, any downstream CI wiring, and detecting an over-declared floor.tests/test_php_phpversion_floor.pyguards both modules structurally, red-first. -
php.mdandwordpress.mdname 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'sphpcs.xml.distwas 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 centralkntnt/coding-standardComposer package exists, the idea remaining a possible future issue.tests/test_coding_standard_phpcs_tooling.pyguards all of the above structurally, red-first.
/orchestrate's planning flow now assesses implicit issue relationships beyond the explicitBlocked byedge 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.mdstep 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'ssoft_notesarray 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 explicitBlocked byedge, 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--planpresents) alongside the reason for each, exactly like the existingno_briefflag — 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 planis unchanged — this is prose-level LLM-side planning guidance only.tests/test_orchestrate_skill.pycovers the new instructions, thesoft_notesinput, the confirm-gate surfacing, and the qualified wave-independence wording structurally.general.mdis 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 withphp.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.mdnow 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 BiomeLine width: 100is 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.pyguards 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.MultipleStatementAlignmentandWordPress.Arrays.MultipleStatementAlignmentare one-directional and can only ever demand alignment, so a project'sphpcs.xml.distmerely excludes them and aligned code passes the gate silently. Triage settled #38 as prose-only (no custom sniff, nokntnt/coding-standardpackage for now): the rule's own bullet inlib/coding-standard/general.mdnow 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.pyguards the wording and that this repository's own dogfoodedagents.d/coding-standard/general.mdnever 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, contradictinggeneral.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, naminggeneral.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.mdis unchanged — its TDD rule is referenced, not modified.tests/test_php_tooling_docs.pyguards 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.
/orchestratemerge 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 theimplementprompt 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 commonCHANGELOG.md, a shared module) degraded into a park generator. Three fixes inskills/orchestrate/orchestrate.workflow.js: (1) theimplementprompt now creates the feature branch fresh off the up-to-date default tip withgit checkout -B, mirroringintegrationHotfix, 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) theintegrateprompt 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 (reconcilerebases and resolves keeping both sides' concerns,reverifyReconcilere-verifies only that resolution as a single targeted agent, then it lands via the same linear fast-forwardintegratestep) repairs it, merge mode only, bounded bymaxFixRounds, parking only when the re-verify blocks or the cap is hit. A new pureisReconcilableConflict(record)helper (mirrored byte-for-byte inlib/orchestrate/engine-helpers.mjs, drift-guarded) decides eligibility from a newconflictflag the integrator threads onto a parked record viaINTEGRATE_SCHEMA.skills/orchestrate/SKILL.mdstep 4 documents the fresh-off-current-tip fork and the reconcile fallback.tests/test_orchestrate_workflow.pycovers thegit checkout -Bfork, the corrected integrate invariant, theconflictschema field and its threading, the worktree-isolatedreconcileand single read-onlyreverifyReconcileagents, the merge-gated wave-loop hook, and the bounded reconcile loop; theisReconcilableConflictbehaviour is exercised through node.lib/coding-standard/php.mdmandated first-class callable syntax unconditionally, which is wrong for WordPress hook callbacks (#42).$this->method(...)builds a freshClosureat the call site, and aClosure's hook id (_wp_filter_build_unique_id()) is tied to that unreachable instance, so no other code can everremove_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 foradd_action()/add_filter()callbacks, nor any registry where a callback must remain individually removable.lib/coding-standard/wordpress.mdgains 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.pyguards both modules structurally.lib/coding-standard/wordpress.mdsaid 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 themestyle.cssheader are not prose comments —get_file_data()parses them one field per line, with no continuation syntax, so wrapping a longDescription: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 itsField: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.pyguards all of the above structurally, red-first./release --yesnow 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--yesthe 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--yesargument description and step 7's confirmation-gate prose now state the uniform rule —--yessuppresses all interactive prompts, no exceptions — anddocs/man/release.md(reachable fromREADME.md's manpage link) documents it accordingly.tests/test_commit_push_release_docs.pyguards that the carve-out wording is gone and the uniform rule and the reporting requirement are stated (#44).
0.13.0 – 2026-07-14
- New
/commitskill — commit the working tree without pushing. The routine save operation, factored out as the innermost of the three release-workflow skills. It reconcilesCHANGELOG.md's[Unreleased]section against the real changes since the last release (vialib/changelog.md), then stages and commits the current branch (via the newlib/commit.md) behind a single confirmation gate, and stops — no push, no version bump, no tag, no branch integration, no platform release. Unlike/pushand/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/commitstops only when the working tree is genuinely clean. Its arguments mirror/push—"message"for an exact commit message,--yesto skip the gate. It triggers on/commitor an obvious commit-without-push request in any language;commit and pushand a barepushroute to/push, and an ambiguous barecommit(which might mean a rawgit commit) asks first.
/pushand/releasenow share one commit spine with/commit. The stage-and-commit mechanic — the.gitignoresafety net,git add -A && git commit, never bypassing commit hooks (--no-verify), and the commit-message default — was duplicated as prose in both/pushand/release. It is now factored intolib/commit.md, the sibling oflib/changelog.md, and all three release-workflow skills reference it:/releasesupplies its ownRelease 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.pyguards the shared spine: thatlib/commit.mdexists and all three skills reference it, that/commitnever runsgit push, and that the README's spelled-out skill counts track the actualskills/directories.
0.12.0 – 2026-07-14
/orchestratedecouples per-issue verification rigor from the--leveldial (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 plangains--max-lenses=N, mirroring--max-fix-roundsexactly (rejected on a negative value, per-run only, never a policy default): a purecap_lenses()post-step truncates each issue's level-and-risk-derivedlensespanel to at mostNafterderive_rigorruns, so it only ever lowers a panel, never raises one.N ≥ 1is a plain, floor-respecting cap;N = 0is 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 (aRisk:marker orrisk:*label), the panel still lands at0(the flag holds) and the plan'swarningsnames the issue for gate visibility rather than silently re-escalating it. Inskills/orchestrate/orchestrate.workflow.js,lensesFornow distinguishes an explicitly emptylensesarray (--max-lenses=0) from an absent one (which still falls back toDEFAULT_LENSES), andbuildAndVerifyreads 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 pureshouldEscalateInSitu(report, panel)helper (mirrored inlib/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 newinSituHazardfield and the panel is empty; on true,withInSituHazardfolds that hazard into a one-lensDEFAULT_LENSESpanel 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--prflag is the explicit conservative partner of--merge: precedence is explicit flag > declared policy marker > the conservative PR default,--prand--mergetogether is an error, and merge authority is never inferred.skills/orchestrate/SKILL.mddocuments both flags in §Arguments, the ADR-0003 §5 risk precedence and the--max-lenses=0use-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.mddocuments the two merge modi operandi (merge-policy: mergefor solo-on-mainvs. the PR default for multiple users, with--pr/--mergeas the per-run exceptions and where the policy marker is recorded) and the--level=XL --max-lenses=0 --mergequick-orchestrating idiom alongside the retroactive/code-review <base>idiom (ADR-0003 §7).docs/adr/0001-orchestrate-control-model.mdgains a back-reference noting §1/§5/§7 are amended by ADR-0003.tests/test_orchestrate.py,tests/test_orchestrate_workflow.py, andtests/test_orchestrate_skill.pycover 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
/orchestrategains the--levelambition 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 vocabularyXS | S | M | L | XL(defaultM). Because the engine is a deterministic Workflow script with no primitive to enumerate live models, the orchestrator (the session-model planning pass) resolves--levelinto a per-role(model, effort)against the harness's live model list and passes it to the engine asargs.roles = { judgment, implementer, mechanical }; the engine stores no model-name table and only applies what it is handed. Inskills/orchestrate/orchestrate.workflow.jsa new pureroleTuninghelper (tested source of truth inlib/orchestrate/engine-helpers.mjs, byte-identical inline copy drift-guarded) turns one role's resolution into anagent()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 readsconfig.roles(default{}) and spreads the fragment into each sub-agent: judgment ontoverify/reverifyFindings/integrationReview, implementer ontoimplement/fix/integrationHotfix, and mechanical ontointegrateand the teardown agent.skills/orchestrate/SKILL.mddocuments 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.pyguards the exported/drift-guarded helper, theconfig.rolesread, each role's spread, and the absence of any hardcoded model/effort literal, plus a behavioural node run ofroleTuningover a table;tests/test_orchestrate_skill.pyguards the §Arguments dial and the §Model-and-effort derivation, ladder, and guardrails structurally./orchestrateverification rigor is now derived from--leveland 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 plannow accepts--level(XS|S|M|L|XL, defaultM) and--max-fix-rounds, and emits each issue's verifier panel as alensesarray plus a run-levelmaxFixRoundscap 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 explicitRisk: high|medium|lowmarker in the issue body or Agent Brief — or an optionalrisk:*label — is read deterministically and escalates the rank escalate-only (the highest of level baseline and risk signal wins), so aRisk: highon anXSissue pulls just that issue to theXLtier; risk never lowers rigor below the level baseline, an inviolable floor holds at ≥ 1 lens and a fix-round floor of 1 (0reachable only via an explicit--max-fix-rounds=0), and an explicitRisk: lowcontradicted by a hazard label is surfaced in the plan'swarningsrather than silently applied. The lens count is settled in the helper; the engine'slensesForconsumes the array unchanged and the orchestrator tailors each lens's prose to the issue.tests/test_orchestrate.pycovers the derivation, the escalate-only max, the floor, the--max-fix-rounds=0route, and the disagreement warning./orchestrategains 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 atXS/S, balanced spec atM, goals + constraints atL, none atXL).skills/orchestrate/orchestrate.workflow.jsconsumes two newargsfields — a per-issueissues[].planstring and a run-levelimplementerMode ∈ { execute, balanced, autonomous }marker — and a new inlineplanOverlayhelper (mirrored byte-for-byte inlib/orchestrate/engine-helpers.mjsand drift-guarded) composes an additive "how" overlay for theimplementprompt 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 againstObject.prototypekey collisions) and applies toimplementonly;fixandintegrationHotfixare unchanged. The Agent Brief stays the authoritative what and the tests bind to the acceptance criteria, never to the plan.tests/test_orchestrate_workflow.pyguards the three framings, the mode selection, the plan interpolation, the untouchedfix/integrationHotfixprompts, and the clean no-plan degradation.
/orchestrateseparates--yesfrom merge authority (ADR-0001 §7).--yesnow 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--mergeor a once-per-project/global merge policy — and absent both the plugin default is PR, so--yesalone opens one pull request per issue and never walks tomain.skills/orchestrate/SKILL.mddocuments the separation in §Arguments and the operating contract, names where the merge policy is recorded and read (anorchestrate: merge-policy: merge|prmarker in the project'sAGENTS.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.pyguards the separation, the policy location, the safety-floor elements, and the gate wording structurally./orchestrateSKILL.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 to0rounds),--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-issueRisk: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-issueplanoverlay and the run-levelimplementerModeframing). The step-3 cost estimate is now level-aware — the per-issue agent count scales with the level's panel sizeP(1/1/1/2/3) and fix-round capF(1/1/1/2/2) asN × (P + F + 2), reducing to the leanN × 4 + 3at the defaultM— 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 enumerateimplementerModeand each issue'splanalongsideroles/lenses, and a stale "every sub-agent runs at the strong tier" claim is corrected to the level-derived tiers.tests/test_orchestrate_skill.pygains 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
/orchestrateconfirm 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.mdstep 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 formagents ≈ 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 settableargsknob with default 60000 tokens in step 4; the run'sbudget.total; a lowermaxFixRounds; 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 withgh issue view <n>(itsstate/closedfield), and explicitly does not trust agh issue listre-query, whose endpoint is eventually consistent and reads stale immediately after a close.tests/test_orchestrate_skill.pyguards the estimate formula, the lean-defaults wording, the slice size, the cap-above-threshold requirement, and the per-issuegh issue viewclosing rule structurally./orchestratenow 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. Inskills/orchestrate/orchestrate.workflow.js, after the wave loop and inside the teardowntry(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 newintegrationReviewagent reviews it: one read-only adversarial reviewer given a per-issue verifier's full rigor (never a token smoke test), carrying the sharedAGENT_CONSTRAINTS, returning theVERDICT_SCHEMA, and its clear decision routed throughblockingFindingsso 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-touchingintegrationHotfixagent (worktree-isolated per #14, bound byAGENT_CONSTRAINTSper #15) addresses only those findings test-first on a branch created fresh off the up-to-date default branch withgit checkout -B(resetting any stale ref a prior run left), lands through the existing linear rebase-fast-forwardintegratestep, and the combined diff is re-reviewed — capped by a documentedmaxIntegrationRounds(defaultmaxFixRounds, i.e. 1), with the hotfix branch tracked inbuiltBranchesso 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 anintegrationfield alongside the unchangedverdicts/parked.skills/orchestrate/SKILL.mdstep 5 describes the always-run, mode-aware review, andtests/test_orchestrate_workflow.pyguards 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.
/orchestrateleft 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 branchworktree-<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 therunIdfrom the Workflow launch, lists this run's own branches withgit branch --list "worktree-<runId>-*"and deletes each withgit branch -D, confined to the exactrunIdso 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 capturedrunIdis non-empty first so a degenerateworktree--*glob skips rather than force-deletes every run's scaffolding.tests/test_orchestrate_skill.pyguards the run-scoped prefix, therunIdconfinement, the scopedgit branch -D, and the preserved #14 teardown structurally./orchestratein 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--mergemode. In the conservative default PR modeintegratemerely opens a pull request and merges nothing, yetskills/orchestrate/orchestrate.workflow.jsstill added the issue to thelandedset 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 tolandedonly 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.mdnow documents the enforcement in themerge_requirednote and the wave-outcome paragraph, andtests/test_orchestrate_workflow.pyguards the merge-gatedlanded.addand the mode-aware park reason structurally, plus a behavioural run of the realunlandedPrerequisiteshelper over one dependency graph in both modes showing merge mode builds the chain while PR mode parks the dependent and cascades./orchestratenever threaded the feature branch onto the per-issue record, so integration sawundefined. Inskills/orchestrate/orchestrate.workflow.jsthetoRecordhelper built the recordbuildAndVerifyreturns by copyinggates/remaining_for_human/assumptions/blockersoff the implementer result but NOTimpl.branch— so every done or parked record carriedbranch: undefined. Downstream this broke two consumers that read the branch:integraterendered "Rebase branch undefined onto…" / "Open a pull request for branch undefined", and — worse — the mandatory finalintegrationReviewin 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.toRecordnow setsbranch: impl?.branch(optional-chained, so a null implementer parks cleanly rather than throwing), threading the real branch onto every record sointegrateandintegrationReviewreceive it. A behavioural test intests/test_orchestrate_workflow.pyextractstoRecordand runs its real logic through node, asserting a done record actually carries its branch./orchestratetreated the Agent Brief as mandatory, so an issue with none could not be built. Theimplement,verify, andreverifyFindingsprompts inskills/orchestrate/orchestrate.workflow.jstold 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 optionalcommentsfield (a list of{"body": ...}objects, tolerating bare strings and an absent field) and flags issues lacking a brief: anIssue.no_briefboolean surfaced per issue in the plan'sissues[]and as a convenience top-levelissues_without_brieflist, 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.mdstep 2 fetchescommentsin thegh issue listcommand and documents that flagged issues are still built from their body + acceptance criteria;tests/test_orchestrate.pyandtests/test_orchestrate_workflow.pycover the flag, the heading anchor, and the fallback wording.
- The project's quality gate now enforces
ruff format --checkandmypyeverywhere it runs. CI (.github/workflows/audit.yml) and pre-commit (.pre-commit-config.yaml) ran onlyscripts/audit.py+pytest, so code could drift on formatting or typing while the gate stayed green — the/orchestrateengine'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, anduv run scripts/audit.py— added as steps in the CI job and as locallanguage: systemhooks in pre-commit, alongside the existing audit + tests.CONTRIBUTING.mdnow 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 intoruff formatcompliance — a pureruff formatresult, whitespace and style only, no behaviour change;mypy scripts testswas already clean. /orchestrateverification 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. Inskills/orchestrate/orchestrate.workflow.jsthe 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-issuelensesoverride still lets planning raise a genuinely high-risk issue to 2–3 focused lenses.maxFixRoundsnow 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 targetedreverifyFindingsagent (a read-only reviewer bound by the sharedAGENT_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.mddocuments the single broad reviewer, the default cap of 1, and the fixed-findings re-verify, andtests/test_orchestrate_workflow.pyguards each structurally.
-
/orchestrateintegrated in an end-of-run batch and could leave feature branches non-linear. Inskills/orchestrate/orchestrate.workflow.jsthe 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 accrueMerge 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. Theintegratemerge 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.mdnow describes serial per-issue dispatch with immediate linear integration, andtests/test_orchestrate_workflow.pyguards the linear-integration clause, the per-issue immediate integration, and the preserved empty-plan and budget-floor behaviour structurally. -
/orchestratesub-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 agit reset --hard <base>issued while an agent was on a feature branch silently reset that branch and discarded its commits.skills/orchestrate/orchestrate.workflow.jsnow defines the forbidding text once in a singleAGENT_CONSTRAINTSconstant — 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), NEVERgit reset --hardon 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 withgit checkout -- ., never delete untracked files) — and interpolates that same constant into theimplement,fix, andverifyprompts.integrateis deliberately excluded because merging/pushing is its job.skills/orchestrate/SKILL.mdstates the rule in its operating contract, andtests/test_orchestrate_workflow.pyguards the single definition, the per-prompt reuse, integrate's exclusion, and the constant's content structurally. -
/orchestratesub-agents shared one working tree and left worktrees behind. Inskills/orchestrate/orchestrate.workflow.jsthefixagent 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 (implementandfix, and any future salvage/hotfix agent) now carriesisolation: 'worktree'; the read-only verifiers need none andintegratestays un-isolated as the sole mutator of the real default branch. Because a worktree-isolatedfixlands in a fresh worktree while its target branch is still checked out in the implementer's persisted one, thefixprompt 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 atry/finallythat fires on both the clean-completion and the parked/blocked paths, dispatches a non-isolated teardown agent that removes only those run-created worktrees withgit worktree remove --force(which keeps the branch ref) followed bygit worktree prune— never deleting a branch or resetting, so no branch ref is lost.tests/test_orchestrate_workflow.pyguards the isolation and teardown constructs structurally. -
/orchestrateengine could not launch.skills/orchestrate/orchestrate.workflow.jsdeclared three top-level exports (meta,normalizeArgs,planIsEmpty), but the Workflow harness tolerates only a single leadingexport const metaand rejects any other top-levelexport/importwith aSyntaxErrorat launch — so the skill's preferred execution path was dead on arrival.normalizeArgsandplanIsEmptyare now plain internalconsts, leavingexport const metaas the only top-level export and no top-levelimport. Their logic is mirrored byte-for-byte in a new importable modulelib/orchestrate/engine-helpers.mjsthat is unit tested, andtests/test_orchestrate_workflow.pynow guards the single-export constraint structurally as well.skills/orchestrate/SKILL.mddocuments the constraint.
0.9.0 – 2026-06-26
/initskill — bootstraps a new project to the Kntnt baseline in one pass:git init, theAGENTS.md/CLAUDE.mdskeleton (viakntnt-skills:agents-md --force), the coding standard scaffolded intoagents.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 inscripts/init.py(gitignore,templates, andlicensecommands), covered bytests/test_init.py./doctorskill — init's idempotent reconciler. Deterministic checks inscripts/doctor.py(git state,.gitignorecoverage, the coding standard's home and sync, the licence/NOTICE pairing) emit JSON findings; a read-only Workflow (skills/doctor/doctor.workflow.js) checks whetherAGENTS.md, theagents.d/files, and the README still match the real code. It applies only the fixes you select (--yesapplies all) and never commits. Covered bytests/test_doctor.py.lib/templates/— generic, tokenisedREADME.md,CHANGELOG.md,CONTRIBUTING.md, andNOTICEthat/initrenders. The README embodies the audience-layered structure (Users → Extenders → Contributors) with the fixed boilerplate blocks;CONTRIBUTINGcarries a licence-adaptive inbound-licensing paragraph and a behavioural-expectations line.lib/gitignore/per-module fragments —php.txt,typescript.txt,python.txt, andwordpress-block.txt, composed ontobase.txtand deduplicated when/initor/doctorbuild a.gitignore.scaffold.pyprerequisite closure — requesting an override module now pulls in what it builds on automatically (wordpressaddsphp;wordpress-blockaddswordpressandtypescript).audit.pystructural checks — skills carry valid frontmatter (namematches directory), thelib/gitignore/fragments name real modules, andlib/templates/is present.
- The coding standard's scaffolded files are now plugin-owned, verbatim.
scaffold.pyno longer writes a privatemanifest.json; a project is "scaffolded" exactly whenagents.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 --updatereconciles every difference to the canonical content.--forcenow only overrides the project-root sanity check.AGENTS.mdReferences are backticked.coderand thecoding-standard/READMEprose are updated to match. /orchestratereads the coding standard fromagents.d/coding-standard/instead of the stale monolithicdocs/coding-standards.md; sub-agents readgeneral.mdplus the module(s) for the language or framework they touch, and absence is detected as a missingagents.d/coding-standard/directory (remedied by/coding-standard, notcoder).lib/gitignore-base.txtmoved tolib/gitignore/base.txt(therelease,push, and README references follow).
- The coding-standard
manifest.jsonand its machinery — the stored/on-disk/fresh three-way hashing, thegeneratedWithversion 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
coderstandard — the prose ofgeneral.mdandtypescript.mdwas tightened across nine small edits, dropping filler and redundancy (real,genuinely, a straybelowandinstead, "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
/kntnt-code-skills:help <skill-name>always rendered the overview instead of the named skill's detail. The command template passed the skill name toscripts/help.pythrough$1, but Claude Code's slash-command substitution numbers positional arguments from zero —$0is the first argument and$1the second — so with a single argument$1expanded 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), sohelp <skill-name>renders that skill's detail.
0.8.1 – 2026-06-20
README.mdprose 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-izespellings (organized, authorizes) became British-ise(organised, authorises). Prose wording and meaning are unchanged — only mechanics.
- A stale cross-reference in
README.mdpointed at a section called Howcoderis organized; the actual heading is How the coding standard is organised. The reference now matches the heading.
0.8.0 – 2026-06-20
- New
/coding-standardskill — 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 createsagents.d/coding-standard/<module>.md(one on-demand file per applicable module) plus a privatemanifest.json, wires a## Referencespointer to each intoAGENTS.md, and bridgesCLAUDE.mdwith@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--updatereconciles 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.
- The
coderskill 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 onlySKILL.mdplus 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. Sogeneral.mdloads 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 togeneral.mdplus that language's own recognised standard rather than stalling. When the project has already scaffolded the standard intoagents.d/coding-standard/(via/coding-standard),coderloads nothing at all from the plugin and defers to the project's own on-demandAGENTS.mdReferences, which the harness already follows when a task needs them — that snapshot is authoritative by design until/coding-standard --updateis run, so deferring is both leaner and more correct than re-reading the plugin'slib/copies; the one exception is an axis added to the project since it was scaffolded, wherecoderpulls that single module fromlib/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 ofcoder's bootstrap into the language modules plus a shared Standalone-script packaging section ingeneral.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 separateskills/coder/standalone-scripts.mdfile was removed. - Markdown sources across the plugin (the
coderstandard modules and itsSKILL.md,skills/push/SKILL.md,commands/help.md,lib/changelog.md, andskills/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.
coderapplies it while writing, refactoring, or reviewing code and never writes any files into the project; the new/coding-standardskill 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-standardinvocation. 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/tolib/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 fromskills/coder/bin/scaffoldtoscripts/scaffold.pyand gained atests/test_scaffold.pysuite. - Scaffolded files now live together in
agents.d/coding-standard/— one<module>.mdper module, thecoding-filename prefix dropped because the directory is the namespace — instead of flatagents.d/coding-<module>.mdfiles, isolating the plugin's footprint from other contributors toagents.d/.AGENTS.mdReferences are now pure pointers; the prerequisite and precedence wiring an override module needs lives only in that module's generated header. - The
coderstandard modules were tightened for the on-demand model — roughly 15% fewer words overall, and about 25% ingeneral.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.jsonalone; the per-skillversionfrontmatter field (previously carried bycoder) has been dropped, andscripts/audit.pyno longer checks it.
skills/coder/templates/claude-md-template.md— the scaffolder now generates theCLAUDE.mdbridge and a minimalAGENTS.mdinline, so the starter template is no longer used.
.claude-plugin/marketplace.json— the bundled plugin'ssourcewas the string shorthand"./", which a local/plugin marketplace addaccepts but Claude Cowork's remote sync rejects: Cowork clones the repository server-side and requires each plugin'ssourceto be an object whosesourcefield is one ofgithub,url, orgit-subdir, so adding the repository as a marketplace failed withREMOTE_SYNC_FAILED. The entry now uses thegithubobject form ({ "source": "github", "repo": "Kntnt/kntnt-code-skills" }), so the Cowork marketplace add syncs successfully while the local/plugin marketplace addkeeps resolving as before.
0.7.0 – 2026-06-18
/orchestrateplan output (scripts/orchestrate.py) — the deterministic plan now records dependency provenance and integration guidance: adependency_edgeslist giving each derived edge with the keyword it came from, asoft_noteslist that surfaces non-blocking mentions (Relates to, "touches the same files as …") without turning them into edges, and amerge_requiredflag with a human-readablemerge_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 baremain. 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.mddocuments the newmerge_requiredsignal.
/orchestrateengine (skills/orchestrate/orchestrate.workflow.js) — the Workflow engine read its run configuration as ifargswere an object, but the harness deliversargsas a JSON string, so every field wasundefined: 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 normalizesargsonce 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./orchestrateplanner (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 byheading; 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
coderstandard — a Defensive coding rule ingeneral.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/catcharound calls that cannot throw, re-validation of already-validated data, deadelsebranches, 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 recommendedBash(uv:*)permission for contributors, while.gitignoreis 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.
coderstandard — the TDD rule ingeneral.mdstrengthened 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.py—promotenow 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.mdheadings were normalized from em-dash to en-dash to match.
0.5.0 – 2026-06-16
/orchestrateskill (skills/orchestrate/SKILL.md) — an away-from-keyboard, multi-agent build that turns a project'sready-for-agentissues (from theto-issues→triagepipeline, each with an agent brief and aBlocked bygraph) into implemented, independently verified, integrated code. A deterministic helper (scripts/orchestrate.py, with a pytest suite intests/) 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 headlessclaude -p), with the strong model and high effort spent on implementers and verifiers rather than routing; it excludesready-for-humanissues, scales verification to risk, caps the fix↔verify loop, and stops short of releasing — bump, tag, and platform release stay with/release. Auto-discovered byscripts/help.py, so/helplists it without further wiring.
0.4.0 – 2026-06-02
/releaseand/pushslash commands (skills) that automate the "bump, commit, tag, push, release" workflow across any project./releasereconcilesCHANGELOG.mdagainst 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, tagsvX.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./pushis 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/releaseand/push, andgitignore-base.txt, a universal.gitignorebaseline offered on first run when a project has none.scripts/release.py— a standalone PEP 723 script run viauvthat 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) andextract(re-emit an existing version's body when resuming a partial release).
README.mdrestructured around three audiences (users, then builders, then contributors) and expanded to document the/releaseand/pushskills, the planned forge support — GitHub today viagh; GitLab viaglaband the Gitea/Forgejo family (including Codeberg) viatea, detected by the remote's host rather than a fixed domain — and the newlib/andscripts/release.py. Versioning now names both governing standards (Semantic Versioning and Keep a Changelog 1.1.0).
0.3.0 – 2026-05-29
/helpslash command (/kntnt-code-skills:help [skill-name]) and its rendererscripts/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.pyrenders the whole block from.claude-plugin/plugin.jsonand eachskills/<name>/SKILL.md, so the help text can never drift from the actual skills. The renderer is a standalone PEP 723 script run viauv.
- The
coderskill's frontmatterdescriptionrewritten 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/scaffoldreverted from a Bun/TypeScript script to a command-style Python script run viauv(#!/usr/bin/env -S uv run --scriptshebang, PEP 723 inline metadata, standard-library only). Behaviour-equivalent to the TypeScript version — same flags, exit codes, andCANONICAL_ORDER.scripts/audit.pyis now a standalone PEP 723 script run viauvrather than apython3shebang script: PEP 723 metadata pinsrequires-python, the deprecatedtyping.Callableimport moved tocollections.abc, and the source is ruff-formatted. The pre-commit hook and theauditGitHub Actions job invoke it withuv run, and itsCANONICAL_ORDERmatcher now tolerates the annotated Python declaration inbin/scaffold.README.mdupdated throughout to reflect the Python scaffolder, the uv-run helper scripts, and the new/helpcommand.
0.2.1 – 2026-05-29
LICENSE(Apache License 2.0) andNOTICE— 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.yamland.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 thatplugin.jsonis well-formed and itsversionmatches the latest changelog heading, that the topic-module files andbin/scaffold'sCANONICAL_ORDERagree, and that thecoderskill's frontmatter version tracksplugin.json.
- License changed from MIT to Apache 2.0. The
licensefield in.claude-plugin/plugin.jsonwasMITbut no licence file ever shipped; the project now declares Apache 2.0 with a fullLICENSE, aNOTICE, and matchingCONTRIBUTING.mdguidance. README.mdrefreshed and restructured to mirror thekntnt-text-skillslayout — 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→@v6andactions/setup-python@v5→@v6— clearing the Node 20 deprecation warning. Removed a dead*.skillstanza from.gitignorethat documented output of apackage_skill.pynot present in this repo.
0.2.0 – 2026-05-29
- 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 underskills/coder/. The plugin is now installable with the two-step marketplace flow (/plugin marketplace add Kntnt/kntnt-code-skillsthen/plugin install kntnt-code-skills@kntnt-code-skills); verified withclaude plugin validate .. README.mdinstall instructions and component table updated to the new layout, plus a--plugin-dirlocal-development path.
0.1.1 – 2026-05-28
- Documentation fixes in
SKILL.md. The flow, step 3 dropped its inline canonical-order list (which had gone stale, omittingpythonandbash) and now points to step 4 andbin/scaffold'sCANONICAL_ORDERas 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, becausebin/scaffoldvalidates every--includeagainst 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
- 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) andbash.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 oldscripts/scaffold.pyand strict-typechecked.
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 separatetsc --noEmitpass.