@jaybeeuu/agent-cortex 1.42.0 → 1.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/.agents/skills/style-versioning/SKILL.md +73 -2
  2. package/.github/workflows/ci.yml +47 -27
  3. package/AGENTS.md +23 -2
  4. package/CHANGELOG.md +166 -0
  5. package/README.md +125 -73
  6. package/agents/README.md +17 -4
  7. package/bin/agent-cortex.mjs +48 -30
  8. package/bin/installers/pi.mjs +885 -237
  9. package/docs/claude-hooks.md +5 -4
  10. package/docs/commit-signing.md +231 -0
  11. package/docs/ideas/enable-signed-commit-enforcement-changesets-bot-commits-must-be-signed.md +10 -1
  12. package/docs/ideas/extension-manifests.md +5 -0
  13. package/docs/research/disable-model-invocation.md +103 -0
  14. package/docs/research/skill-design-research.md +2 -2
  15. package/docs/skills/skill-anatomy.md +5 -1
  16. package/docs/technical-direction/harness-config.md +25 -1
  17. package/docs/technical-direction/migration-execution-labels.md +17 -9
  18. package/extensions/agent-modes/discover.test.ts +19 -0
  19. package/extensions/agent-modes/discover.ts +25 -3
  20. package/extensions/session-start/README.md +38 -0
  21. package/extensions/session-start/index.test.ts +122 -0
  22. package/extensions/session-start/index.ts +27 -0
  23. package/extensions/session-start/package.json +19 -0
  24. package/extensions/session-start/tsconfig.json +12 -0
  25. package/extensions/skill-stats/pnpm-lock.yaml +329 -0
  26. package/extensions/subagent/README.md +1 -1
  27. package/extensions/subagent/agents.test.ts +176 -0
  28. package/extensions/subagent/agents.ts +102 -11
  29. package/extensions/subagent/index.ts +2 -1
  30. package/extensions/subagent/package.json +1 -1
  31. package/lib/cli.mjs +35 -9
  32. package/package.json +6 -5
  33. package/pi/settings.json +1 -7
  34. package/plugin.json +1 -1
  35. package/pnpm-workspace.yaml +6 -0
  36. package/scripts/check-changeset.mjs +42 -0
  37. package/scripts/lib/release-gates.mjs +61 -0
  38. package/scripts/lib/verify-worker-tree.mjs +107 -0
  39. package/scripts/resolve-publish-command.mjs +17 -0
  40. package/skills/planning/create-task/scripts/pnpm-lock.yaml +342 -0
  41. package/skills/productivity/validate-agent-dir/scripts/pnpm-lock.yaml +329 -0
  42. package/skills/workflow/git-workflow/SKILL.md +35 -1
  43. package/skills/workflow/ralph/REFERENCE.md +17 -1
  44. package/skills/workflow/ralph/SKILL.md +4 -1
  45. package/skills/workflow/run-pipeline-stage/SKILL.md +11 -4
  46. package/skills/workflow/run-pipeline-stage/playbooks/code.md +2 -0
  47. package/skills/workflow/run-pipeline-stage/playbooks/document.md +3 -0
  48. package/skills/workflow/run-pipeline-stage/playbooks/fix.md +2 -0
  49. package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +11 -0
  50. package/skills/workflow/run-pipeline-stage/scripts/pnpm-lock.yaml +342 -0
  51. package/test/cli.test.mjs +26 -29
  52. package/test/helpers/fake-pi.mjs +132 -0
  53. package/test/pi-installer.test.mjs +345 -286
  54. package/test/pi-register.test.mjs +342 -0
  55. package/test/pi-sweep.test.mjs +619 -0
  56. package/test/release-gates.test.mjs +190 -0
  57. package/test/verify-worker-tree.test.mjs +120 -0
  58. package/token-map.README.md +7 -6
  59. package/token-map.json +6 -5
  60. package/extensions/session-start.ts +0 -44
  61. package/lib/pi-packages.mjs +0 -169
  62. package/test/pi-packages.test.mjs +0 -295
@@ -54,11 +54,29 @@ Fix status bead showing wrong priority after update
54
54
 
55
55
  ### Rules
56
56
 
57
+ - **Every PR adds a changeset.** CI fails otherwise. The only opt-out is the
58
+ `no-publish` label (see below).
57
59
  - The frontmatter package name must match exactly: `"@jaybeeuu/agent-cortex"`
58
60
  - The description should be one or two sentences, user-facing (it goes into CHANGELOG.md)
59
61
  - One changeset per PR. If a PR has multiple unrelated changes, use multiple changeset files.
60
62
  - The filename doesn't matter to changesets — only the frontmatter and description do.
61
63
 
64
+ ## The PR gate
65
+
66
+ `changeset-check` runs on every pull request and fails it unless the PR adds a
67
+ `.changeset/*.md` file. **Publishing is the default; `no-publish` is the only
68
+ opt-out** — skipping a release is an explicit, reviewable decision, never an accident.
69
+
70
+ | Exemption | Applies to |
71
+ |---|---|
72
+ | `changeset-release/*` head branch, or actor `github-actions[bot]` | the changesets bot's Version Packages PR — it consumes changesets and adds none of its own |
73
+ | `no-publish` label | a PR that genuinely ships nothing |
74
+
75
+ The `no-publish` label must exist in the repository's label settings — a one-off
76
+ human step (`gh label create no-publish`), not something a PR can ship. If the gate
77
+ fails and the PR really ships nothing, ask for the label; do not manufacture a
78
+ changeset to make it green.
79
+
62
80
  ## After merge to main
63
81
 
64
82
  The `release` job in `.github/workflows/ci.yml` handles everything automatically:
@@ -72,7 +90,54 @@ The `release` job in `.github/workflows/ci.yml` handles everything automatically
72
90
 
73
91
  `changesets/action` execs the `version`/`publish` inputs without a shell, so each
74
92
  must be a single unquoted command token — keep release logic in pnpm scripts
75
- (`version-packages`, `publish-package`), never inline shell strings.
93
+ (`version-packages`, `publish-package`, `publish-noop`), never inline shell strings.
94
+
95
+ ### Approve CI on the Version Packages PR
96
+
97
+ The version PR arrives `BLOCKED` with an empty check list, and nothing is broken:
98
+ `changesets/action` pushes that branch with `GITHUB_TOKEN`, so GitHub parks the CI
99
+ run the push creates behind **“Approve workflows to run”** (`action_required`, zero
100
+ jobs). A parked run reports no checks, so the five required contexts are
101
+ unsatisfiable until someone approves it. Approve it from the PR's merge box, or:
102
+
103
+ ```bash
104
+ run=$(gh run list --branch changeset-release/main --limit 20 \
105
+ --json databaseId,conclusion --jq 'map(select(.conclusion=="action_required"))[0].databaseId')
106
+ gh api -X POST "repos/{owner}/{repo}/actions/runs/$run/approve"
107
+ ```
108
+
109
+ An approval is per head commit. Every merge to `main` force-updates the version
110
+ branch and starts a fresh parked run, so approve immediately before merging the
111
+ version PR — not once per release. Do not reach for a PAT or GitHub App token to
112
+ skip this: one click is cheaper than a long-lived credential in repository
113
+ secrets, and the `GITHUB_TOKEN` the job already holds cannot approve its own run.
114
+
115
+ Which of the two publish scripts runs is decided per push, not hard-coded: labels are
116
+ invisible on a push event, so the job resolves the PR merged for the pushed commit via
117
+ the GitHub API and reads its labels — `publish-noop` when the PR carried `no-publish`,
118
+ `publish-package` otherwise. That needs no new secret or scope; the job already holds
119
+ `GITHUB_TOKEN` and `pull-requests: write`. A direct push with no merged PR resolves to
120
+ no labels and so publishes, which keeps a genuine publish of an already-published
121
+ version failing loudly — there is deliberately no idempotency guard.
122
+
123
+ ### Merge method and commit signatures
124
+
125
+ Land a Version Packages PR with `gh pr merge <n> --squash`. Agent and human commits here are
126
+ GPG-signed by the environment and verify as `valid`; the `github-actions[bot]` commit is
127
+ `unsigned` because `GITHUB_TOKEN` cannot sign. The commit that lands is signed by GitHub,
128
+ which is why `main`'s history reads `valid`: PR #161's unsigned head `fe7ef84` landed as
129
+ `dd347471b1` with `committer=GitHub`, `verified=true`.
130
+
131
+ Squash re-signs the landing commit; it does not escape an enforced signature rule. GitHub
132
+ evaluates a pull request by creating a test merge commit and checking the commits it
133
+ introduces — head-branch commits included — so an enforced `required_signatures` rule blocks
134
+ a squash merge too, and the remedy is then a signed commit on the branch, not another merge
135
+ method. `allow_merge_commit` is off and `required_linear_history` is on, so a merge commit is
136
+ not an option — squash or rebase only, and only squash lands a commit GitHub signs.
137
+
138
+ See `docs/commit-signing.md` for the evidence, the ruleset reads that show only
139
+ repository-level rules applying to `main` (a read that covers organization rules too), and the
140
+ human-only follow-ups, including the merge probe that closes AC1.
76
141
 
77
142
  ### Version lockstep
78
143
 
@@ -88,9 +153,15 @@ All version numbers stay in lockstep automatically:
88
153
 
89
154
  - **Running `pnpm changeset`**: Interactive, wastes tokens. Write the file directly.
90
155
  - **Bumping versions manually**: Don't touch `"version"` in `package.json` or `plugin.json`. Create a changeset.
91
- - **Skipping the changeset**: A PR without a changeset won't trigger a release. The changesets bot will remind you.
156
+ - **Omitting the changeset**: CI fails the PR. Publishing is the default; the only
157
+ opt-out is the `no-publish` label, which must already exist in the repo's settings.
92
158
  - **Wrong bump type**: `patch` for fixes, `minor` for additions, `major` for breakage. When in doubt, ask the user.
93
159
  - **Wrong package name in frontmatter**: Must be `"@jaybeeuu/agent-cortex"` exactly.
160
+ - **Merging a Version Packages PR with anything but squash**: the bot's commit is unsigned,
161
+ and squash is the path this repo has landed on (a rebase merge adds the head-branch commit
162
+ without signature verification).
163
+ Squash does not, however, bypass an enforced signature rule — if one is confirmed, the
164
+ branch needs a signed commit. See `docs/commit-signing.md`.
94
165
 
95
166
  ## What changed from the old workflow
96
167
 
@@ -5,6 +5,12 @@ on:
5
5
  branches:
6
6
  - main
7
7
  pull_request:
8
+ # The default types (opened/synchronize/reopened) would make the documented
9
+ # `no-publish` opt-out unreachable: labelling a PR after the check failed
10
+ # would never re-trigger it, and a manual re-run replays the original event
11
+ # payload. Label events keep the gate honest in both directions — adding the
12
+ # label turns it green, removing it turns it red again.
13
+ types: [opened, synchronize, reopened, labeled, unlabeled]
8
14
 
9
15
  jobs:
10
16
  setup:
@@ -42,6 +48,12 @@ jobs:
42
48
 
43
49
  changeset-check:
44
50
  name: Changeset check
51
+ # The gate is a pure, idempotent recomputation of the diff plus PR metadata,
52
+ # so every pull_request event must run it. Do NOT narrow this to the
53
+ # labels that can change the verdict: GitHub reports a skipped job as a
54
+ # success (docs.github.com - about-status-checks), so skipping on unrelated
55
+ # label churn would silently turn a failing gate green and let a
56
+ # changeset-less PR merge.
45
57
  if: github.event_name == 'pull_request'
46
58
  runs-on: ubuntu-latest
47
59
  steps:
@@ -49,35 +61,25 @@ jobs:
49
61
  uses: actions/checkout@v4
50
62
  with:
51
63
  fetch-depth: 0
52
- - name: Check for changeset
64
+
65
+ - name: Setup Node
66
+ uses: actions/setup-node@v6
67
+ with:
68
+ node-version: 24
69
+
70
+ # Publishing is the default: every PR must add a changeset. The only
71
+ # exemptions are the changesets bot's Version Packages PR (it consumes
72
+ # changesets and adds none) and PRs explicitly labelled `no-publish`.
73
+ # Deciding that in code keeps the rule unit-tested; this step only feeds
74
+ # it the diff and the PR metadata.
75
+ - name: Require a changeset unless labelled no-publish
53
76
  env:
54
77
  BASE_REF: ${{ github.event.pull_request.base.ref }}
78
+ PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
79
+ PR_ACTOR: ${{ github.event.pull_request.user.login }}
80
+ PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }}
55
81
  run: |
56
- # Paths that require a changeset when modified
57
- VERSIONED_PATHS='extensions/|skills/|agents/|package\.json|plugin\.json'
58
-
59
- changed=$(git diff --name-only "origin/${BASE_REF}...HEAD")
60
- echo "Changed files:"
61
- echo "$changed"
62
-
63
- versioned=$(echo "$changed" | grep -E "$VERSIONED_PATHS" || true)
64
- if [ -z "$versioned" ]; then
65
- echo "No versioned paths changed — skipping changeset check."
66
- exit 0
67
- fi
68
-
69
- changesets=$(echo "$changed" | grep -E '^\.changeset/.*\.md$' | grep -v 'README.md' || true)
70
- if [ -z "$changesets" ]; then
71
- echo ""
72
- echo "::error::This PR modifies versioned paths but does not include a changeset."
73
- echo "Run 'pnpm changeset' to add one. See: style-versioning skill."
74
- echo ""
75
- echo "Versioned paths changed:"
76
- echo "$versioned"
77
- exit 1
78
- fi
79
-
80
- echo "Changeset found: $changesets"
82
+ git diff --name-status "origin/${BASE_REF}...HEAD" | node scripts/check-changeset.mjs
81
83
 
82
84
  lint:
83
85
  name: Lint
@@ -263,6 +265,22 @@ jobs:
263
265
  - name: Install dependencies
264
266
  run: pnpm install --frozen-lockfile
265
267
 
268
+ # Labels are invisible on a push event, so resolve the PR merged for this
269
+ # commit through the API and read its labels. No new secret or scope: the
270
+ # job already holds pull-requests: write, gh is preinstalled, and
271
+ # GITHUB_TOKEN is already here. A commit with no merged PR (direct push)
272
+ # resolves to no labels — publish stays the default, so attempting to
273
+ # publish an already-published version still fails loudly.
274
+ - name: Resolve publish command
275
+ id: gate
276
+ env:
277
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
278
+ run: |
279
+ labels=$(gh api "repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" \
280
+ --jq '[.[] | select(.merged_at != null) | .labels[].name]')
281
+ publish=$(node scripts/resolve-publish-command.mjs "$labels")
282
+ echo "publish=${publish}" >> "$GITHUB_OUTPUT"
283
+
266
284
  - name: Create Release Pull Request or Publish
267
285
  id: changesets
268
286
  uses: changesets/action@v1
@@ -277,7 +295,9 @@ jobs:
277
295
  # committed needs regenerating) before the action commits, so the
278
296
  # Version Packages PR always passes the drift gates.
279
297
  version: pnpm version-packages
280
- publish: pnpm publish-package
298
+ # pnpm publish-package when the merged PR is unlabelled, pnpm
299
+ # publish-noop when it carries `no-publish`.
300
+ publish: ${{ steps.gate.outputs.publish }}
281
301
  commit: "chore: version packages"
282
302
  title: "chore: version packages"
283
303
  env:
package/AGENTS.md CHANGED
@@ -13,7 +13,7 @@ available in the author's coding sessions.
13
13
 
14
14
  ```
15
15
  agent-cortex/
16
- ├── package.json # PI package manifest (pi: { extensions, skills, packages })
16
+ ├── package.json # PI package manifest (pi: { extensions, skills }) + pi runtime deps
17
17
  ├── plugin.json # GitHub Copilot plugin manifest — agents, skills, MCP servers
18
18
  ├── agents/ # Custom agents — canonical composable <name>/ dirs
19
19
  │ ├── *.agent.md # generated from <name>/ by scripts/build-copilot-agents.mjs
@@ -38,7 +38,7 @@ agent-cortex/
38
38
  ├── claude-extras/ # Hand-authored Claude plugin extras
39
39
  │ ├── .mcp.json # MCP servers (context7, github) — copied into installs
40
40
  │ └── scripts/ # statusline-command.sh — copied into installs (executable)
41
- └── package.json # PI package manifest (pi: { extensions, skills, packages })
41
+ └── package.json # PI package manifest (pi: { extensions, skills }) + pi runtime deps
42
42
  ```
43
43
 
44
44
  ## Versioning
@@ -53,6 +53,11 @@ Generated automatically by changesets. Do not edit `CHANGELOG.md` manually.
53
53
 
54
54
  ## Coding Conventions
55
55
 
56
+ - **Landing method**: pull requests are squash-merged. Merge commits are disabled in the
57
+ repository settings and `main` requires linear history, so a branch's own commit shape never
58
+ reaches `main` — bring a behind branch up to date by merging `main` in rather than rebasing it.
59
+ `docs/commit-signing.md` records why squash is the only landing path that produces a signed
60
+ commit; the rebase-versus-merge reasoning lives in the `git-workflow` skill.
56
61
  - **Never block the main thread**: no `*Sync` functions (`readFileSync`, `spawnSync`, …).
57
62
  Tooling code in `bin/`, `scripts/`, `lib/`, `hooks/`, and `test/` must be async —
58
63
  enforced by `scripts/check-no-sync.mjs` (runs via `pnpm lint`). Skill-owned
@@ -63,6 +68,12 @@ Generated automatically by changesets. Do not edit `CHANGELOG.md` manually.
63
68
 
64
69
  ## Skill Conventions
65
70
 
71
+ - **Skills are portable, not repo documentation.** `skills/` is installed onto arbitrary machines
72
+ (Copilot, pi, Claude Code), so it describes general practice and the author's portable
73
+ preferences — never how this repository is configured or how to work in it. Facts about this
74
+ checkout (branch bases, merge method, signature rules, required checks, release mechanics) belong
75
+ in `AGENTS.md`, `docs/`, or the tracker. Repo-local `.agents/skills/` follows the same rule: a
76
+ skill is a skill, not a place for this repo's configuration.
66
77
  - `SKILL.md` must include a YAML front-matter block with `name` and `description`.
67
78
  - `description` must be ≤ 1024 characters. First sentence: what it does. Second: "Use when…".
68
79
  - Keep `SKILL.md` near the ~150-line target in `docs/skills/skill-anatomy.md`, overflowing into
@@ -96,6 +107,16 @@ Generated automatically by changesets. Do not edit `CHANGELOG.md` manually.
96
107
  `--output <dir>` is the generate-only form (tests/CI materialiser checks); `ralph`
97
108
  is authored natively in `agents-native/` and copied verbatim; the other agents compose from their
98
109
  `claude/` harness dirs).
110
+ - The pi package is materialised by `bin/installers/pi.mjs`: `agent-cortex install pi` stages a
111
+ self-contained pi package in `~/.agent-cortex/pi` (manifest, flat rendered agents, token-substituted
112
+ skills, a copy of `extensions/`, `token-map.json`, and the bundled `pi.packages` closure under
113
+ `node_modules/`) and registers that directory with pi through the `pi` CLI — pi's own
114
+ `~/.pi/agent/settings.json` is read, never written. `--output <dir>` stages only (no registration,
115
+ no sweep) and `--dry-run` reports the plan. Once registration succeeds, a one-shot migration sweeps
116
+ the copies the pre-staged-model installer rendered into `~/.pi/agent`, attributing them by name from
117
+ the package root and recording completion in `~/.agent-cortex/legacy-sweep.json`; `~/.pi/agent/extensions`
118
+ is never touched. Run it from the main checkout: the staged package is a snapshot of the checkout you
119
+ invoked, so a run from a ralph worktree stages that worktree's content.
99
120
  - Keep orchestration logic in the agent file; extract shared per-task workflow into a skill
100
121
  so it can be reused (e.g. ralph delegates per-task stage execution to `run-pipeline-stage`).
101
122
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,171 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.43.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b6fe713: `agent-cortex install pi` now sweeps the artifacts the pre-staged-model
8
+ installer rendered into pi's own user directory. Once the staged package at
9
+ `~/.agent-cortex/pi` is registered, those copies are a second source of the same
10
+ agent or skill — the skill-collision diagnostics the staged model exists to
11
+ remove — so the installer removes the ones it can attribute to agent-cortex:
12
+ `agents/<name>.agent.md` and `skills/<group>/<name>` for agents and skills it
13
+ ships or once shipped (`pipeline-worker`, the `migration` skill), plus a
14
+ `.agent-cortex-install.json` path record left in that directory listing those
15
+ copies.
16
+
17
+ The sweep is a one-time migration, not a standing rule: attribution is by name
18
+ from the package root, which cannot tell a rendered copy from a file you wrote
19
+ under the same name, so a finished migration is recorded in
20
+ `~/.agent-cortex/legacy-sweep.json` — keyed by the pi user directory, never
21
+ inside it — and nothing there is removed again. A completed migration leaves
22
+ `~/.pi/agent` with no agent-cortex file in it. Only a migration that removed
23
+ everything it attributed, from directories it could read, is recorded: a copy
24
+ that could not be deleted, or a directory that could not be listed, is warned
25
+ about and retried on the next install, and a record that cannot be written
26
+ warns without failing an install that has already registered. Files whose name this package
27
+ does not ship are left alone, and pi's
28
+ `extensions/` directory is never touched — no installer has ever written there,
29
+ so its contents are yours. pi's own `settings.json` and `keybindings.json` are
30
+ never touched either.
31
+
32
+ The sweep runs only after registration succeeds, follows the settings file the
33
+ installer reads (`~/.pi/agent`), and is skipped entirely by `--dry-run` and
34
+ `--output <dir>`. A pi agent dir relocated with `PI_CODING_AGENT_DIR` keeps its
35
+ copies: the installer does not inspect it.
36
+
37
+ - b6fe713: `agent-cortex install pi` now registers the staged package with pi: after
38
+ staging `~/.agent-cortex/pi` it drives pi's own `pi install` so pi loads the
39
+ agents, skills, and extensions from that directory. Registration is idempotent
40
+ by state — a re-run after `git pull && agent-cortex install pi` changes nothing —
41
+ and a source pointing at a raw agent-cortex checkout is removed first, so exactly
42
+ one agent-cortex source stays configured. A superseded source that `pi remove`
43
+ cannot drop fails the install rather than reporting success — two configured
44
+ sources both load, so leaving both in place is a failed registration, not a
45
+ warning. The installer still writes nothing into `~/.pi/agent`; it only reads
46
+ pi's `settings.json` to decide the plan. A missing `pi` CLI warns and prints the
47
+ exact commands (failing only with `--require-register`), and `--dry-run` prints
48
+ the registration plan without writing or spawning anything. `--output <dir>`
49
+ remains the stage-only form.
50
+ - b6fe713: `agent-cortex install pi` now stages a self-contained pi package at
51
+ `~/.agent-cortex/pi` (manifest, agents, skills, extensions, and the bundled
52
+ pi-questions / pi-web-access trees) instead of writing into pi's own
53
+ `~/.pi/agent` directories. Register the staged package with `pi install
54
+ ~/.agent-cortex/pi`. `--no-provision` is gone — the required packages travel
55
+ inside the staged tree — and the installer no longer materialises pi's
56
+ `settings.json` / `keybindings.json`.
57
+
58
+ The committed `pi/settings.json` template drops its `npm:pi-questions` and
59
+ `npm:pi-web-access@0.10.7` entries for the same reason: once the staged package
60
+ carries those tools, the `packages` array holds only the optional personal
61
+ toolset (`@getpipher/vision`, `@hypabolic/pi-hypa`, `context-mode`), so there is
62
+ one source of truth for `ask_questions` / `fetch_content` instead of two that can
63
+ drift. Its `../../src/agent-cortex` entry goes too, so the staged package is the
64
+ only agent-cortex source pi loads.
65
+
66
+ The `subagent` extension discovers agents from the package it ships inside as
67
+ well as from pi's own user agent dir, so a staged package supplies the agents it
68
+ ships instead of depending on `~/.pi/agent` copies. A source checkout's
69
+ composable `agents/` tree is left to `agent-modes`, which composes it at runtime;
70
+ the flat renders committed there are for other harnesses and are not read as pi
71
+ agents. Frontmatter `tools` written as a YAML sequence is accepted alongside the
72
+ scalar form.
73
+
74
+ ### Patch Changes
75
+
76
+ - e15cf37: Stop documenting a fixed local-bin `bd` path as the authoritative bd binary. The migration technical-direction doc now describes `bd` as resolved from `PATH` with `BD_PATH` as the override, warns against pinning an orphaned binary (schema-version mismatch risk), and its script-package constraint reflects the post-D2 root workspace membership.
77
+ - 937f610: Document the commit-signing expectation behind the "Commits must have verified signatures"
78
+ merge block. Agent commits are GPG-signed by the environment and verify as `valid`; the
79
+ `github-actions[bot]` commit on a Version Packages PR is `unsigned` because `GITHUB_TOKEN`
80
+ cannot sign. The squash merge GitHub performs re-signs the commit that lands, which is why
81
+ `main`'s history reads `valid` — but a squash merge does **not** escape an enforced signature
82
+ rule, since GitHub checks head-branch commits through its test merge, so the remedy in that
83
+ case is a signed commit rather than a different merge method. The new `docs/commit-signing.md`
84
+ records the evidence, the ruleset reads that show no signature rule applied to `main` at
85
+ either repository or organization level (while org ruleset definitions stay unreadable), the
86
+ open acceptance criterion with its human merge probe, and the release policy: land Version
87
+ Packages by squash merge, never put a signing key in CI secrets, with an explicit
88
+ contingency if an organization-level signature rule turns out to be enforced.
89
+ `style-versioning` points at it.
90
+ - 0f9b7f8: Drop the hardcoded skill blurb from the pi `session-start` extension. The blurb advertised three
91
+ skills with `/skill:<name>` syntax that pi cannot resolve, and pi's own system prompt already lists
92
+ every discovered skill with its absolute `SKILL.md` path, so the local copy was incomplete and
93
+ drifted from `skills/` silently — verbatim text an agent reads first each session (and on
94
+ 2026-10-27 misread a missing-extension outage as a missing-skills problem). Session start now
95
+ injects only the project context files (AGENTS.md, `docs/user-preferences.md`). The extension moves
96
+ to `extensions/session-start/` (`index.ts`, `package.json`, `README.md`, `index.test.ts`) per the
97
+ one-directory-per-extension convention; the new test pins the contract: no `/skill:` syntax, no
98
+ hardcoded skill names, context files injected when present, nothing injected when absent.
99
+ - 6be3679: Stop the Release job re-publishing an already-published version. Every PR now
100
+ has to add a changeset unless it is labelled `no-publish` (the changesets bot's
101
+ Version Packages PR is exempt), and the Release job resolves the merged PR's
102
+ labels for the pushed commit before choosing between `publish-package` and the
103
+ new `publish-noop` script. `README.md` and the `style-versioning` skill document
104
+ the gate, including the out-of-band requirement that the `no-publish` label exists
105
+ in the repository's label settings before the opt-out can work.
106
+ - f86c767: `agent-cortex install pi` no longer registers the git worktree it was invoked from as the pi package source. When the package root is a linked worktree (its `.git` is a file and `git rev-parse --git-common-dir` differs from `--git-dir`), the settings `packages` entry now resolves to the main checkout, with a warning naming the checkout that was registered instead. Previously a run inside `.agent-cortex/worktrees/<id>` left `~/.pi/agent/settings.json` pointing at that gitignored, ephemeral worktree — which dangles as soon as the worktree is removed.
107
+ - 0ad2576: Fix subagents starting with no tools at all. `agent-cortex install pi` rendered
108
+ each generated agent's tool list as one space-separated scalar
109
+ (`tools: "bash read grep find task …"`), but every consumer splits that scalar on
110
+ commas: the subagent extension turned it into `pi --tools "<one bogus name>"`,
111
+ whose allowlist matched no tool, so delegated agents (ralph, pipeline workers)
112
+ ran with only MCP tools and reported the `task` tool — and every other tool — as
113
+ missing. The pi renderer now emits the comma-separated form that
114
+ `parseFrontmatter`, the subagent extension, and `pi --tools` all agree on;
115
+ `parseToolList` in the subagent extension and agent-modes' flat-frontmatter
116
+ parser accept comma- and whitespace-separated lists so a re-render cannot
117
+ silently zero out a tool list again; and a round-trip test asserts a rendered
118
+ agent's tools survive the parse.
119
+ - 65cb7f8: The `git-workflow` skill now says how to bring a behind branch up to date, and
120
+ why the answer is usually "merge the base in" rather than "rebase". `git rebase`
121
+ drops merge commits and the patches they carry, so rebasing a branch that has
122
+ already merged the base silently reverts what that merge resolved — the conflict
123
+ resolutions, and any fix from the base the merge brought in — and the result
124
+ reads like a regression in the branch's own change. `--rebase-merges` keeps the
125
+ structure but re-opens the same conflicts, so it earns its cost only when the
126
+ branch history is the deliverable. Rebasing stays right for a linear branch that
127
+ has not been reviewed yet, and then it needs `--force-with-lease`.
128
+
129
+ The choice follows from the repository's landing method, not from taste: a squash
130
+ merge collapses the branch, so its shape never reaches the base branch, while a
131
+ rebase-and-merge requires linear history and therefore a rebase before the first
132
+ review rather than after a merge. `AGENTS.md` now records this repository's
133
+ answer — squash merge, merge commits disabled, `main` requires linear history —
134
+ because that is a fact about this checkout, not portable practice.
135
+
136
+ - 2ae24da: Document what `disable-model-invocation` actually does and how each harness handles it: preserved and honoured by pi and Claude Code, honoured by Copilot CLI but at the cost of model-mediated invocation. Corrects the front-matter guidance in `docs/skills/skill-anatomy.md`, which described the field as affecting how the body is interpreted.
137
+ - 5da1d45: Make pipeline-stage workers prove they delivered, and stop dispatch from being
138
+ improvised.
139
+
140
+ A stage worker's `---REPORT---` was taken at face value: nothing instructed it to
141
+ commit or push, and nothing checked the worktree afterwards. A worker could
142
+ report `OUTCOME: SUCCESS` and leave its changes uncommitted, and ralph would
143
+ advance the parent and open the feature PR from a branch holding none of the
144
+ work. Six runs needed a human to finish the tree by hand.
145
+
146
+ - The stage contract now requires delivery: the stage-runner prompt and the
147
+ code, fix, and document playbooks commit and push before reporting, and the
148
+ REPORT gained `COMMIT`, `PUSHED`, and `PR` fields.
149
+ - `scripts/lib/verify-worker-tree.mjs` checks the worktree itself — clean tree,
150
+ at least one commit ahead of the recorded base — so ralph gates each stage on
151
+ evidence rather than on the worker's prose.
152
+ - ralph stops and reports a missing subagent tool instead of backgrounding
153
+ `pi -p … &`. That substitution has no completion signal, so a finished worker
154
+ and a killed one look identical — the trap that produced the buffered-log and
155
+ post-exit liveness misreadings.
156
+ - `token-map.json` records that pi's `task`/`read_agent`/`wait_for_agents` come
157
+ from this repo's `extensions/subagent`, not from pi.
158
+
159
+ - 931a20e: Document the one manual step in the release loop: the Version Packages PR arrives
160
+ `BLOCKED` with an empty check list because `changesets/action` pushes that branch
161
+ with `GITHUB_TOKEN`, so GitHub parks the CI run the push creates behind "Approve
162
+ workflows to run" and a parked run reports nothing. Approve it — merge box click,
163
+ or the documented `gh api … /approve` one-liner — and the five required checks
164
+ start. The approval is per head commit, so it is needed again after any further
165
+ merge to `main`, which force-updates the version branch. Adding a PAT or GitHub App
166
+ token would let the bot trigger its own CI, but one click per release is a better
167
+ trade than a long-lived credential in repository secrets.
168
+
3
169
  ## 1.42.0
4
170
 
5
171
  ### Minor Changes