@tea-agent/loop-agent 0.1.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/AGENTS.md +121 -0
- package/CHANGELOG.md +37 -0
- package/README.md +144 -0
- package/bin/loop-agent.js +21 -0
- package/dist/adapters/aimax.js +91 -0
- package/dist/adapters/context.js +32 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/loop-agent.js +98 -0
- package/dist/adapters/types.js +1 -0
- package/dist/cli/catalog.js +259 -0
- package/dist/cli/help.js +55 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/program.js +505 -0
- package/dist/cli.js +12 -0
- package/dist/commands/closeout.js +13 -0
- package/dist/commands/coverage-audit.js +14 -0
- package/dist/commands/cursor-prompt.js +222 -0
- package/dist/commands/cursor-worker.js +43 -0
- package/dist/commands/dag-approve.js +102 -0
- package/dist/commands/dag-final-verification.js +76 -0
- package/dist/commands/dag-init-hybrid.js +56 -0
- package/dist/commands/dag-reconcile-tasks.js +51 -0
- package/dist/commands/dag-reject.js +91 -0
- package/dist/commands/dag-report.js +177 -0
- package/dist/commands/dag-resume.js +34 -0
- package/dist/commands/dag-run-task.js +470 -0
- package/dist/commands/dag-validate.js +186 -0
- package/dist/commands/dag-workflow-compile.js +91 -0
- package/dist/commands/dag-workflow-plan.js +130 -0
- package/dist/commands/dag-workflow-validate.js +66 -0
- package/dist/commands/delegate.js +132 -0
- package/dist/commands/docs-archive.js +5 -0
- package/dist/commands/docs-audit.js +5 -0
- package/dist/commands/doctor.js +50 -0
- package/dist/commands/goal.js +92 -0
- package/dist/commands/handoff-check.js +5 -0
- package/dist/commands/harvest.js +44 -0
- package/dist/commands/inspect.js +11 -0
- package/dist/commands/instructions.js +195 -0
- package/dist/commands/knowledge.js +64 -0
- package/dist/commands/loop-benchmark.js +72 -0
- package/dist/commands/loop.js +241 -0
- package/dist/commands/new-task.js +5 -0
- package/dist/commands/pi-prompt.js +181 -0
- package/dist/commands/pi-reuse-benchmark.js +153 -0
- package/dist/commands/plan-list.js +5 -0
- package/dist/commands/promote-run.js +29 -0
- package/dist/commands/reference-index.js +16 -0
- package/dist/commands/run-dag.js +184 -0
- package/dist/commands/spine.js +38 -0
- package/dist/commands/stats.js +84 -0
- package/dist/commands/status.js +56 -0
- package/dist/commands/study-init.js +192 -0
- package/dist/commands/workflow.js +259 -0
- package/dist/commands/worktree-create.js +31 -0
- package/dist/commands/worktree-list.js +5 -0
- package/dist/commands/worktree-remove.js +26 -0
- package/dist/cursor-worker-entry.js +8 -0
- package/dist/executors/config-core.js +55 -0
- package/dist/executors/config.js +2 -0
- package/dist/executors/cursor-artifacts.js +33 -0
- package/dist/executors/cursor-execution-log.js +81 -0
- package/dist/executors/cursor-executor-artifacts.js +135 -0
- package/dist/executors/cursor-executor.js +468 -0
- package/dist/executors/cursor-run.js +115 -0
- package/dist/executors/cursor-tool.js +94 -0
- package/dist/executors/cursor-worker-client.js +213 -0
- package/dist/executors/cursor-worker-protocol.js +18 -0
- package/dist/executors/cursor-worker-server.js +54 -0
- package/dist/executors/cursor-worker.js +3 -0
- package/dist/executors/cursor.js +6 -0
- package/dist/executors/dag-cursor-executor.js +88 -0
- package/dist/executors/dag-pi-executor.js +322 -0
- package/dist/executors/dag-static-executor.js +45 -0
- package/dist/executors/dag.js +4 -0
- package/dist/executors/index.js +8 -0
- package/dist/executors/model-routing.js +60 -0
- package/dist/executors/pi-event-serializer.js +43 -0
- package/dist/executors/pi-executor.js +606 -0
- package/dist/executors/pi-reuse-benchmark.js +316 -0
- package/dist/executors/pi-runtime-reuse.js +29 -0
- package/dist/executors/pi-sdk-executor.js +255 -0
- package/dist/executors/pi-sdk.js +1 -0
- package/dist/executors/pi.js +3 -0
- package/dist/executors/shell-executor.js +300 -0
- package/dist/executors/shell-presets.js +47 -0
- package/dist/executors/shell-verification.js +251 -0
- package/dist/executors/shell-write-guard.js +126 -0
- package/dist/executors/shell.js +3 -0
- package/dist/executors/static.js +1 -0
- package/dist/governance/checks.js +434 -0
- package/dist/governance/harness.js +9 -0
- package/dist/governance/index.js +3 -0
- package/dist/governance/manifest-types.js +128 -0
- package/dist/governance/manifest.js +2 -0
- package/dist/governance/path-guard.js +69 -0
- package/dist/governance/path-guards.js +2 -0
- package/dist/governance/profiles.js +3 -0
- package/dist/governance/requirement-coverage.js +425 -0
- package/dist/governance/skill-safety.js +135 -0
- package/dist/governance/spine-audit.js +152 -0
- package/dist/records/closeout.js +2 -0
- package/dist/records/harvest.js +236 -0
- package/dist/records/index.js +3 -0
- package/dist/records/one-shot-runs.js +421 -0
- package/dist/records/promotion.js +199 -0
- package/dist/shared/artifacts-core.js +88 -0
- package/dist/shared/artifacts.js +2 -0
- package/dist/shared/context-files.js +32 -0
- package/dist/shared/context.js +2 -0
- package/dist/shared/copy-dir.js +17 -0
- package/dist/shared/git-progress.js +165 -0
- package/dist/shared/index.js +5 -0
- package/dist/shared/logger.js +23 -0
- package/dist/shared/one-shot-prompt-args.js +98 -0
- package/dist/shared/path-refs.js +31 -0
- package/dist/shared/prompts.js +26 -0
- package/dist/shared/reference-context.js +238 -0
- package/dist/shared/timeout-policy.js +19 -0
- package/dist/shared/timeout.js +1 -0
- package/dist/shared/types.js +5 -0
- package/dist/task/config-types.js +97 -0
- package/dist/task/config.js +2 -0
- package/dist/task/delegate.js +220 -0
- package/dist/task/goal-audit.js +51 -0
- package/dist/task/goal-policy.js +8 -0
- package/dist/task/goal.js +3 -0
- package/dist/task/ids.js +1 -0
- package/dist/task/index.js +9 -0
- package/dist/task/lifecycle.js +1 -0
- package/dist/task/paths.js +1 -0
- package/dist/task/read-model.js +149 -0
- package/dist/task/runtime.js +699 -0
- package/dist/task/source-state.js +1 -0
- package/dist/task/state.js +55 -0
- package/dist/task/subagent-guidance.js +1 -0
- package/dist/task/workflow-state-types.js +92 -0
- package/dist/task/worktree-cleanup.js +140 -0
- package/dist/task/worktree.js +171 -0
- package/dist/workflows/dag/authoring.js +8 -0
- package/dist/workflows/dag/authority-surface.js +138 -0
- package/dist/workflows/dag/canvas-observer.js +474 -0
- package/dist/workflows/dag/decision-envelope.js +502 -0
- package/dist/workflows/dag/decision-evidence.js +153 -0
- package/dist/workflows/dag/decision-gates.js +1 -0
- package/dist/workflows/dag/executor-registry.js +25 -0
- package/dist/workflows/dag/facts.js +4 -0
- package/dist/workflows/dag/failure-category.js +111 -0
- package/dist/workflows/dag/final-verification.js +180 -0
- package/dist/workflows/dag/governance-constants.js +5 -0
- package/dist/workflows/dag/governance-profile.js +405 -0
- package/dist/workflows/dag/index.js +6 -0
- package/dist/workflows/dag/init-hybrid.js +855 -0
- package/dist/workflows/dag/knowledge-curator.js +162 -0
- package/dist/workflows/dag/lifecycle.js +484 -0
- package/dist/workflows/dag/prompt-source.js +88 -0
- package/dist/workflows/dag/prompt.js +130 -0
- package/dist/workflows/dag/reconcile-tasks.js +404 -0
- package/dist/workflows/dag/recovery-recommendation.js +226 -0
- package/dist/workflows/dag/repair-artifact.js +136 -0
- package/dist/workflows/dag/report.js +1019 -0
- package/dist/workflows/dag/runner.js +1677 -0
- package/dist/workflows/dag/runtime.js +5 -0
- package/dist/workflows/dag/skill-instructions.js +471 -0
- package/dist/workflows/dag/skills.js +41 -0
- package/dist/workflows/dag/spec.js +3 -0
- package/dist/workflows/dag/topo.js +30 -0
- package/dist/workflows/dag/types.js +275 -0
- package/dist/workflows/dag/upstream-artifacts.js +95 -0
- package/dist/workflows/dag/validate.js +527 -0
- package/dist/workflows/dynamic/artifacts.js +65 -0
- package/dist/workflows/dynamic/compile.js +360 -0
- package/dist/workflows/dynamic/compileTypes.js +1 -0
- package/dist/workflows/dynamic/errors.js +5 -0
- package/dist/workflows/dynamic/index.js +7 -0
- package/dist/workflows/dynamic/profiles.js +156 -0
- package/dist/workflows/dynamic/spec.js +114 -0
- package/dist/workflows/dynamic/validate.js +275 -0
- package/dist/workflows/loop/actions.js +1334 -0
- package/dist/workflows/loop/benchmark.js +510 -0
- package/dist/workflows/loop/closeout.js +134 -0
- package/dist/workflows/loop/context.js +48 -0
- package/dist/workflows/loop/events.js +25 -0
- package/dist/workflows/loop/hash.js +32 -0
- package/dist/workflows/loop/index.js +8 -0
- package/dist/workflows/loop/paths.js +17 -0
- package/dist/workflows/loop/rounds.js +81 -0
- package/dist/workflows/loop/signals.js +55 -0
- package/dist/workflows/loop/state.js +116 -0
- package/dist/workflows/loop/templates.js +54 -0
- package/dist/workflows/loop/types.js +28 -0
- package/docs/README.md +62 -0
- package/docs/agent-dag-recovery-playbook.md +158 -0
- package/docs/agent-dag-runner.md +40 -0
- package/docs/cursor-executor-usage.md +25 -0
- package/docs/decisions/README.md +3 -0
- package/docs/design/README.md +36 -0
- package/docs/development-principles.md +71 -0
- package/docs/dynamic-workflow-dag-engine-roadmap.md +1749 -0
- package/docs/exec-plans/README.md +6 -0
- package/docs/exec-plans/active/README.md +5 -0
- package/docs/exec-plans/completed/README.md +5 -0
- package/docs/feature-workflow.md +184 -0
- package/docs/harness-methodology-debugging.md +153 -0
- package/docs/harness-methodology-tdd.md +130 -0
- package/docs/harness-methodology-verification.md +27 -0
- package/docs/loop-agent-harness.md +42 -0
- package/docs/progress/README.md +3 -0
- package/docs/reports/README.md +3 -0
- package/docs/templates/adr.md +60 -0
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -0
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -0
- package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -0
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -0
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -0
- package/docs/templates/agent-dag-report.schema.json +423 -0
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -0
- package/docs/templates/agent-dag.base.json +195 -0
- package/docs/templates/agent-dag.final-verification.json +190 -0
- package/docs/templates/agent-dag.schema.json +316 -0
- package/docs/templates/agent-dag.supervised-implementation.json +500 -0
- package/docs/templates/exec-plan.md +64 -0
- package/docs/templates/feature-spec.md +53 -0
- package/docs/templates/hybrid-dag.json +193 -0
- package/docs/templates/progress-log.md +17 -0
- package/docs/templates/project-start-checklist.md +9 -0
- package/docs/templates/qa-report.md +42 -0
- package/docs/templates/sprint-contract.md +29 -0
- package/docs/verification-matrix.md +30 -0
- package/examples/decision-gate-agent-dag.json +123 -0
- package/examples/example-dag.json +51 -0
- package/examples/hybrid-loop-agent-dag.json +194 -0
- package/harness.json +92 -0
- package/package.json +61 -0
- package/skills/ai-engineering-context/SKILL.md +48 -0
- package/skills/loop-agent/SKILL.md +260 -0
- package/skills/loop-agent/references/README.md +63 -0
- package/skills/loop-agent/references/command-reference.md +315 -0
- package/skills/loop-agent/references/harness-policy.md +258 -0
- package/skills/loop-agent/references/hybrid-dag.md +216 -0
- package/skills/loop-agent/references/learned/README.md +21 -0
- package/skills/loop-agent/references/model-routing.md +36 -0
- package/skills/loop-agent/references/multi-worktree.md +54 -0
- package/skills/loop-agent/references/one-shot-runs.md +85 -0
- package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -0
- package/skills/loop-agent/references/pi-prompt.md +23 -0
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +83 -0
- package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -0
- package/skills/loop-agent/references/task-workflow.md +84 -0
- package/skills/loop-agent/references/verification-and-failure-handling.md +74 -0
- package/skills/requesting-code-review/SKILL.md +101 -0
- package/skills/requesting-code-review/code-reviewer.md +168 -0
- package/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/skills/systematic-debugging/SKILL.md +296 -0
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/skills/systematic-debugging/find-polluter.sh +63 -0
- package/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/skills/systematic-debugging/test-academic.md +14 -0
- package/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/skills/verification-before-completion/SKILL.md +154 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { assertPlanFreshForImplement, markSpecCompleted, refreshSourceState, } from "./runtime.js";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { copyFile, readFile, rename, unlink, writeFile, } from 'node:fs/promises';
|
|
2
|
+
import { workflowStateSchema } from '../shared/types.js';
|
|
3
|
+
function parseWorkflowState(raw) {
|
|
4
|
+
try {
|
|
5
|
+
return workflowStateSchema.parse(JSON.parse(raw));
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Persist backup atomically (tmp → rename), tolerant of Windows rename semantics. */
|
|
12
|
+
async function writeBackupCopy(bakPath, json) {
|
|
13
|
+
const bakTmp = `${bakPath}.${process.pid}.tmp`;
|
|
14
|
+
await writeFile(bakTmp, json, 'utf-8');
|
|
15
|
+
try {
|
|
16
|
+
await rename(bakTmp, bakPath);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
await copyFile(bakTmp, bakPath);
|
|
20
|
+
await unlink(bakTmp).catch(() => { });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function loadWorkflowState(statePath) {
|
|
24
|
+
const bakPath = `${statePath}.bak`;
|
|
25
|
+
let raw = '';
|
|
26
|
+
try {
|
|
27
|
+
raw = await readFile(statePath, 'utf-8');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
raw = '';
|
|
31
|
+
}
|
|
32
|
+
const primary = raw.trim() ? parseWorkflowState(raw) : null;
|
|
33
|
+
if (primary)
|
|
34
|
+
return primary;
|
|
35
|
+
try {
|
|
36
|
+
raw = await readFile(bakPath, 'utf-8');
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
raw = '';
|
|
40
|
+
}
|
|
41
|
+
const fromBak = raw.trim() ? parseWorkflowState(raw) : null;
|
|
42
|
+
if (fromBak) {
|
|
43
|
+
const json = `${JSON.stringify(fromBak, null, 2)}\n`;
|
|
44
|
+
await writeFile(statePath, json, 'utf-8');
|
|
45
|
+
await writeBackupCopy(bakPath, json);
|
|
46
|
+
return fromBak;
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`workflow state unreadable or missing: ${statePath} (no valid ${bakPath})`);
|
|
49
|
+
}
|
|
50
|
+
export async function saveWorkflowState(statePath, state) {
|
|
51
|
+
const json = `${JSON.stringify(state, null, 2)}\n`;
|
|
52
|
+
const bakPath = `${statePath}.bak`;
|
|
53
|
+
await writeFile(statePath, json, 'utf-8');
|
|
54
|
+
await writeBackupCopy(bakPath, json);
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { resolvePiSubagentTools, resolveSubagentGuidance, shouldInjectSubagentGuidance, SUBAGENT_GUIDANCE, SUBAGENT_GUIDANCE_STANDARD, SUBAGENT_GUIDANCE_STRONG, } from "./runtime.js";
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { taskExecutorSchema } from "../governance/manifest-types.js";
|
|
3
|
+
export const stepNameSchema = z.enum([
|
|
4
|
+
"analyze",
|
|
5
|
+
"plan",
|
|
6
|
+
"spec",
|
|
7
|
+
"implement",
|
|
8
|
+
"verify",
|
|
9
|
+
"retrospective",
|
|
10
|
+
]);
|
|
11
|
+
export const workflowTaskStatusSchema = z.enum([
|
|
12
|
+
"draft",
|
|
13
|
+
"active",
|
|
14
|
+
"completed",
|
|
15
|
+
"failed",
|
|
16
|
+
]);
|
|
17
|
+
export const taskGoalStatusSchema = z.enum([
|
|
18
|
+
"active",
|
|
19
|
+
"paused",
|
|
20
|
+
"budget_limited",
|
|
21
|
+
"complete",
|
|
22
|
+
]);
|
|
23
|
+
export const taskGoalStateSchema = z.object({
|
|
24
|
+
objective: z.string().default(""),
|
|
25
|
+
status: taskGoalStatusSchema.default("paused"),
|
|
26
|
+
tokenBudget: z.number().int().positive().nullable().default(null),
|
|
27
|
+
tokensUsed: z.number().int().nonnegative().default(0),
|
|
28
|
+
timeUsedSeconds: z.number().int().nonnegative().default(0),
|
|
29
|
+
pendingObjectiveUpdatedNotice: z.boolean().default(false),
|
|
30
|
+
pendingBudgetLimitNotice: z.boolean().default(false),
|
|
31
|
+
/** Number of legacy goal continuation attempts in the current goal lifecycle */
|
|
32
|
+
continuationRuns: z.number().int().nonnegative().default(0),
|
|
33
|
+
createdAt: z.string().default(""),
|
|
34
|
+
updatedAt: z.string().default(""),
|
|
35
|
+
});
|
|
36
|
+
export const workflowDecisionSchema = z.object({
|
|
37
|
+
time: z.string(),
|
|
38
|
+
step: stepNameSchema,
|
|
39
|
+
fromStatus: workflowTaskStatusSchema,
|
|
40
|
+
toStatus: workflowTaskStatusSchema,
|
|
41
|
+
reason: z.string(),
|
|
42
|
+
evidence: z.string().optional().default(""),
|
|
43
|
+
});
|
|
44
|
+
export const sourceStateSchema = z.object({
|
|
45
|
+
currentHash: z.string().default(""),
|
|
46
|
+
plannedHash: z.string().default(""),
|
|
47
|
+
trackedFiles: z.array(z.string()).default([]),
|
|
48
|
+
lastCheckedAt: z.string().default(""),
|
|
49
|
+
lastPlannedAt: z.string().default(""),
|
|
50
|
+
lastPlannedStep: z.enum(["plan", "spec"]).nullable().default(null),
|
|
51
|
+
stalePlan: z.boolean().default(false),
|
|
52
|
+
});
|
|
53
|
+
export const workflowStateSchema = z.object({
|
|
54
|
+
taskId: z.string(),
|
|
55
|
+
status: workflowTaskStatusSchema,
|
|
56
|
+
currentStep: stepNameSchema,
|
|
57
|
+
completedSteps: z.array(stepNameSchema),
|
|
58
|
+
lastUpdated: z.string(),
|
|
59
|
+
executor: z.object({
|
|
60
|
+
name: taskExecutorSchema.default("pi"),
|
|
61
|
+
mode: z.enum(["json", "sdk"]).optional().default("json"),
|
|
62
|
+
}),
|
|
63
|
+
artifacts: z.object({
|
|
64
|
+
analysis: z.boolean(),
|
|
65
|
+
plan: z.boolean(),
|
|
66
|
+
modifyLog: z.boolean(),
|
|
67
|
+
verifyResult: z.boolean(),
|
|
68
|
+
retrospective: z.boolean(),
|
|
69
|
+
}),
|
|
70
|
+
sourceState: sourceStateSchema.default({
|
|
71
|
+
currentHash: "",
|
|
72
|
+
plannedHash: "",
|
|
73
|
+
trackedFiles: [],
|
|
74
|
+
lastCheckedAt: "",
|
|
75
|
+
lastPlannedAt: "",
|
|
76
|
+
lastPlannedStep: null,
|
|
77
|
+
stalePlan: false,
|
|
78
|
+
}),
|
|
79
|
+
goal: taskGoalStateSchema.default({
|
|
80
|
+
objective: "",
|
|
81
|
+
status: "paused",
|
|
82
|
+
tokenBudget: null,
|
|
83
|
+
tokensUsed: 0,
|
|
84
|
+
timeUsedSeconds: 0,
|
|
85
|
+
pendingObjectiveUpdatedNotice: false,
|
|
86
|
+
pendingBudgetLimitNotice: false,
|
|
87
|
+
continuationRuns: 0,
|
|
88
|
+
createdAt: "",
|
|
89
|
+
updatedAt: "",
|
|
90
|
+
}),
|
|
91
|
+
decisionLog: z.array(workflowDecisionSchema).default([]),
|
|
92
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { copyFile, mkdir, rm, stat } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { loadHarnessManifest } from '../governance/harness.js';
|
|
4
|
+
import { appendWorkflowLog } from '../shared/logger.js';
|
|
5
|
+
import { getTaskDir, getTaskPaths } from './runtime.js';
|
|
6
|
+
import { copyDir } from '../shared/copy-dir.js';
|
|
7
|
+
import { removeWorktree, resolveWorktreePath } from './worktree.js';
|
|
8
|
+
/**
|
|
9
|
+
* If cwd is `.worktrees/<taskId>`, return the main repo root; otherwise treat cwd as main repo.
|
|
10
|
+
*/
|
|
11
|
+
export function inferMainRepoRoot(cwd, taskId) {
|
|
12
|
+
if (process.platform === 'win32' && cwd.startsWith('/') && !cwd.startsWith('//')) {
|
|
13
|
+
const resolved = path.posix.normalize(cwd);
|
|
14
|
+
if (path.posix.basename(resolved) === taskId && path.posix.basename(path.posix.dirname(resolved)) === '.worktrees') {
|
|
15
|
+
return {
|
|
16
|
+
mainRepoRoot: path.posix.dirname(path.posix.dirname(resolved)),
|
|
17
|
+
worktreePath: resolved,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return { mainRepoRoot: resolved, worktreePath: null };
|
|
21
|
+
}
|
|
22
|
+
const resolved = path.resolve(cwd);
|
|
23
|
+
if (path.basename(resolved) === taskId && path.basename(path.dirname(resolved)) === '.worktrees') {
|
|
24
|
+
return {
|
|
25
|
+
mainRepoRoot: path.dirname(path.dirname(resolved)),
|
|
26
|
+
worktreePath: resolved,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return { mainRepoRoot: resolved, worktreePath: null };
|
|
30
|
+
}
|
|
31
|
+
async function pathExists(p) {
|
|
32
|
+
try {
|
|
33
|
+
await stat(p);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Copy artifacts/logs/state from worktree task dir into main repo task dir. */
|
|
41
|
+
export async function archiveTaskDirFromWorktree(mainRepoRoot, worktreePath, taskId) {
|
|
42
|
+
const srcTaskDir = getTaskPaths(worktreePath, taskId).taskDir;
|
|
43
|
+
const destTaskDir = getTaskPaths(mainRepoRoot, taskId).taskDir;
|
|
44
|
+
for (const sub of ['artifacts', 'logs']) {
|
|
45
|
+
const srcSub = path.join(srcTaskDir, sub);
|
|
46
|
+
const destSub = path.join(destTaskDir, sub);
|
|
47
|
+
if (await pathExists(srcSub)) {
|
|
48
|
+
await mkdir(destSub, { recursive: true });
|
|
49
|
+
await copyDir(srcSub, destSub);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const srcStateFile = path.join(srcTaskDir, '.workflow_state.json');
|
|
53
|
+
const destStateFile = path.join(destTaskDir, '.workflow_state.json');
|
|
54
|
+
if (await pathExists(srcStateFile)) {
|
|
55
|
+
await mkdir(path.dirname(destStateFile), { recursive: true });
|
|
56
|
+
await copyFile(srcStateFile, destStateFile);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* After a task reaches `completed`, archive worktree task data to the main repo and
|
|
61
|
+
* remove the git worktree directory. The task branch is kept for `harvest` merge.
|
|
62
|
+
*/
|
|
63
|
+
export async function cleanupWorktreeAfterTaskCompleted(repoRoot, taskId, opts = {}) {
|
|
64
|
+
const { mainRepoRoot, worktreePath: inferredPath } = inferMainRepoRoot(repoRoot, taskId);
|
|
65
|
+
let manifest;
|
|
66
|
+
try {
|
|
67
|
+
manifest = await loadHarnessManifest(mainRepoRoot);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
manifest = undefined;
|
|
71
|
+
}
|
|
72
|
+
const worktreePath = inferredPath ?? resolveWorktreePath(mainRepoRoot, manifest, taskId);
|
|
73
|
+
if (!(await pathExists(worktreePath))) {
|
|
74
|
+
return {
|
|
75
|
+
attempted: false,
|
|
76
|
+
archived: false,
|
|
77
|
+
worktreeRemoved: false,
|
|
78
|
+
mainRepoRoot,
|
|
79
|
+
message: 'no worktree directory',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const mainTaskDir = getTaskDir(mainRepoRoot, taskId);
|
|
83
|
+
let archived = false;
|
|
84
|
+
let worktreeRemoved = false;
|
|
85
|
+
try {
|
|
86
|
+
await archiveTaskDirFromWorktree(mainRepoRoot, worktreePath, taskId);
|
|
87
|
+
archived = true;
|
|
88
|
+
await appendWorkflowLog(mainTaskDir, `archived task artifacts/logs from worktree before cleanup (${worktreePath})`);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
92
|
+
await appendWorkflowLog(mainTaskDir, `worktree archive before cleanup failed: ${message}`);
|
|
93
|
+
return {
|
|
94
|
+
attempted: true,
|
|
95
|
+
archived: false,
|
|
96
|
+
worktreeRemoved: false,
|
|
97
|
+
mainRepoRoot,
|
|
98
|
+
worktreePath,
|
|
99
|
+
message,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
await removeWorktree(mainRepoRoot, manifest, {
|
|
104
|
+
taskId,
|
|
105
|
+
path: worktreePath,
|
|
106
|
+
// Task dir is archived; remaining .harness (often gitignored) must not block removal.
|
|
107
|
+
force: true,
|
|
108
|
+
deleteBranch: opts.deleteBranch === true,
|
|
109
|
+
});
|
|
110
|
+
worktreeRemoved = true;
|
|
111
|
+
await appendWorkflowLog(mainTaskDir, `worktree removed after task completed (${worktreePath})`);
|
|
112
|
+
try {
|
|
113
|
+
if (await pathExists(worktreePath)) {
|
|
114
|
+
await rm(worktreePath, { recursive: true, force: true });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
/* orphan .harness dir after git worktree remove */
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
await appendWorkflowLog(mainTaskDir, `worktree remove after completed failed: ${message}`);
|
|
124
|
+
return {
|
|
125
|
+
attempted: true,
|
|
126
|
+
archived,
|
|
127
|
+
worktreeRemoved: false,
|
|
128
|
+
mainRepoRoot,
|
|
129
|
+
worktreePath,
|
|
130
|
+
message,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
attempted: true,
|
|
135
|
+
archived,
|
|
136
|
+
worktreeRemoved,
|
|
137
|
+
mainRepoRoot,
|
|
138
|
+
worktreePath,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { access, mkdir, realpath } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the worktree root directory from a harness manifest.
|
|
6
|
+
* Falls back to `<repoRoot>/.worktrees` when not configured.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveWorktreeRoot(repoRoot, manifest) {
|
|
9
|
+
const relativePath = manifest?.worktree?.rootRelativePath ?? '.worktrees';
|
|
10
|
+
return path.resolve(repoRoot, relativePath);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the worktree directory path for a given taskId.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveWorktreePath(repoRoot, manifest, taskId) {
|
|
16
|
+
const root = resolveWorktreeRoot(repoRoot, manifest);
|
|
17
|
+
return path.join(root, taskId);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve repoRoot to its real path (resolving symlinks) so that paths match
|
|
21
|
+
* what `git worktree list --porcelain` returns (e.g. /tmp → /private/tmp on macOS).
|
|
22
|
+
*/
|
|
23
|
+
async function resolveRepoRoot(repoRoot) {
|
|
24
|
+
try {
|
|
25
|
+
return await realpath(repoRoot);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return repoRoot;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create a new git worktree for a task.
|
|
33
|
+
*
|
|
34
|
+
* @returns The created worktree path, branch name, and base ref.
|
|
35
|
+
*/
|
|
36
|
+
export async function createWorktree(repoRoot, manifest, opts) {
|
|
37
|
+
const resolvedRoot = await resolveRepoRoot(repoRoot);
|
|
38
|
+
const worktreePath = resolveWorktreePath(resolvedRoot, manifest, opts.taskId);
|
|
39
|
+
const branch = opts.branch ?? `task/${opts.taskId}`;
|
|
40
|
+
const base = opts.base ?? 'HEAD';
|
|
41
|
+
// Check if worktree path already exists
|
|
42
|
+
try {
|
|
43
|
+
await access(worktreePath);
|
|
44
|
+
throw new Error(`worktree path already exists: ${worktreePath}`);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error.code !== 'ENOENT') {
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Ensure parent directory exists
|
|
52
|
+
await mkdir(path.dirname(worktreePath), { recursive: true });
|
|
53
|
+
// Build git worktree add arguments
|
|
54
|
+
const addArgs = ['worktree', 'add', worktreePath];
|
|
55
|
+
// Check if branch already exists; if so, don't pass -b
|
|
56
|
+
const branchExists = await gitBranchExists(resolvedRoot, branch);
|
|
57
|
+
if (!branchExists) {
|
|
58
|
+
addArgs.push('-b', branch);
|
|
59
|
+
}
|
|
60
|
+
addArgs.push(base);
|
|
61
|
+
await runGit(resolvedRoot, addArgs);
|
|
62
|
+
return { path: worktreePath, branch, base };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* List git worktrees by parsing `git worktree list --porcelain`.
|
|
66
|
+
*/
|
|
67
|
+
export async function listWorktrees(repoRoot) {
|
|
68
|
+
const resolvedRoot = await resolveRepoRoot(repoRoot);
|
|
69
|
+
const stdout = await runGit(resolvedRoot, ['worktree', 'list', '--porcelain']);
|
|
70
|
+
const entries = [];
|
|
71
|
+
const blocks = stdout.split('\n\n').filter((b) => b.trim().length > 0);
|
|
72
|
+
for (const block of blocks) {
|
|
73
|
+
const lines = block.trim().split('\n');
|
|
74
|
+
let entryPath = '';
|
|
75
|
+
let branch;
|
|
76
|
+
let head = '';
|
|
77
|
+
let bare = false;
|
|
78
|
+
let locked = false;
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
if (line.startsWith('worktree ')) {
|
|
81
|
+
entryPath = line.slice('worktree '.length);
|
|
82
|
+
}
|
|
83
|
+
else if (line.startsWith('branch ')) {
|
|
84
|
+
const ref = line.slice('branch '.length);
|
|
85
|
+
// Convert refs/heads/<name> to <name>
|
|
86
|
+
branch = ref.replace(/^refs\/heads\//, '');
|
|
87
|
+
}
|
|
88
|
+
else if (line.startsWith('HEAD ')) {
|
|
89
|
+
head = line.slice('HEAD '.length);
|
|
90
|
+
}
|
|
91
|
+
else if (line === 'bare') {
|
|
92
|
+
bare = true;
|
|
93
|
+
}
|
|
94
|
+
else if (line === 'locked') {
|
|
95
|
+
locked = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (entryPath) {
|
|
99
|
+
entries.push({ path: path.normalize(entryPath), branch, head, bare, locked });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Remove a git worktree.
|
|
106
|
+
* Either `taskId` or `path` must be provided.
|
|
107
|
+
*/
|
|
108
|
+
export async function removeWorktree(repoRoot, manifest, opts) {
|
|
109
|
+
const resolvedRoot = await resolveRepoRoot(repoRoot);
|
|
110
|
+
const worktreePath = opts.path ?? (opts.taskId ? resolveWorktreePath(resolvedRoot, manifest, opts.taskId) : undefined);
|
|
111
|
+
if (!worktreePath) {
|
|
112
|
+
throw new Error('removeWorktree requires either taskId or path');
|
|
113
|
+
}
|
|
114
|
+
const removeArgs = ['worktree', 'remove'];
|
|
115
|
+
if (opts.force) {
|
|
116
|
+
removeArgs.push('--force');
|
|
117
|
+
}
|
|
118
|
+
removeArgs.push(worktreePath);
|
|
119
|
+
await runGit(resolvedRoot, removeArgs);
|
|
120
|
+
// Optionally delete the branch
|
|
121
|
+
if (opts.deleteBranch && opts.taskId) {
|
|
122
|
+
const branch = `task/${opts.taskId}`;
|
|
123
|
+
try {
|
|
124
|
+
await runGit(resolvedRoot, ['branch', '-d', branch]);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Branch may already be deleted or not exist; ignore
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Internal helpers
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
/**
|
|
135
|
+
* Run a git command and return stdout. Throws on non-zero exit.
|
|
136
|
+
*/
|
|
137
|
+
async function runGit(cwd, args) {
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const child = spawn('git', ['-C', cwd, ...args], {
|
|
140
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
141
|
+
});
|
|
142
|
+
const stdoutChunks = [];
|
|
143
|
+
const stderrChunks = [];
|
|
144
|
+
child.stdout?.on('data', (chunk) => stdoutChunks.push(chunk));
|
|
145
|
+
child.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
|
|
146
|
+
child.on('error', (err) => {
|
|
147
|
+
reject(new Error(`git ${args.join(' ')} spawn error: ${err.message}`));
|
|
148
|
+
});
|
|
149
|
+
child.on('close', (code) => {
|
|
150
|
+
const stdout = Buffer.concat(stdoutChunks).toString('utf-8');
|
|
151
|
+
const stderr = Buffer.concat(stderrChunks).toString('utf-8');
|
|
152
|
+
if (code !== 0) {
|
|
153
|
+
reject(new Error(`git ${args.join(' ')} failed (exit ${code}): ${stderr.trim()}`));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
resolve(stdout);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Check if a branch exists in the local git repository.
|
|
162
|
+
*/
|
|
163
|
+
async function gitBranchExists(cwd, branchName) {
|
|
164
|
+
try {
|
|
165
|
+
const stdout = await runGit(cwd, ['branch', '--list', branchName]);
|
|
166
|
+
return stdout.trim().length > 0;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./authority-surface.js";
|
|
2
|
+
export * from "./init-hybrid.js";
|
|
3
|
+
export * from "./prompt-source.js";
|
|
4
|
+
export * from "./prompt.js";
|
|
5
|
+
export * from "../../executors/shell-presets.js";
|
|
6
|
+
export * from "./skill-instructions.js";
|
|
7
|
+
export * from "./skills.js";
|
|
8
|
+
export * from "./final-verification.js";
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export const AUTHORITY_SURFACE_AUDIT_PROMPT_PATH = "docs/templates/agent-dag-authority-surface-audit.prompt.md";
|
|
2
|
+
export const AUTHORITY_SURFACE_EXPLICIT_MARKERS = [
|
|
3
|
+
/authority-surface-audit/i,
|
|
4
|
+
/<!--\s*authority-surface-audit\s*-->/i,
|
|
5
|
+
];
|
|
6
|
+
export const AUTHORITY_SIGNAL_RULES = [
|
|
7
|
+
{
|
|
8
|
+
id: "permission-authority",
|
|
9
|
+
pattern: /权限|permission|authority|完成权|ownership\s+gate/i,
|
|
10
|
+
message: "task/source mentions permission, authority, or completion ownership",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "state-machine",
|
|
14
|
+
pattern: /状态机|state\s*machine|workflow\s*state|task\.json\.status|\.workflow_state/i,
|
|
15
|
+
message: "task/source mentions workflow/task state machines",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "tool-exposure",
|
|
19
|
+
pattern: /tool\s*exposure|model-facing|工具暴露|工具表|api\s*surface|普通工具表/i,
|
|
20
|
+
message: "task/source mentions model-facing tools or API surface",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "completion-authority",
|
|
24
|
+
pattern: /update_goal|complete\s*gate|completion\s*authority|orchestrator.*complete|完成.*gate/i,
|
|
25
|
+
message: "task/source mentions goal completion authority or orchestrator gates",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "bypass-path",
|
|
29
|
+
pattern: /bypass\s*path|绕过.*gate|绕过.*verifier|fail\s*closed/i,
|
|
30
|
+
message: "task/source mentions bypass paths or fail-closed gates",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "orchestrator-internal",
|
|
34
|
+
pattern: /orchestrator-only|orchestrator\s*owned|internal\s*path|orchestrator\s*only/i,
|
|
35
|
+
message: "task/source mentions orchestrator-only or internal ownership paths",
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
function collectAuthoritySourceText(sources) {
|
|
39
|
+
return [
|
|
40
|
+
sources.taskConfig.title,
|
|
41
|
+
sources.taskConfig.taskId,
|
|
42
|
+
...sources.taskConfig.hardConstraints,
|
|
43
|
+
sources.requirementMarkdown,
|
|
44
|
+
sources.constraintMarkdown ?? "",
|
|
45
|
+
].join("\n");
|
|
46
|
+
}
|
|
47
|
+
export function hasExplicitAuthoritySurfaceAudit(text, taskConfig) {
|
|
48
|
+
const corpus = [
|
|
49
|
+
text,
|
|
50
|
+
...(taskConfig?.hardConstraints ?? []),
|
|
51
|
+
].join("\n");
|
|
52
|
+
return AUTHORITY_SURFACE_EXPLICIT_MARKERS.some((pattern) => pattern.test(corpus));
|
|
53
|
+
}
|
|
54
|
+
export function detectAuthoritySurfaceSignals(text) {
|
|
55
|
+
const signals = [];
|
|
56
|
+
for (const rule of AUTHORITY_SIGNAL_RULES) {
|
|
57
|
+
if (rule.pattern.test(text)) {
|
|
58
|
+
signals.push({ id: rule.id, message: rule.message });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return signals;
|
|
62
|
+
}
|
|
63
|
+
export function resolveAuthoritySurfaceAudit(sources) {
|
|
64
|
+
const corpus = collectAuthoritySourceText(sources);
|
|
65
|
+
const explicit = hasExplicitAuthoritySurfaceAudit(corpus, sources.taskConfig);
|
|
66
|
+
const signals = detectAuthoritySurfaceSignals(corpus);
|
|
67
|
+
const reasons = [];
|
|
68
|
+
if (explicit) {
|
|
69
|
+
reasons.push("authority-surface-audit explicitly enabled via task source marker or hardConstraint");
|
|
70
|
+
}
|
|
71
|
+
for (const signal of signals) {
|
|
72
|
+
reasons.push(`authority signal: ${signal.id} (${signal.message})`);
|
|
73
|
+
}
|
|
74
|
+
const enabled = explicit || signals.length > 0;
|
|
75
|
+
if (!enabled) {
|
|
76
|
+
reasons.push("no authority signals or explicit enablement; authority surface audit omitted");
|
|
77
|
+
}
|
|
78
|
+
return { enabled, explicit, signals, reasons };
|
|
79
|
+
}
|
|
80
|
+
export function buildAuthoritySurfaceAuditNode(sources) {
|
|
81
|
+
return {
|
|
82
|
+
id: "authority-surface-audit-pi",
|
|
83
|
+
depends_on: ["hard-verify-shell"],
|
|
84
|
+
role: "verifier",
|
|
85
|
+
executor: "pi",
|
|
86
|
+
complexity: "HIGH",
|
|
87
|
+
writePolicy: "read-only",
|
|
88
|
+
allowedPaths: sources.taskConfig.allowedPaths.length > 0
|
|
89
|
+
? sources.taskConfig.allowedPaths
|
|
90
|
+
: ["**"],
|
|
91
|
+
forbiddenPaths: [
|
|
92
|
+
".harness/**",
|
|
93
|
+
".harness/dag-runs/**",
|
|
94
|
+
"artifacts/**",
|
|
95
|
+
...sources.taskConfig.forbiddenPaths,
|
|
96
|
+
],
|
|
97
|
+
outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision; cites code/test/tool-table/API surface evidence for authority questions. No file writes.",
|
|
98
|
+
subtask_prompt: [
|
|
99
|
+
"Audit authority surface for state-write ownership, model-facing tools/APIs, orchestrator-only paths, and completion-authority bypass paths.",
|
|
100
|
+
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
101
|
+
"Answer who can write state, what is model-facing, whether orchestrator-only paths stay internal, and whether any bypass path exists.",
|
|
102
|
+
"Cite concrete evidence from code, tests, tool tables, CLI/registry surfaces, or API schemas. Read-only: do not modify files.",
|
|
103
|
+
`Follow invariants in ${AUTHORITY_SURFACE_AUDIT_PROMPT_PATH}.`,
|
|
104
|
+
].join("\n\n"),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export function buildAuthoritySurfaceGateNode(sources) {
|
|
108
|
+
return {
|
|
109
|
+
id: "authority-surface-gate-shell",
|
|
110
|
+
depends_on: ["authority-surface-audit-pi"],
|
|
111
|
+
role: "verifier",
|
|
112
|
+
executor: "shell",
|
|
113
|
+
complexity: "LOW",
|
|
114
|
+
writePolicy: "read-only",
|
|
115
|
+
allowedPaths: sources.taskConfig.allowedPaths.length > 0
|
|
116
|
+
? sources.taskConfig.allowedPaths
|
|
117
|
+
: ["**"],
|
|
118
|
+
forbiddenPaths: [
|
|
119
|
+
".harness/**",
|
|
120
|
+
".harness/dag-runs/**",
|
|
121
|
+
"artifacts/**",
|
|
122
|
+
...sources.taskConfig.forbiddenPaths,
|
|
123
|
+
],
|
|
124
|
+
outputContract: "Deterministic authority surface audit verdict gate: exit 0 only when authority-surface-audit-pi first extracted VERDICT line is pass.",
|
|
125
|
+
subtask_prompt: "Deterministic gate: block review/closeout unless authority-surface-audit-pi emitted VERDICT: pass.",
|
|
126
|
+
shell: {
|
|
127
|
+
commands: [],
|
|
128
|
+
verdictGate: {
|
|
129
|
+
fromNodeId: "authority-surface-audit-pi",
|
|
130
|
+
accept: ["VERDICT: pass"],
|
|
131
|
+
label: "authority surface audit",
|
|
132
|
+
lineMode: "first-verdict-line",
|
|
133
|
+
},
|
|
134
|
+
cwd: ".",
|
|
135
|
+
timeoutMs: 60000,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|