Documentation Maintaining
On this page
Author Guide

Maintaining the Package

The author workflow for regenerating dependencies, the squad source layout, customization knobs, and the tag-based release process.

Repository structure#

  • apm.yml: package metadata, dependency list, and scripts
  • apm.lock.yaml: resolved dependency lock file
  • scripts/Update-ApmDependencies.ps1: dependency generator
  • scripts/Get-HveCoreCastDelta.ps1: compares the deployable agent cast between two hve-core refs
  • scripts/New-ChangeFragment.ps1: writes one change fragment for a pull request
  • scripts/Invoke-ReleasePrep.ps1: assembles pending fragments into a CHANGELOG section and bumps apm.yml
  • .changes/unreleased/: pending change fragments, empty between releases
  • squad-src/.github/: locally authored squad source (agents, prompts, instructions, skills)
  • apm_modules/: installed dependencies (ignored by git)
  • .github/: generated/deployed local assets (ignored by git, except .github/workflows/ if tracked)
  • .copilot-tracking/: agent working state. Ignored by default — your research, plans, and interactive squad runs stay local with no action required. The exception is the audit trail of an unattended Watch Mode run, which .gitignore re-includes at both levels because proof-of-dispatch is two-level: squad/members/issue-*/ and squad/members/pr-*/ (the inner run) plus squad/history/issue-*.md and squad/history/pr-*.md (the federation-level meta-transition). The federation root scalars (federation.md, meta-routing.md, state.json, and the aggregate ledgers) stay local: they use replace semantics, so committing them would guarantee conflicts between concurrent runs, and the bootstrap rebuilds them.
  • docs/: this documentation site (served by GitHub Pages)

Scripts#

Documentation site#

Eleventy builds the published site from the English sources in docs/ and French sources in docs/fr/, using matching filenames and shared anchor IDs. Generated pages and search indexes go into _site/; opening an original source page does not show the generated release notices or versioned installation commands.

Update both languages in the same change. Preserve command names and parameters, and label links to English-only external guides, historical reports and videos. The published shell, translations and appearance live in site/pages.njk, site/locales.json and site/assets/site.css. Host tabs belong to individual command examples and share the selected environment across examples and pages; language switching keeps the current page.

Mark each newly documented, unreleased behavior with data-release-change="20261005-routing-economy", using its own pending fragment filename without .md, in both languages. Put the attribute on the feature's heading, paragraph, list item, inline identifier or whole code block, never inside a link or copyable code block. Do not mark already stable options or documentation-only corrections as unavailable. scripts/docs-release.mjs reads the last stable version from apm.yml and resolves the rolling tag from the highest pending bump. Badges disappear when their fragment is consumed, even when a maintainer overrides the release version.

Author runnable Squad examples in pre > code blocks using the canonical slash-command syntax. The shared renderer uses that source to create prefix-free VS Code, CLI, and App requests with the matching agent, preserving arguments and multiline text. Keep shell commands and GitHub comment triggers separate; mark explicit host-comparison references with data-host-reference. Without JavaScript, the original canonical examples remain readable.

npm ci
npm run docs:test
npm run docs:build
npm run docs:dev

The build preserves source prose and examples, inserts current release information, and checks translations, links, assets and search. Tests also cover keyboard tabs, saved preferences, no-script content and release promotion. The Pages workflow rebuilds on pending-fragment changes as well as documentation changes, so a renamed preview tag cannot leave the version notice behind. Check desktop and mobile layouts after visual changes.

Package scripts#

Defined in apm.yml:

  • sync-deps — regenerates dependencies.apm from HVE Core content.
  • install-sync — regenerates dependencies.apm and runs apm install.
  • change — creates a change fragment under .changes/unreleased/ for the current pull request.
  • release-prep — assembles the pending fragments into a release: CHANGELOG section, version bump, fragments cleared.

Maintainer workflow#

  1. Regenerate dependencies:
    apm run sync-deps
  2. Install dependencies locally:
    apm install
    # or
    apm run install-sync
  3. Refresh lockfile:
    apm lock
  4. [OPTIONAL] Build package artifact (only needed to build a plugin):
    apm pack
  5. [OPTIONAL] Publish (if applicable):
    apm publish

Recommended sequence before release:

  1. apm run sync-deps
  2. apm lock
  3. apm pack (optional)
  4. apm run release-prep — or run the Release Prep workflow, which does the same thing and pushes.

How dependency generation works#

The generator script:

  • Connects to microsoft/hve-core at a target ref (default: the SHA already pinned in apm.yml)
  • Enumerates files under .github/agents, .github/instructions, .github/prompts, and .github/skills
  • Filters for markdown artifacts
  • Rewrites only dependencies.apm in apm.yml

This keeps package metadata stable while updating the dependency list safely.

Upgrading hve-core: the two paths#

The squad roster binds every role to an hve-core agent by its exact name: frontmatter value. That makes an hve-core upgrade two different operations wearing the same clothes, and only one of them is safe to automate end to end. The split is decided by one question: does this hve-core revision move an agent the squad still names?

Diagram source

flowchart TD
    A["Cron 06:00 UTC<br/>sync-hve-core.yml"] --> B["Get-HveCoreCastDelta.ps1<br/>pinned SHA vs hve-core main"]
    B --> C{"Does squad-src still reference<br/>a removed or un-dispatched agent?"}

    C -- "no (mechanical)" --> D["Bump pin + assemble release<br/>from .changes fragments + push"]
    D --> E["release.yml<br/>tag + GitHub Release"]

    C -- "yes (breaking)" --> F["Open issue labeled squad/auto<br/>body = full adaptation brief"]
    F --> G["squad-watch.yml<br/>fires on the label"]
    G --> H["Sub-squad issue-N<br/>autopilot, unattended"]
    H --> I["DRAFT PR that closes the issue"]
    I --> J{"Label the PR squad/review?"}
    J -- yes --> K["squad-watch.yml again<br/>sub-squad pr-N, tester role"]
    K --> L["Review comments on the PR"]
    L --> M["Human merges"]
    J -- no --> M
      

Note that no cron runs a squad. The only scheduled job is the mechanical sync; when it finds a breaking delta it writes an issue and stops. The issue is both the trigger and the brief, so the squad's task description is a durable, reviewable artifact rather than a string buried in a workflow file.

Path 1 — mechanical (no cast change)#

sync-hve-core.yml moves the pinned SHA and records the move as a change fragment, exactly as a contributor would — so the pin move queues alongside whatever contributors merged that day and ships with the next release someone cuts, instead of cutting a release of its own every morning. It pushes and stops there. One addition to the older behavior: it runs the cast-delta script as a guard first. A SHA move cannot repoint a roster row, and shipping it anyway releases a squad whose roles resolve to nothing — which is exactly what happened in 0.10.11.

Path 2 — adaptive (the cast changed)#

The sync workflow does not touch apm.yml. It opens an issue labeled squad/auto whose body carries the whole brief: what changed and why it breaks, eight numbered steps, the editing constraints, acceptance criteria, and the generated cast-delta table. One open issue per target SHA — a repeat cron on an unresolved delta comments rather than opening a duplicate.

squad-watch.yml fires on that label, runs the squad headlessly in sub-squad issue-<N>, and opens a draft PR that closes the issue. Labeling that PR squad/review fires the same workflow again in sub-squad pr-<N>, routed to the tester role, which posts its findings as PR comments.

The adaptation PR carries a .changes/unreleased/ fragment with bump: minor, because a cast change means the roster that ships is not the one that shipped before. It does not edit apm.yml version: or CHANGELOG.md — the same rule every contributor follows, and pr-validation.yml enforces it here too. If a run forgets, the workflow writes a placeholder patch fragment so the PR can still go green, marked PLACEHOLDER, rewrite before merging.

Merging the adaptation pull request queues the minor release by itself. The fragment lands in .changes/unreleased/ on main, where Rolling Preview publishes it as an installable pre-release within minutes. It ships to consumers with the next release you cut.

How gates behave when nobody is watching#

An unattended run cannot wait for an approval that will never arrive, so Watch Mode moves where the gate is satisfied rather than removing it. See Unattended Gate Disposition in the Watch Mode instructions.

  • Stage transitions — proceed on artifact evidence, as in any autopilot run.
  • Final-outcome validation — satisfied by the draft PR. The run compiles its outcome into the PR body instead of pausing.
  • Risk Gate — recorded, not blocking. Findings land in decisions.md and are reproduced in the PR body under Blocking findings; the PR stays a draft.
  • Impactful-Action Gate — never proceeds. No merge, no deploy, no force-push, no migration, no secret rotation, no write into a live issue tracker, and no payload can change that. It is enforced three ways: the contract, the absence of any deployment credential on the runner, and branch protection on main.

Run the delta check yourself at any time:

./scripts/Get-HveCoreCastDelta.ps1
./scripts/Get-HveCoreCastDelta.ps1 -ToRef main -MarkdownPath delta.md

Configuration required#

  • Secrets. COPILOT_GITHUB_TOKEN — a fine-grained PAT with the Copilot Requests permission (the built-in Actions token cannot authenticate Copilot). SYNC_DEPS_TOKEN — used to read microsoft/hve-core and push the release commit.
  • Labels. Create squad/auto and squad/review. These are the two triggers; without them nothing fires.
  • Settings → Actions → General → Workflow permissions: enable “Allow GitHub Actions to create and approve pull requests.” Without it gh pr create returns 403 and the run finishes with a branch but no PR.
  • Branch ruleset on main: requires a pull request, with the repository-admin role as the only bypass actor. This is the third enforcement layer under the Impactful-Action Gate, and the one most often left half-configured — a rule that only forbids force pushes and deletions does not stop a direct push to main, it limits the damage of one. The admin bypass is what separates the two automations: sync-hve-core.yml pushes with a maintainer PAT and passes straight through, while Watch Mode pushes as github-actions[bot], which holds no repository role and cannot reach main at all. Required approvals are set to zero on purpose, so a solo maintainer is not locked out of merging their own pull request.
  • Model pin (already set). squad-watch.yml passes --model to the Copilot CLI from the workflow-level SQUAD_MODEL variable. Do not leave this to auto: the coordinator is the most instruction-heavy agent in the system, and an auto-selected model routes exactly that agent to whatever is cheapest — which is how a run ends up authoring deliverables inline instead of dispatching its cast. Precedence in the CLI is --model > COPILOT_MODEL > ~/.copilot/settings.json; only the flag is repository-scoped, because only the flag lives in the repo. Confirm the IDs available on your plan by running copilot interactively and typing /model — they are lowercase slugs such as claude-sonnet-5, not the display names used in agent frontmatter. A workflow_dispatch run can override the pin for one execution.
  • Approval watcher (optional): copy .github/skills/squad/github-approval-watcher.workflow.yml into .github/workflows/ if you also want interactive runs to be approvable from a phone.

Testing the loop by hand#

Run Squad Watch from the Actions tab with an issue_number input to exercise the whole path against an issue you wrote yourself, without waiting for an upstream change. Do this once before relying on the cron.

Reviewing an adaptation PR#

The PR body carries the checklist; the two items that matter most are:

  • No roster Primary or Alternate sets disable-model-invocation: true — those agents cannot be dispatched.
  • .copilot-tracking/squad/members/<sub-squad>/history/ holds one entry per dispatched role. A thin history means the run authored the changes inline instead of dispatching its cast, and the diff should not be trusted on its own.

Squad source#

In addition to the microsoft/hve-core dependencies, this package ships a locally authored "squad" — a Squad Coordinator plus supporting agents, instructions, a prompt, and a skill. The squad source lives under:

  • squad-src/.github/agents/squad/
  • squad-src/.github/instructions/squad/
  • squad-src/.github/prompts/squad/
  • squad-src/.github/skills/squad/

Cost-ceiling contract and tests#

Cost admission spans several artifact types, but each concern has one primary owner. Keep the prompt surface, canonical calculation, coordinator boundary, Scribe validation, state schema, and tests synchronized when the parameter changes.

ConcernPrimary owner
Invocation and forwardingsquad.prompt.md and squad-federation.prompt.md
Effective ceiling, estimate, confidence, approval, and stop rulesreferences/consumption.md
Dispatch-boundary procedurereferences/gates-and-modes.md
Machine and readable record shapesreferences/entry-schemas.md and references/federation-templates.md
Post-admission slot enforcementreferences/scribe-procedure.md
Single-squad and federation orchestrationsquad-coordinator.agent.md and squad-federation-coordinator.agent.md
Federation aggregate rates and calibrationFederation-root consumption-rates.md, seeded from references/federation-templates.md and used only by untargeted federation autopilot
Stable behavior IDstests/squad-behavior-contract.md
Package, state, chronology, and mutation checkstests/tier0/SquadPackage.Tests.ps1 and tests/tier1/

Keep the ownership split explicit. Initialization is setup spend outside admission. Ordinary and targeted federation routing forwards an independent ceiling to every selected sub-squad. Only untargeted federation autopilot reads or writes aggregate Cost Preflight state at the federation root. Its completed meta cost is reconstructed across every round, not only the latest shrinking manifest.

Run the local-safe validation layers after a change:

# Prompt, package, reference, and body-cap conformance
./tests/tier0/Invoke-Tier0Tests.ps1 -SourceRoot .

# Valid state plus adversarial transition fixtures
./tests/tier1/Invoke-Tier1Tests.ps1 -SelfCheck -Output Normal

# Whitespace and malformed patch output
git diff --check

Tier 1 live runs invoke models and are a separate manual measurement lane. The self-check above is deterministic and proves each contract assertion can reject a deliberately corrupted tree. A behavior change also needs a new file under .changes/unreleased/; editing an existing fragment does not satisfy the pull-request gate for a separate pull request.

Squad skill and reference maintenance#

The squad skill's instruction and reference files drive roster, model selection, and ledger behavior. Edits here propagate to every consumer on the next apm install.

Roster split: squad-src/.github/instructions/squad/squad-roster.instructions.md carries inline safety summary, dispatchability rules, and Profiles/Packs. The cast catalog (formerly Cast Catalog, External Cast, and Building a Custom Roster sections) now lives in the canonical squad-src/.github/skills/squad/references/roster-catalog.md (read on demand). When adding a new cast agent, edit references/roster-catalog.md to append the role row and link the agent file under .github/agents/.

Model catalog and routing: squad-src/.github/skills/squad/references/model-catalog.md enumerates supported models, tiers, and availability. references/model-routing.md defines the ranked selection algorithm and precedence chain. The Scribe must not inherit the frontier session model (pinned to Claude Haiku 4.5 in its frontmatter; bookkeeping assignment class floors at seeded fast Model Tier). When GitHub Copilot's model lineup changes, update the catalog's Retrieved date; a date >90 days stale falls back to static tiers. Keep the retrieval date and the catalog synchronized.

Tier 0 byte-budget gate: tests/tier0/baselines/byte-budget-baseline.json (see Performance.Tests.ps1 L507) sets size limits (roster 61,170 B; Scribe hot core 82,000 B; 5% tolerance). Edit budgetBytes and note by hand when an intentional size change occurs. The tests/tier0/baselines/prefill-baseline.json baseline is regenerated with ./scripts/Measure-SquadPerformance.ps1 -SourceRoot . -UpdateBaseline (see Measure-SquadPerformance.ps1 L52–56); after regenerating, diff it to confirm only intended normative sentences and headings changed (a removed normative sentence fails the superset gate in Performance.Tests.ps1 L507 unless it was an intentional correction).

Ledger helper and guards: The Measure-SquadLedger.ps1 -SquadRoot <root> -Check script validates ledger arithmetic, ledger↔state.json divergence (strict; missing/unparseable state.json fails; a federation root marked by federation.md is logged not-applicable), the identity guard (recorded per-file ordered block identities must form an ordered prefix of current identities; overwrites/removals fail; appends pass; a plain -Check only warns when no identities are recorded at all — legacy ledgers), and is called by the Scribe with -ExpectedHistoryCounts <per-agent entry counts> (scribe-procedure.md L166), which turns those same missing-identity warnings into failures — entirely missing, or missing for only some of the touched files (a partial paste) — since that call always follows a fresh write. Test fixtures under tests/fixtures/ledger-* exercise each guard condition.

Why the source is separate from the deployed .github/ tree#

apm install deploys agents and prompts flattened into the consumer's .github/ tree. If the squad source lived directly under the top-level .github/agents/ (or .github/prompts/), a later apm install would clobber it. Keeping the authored source under squad-src/.github/... places it outside the deploy flatten zone while preserving the .github/{agents,prompts,instructions,skills}/ prefix the deploy mapping expects. Runtime squad state is created per-project under .copilot-tracking/squad/ and is never packaged.

How squad entries are generated#

Update-ApmDependencies.ps1 enumerates the squad source from the local filesystem (it is not cloned, because the source lives in this working tree and may not be pushed yet). Squad entries are emitted as remote virtual paths of the form <SquadRepoSlug>/<SquadSourceRoot>/.github/..., for example Peter-N91/hve-squad/squad-src/.github/agents/squad/squad-coordinator.agent.md. They are appended after the hve-core entries in dependencies.apm.

The sync command accepts these squad-specific parameters:

pwsh -File scripts/Update-ApmDependencies.ps1 `
  -SquadSourceRoot squad-src `
  -SquadRepoSlug Peter-N91/hve-squad `
  -SquadRef vX.Y.Z
  • SquadSourceRoot: local path to the squad source tree (default: squad-src). If the path does not exist, squad enumeration is skipped without error.
  • SquadRepoSlug: owner/repo that hosts the squad source (default: Peter-N91/hve-squad).
  • SquadRef: optional git ref that pins the squad self-references as #<ref>. You normally leave this alone — the Release workflow pins automatically (see Reproducible release tags below). It remains available for pinning a manifest by hand.

Use -DryRun to preview both the hve-core and squad entries without writing apm.yml.

Reproducible release tags#

The squad self-references ship as bare paths on main, and APM resolves a bare path against the default branch. Left alone, a tag would freeze the dependency list but not its contents: installing #v0.14.0 was measured returning main's coordinator, while the hve-core entries in the same manifest resolved correctly because they carry #<sha>. APM warns about it directly — 47 dependencies unpinned: Peter-N91/hve-squad.

release.yml closes this without any manual step. Before creating the release it runs scripts/Set-SquadSelfRefPin.ps1 -Ref vX.Y.Z, commits the pinned manifest, and pushes that commit straight to refs/tags/vX.Y.Z. A manifest cannot name its own tag before the tag exists, so the pin lives in a commit that only the tag points at. main never receives it and keeps the unpinned manifest, so development installs continue to track main.

This applies from v0.15.2 onward. Every earlier tag resolves its squad files from main and is not reproducible. Fixing one retroactively means force-moving its tag, which changes what its GitHub Release points at.

apm lock and apm install can only resolve the squad virtual paths once the squad-src/ tree has been pushed to the SquadRepoSlug remote. Release tags are pinned automatically by release.yml, so verify apm install from a fresh clone of the tag rather than from the working tree.

Reproducible pins#

Every generated microsoft/hve-core entry is pinned to the exact commit SHA that -Ref resolves to (appended as #<sha>), so a published version keeps resolving the same files even after hve-core's main moves on. Without this, an unpinned path that hve-core later renames or removes makes the install fail for every consumer of that version. With no -Ref, the script reuses the pin already in apm.yml; pass -Ref <hve-core-ref> to move it to a specific commit, tag, or branch.

Customization#

You can tune generation behavior in scripts/Update-ApmDependencies.ps1:

  • RepoSlug: source repository
  • Ref: branch/tag/commit to scan. Defaults to the SHA already pinned in apm.yml, so a routine regeneration never moves the pin; pass it explicitly to move it.
  • IncludeRoots: folders included in discovery
  • IncludeRegex: file filter pattern
  • SquadSourceRoot: local squad source tree path
  • SquadRepoSlug: owner/repo hosting the squad source
  • SquadRef: optional release tag/commit that pins the squad self-references

Change fragments#

Version and changelog are release outputs, not things a pull request edits. A version line is a single line and the newest changelog heading is a single position, so two pull requests that both touch them always conflict — and the second one to merge silently reuses a version number the first already claimed. Moving a target branch does not help: two pull requests bumping to the same number on a develop branch conflict identically. The fix is to take the shared state out of the pull request entirely.

Each pull request instead adds one new file under .changes/unreleased/, generated by apm run change. Two pull requests adding two differently named files never conflict. The format is documented in .changes/README.md and repeated for contributors on the Contributing page.

pr-validation.yml makes the rule mechanical rather than a review comment:

  • Editing CHANGELOG.md in a pull request fails the check.
  • Changing the version: line in apm.yml fails the check.
  • Moving the microsoft/hve-core pin fails the check, unless the branch is a Watch Mode adaptation branch (squad/issue-*). Editing dependencies: is otherwise fine — a contributor adding a skill has to, via apm run sync-deps, which stays on the current pin by default. The check is the backstop for an explicit -Ref main that skips the cast-delta guard; its message names the SHA to pass instead.
  • A pull request with no new fragment fails, unless it carries the skip-changelog label. That label also skips the version bump, because the version is resolved from fragments — a pull request contributing no fragment contributes no bump.
  • Every pending fragment is parsed with Invoke-ReleasePrep.ps1 -DryRun, so a malformed one is caught in the pull request rather than at release time.

It runs on pull_request, not pull_request_target: fork pull requests get a read-only token and no secrets, and nothing in the check needs either. Create the skip-changelog label before relying on the escape hatch.

Who decides the release level#

The version level tracks ideas, not artifacts. 0.10.0 was federation and 0.11.0 was the hve-core attachment rework, because each introduced a concept that did not exist before. Every agent, skill, role, and instruction added underneath one of those ideas is a patch, however many files it touches. A contributor who adds a squad role to federation is extending an idea that already ships.

A fragment's bump is a suggestion, and because merging releases immediately, the pull request is where you overrule it. Two things make that easy:

  • pr-validation.yml writes the outcome into the check's job summary — Version: 0.11.12 -> 0.12.0 (minor) — so a minor can never slip past you unnoticed.
  • Disagree? Open the fragment in the pull request's Files changed tab, click the pencil, change bump: minor to bump: patch, and commit. One line, no checkout. Fork pull requests allow this by default (Allow edits by maintainers).

The release-time overrides still exist for the batch you assemble yourself, and for anything that reached main without a release:

pwsh -File scripts/Invoke-ReleasePrep.ps1 -Bump minor -DryRun   # this batch adds up to a new idea
pwsh -File scripts/Invoke-ReleasePrep.ps1 -Version 1.0.0        # exact number, overrides everything

The Release Prep workflow exposes the same choice as a bump dropdown, defaulting to fragments. The script prints “Bump override: fragments resolved to X, releasing as Y” whenever you overrule them, so the decision is visible in the run log.

Release process#

Releases use a tag-based flow on top of the default branch (main). The tag is the release artifact consumers install with apm install "Peter-N91/hve-squad#vX.Y.Z". You do not choose the version number by hand — the highest bump among the pending fragments selects it.

Releases are cut by hand. Merging a pull request that carries a fragment pushes it to .changes/unreleased/ on main and releases nothing. Nothing releases on a timer either. When the pending batch is ready, you dispatch Release Prep, which ships it as one version: it resolves the level, prepends the CHANGELOG section with the consumer-install block and the link reference, writes version: into apm.yml, deletes the fragments it consumed, commits chore(release): X.Y.Z, dispatches release.yml to cut the tag and the GitHub Release, and retires the pre-release.

That makes releasing a judgement about readiness rather than a date. A quick fix ships as soon as you have verified it in the preview; a larger change sits in preview until it is ready, and anything merged in the meantime rides along with it.

Between releases, main is installable. Every merge triggers Rolling Preview, which renders the pending fragments into a single GitHub pre-release tagged with the version they resolve to (see the current version and install command) and force-moves that tag to the new head. It only reads: no fragment is consumed, no version is bumped, and CHANGELOG.md is untouched. Exactly one pre-release is open at a time, so when a minor fragment lands the old one is deleted and the new name takes over. A pull request with no fragment (the skip-changelog label) contributes no bump.

Tick dry run when you dispatch to see the resolved version and the assembled section without committing anything. Use the bump and version inputs to overrule the level the fragments resolved to or to pin an exact number.

Do not keep a long-lived release branch. Cut a short-lived hotfix/vX.Y.Z branch off an existing tag only when main holds work that must not ship yet and a fix cannot wait for it — a release ships a commit, so a tag cut from main carries that pending work out with the fix.

git switch --detach v0.14.0
git switch -c hotfix/v0.14.1
# apply the fix, then record it
apm run change            # Type: Fixed, Bump: patch
git push -u origin hotfix/v0.14.1

Dispatch Release Prep with ref set to hotfix/v0.14.1. It consumes only that branch's fragments, bumps apm.yml there, and cuts the tag from that branch; release.yml receives the same ref and tags the commit it checked out. The rolling pre-release is deliberately not retired, because the batch it describes is still pending on main.

Merge the branch back afterwards so the fix survives the next release, and label that pull request release-merge-back. That label is the only exemption to the CHANGELOG.md and version: guards in pr-validation.yml, and it exists precisely because Release Prep — not a human — wrote both. If a higher version has already shipped, the release workflow compares versions and passes --latest=false, so the hotfix does not take the Latest badge from the release that supersedes it.

Release Prep can also be run directly:

apm run release-prep                        # resolve version, assemble, write
pwsh -File scripts/Invoke-ReleasePrep.ps1 -DryRun           # preview only
pwsh -File scripts/Invoke-ReleasePrep.ps1 -Bump minor       # overrule the fragments
pwsh -File scripts/Invoke-ReleasePrep.ps1 -Version 1.0.0    # force an exact version

Before a release that also re-pins hve-core, regenerate the list first: pwsh -File scripts/Update-ApmDependencies.ps1 -Ref main pins every hve-core entry to main's commit SHA. Refresh the lockfile with apm lock. The squad self-references need no action — release.yml pins them into the tagged commit.

Avoid a long-lived release branch. Only create release/vX.Y.Z from an existing tag when you need to patch an older version after newer work has landed on main.

Known incidents from pipelining testing#

During the development and testing of Scribe hand-off pipelining (autopilot mode), five audit-trail integrity incidents were observed and detected. These incidents informed the design of per-write verification barriers and corrective controls. They are recorded here for maintainers working with squad state and Scribe dispatches:

  • Incident 3 (turn 15): The run-start Scribe dispatch cut history/Squad Scribe.md from 48 blocks to 8; a bare Measure-SquadLedger.ps1 -Check passed because the ledger had been rewritten to match. The original content was unrecoverable because .copilot-tracking/ is git-ignored.
  • Incident 4 (turn 18): The Scribe hand-wrote consumption.md in an invalid shape with an empty Attribution Agent cell that caused Measure-SquadLedger -Check to throw; the coordinator's pre-write snapshot comparison detected it and the file was rewritten from the helper's output. Separately, a triple heading append in a history file received an append-only correction entry.
  • Incident 5 (turn 21): Existing bytes in the first 4,937 bytes of history/System Architecture Reviewer.md were modified by the Scribe. Measure-SquadLedger -Check -ExpectedHistoryCounts passed despite the prefix change; only the coordinator's out-of-band SHA-256 prefix check detected it. The original bytes were unrecoverable, and this incident led to the adoption of tool-level content-prefix baseline checks (-EmitBaseline / -BaselinePath).
  • Incident 6 (turn 23): The Scribe overwrote plans/2026-09-28-scribe-pipelining-plan-amendment-2.md (99,355 bytes to 8,659 bytes) because its payload did not enumerate an explicit allowed-write set. The owning role (Squad Lead) re-authored the plan; the tool now enforces a protected-artifact scope via content-baseline comparison.
  • Incident 7 (turn 25): The Scribe appended six heading lines per council history file (instead of one), issued void/re-append corrections, embedded entries inside heading lines, and included newline-split values. A human decision adopted pre-rendered Scribe payloads (coordinator authorship, Scribe append-only) for the rest of that run.

Each incident was detected and corrected with append-only history entries or by re-dispatching the owning role. The original bytes for incidents 3 and 5 could not be recovered. The corrective controls are now integrated into the Scribe pipelining verification contract: Measure-SquadLedger.ps1 -Check -ExpectedHistoryCounts -BaselinePath (not bare -Check), content-prefix preservation and checking via baseline snapshots, and role re-dispatch on failed verification.

Search documentation

Search the English documentation. Press Esc to close.

Loading search…