@miller-tech/uap 1.150.2 → 1.151.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/dist/.tsbuildinfo +1 -1
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +190 -230
- package/dist/cli/deliver.js.map +1 -1
- package/dist/delivery/index.d.ts +2 -0
- package/dist/delivery/index.d.ts.map +1 -1
- package/dist/delivery/index.js +2 -0
- package/dist/delivery/index.js.map +1 -1
- package/dist/delivery/orchestrated-mission.d.ts +96 -0
- package/dist/delivery/orchestrated-mission.d.ts.map +1 -0
- package/dist/delivery/orchestrated-mission.js +249 -0
- package/dist/delivery/orchestrated-mission.js.map +1 -0
- package/dist/delivery/task-orchestrator.d.ts +5 -4
- package/dist/delivery/task-orchestrator.d.ts.map +1 -1
- package/dist/delivery/task-orchestrator.js.map +1 -1
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/cli/deliver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAqBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIzD,OAAO,EAA2E,KAAK,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAKjJ;;;;;;;;;;;GAWG;AACH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG;IAAE,iBAAiB,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAQhF;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,iBAAiB,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,GAAG,OAAO,CAMV;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,GAAG,IAAI,CAIhB;AAED,wBAAgB,wBAAwB,CACtC,CAAC,EAAE,gBAAgB,EACnB,iBAAiB,EAAE,OAAO,GACzB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAiBvD;
|
|
1
|
+
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/cli/deliver.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/deliver.js
CHANGED
|
@@ -94,7 +94,7 @@ export function resolveAcceptanceVerdict(r, acceptancePrimary) {
|
|
|
94
94
|
import { createAgenticExecutor, noopApplier, selectExecutorMode, lockContractFiles } from '../delivery/agentic-executor.js';
|
|
95
95
|
import { createRepairEscalation } from '../delivery/repair-escalation.js';
|
|
96
96
|
import { clearStop, isStopRequested, loadRunState, newRunId, saveRunState } from '../delivery/run-state.js';
|
|
97
|
-
import {
|
|
97
|
+
import { phaseInstruction, planDeliveryPhases, shouldDecompose } from '../delivery/decompose.js';
|
|
98
98
|
import { runEpics } from '../delivery/epic-controller.js';
|
|
99
99
|
import { initLedger, markItem } from '../delivery/completion-ledger.js';
|
|
100
100
|
import { loadUapConfigRaw } from '../utils/config-loader.js';
|
|
@@ -112,16 +112,15 @@ function cfgRawEarlyForUvFactory(projectRoot) {
|
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
114
|
import { resolveSessionTokenBudget, sessionWorkingBudget, discoverModelContextWindow, CONTEXT_BUDGET_MARKER } from '../delivery/context-budget.js';
|
|
115
|
-
import { orchestrate } from '../delivery/task-orchestrator.js';
|
|
116
115
|
import { preflightProject, formatPreflightFailure } from '../delivery/project-preflight.js';
|
|
117
116
|
import { resolveFidelity } from '../delivery/fidelity.js';
|
|
118
117
|
import { installRunExitRecorder } from '../delivery/run-exit.js';
|
|
119
118
|
import { shouldDetach, relaunchDetached, isDetachedChild, canHostDetachLog, NO_DETACH_ENV, } from './deliver-detach.js';
|
|
120
|
-
import { extractContract } from '../delivery/contract-extractor.js';
|
|
121
119
|
import { completeDeliveryTask, openDeliveryTask, recordDeliveryOutcome, recordOrchestratorTaskOutcome, reopenDeliveryTask } from '../delivery/task-sync.js';
|
|
122
120
|
import { autoMineHaloTraces, summarizeWeaknesses, weaknessGuidance, loadPersistedWeaknesses } from '../delivery/auto-mine.js';
|
|
123
121
|
import { createGitWorktreeProvider } from '../delivery/candidate-workspace.js';
|
|
124
122
|
import { createTaskWorkspaceManager, resolveParallelTasks } from '../delivery/task-workspace.js';
|
|
123
|
+
import { runOrchestratedMission as runOrchestratedMissionCore, foldDeliveryResult } from '../delivery/orchestrated-mission.js';
|
|
125
124
|
import { createHaloDeliveryTracer } from '../delivery/halo-trace.js';
|
|
126
125
|
import { haloTracePath, isHaloTracingEnabled } from '../observability/halo-exporter.js';
|
|
127
126
|
import { createRunCoordinator } from '../delivery/run-coordinator.js';
|
|
@@ -1617,154 +1616,61 @@ async function runDeliver(instruction, options) {
|
|
|
1617
1616
|
* the per-task executor; a completed task publishes a compact summary its
|
|
1618
1617
|
* dependents read instead of source.
|
|
1619
1618
|
*/
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
const mergeProtected = new Set(options.protectTests !== false
|
|
1650
|
-
? [...snapshotProtection(projectRoot).protectedFiles, ...listGateConfigFiles(projectRoot)].map((f) => f.toLowerCase())
|
|
1651
|
-
: []);
|
|
1652
|
-
let mergeChain = Promise.resolve();
|
|
1653
|
-
const withMergeLock = (fn) => {
|
|
1654
|
-
const next = mergeChain.then(fn, fn);
|
|
1655
|
-
mergeChain = next.then(() => undefined, () => undefined);
|
|
1656
|
-
return next;
|
|
1657
|
-
};
|
|
1658
|
-
// Regexes built from strings so policy hooks never mistake a literal for a path.
|
|
1659
|
-
const SOURCE_FILE_RE = new RegExp('[.](js|mjs|cjs|ts|tsx|jsx|py)$');
|
|
1660
|
-
const NEW_TASKS_RE = new RegExp('NEW_TASKS:\\s*(\\[[\\s\\S]*?\\])');
|
|
1661
|
-
/** Run one task's convergence loop against `root` with executor `exec` -
|
|
1662
|
-
* the historic in-tree body, parameterized so isolated workspaces reuse it
|
|
1663
|
-
* verbatim. Turns/history flow into the mission aggregate as before. */
|
|
1664
|
-
const runTaskAt = async (ctx, task, root, exec) => {
|
|
1665
|
-
const taskRecord = await openDeliveryTask(`${task.title} — ${task.goal.slice(0, 120)}`, projectRoot, missionTask?.id);
|
|
1666
|
-
acceptanceSpecByRoot.set(root, ctx.prompt);
|
|
1667
|
-
if (root === projectRoot)
|
|
1668
|
-
acceptanceSpec = ctx.prompt;
|
|
1669
|
-
evidenceFor(root).writes = 0;
|
|
1670
|
-
try {
|
|
1671
|
-
const loop = new ConvergenceLoop({
|
|
1672
|
-
...loopConfig,
|
|
1673
|
-
projectRoot: root,
|
|
1674
|
-
baselineCheck: false,
|
|
1675
|
-
resumeFrom: undefined,
|
|
1676
|
-
// Checkpoints describe THE mission tree; isolated roots must not
|
|
1677
|
-
// clobber the durable run state.
|
|
1678
|
-
// The inherited explorer's workspaceProvider is bound to the MAIN
|
|
1679
|
-
// projectRoot; inside an isolated task it must not run at all.
|
|
1680
|
-
...(root !== projectRoot ? { onCheckpoint: undefined, explorer: undefined } : {}),
|
|
1681
|
-
onIteration: makeIterationHook(root),
|
|
1682
|
-
}, exec, seams);
|
|
1683
|
-
const r = await loop.deliver(ctx.prompt);
|
|
1684
|
-
all.turns += r.turns;
|
|
1685
|
-
all.history.push(...r.history);
|
|
1686
|
-
all.totalDurationMs += r.totalDurationMs;
|
|
1687
|
-
if (r.bestScore > all.bestScore) {
|
|
1688
|
-
all.bestScore = r.bestScore;
|
|
1689
|
-
all.bestTurn = r.bestTurn;
|
|
1690
|
-
}
|
|
1691
|
-
all.finalFeedback = r.finalFeedback;
|
|
1692
|
-
all.finalOutput = r.finalOutput;
|
|
1693
|
-
completeDeliveryTask(taskRecord, r);
|
|
1694
|
-
const files = [...new Set(r.history.flatMap((h) => h.filesApplied ?? []))];
|
|
1695
|
-
// P4 - extract the VERIFIED public contract of what this task built so
|
|
1696
|
-
// dependents load the interface (a few hundred chars), not the source.
|
|
1697
|
-
let contract;
|
|
1698
|
-
if (r.success && files.length > 0) {
|
|
1699
|
-
try {
|
|
1700
|
-
const { readFileSync } = await import('fs');
|
|
1701
|
-
const { join } = await import('path');
|
|
1702
|
-
const srcs = files
|
|
1703
|
-
.filter((f) => SOURCE_FILE_RE.test(f))
|
|
1704
|
-
.map((f) => {
|
|
1705
|
-
try {
|
|
1706
|
-
return { path: f, content: readFileSync(join(root, f), 'utf-8') };
|
|
1707
|
-
}
|
|
1708
|
-
catch {
|
|
1709
|
-
return null;
|
|
1710
|
-
}
|
|
1711
|
-
})
|
|
1712
|
-
.filter((x) => x !== null);
|
|
1713
|
-
contract = extractContract(srcs).contract || undefined;
|
|
1714
|
-
}
|
|
1715
|
-
catch {
|
|
1716
|
-
contract = undefined;
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
// P5 - adaptive re-planning feed: a task may surface work the initial
|
|
1720
|
-
// plan missed by emitting a `NEW_TASKS: [ {id,title,goal,deps} ]` JSON
|
|
1721
|
-
// array in its output. Parsed through the same validator as the planner
|
|
1722
|
-
// (well-formed only), then folded into the DAG by orchestrate() (which
|
|
1723
|
-
// dedupes, topo-resorts, and caps at maxTasks). Only structural fields
|
|
1724
|
-
// are used - never free-form model text as durable memory.
|
|
1725
|
-
let newTasks;
|
|
1726
|
-
if (r.success) {
|
|
1727
|
-
try {
|
|
1728
|
-
const marker = NEW_TASKS_RE.exec(r.finalOutput || r.finalFeedback || '');
|
|
1729
|
-
if (marker) {
|
|
1730
|
-
const parsed = parsePhaseArray(marker[1]).filter((t) => t.id !== task.id);
|
|
1731
|
-
if (parsed.length > 0) {
|
|
1732
|
-
newTasks = parsed.map((t) => ({
|
|
1733
|
-
id: t.id, title: t.title, goal: t.goal,
|
|
1734
|
-
...(t.deps ? { deps: t.deps } : {}),
|
|
1735
|
-
}));
|
|
1736
|
-
console.log(chalk.dim(` ↳ re-planning: task ${task.id} discovered ${newTasks.length} follow-up task(s)`));
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
catch {
|
|
1741
|
-
newTasks = undefined;
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
return {
|
|
1745
|
-
taskId: task.id,
|
|
1746
|
-
success: r.success,
|
|
1747
|
-
turns: r.turns,
|
|
1748
|
-
// ATG minimal repair: a FAILED task's summary must carry the failure
|
|
1749
|
-
// (the retry's only new information), never a goal restatement.
|
|
1750
|
-
summary: r.success
|
|
1751
|
-
? `${task.goal.slice(0, 160)}${files.length ? ` [files: ${files.join(', ')}]` : ''}`
|
|
1752
|
-
: `${task.goal.slice(0, 120)} — FAILED: ${(r.finalFeedback || 'gates did not pass').slice(0, 280)}`,
|
|
1753
|
-
...(contract ? { contract } : {}),
|
|
1754
|
-
...(newTasks && newTasks.length > 0 ? { newTasks } : {}),
|
|
1755
|
-
};
|
|
1756
|
-
}
|
|
1757
|
-
finally {
|
|
1758
|
-
acceptanceSpecByRoot.delete(root);
|
|
1759
|
-
}
|
|
1760
|
-
};
|
|
1761
|
-
const orchResult = await orchestrate({
|
|
1762
|
-
mission: instruction,
|
|
1763
|
-
tasks,
|
|
1619
|
+
/**
|
|
1620
|
+
* Wire deliver's ambient context (loop config, executors, judge state, task
|
|
1621
|
+
* records, gates) into the extracted orchestrated-mission runner
|
|
1622
|
+
* (src/delivery/orchestrated-mission.ts). Shared by the top-level
|
|
1623
|
+
* orchestrated path and the epic controller's inner missions — and, because
|
|
1624
|
+
* the runner is seam-injected, the orchestrate() wiring is finally
|
|
1625
|
+
* unit-testable (the PR #516 inert-concurrency critical could not recur
|
|
1626
|
+
* unobserved).
|
|
1627
|
+
*/
|
|
1628
|
+
const buildOrchestratedDeps = (missionText, taskPhases, parallelTasks, parentTaskId = missionTask?.id) => {
|
|
1629
|
+
const mergeProtected = new Set([
|
|
1630
|
+
...(options.protectTests !== false
|
|
1631
|
+
? [...snapshotProtection(projectRoot).protectedFiles, ...listGateConfigFiles(projectRoot)]
|
|
1632
|
+
: []),
|
|
1633
|
+
// Locked contract files are read-only for later epics; the merge
|
|
1634
|
+
// boundary re-checks them like the other protected surfaces instead
|
|
1635
|
+
// of trusting the in-worktree write_file guard transitively.
|
|
1636
|
+
...contractLock,
|
|
1637
|
+
].map((f) => f.toLowerCase()));
|
|
1638
|
+
return {
|
|
1639
|
+
instruction: missionText,
|
|
1640
|
+
projectRoot,
|
|
1641
|
+
tasks: taskPhases.map((ph) => ({
|
|
1642
|
+
id: ph.id,
|
|
1643
|
+
title: ph.title,
|
|
1644
|
+
goal: ph.goal,
|
|
1645
|
+
...(ph.deps ? { deps: ph.deps } : {}),
|
|
1646
|
+
})),
|
|
1647
|
+
parallelTasks,
|
|
1764
1648
|
contextBudgetChars: Number(process.env.UAP_DELIVER_CONTEXT_BUDGET ?? 6000),
|
|
1765
1649
|
maxTasks: Number(process.env.UAP_DELIVER_MAX_TASKS ?? 40),
|
|
1766
|
-
|
|
1767
|
-
|
|
1650
|
+
workspaceManager: parallelTasks > 1 ? createTaskWorkspaceManager(projectRoot) : null,
|
|
1651
|
+
note: (line) => console.log(line.startsWith('▶')
|
|
1652
|
+
? chalk.bold(line)
|
|
1653
|
+
: line.includes('unavailable') || line.includes('conflicted')
|
|
1654
|
+
? chalk.yellow(line)
|
|
1655
|
+
: line.includes('parallel tasks:') || line.includes('post-merge verification')
|
|
1656
|
+
? chalk.cyan(line)
|
|
1657
|
+
: chalk.dim(line)),
|
|
1658
|
+
beginTaskSpec: (root, spec) => {
|
|
1659
|
+
acceptanceSpecByRoot.set(root, spec);
|
|
1660
|
+
if (root === projectRoot)
|
|
1661
|
+
acceptanceSpec = spec;
|
|
1662
|
+
evidenceFor(root).writes = 0;
|
|
1663
|
+
},
|
|
1664
|
+
endTaskSpec: (root) => {
|
|
1665
|
+
acceptanceSpecByRoot.delete(root);
|
|
1666
|
+
// An in-tree task re-pointed the shared spec; restore the mission-
|
|
1667
|
+
// level spec so a later watch-ci re-converge never judges against a
|
|
1668
|
+
// stale task prompt.
|
|
1669
|
+
if (root === projectRoot)
|
|
1670
|
+
acceptanceSpec = missionText;
|
|
1671
|
+
},
|
|
1672
|
+
openTask: (title) => openDeliveryTask(title, projectRoot, parentTaskId),
|
|
1673
|
+
completeTask: (record, r) => completeDeliveryTask(record, r),
|
|
1768
1674
|
// P3 — per-task memory retrieval: pull the few most relevant established
|
|
1769
1675
|
// decisions/patterns/gotchas for THIS task's goal (a small semantic
|
|
1770
1676
|
// query, not the full spec), so a fresh-context task reconstructs "what
|
|
@@ -1794,85 +1700,62 @@ async function runDeliver(instruction, options) {
|
|
|
1794
1700
|
return;
|
|
1795
1701
|
await recordOrchestratorTaskOutcome(task.id, task.title, outcome.contract ?? outcome.summary, projectRoot);
|
|
1796
1702
|
},
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
//
|
|
1801
|
-
//
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
if (wsManager) {
|
|
1805
|
-
console.log(chalk.yellow(` ⇉ task ${task.id}: workspace unavailable - running in-tree (serialized)`));
|
|
1806
|
-
// In-tree execution mutates the shared tree; hold the lock for the
|
|
1807
|
-
// WHOLE run so it never races an isolated task's merge-back.
|
|
1808
|
-
return withMergeLock(() => runTaskAt(ctx, task, projectRoot, executor));
|
|
1809
|
-
}
|
|
1810
|
-
return runTaskAt(ctx, task, projectRoot, executor);
|
|
1811
|
-
}
|
|
1703
|
+
isMergeBlocked: (f) => mergeProtected.has(f.toLowerCase()),
|
|
1704
|
+
verifyCombined: () => {
|
|
1705
|
+
// Mirror the loop's redetect-merge: redetection REASSIGNS the loop's
|
|
1706
|
+
// local rungs and never mutates this captured t0 array, so a
|
|
1707
|
+
// from-scratch build's mid-mission gates must be re-merged here or
|
|
1708
|
+
// the combined tree is verified against stale t0 gates only.
|
|
1709
|
+
let combinedRungs = rungs;
|
|
1812
1710
|
try {
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
projectRoot: ws.root,
|
|
1820
|
-
endpoint: agenticEndpoint,
|
|
1821
|
-
temperature,
|
|
1822
|
-
contextTokenBudget: sessionBudget,
|
|
1823
|
-
contractFiles: contractLock,
|
|
1824
|
-
protectedFiles: options.protectTests !== false ? snapshotProtection(ws.root).protectedFiles : new Set(),
|
|
1825
|
-
protectGateConfigs: options.protectTests !== false,
|
|
1826
|
-
allowBash: options.allowBash === true || process.env.UAP_DELIVER_ALLOW_BASH === '1',
|
|
1827
|
-
onEvent: (e) => console.log(chalk.dim(` [${task.id} r${e.round} ${e.kind}${e.tool ? `:${e.tool}` : ''}] ${e.detail ?? ''}`)),
|
|
1828
|
-
})
|
|
1829
|
-
: executor;
|
|
1830
|
-
const outcome = await runTaskAt(ctx, task, ws.root, taskExecutor);
|
|
1831
|
-
if (!outcome.success)
|
|
1832
|
-
return outcome;
|
|
1833
|
-
const merged = await withMergeLock(() => ws.mergeBack((f) => mergeProtected.has(f.toLowerCase())));
|
|
1834
|
-
if (!merged.ok) {
|
|
1835
|
-
console.log(chalk.yellow(` ⇉ task ${task.id}: merge-back conflicted (${merged.reason ?? 'apply failed'}) - minimal repair retries on the updated tree`));
|
|
1836
|
-
return {
|
|
1837
|
-
taskId: task.id,
|
|
1838
|
-
success: false,
|
|
1839
|
-
turns: 0,
|
|
1840
|
-
summary: `${task.goal.slice(0, 120)} — FAILED: the tree changed underneath this task (parallel merge conflict) — rebuild it against the CURRENT state of the repository. Detail: ${(merged.reason ?? 'could not apply the task delta').slice(0, 200)}`,
|
|
1841
|
-
};
|
|
1842
|
-
}
|
|
1843
|
-
if (merged.files.length > 0) {
|
|
1844
|
-
console.log(chalk.dim(` ⇉ task ${task.id}: merged ${merged.files.length} file(s) into the main tree`));
|
|
1845
|
-
}
|
|
1846
|
-
return outcome;
|
|
1711
|
+
const detected = detectRungs(projectRoot);
|
|
1712
|
+
const have = new Set(rungs.map((r) => r.id));
|
|
1713
|
+
const allow = loopConfig.redetectFilter ?? ((r) => tierOf(r) === 'fast' || tierOf(r) === 'runtime');
|
|
1714
|
+
const added = detected.filter((r) => !have.has(r.id) && allow(r));
|
|
1715
|
+
if (added.length > 0)
|
|
1716
|
+
combinedRungs = [...rungs, ...added];
|
|
1847
1717
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1718
|
+
catch {
|
|
1719
|
+
// detection unavailable - verify against the t0 rungs
|
|
1850
1720
|
}
|
|
1721
|
+
return Promise.resolve(tieredRunner(combinedRungs, projectRoot, {}));
|
|
1851
1722
|
},
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1723
|
+
runLoop: async ({ root, prompt, taskId, isolated }) => {
|
|
1724
|
+
// The agentic executor binds tools to a root at construction — give
|
|
1725
|
+
// an isolated task its own, with the same protections re-derived for
|
|
1726
|
+
// its tree. The blind executor is root-free (the per-task loop's
|
|
1727
|
+
// projectRoot directs the applier and gates).
|
|
1728
|
+
const exec = isolated && agentic
|
|
1729
|
+
? createAgenticExecutor(model, {
|
|
1730
|
+
projectRoot: root,
|
|
1731
|
+
endpoint: agenticEndpoint,
|
|
1732
|
+
temperature,
|
|
1733
|
+
contextTokenBudget: sessionBudget,
|
|
1734
|
+
contractFiles: contractLock,
|
|
1735
|
+
protectedFiles: options.protectTests !== false ? snapshotProtection(root).protectedFiles : new Set(),
|
|
1736
|
+
protectGateConfigs: options.protectTests !== false,
|
|
1737
|
+
allowBash: options.allowBash === true || process.env.UAP_DELIVER_ALLOW_BASH === '1',
|
|
1738
|
+
onEvent: (e) => console.log(chalk.dim(` [${taskId} r${e.round} ${e.kind}${e.tool ? `:${e.tool}` : ''}] ${e.detail ?? ''}`)),
|
|
1739
|
+
})
|
|
1740
|
+
: executor;
|
|
1741
|
+
const loop = new ConvergenceLoop({
|
|
1742
|
+
...loopConfig,
|
|
1743
|
+
projectRoot: root,
|
|
1744
|
+
baselineCheck: false,
|
|
1745
|
+
resumeFrom: undefined,
|
|
1746
|
+
// Checkpoints describe THE mission tree, and the inherited
|
|
1747
|
+
// explorer's workspaceProvider is bound to the MAIN projectRoot —
|
|
1748
|
+
// neither may run inside an isolated task.
|
|
1749
|
+
...(isolated ? { onCheckpoint: undefined, explorer: undefined } : {}),
|
|
1750
|
+
onIteration: makeIterationHook(root),
|
|
1751
|
+
}, exec, seams);
|
|
1752
|
+
return loop.deliver(prompt);
|
|
1753
|
+
},
|
|
1754
|
+
};
|
|
1755
|
+
};
|
|
1756
|
+
const runOrchestratedMission = async () => {
|
|
1757
|
+
const parallelTasks = resolveParallelTasks(cfgRaw.deliver?.parallelTasks);
|
|
1758
|
+
return runOrchestratedMissionCore(buildOrchestratedDeps(instruction, phases, parallelTasks));
|
|
1876
1759
|
};
|
|
1877
1760
|
const runPhasedMission = async () => {
|
|
1878
1761
|
const all = {
|
|
@@ -1940,6 +1823,7 @@ async function runDeliver(instruction, options) {
|
|
|
1940
1823
|
success: true, alreadyDelivered: false, turns: 0, bestScore: 0, bestTurn: 0,
|
|
1941
1824
|
history: [], finalFeedback: '', finalOutput: '', totalDurationMs: 0,
|
|
1942
1825
|
};
|
|
1826
|
+
const epicParallelTasks = resolveParallelTasks(cfgRaw.deliver?.parallelTasks);
|
|
1943
1827
|
const contractsFirst = process.env.UAP_DELIVER_CONTRACTS !== '0';
|
|
1944
1828
|
const scaffoldFirst = process.env.UAP_DELIVER_SCAFFOLD !== '0';
|
|
1945
1829
|
const planned = await planDeliveryPhases(instruction, verdictExecutor, undefined, {
|
|
@@ -2032,14 +1916,90 @@ async function runDeliver(instruction, options) {
|
|
|
2032
1916
|
// prompt carries process instructions ("read X first"), prior-epic
|
|
2033
1917
|
// summaries, and retry feedback — none verifiable from code, so a
|
|
2034
1918
|
// small judge rejects objectively-green turns against them forever.
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
1919
|
+
const epicSpec = `EPIC — ${epic.title}:\n${epic.goal}` +
|
|
1920
|
+
(epic.criteria?.length ? `\nAcceptance criteria:\n${epic.criteria.map((c) => `- ${c}`).join('\n')}` : '') +
|
|
1921
|
+
(!epic.contracts && contractLock.size > 0 ? `\n(Builds against locked contracts: ${[...contractLock].join(', ')})` : '') +
|
|
1922
|
+
(fillsScaffold ? '\n(FILL epic: no todo!()/NotImplementedError/TODO-throw stub markers may remain in the files it implements; signatures must be unchanged.)' : '') +
|
|
1923
|
+
(epic.scaffold ? '\n(SCAFFOLD epic: complete compiling signatures with stub bodies are the DELIVERABLE — unimplemented logic is expected and correct here.)' : '');
|
|
1924
|
+
acceptanceSpec = epicSpec;
|
|
2041
1925
|
specChangeEvidence.writes = 0; // fresh epic — breaker needs fresh diff evidence
|
|
2042
1926
|
const epicTask = await openDeliveryTask(`${epic.title} \u2014 ${epic.goal.slice(0, 120)}`, projectRoot, missionTask?.id);
|
|
1927
|
+
// Epic-path parallel dispatch (PR #516 follow-up): with
|
|
1928
|
+
// deliver.parallelTasks > 1, an epic whose goal itself decomposes
|
|
1929
|
+
// runs as an orchestrated task DAG — worktree-isolated parallel
|
|
1930
|
+
// dispatch inside the DEFAULT (epics-on) path. Only the PLANNER call
|
|
1931
|
+
// is fail-soft (a miss falls through to the classic single loop); a
|
|
1932
|
+
// runner exception must propagate — swallowing it would silently
|
|
1933
|
+
// restart the epic on a partially-merged tree.
|
|
1934
|
+
if (epicParallelTasks > 1) {
|
|
1935
|
+
// Retry feedback must reach the decomposition (fresh tasks that fix
|
|
1936
|
+
// the failure) — mirroring splitEpic — because the orchestrator's
|
|
1937
|
+
// 300-char mission snippet cannot carry it into every task.
|
|
1938
|
+
const epicPlanGoal = `${epic.title}: ${epic.goal}` +
|
|
1939
|
+
(ctx.lastFailure
|
|
1940
|
+
? `\n\n(The previous attempt did not complete: ${ctx.lastFailure.slice(0, 300)}. The tasks you produce must FIX this.)`
|
|
1941
|
+
: '');
|
|
1942
|
+
let epicPlan = [];
|
|
1943
|
+
try {
|
|
1944
|
+
epicPlan = await planDeliveryPhases(epicPlanGoal, verdictExecutor, undefined,
|
|
1945
|
+
// The mission-level plan already passed validation and each
|
|
1946
|
+
// task converges against real gates — skip the judge call here.
|
|
1947
|
+
{ sessionTokenBudget: sessionBudget, thoughtExperiment: false });
|
|
1948
|
+
}
|
|
1949
|
+
catch {
|
|
1950
|
+
epicPlan = [];
|
|
1951
|
+
}
|
|
1952
|
+
if (epicPlan.length < 2) {
|
|
1953
|
+
console.log(chalk.dim(` ⇉ epic ${epic.id}: no usable task decomposition — classic single-loop epic`));
|
|
1954
|
+
}
|
|
1955
|
+
else {
|
|
1956
|
+
console.log(chalk.cyan(` ⇉ epic ${epic.id}: decomposed into ${epicPlan.length} tasks — orchestrated parallel dispatch`));
|
|
1957
|
+
// The orchestrator's mission snippet carries the HEAD of this
|
|
1958
|
+
// text — lead with the epic essence, then retry + steering.
|
|
1959
|
+
const epicMissionText = `EPIC — ${epic.title}: ${epic.goal}` +
|
|
1960
|
+
(ctx.lastFailure ? `\nPREVIOUS ATTEMPT FEEDBACK (address this): ${ctx.lastFailure}` : '') +
|
|
1961
|
+
`${contractsNote}${scaffoldNote}${priors}`;
|
|
1962
|
+
const r = await runOrchestratedMissionCore(buildOrchestratedDeps(epicMissionText, epicPlan, epicParallelTasks, epicTask?.id));
|
|
1963
|
+
foldDeliveryResult(all, r);
|
|
1964
|
+
completeDeliveryTask(epicTask, r);
|
|
1965
|
+
const files = [...new Set(r.history.flatMap((h) => h.filesApplied ?? []))];
|
|
1966
|
+
if (epic.contracts)
|
|
1967
|
+
lastContractEpicFiles = files;
|
|
1968
|
+
// Epic-level acceptance parity: the classic path judges every
|
|
1969
|
+
// green turn against the EPIC spec (criteria, FILL "no stub
|
|
1970
|
+
// markers remain", locked contracts). Green tasks + a green
|
|
1971
|
+
// combined tree still must satisfy it — one judge call.
|
|
1972
|
+
// Fail-soft on judge availability; hard on a completed verdict.
|
|
1973
|
+
if (r.success && options.acceptance && !skipJudgeForSimple) {
|
|
1974
|
+
try {
|
|
1975
|
+
const judged = await runAcceptanceGate({
|
|
1976
|
+
spec: epicSpec,
|
|
1977
|
+
projectRoot,
|
|
1978
|
+
executor: verdictExecutor,
|
|
1979
|
+
runtimeNote: 'Objective project gates ALL PASSED for every task and on the combined tree — treat build/test requirements as objectively verified.',
|
|
1980
|
+
});
|
|
1981
|
+
const verdict = resolveAcceptanceVerdict(judged, acceptancePrimary);
|
|
1982
|
+
if (!verdict.passed) {
|
|
1983
|
+
return {
|
|
1984
|
+
success: false,
|
|
1985
|
+
turns: r.turns,
|
|
1986
|
+
summary: `${epic.goal.slice(0, 120)} — tasks green but EPIC acceptance failed: ${(verdict.feedback ?? '').slice(0, 240)}`,
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
catch {
|
|
1991
|
+
// judge unavailable — the objective verdicts stand
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
const budgetHit = !r.success && r.history.some((h) => h.budgetStopped);
|
|
1995
|
+
return {
|
|
1996
|
+
success: r.success,
|
|
1997
|
+
turns: r.turns,
|
|
1998
|
+
summary: `${epic.goal.slice(0, 140)}${files.length ? ` [files: ${files.join(', ')}]` : ''}` +
|
|
1999
|
+
(budgetHit ? ` ${CONTEXT_BUDGET_MARKER} session(s) exceeded the context budget — scope is too large for one session` : ''),
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2043
2003
|
const loop = new ConvergenceLoop({ ...loopConfig, baselineCheck: false, resumeFrom: undefined, onIteration: makeIterationHook() }, executor, seams);
|
|
2044
2004
|
const r = await loop.deliver(scoped);
|
|
2045
2005
|
all.turns += r.turns;
|