@aperant/framework 0.17.0 → 0.20.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 +261 -0
- package/agents/apt-executor.md +6 -0
- package/agents/apt-planner.md +3 -2
- package/bin/features-reconcile-check.mjs +77 -0
- package/dist/cli/artifacts/self-stage.d.mts.map +1 -1
- package/dist/cli/artifacts/self-stage.mjs +2 -1
- package/dist/cli/artifacts/self-stage.mjs.map +1 -1
- package/dist/cli/cli-wrappers/features-reconcile.d.mts +2 -0
- package/dist/cli/cli-wrappers/features-reconcile.d.mts.map +1 -0
- package/dist/cli/cli-wrappers/features-reconcile.mjs +9 -0
- package/dist/cli/cli-wrappers/features-reconcile.mjs.map +1 -0
- package/dist/cli/commands/ci-watch.mjs +49 -2
- package/dist/cli/commands/ci-watch.mjs.map +1 -1
- package/dist/cli/commands/commit.mjs +3 -3
- package/dist/cli/commands/commit.mjs.map +1 -1
- package/dist/cli/commands/event.mjs +16 -16
- package/dist/cli/commands/event.mjs.map +1 -1
- package/dist/cli/commands/features-reconcile.d.mts +2 -0
- package/dist/cli/commands/features-reconcile.d.mts.map +1 -0
- package/dist/cli/commands/features-reconcile.mjs +143 -0
- package/dist/cli/commands/features-reconcile.mjs.map +1 -0
- package/dist/cli/commands/health-check.d.mts +2 -2
- package/dist/cli/commands/health-check.mjs +2 -2
- package/dist/cli/commands/merge-integrate.mjs +3 -3
- package/dist/cli/commands/merge-integrate.mjs.map +1 -1
- package/dist/cli/commands/produce.d.mts +9 -0
- package/dist/cli/commands/produce.d.mts.map +1 -0
- package/dist/cli/commands/produce.mjs +1345 -0
- package/dist/cli/commands/produce.mjs.map +1 -0
- package/dist/cli/commands/task.d.mts +16 -1
- package/dist/cli/commands/task.d.mts.map +1 -1
- package/dist/cli/commands/task.mjs +434 -266
- package/dist/cli/commands/task.mjs.map +1 -1
- package/dist/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
- package/dist/cli/coordination/auto-emit-artifact-ready.mjs.map +1 -1
- package/dist/cli/coordination/event-schema.d.mts +5 -3
- package/dist/cli/coordination/event-schema.d.mts.map +1 -1
- package/dist/cli/coordination/event-schema.mjs +245 -21
- package/dist/cli/coordination/event-schema.mjs.map +1 -1
- package/dist/cli/coordination/store.d.mts +2 -2
- package/dist/cli/coordination/store.mjs +4 -4
- package/dist/cli/coordination/store.mjs.map +1 -1
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +4 -2
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/features/area-shape.d.mts +11 -0
- package/dist/cli/features/area-shape.d.mts.map +1 -0
- package/dist/cli/features/area-shape.mjs +141 -0
- package/dist/cli/features/area-shape.mjs.map +1 -0
- package/dist/cli/features/delta.d.mts +78 -0
- package/dist/cli/features/delta.d.mts.map +1 -0
- package/dist/cli/features/delta.mjs +591 -0
- package/dist/cli/features/delta.mjs.map +1 -0
- package/dist/cli/features/reconcile.d.mts +236 -0
- package/dist/cli/features/reconcile.d.mts.map +1 -0
- package/dist/cli/features/reconcile.mjs +1126 -0
- package/dist/cli/features/reconcile.mjs.map +1 -0
- package/dist/cli/features/surface-map.d.mts +63 -0
- package/dist/cli/features/surface-map.d.mts.map +1 -0
- package/dist/cli/features/surface-map.mjs +191 -0
- package/dist/cli/features/surface-map.mjs.map +1 -0
- package/dist/cli/{commands/features-audit.d.mts → features/write-root.d.mts} +4 -3
- package/dist/cli/features/write-root.d.mts.map +1 -0
- package/dist/cli/features/write-root.mjs +137 -0
- package/dist/cli/features/write-root.mjs.map +1 -0
- package/dist/cli/gate/gates/features-reconciled.d.mts +36 -0
- package/dist/cli/gate/gates/features-reconciled.d.mts.map +1 -0
- package/dist/cli/gate/gates/features-reconciled.mjs +67 -0
- package/dist/cli/gate/gates/features-reconciled.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/help.d.mts.map +1 -1
- package/dist/cli/help.mjs +31 -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 +2 -0
- package/dist/cli/install/legacy-paths.mjs.map +1 -1
- package/dist/cli/produce/blind-probe.d.mts +85 -0
- package/dist/cli/produce/blind-probe.d.mts.map +1 -0
- package/dist/cli/produce/blind-probe.mjs +217 -0
- package/dist/cli/produce/blind-probe.mjs.map +1 -0
- package/dist/cli/produce/claim.d.mts +188 -0
- package/dist/cli/produce/claim.d.mts.map +1 -0
- package/dist/cli/produce/claim.mjs +518 -0
- package/dist/cli/produce/claim.mjs.map +1 -0
- package/dist/cli/produce/done-gate.d.mts +87 -0
- package/dist/cli/produce/done-gate.d.mts.map +1 -0
- package/dist/cli/produce/done-gate.mjs +200 -0
- package/dist/cli/produce/done-gate.mjs.map +1 -0
- package/dist/cli/produce/events.d.mts +77 -0
- package/dist/cli/produce/events.d.mts.map +1 -0
- package/dist/cli/produce/events.mjs +126 -0
- package/dist/cli/produce/events.mjs.map +1 -0
- package/dist/cli/produce/evidence-oracle.d.mts +63 -0
- package/dist/cli/produce/evidence-oracle.d.mts.map +1 -0
- package/dist/cli/produce/evidence-oracle.mjs +122 -0
- package/dist/cli/produce/evidence-oracle.mjs.map +1 -0
- package/dist/cli/produce/ledger.d.mts +140 -0
- package/dist/cli/produce/ledger.d.mts.map +1 -0
- package/dist/cli/produce/ledger.mjs +272 -0
- package/dist/cli/produce/ledger.mjs.map +1 -0
- package/dist/cli/produce/probe-family.d.mts +53 -0
- package/dist/cli/produce/probe-family.d.mts.map +1 -0
- package/dist/cli/produce/probe-family.mjs +160 -0
- package/dist/cli/produce/probe-family.mjs.map +1 -0
- package/dist/cli/produce/projection.d.mts +55 -0
- package/dist/cli/produce/projection.d.mts.map +1 -0
- package/dist/cli/produce/projection.mjs +97 -0
- package/dist/cli/produce/projection.mjs.map +1 -0
- package/dist/cli/produce/run-id.d.mts +42 -0
- package/dist/cli/produce/run-id.d.mts.map +1 -0
- package/dist/cli/produce/run-id.mjs +79 -0
- package/dist/cli/produce/run-id.mjs.map +1 -0
- package/dist/cli/produce/saga.d.mts +180 -0
- package/dist/cli/produce/saga.d.mts.map +1 -0
- package/dist/cli/produce/saga.mjs +290 -0
- package/dist/cli/produce/saga.mjs.map +1 -0
- package/dist/cli/produce/scheduler.d.mts +165 -0
- package/dist/cli/produce/scheduler.d.mts.map +1 -0
- package/dist/cli/produce/scheduler.mjs +399 -0
- package/dist/cli/produce/scheduler.mjs.map +1 -0
- package/dist/cli/produce/setpoint.d.mts +52 -0
- package/dist/cli/produce/setpoint.d.mts.map +1 -0
- package/dist/cli/produce/setpoint.mjs +113 -0
- package/dist/cli/produce/setpoint.mjs.map +1 -0
- package/dist/cli/produce/verification-ttl.d.mts +75 -0
- package/dist/cli/produce/verification-ttl.d.mts.map +1 -0
- package/dist/cli/produce/verification-ttl.mjs +169 -0
- package/dist/cli/produce/verification-ttl.mjs.map +1 -0
- package/dist/cli/release-notes/ship-autodraft.mjs +2 -2
- package/dist/cli/roadmap/{conductor-view.d.mts → showrunner-view.d.mts} +3 -3
- package/dist/cli/roadmap/showrunner-view.d.mts.map +1 -0
- package/dist/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +7 -7
- package/dist/cli/roadmap/showrunner-view.mjs.map +1 -0
- package/dist/cli/variant/gallery.d.mts.map +1 -1
- package/dist/cli/variant/gallery.mjs +7 -3
- package/dist/cli/variant/gallery.mjs.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +2 -1
- package/dist/plugin/agents/apt-executor.md +6 -0
- package/dist/plugin/agents/apt-planner.md +3 -2
- package/dist/plugin/skills/apt/SKILL.md +112 -38
- package/dist/plugin/skills/apt-close-task/SKILL.md +2 -2
- package/dist/plugin/skills/apt-debug/SKILL.md +22 -24
- package/dist/plugin/skills/apt-execute/SKILL.md +35 -26
- package/dist/plugin/skills/apt-fan-out/SKILL.md +4 -4
- package/dist/plugin/skills/apt-handoff/SKILL.md +1 -1
- package/dist/plugin/skills/apt-mockup/SKILL.md +1 -1
- package/dist/plugin/skills/apt-plan/SKILL.md +5 -5
- package/dist/plugin/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
- package/dist/plugin/skills/apt-produce/SKILL.md +606 -0
- package/dist/plugin/skills/apt-quick/SKILL.md +27 -33
- package/dist/plugin/skills/apt-review/SKILL.md +14 -11
- package/dist/plugin/skills/apt-run/SKILL.md +132 -3
- package/dist/plugin/skills/apt-ship/SKILL.md +29 -5
- package/dist/plugin/skills/apt-spar/SKILL.md +5 -3
- package/dist/plugin/skills/apt-watch-ci/SKILL.md +6 -4
- package/dist/schemas/feature-registry.d.ts.map +1 -1
- package/dist/schemas/feature-registry.js +34 -3
- package/dist/schemas/feature-registry.js.map +1 -1
- package/dist/types/config.d.ts +8 -5
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +4 -4
- package/prompts/{conductor-framework-context.md → showrunner-framework-context.md} +1 -1
- package/prompts/{conductor-status-check.md → showrunner-status-check.md} +1 -1
- package/prompts/{conductor-sub-agent.md → showrunner-sub-agent.md} +6 -6
- package/prompts/{conductor-system.md → showrunner-system.md} +8 -8
- package/skills/apt/SKILL.md +112 -38
- package/skills/apt-close-task/SKILL.md +2 -2
- package/skills/apt-debug/SKILL.md +22 -24
- package/skills/apt-execute/SKILL.md +35 -26
- package/skills/apt-fan-out/SKILL.md +4 -4
- package/skills/apt-handoff/SKILL.md +1 -1
- package/skills/apt-mockup/SKILL.md +1 -1
- package/skills/apt-plan/SKILL.md +5 -5
- package/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
- package/skills/apt-produce/SKILL.md +606 -0
- package/skills/apt-quick/SKILL.md +27 -33
- package/skills/apt-review/SKILL.md +14 -11
- package/skills/apt-run/SKILL.md +132 -3
- package/skills/apt-ship/SKILL.md +29 -5
- package/skills/apt-spar/SKILL.md +5 -3
- package/skills/apt-watch-ci/SKILL.md +6 -4
- package/src/cli/artifacts/self-stage.mjs +2 -1
- package/src/cli/cli-wrappers/features-reconcile.mjs +9 -0
- package/src/cli/commands/ci-watch.mjs +51 -2
- package/src/cli/commands/commit.mjs +3 -3
- package/src/cli/commands/event.mjs +16 -16
- package/src/cli/commands/features-reconcile.mjs +157 -0
- package/src/cli/commands/health-check.mjs +2 -2
- package/src/cli/commands/merge-integrate.mjs +3 -3
- package/src/cli/commands/produce.mjs +1466 -0
- package/src/cli/commands/task.mjs +482 -285
- package/src/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
- package/src/cli/coordination/event-schema.d.ts +4 -2
- package/src/cli/coordination/event-schema.mjs +276 -21
- package/src/cli/coordination/store.mjs +4 -4
- package/src/cli/dispatch.mjs +4 -2
- package/src/cli/features/area-shape.mjs +140 -0
- package/src/cli/features/delta.mjs +625 -0
- package/src/cli/features/reconcile.mjs +1169 -0
- package/src/cli/features/surface-map.mjs +192 -0
- package/src/cli/features/write-root.mjs +140 -0
- package/src/cli/gate/gates/features-reconciled.mjs +70 -0
- package/src/cli/gate/gates/index.mjs +1 -0
- package/src/cli/help.mjs +31 -1
- package/src/cli/install/legacy-paths.mjs +2 -0
- package/src/cli/produce/blind-probe.mjs +245 -0
- package/src/cli/produce/claim.mjs +543 -0
- package/src/cli/produce/done-gate.mjs +238 -0
- package/src/cli/produce/events.mjs +131 -0
- package/src/cli/produce/evidence-oracle.mjs +133 -0
- package/src/cli/produce/ledger.mjs +284 -0
- package/src/cli/produce/probe-family.mjs +168 -0
- package/src/cli/produce/projection.mjs +105 -0
- package/src/cli/produce/run-id.mjs +84 -0
- package/src/cli/produce/saga.mjs +303 -0
- package/src/cli/produce/scheduler.mjs +423 -0
- package/src/cli/produce/setpoint.mjs +122 -0
- package/src/cli/produce/verification-ttl.mjs +191 -0
- package/src/cli/release-notes/ship-autodraft.mjs +2 -2
- package/src/cli/roadmap/showrunner-view.d.ts +10 -0
- package/src/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +6 -6
- package/src/cli/variant/gallery.mjs +7 -3
- package/templates/aperant-claude-md-appendix.md +2 -2
- package/workflows/scan-features.md +17 -11
- package/dist/cli/cli-wrappers/features-audit.d.mts +0 -2
- package/dist/cli/cli-wrappers/features-audit.d.mts.map +0 -1
- package/dist/cli/cli-wrappers/features-audit.mjs +0 -8
- package/dist/cli/cli-wrappers/features-audit.mjs.map +0 -1
- package/dist/cli/commands/features-audit.d.mts.map +0 -1
- package/dist/cli/commands/features-audit.mjs +0 -293
- package/dist/cli/commands/features-audit.mjs.map +0 -1
- package/dist/cli/features/registry-audit.d.mts +0 -56
- package/dist/cli/features/registry-audit.d.mts.map +0 -1
- package/dist/cli/features/registry-audit.mjs +0 -264
- package/dist/cli/features/registry-audit.mjs.map +0 -1
- package/dist/cli/roadmap/conductor-view.d.mts.map +0 -1
- package/dist/cli/roadmap/conductor-view.mjs.map +0 -1
- package/src/cli/cli-wrappers/features-audit.mjs +0 -8
- package/src/cli/commands/features-audit.mjs +0 -302
- package/src/cli/features/registry-audit.mjs +0 -254
- package/src/cli/roadmap/conductor-view.d.ts +0 -10
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* surface-map.mjs — feature-surface qualification + area suggestion for the
|
|
3
|
+
* features-reconcile delta ledger (replaces registry-audit.mjs's
|
|
4
|
+
* `detectFeatureKind`/`inferArea`).
|
|
5
|
+
*
|
|
6
|
+
* Dependency-free: node:-builtins only (in fact, zero imports) — this module
|
|
7
|
+
* sits inside the bare-node CI entry's import closure (ID-08).
|
|
8
|
+
*
|
|
9
|
+
* THE MAPPING TABLE (ID-06) — this header IS the contract:
|
|
10
|
+
*
|
|
11
|
+
* | Path family | kind | area resolution |
|
|
12
|
+
* |--------------------------------------------------------------------|------------------|----------------------------------------------------|
|
|
13
|
+
* | packages/ui/src/sections/<a>/** /*.tsx | ui-component | alias(<a>) — deterministic |
|
|
14
|
+
* | packages/ui/src/{components,design-system}/** /*.tsx | ui-component | 'chrome' (existing precedent in chrome.json) |
|
|
15
|
+
* | packages/ui/src/hooks/use-*.ts{,x} | hook | token-match against known areas, else null |
|
|
16
|
+
* | apps/desktop/src/renderer/src/** /*.tsx | ui-component | token-match (features/settings/** → settings), else null |
|
|
17
|
+
* | apps/web/src/app/<seg>/** | ui-component | alias(<seg>) when it names a known area, else null |
|
|
18
|
+
* | apps/desktop/src/main/ipc-handlers/*.ts | ipc-channel | token-match (inbox-*-handlers → inbox), else null |
|
|
19
|
+
* | packages/backend/convex/** /*.ts | backend-function | token-match, else null |
|
|
20
|
+
* | packages/core/src/{services,pipeline,workflows,providers,inbox,terminal,credentials}/** /*.ts | core-service | first path segment / token-match, else null |
|
|
21
|
+
*
|
|
22
|
+
* Exclusions (always, checked BEFORE any family match): test files
|
|
23
|
+
* (`\.(test|spec)\.`), `__tests__/` dirs, `.stories.` files, `_generated/`
|
|
24
|
+
* dirs.
|
|
25
|
+
*
|
|
26
|
+
* Area rules:
|
|
27
|
+
* - AREA_ALIASES = { terminal: 'terminals', conductor: 'showrunner' } is
|
|
28
|
+
* applied at EVERY resolution site.
|
|
29
|
+
* - `hooks` / `ipc` / `services` are NEVER emitted as areas (the junk-drawer
|
|
30
|
+
* failure mode is structurally impossible) — they are filtered even if a
|
|
31
|
+
* caller supplies them in `knownAreas`.
|
|
32
|
+
* - `area: null` means "the writer agent must assign it" — `--check` and
|
|
33
|
+
* `--write` fail closed on a null area.
|
|
34
|
+
* - Token-match compares path tokens (split on `/ - _ .`) against the
|
|
35
|
+
* caller-supplied `knownAreas` list, alias-normalized, with a simple
|
|
36
|
+
* plural tolerance (`competitor` matches `competitors`).
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
export const AREA_ALIASES = Object.freeze({
|
|
40
|
+
terminal: 'terminals',
|
|
41
|
+
conductor: 'showrunner',
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/** Pseudo-areas that must never be suggested (retired junk drawers). */
|
|
45
|
+
const JUNK_AREAS = new Set(['hooks', 'ipc', 'services'])
|
|
46
|
+
|
|
47
|
+
/** Apply the alias map to a raw area token. */
|
|
48
|
+
export function applyAreaAlias(area) {
|
|
49
|
+
if (typeof area !== 'string') return area
|
|
50
|
+
return AREA_ALIASES[area] ?? area
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Always-on exclusions: test files, __tests__ dirs, storybook stories,
|
|
55
|
+
* generated code.
|
|
56
|
+
*/
|
|
57
|
+
export function isExcludedPath(norm) {
|
|
58
|
+
if (/\.(test|spec)\./.test(norm)) return true
|
|
59
|
+
if (/(^|\/)__tests__\//.test(norm)) return true
|
|
60
|
+
if (/\.stories\./.test(norm)) return true
|
|
61
|
+
if (/(^|\/)_generated\//.test(norm)) return true
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Normalize the caller-supplied known-area list: strings only, junk areas
|
|
67
|
+
* dropped (they are never legal suggestions even if a stale file exists).
|
|
68
|
+
*/
|
|
69
|
+
function normalizeKnownAreas(knownAreas) {
|
|
70
|
+
const out = new Set()
|
|
71
|
+
for (const a of knownAreas || []) {
|
|
72
|
+
if (typeof a !== 'string' || !a) continue
|
|
73
|
+
if (JUNK_AREAS.has(a)) continue
|
|
74
|
+
out.add(a)
|
|
75
|
+
}
|
|
76
|
+
return out
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Token-match a set of path tokens against the known-area set.
|
|
81
|
+
* A token `t` matches area `a` when alias(t) === a or alias(t) + 's' === a.
|
|
82
|
+
* Returns the first matching area (token order), or null.
|
|
83
|
+
*/
|
|
84
|
+
function tokenMatchArea(tokens, knownSet) {
|
|
85
|
+
for (const raw of tokens) {
|
|
86
|
+
if (!raw) continue
|
|
87
|
+
const t = applyAreaAlias(raw.toLowerCase())
|
|
88
|
+
if (JUNK_AREAS.has(t)) continue
|
|
89
|
+
if (knownSet.has(t)) return t
|
|
90
|
+
if (knownSet.has(`${t}s`)) return `${t}s`
|
|
91
|
+
}
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Split a path fragment into candidate tokens: segments, kebab/underscore/dot
|
|
97
|
+
* parts, and camelCase words (convex files are camelCase — roadmapItems.ts).
|
|
98
|
+
*/
|
|
99
|
+
function tokenize(fragment) {
|
|
100
|
+
const tokens = []
|
|
101
|
+
for (const seg of fragment.split('/')) {
|
|
102
|
+
if (!seg) continue
|
|
103
|
+
tokens.push(seg)
|
|
104
|
+
for (const part of seg.split(/[-_.]/)) {
|
|
105
|
+
if (!part) continue
|
|
106
|
+
if (part !== seg) tokens.push(part)
|
|
107
|
+
for (const camel of part.split(/(?=[A-Z])/)) {
|
|
108
|
+
if (camel && camel !== part) tokens.push(camel)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return tokens
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Qualify a repo-relative file path as a feature surface.
|
|
117
|
+
*
|
|
118
|
+
* @param {string} filepath — repo-relative path (posix or win separators)
|
|
119
|
+
* @param {string[]} [knownAreas] — existing registry area names (callers pass
|
|
120
|
+
* the `.aperant/features/*.json` basenames); used only for token-match
|
|
121
|
+
* families. Deterministic families (sections, design-system) need none.
|
|
122
|
+
* @returns {{ kind: string, suggestedArea: string | null } | null}
|
|
123
|
+
* null when the path does not qualify at all.
|
|
124
|
+
*/
|
|
125
|
+
export function qualifiesAsFeatureSurface(filepath, knownAreas = []) {
|
|
126
|
+
if (!filepath || typeof filepath !== 'string') return null
|
|
127
|
+
const norm = filepath.replace(/\\/g, '/').replace(/^\.\//, '')
|
|
128
|
+
if (isExcludedPath(norm)) return null
|
|
129
|
+
const knownSet = normalizeKnownAreas(knownAreas)
|
|
130
|
+
|
|
131
|
+
// packages/ui/src/sections/<a>/**/*.tsx → ui-component, alias(<a>) deterministic
|
|
132
|
+
let m = /^packages\/ui\/src\/sections\/([^/]+)\/.+\.tsx$/.exec(norm)
|
|
133
|
+
if (m) {
|
|
134
|
+
const area = applyAreaAlias(m[1])
|
|
135
|
+
return { kind: 'ui-component', suggestedArea: JUNK_AREAS.has(area) ? null : area }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// packages/ui/src/{components,design-system}/**/*.tsx → ui-component, chrome
|
|
139
|
+
if (/^packages\/ui\/src\/(components|design-system)\/.+\.tsx$/.test(norm)) {
|
|
140
|
+
return { kind: 'ui-component', suggestedArea: 'chrome' }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// packages/ui/src/hooks/use-*.ts{,x} → hook, token-match
|
|
144
|
+
m = /^packages\/ui\/src\/hooks\/(use-[A-Za-z0-9-]+)\.tsx?$/.exec(norm)
|
|
145
|
+
if (m) {
|
|
146
|
+
const tokens = tokenize(m[1]).filter((t) => t !== 'use')
|
|
147
|
+
return { kind: 'hook', suggestedArea: tokenMatchArea(tokens, knownSet) }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// apps/desktop/src/renderer/src/**/*.tsx → ui-component, token-match
|
|
151
|
+
m = /^apps\/desktop\/src\/renderer\/src\/(.+)\.tsx$/.exec(norm)
|
|
152
|
+
if (m) {
|
|
153
|
+
return { kind: 'ui-component', suggestedArea: tokenMatchArea(tokenize(m[1]), knownSet) }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// apps/web/src/app/<seg>/** → ui-component, alias(<seg>) when known
|
|
157
|
+
m = /^apps\/web\/src\/app\/([^/]+)\/.+\.tsx?$/.exec(norm)
|
|
158
|
+
if (m) {
|
|
159
|
+
const seg = applyAreaAlias(m[1])
|
|
160
|
+
const area = !JUNK_AREAS.has(seg) && knownSet.has(seg) ? seg : null
|
|
161
|
+
return { kind: 'ui-component', suggestedArea: area }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// apps/desktop/src/main/ipc-handlers/*.ts → ipc-channel, token-match
|
|
165
|
+
m = /^apps\/desktop\/src\/main\/ipc-handlers\/([^/]+)\.ts$/.exec(norm)
|
|
166
|
+
if (m) {
|
|
167
|
+
return { kind: 'ipc-channel', suggestedArea: tokenMatchArea(tokenize(m[1]), knownSet) }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// packages/backend/convex/**/*.ts → backend-function, token-match
|
|
171
|
+
m = /^packages\/backend\/convex\/(.+)\.ts$/.exec(norm)
|
|
172
|
+
if (m) {
|
|
173
|
+
return { kind: 'backend-function', suggestedArea: tokenMatchArea(tokenize(m[1]), knownSet) }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// packages/core/src/{services,pipeline,workflows,providers,inbox,terminal,credentials}/**/*.ts
|
|
177
|
+
// → core-service; first path segment / token-match.
|
|
178
|
+
m =
|
|
179
|
+
/^packages\/core\/src\/(services|pipeline|workflows|providers|inbox|terminal|credentials)\/(.+)\.ts$/.exec(
|
|
180
|
+
norm,
|
|
181
|
+
)
|
|
182
|
+
if (m) {
|
|
183
|
+
// The family segment itself is the first candidate token (pipeline →
|
|
184
|
+
// pipeline, terminal → terminals via alias, inbox → inbox); junk
|
|
185
|
+
// segments (services) and non-area segments (workflows, providers,
|
|
186
|
+
// credentials) fall through to token-match on the rest of the path.
|
|
187
|
+
const tokens = [m[1], ...tokenize(m[2])]
|
|
188
|
+
return { kind: 'core-service', suggestedArea: tokenMatchArea(tokens, knownSet) }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* write-root.mjs — BUG-025b worktree write-redirect resolution (ID-11).
|
|
3
|
+
*
|
|
4
|
+
* Moved verbatim from the retired legacy audit command so the mutating
|
|
5
|
+
* features-reconcile verbs (--draft / --write) keep the exact 4-layer
|
|
6
|
+
* behaviour: registry writes historically leaked generated `*.json` into main
|
|
7
|
+
* when the executor invoked the CLI from a worktree-isolated task. The fix
|
|
8
|
+
* applies a 4-step resolution order (see resolveWriteRoot below):
|
|
9
|
+
* (1) cwd IS a worktree root → write there (current behaviour)
|
|
10
|
+
* (2) --task-id maps to an active non-terminal worktree task →
|
|
11
|
+
* redirect writes to that worktree's `.aperant/features/`
|
|
12
|
+
* (3) multiple active worktree-isolated tasks + no --task-id → error
|
|
13
|
+
* (4) `--no-worktree-redirect` opt-out preserves layer (1) behaviour
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { spawnSync } from 'node:child_process'
|
|
17
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
18
|
+
import { join, resolve, sep } from 'node:path'
|
|
19
|
+
import { resolveMainRepoRoot } from '../util/state-paths.mjs'
|
|
20
|
+
|
|
21
|
+
const TERMINAL_PHASES = new Set(['completed', 'cancelled', 'closed', 'shipped', 'merged'])
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* BUG-025b — resolve where the mutating features-reconcile verbs should write.
|
|
25
|
+
* See module header for the 4-step order. Returns `{ writeRoot, redirect_notice }`
|
|
26
|
+
* on success or a synthetic error envelope (`{ error: { ... } }`) on the
|
|
27
|
+
* ambiguous-worktree case.
|
|
28
|
+
*
|
|
29
|
+
* @internal — exported for unit testing.
|
|
30
|
+
*
|
|
31
|
+
* @param {{projectDir: string, taskId?: string | null, noRedirect?: boolean}} args
|
|
32
|
+
* @returns {{writeRoot?: string, redirect_notice?: string | null, error?: {kind: string, message: string, active_worktree_tasks: string[]}}}
|
|
33
|
+
*/
|
|
34
|
+
export function resolveWriteRoot({ projectDir, taskId, noRedirect }) {
|
|
35
|
+
const root = resolve(projectDir)
|
|
36
|
+
if (noRedirect === true) {
|
|
37
|
+
return { writeRoot: root, redirect_notice: null }
|
|
38
|
+
}
|
|
39
|
+
// Layer (1): cwd IS a worktree root. Worktrees have `git rev-parse
|
|
40
|
+
// --git-dir` != `git rev-parse --git-common-dir`. Falling through to
|
|
41
|
+
// the existing behaviour writes to this worktree's .aperant/features.
|
|
42
|
+
// Fast path: git sets GIT_DIR and GIT_COMMON_DIR in the environment
|
|
43
|
+
// when running inside a worktree hook context — if both are set and
|
|
44
|
+
// differ, we are in a worktree. This env-var check is ONLY valid at
|
|
45
|
+
// Layer (1) where we ask "is this process in a worktree?"; it must
|
|
46
|
+
// NOT be used to validate an untrusted external path (Layer 2 below).
|
|
47
|
+
const envDir = process.env.GIT_DIR
|
|
48
|
+
const envCommonDir = process.env.GIT_COMMON_DIR
|
|
49
|
+
if (envDir && envCommonDir) {
|
|
50
|
+
if (resolve(envDir) !== resolve(envCommonDir)) {
|
|
51
|
+
return { writeRoot: root, redirect_notice: null }
|
|
52
|
+
}
|
|
53
|
+
} else if (isWorktreeRoot(root)) {
|
|
54
|
+
return { writeRoot: root, redirect_notice: null }
|
|
55
|
+
}
|
|
56
|
+
// Layer (2): --task-id maps to an active non-terminal worktree-isolated
|
|
57
|
+
// task → redirect.
|
|
58
|
+
const stateActive = readActiveTasks(root)
|
|
59
|
+
if (taskId) {
|
|
60
|
+
const task = stateActive[taskId]
|
|
61
|
+
if (
|
|
62
|
+
task &&
|
|
63
|
+
!TERMINAL_PHASES.has(task.lifecycle_phase) &&
|
|
64
|
+
typeof task.worktree_path === 'string' &&
|
|
65
|
+
existsSync(task.worktree_path)
|
|
66
|
+
) {
|
|
67
|
+
const resolvedWorktree = resolve(task.worktree_path)
|
|
68
|
+
const worktreesDir = resolve(root, '.aperant', 'worktrees') + sep
|
|
69
|
+
const isUnderProject = resolvedWorktree.startsWith(root + sep) || resolvedWorktree === root
|
|
70
|
+
const isUnderWorktrees = resolvedWorktree.startsWith(worktreesDir)
|
|
71
|
+
// Accept only paths under the project root, under .aperant/worktrees/,
|
|
72
|
+
// or confirmed as a real git worktree by git itself. Rejects arbitrary
|
|
73
|
+
// paths injected via a tampered state.json while preserving test
|
|
74
|
+
// fixtures that use ad-hoc worktree paths outside the project tree.
|
|
75
|
+
if (!isUnderProject && !isUnderWorktrees && !isWorktreeRoot(resolvedWorktree)) {
|
|
76
|
+
return { writeRoot: root, redirect_notice: null }
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
writeRoot: resolvedWorktree,
|
|
80
|
+
redirect_notice: `features-reconcile redirected writes to worktree ${resolvedWorktree} (--task-id ${taskId})`,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Layer (3): multiple active worktree-isolated tasks AND no --task-id
|
|
85
|
+
// → actionable error. Single-worktree case is OK to default to root
|
|
86
|
+
// (the executor will set up the worktree's registry on first scan).
|
|
87
|
+
const activeWorktreeTasks = Object.entries(stateActive).filter(([_id, t]) => {
|
|
88
|
+
return (
|
|
89
|
+
t &&
|
|
90
|
+
!TERMINAL_PHASES.has(t.lifecycle_phase) &&
|
|
91
|
+
typeof t.worktree_path === 'string' &&
|
|
92
|
+
existsSync(t.worktree_path)
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
if (!taskId && activeWorktreeTasks.length > 1) {
|
|
96
|
+
return {
|
|
97
|
+
error: {
|
|
98
|
+
kind: 'ambiguous-worktree-context',
|
|
99
|
+
message: 'ambiguous worktree context; pass --task-id <id> or --no-worktree-redirect',
|
|
100
|
+
active_worktree_tasks: activeWorktreeTasks.map(([id]) => id),
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Default: current behaviour (write into cwd).
|
|
105
|
+
return { writeRoot: root, redirect_notice: null }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function isWorktreeRoot(projectDir) {
|
|
109
|
+
try {
|
|
110
|
+
const dirRes = spawnSync('git', ['rev-parse', '--git-dir'], {
|
|
111
|
+
cwd: projectDir,
|
|
112
|
+
encoding: 'utf-8',
|
|
113
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
114
|
+
})
|
|
115
|
+
const commonRes = spawnSync('git', ['rev-parse', '--git-common-dir'], {
|
|
116
|
+
cwd: projectDir,
|
|
117
|
+
encoding: 'utf-8',
|
|
118
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
119
|
+
})
|
|
120
|
+
if (dirRes.status !== 0 || commonRes.status !== 0) return false
|
|
121
|
+
return (dirRes.stdout || '').trim() !== (commonRes.stdout || '').trim()
|
|
122
|
+
} catch {
|
|
123
|
+
return false
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function readActiveTasks(projectDir) {
|
|
128
|
+
// state-fork-fix: read the AUTHORITATIVE main board — readActiveTasks finds
|
|
129
|
+
// each task's worktree_path for the BUG-025b feature-write redirect, so it
|
|
130
|
+
// MUST read main or the redirect-target lookup itself forks. (The feature
|
|
131
|
+
// WRITE path / resolveWriteRoot stays untouched.)
|
|
132
|
+
const statePath = join(resolveMainRepoRoot(projectDir), '.aperant', 'state.json')
|
|
133
|
+
if (!existsSync(statePath)) return {}
|
|
134
|
+
try {
|
|
135
|
+
const parsed = JSON.parse(readFileSync(statePath, 'utf-8'))
|
|
136
|
+
return parsed?.active_tasks ?? {}
|
|
137
|
+
} catch {
|
|
138
|
+
return {}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
* features-reconciled — blocking gate for `apt:ship` (ID-10).
|
|
4
|
+
*
|
|
5
|
+
* Runs the features-reconcile 4-clause checker IN-PROCESS (no shell-out)
|
|
6
|
+
* against the task's worktree (when present), verifying the write-then-verify
|
|
7
|
+
* ordering: apt-ship §0.0 materializes pending deltas BEFORE the §0 gate
|
|
8
|
+
* check reaches this gate.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately has NO applies_to_profile — unlike verify-approved /
|
|
11
|
+
* review-clean, this gate runs under BOTH profiles so QUICK/DEBUG-shipped PRs
|
|
12
|
+
* are covered too (per gate/registry.mjs, omitting applies_to_profile means
|
|
13
|
+
* the gate applies universally).
|
|
14
|
+
*
|
|
15
|
+
* Deliberately returns NO inputs[] — the deltas/receipts/area dir contents
|
|
16
|
+
* are dynamic and the check is cheap, so the verdict is never cached.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync } from 'node:fs'
|
|
20
|
+
import { checkReconciled, draftRepairCommand } from '../../features/reconcile.mjs'
|
|
21
|
+
import { registerGate } from '../registry.mjs'
|
|
22
|
+
|
|
23
|
+
const gate = {
|
|
24
|
+
id: 'features-reconciled',
|
|
25
|
+
description: 'feature-registry deltas reconciled (coverage + schema + integrity + no orphans)',
|
|
26
|
+
severity: 'blocking',
|
|
27
|
+
skills: ['ship'],
|
|
28
|
+
async check(ctx) {
|
|
29
|
+
const worktree = ctx.taskRecord?.worktree_path
|
|
30
|
+
const checkRoot =
|
|
31
|
+
typeof worktree === 'string' && worktree && existsSync(worktree) ? worktree : ctx.sharedRoot
|
|
32
|
+
const base = ctx.taskRecord?.base_branch ?? 'main'
|
|
33
|
+
let result
|
|
34
|
+
try {
|
|
35
|
+
result = checkReconciled({ root: checkRoot, base, taskId: ctx.taskId })
|
|
36
|
+
} catch (e) {
|
|
37
|
+
return {
|
|
38
|
+
status: 'fail',
|
|
39
|
+
reason: `features-reconcile check threw: ${e.message}`,
|
|
40
|
+
evidence: { checkRoot, base },
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (result.status === 'skip') {
|
|
44
|
+
return {
|
|
45
|
+
status: 'skip',
|
|
46
|
+
reason: `features-reconcile skipped: ${result.reason} (docs_policy opt-out)`,
|
|
47
|
+
evidence: { checkRoot, policy: result.policy ?? null },
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (result.status === 'ok') {
|
|
51
|
+
return {
|
|
52
|
+
status: 'pass',
|
|
53
|
+
reason: `feature registry reconciled (${result.qualifying_files?.length ?? 0} qualifying file(s) covered; schema valid)`,
|
|
54
|
+
evidence: { checkRoot, base: result.base },
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const findings = result.findings ?? []
|
|
58
|
+
return {
|
|
59
|
+
status: 'fail',
|
|
60
|
+
reason:
|
|
61
|
+
`feature registry NOT reconciled — ${findings.length} finding(s). ` +
|
|
62
|
+
`Repair: ${draftRepairCommand(ctx.taskId)} (fill dispositions), then ` +
|
|
63
|
+
`node packages/framework/bin/apt-tools.mjs features-reconcile . --write --task-id ${ctx.taskId}`,
|
|
64
|
+
evidence: { checkRoot, base: result.base, findings },
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
registerGate(gate)
|
|
70
|
+
export default gate
|
|
@@ -18,6 +18,7 @@ import './verify-approved.mjs'
|
|
|
18
18
|
import './review-clean.mjs'
|
|
19
19
|
import './followups-adjudicated.mjs'
|
|
20
20
|
import './gitignore-in-sync.mjs'
|
|
21
|
+
import './features-reconciled.mjs'
|
|
21
22
|
import './consistency-check.mjs'
|
|
22
23
|
import './tdd-iron-law.mjs'
|
|
23
24
|
import './r-persona-alignment.mjs'
|
package/src/cli/help.mjs
CHANGED
|
@@ -29,6 +29,7 @@ Commands:
|
|
|
29
29
|
risk-check <dir> --paths <p1> [--operation <type>] Classify file paths by risk level
|
|
30
30
|
|
|
31
31
|
task create <dir> --description <text> [--scope <s>] [--milestone <id>] [--phase <id>] [--roadmap-ref-url <url>] [--depends-on <refs>] [--track <T>] [--autonomy <N>] Create isolated task
|
|
32
|
+
task ensure <dir> --description <text> --intent <create-new|require-existing|self-managed|none> [--track <T>] [--autonomy <N>] [--scope <s>] [--id <id>] Deterministic idempotent allocator: create-new provisions a per-task worktree (idempotent on re-run), require-existing resolves the active task without minting, none/self-managed allocate nothing
|
|
32
33
|
task list <dir> [--scope <s>] [--blocked-by <id>] List active tasks (optionally filtered)
|
|
33
34
|
task get <dir> --id <task-id> Get task details and artifacts
|
|
34
35
|
task update <dir> --id <id> [--lifecycle-phase <p>] [--pr-url <url>] [--subtasks-total <N>] [--depends-on <refs>] Update non-linkage task state; --pr-url may arm ci-watch per ci_watch.after_ship
|
|
@@ -37,7 +38,7 @@ Commands:
|
|
|
37
38
|
task close-merged <dir> [--task <id>] [--narrate-only] [--dry-run] Sweep shipped-pending-merge tasks for confirmed merges (--narrate-only returns pending_narration[] entries read-only without closing).
|
|
38
39
|
task narration-drain <dir> --task <task-id> Remove a row from state.pending_narration[] under withFileLock (called by /apt:close-task --narrate-only after a successful narrator spawn)
|
|
39
40
|
task complete-subtask <dir> --task-id <id> --subtask-id <sid|a+b+c> [--commit <sha>] [--notes <text>] Atomic plan ↔ build-progress sync
|
|
40
|
-
features-
|
|
41
|
+
features-reconcile <dir> (--draft|--write|--check|--gc) [--task-id <id>] [--subtask-id <id>] [--base <ref>] [--dry-run] [--warn-only] [--max-age-days <n>] [--no-worktree-redirect] Feature-registry delta ledger: --draft scaffolds a per-writer delta from the base..HEAD diff, --write materializes deltas into area JSONs + a receipt, --check verifies coverage/schema/integrity/orphans, --gc prunes merged receipts (BUG-025b: --task-id redirects writes to active task's worktree)
|
|
41
42
|
docs-audit <dir> [--diff-files <csv>|--since <ref>|--dry-run] [--policy <path>] [--format json|md] Audit documented surfaces for docs-drift
|
|
42
43
|
|
|
43
44
|
lock claim <dir> --subtask <id> Claim exclusive lock on a subtask
|
|
@@ -136,6 +137,35 @@ Commands:
|
|
|
136
137
|
ci-watch start|stop|status|tick <dir> [--pr <N>] [...] /apt:watch-ci primitives — see SKILL.md for full flag matrix
|
|
137
138
|
ci-watch sweep <dir> --orphaned [--dry-run] BUG-027: enumerate ci-watches state files, query gh per PR, remove terminal-state (MERGED|CLOSED) entries (manual GC; rate-limited)
|
|
138
139
|
|
|
140
|
+
produce start <dir> --outcome "<o>" [--run-id <id>] /apt:produce M1 — freeze an immutable acceptance setpoint under .aperant/produce/<run-id>/ (idempotent-reuse on --run-id, never overwrites); emits produce.run.started + produce.setpoint.frozen
|
|
141
|
+
produce add-milestone <dir> --run-id <id> --title "<t>" [--id <mid>] Append produce.roadmap.milestone_added + refresh the derived roadmap.json projection cache
|
|
142
|
+
produce set-milestone-status <dir> --run-id <id> --milestone <mid> --status <planned|in_progress|done> [--verdict-path <p>] Append produce.roadmap.milestone_status_changed; --status done is the M2 two-gate adjudication (visible oracle AND blind probe)
|
|
143
|
+
produce status <dir> --run-id <id> Re-project the roadmap from the append-only event log (no in-memory authority)
|
|
144
|
+
produce tick <dir> --run-id <id> Run exactly ONE wake tick — re-project from disk, emit produce.tick, compute next_action + next_tick_hint, then exit (no loop)
|
|
145
|
+
produce record-implementer <dir> --run-id <id> --milestone <mid> --implementer-family <fam> /apt:produce M2 — record the implementer family (the cross-family disjointness anchor)
|
|
146
|
+
produce resolve-probe-family <dir> --run-id <id> [--milestone <mid>] [--implementer-family <fam>] [--host-family <fam>] [--available-families a,b,c] M2 — resolve a probe/oracle family DISJOINT from the implementer + a LIVE model (never hardcoded); available:false when none
|
|
147
|
+
produce oracle prepare <dir> --run-id <id> --milestone <mid> --verdict-path <p> M2 — extract verify-proof's v2 verdict (must be approved) + tests[] evidence into a cross-family judge payload
|
|
148
|
+
produce oracle record <dir> --run-id <id> --milestone <mid> --verdict <pass|fail> --oracle-family <fam> M2 — record produce.oracle.judged (refused when oracle_family === implementer)
|
|
149
|
+
produce probe freeze <dir> --run-id <id> --milestone <mid> --probe-file <p> --author-family <fam> M2 — freeze an immutable held-out probe set (hash + family only; refused when author_family === implementer)
|
|
150
|
+
produce probe record <dir> --run-id <id> --milestone <mid> --result <pass|fail> --probe-family <fam> --head-sha <sha> M2 — record produce.probe.run (result/family/sha only; refused when probe_family === implementer)
|
|
151
|
+
produce probe sweep <dir> --run-id <id> [--sample N] M2 — deterministic rotating sample of already-done milestones to blind-re-probe against HEAD
|
|
152
|
+
produce canary <dir> --run-id <id> M2 — green-on-visible MINUS green-on-blind + per-milestone breakdown (the reward-hacking canary)
|
|
153
|
+
produce declare-criteria <dir> --run-id <id> --milestone <mid> --criterion "<text>" [--criterion-id <cid>] [--scope-change "<reason>"] /apt:produce M3 — open one acceptance criterion (V += 1); rejected after descent unless --scope-change justifies the increase (anti-windup)
|
|
154
|
+
produce progress <dir> --run-id <id> M3 — re-read the Lyapunov V-series from the log: open-criteria count strictly decreasing across shipped milestones + any un-sanctioned-increase violations (flags, never rejects)
|
|
155
|
+
produce saga enter <dir> --run-id <id> --milestone <mid> --phase <plan|execute|verify|review|ship> /apt:produce M4 — append produce.saga.phase_entered (separate fold from the roadmap projection)
|
|
156
|
+
produce saga complete <dir> --run-id <id> --milestone <mid> --phase <plan|execute|verify|review|ship> M4 — append produce.saga.phase_completed
|
|
157
|
+
produce saga compensate <dir> --run-id <id> --milestone <mid> --phase <p> [--reason "<r>"] M4 — fire the FORWARD compensator (verify|review|ship ships a revert + reopens + re-plans; NEVER rolls back a merged PR); records the descriptor, does not execute it
|
|
158
|
+
produce saga resolve-reviewer <dir> --run-id <id> --milestone <mid> [--host-family <fam>] [--available-families a,b,c] M4 — resolve a REVIEW-phase reviewer family DISJOINT from the implementer + a LIVE model (reuses resolveProbeFamily; never hardcoded); available:false when none
|
|
159
|
+
produce saga review <dir> --run-id <id> --milestone <mid> --verdict <pass|fail> --reviewer-family <fam> M4 — record produce.saga.reviewed (refused when reviewer_family === implementer)
|
|
160
|
+
produce saga status <dir> --run-id <id> M4 — re-project per-milestone saga phase state + pending compensation from the log (no roadmap status mutation)
|
|
161
|
+
produce ttl select <dir> --run-id <id> --head-sha <sha> [--sample N] M4 — deterministic rotating sample of OLD done milestones STALE against HEAD (reuses the M2 rotation; a milestone re-verified against current HEAD is excluded as fresh)
|
|
162
|
+
produce ttl reverify <dir> --run-id <id> --milestone <mid> --result <pass|fail> --probe-family <fam> --head-sha <sha> M4 — cross-family re-verify; --result fail appends ttl.reverified + ttl.regression_detected + fires the FORWARD compensator (ships a revert, not a rollback)
|
|
163
|
+
produce schedule <dir> --run-id <id> [--wip N] /apt:produce M5 — WSJF-rank the ready milestones (cost-of-delay / job-size) + a WIP-limited pick; dependency-aware readiness (blocked / cycle), M4-regression re-entry; read-only
|
|
164
|
+
produce claim declare <dir> --run-id <id> --profile <p> --claim-file <path> [--claim-id <id>] /apt:produce M6 — validate + freeze an immutable Claim Envelope to claims/<claim-id>.json (interop, not a 4th store); rejects a global/unbounded scope, empty preconditions, or missing expiry fail-closed; logs produce.claim.declared (content_hash only)
|
|
165
|
+
produce claim list <dir> --run-id <id> M6 — re-project the claim ledger (declared − expired) folded from the append-only log; produce.claim.* never enters the roadmap projection
|
|
166
|
+
produce claim expire <dir> --run-id <id> --claim-id <id> [--reason "<r>"] M6 — append produce.claim.expired; the claim drops out of the ledger's active set (anti-corrosion manual expiry)
|
|
167
|
+
produce claim consume <dir> --run-id <id> [--milestone <mid>] M6 — emit the STRUCTURED delta {claim_id, targets:[{kind, requirement, ref?}]} derived from resolution_verifier (never a lesson-text paste); skips expired / HEAD-invalidated / precondition-unmatched claims
|
|
168
|
+
|
|
139
169
|
followups check <dir> --task-id <id> G45: derive the expected follow-up universe (Out of Scope ∪ raw review rows ∪ qa findings) + report missing/unadjudicated/unfixed/undispositioned rows; exit 1 when the ledger is dishonest
|
|
140
170
|
followups disposition <dir> --task-id <id> --row <row-id> --kind <filed_task|executed_commit|parked_note|dropped_by_user> --ref <ref> [--reason <text>] G45: flip a ledger row to dispositioned (atomic; dropped_by_user requires --reason)
|
|
141
171
|
followups policy <dir> --task-id <id> G45: effective disposition policy (ask|file|chain + max_chain_depth) from config.followups when set, else autonomy-derived
|
|
@@ -108,6 +108,7 @@ export const LEGACY_INSTALL_PATHS = Object.freeze({
|
|
|
108
108
|
'agents/apt-personas.md',
|
|
109
109
|
'agents/apt-plan.md',
|
|
110
110
|
'agents/apt-pr-review.md',
|
|
111
|
+
'agents/apt-produce.md',
|
|
111
112
|
'agents/apt-prototype.md',
|
|
112
113
|
'agents/apt-quick.md',
|
|
113
114
|
'agents/apt-release-notes.md',
|
|
@@ -153,6 +154,7 @@ export const LEGACY_INSTALL_PATHS = Object.freeze({
|
|
|
153
154
|
'commands/apt-personas.md',
|
|
154
155
|
'commands/apt-plan.md',
|
|
155
156
|
'commands/apt-pr-review.md',
|
|
157
|
+
'commands/apt-produce.md',
|
|
156
158
|
'commands/apt-prototype.md',
|
|
157
159
|
'commands/apt-quick.md',
|
|
158
160
|
'commands/apt-release-notes.md',
|