@aperant/framework 0.6.5 → 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/README.md +17 -251
- 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/commit.d.mts.map +1 -1
- package/dist/cli/commands/commit.mjs +25 -8
- package/dist/cli/commands/commit.mjs.map +1 -1
- package/dist/cli/commands/event.d.mts.map +1 -1
- package/dist/cli/commands/event.mjs +66 -0
- package/dist/cli/commands/event.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/modes.d.mts.map +1 -1
- package/dist/cli/commands/modes.mjs +2 -1
- package/dist/cli/commands/modes.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/commands/task.d.mts.map +1 -1
- package/dist/cli/commands/task.mjs +285 -139
- package/dist/cli/commands/task.mjs.map +1 -1
- package/dist/cli/commands/triage.d.mts.map +1 -1
- package/dist/cli/commands/triage.mjs +9 -5
- package/dist/cli/commands/triage.mjs.map +1 -1
- 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/coordination/auto-emit-artifact-ready.d.mts +16 -0
- package/dist/cli/coordination/auto-emit-artifact-ready.d.mts.map +1 -0
- package/dist/cli/coordination/auto-emit-artifact-ready.mjs +88 -0
- package/dist/cli/coordination/auto-emit-artifact-ready.mjs.map +1 -0
- package/dist/cli/coordination/event-schema.d.mts +16 -0
- package/dist/cli/coordination/event-schema.d.mts.map +1 -0
- package/dist/cli/coordination/event-schema.mjs +161 -0
- package/dist/cli/coordination/event-schema.mjs.map +1 -0
- package/dist/cli/coordination/store.d.mts +6 -0
- package/dist/cli/coordination/store.d.mts.map +1 -1
- package/dist/cli/coordination/store.mjs +14 -0
- package/dist/cli/coordination/store.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/help.mjs +2 -2
- package/dist/cli/help.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/cli/roadmap/conductor-view.d.mts +13 -0
- package/dist/cli/roadmap/conductor-view.d.mts.map +1 -0
- package/dist/cli/roadmap/conductor-view.mjs +31 -0
- package/dist/cli/roadmap/conductor-view.mjs.map +1 -0
- package/dist/cli/route/skill-discover.d.mts.map +1 -1
- package/dist/cli/route/skill-discover.mjs +2 -1
- package/dist/cli/route/skill-discover.mjs.map +1 -1
- package/dist/cli/task/ids.d.mts +7 -4
- package/dist/cli/task/ids.d.mts.map +1 -1
- package/dist/cli/task/ids.mjs +11 -10
- package/dist/cli/task/ids.mjs.map +1 -1
- package/dist/types/config.d.ts +11 -7
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +9 -1
- package/prompts/conductor-status-check.md +23 -0
- package/prompts/conductor-sub-agent.md +57 -0
- package/prompts/conductor-system.md +172 -0
- package/skills/apt-close-task/SKILL.md +25 -0
- package/skills/apt-diagram/SKILL.md +45 -9
- package/skills/apt-plan/SKILL.md +12 -0
- package/skills/apt-plan/adapters/conductor.md +98 -0
- 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 +148 -3
- package/skills/apt-ship/SKILL.md +74 -12
- package/skills/apt-spar/SKILL.md +290 -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/commit.mjs +27 -8
- package/src/cli/commands/event.mjs +68 -0
- package/src/cli/commands/init.mjs +101 -9
- package/src/cli/commands/modes.mjs +2 -1
- package/src/cli/commands/release-notes.mjs +187 -0
- package/src/cli/commands/task.mjs +305 -152
- package/src/cli/commands/triage.mjs +14 -5
- 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/coordination/auto-emit-artifact-ready.mjs +96 -0
- package/src/cli/coordination/event-schema.d.ts +13 -0
- package/src/cli/coordination/event-schema.mjs +174 -0
- package/src/cli/coordination/store.mjs +14 -0
- package/src/cli/dispatch.mjs +2 -0
- package/src/cli/gate/gates/review-clean.mjs +4 -2
- package/src/cli/help.mjs +2 -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/src/cli/roadmap/conductor-view.d.ts +10 -0
- package/src/cli/roadmap/conductor-view.mjs +31 -0
- package/src/cli/route/skill-discover.mjs +2 -1
- package/src/cli/task/ids.mjs +15 -13
- 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:
|
|
@@ -1017,6 +1148,8 @@ Map the user's selections to config values:
|
|
|
1017
1148
|
- "Ask me" → `pr_review.suggest_after_ship: "ask"`
|
|
1018
1149
|
- "Never" → `pr_review.suggest_after_ship: "never"`
|
|
1019
1150
|
|
|
1151
|
+
Note: task ids are slug-first since v0.6.6 — the date-format setting controls the trailing date component only.
|
|
1152
|
+
|
|
1020
1153
|
**Date Format:**
|
|
1021
1154
|
- "DD-MM-YY" → `preferences.date_format: "DD-MM-YY"`
|
|
1022
1155
|
- "MM-DD-YY" → `preferences.date_format: "MM-DD-YY"`
|
|
@@ -1032,7 +1165,7 @@ Map the user's selections to config values:
|
|
|
1032
1165
|
- "File (default)" → `diagram.mode: "file"`
|
|
1033
1166
|
- "MCP (live canvas)" → `diagram.mode: "mcp"`
|
|
1034
1167
|
|
|
1035
|
-
|
|
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).
|
|
1036
1169
|
|
|
1037
1170
|
**Orchestration (Auto mode):** "Auto (Recommended)" → all three fields set to `"auto"`:
|
|
1038
1171
|
- `orchestration.phase_delegation: "auto"`
|
|
@@ -1147,6 +1280,7 @@ Display:
|
|
|
1147
1280
|
| Review Mode | {Auto / Agent Team / Sub-Agents / Sequential} |
|
|
1148
1281
|
| Debate Mode | {Auto / Agent Team / Sub-Agents / Sequential} |
|
|
1149
1282
|
| Visibility | {solo / team / oss} (overrides if any) |
|
|
1283
|
+
| Changelog & Release Notes | dev: {Yes/No}; release-notes: {Yes/No} (audience, persona-filter) |
|
|
1150
1284
|
| Saved as Defaults | {Yes/No} |
|
|
1151
1285
|
|
|
1152
1286
|
Config saved to: .aperant/config.json
|
|
@@ -1158,6 +1292,17 @@ Commands now use these settings automatically:
|
|
|
1158
1292
|
/apt:setup — change settings anytime
|
|
1159
1293
|
```
|
|
1160
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
|
+
|
|
1161
1306
|
### 8a. PreCompact Hook Status
|
|
1162
1307
|
|
|
1163
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).
|
|
@@ -289,11 +336,13 @@ Capture the PR URL from the output into `pr_url` (for example, the last line of
|
|
|
289
336
|
**Record `pr_url` on the task record (C56 A2).** `/apt:close-task` and the auto-close-phase path in `task close` both resolve the PR number from this URL to poll `gh pr view` for merge state — without it, the closer side cannot run:
|
|
290
337
|
|
|
291
338
|
```bash
|
|
292
|
-
node packages/framework/bin/apt-tools.mjs task update . --id {task-id} --pr-url {PR_URL}
|
|
339
|
+
ship_update=$(node packages/framework/bin/apt-tools.mjs task update . --id {task-id} --pr-url {PR_URL})
|
|
293
340
|
```
|
|
294
341
|
|
|
295
342
|
This same call **atomically flips `lifecycle_phase` from `reviewing` to `shipped-pending-merge`** when the task is in `reviewing` and no explicit `--lifecycle-phase` flag is passed. Section 6 is now the single load-bearing step that both records the PR and transitions the lifecycle; Section 8 below documents the semantics but no longer issues a separate command.
|
|
296
343
|
|
|
344
|
+
It also evaluates `ci_watch.after_ship` and may arm the post-ship CI watcher. Preserve the JSON envelope in `ship_update`; Section 7.2 reads `ci_watch_armed` and `ci_watch_skip_reason` from it instead of issuing a second auto-start command.
|
|
345
|
+
|
|
297
346
|
Display the resolution so users see which branch/base was used:
|
|
298
347
|
|
|
299
348
|
```
|
|
@@ -352,29 +401,31 @@ suggest_mode=$(jq -r '.pr_review.suggest_after_ship // "always"' .aperant/config
|
|
|
352
401
|
|
|
353
402
|
### 7.2 Post-ship /apt:watch-ci hook
|
|
354
403
|
|
|
355
|
-
After Section 7.1's hint resolves (whether it ran the deep review or skipped),
|
|
404
|
+
After Section 7.1's hint resolves (whether it ran the deep review or skipped), report or interactively arm the post-ship CI watcher. The hook is controlled by `ci_watch.after_ship` in `.aperant/config.json` (tri-state: `auto` | `ask` | `never`). The default depends on host capability — `auto` on cron-capable hosts (Claude Code, Pi), `never` elsewhere.
|
|
356
405
|
|
|
357
406
|
**Skip entirely** when no PR URL was captured in Section 6 (library `npm publish`-only path — same skip rule as 7.1).
|
|
358
407
|
|
|
359
|
-
**Read
|
|
408
|
+
**Read the Section 6 task-update envelope first:**
|
|
360
409
|
|
|
361
410
|
```bash
|
|
411
|
+
ci_watch_armed=$(echo "$ship_update" | jq -r '.ci_watch_armed // false')
|
|
412
|
+
ci_watch_skip_reason=$(echo "$ship_update" | jq -r '.ci_watch_skip_reason // empty')
|
|
413
|
+
ci_watch_host=$(echo "$ship_update" | jq -r '.ci_watch.host // "unknown"')
|
|
414
|
+
ci_watch_deadline=$(echo "$ship_update" | jq -r '.ci_watch.deadline // empty')
|
|
362
415
|
host_cron=$(node packages/framework/bin/apt-tools.mjs host-detect | jq -r '.host.capabilities.cron')
|
|
363
|
-
after_ship_default=$([ "$host_cron" = "true" ] && echo "auto" || echo "never")
|
|
364
|
-
after_ship_mode=$(jq -r --arg d "$after_ship_default" '.ci_watch.after_ship // $d' .aperant/config.json)
|
|
365
416
|
```
|
|
366
417
|
|
|
367
418
|
**Autonomy interaction** (apply BEFORE branching, same rule as 7.1): when `autonomy === 3` (YOLO), degrade `ask` → `auto`.
|
|
368
419
|
|
|
369
|
-
**Branch on
|
|
420
|
+
**Branch on the task-update envelope:**
|
|
370
421
|
|
|
371
|
-
- **`
|
|
422
|
+
- **`ci_watch_armed=true` AND `host_cron=true`** — emit:
|
|
372
423
|
|
|
373
424
|
```
|
|
374
|
-
[APT:ship] CI watcher armed (host cron: {
|
|
425
|
+
[APT:ship] CI watcher armed (host cron: {ci_watch_host}). Deadline: {ci_watch_deadline}. Stop: /apt:watch-ci --stop --pr {N}.
|
|
375
426
|
```
|
|
376
427
|
|
|
377
|
-
- **`
|
|
428
|
+
- **`ci_watch_armed=true` AND `host_cron=false`** — emit:
|
|
378
429
|
|
|
379
430
|
```
|
|
380
431
|
[APT:ship] CI watcher state created (host has no cron; advance manually: /apt:watch-ci --tick --pr {N}).
|
|
@@ -382,15 +433,26 @@ after_ship_mode=$(jq -r --arg d "$after_ship_default" '.ci_watch.after_ship // $
|
|
|
382
433
|
|
|
383
434
|
This is the honest-gap surface per D-03 — we never lie about cron-capability.
|
|
384
435
|
|
|
385
|
-
- **`ask`** — emit:
|
|
436
|
+
- **`ci_watch_skip_reason=ask-deferred`** — emit:
|
|
386
437
|
|
|
387
438
|
```
|
|
388
439
|
[APT:ship] Arm CI watcher for PR {N} now? [Y/n]
|
|
389
440
|
```
|
|
390
441
|
|
|
391
|
-
On `Y` (or empty enter),
|
|
442
|
+
On `Y` (or empty enter), call:
|
|
392
443
|
|
|
393
|
-
|
|
444
|
+
```bash
|
|
445
|
+
node packages/framework/bin/apt-tools.mjs ci-watch start . --pr {pr_number} --task-id {task-id} --host {cli}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Then emit the same host-cron-aware success banner used for `ci_watch_armed=true`. On `N` or any other response, proceed silently to Section 8.
|
|
449
|
+
|
|
450
|
+
- **`ci_watch_skip_reason=disabled`** — silent. Proceed directly to Section 8.
|
|
451
|
+
|
|
452
|
+
- **Any other skip reason** — emit:
|
|
453
|
+
```
|
|
454
|
+
[APT:ship] CI watcher not armed: {ci_watch_skip_reason}
|
|
455
|
+
```
|
|
394
456
|
|
|
395
457
|
Section 7.2 is purely additive — it does not affect Section 7.1's `pr_review.suggest_after_ship` policy or any other config block.
|
|
396
458
|
|