@tea-agent/loop-agent 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +98 -11
- package/README.md +24 -5
- package/dist/application/evaluation/alias.js +184 -0
- package/dist/application/evaluation/budget.js +192 -0
- package/dist/application/evaluation/campaign-hash.js +47 -0
- package/dist/application/evaluation/campaign-matrix.js +372 -0
- package/dist/application/evaluation/campaign-scorecard.js +135 -0
- package/dist/application/evaluation/campaign.js +370 -0
- package/dist/application/evaluation/candidate.js +23 -6
- package/dist/application/evaluation/corpus-hash.js +38 -0
- package/dist/application/evaluation/corpus.js +56 -0
- package/dist/application/evaluation/experiment.js +294 -0
- package/dist/application/evaluation/ignition.js +198 -0
- package/dist/application/evaluation/integrity-audit.js +162 -0
- package/dist/application/evaluation/outer-loop.js +132 -0
- package/dist/application/evaluation/pi-cell-executor.js +39 -0
- package/dist/application/evaluation/private-verifier.js +46 -0
- package/dist/application/evaluation/promotion-policy.js +151 -0
- package/dist/application/evaluation/proposer.js +98 -0
- package/dist/application/evaluation/types.js +522 -0
- package/dist/cli/command-definitions.js +19 -3
- package/dist/commands/eval.js +1176 -13
- package/dist/commands/init.js +4 -1
- package/dist/infrastructure/evaluation/alias-store.js +199 -0
- package/dist/infrastructure/evaluation/campaign-store.js +154 -0
- package/dist/infrastructure/evaluation/corpus-store.js +181 -0
- package/dist/infrastructure/evaluation/experiment-store.js +124 -0
- package/dist/infrastructure/evaluation/ignition-store.js +82 -0
- package/dist/infrastructure/evaluation/private-verifier-store.js +145 -0
- package/dist/infrastructure/evaluation/proposer-store.js +78 -0
- package/dist/worker/cli.js +6 -3
- package/dist/worker/delivery/final-verification.js +96 -8
- package/dist/worker/delivery/package.js +23 -4
- package/dist/worker/delivery/verification-bundle.js +521 -0
- package/dist/worker/feature/fullstack-validate.js +337 -0
- package/dist/worker/feature/profile-schema.js +44 -0
- package/dist/worker/feature/ready-plan-projection.js +1 -0
- package/dist/worker/feature/reducer.js +2 -0
- package/dist/worker/feature/review.js +106 -11
- package/dist/worker/materialize/harness-task-materializer.js +5 -0
- package/dist/worker/observability/read-model.js +7 -0
- package/dist/worker/observe/static/views/task.js +1 -0
- package/dist/worker/outcomes/adapters.js +144 -0
- package/dist/worker/outcomes/evidence-tokens.js +29 -0
- package/dist/worker/outcomes/gate.js +40 -0
- package/dist/worker/outcomes/projector.js +185 -0
- package/dist/worker/outcomes/registry.js +1 -0
- package/dist/worker/outcomes/store.js +131 -0
- package/dist/worker/outcomes/types.js +79 -0
- package/dist/worker/report/morning-report.js +4 -3
- package/dist/worker/run-task/run-task.js +85 -2
- package/dist/worker/runner/run-ready.js +32 -1
- package/dist/worker/task-graph/acceptance-schema.js +12 -0
- package/dist/worker/task-graph/ready-planner.js +131 -0
- package/dist/worker/task-graph/task-graph-schema.js +31 -0
- package/dist/worker/task-graph/validate.js +44 -4
- package/dist/worker/task-spec/schema.js +9 -0
- package/dist/worker/task-spec/validate.js +39 -0
- package/dist/worker/task-spec/workflow-routing.js +149 -0
- package/dist/workflows/dag/budget-enforcement.js +67 -0
- package/dist/workflows/dag/context-policy.js +137 -0
- package/dist/workflows/dag/init-hybrid.js +27 -11
- package/dist/workflows/dag/knowledge-curator.js +3 -0
- package/dist/workflows/dag/node-execution.js +11 -4
- package/dist/workflows/dag/prompt.js +1 -1
- package/dist/workflows/dag/runner.js +43 -16
- package/dist/workflows/dag/skill-snapshot.js +11 -7
- package/dist/workflows/dag/types.js +18 -0
- package/docs/README.md +1 -0
- package/docs/init-surface.manifest.json +7 -7
- package/docs/templates/branch-merge-report.md +0 -1
- package/docs/templates/evaluation/campaign-budget-v1.json +12 -0
- package/docs/templates/evaluation/campaign-dogfood-v0.json +24 -0
- package/docs/templates/evaluation/campaign-evidence-v1.json +44 -0
- package/docs/templates/evaluation/context-policy-baseline-v1.json +17 -0
- package/docs/templates/evaluation/context-policy-role-specialized-v1.json +28 -0
- package/docs/templates/evaluation/corpus-dogfood-v0.manifest.json +118 -0
- package/docs/templates/evaluation/matrix-dag-dry-run-v1.json +21 -0
- package/docs/templates/evaluation/matrix-fixture-v1.json +10 -0
- package/docs/templates/evaluation/private-verifier-dogfood-v0.json +16 -0
- package/docs/templates/product-line/AGENTS.md +1 -0
- package/docs/templates/product-line/README.md +17 -0
- package/docs/templates/product-line/acceptance.yaml +9 -0
- package/docs/templates/product-line/feature.yaml +11 -0
- package/docs/templates/product-line/task-graph.yaml +8 -0
- package/docs/templates/product-line/task.yaml +4 -0
- package/package.json +6 -16
- package/skills/browser-tools/SKILL.md +2 -2
- package/skills/frontend-design-review/references/review-checklist.md +27 -45
- package/skills/frontend-implementation/references/node-contracts.md +4 -4
- package/skills/frontend-review/SKILL.md +3 -1
- package/skills/frontend-review/references/review-findings.md +2 -1
- package/skills/frontend-verification/SKILL.md +3 -1
- package/skills/frontend-verification/references/verification-checklist.md +13 -22
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/docs/agent-dag-recovery-playbook.md +0 -195
- package/docs/agent-dag-runner.md +0 -67
- package/docs/cursor-prompt-sidecar.md +0 -36
- package/docs/decisions/README.md +0 -18
- package/docs/design/README.md +0 -167
- package/docs/development-principles.md +0 -73
- package/docs/exec-plans/README.md +0 -6
- package/docs/exec-plans/active/README.md +0 -13
- package/docs/exec-plans/completed/README.md +0 -108
- package/docs/feature-workflow.md +0 -414
- package/docs/loop-agent-harness.md +0 -142
- package/docs/production-readiness.md +0 -96
- package/docs/progress/README.md +0 -81
- package/docs/reports/README.md +0 -163
- package/docs/verification-matrix.md +0 -70
- package/scripts/check-product-line-docs.sh +0 -29
- package/scripts/check-task-pool-root.sh +0 -32
- package/scripts/kb-graph-incremental-prepare.sh +0 -5
- package/scripts/kb-graph-materialize.sh +0 -4
- package/scripts/kb-graph-promote.sh +0 -4
- package/scripts/kb-query.sh +0 -5
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { hostname } from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { isHardBudgetBreached, resolveEffectiveMaxConcurrent, } from "../../application/evaluation/budget.js";
|
|
4
5
|
import { readCandidateRecord } from "../../infrastructure/evaluation/candidate-store.js";
|
|
5
6
|
import { CANONICAL_TASK_ID_PATTERN, formatLocalCompactDate, } from "../../task/runtime.js";
|
|
7
|
+
import { assertFrozenBudget, initRunBudgetLedger, preflightBudgetOrBreach, recordFinishedNodeBudget, writeBudgetLedgerArtifacts, } from "./budget-enforcement.js";
|
|
6
8
|
import { getDagRunDir, isTerminalDagRunStatus, locateDagRun, readHumanApprovalArtifact, requireActiveDagRun, } from "./lifecycle.js";
|
|
7
9
|
import { moveToCompletedRunDir, moveToPausedRunDir, prepareActiveRunDir, writeRunSpec, writeRunState, } from "./run-store.js";
|
|
8
10
|
import { createDagNodeExecutor } from "./executor-registry.js";
|
|
@@ -120,7 +122,7 @@ export function createInitialRunState(spec, opts, ranks, runId = opts.runId ?? "
|
|
|
120
122
|
: {}),
|
|
121
123
|
};
|
|
122
124
|
}
|
|
123
|
-
|
|
125
|
+
const state = {
|
|
124
126
|
version: 1,
|
|
125
127
|
title: spec.title,
|
|
126
128
|
runId,
|
|
@@ -147,6 +149,8 @@ export function createInitialRunState(spec, opts, ranks, runId = opts.runId ?? "
|
|
|
147
149
|
}
|
|
148
150
|
: {}),
|
|
149
151
|
};
|
|
152
|
+
initRunBudgetLedger(state, spec.budget);
|
|
153
|
+
return state;
|
|
150
154
|
}
|
|
151
155
|
export function assertFrozenEvaluationBinding(spec, state) {
|
|
152
156
|
const declared = spec.evaluation;
|
|
@@ -183,7 +187,7 @@ export async function runDag(spec, opts) {
|
|
|
183
187
|
controllerVersion: runningIdentity.packageVersion,
|
|
184
188
|
});
|
|
185
189
|
const { ranks } = topoSortToRanks(spec);
|
|
186
|
-
const maxConcurrent = Math.max(1, opts.maxConcurrent ?? 4);
|
|
190
|
+
const { maxConcurrent } = resolveEffectiveMaxConcurrent(Math.max(1, opts.maxConcurrent ?? 4), spec.budget);
|
|
187
191
|
let runId = opts.runId;
|
|
188
192
|
if (runId) {
|
|
189
193
|
for (const warning of validateDagRunIdFormat(runId).warnings) {
|
|
@@ -268,6 +272,7 @@ export async function resumeDagRun(opts) {
|
|
|
268
272
|
throw new Error(`dag run ${opts.runId} is already terminal (status=${state.status})`);
|
|
269
273
|
}
|
|
270
274
|
assertFrozenEvaluationBinding(spec, state);
|
|
275
|
+
assertFrozenBudget(spec.budget, state.budget, state.runId);
|
|
271
276
|
// Runtime contract + controller identity must be re-verified before executing
|
|
272
277
|
// any remaining node on resume; drift fails closed.
|
|
273
278
|
const runningIdentity = resolveRunningControllerIdentity();
|
|
@@ -311,7 +316,7 @@ export async function resumeDagRun(opts) {
|
|
|
311
316
|
catch (error) {
|
|
312
317
|
throw new Error(`skill snapshot validation failed on resume: ${error instanceof Error ? error.message : String(error)}`);
|
|
313
318
|
}
|
|
314
|
-
const maxConcurrent = Math.max(1, opts.maxConcurrent ?? 4);
|
|
319
|
+
const { maxConcurrent } = resolveEffectiveMaxConcurrent(Math.max(1, opts.maxConcurrent ?? 4), spec.budget);
|
|
315
320
|
state.status = "running";
|
|
316
321
|
const resumedAt = new Date().toISOString();
|
|
317
322
|
state.runner = {
|
|
@@ -410,23 +415,43 @@ async function executeDagCheckpoint(input) {
|
|
|
410
415
|
tasksById,
|
|
411
416
|
meta: { runDir, runId: state.runId, spec },
|
|
412
417
|
}),
|
|
413
|
-
executeScheduledNode: (nodeId, executeNode, onPause) =>
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
state
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
418
|
+
executeScheduledNode: async (nodeId, executeNode, onPause) => {
|
|
419
|
+
if (isHardBudgetBreached(state.budgetLedger))
|
|
420
|
+
return;
|
|
421
|
+
const preBreach = preflightBudgetOrBreach(state);
|
|
422
|
+
if (preBreach && isHardBudgetBreached(state.budgetLedger)) {
|
|
423
|
+
await writeBudgetLedgerArtifacts(runDir, state.budgetLedger);
|
|
424
|
+
await persistState();
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
await executeDagNode({
|
|
428
|
+
nodeId,
|
|
429
|
+
tasksById,
|
|
430
|
+
state,
|
|
431
|
+
spec,
|
|
432
|
+
cwd,
|
|
433
|
+
runDir,
|
|
434
|
+
executeNode,
|
|
435
|
+
executeDynamicNode,
|
|
436
|
+
observer: input.observer,
|
|
437
|
+
persistState,
|
|
438
|
+
onPause,
|
|
439
|
+
});
|
|
440
|
+
const node = state.nodes[nodeId];
|
|
441
|
+
if (node &&
|
|
442
|
+
(node.status === "FINISHED" || node.status === "ERROR")) {
|
|
443
|
+
recordFinishedNodeBudget(state, node);
|
|
444
|
+
await writeBudgetLedgerArtifacts(runDir, state.budgetLedger);
|
|
445
|
+
await persistState();
|
|
446
|
+
}
|
|
447
|
+
},
|
|
426
448
|
});
|
|
427
449
|
if (pausedByNodeId) {
|
|
428
450
|
break;
|
|
429
451
|
}
|
|
452
|
+
if (isHardBudgetBreached(state.budgetLedger)) {
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
430
455
|
const convergenceDecision = await runConvergencePassController({
|
|
431
456
|
spec,
|
|
432
457
|
state,
|
|
@@ -446,6 +471,7 @@ async function executeDagCheckpoint(input) {
|
|
|
446
471
|
}
|
|
447
472
|
state.finishedAt = new Date().toISOString();
|
|
448
473
|
const runDirBeforeTransfer = runDir;
|
|
474
|
+
await writeBudgetLedgerArtifacts(runDir, state.budgetLedger);
|
|
449
475
|
if (pausedByNodeId) {
|
|
450
476
|
state.status = "paused";
|
|
451
477
|
await persistState();
|
|
@@ -466,6 +492,7 @@ async function executeDagCheckpoint(input) {
|
|
|
466
492
|
if (state.convergence) {
|
|
467
493
|
relocateConvergenceArtifactPaths(state.convergence, runDirBeforeTransfer, runDir);
|
|
468
494
|
}
|
|
495
|
+
await writeBudgetLedgerArtifacts(runDir, state.budgetLedger);
|
|
469
496
|
await persistState({ allowCompletedFactsWrite: true });
|
|
470
497
|
return {
|
|
471
498
|
title: spec.title,
|
|
@@ -3,9 +3,9 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { writeTextAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
6
|
+
import { resolveContextPolicy } from "./context-policy.js";
|
|
6
7
|
import { buildDagNodePromptEnvelope } from "./prompt.js";
|
|
7
|
-
import {
|
|
8
|
-
import { resolveDagNodeSkills } from "./skills.js";
|
|
8
|
+
import { resolveDagSkillInstructions, } from "./skill-instructions.js";
|
|
9
9
|
export const SKILL_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
10
10
|
export const SKILL_SNAPSHOT_RESOLVER_VERSION = 1;
|
|
11
11
|
export const SKILL_SNAPSHOT_REL_PATH = ".runtime/skill-snapshot.json";
|
|
@@ -117,11 +117,13 @@ function profileId(input) {
|
|
|
117
117
|
export function buildDagSkillSnapshotProfileRequest(spec, task) {
|
|
118
118
|
if (task.executor !== "pi")
|
|
119
119
|
return undefined;
|
|
120
|
+
const policy = resolveContextPolicy(spec);
|
|
121
|
+
const budget = policy.resolveSkillInstructionBudget(task);
|
|
120
122
|
const request = {
|
|
121
|
-
skills: [...
|
|
122
|
-
includeLearnedPatterns:
|
|
123
|
-
perSkillMaxChars:
|
|
124
|
-
totalMaxChars:
|
|
123
|
+
skills: [...policy.resolveSkills(spec, task)],
|
|
124
|
+
includeLearnedPatterns: budget.includeLearnedPatterns,
|
|
125
|
+
perSkillMaxChars: budget.perSkillMaxChars,
|
|
126
|
+
totalMaxChars: budget.totalMaxChars,
|
|
125
127
|
};
|
|
126
128
|
return { id: profileId(request), ...request };
|
|
127
129
|
}
|
|
@@ -511,7 +513,8 @@ function stripPromptText(instruction) {
|
|
|
511
513
|
return metadata;
|
|
512
514
|
}
|
|
513
515
|
export function buildNodePromptFromSnapshot(input) {
|
|
514
|
-
const
|
|
516
|
+
const policy = resolveContextPolicy(input.spec);
|
|
517
|
+
const skillNames = policy.resolveSkills(input.spec, input.task);
|
|
515
518
|
const resolvedSkillInstructions = resolveNodeSkillsFromSnapshot(input.snapshot, input.spec, input.task);
|
|
516
519
|
return {
|
|
517
520
|
prompt: buildDagNodePromptEnvelope({
|
|
@@ -520,6 +523,7 @@ export function buildNodePromptFromSnapshot(input) {
|
|
|
520
523
|
upstream: input.upstream,
|
|
521
524
|
resolvedSkills: skillNames,
|
|
522
525
|
resolvedSkillInstructions,
|
|
526
|
+
maxUpstreamChars: policy.resolveMaxUpstreamChars(input.task),
|
|
523
527
|
}),
|
|
524
528
|
resolvedSkills: resolvedSkillInstructions.map(stripPromptText),
|
|
525
529
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { campaignBudgetSchema, } from "../../application/evaluation/budget.js";
|
|
2
3
|
import { assertDagPromptSourceRule } from "./prompt-source.js";
|
|
3
4
|
import { dagRetryPolicySchema } from "./retry-policy.js";
|
|
4
5
|
export const dagComplexitySchema = z.enum(["HIGH", "MED", "LOW"]);
|
|
@@ -105,12 +106,18 @@ export const dagWritePolicySchema = z.enum([
|
|
|
105
106
|
"exclusive",
|
|
106
107
|
"none",
|
|
107
108
|
]);
|
|
109
|
+
export const contextPolicyIdSchema = z.enum([
|
|
110
|
+
"baseline-v1",
|
|
111
|
+
"role-specialized-v1",
|
|
112
|
+
]);
|
|
108
113
|
export const dagDefaultsSchema = z
|
|
109
114
|
.object({
|
|
110
115
|
executor: dagNodeExecutorSchema.optional(),
|
|
111
116
|
model: z.string().optional(),
|
|
112
117
|
piBackend: z.enum(["sdk-first", "cli-only"]).optional(),
|
|
113
118
|
contextProfile: z.string().optional(),
|
|
119
|
+
/** Eval Lab ContextPolicy seam; default baseline-v1 when omitted. */
|
|
120
|
+
contextPolicyId: contextPolicyIdSchema.optional(),
|
|
114
121
|
skills: z.array(z.string()).optional(),
|
|
115
122
|
writePolicy: dagWritePolicySchema.optional(),
|
|
116
123
|
})
|
|
@@ -300,6 +307,8 @@ export const dagEvaluationBindingSchema = z
|
|
|
300
307
|
taskRef: z.string().min(1).optional(),
|
|
301
308
|
})
|
|
302
309
|
.strict();
|
|
310
|
+
/** Eval Lab Campaign Budget (W3.4); enforced by runner ledger when mode=hard. */
|
|
311
|
+
export const dagBudgetSchema = campaignBudgetSchema;
|
|
303
312
|
export const dagSourceBindingSchema = z.object({
|
|
304
313
|
schemaVersion: z.literal(1),
|
|
305
314
|
taskId: z.string().min(1),
|
|
@@ -320,6 +329,8 @@ export const dagSpecSchema = z
|
|
|
320
329
|
title: z.string().min(1),
|
|
321
330
|
runtimeContract: dagRuntimeContractSchema.optional(),
|
|
322
331
|
evaluation: dagEvaluationBindingSchema.optional(),
|
|
332
|
+
/** Optional hard/record-only budget; requires version 3. */
|
|
333
|
+
budget: dagBudgetSchema.optional(),
|
|
323
334
|
sourceBinding: dagSourceBindingSchema.optional(),
|
|
324
335
|
outputLanguage: dagOutputLanguageSchema.optional(),
|
|
325
336
|
objective: z.string().optional(),
|
|
@@ -340,6 +351,13 @@ export const dagSpecSchema = z
|
|
|
340
351
|
path: ["evaluation"],
|
|
341
352
|
});
|
|
342
353
|
}
|
|
354
|
+
if (spec.budget && spec.version !== 3) {
|
|
355
|
+
ctx.addIssue({
|
|
356
|
+
code: z.ZodIssueCode.custom,
|
|
357
|
+
message: "budget requires DagSpec version 3",
|
|
358
|
+
path: ["budget"],
|
|
359
|
+
});
|
|
360
|
+
}
|
|
343
361
|
if (spec.runtimeContract && spec.version !== 3) {
|
|
344
362
|
ctx.addIssue({
|
|
345
363
|
code: z.ZodIssueCode.custom,
|
package/docs/README.md
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
- `design/archive/2026-07-14-loop-agent-self-update-notifier.md` — loop-agent CLI 自更新提醒设计(已实现;历史设计说明)
|
|
14
14
|
- `development-principles.md` — 仓库开发原则
|
|
15
|
+
- `github-collaboration.md` — 内部研发人员的轻量 GitHub 协作指南:短分支、简短 PR、CI 与 Squash Merge
|
|
15
16
|
- `architecture/runtime-boundaries.md` — runtime 层边界与依赖方向
|
|
16
17
|
- `architecture/README.md` — 架构文档目录索引与阅读路径
|
|
17
18
|
- `architecture/system-overview.md` — loop-agent / agent-worker / 治理层 / 外部系统全景
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
"harness.json",
|
|
9
9
|
"bin/loop-agent.js",
|
|
10
10
|
"docs/README.md",
|
|
11
|
+
"docs/harness-methodology-tdd.md",
|
|
12
|
+
"docs/harness-methodology-verification.md",
|
|
13
|
+
"docs/harness-methodology-debugging.md",
|
|
11
14
|
"docs/init-surface.manifest.json",
|
|
12
15
|
"docs/architecture/runtime-boundaries.md",
|
|
13
16
|
"docs/architecture/README.md",
|
|
@@ -36,18 +39,13 @@
|
|
|
36
39
|
"docs/templates/product-line/test-plan.md",
|
|
37
40
|
"docs/templates/product-line/task-graph.yaml",
|
|
38
41
|
"docs/templates/product-line/task.yaml",
|
|
42
|
+
"docs/templates/product-line/feature.yaml",
|
|
39
43
|
"docs/templates/product-line/closeout.yaml",
|
|
40
44
|
"docs/templates/product-line/links.md",
|
|
41
|
-
"scripts/check-product-line-docs.sh",
|
|
42
|
-
"scripts/check-task-pool-root.sh",
|
|
43
45
|
"scripts/kb-bootstrap-init-skeleton.sh",
|
|
44
|
-
"scripts/kb-query.sh",
|
|
45
46
|
"scripts/kb-query.mjs",
|
|
46
|
-
"scripts/kb-graph-materialize.sh",
|
|
47
47
|
"scripts/kb-graph-materialize.mjs",
|
|
48
|
-
"scripts/kb-graph-promote.sh",
|
|
49
48
|
"scripts/kb-graph-promote.mjs",
|
|
50
|
-
"scripts/kb-graph-incremental-prepare.sh",
|
|
51
49
|
"scripts/kb-graph-incremental-prepare.mjs",
|
|
52
50
|
"docs/templates/agent-dag.schema.json",
|
|
53
51
|
"examples/example-dag.json",
|
|
@@ -109,6 +107,7 @@
|
|
|
109
107
|
"docs/templates/product-line/test-plan.md",
|
|
110
108
|
"docs/templates/product-line/task-graph.yaml",
|
|
111
109
|
"docs/templates/product-line/task.yaml",
|
|
110
|
+
"docs/templates/product-line/feature.yaml",
|
|
112
111
|
"docs/templates/product-line/closeout.yaml",
|
|
113
112
|
"docs/templates/product-line/links.md",
|
|
114
113
|
"scripts/check-skill-entry.sh",
|
|
@@ -178,10 +177,11 @@
|
|
|
178
177
|
"docs/templates/product-line/test-plan.md": "copied",
|
|
179
178
|
"docs/templates/product-line/task-graph.yaml": "copied",
|
|
180
179
|
"docs/templates/product-line/task.yaml": "copied",
|
|
180
|
+
"docs/templates/product-line/feature.yaml": "copied",
|
|
181
181
|
"docs/templates/product-line/closeout.yaml": "copied",
|
|
182
182
|
"docs/templates/product-line/links.md": "copied",
|
|
183
183
|
"scripts/check-skill-entry.sh": "generated",
|
|
184
|
-
"scripts/check-product-line-docs.sh": "
|
|
184
|
+
"scripts/check-product-line-docs.sh": "generated",
|
|
185
185
|
"scripts/check-repo.sh": "generated",
|
|
186
186
|
"scripts/ci-governance.sh": "generated",
|
|
187
187
|
"scripts/ci-tests.sh": "generated",
|
|
@@ -90,4 +90,3 @@ Add focused tests and real init/update/browser/CLI smokes required by the merged
|
|
|
90
90
|
- Conflict files: `<paths>`.
|
|
91
91
|
- First tests to run if a feature appears missing: `<commands>`.
|
|
92
92
|
- Safe rollback strategy: prefer `git revert -m 1 <merge-commit>` after review; do not use destructive reset on shared work.
|
|
93
|
-
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"campaignId": "camp-dogfood-context-policy-ab",
|
|
4
|
+
"createdAt": "2026-07-19T00:00:00.000Z",
|
|
5
|
+
"status": "prepared",
|
|
6
|
+
"policyVersion": "promotion-policy-v1",
|
|
7
|
+
"incumbentCandidateId": "REPLACE_INCUMBENT_CANDIDATE_ID",
|
|
8
|
+
"challengerCandidateIds": ["REPLACE_CHALLENGER_CANDIDATE_ID"],
|
|
9
|
+
"corpusId": "corpus-dogfood-v0",
|
|
10
|
+
"corpusHash": "REPLACE_AFTER_CORPUS_REGISTER",
|
|
11
|
+
"privateVerifierId": "pv-dogfood-v0",
|
|
12
|
+
"privateVerifierManifestHash": "REPLACE_AFTER_PRIVATE_VERIFIER_REGISTER",
|
|
13
|
+
"budget": {
|
|
14
|
+
"schemaVersion": 1,
|
|
15
|
+
"mode": "hard",
|
|
16
|
+
"limits": {
|
|
17
|
+
"maxExecutorCalls": 500,
|
|
18
|
+
"maxWallTimeMs": 86400000,
|
|
19
|
+
"maxRepairPasses": 50,
|
|
20
|
+
"maxConcurrency": 2
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"notes": "M4 dogfood: single-variable Context Policy A/B. Replace candidate/corpus/privateVerifier hashes after register. held_out cells are promotion_gate_only."
|
|
24
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"campaignId": "campaign-dogfood-v0",
|
|
4
|
+
"challengerCandidateId": "cand-challenger-example",
|
|
5
|
+
"cells": [
|
|
6
|
+
{
|
|
7
|
+
"candidateId": "cand-baseline-example",
|
|
8
|
+
"taskRef": "synthetic/private-1",
|
|
9
|
+
"seed": 1,
|
|
10
|
+
"split": "private",
|
|
11
|
+
"verifyPassed": false
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"candidateId": "cand-challenger-example",
|
|
15
|
+
"taskRef": "synthetic/private-1",
|
|
16
|
+
"seed": 1,
|
|
17
|
+
"split": "private",
|
|
18
|
+
"verifyPassed": true
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"candidateId": "cand-baseline-example",
|
|
22
|
+
"taskRef": "synthetic/heldout-1",
|
|
23
|
+
"seed": 1,
|
|
24
|
+
"split": "held_out",
|
|
25
|
+
"verifyPassed": true
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"candidateId": "cand-challenger-example",
|
|
29
|
+
"taskRef": "synthetic/heldout-1",
|
|
30
|
+
"seed": 1,
|
|
31
|
+
"split": "held_out",
|
|
32
|
+
"verifyPassed": true
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"diffs": [],
|
|
36
|
+
"verifyCommands": {
|
|
37
|
+
"incumbent": ["npm test", "npm run typecheck"],
|
|
38
|
+
"challenger": ["npm test", "npm run typecheck"]
|
|
39
|
+
},
|
|
40
|
+
"budgetStatus": {
|
|
41
|
+
"incumbent": "ok",
|
|
42
|
+
"challenger": "ok"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"policyId": "baseline-v1",
|
|
4
|
+
"description": "Current DAG context assembly: shared upstream char budget, role skill defaults, learned patterns only for implementer.",
|
|
5
|
+
"knobs": {
|
|
6
|
+
"maxUpstreamCharsByRole": {
|
|
7
|
+
"default": 2000
|
|
8
|
+
},
|
|
9
|
+
"includeLearnedPatternsRoles": ["implementer"],
|
|
10
|
+
"perSkillMaxCharsByRole": {
|
|
11
|
+
"default": 3000
|
|
12
|
+
},
|
|
13
|
+
"totalMaxCharsByRole": {
|
|
14
|
+
"default": 12000
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"policyId": "role-specialized-v1",
|
|
4
|
+
"description": "Role-specialized upstream and skill-instruction budgets; skill names still resolve via baseline merge order.",
|
|
5
|
+
"knobs": {
|
|
6
|
+
"maxUpstreamCharsByRole": {
|
|
7
|
+
"default": 2000,
|
|
8
|
+
"scout": 1200,
|
|
9
|
+
"reviewer": 1200,
|
|
10
|
+
"implementer": 3000,
|
|
11
|
+
"verifier": 1600,
|
|
12
|
+
"closeout": 1600,
|
|
13
|
+
"planner": 2000,
|
|
14
|
+
"supervisor": 2000
|
|
15
|
+
},
|
|
16
|
+
"includeLearnedPatternsRoles": ["implementer", "closeout"],
|
|
17
|
+
"perSkillMaxCharsByRole": {
|
|
18
|
+
"default": 3000,
|
|
19
|
+
"scout": 2500,
|
|
20
|
+
"implementer": 3500
|
|
21
|
+
},
|
|
22
|
+
"totalMaxCharsByRole": {
|
|
23
|
+
"default": 12000,
|
|
24
|
+
"scout": 10000,
|
|
25
|
+
"implementer": 14000
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"corpusId": "corpus-dogfood-v0",
|
|
4
|
+
"createdAt": "2026-07-19T00:00:00.000Z",
|
|
5
|
+
"description": "W1.5 initial dogfood corpus contract: 15 heterogeneous tasks with public/private/held_out splits. Contract-only; not live campaign evidence.",
|
|
6
|
+
"tasks": [
|
|
7
|
+
{
|
|
8
|
+
"taskRef": "features/F-2026-001/login-flow",
|
|
9
|
+
"class": "fullstack",
|
|
10
|
+
"split": "public",
|
|
11
|
+
"seeds": [1, 2, 3],
|
|
12
|
+
"featureId": "F-2026-001",
|
|
13
|
+
"description": "Dogfood login Feature packet (public feedback allowed)."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"taskRef": "features/F-2026-002/profile-filter",
|
|
17
|
+
"class": "implement",
|
|
18
|
+
"split": "public",
|
|
19
|
+
"seeds": [1, 2],
|
|
20
|
+
"featureId": "F-2026-002",
|
|
21
|
+
"description": "Profile filter implementation path."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"taskRef": "features/F-2026-005/welcome-banner",
|
|
25
|
+
"class": "fullstack",
|
|
26
|
+
"split": "public",
|
|
27
|
+
"seeds": [1, 2, 3],
|
|
28
|
+
"featureId": "F-2026-005",
|
|
29
|
+
"description": "fullstack-v1 welcome sample (public)."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"taskRef": "synthetic/docs-agents-converge",
|
|
33
|
+
"class": "docs",
|
|
34
|
+
"split": "public",
|
|
35
|
+
"seeds": [1, 2],
|
|
36
|
+
"description": "Docs/AGENTS converge style task (synthetic contract slot)."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"taskRef": "synthetic/docs-changelog-summary",
|
|
40
|
+
"class": "docs",
|
|
41
|
+
"split": "public",
|
|
42
|
+
"seeds": [1, 2, 3],
|
|
43
|
+
"description": "Changelog-style documentation task."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"taskRef": "synthetic/frontend-welcome-render",
|
|
47
|
+
"class": "frontend",
|
|
48
|
+
"split": "public",
|
|
49
|
+
"seeds": [1, 2],
|
|
50
|
+
"description": "Frontend render/accessibility contract slot."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"taskRef": "features/F-2026-003/empty-profile-regression",
|
|
54
|
+
"class": "bugfix",
|
|
55
|
+
"split": "private",
|
|
56
|
+
"seeds": [1, 2, 3],
|
|
57
|
+
"featureId": "F-2026-003",
|
|
58
|
+
"description": "Empty profile id/displayName regression (private selection)."
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"taskRef": "features/F-2026-004/versioned-bootstrap",
|
|
62
|
+
"class": "bootstrap",
|
|
63
|
+
"split": "private",
|
|
64
|
+
"seeds": [1, 2],
|
|
65
|
+
"featureId": "F-2026-004",
|
|
66
|
+
"description": "Versioned self-host bootstrap canary (private)."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"taskRef": "synthetic/backend-test-welcome-api",
|
|
70
|
+
"class": "backend-test",
|
|
71
|
+
"split": "private",
|
|
72
|
+
"seeds": [1, 2, 3],
|
|
73
|
+
"description": "Backend API test workflow contract slot."
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"taskRef": "synthetic/refactor-outcome-tokens",
|
|
77
|
+
"class": "refactor",
|
|
78
|
+
"split": "private",
|
|
79
|
+
"seeds": [1, 2],
|
|
80
|
+
"description": "Bounded refactor around evidence token naming."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"taskRef": "synthetic/implement-ready-planner-gate",
|
|
84
|
+
"class": "implement",
|
|
85
|
+
"split": "private",
|
|
86
|
+
"seeds": [1, 2, 3],
|
|
87
|
+
"description": "Ready Planner artifact-gate implementation slot."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"taskRef": "synthetic/heldout-docs-handoff",
|
|
91
|
+
"class": "docs",
|
|
92
|
+
"split": "held_out",
|
|
93
|
+
"seeds": [1, 2],
|
|
94
|
+
"description": "Held-out docs/handoff generalization slot; never used for candidate generation."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"taskRef": "synthetic/heldout-bugfix-gate",
|
|
98
|
+
"class": "bugfix",
|
|
99
|
+
"split": "held_out",
|
|
100
|
+
"seeds": [1, 2, 3],
|
|
101
|
+
"description": "Held-out bugfix gate; promotion-only signal."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"taskRef": "synthetic/heldout-fullstack-smoke",
|
|
105
|
+
"class": "fullstack",
|
|
106
|
+
"split": "held_out",
|
|
107
|
+
"seeds": [1, 2],
|
|
108
|
+
"description": "Held-out fullstack smoke; not for public feedback."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"taskRef": "synthetic/heldout-bootstrap-slot",
|
|
112
|
+
"class": "bootstrap",
|
|
113
|
+
"split": "held_out",
|
|
114
|
+
"seeds": [1, 2, 3],
|
|
115
|
+
"description": "Held-out bootstrap identity/slot discipline."
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"title": "eval-matrix-cell-dry-run",
|
|
4
|
+
"runtimeContract": {
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"agentRuntime": "pi-only",
|
|
7
|
+
"repairWriterProtocol": "explicit-node-v1"
|
|
8
|
+
},
|
|
9
|
+
"tasks": [
|
|
10
|
+
{
|
|
11
|
+
"id": "matrix-cell-static",
|
|
12
|
+
"depends_on": [],
|
|
13
|
+
"complexity": "LOW",
|
|
14
|
+
"executor": "static",
|
|
15
|
+
"subtask_prompt": "Matrix cell placeholder; evaluation binding is injected by campaign matrix.",
|
|
16
|
+
"static": {
|
|
17
|
+
"resultMarkdown": "matrix cell dry-run placeholder"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"privateVerifierId": "pv-dogfood-v0",
|
|
4
|
+
"createdAt": "2026-07-19T00:00:00.000Z",
|
|
5
|
+
"description": "M4 dogfood private verifier. Controller-only; never enter candidate contentRefs/prompts/skill snapshots.",
|
|
6
|
+
"checks": [
|
|
7
|
+
{
|
|
8
|
+
"checkId": "node-version-ok",
|
|
9
|
+
"kind": "shell",
|
|
10
|
+
"command": ["node", "-e", "process.exit(process.versions.node ? 0 : 1)"],
|
|
11
|
+
"cwd": ".",
|
|
12
|
+
"timeoutMs": 15000,
|
|
13
|
+
"expectedExitCode": 0
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
- Treat `constraints.allowed_paths` and `constraints.forbidden_paths` as machine-enforced write boundaries; review DAG writer `writeSet` before execution.
|
|
5
5
|
- Advance only tasks whose dependencies are complete. Preserve failed run records; retries receive new worker run IDs.
|
|
6
6
|
- A Ready task must have acceptance references, non-empty allowed/forbidden paths, and deterministic verification commands.
|
|
7
|
+
- For `fullstack-v1` packets (`feature.yaml`), every TaskSpec should declare `execution.workflow`; required ACs need implementation/verification refs, `required_evidence`, and integration policy. Parallel writers must not share overlapping `allowed_paths`.
|
|
7
8
|
- QA records an independent verdict and evidence. Do not write `status: success` closeout until `qa_verdict: pass` and non-empty `qa_evidence` exist.
|
|
8
9
|
- Human gates remain human decisions. Record owner, time, reason, evidence, and follow-up without rewriting failed history.
|
|
@@ -7,3 +7,20 @@ agent-worker task validate-feature <feature-dir>
|
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
The validator checks acceptance ID uniqueness, task references and dependencies, cycles, TaskSpec/path/verification completeness, and QA evidence before a successful closeout.
|
|
10
|
+
|
|
11
|
+
## Profiles
|
|
12
|
+
|
|
13
|
+
| Profile | How to declare | Behavior |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `generic`(默认) | 省略 `feature.yaml`,或 `profile: generic` | 保持 legacy 校验;不强制 `execution.workflow` / 双覆盖 AC |
|
|
16
|
+
| `fullstack-v1` | `feature.yaml` 中 `profile: fullstack-v1` 并声明 `scope` | 强制显式 workflow、required AC 双覆盖、frontend-test 依赖、writer writeSet 串行等结构门禁 |
|
|
17
|
+
|
|
18
|
+
端到端全栈 dogfood 样板见仓库 `features/F-2026-005/`(欢迎语链路:契约 → BE/FE 实现 → backend-test / frontend-test → Final Verification)。
|
|
19
|
+
|
|
20
|
+
## Minimal files
|
|
21
|
+
|
|
22
|
+
- `feature.yaml`(可选;fullstack 必填)
|
|
23
|
+
- `requirement.md` / `design.md` / `test-plan.md`
|
|
24
|
+
- `acceptance.yaml`
|
|
25
|
+
- `tasks/task-graph.yaml` + `tasks/*.yaml`
|
|
26
|
+
- `links.md` / `closeout.yaml`(按交付阶段)
|
|
@@ -12,3 +12,12 @@ acceptance:
|
|
|
12
12
|
expected_task_refs:
|
|
13
13
|
- BE-001
|
|
14
14
|
- QA-001
|
|
15
|
+
# fullstack-v1 required AC fields (optional for generic packets):
|
|
16
|
+
# implementation_task_refs:
|
|
17
|
+
# - BE-001
|
|
18
|
+
# verification_task_refs:
|
|
19
|
+
# - BE-TEST-001
|
|
20
|
+
# required_evidence:
|
|
21
|
+
# - backend-test-result-v1
|
|
22
|
+
# - shell_verification
|
|
23
|
+
# integration: real-required
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
schema_version: 1
|
|
2
|
+
feature_id: F-YYYY-NNN
|
|
3
|
+
# generic = legacy/simple packet (default when this file is absent)
|
|
4
|
+
# fullstack-v1 = enable structural gates for explicit workflows + dual-coverage ACs
|
|
5
|
+
profile: generic
|
|
6
|
+
# Uncomment and set true flags when profile is fullstack-v1:
|
|
7
|
+
# scope:
|
|
8
|
+
# backend: true
|
|
9
|
+
# frontend: true
|
|
10
|
+
# backendVerification: true
|
|
11
|
+
# frontendVerification: true
|