Skip to content

iap/pstack

v0.15.5MIT

if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence.

Changelog

All notable changes to the pstack-hermes port tooling will be documented in this file.

[Unreleased]

Changed

  • docs/agent/review-ops.md rewritten as an explicit review loop: Qodo first, then manual CodeRabbit (.coderabbit.yaml disables auto-review), SHA-bound "clean", finding-vs-recommendation triage, GraphQL-first thread tooling with a documented REST fallback (list/reply only; resolution stays GraphQL), a per-round ledger, and a loop budget with escalation.
  • docs/agent/review-ops.md corrected the Qodo contract: Qodo reviews the open PR automatically and is not re-run per push, so its review commit is recorded but not treated as a current-head verdict; CodeRabbit remains the SHA-bound per-head gate, and the one-request-per-SHA rule is explicitly CodeRabbit-only. Agents in the automatic setup must not post /agentic_review.
  • New docs/agent/dev-ops.md (router rows in AGENTS.md and docs/agent/README.md): scope discipline (no drive-by edits; out-of-scope work becomes an issue or separate PR) and parallel-work rules (parallel reads/triage, one writer per file, never parallelize rebase/rebuild/push).
  • Agent docs consolidated: AGENTS.md is now a short always-on contract plus a router; the work loop, hermes vocabulary table and verify prose live only under docs/agent/. Root-layout drift removed from AGENTS.md, CONTRIBUTING.md, the PR template and tooling help text (pstack/ subdirectory paths, $PWD/pstack doctor target, atomic-swap claim), and README.md is correctly classified as converter output.
  • tools/check_docs.py (+ test_check_docs.py, CI lint-and-tests step): gates router links, docs/agent/layout.md vs .gitattributes, UPSTREAM_PIN declarer lists vs check_pins.py, stale pstack/ paths, and publish-dist copy sources. Added to the local verify block.
  • Drop after-install.md: converter no longer emits it; first-run guidance stays in package README / docs/USING.md. Publish/release/CI package lists and root-layout obsolete cleanup updated. Next convert refresh clears the stale provenance fix line.
  • Agent docs job flow (hermes-guide style): short always-on AGENTS.md; deep ops under docs/agent/ only (layout, convert-adapt, upstream-drift, review-ops + router). Removed redundant docs/AGENTS.md and the docs/RUNBOOK-upstream-drift.md stub. One owner per fact: convert/verify in convert-adapt, layout paths in layout, CONTRIBUTING keeps Setup/Actions/PR/release. Root-layout facts corrected (skills/ / config/ at repo root; three UPSTREAM_PIN declarers).
  • Restructure round for the native-hermes install model: the README leads with the native hermes plugins install/update path (development tree now on master), the issue forms are project-only — the dual-load platform dropdown is replaced by an install-route field and the upstream-method contact link now points at this repo's user guide — and docs/MODELS.md documents the provisional upstream-name → hermes-default model mapping (code/judgment/reviewer roles) alongside the shipped panel.
  • The master rename is wired end to end: CI and publish triggers filter on master, the drift-watcher scheduler stub dispatches ref=master (source asset and dist copy), and the CONTRIBUTING/AGENTS install-route references are updated. The dist default-branch shims are now documented as a set — scheduler, labeler, dependabot — with their re-bootstrap sources.

Fixed

  • tools/convert.py rewrites stale pstack/skills/... references to skills/... for the repo-root layout and records the count in provenance; tools/assets/hermesbot/SKILL.md no longer duplicates the webhook-subscribe step. Both had been hand-edited in the generated tree, so a rebuild reverted them. The copied Comment Sicko persona now goes through the normal text pass, so a Windows clone with autocrlf=true cannot ship CRLF.
  • Rebuilt the committed package from pin c47b1284: drops the two upstream-removed skills/how/references/ prompts, picks up the current README install/update wording, and matches the converter exactly again.
  • dist_checkout_check.py --source-tree no longer requires the repository-root .gitattributes to be byte-identical to the legacy dist asset; the root rule carries linguist-generated metadata, so the CI build job could never pass. It also stages only the paths publish-dist.yml ships, since tooling, docs and .venv were tripping the untracked-file refusal. The autocrlf clone remains the assertion.
  • test_release_delivery.py fixture no longer deletes the orphan tree and then reads the files back: git checkout --orphan already keeps the package, so only the provenance file is force-removed and rewritten.
  • tools/check_action_inputs.py no longer dies on a stale Windows WinINET registry proxy (SSL: UNEXPECTED_EOF_WHILE_READING). The default opener now uses environment proxies only (--system-proxy opts back in), transient TLS/connection/5xx failures retry with backoff, and --transport auto falls back to gh api when gh is available. Unresolved actions remain a tool error (exit 2), never a pass. The gh fallback now takes the ref from the parsed uses: reference instead of re-splitting the raw URL, so refs containing / (e.g. feature/windows) resolve to the right file.
  • tools/check_docs.py closes review gaps: stale root generated-file paths (pstack/plugin.json, pstack/README.md, pstack/.gitattributes) are flagged like the subdirectory paths; a bare pstack/ package path is rejected in AGENTS.md / CONTRIBUTING.md while upstream-clone references stay legal; publish-dist copy sources inside an inline conditional (if [ -d assets ]; then cp -a assets "$dist/"; fi) are checked; AGENTS.md joins the UPSTREAM_PIN declarer scan; and every linguist-generated marker must appear in a generated/copied layout.md row.
  • LICENSE restored to every generated-surface list (AGENTS.md, CONTRIBUTING.md, PR template, docs/agent/layout.md); tools/convert.py writes it and .gitattributes marks it generated, but the rewrite had dropped it.
  • AGENTS.md restores the package-identity guardrail dropped in the rewrite: keep iap/pstack-hermes, never create or rename a repository for an install alias (#100), and update mechanics live in the package README. The .github/ row no longer hard-codes the declarer count.
  • CONTRIBUTING.md corrected to the root layout and to three UPSTREAM_PIN declarers (upstream-drift-watch was removed); the release wording no longer pins a release line.
  • Labeler restored after the default-branch flip: pull_request_target fires only from the default branch, so it had silently stopped labeling PRs. dist now carries the labeler workflow and its config (the action resolves configuration-path via the API at the default-branch ref - the first shim run 404'd without it) plus the dependabot config (target-branch: master). Verified live: a probe PR received its labels again, and the drift-watcher stub dispatches ref=master.
  • labels-bootstrap could never succeed: the job has no checkout, so its gh label calls failed with "not a git repository" (both recorded runs failed the same way). It now passes GH_REPO explicitly, so the ensure-labels recovery path works.

[0.4.4] - 2026-10-06

Added

  • tools/check_action_inputs.py + CI step: fails when a step passes a with: key the pinned action does not declare. The runner only warns on such a key (Unexpected input(s)) and silently ignores the value - the class behind the syncLabels / sync-labels defect fixed above. Verified as a genuine gap before adding the gate: with the defect reintroduced, actionlint exits 0 and the full pytest suite passes unchanged. Resolves local actions from disk and pinned remote actions from raw.githubusercontent.com; a fetch failure is a tool error, never a pass. Unit tests inject the fetch, so they run offline; the CI step is the only networked consumer.
  • .github/dependabot.yml: weekly grouped updates for the github-actions ecosystem (updates both the pinned SHA and the # vN comment) and for uv (pyproject.toml + uv.lock). The repo pins everything; with no update bot the pins silently rot, which was the largest structural gap in the workflow layer.
  • tools/check_pins.py + CI step: fails when UPSTREAM_PIN is missing, duplicated, malformed or out of step across ci.yml / release.yml / publish-plugin.yaml / upstream-drift-watch.yml. The lock-step rule was documented in two files and enforced by none; this makes it machine-checked and de-risks the re-pin procedure.
  • tools/check_action_shell.py + CI step: shellchecks every composite action's run: blocks. actionlint does not lint .github/actions/*/action.yml steps (verified against 1.7.12 by injecting a deliberate shell error), so the shared bash had no static check - a class that had already produced a real bug (see Fixed).
  • CI also gains: concurrency (superseded PR runs cancel; non-PR runs take a per-run group so every main push keeps its full run - tightened after the review below, see Fixed), timeout-minutes on every job, and 14-day retention on the build artifacts.
  • .github/actions/drift-issue-upsert/: shared composite action for the idempotent issue-by-title upsert both drift watchers need; the two callers' copies had already drifted apart (different jq expressions for the same intent).

Changed

  • Repository Actions policy tightened (settings, not files): allowed actions are now an allowlist - GitHub-owned actions plus astral-sh/setup-uv@* and oven-sh/setup-bun@* - with sha_pinning_required still on, and the default GITHUB_TOKEN can no longer create or approve pull requests. Introducing any other action now requires a deliberate settings change. CONTRIBUTING documents the policy and the "add to the allowlist first" rule; its UPSTREAM_PIN lock-step bullet is corrected from three files to four (publish-dist.yml was missing after the publisher rename).
  • Single-source installs. The package now installs from this one repository: the built tree is published to its dist branch, which is the repository's default branch, so hermes plugins install iap/pstack-hermes works root-level and hermes plugins update pstack updates natively. The separate dist repo is retired; no second repository is ever published. (A main-subdir install of pstack/ remains as a legacy pinned-style alternative.) dist also carries a scheduler stub so the weekly drift watchers keep firing despite the default-branch flip; manual recovery for dispatch-only workflows is gh workflow run <file> --ref main.
  • Re-pin to upstream c47b1284 (pstack v0.15.5); pstack/ rebuilt. Every converter anchor was re-anchored or pruned against the new upstream text (36 dead anchors at the new pin -> 0; T8-T14 + delegation), F16 retired (upstream now ships the slug-agnostic lane check itself), reflect's 4-column reviewer table adopted with the upstream budget key in the setup-written panel shape, and the two new upstream principles ported (principle-attack-the-premise, principle-test-behavior-not-implementation) - 45 -> 47 skills. Prose model defaults refreshed (grok-4.7-xhigh-fast, claude-opus-5-5-max); the shipped panel keeps its vendor-qualified verified slugs. UPSTREAM_PIN bumped in all four workflows (lock-step enforced by tools/check_pins.py); details and the triage correction (no skills/orchestrate ever existed) tracked in issue #94.
  • Docs / agent policy (Phase A, no package rebuild): AGENTS.md and docs/USING.md codify the stock-skills write surface — Hermes self-improvement (skill_manage / learning) targets local skills, skill taps, or a dist fork, not stock plugin skills. Stack forge freeze: Graphite (gt) default, GitHub (gh) fallback, GitLab (glab) halted. README Known limitations aligned (was incorrectly describing Graphite as optional-only).

Fixed

  • Dev-tree README skill counts corrected: the skills bullet said 45 (24 + 21 principle-*) while the generated package README and .build-provenance.txt already said 47 (24 + 23) after the v0.15.5 re-pin. Docs-only; pstack/ untouched (#80).
  • Label sync never ran (found while auditing every with: key against each action's declared inputs - the only such mismatch in the repo). labeler.yml passed syncLabels: true, but the input id is sync-labels: the runner logs Unexpected input(s) 'syncLabels' and the action falls back to its declared default (off), so stale labels were never removed on synchronize despite the step comment promising exactly that. Corrected to sync-labels (kebab-case; both v5 and the v7 that #69 landed declare it). Evidence: run 37394726141's log records the ignored input verbatim.
  • .github/actions/build-package was outside dependabot's scan. The root github-actions entry covers .github/workflows plus a repo-root action.yml, not .github/actions/*/action.yml - observed when #69's setup-uv bump updated every workflow and skipped the shared action, leaving its pin behind and on a path to silent rot, the exact failure mode dependabot.yml exists to prevent (AGENTS.md: pins are "bumped by dependabot, not by hand"). Added a directory-scoped entry (the actions/github-script pattern) and caught the pin up to the SHA the workflows now use.
  • CI concurrency, from the manual Macroscope review of PR #67: cancel-in-progress: false alone did not make the "main runs stay whole" claim true. By default only one run may be pending per concurrency group and a newer pending run cancels the older one, so two merges in quick succession could cancel a queued main run before it ever started and the intermediate commit would skip verify-determinism. Non-PR runs now take a per-run group (github.run_id); semantics verified against GitHub's concurrency documentation.
  • Drift watchers (High), from the same review: moving the issue upsert into its own composite step silently dropped its guard. The check step's no-drift exit 0s end only that step, so the upsert still ran with $RUNNER_TEMP/drift-body.md absent and gh issue comment/create --body-file failed - the workflow would fail on the common no-drift path (latent today, because upstream drift keeps the body-writing path active; it would start failing the week after a re-pin). Both watchers now emit drift=true from the check step only on the path that also writes the body, and the upsert step is conditioned on it; pinned for both watchers by tools/tests/test_workflow_review_guards.py.
  • Drift watchers: the "not creating a possible duplicate" guard could never fire. existing="$(gh ... || lookup_status=$?)" set the variable inside the subshell, so the parent always saw the initial 0; when gh issue list failed, the workflow proceeded with an empty result and CREATED a duplicate tracking issue - the exact failure the guard's message claims to prevent. Reproduced in bash before fixing; shellcheck SC2030/SC2031 flagged it once the shellcheck gate existed. The upsert now lives in the shared drift-issue-upsert action with the status captured outside the substitution, and one jq form for both callers.
  • Action version pins had drifted between workflows (actions/checkout at v4 in the drift watchers vs v7 elsewhere; astral-sh/setup-uv v5 vs v10). Unified; dependabot keeps them current from here.
  • Model panel no longer written into the plugin directory (#57). hermes plugins update replaces the installed package tree, so a config/models.json written next to plugin.yaml was silently discarded on every update - the user's per-role model choices reverted to the shipped defaults with no warning. setup-pstack now writes pstack-models.json in the hermes config directory beside config.yaml, which survives reinstalls, and the consuming skills (why, reflect, arena, interrogate) read that path while still tolerating a legacy in-package panel so existing customizations are migrated rather than ignored. The shipped default panel stays in the package - only the user override moves.
  • dist_checkout_check: the gate no longer substitutes its own .gitattributes for the tree's, which made it blind to the regression it exists to catch. stage_tree copied tools/assets/dist.gitattributes over whatever the dist tree actually held, so a published tree missing the rule - the publisher cp line deleted, or the rule stale - still validated clean. A staged tree must now carry the rule and match the tested one; the source pstack/ tree (which legitimately has none, since the publisher adds it) is still allowed. Both refusals are counterfactual-checked: dropping either guard fails a test.
  • dist_checkout_check: the source-tree exemption is an explicit --source-tree flag instead of a directory-name guess. A staged dist checkout is legitimately named pstack, so the name-based rule let a published tree with no attributes file at all validate clean - the same blind spot the previous entry closes, reached by another route.
  • dist_checkout_check: git output is decoded as UTF-8 with surrogateescape instead of the ambient locale codec. Under cp1252 a non-ASCII path came back mangled, stopped matching its staged key, and was skipped as "absent" - so its CRLF and byte-identity assertions never ran while the gate reported clean.
  • dist_checkout_check: a package file git never tracks (typically excluded by a packaged .gitignore) is now refused rather than skipped, since it would otherwise never be verified while the gate reported clean.
  • dist_checkout_check: a git failure now exits 2 as documented instead of propagating a RuntimeError traceback at exit 1; a tracked file with no staged counterpart is reported as unverified rather than silently skipped; dead BINARY_SUFFIXES is removed; and the shipped dist.gitattributes gained its missing trailing newline.
  • Reworded the *.png binary justification in publish-plugin.yaml: it claimed text=auto would corrupt assets/logo.png, but git's own content sniffing already prevents that and only a deliberate *.png text rule corrupts it. The line is kept as intent, not as the thing currently doing the work.
  • upstream-drift-watch: the job now runs at all. Its gh issue list/comment/create calls carry no --repo, so gh resolves the target from the git checkout - and the job had no actions/checkout, so every run failed with failed to run git: fatal: not a git repository. It has failed this way on every run since 2026-09-14, silently, because the last success was 2026-09-07 when upstream had not yet moved past the pin. The checkout is pinned by SHA with persist-credentials: false, matching model-drift-watch.yml. Also dropped the blobless --filter=blob:none clone (this job needs two commits of pstack/; the promisor repo only made the diff depend on network at diff time), and the drift diff exit status is now checked rather than used only as a condition, so a failed diff is reported as one. This is the guard that should have raised #53 (pstack is at v0.15.5 upstream; the pin is still v0.14.8). Only the blobless attempt was verified as usable, so when the shallow fallback also failed, set -e aborted at the fetch and the run logged two copies of fatal: not a git repository with nothing pointing at the real fault. Both attempts are now checked the same way, and a total failure exits with the clone error itself. This job has failed this way on every run since 2026-09-14 — silently, because the last success was 2026-09-07 when upstream had not yet moved past the pin. It is the guard that exists to report upstream drift, and issue #53 is the drift it should have raised (pstack is now at v0.15.5 upstream; the pin is still v0.14.8).

Added

  • tools/dist_checkout_check.py: regression check for the property the build gates are structurally unable to see. The build job validates the source pstack/ directory, where files are LF because the converter wrote them that way, so it cannot catch a line-ending or binary-asset regression introduced by the dist publish step. This stages the package the way publish-plugin.yaml does, clones it with core.autocrlf=true (the Windows default, and what hermes plugins install does), then asserts every tracked text file checked out LF and every non-text file byte-identical. Wired into the build job and re-run against the staged tree in publish-plugin.yaml before it commits, so a bad tree is never published. The attributes rule moved to tools/assets/dist.gitattributes, which the publisher copies verbatim, so the rule and the test that guards it cannot drift apart.

Fixed

  • Coverage gap: the check classified a file as text only when it had a known extension or a listed name, and as binary only when its bytes contained a NUL. This package ships extension-less text files (LICENSE and the watch-pr launcher), which matched neither test and were therefore silently skipped - the checker reported a clean tree without ever looking at 2 of 134 files. Text is now the default and binary requires a NUL byte, so the two branches are mutually exclusive and exhaustive and every tracked file is checked.
  • Dist repo line endings: publish-plugin.yaml now writes a .gitattributes (* text=auto eol=lf, *.png binary) into the published iap/pstack tree. The dist repo is consumed by hermes plugins install as a git clone, so on a consumer with core.autocrlf=true (the Windows default) every text file - including the user-editable config/models.json - was checked out as CRLF, and hermes plugins update's stash/pull/reapply then rewrote it again, drifting the package away from its own LF-only contract. Measured on a forced checkout with autocrlf=true: 38 CRLF before, 0 after, assets/logo.png byte-identical either way.
  • test_validate_encoding.py fixture: wrote the LF-only SKILL.md fixture with write_text, which translates \n to os.linesep on Windows, so the assertion failed there for a platform reason rather than a product reason. Now uses write_bytes with explicit \n. check_encoding itself was correct and is unchanged; this only makes the fixture platform-independent. (lint-and-tests runs on ubuntu-latest only, so the windows-latest build matrix never exercised this test.)

[0.4.3] - 2026-09-18

Added

  • publish-plugin.yaml: CI publisher that publishes the validated built package to the plugin-only dist repo iap/pstack (build output; append-only commits so installed clones can fast-forward — history is never rewritten; skipped with a warning until PLUGIN_DIST_TOKEN is configured; every real publish is tagged pstack-<date>-<sha>, because the plugin catalog admits only repos with real tags). The dist repo's tree root is the package, enabling the root install shorthand hermes plugins install iap/pstack --enable — no subdir — and in-place hermes plugins update pstack (root installs keep .git; verified against the hermes installer source). Requires the one-time PLUGIN_DIST_TOKEN secret (fine-grained PAT, contents: read/write on the dist repo only).
  • Agent-facing docs: AGENTS.md (harness identity, generated-tree rule, hermes-vs-Cursor vocabulary, primitive mapping) and a CONTRIBUTING rewrite (orientation, Bun setup, common-task recipes, publish stage).
  • docs/USING.md "Customizing skills": three routes by intent (edit in place for small tweaks, fork the dist repo for owned variants, skill taps for adding new skills alongside) with the keep-hermes-vocabulary warning for end users.
  • validate.check_conflict_markers: static-stage gate banning unambiguous VCS conflict-marker line-starts package-wide (a bare ======= stays legal as a setext underline; node_modules exempt), with regression tests.

Changed

  • README install routes reorganized: root install via the dist repo is primary; the iap/pstack-hermes/pstack subdir install is documented as the legacy route (still works). Update instructions split by route (in-place update for root installs; reinstall for subdir installs).

Fixed

  • Converter: T13_MAP's frontier-provider entry was anchored on text that only exists after the T13_MAP forge-wording entry and T14_MAP's first entry run — an ordering that can never match a clean pinned clone (the local 2026-09-14 build predated the map edit and its pstack/ was stale). Re-anchored the entry to the pinned paragraph, pruned the two subsumed entries (T13_MAP forge-wording line, T14_MAP first entry — their intent is carried by the full-paragraph provider-selection rewrite), and rebuilt from the clean pin: orchestrate.md and the package README now carry the T15 provider-selection wording, with the anchor audit passing against a fresh clone of 93b00b8.
  • T15b robustness (carried through the converter after a direct-to-generated-file edit was caught by the reproducibility gate): GitHub-native discovery now scopes itself to an explicit --prs pin instead of validating every PR in the repository as one stack, gh pr list passes an explicit --limit 100 so connected stacks beyond 30 PRs are not silently truncated, and auto-provider fallback is persisted into frontier.json as a fallback field (which providers were skipped) so a Graphite → GitHub fallback is coordinator-visible instead of silent. The GitLab provider remains reserved ("not implemented yet") pending a reviewed glab integration.

[0.4.2] - 2026-09-13

Added

  • Docs: docs/RUNBOOK-upstream-drift.md (re-pin procedure), docs/USING.md (invocation model, first run, expectations). Adaptation decisions tracked in issues #94/#95. Review round: the upstream closure date corrected to 2026-09-10 (closed unmerged; the API attests no closer), and the panel-consumer claim now quotes the actual role-line phrases.
  • slug_drift.py --prose: scans skill markdown for backtick-quoted model-slug defaults (including vendor/model forms) once the provider catalog loads; model-drift-watch.yml passes --prose and reports prose-only findings as informational (exit 0) — a catalog tool error (exit 2) skips the scan, since the panel slugs are verified against the loaded catalog first (#25/#29)
  • CONTRIBUTING: document the GitHub alert-callout style for docs — and where it does not apply (#30)

Changed

  • Structure hygiene: pstack/ marked linguist-generated in .gitattributes (diffs collapse in review) and PR guidance now requires rebuilt from pin <sha> in the body; README documents the two version lines (package 0.14.8 vs repo v0.4.x) and corrects the transform range to T1–T13; the package README's Known limitations gains the optional-MCP-servers note (template change + rebuild); the PR template gains the matching conditional pstack/ rebuild-disclosure fields
  • Main README rewritten Hermes-first: first-screen unofficial-port identity (not affiliated with Cursor or Lauren Tan; Cursor users go upstream), install up front, Cursor dual-load demoted to a trailing incidental/unsupported note; layout line corrected T1–T11 to T1–T12
  • Converter package-README template: uv run --frozen regenerate command and a CLI-install section replacing the manual-copy path; T13 gains the missed orchestrate.md cursor-team-kit anchor — rebuilt package from pinned 93b00b8 now carries zero cursor-team-kit references in shipped skills
  • T13 conversion pass: residual Cursor-vendor coupling reworded across the package — cursor-team-kit references (a deslop pass, control-ui/control-cli), the cloud-agent fleet wording, and Graphite (gt) phrasing; T13_MAP wired into the conversion flow and the anchor audit (115/115)
  • PR template: replace the unrunnable python3 convert.py checkbox with the real uv run --frozen tools/convert.py --source <pstack-clone> --out pstack invocation (the script lives in tools/ and requires --source); add the tools/scanner_gate.py --package pstack line that actually enforces the banned-construct checkbox; note the gateway-restart warning in the doctor line as unrelated noise
  • SECURITY.md reporting triage: issues caused by this port (adaptations, exclusions, configuration, install) belong to this repository; upstream reports are reserved for genuine platform or original-project defects — including a Hermes defect that surfaces only while a plugin is installed
  • Issue chooser: the port-problem link now opens the Bug report form directly (issues/new?template=bug_report.yml) instead of the All-issues list

Fixed

  • Residual doc defects: the package README no longer says it rebuilds benny as "hermes cron/loop jobs" (the template is corrected at the source, so the generated line is right by construction), and the control-surface replacement is properly capitalised. The two dead T13 maps — T13_PRINCIPLE_MAP and T13_README_MAP, both defined but never applied or audited — are removed.

[0.4.1] - 2026-09-10

Changed

  • T12: Cursor built-in /loop and /goal references reworded to hermes-native mechanisms — gateway cron jobs as the wake/scheduler (autonomous-run, autopilot-full, autopilot-stack, babysit, bug-fix, multi-phase-plan, shipping, visual-parity) and an armed goal.md in the agent store replacing the goal primitive; trigger phrases de-slashed ("loop until X"); plan checker (check-plan.mjs) marker aligned to accept the new mechanism

Removed

  • Executed pipeline-hardening plan doc dropped from docs/superpowers (#23). The plan shipped in v0.4.0 and the repo's own .gitignore already places agent session artifacts (plans/notes) outside the repo.

[0.4.0] - 2026-09-09

Fixed

  • Delegation phrasing residuals: collapsed doubled delegate_task fragments in reflect (reviewers + synthesizer calls), reworded unbackticked Cursor leftovers (poteto-mode "omit Task model" -> inherit-parent semantics, how "Task subagent" -> delegate subagent)
  • README differences contract: replaced the stale "nothing else modified" claim with the actual pass register (T8/T9/T10, Phase-2A, T11) and a pointer at .build-provenance.txt as the authoritative record; documented the docs/guide/ exclusion; fixed off-by-one cross-reference (see 9 -> see 8)
  • Ban-list inconsistency: unified delegation-vocab ban list under bans.py (single source of truth, 6 tokens)
  • Model-slug drift: weekly CI re-verifies configured slugs against the live OpenRouter catalog
  • Panel dedup guard: rejects duplicate provider slugs inside a role array; inherit-parent selector exempt
  • Empty catalog guard: slug_drift.py raises on empty/malformed catalog instead of reporting every slug as missing
  • Malformed JSON: local input failures return exit 2 (tool error) instead of crashing with exit 1 (misclassified as drift)
  • Issue lookup failure: workflow fails instead of creating duplicate tracking tickets
  • Workflow concurrency: overlapping runs are serialized, not cancelled
  • Stale version reference: removed "hermes v0.20.5" from README template
  • Stale line numbers: removed source line number references that drift across versions
  • Roadmap promise: removed "expected in a future release" claim from README template

Added

  • tools/slug_drift.py — model-slug drift check against provider catalog
  • .github/workflows/model-drift-watch.yml — weekly model-slug drift watch
  • ## Environment section in PR template (OS / Shell / Hermes version)
  • Update instructions in README (uninstall + reinstall workflow)

Changed

  • convert.py: 4 specific-first DELEGATION_MAP pairs + T9_MAP[19] anchor updated to the collapsed intermediate text (anchor audit 78 -> 82)
  • PR template: replaced hand-enumerated 2-token ban list with reference to tools/bans.py
  • CONTRIBUTING.md: updated ban-list documentation to reflect 6-token set
  • README.md: clarified install command (shorthand form required)

[0.3.0] - 2026-09-09

Added

  • Initial converter/validator tooling
  • Atomic builds with rollback
  • Fail-loud anchor audit
  • Scanner gate with shared banned-construct source of truth
  • CI: 2-OS matrix, SHA gates, determinism proof, unit tests, lint
  • Weekly upstream-drift watch