@aperant/framework 0.6.6 → 0.6.7
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.
- package/CHANGELOG.md +155 -0
- package/dist/cli/commands/check-version.d.mts.map +1 -1
- package/dist/cli/commands/check-version.mjs +76 -1
- package/dist/cli/commands/check-version.mjs.map +1 -1
- package/dist/cli/commands/ci-watch.d.mts.map +1 -1
- package/dist/cli/commands/ci-watch.mjs +34 -4
- package/dist/cli/commands/ci-watch.mjs.map +1 -1
- package/dist/cli/commands/init.d.mts +8 -1
- package/dist/cli/commands/init.d.mts.map +1 -1
- package/dist/cli/commands/init.mjs +91 -9
- package/dist/cli/commands/init.mjs.map +1 -1
- package/dist/cli/commands/release-notes.d.mts +11 -0
- package/dist/cli/commands/release-notes.d.mts.map +1 -0
- package/dist/cli/commands/release-notes.mjs +173 -0
- package/dist/cli/commands/release-notes.mjs.map +1 -0
- package/dist/cli/config/load.d.mts +14 -0
- package/dist/cli/config/load.d.mts.map +1 -1
- package/dist/cli/config/load.mjs +40 -0
- package/dist/cli/config/load.mjs.map +1 -1
- package/dist/cli/config/upgrade-gitignore.d.mts.map +1 -1
- package/dist/cli/config/upgrade-gitignore.mjs +6 -0
- package/dist/cli/config/upgrade-gitignore.mjs.map +1 -1
- package/dist/cli/consistency/parse-review.d.mts.map +1 -1
- package/dist/cli/consistency/parse-review.mjs +4 -1
- package/dist/cli/consistency/parse-review.mjs.map +1 -1
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +2 -0
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
- package/dist/cli/gate/gates/review-clean.mjs +4 -2
- package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
- package/dist/cli/install/update-chips.d.mts +23 -0
- package/dist/cli/install/update-chips.d.mts.map +1 -1
- package/dist/cli/install/update-chips.mjs +60 -0
- package/dist/cli/install/update-chips.mjs.map +1 -1
- package/dist/cli/release-notes/compile.d.mts +38 -0
- package/dist/cli/release-notes/compile.d.mts.map +1 -0
- package/dist/cli/release-notes/compile.mjs +244 -0
- package/dist/cli/release-notes/compile.mjs.map +1 -0
- package/dist/cli/release-notes/draft.d.mts +73 -0
- package/dist/cli/release-notes/draft.d.mts.map +1 -0
- package/dist/cli/release-notes/draft.mjs +120 -0
- package/dist/cli/release-notes/draft.mjs.map +1 -0
- package/dist/cli/release-notes/output-dir.d.mts +20 -0
- package/dist/cli/release-notes/output-dir.d.mts.map +1 -0
- package/dist/cli/release-notes/output-dir.mjs +42 -0
- package/dist/cli/release-notes/output-dir.mjs.map +1 -0
- package/dist/cli/release-notes/persona-filter.d.mts +51 -0
- package/dist/cli/release-notes/persona-filter.d.mts.map +1 -0
- package/dist/cli/release-notes/persona-filter.mjs +127 -0
- package/dist/cli/release-notes/persona-filter.mjs.map +1 -0
- package/dist/cli/release-notes/publish.d.mts +23 -0
- package/dist/cli/release-notes/publish.d.mts.map +1 -0
- package/dist/cli/release-notes/publish.mjs +125 -0
- package/dist/cli/release-notes/publish.mjs.map +1 -0
- package/dist/cli/release-notes/ship-autodraft.d.mts +37 -0
- package/dist/cli/release-notes/ship-autodraft.d.mts.map +1 -0
- package/dist/cli/release-notes/ship-autodraft.mjs +97 -0
- package/dist/cli/release-notes/ship-autodraft.mjs.map +1 -0
- package/dist/types/config.d.ts +11 -7
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +133 -133
- package/skills/apt-close-task/SKILL.md +25 -0
- package/skills/apt-diagram/SKILL.md +45 -9
- package/skills/apt-release-notes/SKILL.md +193 -0
- package/skills/apt-release-notes/appendices/persona-voice.md +59 -0
- package/skills/apt-setup/SKILL.md +146 -3
- package/skills/apt-ship/SKILL.md +47 -0
- package/skills/apt-update/SKILL.md +51 -9
- package/skills/apt-watch-ci/SKILL.md +2 -2
- package/src/cli/commands/check-version.mjs +73 -1
- package/src/cli/commands/ci-watch.mjs +35 -4
- package/src/cli/commands/init.mjs +101 -9
- package/src/cli/commands/release-notes.mjs +187 -0
- package/src/cli/config/load.mjs +37 -0
- package/src/cli/config/upgrade-gitignore.mjs +6 -0
- package/src/cli/consistency/parse-review.mjs +3 -1
- package/src/cli/dispatch.mjs +2 -0
- package/src/cli/gate/gates/review-clean.mjs +4 -2
- package/src/cli/install/update-chips.mjs +57 -0
- package/src/cli/release-notes/compile.mjs +261 -0
- package/src/cli/release-notes/draft.mjs +133 -0
- package/src/cli/release-notes/output-dir.mjs +52 -0
- package/src/cli/release-notes/persona-filter.mjs +126 -0
- package/src/cli/release-notes/publish.mjs +128 -0
- package/src/cli/release-notes/ship-autodraft.mjs +106 -0
- package/templates/config.json +28 -3
- package/workflows/docs.md +12 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: apt:release-notes
|
|
3
|
+
description: "Persona-aware user-facing release notes — draft, compile, publish"
|
|
4
|
+
apt-skill-version: {{APT_VERSION}}
|
|
5
|
+
stage: ops
|
|
6
|
+
intent: capture
|
|
7
|
+
when_to_use: "The user wants to draft, compile, or publish persona-aware user-facing release notes for a project that opted in via /apt:setup."
|
|
8
|
+
user_invocable: true
|
|
9
|
+
internal: false
|
|
10
|
+
spawns_agent: false
|
|
11
|
+
agent_name: null
|
|
12
|
+
task_context: none
|
|
13
|
+
default_track: DEEP
|
|
14
|
+
default_execution_mode: auto
|
|
15
|
+
execution_modes:
|
|
16
|
+
- auto
|
|
17
|
+
- step
|
|
18
|
+
allowed-tools: "Bash, Read, Write, Edit, Grep, Glob"
|
|
19
|
+
argument-hint: "apt:release-notes [--draft --pr <n> | --compile <tag> | --publish <tag>]"
|
|
20
|
+
gates: []
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<objective>
|
|
24
|
+
Produce user-facing release notes for a project that opted in via
|
|
25
|
+
`/apt:setup` → Batch 9 → "Publish user-facing release notes? Yes". This
|
|
26
|
+
skill is separate from `/apt:docs changelog` — the dev-flavored
|
|
27
|
+
`CHANGELOG.md` surface stays its responsibility; this skill owns the
|
|
28
|
+
persona-voice-tuned narrative that ships to end users.
|
|
29
|
+
|
|
30
|
+
Three modes:
|
|
31
|
+
|
|
32
|
+
- `--draft --pr <n>` — write a per-PR fragment file (towncrier-style).
|
|
33
|
+
Usually called automatically by `/apt:close-task` on merge; rarely
|
|
34
|
+
invoked by hand.
|
|
35
|
+
- `--compile <tag>` — at release time, read every accumulated fragment,
|
|
36
|
+
filter by the configured persona tier, write a single per-tag
|
|
37
|
+
narrative file, and clear the unreleased dir.
|
|
38
|
+
- `--publish <tag>` — shell `gh release create/edit` to push the
|
|
39
|
+
compiled body to the GitHub Release.
|
|
40
|
+
|
|
41
|
+
Rules of the road (locked):
|
|
42
|
+
|
|
43
|
+
- **Per-project opt-in (spec ID-7).** Presence of `.aperant/personas.json`
|
|
44
|
+
does NOT auto-enable this skill. The toggle is
|
|
45
|
+
`config.changelog.release_notes.enabled` — opt-in is explicit via
|
|
46
|
+
`/apt:setup` Batch 9.
|
|
47
|
+
- **No LLM calls in the CLI (spec ID-3).** The deterministic verbs
|
|
48
|
+
(`apt-tools release-notes draft|compile|publish`) handle file I/O only.
|
|
49
|
+
Persona-voice tuning is authored by you — the host LLM — using the
|
|
50
|
+
`appendices/persona-voice.md` loader.
|
|
51
|
+
- **Triage bucket is non-negotiable (spec ID-6).** Fragments without a
|
|
52
|
+
recognized persona tag MUST surface in a `## Needs Triage` section at
|
|
53
|
+
compile time. Never silent-drop. This is the human curator's safety
|
|
54
|
+
net against trust collapse over time.
|
|
55
|
+
- **Fast Path Guarantee (CLAUDE.md).** This skill is DEEP-track-only
|
|
56
|
+
surface area. `/apt:quick` never enters this skill; the
|
|
57
|
+
`apt-release-notes-quick-exempt.test.ts` regression test pins the
|
|
58
|
+
exemption.
|
|
59
|
+
</objective>
|
|
60
|
+
|
|
61
|
+
<your_environment>
|
|
62
|
+
- **Working directory:** The project root
|
|
63
|
+
- **apt-tools path:** `node packages/framework/bin/apt-tools.mjs` or the locally installed `apt-tools` binary.
|
|
64
|
+
- **gh CLI dependency:** required only for `--publish`. The `publish` verb returns a graceful `{status: 'skipped', reason: 'gh-not-found'}` envelope when `gh` is unavailable — the skill never crashes the host on a missing binary.
|
|
65
|
+
- **Output:** `docs/releases/_unreleased/PR-<n>.md` (fragments) + `docs/releases/<tag>.md` (compiled) + GitHub Release body (when published).
|
|
66
|
+
</your_environment>
|
|
67
|
+
|
|
68
|
+
<state_files>
|
|
69
|
+
## State Files
|
|
70
|
+
|
|
71
|
+
**Reads:**
|
|
72
|
+
- `.aperant/config.json` — `changelog.release_notes.{enabled, output_dir, persona_filter, require_pr_field}` and the stage blocks.
|
|
73
|
+
- `.aperant/personas.json` — persona roster + tier; used by `compile` to filter fragments.
|
|
74
|
+
- `docs/releases/_unreleased/PR-*.md` — fragments accumulated since the last tag.
|
|
75
|
+
- `PROJECT.md` (when present) — Core Value section feeds the hero narrative the host LLM authors at compile time.
|
|
76
|
+
|
|
77
|
+
**Writes:**
|
|
78
|
+
- `docs/releases/_unreleased/PR-<n>.md` — towncrier-style fragments (draft mode).
|
|
79
|
+
- `docs/releases/<tag>.md` — per-tag compiled narrative (compile mode).
|
|
80
|
+
- GitHub Release body via `gh release create/edit` (publish mode).
|
|
81
|
+
</state_files>
|
|
82
|
+
|
|
83
|
+
<process>
|
|
84
|
+
|
|
85
|
+
## 0. Hard-Gate + Opt-in Check
|
|
86
|
+
|
|
87
|
+
No hard gates (`gates: []`). Before running ANY mode, confirm the project opted in:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
rn_enabled=$(jq -r '.changelog.release_notes.enabled // false' .aperant/config.json)
|
|
91
|
+
if [ "$rn_enabled" != "true" ]; then
|
|
92
|
+
echo "[apt:release-notes] This project has not opted into user-facing release notes."
|
|
93
|
+
echo " Run /apt:setup → 'Changelog & Release Notes' to enable."
|
|
94
|
+
exit 0
|
|
95
|
+
fi
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
When opted out, exit cleanly — never crash, never write any file.
|
|
99
|
+
|
|
100
|
+
## 1. Parse Flags
|
|
101
|
+
|
|
102
|
+
The skill's three modes are mutually exclusive. Parse `$ARGUMENTS`:
|
|
103
|
+
|
|
104
|
+
- `--draft --pr <n> --note "<text>" [--persona <name>] [--commits <sha,sha,...>] [--date <YYYY-MM-DD>]` → Draft mode (§2).
|
|
105
|
+
- `--compile <tag>` → Compile mode (§3).
|
|
106
|
+
- `--publish <tag> [--dry-run]` → Publish mode (§4).
|
|
107
|
+
|
|
108
|
+
If no mode flag is passed, display a short usage message and exit.
|
|
109
|
+
|
|
110
|
+
## 2. Draft Mode
|
|
111
|
+
|
|
112
|
+
Used to write a per-PR fragment. Usually this is called automatically by `/apt:close-task` on merge (see `apt-close-task/SKILL.md` §2.6), but it can be invoked by hand for backfills or for manual fragments.
|
|
113
|
+
|
|
114
|
+
Hand the inputs to the deterministic CLI:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
node packages/framework/bin/apt-tools.mjs release-notes draft . \
|
|
118
|
+
--pr {pr_number} \
|
|
119
|
+
--note "{one-sentence user-impact note}" \
|
|
120
|
+
[--persona {persona-name}] \
|
|
121
|
+
[--date {ISO-date}]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The CLI is idempotent — re-running with identical inputs returns `{status: 'ok', written: false, reason: 'no-change'}`. Trust the envelope; do NOT manually inspect the file before running.
|
|
125
|
+
|
|
126
|
+
When you (the host LLM) compose the note, consult `appendices/persona-voice.md` for the persona-voice posture loader. The fragment goes into the user-facing pipeline; voice-tune accordingly.
|
|
127
|
+
|
|
128
|
+
## 3. Compile Mode
|
|
129
|
+
|
|
130
|
+
Used at release time to fold every accumulated fragment into a single per-tag narrative. The CLI does the deterministic work — your job is the narrative pass on the compiled body if the user wants polish beyond the structured per-PR list.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
node packages/framework/bin/apt-tools.mjs release-notes compile . --tag {tag}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The envelope reports:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"status": "ok",
|
|
141
|
+
"command": "release-notes-compile",
|
|
142
|
+
"tag": "{tag}",
|
|
143
|
+
"path": "docs/releases/{tag}.md",
|
|
144
|
+
"fragments_total": N,
|
|
145
|
+
"fragments_in_body": M,
|
|
146
|
+
"fragments_in_triage": K,
|
|
147
|
+
"fragments_filtered": F,
|
|
148
|
+
"cleared_unreleased": true
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
If `fragments_in_triage > 0`, ALERT the user before proceeding to publish — those fragments need persona curation. Offer two options:
|
|
153
|
+
|
|
154
|
+
1. Edit the compiled file directly to move triage entries into the right tier section (and re-tag the persona in the original PR if appropriate).
|
|
155
|
+
2. Proceed to publish with the triage section intact — sometimes the right call when the unmapped change is genuinely cross-cutting.
|
|
156
|
+
|
|
157
|
+
If `fragments_total === 0`, gently note that there's nothing to compile yet (likely the user hasn't merged any PRs since the last release).
|
|
158
|
+
|
|
159
|
+
## 4. Publish Mode
|
|
160
|
+
|
|
161
|
+
Push the compiled body to the GitHub Release:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
node packages/framework/bin/apt-tools.mjs release-notes publish . --tag {tag}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The CLI handles both `gh release create` (new release) and `gh release edit` (existing release). When `gh` is not on PATH, the envelope returns `{status: 'skipped', reason: 'gh-not-found'}` — surface the message to the user and suggest installing the GitHub CLI.
|
|
168
|
+
|
|
169
|
+
For local-only verification, pass `--dry-run` to skip the network call and just confirm the body file exists.
|
|
170
|
+
|
|
171
|
+
## 5. Re-invocation
|
|
172
|
+
|
|
173
|
+
Each mode is safe to re-run:
|
|
174
|
+
|
|
175
|
+
- `draft` — idempotent per spec AC2.
|
|
176
|
+
- `compile` — overwrites `<tag>.md` and re-clears `_unreleased/`. Running compile twice in a row with no new fragments produces an empty body — careful.
|
|
177
|
+
- `publish` — `gh release edit` is the second call's branch, so the GitHub Release body is replaced cleanly.
|
|
178
|
+
|
|
179
|
+
</process>
|
|
180
|
+
|
|
181
|
+
<appendices>
|
|
182
|
+
- `appendices/persona-voice.md` — Aperant-authored posture loader for voice-tuning the note text per persona archetype.
|
|
183
|
+
</appendices>
|
|
184
|
+
|
|
185
|
+
<integration>
|
|
186
|
+
| Skill | How it interacts |
|
|
187
|
+
|-------|------------------|
|
|
188
|
+
| `/apt:setup` Batch 9 | Writes the opt-in toggle + audience + persona-filter that this skill reads. |
|
|
189
|
+
| `/apt:ship` §2.6 | Auto-drafts the `Release note: …` line in the PR body BEFORE this skill runs; ship blocks if `require_pr_field=true` AND no signal. |
|
|
190
|
+
| `/apt:close-task` §2.6 | Invokes `apt-tools release-notes draft` on merge — drops the per-PR fragment automatically. |
|
|
191
|
+
| `/apt:docs changelog` | The dev-flavored sibling. Stays its own surface; this skill does NOT touch `CHANGELOG.md`. |
|
|
192
|
+
| `/apt:personas` | Produces the `.aperant/personas.json` roster that compile mode filters against. |
|
|
193
|
+
</integration>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Persona-voice posture loader
|
|
2
|
+
|
|
3
|
+
Aperant-authored posture loader consumed by `apt-release-notes/SKILL.md`.
|
|
4
|
+
Per the CLAUDE.md `appendices/` convention, this file augments the parent
|
|
5
|
+
skill's context — it is NOT independently router-invocable.
|
|
6
|
+
|
|
7
|
+
## Why this exists
|
|
8
|
+
|
|
9
|
+
The dev `CHANGELOG.md` (owned by `/apt:docs changelog`) describes WHAT
|
|
10
|
+
changed in engineering terms: file paths, function names, task IDs,
|
|
11
|
+
schema bumps. Release notes describe WHY the change matters to a
|
|
12
|
+
specific human. Same merge, different voice. The release-notes pipeline
|
|
13
|
+
hands you a structured fragment payload — your job is to tune the prose
|
|
14
|
+
so the line reads like product-marketing copy for the persona, not like
|
|
15
|
+
a commit message with the prefix stripped.
|
|
16
|
+
|
|
17
|
+
## Posture rules (apply when composing a `Release note: …` line)
|
|
18
|
+
|
|
19
|
+
1. **Verb first.** Start with the user's outcome — "See your shipped
|
|
20
|
+
builds in one tab" — not the implementation ("Added a `/builds` tab
|
|
21
|
+
to the dashboard"). The reader doesn't care that you added a tab;
|
|
22
|
+
they care that they can now see builds.
|
|
23
|
+
2. **Persona-specific framing.**
|
|
24
|
+
- **End-user persona (audience: `end-user`)** — plain language, no
|
|
25
|
+
jargon. Avoid framework names, file paths, API endpoint paths.
|
|
26
|
+
One sentence. Concrete value.
|
|
27
|
+
- **Developer / API persona (audience: `developer-api`)** — API + SDK
|
|
28
|
+
semantics are the value proposition. Naming a new method is good.
|
|
29
|
+
Linking the migration page is good. Don't hide the implementation
|
|
30
|
+
details; they ARE the user-visible surface for this persona.
|
|
31
|
+
- **Internal-team persona (audience: `internal-team`)** — lighter
|
|
32
|
+
polish, faster cadence. One sentence is still ideal but the
|
|
33
|
+
internal team is fine with a touch of process-jargon ("we now
|
|
34
|
+
auto-rotate session tokens") when it conveys real value.
|
|
35
|
+
3. **Triage bucket is your friend.** If you don't have strong signal
|
|
36
|
+
for the persona's framing — leave the line in the form the auto-draft
|
|
37
|
+
produced. The compile step will surface it under `## Needs Triage`
|
|
38
|
+
and the human curator can decide whether to ship it. Forced
|
|
39
|
+
confidence in the note voice is a regression class.
|
|
40
|
+
4. **Tone matches the persona's archetype.** "The Vibe Coder" persona
|
|
41
|
+
wants energy ("Now you can branch off any session in one click").
|
|
42
|
+
"The Reviewer" persona wants precision ("Branch-from-session now
|
|
43
|
+
preserves your review queue with exact ordering"). Read the
|
|
44
|
+
archetype field in personas.json before composing.
|
|
45
|
+
5. **No emoji unless the project's design taste explicitly calls for
|
|
46
|
+
it.** Aperant's house style is restrained; consumer-facing apps with
|
|
47
|
+
"playful" design tokens may differ — defer to the project's design
|
|
48
|
+
brief if one exists.
|
|
49
|
+
|
|
50
|
+
## What this loader does NOT cover
|
|
51
|
+
|
|
52
|
+
- Compile-time narrative beyond the per-PR sentence — that's the host
|
|
53
|
+
LLM's wider job at `/apt:release-notes --compile`, informed by
|
|
54
|
+
`PROJECT.md` Core Value, the personas roster, and the structured
|
|
55
|
+
per-tier sections the compiler writes.
|
|
56
|
+
- Translating between audience values — picking the right audience is
|
|
57
|
+
set at `/apt:setup` Batch 9 time. The audience field doesn't change
|
|
58
|
+
mid-release; voice changes only at the per-fragment level inside the
|
|
59
|
+
audience already chosen.
|
|
@@ -120,7 +120,20 @@ Also check for global defaults at `~/.aperant/defaults.json` — if they exist a
|
|
|
120
120
|
|
|
121
121
|
## 1b. Review Mode (existing config detected)
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
**Canonical signal.** The `onboarding.completed` marker inside `.aperant/config.json` is the canonical signal that a user has walked through the full /apt:setup batch sequence. Structural completeness of the JSON config does NOT imply onboarding completeness — `packages/framework/templates/config.json` ships with all keys populated to sensible defaults by design, so the JSON alone cannot disambiguate "fresh seed" from "fully tuned." Future template additions must not assume otherwise.
|
|
124
|
+
|
|
125
|
+
**Dispatch.** Read `.aperant/config.json` and inspect the `onboarding` block. Branch on the four marker states:
|
|
126
|
+
|
|
127
|
+
| Marker state | `/apt:setup` behaviour |
|
|
128
|
+
|-------------------------------------------------------------|-----------------------------------------------------------------------|
|
|
129
|
+
| No `onboarding` block (legacy pre-fix config) | enter **review mode** (preserve current behaviour; do NOT re-onboard) |
|
|
130
|
+
| `onboarding.seeded_from_template: true` AND `onboarding.completed: false` | enter **full Batch 1-8 flow** (treat as fresh setup) |
|
|
131
|
+
| `onboarding.completed: true` | enter **review mode** |
|
|
132
|
+
| `--reset` flag passed | enter **full Batch 1-8 flow** always (overrides marker) |
|
|
133
|
+
|
|
134
|
+
`init` writes the `seeded_from_template: true` / `completed: false` marker on fresh seed only (see `packages/framework/src/cli/commands/init.mjs::applyInterviewAnswers`). The upgrade path never writes the marker — that is why a legacy unmarked config routes to review mode.
|
|
135
|
+
|
|
136
|
+
If the dispatch lands you in review mode, continue below. If it lands you in the full flow, jump to **Step 2**.
|
|
124
137
|
|
|
125
138
|
First, run host detection to show resolved orchestration:
|
|
126
139
|
|
|
@@ -175,6 +188,8 @@ AskUserQuestion([
|
|
|
175
188
|
{ label: "Orchestration", description: "Phase delegation, review mode, debate mode, agent teams" },
|
|
176
189
|
{ label: "Team Collaboration", description: "Enable/disable team mode — shared task board, roadmap, members" },
|
|
177
190
|
{ label: "Visibility", description: "Artifact-sharing policy — what leaves your machine (roadmap, team.json, decisions, digests, tasks)" },
|
|
191
|
+
{ label: "Task Tracking", description: "Tracker backend (local-only / github-issues / app-inbox), publish to tracker, TDD default, prototype discipline" },
|
|
192
|
+
{ label: "Changelog & Release Notes", description: "Internal dev changelog + opt-in user-facing release notes (audience, persona filter)" },
|
|
178
193
|
{ label: "Everything", description: "Re-run full setup from scratch" },
|
|
179
194
|
{ label: "Done", description: "Keep current settings" }
|
|
180
195
|
]
|
|
@@ -189,7 +204,8 @@ AskUserQuestion([
|
|
|
189
204
|
- "Team Collaboration" → jump to **Step 3d** (Batch 5 only), then skip to **Step 4** and **Step 5**
|
|
190
205
|
- "Visibility" → jump to **Step 3e** (Batch 6 only), then skip to **Step 4** and **Step 5**
|
|
191
206
|
- "Task Tracking" → jump to **Step 3g** (Batch 8 only), then skip to **Step 4** and **Step 5**
|
|
192
|
-
- "
|
|
207
|
+
- "Changelog & Release Notes" → jump to **Step 3h** (Batch 9 only), then skip to **Step 4** and **Step 5**
|
|
208
|
+
- "Everything" → proceed to **Step 2** and run all batches (same as fresh setup). **Legacy-completes-Everything marker write:** if the starting config has no `onboarding` block (legacy unmarked config) and the user completes the full flow, write a full `onboarding` block at Step 5 as `{ completed: true, seeded_from_template: false, seeded_at: <ISO-8601 now> }` to record the explicit onboarding pass.
|
|
193
209
|
- "Done" → display "Settings unchanged." and exit
|
|
194
210
|
|
|
195
211
|
For selective edits, **pre-fill the existing values** as the default selection in each question. The user can confirm or change. Unchanged values are preserved — do not overwrite the entire config, only merge the changed section.
|
|
@@ -978,6 +994,121 @@ Add a row to the Step 8 table:
|
|
|
978
994
|
|
|
979
995
|
---
|
|
980
996
|
|
|
997
|
+
## 3h. Present Settings (Batch 9: Changelog & Release Notes)
|
|
998
|
+
|
|
999
|
+
This batch captures the **persona-aware changelog + release-notes** preferences introduced by `apt-release-notes/SKILL.md`. Two top-level questions: (1) keep an internal dev `CHANGELOG.md` and (2) publish user-facing release notes. Q2 defaults to **No** on internal/library projects (per spec ID-7: the presence of `personas.json` does NOT auto-enable release-notes — opt-in must be explicit).
|
|
1000
|
+
|
|
1001
|
+
If a host supports structured prompts (Claude Code), prefer `AskUserQuestion`. For non-structured hosts (Codex, Gemini-CLI plain mode), present each question as a numbered list and read the reply as a single number per question.
|
|
1002
|
+
|
|
1003
|
+
### 3h.1 Present the dev-changelog picker
|
|
1004
|
+
|
|
1005
|
+
```
|
|
1006
|
+
AskUserQuestion([
|
|
1007
|
+
{
|
|
1008
|
+
question: "Keep an internal dev changelog?",
|
|
1009
|
+
header: "Dev Changelog",
|
|
1010
|
+
options: [
|
|
1011
|
+
{ label: "Yes (Recommended)",
|
|
1012
|
+
description: "Auto-update CHANGELOG.md from commits on every /apt:ship. Dev-flavored, internal." },
|
|
1013
|
+
{ label: "No",
|
|
1014
|
+
description: "Skip the dev changelog entirely." }
|
|
1015
|
+
]
|
|
1016
|
+
}
|
|
1017
|
+
])
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
### 3h.2 Present the release-notes picker
|
|
1021
|
+
|
|
1022
|
+
```
|
|
1023
|
+
AskUserQuestion([
|
|
1024
|
+
{
|
|
1025
|
+
question: "Publish user-facing release notes?",
|
|
1026
|
+
header: "Release Notes",
|
|
1027
|
+
options: [
|
|
1028
|
+
{ label: "No (Recommended for internal / library projects)",
|
|
1029
|
+
description: "Skip the user-facing surface entirely. Most projects pick this." },
|
|
1030
|
+
{ label: "Yes",
|
|
1031
|
+
description: "Enable /apt:release-notes — auto-draft fragments on /apt:ship, compile at tag time." }
|
|
1032
|
+
]
|
|
1033
|
+
}
|
|
1034
|
+
])
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
### 3h.3 If Yes — present the audience + persona-filter sub-pickers
|
|
1038
|
+
|
|
1039
|
+
When Q2 = Yes, ask:
|
|
1040
|
+
|
|
1041
|
+
```
|
|
1042
|
+
AskUserQuestion([
|
|
1043
|
+
{
|
|
1044
|
+
question: "Who is the primary audience for these release notes?",
|
|
1045
|
+
header: "Audience",
|
|
1046
|
+
options: [
|
|
1047
|
+
{ label: "End user", description: "Plain-language, value-focused. Suits consumer apps." },
|
|
1048
|
+
{ label: "Developer / API", description: "Technical, references API + SDK semantics. Suits libraries." },
|
|
1049
|
+
{ label: "Internal team", description: "Lighter polish, faster cadence. Suits company-internal apps." }
|
|
1050
|
+
]
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
question: "Which personas should drive the published notes?",
|
|
1054
|
+
header: "Persona Filter",
|
|
1055
|
+
options: [
|
|
1056
|
+
{ label: "Primary only",
|
|
1057
|
+
description: "Only primary-tier personas surface in published notes." },
|
|
1058
|
+
{ label: "Primary + Secondary (Recommended)",
|
|
1059
|
+
description: "Primary + secondary tiers. Default — balances signal with completeness." },
|
|
1060
|
+
{ label: "All personas",
|
|
1061
|
+
description: "Every tier surfaces. Useful for marketplaces / multi-sided products." }
|
|
1062
|
+
]
|
|
1063
|
+
}
|
|
1064
|
+
])
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
If `.aperant/personas.json` is absent and Q2 = Yes, emit a non-blocking hint:
|
|
1068
|
+
|
|
1069
|
+
```
|
|
1070
|
+
[apt:setup] Note: .aperant/personas.json is missing. Release notes will work but every
|
|
1071
|
+
fragment will land in `## Needs Triage` until you run /apt:personas.
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
This is a hint only — the user can still complete setup; the triage bucket is the intentional safety net (spec ID-6 never-silent-drop).
|
|
1075
|
+
|
|
1076
|
+
### 3h.4 Map answers to `.aperant/config.json`
|
|
1077
|
+
|
|
1078
|
+
Mapping rules (extend Section 4 below):
|
|
1079
|
+
|
|
1080
|
+
| User picked | Config write |
|
|
1081
|
+
|---|---|
|
|
1082
|
+
| Q1: Yes | `changelog.dev.enabled = true` |
|
|
1083
|
+
| Q1: No | `changelog.dev.enabled = false` |
|
|
1084
|
+
| Q2: No | `changelog.release_notes.enabled = false` (DEFAULT — leave audience + persona_filter at template defaults) |
|
|
1085
|
+
| Q2: Yes | `changelog.release_notes.enabled = true` and apply the audience + persona-filter sub-pickers |
|
|
1086
|
+
| Audience: End user | `changelog.release_notes.audience = "end-user"` |
|
|
1087
|
+
| Audience: Developer / API | `changelog.release_notes.audience = "developer-api"` |
|
|
1088
|
+
| Audience: Internal team | `changelog.release_notes.audience = "internal-team"` |
|
|
1089
|
+
| Persona filter: Primary only | `changelog.release_notes.persona_filter = "primary"` |
|
|
1090
|
+
| Persona filter: Primary + Secondary | `changelog.release_notes.persona_filter = "primary+secondary"` (DEFAULT) |
|
|
1091
|
+
| Persona filter: All personas | `changelog.release_notes.persona_filter = "all"` |
|
|
1092
|
+
|
|
1093
|
+
The remaining `release_notes` keys (`output_dir`, `require_pr_field`, `draft.*`, `compile.*`, `publish.*`) keep their template defaults from `packages/framework/templates/config.json` v5. Users wanting to override those edit `.aperant/config.json` directly — they are not surfaced through the Q-block to keep the picker short.
|
|
1094
|
+
|
|
1095
|
+
Migration rule: any existing config WITHOUT a `changelog` block gets defaults silently from the template migrator (`migrateConfigSchema` in `src/cli/config/schema-migrate.mjs`) — this is the same auto_added path Batch 8 relies on for the `task_tracking` / `coding` blocks.
|
|
1096
|
+
|
|
1097
|
+
### 3h.5 Review-Mode routing entry
|
|
1098
|
+
|
|
1099
|
+
Extend the "1b. Review Mode" routing table (§1b) to include "Changelog & Release Notes" as a selectable edit target — jumping back into this Batch 9 picker.
|
|
1100
|
+
|
|
1101
|
+
### 3h.6 Confirmation row
|
|
1102
|
+
|
|
1103
|
+
Add a row to the Step 8 table:
|
|
1104
|
+
```
|
|
1105
|
+
| Changelog & Release Notes | dev: {Yes/No}; release-notes: {Yes/No} (audience: {value}, persona-filter: {value}) |
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
The selective-edit pass through Batch 9 MUST respect the §1b dispatch — it does NOT flip `onboarding.completed`. Marker writes are reserved for the full-flow / "Everything" branches.
|
|
1109
|
+
|
|
1110
|
+
---
|
|
1111
|
+
|
|
981
1112
|
## 4. Map Answers to Config
|
|
982
1113
|
|
|
983
1114
|
Map the user's selections to config values:
|
|
@@ -1034,7 +1165,7 @@ Note: task ids are slug-first since v0.6.6 — the date-format setting controls
|
|
|
1034
1165
|
- "File (default)" → `diagram.mode: "file"`
|
|
1035
1166
|
- "MCP (live canvas)" → `diagram.mode: "mcp"`
|
|
1036
1167
|
|
|
1037
|
-
|
|
1168
|
+
**Diagram MCP consent (per-user, FRAMEWORK-AUDIT-001).** The `diagram.mcpConsent` field is per-user and lives in `.aperant/config.local.json` (gitignored), NOT in the shared `config.json`. If `config.local.json` doesn't exist or has no `diagram.mcpConsent` field, seed `{diagram: {mcpConsent: null}}` in `config.local.json`. The `diagram.mode` field (file vs mcp) stays in `config.json` as team-shared policy and continues to seed there. Existing `mcpConsent` records in `config.local.json` are preserved across mode flips (changing the mode does not invalidate prior consent for the same host).
|
|
1038
1169
|
|
|
1039
1170
|
**Orchestration (Auto mode):** "Auto (Recommended)" → all three fields set to `"auto"`:
|
|
1040
1171
|
- `orchestration.phase_delegation: "auto"`
|
|
@@ -1149,6 +1280,7 @@ Display:
|
|
|
1149
1280
|
| Review Mode | {Auto / Agent Team / Sub-Agents / Sequential} |
|
|
1150
1281
|
| Debate Mode | {Auto / Agent Team / Sub-Agents / Sequential} |
|
|
1151
1282
|
| Visibility | {solo / team / oss} (overrides if any) |
|
|
1283
|
+
| Changelog & Release Notes | dev: {Yes/No}; release-notes: {Yes/No} (audience, persona-filter) |
|
|
1152
1284
|
| Saved as Defaults | {Yes/No} |
|
|
1153
1285
|
|
|
1154
1286
|
Config saved to: .aperant/config.json
|
|
@@ -1160,6 +1292,17 @@ Commands now use these settings automatically:
|
|
|
1160
1292
|
/apt:setup — change settings anytime
|
|
1161
1293
|
```
|
|
1162
1294
|
|
|
1295
|
+
### 8. (continued) Flip `onboarding.completed` on full-flow success
|
|
1296
|
+
|
|
1297
|
+
After a successful **full Batch 1-8 pass** (i.e. the user came in via `--reset`, via the fresh-seed marker-state `seeded_from_template:true / completed:false`, or via the review-mode "Everything" option), patch `.aperant/config.json` to set `onboarding.completed: true`. **Preserve** the existing `seeded_from_template` and `seeded_at` fields untouched — they carry forensic value (debug "did init upgrade overwrite my config?"). Concretely:
|
|
1298
|
+
|
|
1299
|
+
- Read the current `.aperant/config.json` you just wrote in **Step 5**.
|
|
1300
|
+
- Set `onboarding.completed = true`. If the `onboarding` block was already present, preserve the other fields verbatim.
|
|
1301
|
+
- If the starting config was a **legacy unmarked config** (no `onboarding` block — see §1b "Everything" routing), instead write the full block as `{ completed: true, seeded_from_template: false, seeded_at: <ISO-8601 now> }`.
|
|
1302
|
+
- Re-write `.aperant/config.json` with 2-space indentation.
|
|
1303
|
+
|
|
1304
|
+
Selective edits in review mode (Verification, Pipeline, Orchestration, Team Collaboration, Visibility, Task Tracking) DO NOT flip the marker — only a full Batch 1-8 pass does.
|
|
1305
|
+
|
|
1163
1306
|
### 8a. PreCompact Hook Status
|
|
1164
1307
|
|
|
1165
1308
|
After confirming settings, check whether the framework's PreCompact hook is
|
package/skills/apt-ship/SKILL.md
CHANGED
|
@@ -146,6 +146,53 @@ Format as a markdown table:
|
|
|
146
146
|
|
|
147
147
|
If no plan exists (ad-hoc task), list commits without subtask mapping.
|
|
148
148
|
|
|
149
|
+
## 2.6 Release-note auto-draft (persona-aware release-notes opt-in)
|
|
150
|
+
|
|
151
|
+
This section runs ONLY when `.aperant/config.json` has `changelog.release_notes.enabled === true`. When the toggle is `false` (the default for the 9-of-10 internal projects that never publish user-facing notes), skip the section entirely — do NOT compute the feature diff and do NOT touch the PR body draft.
|
|
152
|
+
|
|
153
|
+
Read the toggle:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
rn_enabled=$(jq -r '.changelog.release_notes.enabled // false' .aperant/config.json)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
If `rn_enabled !== "true"`, jump to Section 3.
|
|
160
|
+
|
|
161
|
+
### 2.6.1 Compute the feature-registry diff
|
|
162
|
+
|
|
163
|
+
The auto-draft needs to know which features the PR's commits touched. Use the same `features-audit` surface that `apt:execute` uses post-step:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
base=$(node packages/framework/bin/apt-tools.mjs task get . --id {task-id} | jq -r '.base_branch // "main"')
|
|
167
|
+
feature_diff=$(node packages/framework/bin/apt-tools.mjs features-audit . --diff-since "$base" --json)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The envelope shape (see `packages/framework/src/cli/commands/features-audit.mjs`) returns the list of touched features with their declared `personas: []` field (when the registry's feature row carries one).
|
|
171
|
+
|
|
172
|
+
### 2.6.2 Decide draft vs block via the helper
|
|
173
|
+
|
|
174
|
+
Hand the diff + the project's personas envelope + the filter mode to `ship-autodraft.mjs`. The CLI returns one of three envelopes:
|
|
175
|
+
|
|
176
|
+
- `{status: 'draft', line, persona, matchedFeatures}` — append `line` to the PR body draft built in Section 3.
|
|
177
|
+
- `{status: 'block', reason, message}` — fire the block path below.
|
|
178
|
+
- `{status: 'warn', reason, message}` — log the warning and proceed without the line.
|
|
179
|
+
|
|
180
|
+
The block path triggers when `release_notes.require_pr_field === true` (default) AND auto-draft failed (no feature match OR no persona signal). The exact error string is locked by AC7:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
[APT] No release-note signal found — author manual note in PR body or label PR no-notes
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
When the block fires, STOP — do NOT proceed to `gh pr create`. The user must either (a) author a manual `Release note: …` line in the PR body via `git commit --amend` (or by editing the PR body after open), or (b) label the PR `no-notes` via `gh pr edit --add-label no-notes` and re-run `/apt:ship`. The label-based escape hatch keeps the gate from blocking infra-only PRs that genuinely have no user-facing impact.
|
|
187
|
+
|
|
188
|
+
### 2.6.3 Inject the line into the PR body draft
|
|
189
|
+
|
|
190
|
+
When the helper returns `status: 'draft'`, append the `line` value verbatim to the PR body draft assembled in Section 3, immediately under the `## Summary` heading. The line is one sentence, persona-voice-tuned, suitable for a user-facing surface.
|
|
191
|
+
|
|
192
|
+
### 2.6.4 Helper contract (for testability)
|
|
193
|
+
|
|
194
|
+
The helper lives at `packages/framework/src/cli/release-notes/ship-autodraft.mjs`. It is a pure function — given the feature-diff envelope + personas envelope + filter mode + the `require_pr_field` toggle, it returns the decision. The `decideAutodraft` export is unit-tested via TD-4; the SKILL.md body wires it without re-deriving the decision logic.
|
|
195
|
+
|
|
149
196
|
## 3. Build PR Body
|
|
150
197
|
|
|
151
198
|
**Title:** Derived from task description. Keep it short (<70 characters).
|
|
@@ -42,35 +42,77 @@ The cache file is at `~/.cache/aperant/apt-update-check.json`. It contains `runt
|
|
|
42
42
|
|
|
43
43
|
## 1. Read current state
|
|
44
44
|
|
|
45
|
+
<!-- TODO(followup): support PATH-only / npx-only kernel installs. The
|
|
46
|
+
hardcoded `.aperant/deps/...` path below fails users who don't have a
|
|
47
|
+
project-local kernel directory. Tracked as a separate task. -->
|
|
45
48
|
```bash
|
|
46
49
|
node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs check-version . --json
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
Parse the JSON. Capture:
|
|
52
|
+
Parse the JSON. From 0.6.7 the envelope merges the SessionStart worker's cache (when fresh — written within the last hour to `~/.cache/aperant/apt-update-check.json`). Capture:
|
|
53
|
+
|
|
50
54
|
- `framework.installed_version` — what's currently on disk
|
|
51
|
-
- `framework.latest_version` — what
|
|
55
|
+
- `framework.latest_version` — what npm reports as the latest published version (MAY BE `null` when the worker cache is stale / missing / wrong schema)
|
|
52
56
|
- `runtimes[]` — list of installed runtime ids + per-runtime `stale_files` count
|
|
53
57
|
- `stale_files_total` — drift tally across runtimes
|
|
58
|
+
- `update_available` — true when worker cache is fresh AND `installed_version < latest_version`
|
|
59
|
+
- `dev_install_ahead` — true when local kernel is ahead of published (typical local dev)
|
|
60
|
+
- `not_published` — true when npm returned 404 (package not yet on registry)
|
|
61
|
+
- `lookup_error` — non-null when npm view failed for any non-404 reason (timeout, ENETDOWN, etc.)
|
|
62
|
+
|
|
63
|
+
The envelope is **single-shape** — every field above is always present. When the worker cache is stale or missing, `latest_version` is `null`, the four booleans are `false`, `stale_files_total` falls back to a local-only count, and `lookup_error` is `null`. Your parser doesn't need to branch on cache freshness.
|
|
54
64
|
|
|
55
65
|
If `runtimes[]` is empty, tell the user they haven't installed Aperant into any runtime yet and suggest `apt-tools init . --claude` (or their preferred runtime). Stop.
|
|
56
66
|
|
|
67
|
+
## 1.5. Bump the local kernel from npm when newer is available
|
|
68
|
+
|
|
69
|
+
The Aperant kernel lives at `.aperant/deps/node_modules/@aperant/framework/`. `apt-tools init` re-installs runtime files at THE LOCAL KERNEL'S VERSION — so a stale kernel means `/apt:update` re-stamps manifests at the OLD version even when npm has a newer release. Bump the kernel BEFORE invoking init.
|
|
70
|
+
|
|
71
|
+
First, resolve `latest_version` when the envelope had `null` (stale cache).
|
|
72
|
+
|
|
73
|
+
**Timeout enforcement (ID-04):** The `npm view` call must complete within 10 s — never block the user longer than the SessionStart worker would. Use npm's `--fetch-timeout` flag as the primary mechanism (universally portable across all npm versions); it sets the HTTP fetch timeout in milliseconds:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm view @aperant/framework version --fetch-timeout=10000 --fetch-retries=0 --json --silent 2>/dev/null || true
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
On Linux/macOS with GNU coreutils installed, you MAY additionally wrap with a hard shell-level timer: `timeout 10 npm view ...` (Linux) or `gtimeout 10 npm view ...` (macOS via `brew install coreutils`). Shell-level wrapping is optional — `--fetch-timeout=10000` alone is sufficient because it is universal. Do NOT rely solely on the shell `timeout` command without the npm flag, as it is not available on all systems.
|
|
80
|
+
|
|
81
|
+
If npm exits non-zero or times out, surface a one-line warning (`⚠ npm view failed — using local kernel`) and proceed; the local kernel still works for the runtime refresh, just at the older version.
|
|
82
|
+
|
|
83
|
+
**Security (SEC-001):** Before interpolating `latest_version` into any shell command, verify it matches the semver pattern `^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$`. If it does not match, treat it as missing and fall back to the explicit version pinning path (proceed with the local kernel version, surface `⚠ latest_version invalid — using local kernel`). This protects against a poisoned cache entry reaching a shell command.
|
|
84
|
+
|
|
85
|
+
When `installed_version < latest_version`, bump:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm install --prefix .aperant/deps @aperant/framework@<latest_version> --no-save --no-package-lock --no-audit --no-fund
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
On `npm 404` (`E404` in stderr) treat it as `not_published` and skip the kernel bump (proceed to step 3). On any other npm failure, surface the npm stderr verbatim and STOP — do NOT proceed to init with a half-installed kernel.
|
|
92
|
+
|
|
57
93
|
## 2. Classify the update scenario
|
|
58
94
|
|
|
59
|
-
Pick ONE of three scenarios based on what you
|
|
95
|
+
Pick ONE of three scenarios based on what you parsed in step 1:
|
|
60
96
|
|
|
61
|
-
- **A. Version bump available** — `installed_version !== latest_version` (regardless of drift). Users see this most often.
|
|
62
|
-
- **B. Drift only** — `installed_version === latest_version` but `stale_files_total > 0`. User hand-edited an installed file or something was partially overwritten.
|
|
63
|
-
- **C. Nothing to do** — `installed_version === latest_version` AND `stale_files_total === 0`. Still re-run init to refresh
|
|
97
|
+
- **A. Version bump available** — `installed_version !== latest_version` (regardless of drift). Users see this most often. Step 1.5 bumped the kernel; step 3 re-stamps manifests at the new version.
|
|
98
|
+
- **B. Drift only** — `installed_version === latest_version` but `stale_files_total > 0`. User hand-edited an installed file or something was partially overwritten. Step 1.5 was a no-op; step 3 still needs `--force-runtimes` to re-stamp.
|
|
99
|
+
- **C. Nothing to do** — `installed_version === latest_version` AND `stale_files_total === 0`. Still re-run init with `--force-runtimes` to refresh manifests + cache, but tell the user plainly that no version change happened.
|
|
64
100
|
|
|
65
101
|
## 3. Re-install the currently-installed runtimes
|
|
66
102
|
|
|
67
|
-
Build the flags list from the runtime ids (e.g. `--claude --codex`) and
|
|
103
|
+
Build the flags list from the runtime ids (e.g. `--claude --codex`) and pass `--force-runtimes` so init bypasses the `upgrade_noop` short-circuit (Bug 2 fix, 0.6.7) and re-stamps every manifest:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs init . --claude --force-runtimes
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Example with multiple runtimes installed:
|
|
68
110
|
|
|
69
111
|
```bash
|
|
70
|
-
node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs init .
|
|
112
|
+
node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs init . --claude --codex --force-runtimes
|
|
71
113
|
```
|
|
72
114
|
|
|
73
|
-
**Do NOT add `--yes`, `--solo`, `--team`, or any other Class A flag.** This command is meant to refresh runtime files only — adding `--yes` triggers a config schema migration that can silently flip `share.visibility` to `solo` and re-write `.gitignore`, which is exactly the 0.6.0/0.6.1 dogfood bug. If `init` returns a `requires_input` envelope (schema drift), surface it to the user verbatim and stop; let them decide team vs solo with the explicit flag. From 0.6.4 onward `--yes` infers visibility from existing git tracking, but the skill must still not add it — keep this command scoped to runtime refresh.
|
|
115
|
+
**Do NOT add `--yes`, `--solo`, `--team`, or any other Class A flag.** This command is meant to refresh runtime files only — adding `--yes` triggers a config schema migration that can silently flip `share.visibility` to `solo` and re-write `.gitignore`, which is exactly the 0.6.0/0.6.1 dogfood bug. `--force-runtimes` is the ONLY new flag this skill adds. If `init` returns a `requires_input` envelope (schema drift), surface it to the user verbatim and stop; let them decide team vs solo with the explicit flag. From 0.6.4 onward `--yes` infers visibility from existing git tracking, but the skill must still not add it — keep this command scoped to runtime refresh.
|
|
74
116
|
|
|
75
117
|
Parse the init JSON. Capture for each runtime:
|
|
76
118
|
- `install_root` (shortened to the basename, e.g. `.claude`)
|
|
@@ -69,8 +69,8 @@ Watcher already running for PR {N} (held by sessionId={x}, pid={y}); exiting.
|
|
|
69
69
|
|
|
70
70
|
For the `--tick` mode, execute exactly the following sequence — once — inside this single skill invocation:
|
|
71
71
|
|
|
72
|
-
1. **Poll.** Call `gh pr checks <N> --json name,bucket,state,conclusion,detailsUrl` and `gh pr view <N> --json state,reviewDecision`.
|
|
73
|
-
2. **Classify + state update.** Call `apt-tools ci-watch tick {worktree} --pr {N}`. The cmd internally:
|
|
72
|
+
1. **Poll.** Call `gh pr checks <N> --json name,bucket,state,conclusion,detailsUrl` and `gh pr view <N> --json state,reviewDecision`. Combine both JSON outputs into a single file shaped `{checks: <pr-checks-json>, prMetadata: <pr-view-json>}` (for example with `jq -s '{checks: .[0], prMetadata: .[1]}'`) and save it as `{captured.json}` (any path under the worktree is fine). On any `gh` failure, omit the file and let the cmd's `tick` route to `infra-backoff` (it surfaces the same action when no payload is available).
|
|
73
|
+
2. **Classify + state update.** Call `apt-tools ci-watch tick {worktree} --pr {N} --payload-file {captured.json}`. The cmd reads `{captured.json}` from disk and runs the classifier — it does NOT shell out to `gh` itself (that boundary stays with the orchestrator skill; see commands/ci-watch.mjs:15). The cmd internally:
|
|
74
74
|
- acquires the per-PR lock (BUSY → exit 2 → step §2);
|
|
75
75
|
- reads the per-PR state file;
|
|
76
76
|
- runs the stop-matrix classifier (`packages/framework/src/cli/ci-watch/stop-matrix.mjs`);
|