@aperant/framework 0.13.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -0
- package/README.md +1 -1
- package/agents/apt-pr-review-crossplatform.md +189 -0
- package/agents/apt-pr-review-i18n.md +190 -0
- package/dist/cli/commands/audit-branch-current.d.mts +27 -5
- package/dist/cli/commands/audit-branch-current.d.mts.map +1 -1
- package/dist/cli/commands/audit-branch-current.mjs +3 -3
- package/dist/cli/commands/audit-branch-current.mjs.map +1 -1
- package/dist/cli/commands/event.d.mts.map +1 -1
- package/dist/cli/commands/event.mjs +6 -4
- package/dist/cli/commands/event.mjs.map +1 -1
- package/dist/cli/commands/fan-out.d.mts.map +1 -1
- package/dist/cli/commands/fan-out.mjs +3 -1
- package/dist/cli/commands/fan-out.mjs.map +1 -1
- package/dist/cli/commands/features-audit.d.mts.map +1 -1
- package/dist/cli/commands/features-audit.mjs +6 -1
- package/dist/cli/commands/features-audit.mjs.map +1 -1
- package/dist/cli/commands/followups.d.mts +5 -0
- package/dist/cli/commands/followups.d.mts.map +1 -0
- package/dist/cli/commands/followups.mjs +266 -0
- package/dist/cli/commands/followups.mjs.map +1 -0
- package/dist/cli/commands/health-check.d.mts.map +1 -1
- package/dist/cli/commands/health-check.mjs +3 -1
- package/dist/cli/commands/health-check.mjs.map +1 -1
- package/dist/cli/commands/init.d.mts.map +1 -1
- package/dist/cli/commands/init.mjs +30 -1
- package/dist/cli/commands/init.mjs.map +1 -1
- package/dist/cli/commands/merge-detect.d.mts +29 -0
- package/dist/cli/commands/merge-detect.d.mts.map +1 -0
- package/dist/cli/commands/merge-detect.mjs +196 -0
- package/dist/cli/commands/merge-detect.mjs.map +1 -0
- package/dist/cli/commands/merge-integrate.d.mts +28 -0
- package/dist/cli/commands/merge-integrate.d.mts.map +1 -0
- package/dist/cli/commands/merge-integrate.mjs +383 -0
- package/dist/cli/commands/merge-integrate.mjs.map +1 -0
- package/dist/cli/commands/pr-review/detect-specialists.d.mts +58 -0
- package/dist/cli/commands/pr-review/detect-specialists.d.mts.map +1 -0
- package/dist/cli/commands/pr-review/detect-specialists.mjs +984 -0
- package/dist/cli/commands/pr-review/detect-specialists.mjs.map +1 -0
- package/dist/cli/commands/pr-review.d.mts.map +1 -1
- package/dist/cli/commands/pr-review.mjs +11 -2
- package/dist/cli/commands/pr-review.mjs.map +1 -1
- package/dist/cli/commands/precompact-hook.d.mts.map +1 -1
- package/dist/cli/commands/precompact-hook.mjs +10 -6
- package/dist/cli/commands/precompact-hook.mjs.map +1 -1
- package/dist/cli/commands/roadmap.d.mts.map +1 -1
- package/dist/cli/commands/roadmap.mjs +24 -11
- package/dist/cli/commands/roadmap.mjs.map +1 -1
- package/dist/cli/commands/seed.d.mts.map +1 -1
- package/dist/cli/commands/seed.mjs +3 -1
- package/dist/cli/commands/seed.mjs.map +1 -1
- package/dist/cli/commands/state.d.mts.map +1 -1
- package/dist/cli/commands/state.mjs +9 -3
- package/dist/cli/commands/state.mjs.map +1 -1
- package/dist/cli/commands/statusline.d.mts.map +1 -1
- package/dist/cli/commands/statusline.mjs +5 -1
- package/dist/cli/commands/statusline.mjs.map +1 -1
- package/dist/cli/commands/task.d.mts.map +1 -1
- package/dist/cli/commands/task.mjs +126 -23
- package/dist/cli/commands/task.mjs.map +1 -1
- package/dist/cli/commands/worktree.d.mts +1 -1
- package/dist/cli/commands/worktree.d.mts.map +1 -1
- package/dist/cli/commands/worktree.mjs +282 -83
- package/dist/cli/commands/worktree.mjs.map +1 -1
- package/dist/cli/config/post-merge-sweep.d.mts.map +1 -1
- package/dist/cli/config/post-merge-sweep.mjs +3 -2
- package/dist/cli/config/post-merge-sweep.mjs.map +1 -1
- package/dist/cli/config/share-policy.d.mts +3 -1
- package/dist/cli/config/share-policy.d.mts.map +1 -1
- package/dist/cli/config/share-policy.mjs +16 -3
- package/dist/cli/config/share-policy.mjs.map +1 -1
- package/dist/cli/config/upgrade-gitignore.d.mts.map +1 -1
- package/dist/cli/config/upgrade-gitignore.mjs +27 -5
- package/dist/cli/config/upgrade-gitignore.mjs.map +1 -1
- package/dist/cli/consistency/parse-qa.mjs +12 -1
- package/dist/cli/consistency/parse-qa.mjs.map +1 -1
- package/dist/cli/consistency/parse-review.d.mts +23 -0
- package/dist/cli/consistency/parse-review.d.mts.map +1 -1
- package/dist/cli/consistency/parse-review.mjs +36 -0
- package/dist/cli/consistency/parse-review.mjs.map +1 -1
- package/dist/cli/consistency/parse-spec.d.mts +16 -0
- package/dist/cli/consistency/parse-spec.d.mts.map +1 -1
- package/dist/cli/consistency/parse-spec.mjs +44 -0
- package/dist/cli/consistency/parse-spec.mjs.map +1 -1
- package/dist/cli/coordination/agent-identity.d.mts +5 -2
- package/dist/cli/coordination/agent-identity.d.mts.map +1 -1
- package/dist/cli/coordination/agent-identity.mjs +6 -4
- package/dist/cli/coordination/agent-identity.mjs.map +1 -1
- package/dist/cli/coordination/store.d.mts +1 -1
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +4 -0
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/followups/derive.d.mts +63 -0
- package/dist/cli/followups/derive.d.mts.map +1 -0
- package/dist/cli/followups/derive.mjs +115 -0
- package/dist/cli/followups/derive.mjs.map +1 -0
- package/dist/cli/followups/schema.d.mts +165 -0
- package/dist/cli/followups/schema.d.mts.map +1 -0
- package/dist/cli/followups/schema.mjs +251 -0
- package/dist/cli/followups/schema.mjs.map +1 -0
- package/dist/cli/gate/context.d.mts +1 -1
- package/dist/cli/gate/gates/followups-adjudicated.d.mts +69 -0
- package/dist/cli/gate/gates/followups-adjudicated.d.mts.map +1 -0
- package/dist/cli/gate/gates/followups-adjudicated.mjs +141 -0
- package/dist/cli/gate/gates/followups-adjudicated.mjs.map +1 -0
- package/dist/cli/gate/gates/index.mjs +1 -0
- package/dist/cli/gate/gates/index.mjs.map +1 -1
- package/dist/cli/git/materialize-env.d.mts +39 -0
- package/dist/cli/git/materialize-env.d.mts.map +1 -0
- package/dist/cli/git/materialize-env.mjs +217 -0
- package/dist/cli/git/materialize-env.mjs.map +1 -0
- package/dist/cli/help.d.mts.map +1 -1
- package/dist/cli/help.mjs +9 -1
- package/dist/cli/help.mjs.map +1 -1
- package/dist/cli/install/legacy-paths.d.mts.map +1 -1
- package/dist/cli/install/legacy-paths.mjs +5 -0
- package/dist/cli/install/legacy-paths.mjs.map +1 -1
- package/dist/cli/roadmap/lifecycle.d.mts.map +1 -1
- package/dist/cli/roadmap/lifecycle.mjs +4 -1
- package/dist/cli/roadmap/lifecycle.mjs.map +1 -1
- package/dist/cli/task/ids.d.mts +9 -0
- package/dist/cli/task/ids.d.mts.map +1 -1
- package/dist/cli/task/ids.mjs +9 -0
- package/dist/cli/task/ids.mjs.map +1 -1
- package/dist/cli/task/index-md.d.mts +5 -0
- package/dist/cli/task/index-md.d.mts.map +1 -1
- package/dist/cli/task/index-md.mjs +12 -4
- package/dist/cli/task/index-md.mjs.map +1 -1
- package/dist/cli/util/io.d.mts +6 -0
- package/dist/cli/util/io.d.mts.map +1 -1
- package/dist/cli/util/io.mjs +16 -1
- package/dist/cli/util/io.mjs.map +1 -1
- package/dist/cli/util/project.d.mts +7 -2
- package/dist/cli/util/project.d.mts.map +1 -1
- package/dist/cli/util/project.mjs +11 -14
- package/dist/cli/util/project.mjs.map +1 -1
- package/dist/cli/util/state-paths.d.mts +19 -0
- package/dist/cli/util/state-paths.d.mts.map +1 -0
- package/dist/cli/util/state-paths.mjs +45 -0
- package/dist/cli/util/state-paths.mjs.map +1 -0
- package/dist/plugin/.claude-plugin/plugin.json +4 -1
- package/dist/plugin/agents/apt-pr-review-crossplatform.md +189 -0
- package/dist/plugin/agents/apt-pr-review-i18n.md +190 -0
- package/dist/plugin/skills/apt/SKILL.md +9 -3
- package/dist/plugin/skills/apt-close-task/SKILL.md +48 -0
- package/dist/plugin/skills/apt-debug/SKILL.md +19 -9
- package/dist/plugin/skills/apt-execute/SKILL.md +3 -1
- package/dist/plugin/skills/apt-fan-out/SKILL.md +65 -1
- package/dist/plugin/skills/apt-merge-conflict-resolution/SKILL.md +227 -0
- package/dist/plugin/skills/apt-pr-review/SCHEMA.md +4 -2
- package/dist/plugin/skills/apt-pr-review/SKILL.md +117 -29
- package/dist/plugin/skills/apt-quick/SKILL.md +24 -1
- package/dist/plugin/skills/apt-review/SKILL.md +6 -0
- package/dist/plugin/skills/apt-run/SKILL.md +1 -1
- package/dist/plugin/skills/apt-setup/SKILL.md +21 -3
- package/dist/plugin/skills/apt-ship/SKILL.md +32 -11
- package/dist/plugin/skills/apt-terminal/SKILL.md +8 -0
- package/dist/plugin/skills/apt-triage/SKILL.md +4 -0
- package/dist/plugin/skills/apt-verify/SKILL.md +24 -0
- package/dist/plugin/skills/apt-watch-ci/SKILL.md +3 -1
- package/dist/types/config.d.ts +33 -0
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +1 -1
- package/prompts/conductor-framework-context.md +1 -1
- package/prompts/github/pr_crossplatform_agent.md +283 -0
- package/prompts/github/pr_i18n_agent.md +279 -0
- package/prompts/inbox-clarification.md +18 -11
- package/skills/apt/SKILL.md +9 -3
- package/skills/apt-close-task/SKILL.md +48 -0
- package/skills/apt-debug/SKILL.md +19 -9
- package/skills/apt-execute/SKILL.md +3 -1
- package/skills/apt-fan-out/SKILL.md +65 -1
- package/skills/apt-merge-conflict-resolution/SKILL.md +227 -0
- package/skills/apt-pr-review/SCHEMA.md +4 -2
- package/skills/apt-pr-review/SKILL.md +117 -29
- package/skills/apt-quick/SKILL.md +24 -1
- package/skills/apt-review/SKILL.md +6 -0
- package/skills/apt-run/SKILL.md +1 -1
- package/skills/apt-setup/SKILL.md +21 -3
- package/skills/apt-ship/SKILL.md +32 -11
- package/skills/apt-terminal/SKILL.md +8 -0
- package/skills/apt-triage/SKILL.md +4 -0
- package/skills/apt-verify/SKILL.md +24 -0
- package/skills/apt-watch-ci/SKILL.md +3 -1
- package/src/cli/commands/audit-branch-current.mjs +3 -3
- package/src/cli/commands/event.mjs +6 -4
- package/src/cli/commands/fan-out.mjs +3 -1
- package/src/cli/commands/features-audit.mjs +6 -1
- package/src/cli/commands/followups.mjs +303 -0
- package/src/cli/commands/health-check.mjs +3 -1
- package/src/cli/commands/init.mjs +29 -0
- package/src/cli/commands/merge-detect.mjs +203 -0
- package/src/cli/commands/merge-integrate.mjs +392 -0
- package/src/cli/commands/pr-review/__fixtures__/android-strings/app/src/main/res/values-fr/strings.xml +3 -0
- package/src/cli/commands/pr-review/__fixtures__/babel-js/package.json +4 -0
- package/src/cli/commands/pr-review/__fixtures__/c-ifdef/src/main.c +4 -0
- package/src/cli/commands/pr-review/__fixtures__/ci-matrix/.github/workflows/ci.yml +10 -0
- package/src/cli/commands/pr-review/__fixtures__/cmake-win/CMakeLists.txt +5 -0
- package/src/cli/commands/pr-review/__fixtures__/diffs/add-arb.patch +9 -0
- package/src/cli/commands/pr-review/__fixtures__/diffs/ci-single-os-dep.patch +13 -0
- package/src/cli/commands/pr-review/__fixtures__/diffs/ci-single-os-list-item-dep.patch +17 -0
- package/src/cli/commands/pr-review/__fixtures__/diffs/lone-weak-t.patch +8 -0
- package/src/cli/commands/pr-review/__fixtures__/diffs/monorepo-b.patch +7 -0
- package/src/cli/commands/pr-review/__fixtures__/diffs/two-weak-xpl.patch +12 -0
- package/src/cli/commands/pr-review/__fixtures__/dropped-underscore/src/app.js +2 -0
- package/src/cli/commands/pr-review/__fixtures__/electron-app/package.json +4 -0
- package/src/cli/commands/pr-review/__fixtures__/i18n-locale-file/src/locales/fr.json +1 -0
- package/src/cli/commands/pr-review/__fixtures__/monorepo/packages/a/locales/fr.json +1 -0
- package/src/cli/commands/pr-review/__fixtures__/monorepo/packages/b/src/index.ts +1 -0
- package/src/cli/commands/pr-review/__fixtures__/py-babel/pyproject.toml +3 -0
- package/src/cli/commands/pr-review/__fixtures__/qt-i18n-dir/i18n/app_fr.ts +11 -0
- package/src/cli/commands/pr-review/__fixtures__/rust-cli/Cargo.toml +7 -0
- package/src/cli/commands/pr-review/__fixtures__/rust-cli/src/main.rs +1 -0
- package/src/cli/commands/pr-review/__fixtures__/ts-dir/ts/foo.ts +3 -0
- package/src/cli/commands/pr-review/detect-specialists.mjs +1029 -0
- package/src/cli/commands/pr-review.mjs +12 -2
- package/src/cli/commands/precompact-hook.mjs +10 -6
- package/src/cli/commands/roadmap.mjs +24 -11
- package/src/cli/commands/seed.mjs +3 -1
- package/src/cli/commands/state.mjs +9 -3
- package/src/cli/commands/statusline.mjs +5 -1
- package/src/cli/commands/task.mjs +135 -23
- package/src/cli/commands/worktree.mjs +298 -81
- package/src/cli/config/post-merge-sweep.mjs +3 -2
- package/src/cli/config/share-policy.mjs +16 -3
- package/src/cli/config/upgrade-gitignore.mjs +27 -5
- package/src/cli/consistency/parse-qa.mjs +11 -1
- package/src/cli/consistency/parse-review.mjs +36 -0
- package/src/cli/consistency/parse-spec.mjs +43 -0
- package/src/cli/coordination/agent-identity.mjs +6 -4
- package/src/cli/dispatch.mjs +4 -0
- package/src/cli/followups/derive.mjs +123 -0
- package/src/cli/followups/schema.mjs +264 -0
- package/src/cli/gate/gates/followups-adjudicated.mjs +158 -0
- package/src/cli/gate/gates/index.mjs +1 -0
- package/src/cli/git/materialize-env.mjs +212 -0
- package/src/cli/help.mjs +9 -1
- package/src/cli/install/legacy-paths.mjs +5 -0
- package/src/cli/roadmap/lifecycle.mjs +4 -1
- package/src/cli/task/ids.mjs +9 -0
- package/src/cli/task/index-md.mjs +12 -4
- package/src/cli/util/io.mjs +15 -1
- package/src/cli/util/project.mjs +11 -14
- package/src/cli/util/state-paths.mjs +45 -0
- package/prompts/followup_planner.md +0 -399
|
@@ -17,6 +17,8 @@ execution_modes:
|
|
|
17
17
|
allowed-tools: "Bash, Read, Grep, Glob, Write"
|
|
18
18
|
argument-hint: "apt:close-task [--all | --task <id>] [--narrate-only]"
|
|
19
19
|
gates: []
|
|
20
|
+
config_keys:
|
|
21
|
+
- followups.after_close
|
|
20
22
|
---
|
|
21
23
|
<objective>
|
|
22
24
|
Confirm a shipped PR has merged on GitHub, then run the closer side of the task lifecycle: flip `lifecycle_phase` from `shipped-pending-merge` to `completed`, strip stale per-PR overrides from `.aperant/config.json`, clean up worktree per policy, and emit `task.closed.merged` events for the audit trail. This is the post-merge counterpart to `/apt:ship`.
|
|
@@ -54,6 +56,50 @@ Run `apt-tools task close-merged . --all --dry-run` first to see what would happ
|
|
|
54
56
|
|
|
55
57
|
If `--task <id>` is passed, narrow to that task.
|
|
56
58
|
|
|
59
|
+
## 1.5 Disposition follow-ups (autonomy-aware — G45)
|
|
60
|
+
|
|
61
|
+
Run this BEFORE §2. The `task close` path that §2 invokes carries a CLI-level disposition gate (G45): it refuses to close while `{task_dir}/followups.json` has any open row, or any `dispositioned` row missing a valid `disposition.kind` + `ref`. Disposition every open row here so §2 closes cleanly without `--force`.
|
|
62
|
+
|
|
63
|
+
For each candidate the dry-run says `close`, read the effective policy:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node packages/framework/bin/apt-tools.mjs followups policy . --task-id <id>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The envelope returns `{ after_close: 'ask' | 'file' | 'chain', max_chain_depth, source }`. `after_close` derives from `config.followups.after_close` when set, else from the task autonomy level (≤1 `ask` / 2 `file` / 3 `chain`). Then enumerate the open rows from `{task_dir}/followups.json` and route each by class + policy:
|
|
70
|
+
|
|
71
|
+
**`ask` (autonomy ≤ 1).** Present each open row to the user with its `class_proposed`, `adjudication.verdict`, and `files`. Offer fix / file / drop:
|
|
72
|
+
- fix → execute the change in this session (only if it is genuinely small + safe), commit, flip the row `status: 'fixed'`, optionally record `executed_commit`.
|
|
73
|
+
- file → the `new-task` flow below.
|
|
74
|
+
- drop → `apt-tools followups disposition . --task-id <id> --row <row-id> --kind dropped_by_user --ref <reason-slug> --reason "<text>"`.
|
|
75
|
+
|
|
76
|
+
**`file` (autonomy 2).** Auto-file every `new-task` row through the EXISTING triage machine (report, do NOT execute):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# 1) create the task seeded from the row
|
|
80
|
+
node packages/framework/bin/apt-tools.mjs task create . --description "<row title>" --scope <scope> [...linkage]
|
|
81
|
+
# 2) enter it at needs-triage; the configured task_tracking.backend mirrors it
|
|
82
|
+
# (github-issues creates/updates a real labeled issue idempotently)
|
|
83
|
+
node packages/framework/bin/apt-tools.mjs triage init . --id <new-task-id>
|
|
84
|
+
# 3) record the disposition (ref = the filed task id; the triage mirror carries the issue URL)
|
|
85
|
+
node packages/framework/bin/apt-tools.mjs followups disposition . --task-id <id> --row <row-id> --kind filed_task --ref <new-task-id>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
There is **NO new `gh issue create` logic** — filing goes through `task create` + `triage init`, and the backend mirror does the GitHub work (ID-08). This works with the local-only default (no `task_tracking` key) AND with `github-issues` when configured; never hard-require gh.
|
|
89
|
+
|
|
90
|
+
**`polish` rows** (any policy) → park in the committable Deferred Ideas section (ID-09): append `- {title} -- deferred because {reason}` under `## Deferred Ideas` in `context/notes/{task-id}.md` (create the section/file if absent), then `apt-tools followups disposition . --task-id <id> --row <row-id> --kind parked_note --ref .aperant/context/notes/{task-id}.md`. Polish rows must NOT linger in the ledger — the ledger is ephemeral.
|
|
91
|
+
|
|
92
|
+
**`chain` (autonomy 3).** Do the `file` flow, THEN auto-chain depth-1 ONLY the filed tasks that triage to `ready-for-agent` (run the chained task through the full pipeline gates). Pass the depth marker on the chained task so the next-level policy degrades `chain → file`:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
node packages/framework/bin/apt-tools.mjs triage init . --id <new-task-id> \
|
|
96
|
+
--extra '{"followups_chain": {"origin_task": "<id>", "depth": 1}}'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`ready-for-human` items stop there by design — they are filed + reported, never auto-executed. `max_chain_depth` is clamped to 1 in v1 (anti-perpetual-motion); `followups policy` already degrades `chain → file` when the current task's `triage.json` carries `extra.followups_chain.depth >= max_chain_depth`.
|
|
100
|
+
|
|
101
|
+
After this step, every open row carries a durable disposition, so §2's `task close` clears the gate.
|
|
102
|
+
|
|
57
103
|
## 2. Confirm merge
|
|
58
104
|
|
|
59
105
|
For each candidate the dry-run says `close`:
|
|
@@ -124,6 +170,8 @@ close-merged emits an envelope with:
|
|
|
124
170
|
|
|
125
171
|
Report the summary verbatim. If anything landed in `skipped[]` with a recoverable reason (`offline`, `rate_limited`), suggest the user re-run once the network clears.
|
|
126
172
|
|
|
173
|
+
**Follow-ups-gate recovery (G45).** The passive post-merge sweep (and any bare `task close-merged` that skips §1.5) inherits the CLI close gate: a merged task whose `followups.json` still has open/undispositioned rows fails its spawned `task close` and lands in `errors[]` with a `task-close-failed` reason — the task STAYS in `shipped-pending-merge`. This is the designed backstop, not a bug. Recover by running `/apt:close-task --task <id>` (which runs §1.5 first to disposition every open row), then the close clears the gate. As a last resort, `apt-tools task close . --id <id> --force --reason "<text>"` bypasses the gate with an audited `task.close.followups-override` event.
|
|
174
|
+
|
|
127
175
|
**Local cleanup recap (best-effort).** On confirmed merge, `task close-merged` runs `computeWorktreeCleanup` against the primary repo (`packages/framework/src/cli/task/worktree-cleanup.mjs:140-230`): `git fetch origin <base>` (failure → recorded as `fetch_failed` warning, cleanup proceeds), `git checkout <base>` (failure → returns `action: 'checkout_failed'` and preserves BOTH the worktree and the task branch — nothing is removed), `git merge --ff-only origin/<base>` (failure → `ff_only_failed` warning, GC still runs), `git worktree remove <wtPath>`, and `git branch -d <taskBranch>` (failure on squash/rebase merges → `branch_delete_failed` warning, `branch_deleted: false` in the envelope; user can `git branch -D` manually). This is why `/apt:watch-ci` auto-merge MUST NOT pass `gh pr merge --delete-branch` — that flag would try to do the same work from inside the worktree where `git switch <base>` cannot succeed (see FRAMEWORK-BUG-019).
|
|
128
176
|
|
|
129
177
|
## 4. Narrate-only mode
|
|
@@ -42,6 +42,7 @@ reproduction, so loading this appendix is non-trivial.
|
|
|
42
42
|
- **apt-tools path:** `node packages/framework/bin/apt-tools.mjs` or the locally installed `apt-tools` binary
|
|
43
43
|
- **Constitution:** Read `AGENTS.md` in the project root if it exists
|
|
44
44
|
- **Debug sessions:** `.aperant/debug/{session-id}/DEBUG.md`
|
|
45
|
+
- **Task-level worktree isolation:** A worktree is provisioned at session start by `task create` (track-agnostic — DEBUG gets one too; parsed + banner-announced in §1a) when `task_isolation.worktree_per_task` is enabled. Read-only investigation (Sections 2-3 Observe / Hypothesize — `git log`, `git diff`, Grep, Read) may run from EITHER cwd; they only read. But once code changes begin — Section 4 (Checkpoint), Section 5b (Execute Test — the minimal change), Section 6b (Implement Fix + commit) — run those Bash commands with `cd {worktree_path} &&` so the fix + checkpoint commits land on the worktree's task branch. Use the `worktree_path` the router passed in skill context or that §1a parsed from `task create`'s envelope. `apt-tools` calls always take the project root as `<project-dir>`. If no `worktree` block was returned, proceed in the project root as before.
|
|
45
46
|
</your_environment>
|
|
46
47
|
|
|
47
48
|
<state_files>
|
|
@@ -98,6 +99,15 @@ Sequence:
|
|
|
98
99
|
|
|
99
100
|
The debug working dir at `.aperant/debug/{session-id}/` stays — that working artifact is preserved (per spec §3 R7). The canonical task record at `.aperant/tasks/{task-id}/` is created in parallel by `task create`, and both share the same id.
|
|
100
101
|
|
|
102
|
+
**Worktree isolation (both paths).** `task create` provisions a worktree when `task_isolation.worktree_per_task` is enabled (track-agnostic — DEBUG gets one too). Parse `worktree.worktree_path`, `worktree.branch`, and `worktree.base_branch` from the `task create` envelope (direct path); on the router path the orchestrator already passed `worktree_path` into your skill context — use that. When a `worktree` block is present, print the banner:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
[APT] Working in isolated worktree: {worktree_path}
|
|
106
|
+
[APT] Task branch: {branch} (from {base_branch})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Investigation (Sections 2-3) may run from either cwd — but once you reach the fix-applying phases (Section 4 Checkpoint, Section 5b Execute Test, Section 6b Implement Fix), prefix those Bash commands with `cd {worktree_path} &&` so commits land on the task branch. If no `worktree` block is returned, proceed in the project root as before.
|
|
110
|
+
|
|
101
111
|
### 1b. Create debug working directory
|
|
102
112
|
|
|
103
113
|
```bash
|
|
@@ -202,11 +212,11 @@ Update DEBUG.md:
|
|
|
202
212
|
|
|
203
213
|
## 4. Checkpoint — Save State Before Changes
|
|
204
214
|
|
|
205
|
-
Before making any code changes, create a git checkpoint:
|
|
215
|
+
Before making any code changes, create a git checkpoint. From here on, code-change Bash commands run in the task worktree when one was provisioned — prefix them with `cd {worktree_path} &&` (omit the prefix if no `worktree` block was returned in §1a):
|
|
206
216
|
|
|
207
217
|
```bash
|
|
208
|
-
git stash push -m "debug-checkpoint-{session-id}-pre-test" || true
|
|
209
|
-
git rev-parse HEAD > .aperant/debug/{session-id}/checkpoints/pre-test.ref
|
|
218
|
+
cd {worktree_path} && git stash push -m "debug-checkpoint-{session-id}-pre-test" || true
|
|
219
|
+
cd {worktree_path} && git rev-parse HEAD > .aperant/debug/{session-id}/checkpoints/pre-test.ref
|
|
210
220
|
```
|
|
211
221
|
|
|
212
222
|
Record the checkpoint in DEBUG.md:
|
|
@@ -215,9 +225,9 @@ Record the checkpoint in DEBUG.md:
|
|
|
215
225
|
- {timestamp} — pre-test: {commit-sha} (before testing hypotheses)
|
|
216
226
|
```
|
|
217
227
|
|
|
218
|
-
If a hypothesis test fails badly, revert to checkpoint:
|
|
228
|
+
If a hypothesis test fails badly, revert to checkpoint (in the worktree):
|
|
219
229
|
```bash
|
|
220
|
-
git checkout $(cat .aperant/debug/{session-id}/checkpoints/pre-test.ref) -- .
|
|
230
|
+
cd {worktree_path} && git checkout $(cat .aperant/debug/{session-id}/checkpoints/pre-test.ref) -- .
|
|
221
231
|
```
|
|
222
232
|
|
|
223
233
|
## 5. Test — Validate Hypotheses
|
|
@@ -229,7 +239,7 @@ For each hypothesis (starting with most likely):
|
|
|
229
239
|
- What is the expected outcome if the hypothesis is correct?
|
|
230
240
|
|
|
231
241
|
### 5b. Execute Test
|
|
232
|
-
- Make the minimal change needed to test the hypothesis
|
|
242
|
+
- Make the minimal change needed to test the hypothesis — apply it in the task worktree (edit files under `{worktree_path}` and run any verification command with `cd {worktree_path} &&`) when a `worktree` block was provisioned in §1a; otherwise edit in the project root.
|
|
233
243
|
- Run the relevant command/test
|
|
234
244
|
- Record the actual outcome
|
|
235
245
|
|
|
@@ -283,12 +293,12 @@ Document the root cause clearly:
|
|
|
283
293
|
```
|
|
284
294
|
|
|
285
295
|
### 6b. Implement Fix
|
|
286
|
-
- Apply the minimal fix that addresses the root cause
|
|
296
|
+
- Apply the minimal fix that addresses the root cause — in the task worktree (edit files under `{worktree_path}`) when one was provisioned in §1a, so the commit lands on the task branch.
|
|
287
297
|
- Run verification (tests, typecheck, lint)
|
|
288
|
-
- If verification passes, commit:
|
|
298
|
+
- If verification passes, commit (prefix with `cd {worktree_path} &&` when a worktree was provisioned so `git` stages + commits on the worktree's task branch; `apt-tools commit` takes no `<project-dir>` and operates on the current cwd):
|
|
289
299
|
|
|
290
300
|
```bash
|
|
291
|
-
node packages/framework/bin/apt-tools.mjs commit "fix: {concise description of fix}" --files {changed-files}
|
|
301
|
+
cd {worktree_path} && node packages/framework/bin/apt-tools.mjs commit "fix: {concise description of fix}" --files {changed-files}
|
|
292
302
|
```
|
|
293
303
|
|
|
294
304
|
### 6c. Update Status
|
|
@@ -555,10 +555,12 @@ Read `state.active_tasks[<task-id>].install_status`. Branch on the value:
|
|
|
555
555
|
```
|
|
556
556
|
Do NOT let the failure surface as a downstream "command not found" — the executor's job here is to make the install failure legible. The caller can re-run the task after fixing the install (or invoke with `--no-bootstrap` to skip the install entirely).
|
|
557
557
|
|
|
558
|
+
> **State resolves to the main repo from any cwd (state-fork-fix).** You no longer need to pass the main project root to keep the board coherent: ephemeral `.aperant/` state — `state.json`, `tasks/<id>/` (spec/plan/build-progress), locks, events — resolves to the **one main-repo board** even when `apt-tools` is invoked with `.` from a worktree cwd. The board never forks. (Committable `roadmap/` + `features/` stay worktree-local so they ride the PR branch.) `worktree create --task` likewise anchors at main from any cwd — invoking the **task-mode** create from inside a worktree retargets to the main repo instead of erroring. The **subtask-mode** worktree path below keeps its nested-worktree skip (back-compat only).
|
|
559
|
+
|
|
558
560
|
**2b. Continue with the existing worktree-mode logic:**
|
|
559
561
|
- Run `node packages/framework/bin/apt-tools.mjs detect-worktree .` — if `is_worktree: true`:
|
|
560
562
|
- Log: "Skipping worktree isolation (already in worktree)"
|
|
561
|
-
- Do NOT create worktrees (
|
|
563
|
+
- Do NOT create SUBTASK-mode worktrees from here (the legacy subtask-mode path refuses nesting; task-mode `worktree create --task` is safe — it anchors at main)
|
|
562
564
|
- Agents execute in the current working directory directly
|
|
563
565
|
- Note: locks still coordinate correctly (apt-tools uses shared lock dir via git common dir)
|
|
564
566
|
- If `use_worktrees` is `true` AND `is_worktree` is `false` AND no task-level `worktree_path` is present: set `WORKTREE_MODE=true`
|
|
@@ -85,6 +85,15 @@ node packages/framework/bin/apt-tools.mjs worktree create --task ${task_id}
|
|
|
85
85
|
|
|
86
86
|
(Skip if `state.active_tasks[task_id].worktree_path` is already set — the create command is idempotent.)
|
|
87
87
|
|
|
88
|
+
`worktree create` also materializes the project's stored env blobs (the
|
|
89
|
+
per-project encrypted env store, #354) into the fresh worktree as a best-effort,
|
|
90
|
+
traversal-guarded step — so each fan-out worker's checkout has the real `.env*`
|
|
91
|
+
files instead of crashing in no-cloud mode. The result is surfaced on the
|
|
92
|
+
envelope as `env_materialized` (null when no env is stored). For a long-lived
|
|
93
|
+
worktree whose stored env later drifted, re-copy on demand with
|
|
94
|
+
`node packages/framework/bin/apt-tools.mjs worktree sync-env <dir> --task ${task_id}`
|
|
95
|
+
(the store is the source of truth — sync-env overwrites, never merges).
|
|
96
|
+
|
|
88
97
|
## 3. Spawn N apt-executor subagents
|
|
89
98
|
|
|
90
99
|
Read the `parallelization.max_tasks` cap from `.aperant/config.json` (or default 4). Slice `task_ids` into batches of size `min(cap, task_ids.length)`. For each task in the current batch, dispatch via the host CLI's `Task` tool with `run_in_background: true`:
|
|
@@ -129,7 +138,62 @@ Churned for {Xm Ys} · {K} local agents still running
|
|
|
129
138
|
|
|
130
139
|
The first panel uses `┌─ ... │` (top-with-stem); the LAST panel uses `└─ ... ` (bottom corner). Footer line: "Churned" = elapsed since the most recent lifecycle event across all in-flight workers; "K local agents still running" = count of workers whose `lifecycle_phase` is non-terminal AND `assigned_to` is non-null.
|
|
131
140
|
|
|
132
|
-
## 5.
|
|
141
|
+
## 5. Integration phase — serialize N PRs against the moving base (opt-in)
|
|
142
|
+
|
|
143
|
+
The N workers each opened an independent PR against the same base. Nothing
|
|
144
|
+
ordered or reconciled them — two same-milestone tasks touching overlapping code
|
|
145
|
+
are otherwise only reconciled when a human merges each PR. This phase closes
|
|
146
|
+
that gap. It is **opt-in** via `pipeline.resolve_merge_conflicts` (default
|
|
147
|
+
false) in `.aperant/config.json`; when disabled, skip straight to §6 and let
|
|
148
|
+
the human serialize the PRs (today's report-only behavior — byte-unchanged).
|
|
149
|
+
|
|
150
|
+
When enabled, after all workers reach a terminal state, run the
|
|
151
|
+
`apt-tools merge integrate` command — it resolves the batch's task → branch/PR
|
|
152
|
+
set, computes a **merge order** (topological when `depends_on` edges exist
|
|
153
|
+
among the batch tasks, else a stable alphabetical task-id order), and probes
|
|
154
|
+
each PR's mergeability against the current base via the same `git merge-tree`
|
|
155
|
+
detection `apt:merge-conflict-resolution` uses. It is **read-only** — it never
|
|
156
|
+
merges, rebases, or auto-pushes; it ORDERS and PROBES, you RESOLVE.
|
|
157
|
+
|
|
158
|
+
First fetch the latest base tips so the probes are against the moving base, then
|
|
159
|
+
call `integrate` with the batch's task list (or `--batch ${batch_id}` to recover
|
|
160
|
+
it from the worker rows):
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
git -C "$conductor_root" fetch origin "$base_branch"
|
|
164
|
+
node packages/framework/bin/apt-tools.mjs merge integrate . --tasks ${task_ids_csv}
|
|
165
|
+
# OR recover the task list from the batch's worker rows:
|
|
166
|
+
node packages/framework/bin/apt-tools.mjs merge integrate . --batch ${batch_id}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Returns a structured integration report:
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"status": "ok",
|
|
173
|
+
"command": "merge-integrate",
|
|
174
|
+
"order_strategy": "topological",
|
|
175
|
+
"order": ["task-a", "task-b"],
|
|
176
|
+
"summary": { "total": 2, "mergeable": 1, "conflicted": 1, "needs_human": 0 },
|
|
177
|
+
"tasks": [
|
|
178
|
+
{ "task_id": "task-a", "status": "mergeable", "action": "advance", "mergeable": true },
|
|
179
|
+
{ "task_id": "task-b", "status": "conflicted", "action": "resolve-conflict",
|
|
180
|
+
"conflicts_preview": ["src/x.ts"],
|
|
181
|
+
"resolution": { "skill": "apt:merge-conflict-resolution", "worktree_path": "…", "base": "origin/main" } }
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Walk the `tasks` array **in `order`** (the command already serialized it):
|
|
187
|
+
|
|
188
|
+
1. **`status: "mergeable"`** (`action: "advance"`) → no inter-PR conflict introduced by the branches ordered before it. Nothing to do; continue.
|
|
189
|
+
2. **`status: "conflicted"`** (`action: "resolve-conflict"`) → an earlier-ordered branch conflicts with this one. Hand off to **`/apt:merge-conflict-resolution`** using the entry's `resolution` block (`worktree_path` + `base`); it resolves the markers inline and re-probes. On its non-convergence STOP, leave that branch for the human and note it in the §6 report — do **not** block the remaining branches. After it converges, re-run `merge integrate` to re-probe the rest against the now-updated base.
|
|
190
|
+
3. **`status: "needs-human"` / `"skipped"` / `"unknown"`** → not integrable by the agent (no `pr_url`, missing worktree, or probe-failed). Carry the `reason` into the §6 report and move on.
|
|
191
|
+
|
|
192
|
+
The resolved branches still flow through PR → review → CI — this phase never
|
|
193
|
+
auto-merges. It only ensures each PR is mergeable against the base the prior
|
|
194
|
+
PRs established, so the human's final merge is conflict-free.
|
|
195
|
+
|
|
196
|
+
## 6. Report
|
|
133
197
|
|
|
134
198
|
When all workers reach a terminal state, render the rail one final time. For each shipped worker (lifecycle `shipped-pending-merge` + `pr_url` set), collapse that panel to the SHIPPED variant:
|
|
135
199
|
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: apt:merge-conflict-resolution
|
|
3
|
+
description: "Agent-inline merge-conflict resolution — detect conflicts via apt-tools merge, resolve markers inline, re-probe, fall back to STOP"
|
|
4
|
+
apt-skill-version: {{APT_VERSION}}
|
|
5
|
+
stage: ship
|
|
6
|
+
intent: x-merge-resolve
|
|
7
|
+
when_to_use: "A clean fast-forward rebase or merge is impossible because the task branch conflicts with its base (apt:ship §0.7, a fan-out integration sweep, or an apt:watch-ci diverged-bailed branch) and you want the executing agent to resolve the conflicts inline before re-probing mergeability."
|
|
8
|
+
user_invocable: true
|
|
9
|
+
internal: false
|
|
10
|
+
spawns_agent: false
|
|
11
|
+
agent_name: null
|
|
12
|
+
task_context: require-existing
|
|
13
|
+
default_execution_mode: auto
|
|
14
|
+
execution_modes:
|
|
15
|
+
- auto
|
|
16
|
+
- step
|
|
17
|
+
allowed-tools: "Bash, Read, Write, Edit, Grep, Glob"
|
|
18
|
+
argument-hint: "apt:merge-conflict-resolution [--base <ref>] [--head <ref>]"
|
|
19
|
+
gates: []
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# /apt:merge-conflict-resolution — Agent-Inline Merge Conflict Resolution
|
|
23
|
+
|
|
24
|
+
<objective>
|
|
25
|
+
You are the Aperant merge-conflict resolver. When a task branch can no longer
|
|
26
|
+
fast-forward or rebase cleanly onto its base, you resolve the conflicting
|
|
27
|
+
files **inline** — reading the conflict markers, deciding the correct merged
|
|
28
|
+
content with full repository context, and writing it back — then re-probe
|
|
29
|
+
mergeability and continue. You are the opt-in resolution path the three
|
|
30
|
+
framework merge gates fall into instead of hard-STOPping: `apt:ship` §0.7
|
|
31
|
+
(single-task base-drift), the fan-out integration sweep (inter-PR conflicts
|
|
32
|
+
as each branch lands), and `apt:watch-ci`'s `diverged-bailed` branch
|
|
33
|
+
(self-heal instead of bail).
|
|
34
|
+
|
|
35
|
+
The decisive architectural rule: resolution is **agent-inline**, never a
|
|
36
|
+
programmatic provider client. The framework CLI is self-contained ESM and
|
|
37
|
+
declares no `@aperant/core` dependency, so there is no app credential store
|
|
38
|
+
or LLM transport to call from `.mjs`. The agent driving the CLI is already
|
|
39
|
+
an LLM with a transport — you read the markers `apt-tools merge` surfaces and
|
|
40
|
+
resolve them with your own reasoning. Detection is deterministic (`git
|
|
41
|
+
merge-tree`); resolution is yours.
|
|
42
|
+
|
|
43
|
+
The human stays the final gate. A resolved merge still flows through PR →
|
|
44
|
+
review → CI; you never push to a protected branch and never auto-merge.
|
|
45
|
+
</objective>
|
|
46
|
+
|
|
47
|
+
<your_environment>
|
|
48
|
+
- **Working directory:** The task worktree (or project root) whose branch needs to integrate its base. All `apt-tools` and `git` calls run with this as the cwd / `<project-dir>` argument.
|
|
49
|
+
- **apt-tools path:** `node packages/framework/bin/apt-tools.mjs` (or `node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs` when running from an install mirror).
|
|
50
|
+
- **Detection primitive:** `apt-tools merge detect <dir> [--base <ref>] [--head <ref>]` — a read-only trial-merge probe (`git merge-tree --write-tree`) that reuses the exact detection from `audit branch-current`. Emits `{ mergeable, conflicts_preview? }`. NO history rewrite, NO index mutation. `--base` defaults to `origin/main`; `--head` defaults to `HEAD`.
|
|
51
|
+
- **Marker reader:** `apt-tools merge markers <dir>` — after a real `git merge`/`git rebase` leaves the worktree conflicted, lists the unmerged paths and reads each file's full content (markers and all) so you can resolve them inline. Emits `{ conflicted, files: [{ path, content }] }`.
|
|
52
|
+
- **git:** You drive the actual `git merge` / `git rebase` / `git add` / `git rebase --continue` yourself — the CLI only observes, it never mutates. Requires git ≥ 2.38 for the `detect` probe.
|
|
53
|
+
</your_environment>
|
|
54
|
+
|
|
55
|
+
<state_files>
|
|
56
|
+
## State Files
|
|
57
|
+
|
|
58
|
+
**Reads:**
|
|
59
|
+
- `.aperant/state.json` — `active_tasks[{task-id}].autonomy` (autonomy gating) and `autonomy.default` fallback.
|
|
60
|
+
- The conflicted source files themselves, via `apt-tools merge markers` (full content with markers).
|
|
61
|
+
|
|
62
|
+
**Writes:**
|
|
63
|
+
- The resolved source files in the worktree (via Edit/Write — your inline resolution).
|
|
64
|
+
- The git index + a merge/rebase-continuation commit, via `git add` + `git commit` / `git rebase --continue` (you drive git; the CLI never does).
|
|
65
|
+
|
|
66
|
+
This skill writes NO `.aperant/` state of its own — it is a leaf resolution
|
|
67
|
+
step invoked by `apt:ship`, `apt:fan-out`, or `apt:watch-ci`, each of which
|
|
68
|
+
owns its own state. Detection (`apt-tools merge detect|markers`) is pure
|
|
69
|
+
read-only.
|
|
70
|
+
</state_files>
|
|
71
|
+
|
|
72
|
+
<process>
|
|
73
|
+
|
|
74
|
+
## 1. Probe mergeability
|
|
75
|
+
|
|
76
|
+
Resolve the base and head refs (defaults: `--base origin/main`, `--head HEAD`).
|
|
77
|
+
Fetch the base first so the probe is against the live remote tip:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git -C "$dir" fetch origin "$base_branch"
|
|
81
|
+
node packages/framework/bin/apt-tools.mjs merge detect "$dir" --base "origin/$base_branch"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Branch on the envelope:
|
|
85
|
+
|
|
86
|
+
- **`mergeable === true`** → nothing to resolve. Return to the caller (or run the clean rebase the caller requested). DONE.
|
|
87
|
+
- **`mergeable === null`** (`audit_warning: "git-version-too-old"` or `"merge-tree-probe-failed"`) → STOP. You cannot safely probe. Emit the warning verbatim and hand back to the human:
|
|
88
|
+
```
|
|
89
|
+
[APT:merge] Cannot resolve — mergeability is unprobeable ({audit_warning}). Upgrade git ≥ 2.38 or rebase manually, then re-run.
|
|
90
|
+
```
|
|
91
|
+
- **`mergeable === false`** → conflicts exist. Print `conflicts_preview` verbatim and proceed to §2.
|
|
92
|
+
|
|
93
|
+
## 2. Materialize the conflict in the worktree
|
|
94
|
+
|
|
95
|
+
The `detect` probe is read-only — it never touched your index. To resolve, you
|
|
96
|
+
need the real conflicted state on disk. Drive git yourself (the SKILL owns
|
|
97
|
+
history changes, the CLI never does):
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git -C "$dir" rebase "origin/$base_branch" # or: git -C "$dir" merge "origin/$base_branch"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Prefer `rebase` for single-task base-drift (apt:ship §0.7) and the fan-out
|
|
104
|
+
integration sweep so the branch stays linear; prefer `merge` only when the
|
|
105
|
+
caller explicitly asked for it. The command will exit non-zero and leave the
|
|
106
|
+
worktree in a conflicted (rebasing/merging) state — that is expected.
|
|
107
|
+
|
|
108
|
+
## 3. Read the markers + resolve inline
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
node packages/framework/bin/apt-tools.mjs merge markers "$dir"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
For each entry in `files`, the `content` field carries the file verbatim with
|
|
115
|
+
`<<<<<<<` / `=======` / `>>>>>>>` markers. For every conflicted file:
|
|
116
|
+
|
|
117
|
+
1. **Understand both sides.** The `HEAD` side (above `=======`) is the current
|
|
118
|
+
branch's change; the other side (below `=======`, up to `>>>>>>>`) is the
|
|
119
|
+
base's change. Read the surrounding file and, when the conflict is
|
|
120
|
+
semantic, the related modules — you have full repository context the
|
|
121
|
+
deterministic merger does not.
|
|
122
|
+
2. **Resolve with intent, not by picking a side.** Produce the content that
|
|
123
|
+
correctly carries BOTH intents where they are compatible (e.g. both sides
|
|
124
|
+
added an import → keep both; both edited the same function → reconcile the
|
|
125
|
+
two edits). Pick one side only when the changes are genuinely mutually
|
|
126
|
+
exclusive and you can justify which wins.
|
|
127
|
+
3. **Remove every marker.** The resolved file MUST contain no `<<<<<<<`,
|
|
128
|
+
`=======`, or `>>>>>>>` line. Write it back with Edit/Write.
|
|
129
|
+
4. **Stage it.** `git -C "$dir" add "<path>"`.
|
|
130
|
+
|
|
131
|
+
Do this for every conflicted path before continuing.
|
|
132
|
+
|
|
133
|
+
## 4. Continue the merge/rebase
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
git -C "$dir" rebase --continue # or: git -C "$dir" commit --no-edit (for a merge)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A rebase may surface a SECOND round of conflicts on the next replayed commit.
|
|
140
|
+
If so, loop back to §3 (`apt-tools merge markers` again) and resolve the new
|
|
141
|
+
round. Repeat until the rebase/merge completes.
|
|
142
|
+
|
|
143
|
+
## 5. Re-probe + verify
|
|
144
|
+
|
|
145
|
+
Confirm the branch is now clean against the base and the tree is whole:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
node packages/framework/bin/apt-tools.mjs merge detect "$dir" --base "origin/$base_branch"
|
|
149
|
+
grep -rEn '^(<<<<<<<|=======|>>>>>>>)' "$dir" --include='*.*' && echo "STRAY MARKERS — re-resolve" || true
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- **`mergeable === true` AND no stray markers** → resolution succeeded. Hand
|
|
153
|
+
back to the caller, which proceeds to its push / PR / re-run-CI step. The
|
|
154
|
+
resolved branch still goes through PR → review → CI; you do NOT auto-merge.
|
|
155
|
+
- **Still `mergeable === false`** → you missed a conflict. Loop back to §3.
|
|
156
|
+
- **Loop budget exhausted** (you have re-entered §3 more than **3** times, or
|
|
157
|
+
the same files keep re-conflicting) → STOP. Abort the in-progress operation
|
|
158
|
+
to leave a clean tree, and hand back to the human:
|
|
159
|
+
```bash
|
|
160
|
+
git -C "$dir" rebase --abort # or: git -C "$dir" merge --abort
|
|
161
|
+
```
|
|
162
|
+
```
|
|
163
|
+
[APT:merge] Could not converge after 3 resolution rounds — handing back. Conflicts: {paths}. Resolve manually, then re-run.
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## 6. Insertion points (where this skill is invoked from)
|
|
167
|
+
|
|
168
|
+
This skill is the opt-in resolution path for three existing framework gates.
|
|
169
|
+
Each gate decides — by autonomy level + config — whether to fall into this
|
|
170
|
+
skill or keep its legacy hard-STOP. The references are doc-only here; the
|
|
171
|
+
gates document the hand-off in their own SKILL.md:
|
|
172
|
+
|
|
173
|
+
- **`apt:ship` §0.7** — the `mergeable === false` branch. Legacy behavior is a
|
|
174
|
+
hard STOP ("resolve manually"). With resolution opted in, the branch hands
|
|
175
|
+
to §2 of this skill instead, then resumes §1 of apt:ship on success.
|
|
176
|
+
- **`apt:fan-out` integration sweep** — the new serialization phase between
|
|
177
|
+
spawn and report. As each worker's branch lands against the moving base,
|
|
178
|
+
re-probe via `apt-tools merge detect` and resolve inter-PR conflicts here
|
|
179
|
+
before the next branch integrates.
|
|
180
|
+
- **`apt:watch-ci` `diverged-bailed`** — instead of bailing on a rebase
|
|
181
|
+
conflict, run this skill to self-heal, then push the resolved branch and let
|
|
182
|
+
the watcher reschedule.
|
|
183
|
+
|
|
184
|
+
</process>
|
|
185
|
+
|
|
186
|
+
<rationalization>
|
|
187
|
+
Common ways an agent rationalizes the wrong move — and why each is wrong:
|
|
188
|
+
|
|
189
|
+
- "I'll just `git checkout --theirs` / `--ours` to clear the conflict fast." →
|
|
190
|
+
That blindly discards one side's intent. The whole reason this is
|
|
191
|
+
agent-inline (not the deterministic core merger) is that you reconcile BOTH
|
|
192
|
+
intents with repository context. Side-picking is the last resort in §3.2,
|
|
193
|
+
not the default.
|
|
194
|
+
- "The probe said `mergeable: false`, I'll resolve from the `conflicts_preview`
|
|
195
|
+
list without reading the files." → The preview is only a path list. You MUST
|
|
196
|
+
materialize the conflict (§2) and read the actual markers (§3) before
|
|
197
|
+
editing — guessing from filenames produces wrong merges.
|
|
198
|
+
- "I resolved the markers, I can skip the re-probe and push." → Without §5 you
|
|
199
|
+
can ship stray markers or an incomplete rebase. Always re-probe + grep for
|
|
200
|
+
stray markers before handing back.
|
|
201
|
+
- "It's still conflicting after a few tries, I'll force it through." → A
|
|
202
|
+
non-converging conflict is a human decision. After 3 rounds, abort to a clean
|
|
203
|
+
tree and hand back — never leave a half-rebased worktree or force a bad
|
|
204
|
+
resolution into a PR.
|
|
205
|
+
- "I'll just merge the PR since I resolved it." → You are not the final gate.
|
|
206
|
+
Resolved branches flow through PR → review → CI. Never auto-merge.
|
|
207
|
+
</rationalization>
|
|
208
|
+
|
|
209
|
+
<autonomy_interaction>
|
|
210
|
+
Read the active task's autonomy level from `.aperant/state.json`
|
|
211
|
+
(`active_tasks[{task-id}].autonomy`, falling back to `autonomy.default`):
|
|
212
|
+
|
|
213
|
+
- **Autonomy 0–1 (plan-only / step)** — after §1 detects conflicts, print the
|
|
214
|
+
`conflicts_preview` and ask `Resolve these N conflicts inline? [Y/n]` before
|
|
215
|
+
driving the rebase in §2. On `N`, exit so the human can resolve manually.
|
|
216
|
+
After resolving each file in §3, show the diff and confirm before staging at
|
|
217
|
+
higher-stakes files (anything under a path the task did not touch).
|
|
218
|
+
- **Autonomy 2 (auto-chain)** — resolve silently through §2–§5; STOP per §5 on
|
|
219
|
+
non-convergence after 3 rounds.
|
|
220
|
+
- **Autonomy 3 (YOLO)** — same as 2: silent on convergence, STOP on
|
|
221
|
+
non-convergence. Even at YOLO you do NOT auto-merge the resulting PR — the
|
|
222
|
+
human/CI gate is non-negotiable.
|
|
223
|
+
|
|
224
|
+
This skill is recoverable by construction: every STOP path (§1 unprobeable,
|
|
225
|
+
§5 non-convergence) aborts the in-progress git operation to leave a clean tree
|
|
226
|
+
before handing back, so a bailout never strands a half-rebased worktree.
|
|
227
|
+
</autonomy_interaction>
|
|
@@ -26,7 +26,7 @@ Every finding produced by a review agent MUST conform to this schema:
|
|
|
26
26
|
{
|
|
27
27
|
"id": "SEC-001",
|
|
28
28
|
"severity": "critical|high|medium|low",
|
|
29
|
-
"category": "security|quality|logic|codebase-fit|test-integrity|performance|auto-checks",
|
|
29
|
+
"category": "security|quality|logic|codebase-fit|test-integrity|performance|i18n|cross-platform|auto-checks",
|
|
30
30
|
"title": "Short description (max 80 chars)",
|
|
31
31
|
"description": "Detailed explanation of the issue",
|
|
32
32
|
"impact": "Real-world consequences if not fixed",
|
|
@@ -62,7 +62,7 @@ Every finding MUST include all of the following. A finding missing any required
|
|
|
62
62
|
|-------|------|-------------|
|
|
63
63
|
| `id` | `string` | Unique identifier. Format: `{CATEGORY_PREFIX}-{NNN}`. See [ID Prefixes](#id-prefixes) below. |
|
|
64
64
|
| `severity` | `enum` | One of: `critical`, `high`, `medium`, `low`. See [Severity Definitions](#severity-definitions). |
|
|
65
|
-
| `category` | `enum` | One of: `security`, `quality`, `logic`, `codebase-fit`, `test-integrity`, `performance`, `auto-checks`. |
|
|
65
|
+
| `category` | `enum` | One of: `security`, `quality`, `logic`, `codebase-fit`, `test-integrity`, `performance`, `i18n`, `cross-platform`, `auto-checks`. The `i18n` and `cross-platform` categories are emitted only by the two CONDITIONAL specialists, which the orchestrator spawns when `detect-specialists` engages them. |
|
|
66
66
|
| `title` | `string` | Short description, max 80 characters. Must be specific enough to act on without reading the full description. |
|
|
67
67
|
| `description` | `string` | Detailed explanation of what the issue is and why it matters. No filler -- every sentence must add information. |
|
|
68
68
|
| `impact` | `string` | Real-world consequences if this issue is not fixed. Must describe a concrete scenario, not a vague risk statement. |
|
|
@@ -100,6 +100,8 @@ Each category has a standard prefix for finding IDs:
|
|
|
100
100
|
| `codebase-fit` | `FIT` | `FIT-001` |
|
|
101
101
|
| `test-integrity` | `TST` | `TST-001` |
|
|
102
102
|
| `performance` | `PRF` | `PRF-001` |
|
|
103
|
+
| `i18n` | `I18N` | `I18N-001` |
|
|
104
|
+
| `cross-platform` | `XPL` | `XPL-001` |
|
|
103
105
|
| `auto-checks` | `CHK` | `CHK-001` |
|
|
104
106
|
|
|
105
107
|
Each agent numbers its findings sequentially starting from 001.
|