@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.
Files changed (117) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +98 -11
  3. package/README.md +24 -5
  4. package/dist/application/evaluation/alias.js +184 -0
  5. package/dist/application/evaluation/budget.js +192 -0
  6. package/dist/application/evaluation/campaign-hash.js +47 -0
  7. package/dist/application/evaluation/campaign-matrix.js +372 -0
  8. package/dist/application/evaluation/campaign-scorecard.js +135 -0
  9. package/dist/application/evaluation/campaign.js +370 -0
  10. package/dist/application/evaluation/candidate.js +23 -6
  11. package/dist/application/evaluation/corpus-hash.js +38 -0
  12. package/dist/application/evaluation/corpus.js +56 -0
  13. package/dist/application/evaluation/experiment.js +294 -0
  14. package/dist/application/evaluation/ignition.js +198 -0
  15. package/dist/application/evaluation/integrity-audit.js +162 -0
  16. package/dist/application/evaluation/outer-loop.js +132 -0
  17. package/dist/application/evaluation/pi-cell-executor.js +39 -0
  18. package/dist/application/evaluation/private-verifier.js +46 -0
  19. package/dist/application/evaluation/promotion-policy.js +151 -0
  20. package/dist/application/evaluation/proposer.js +98 -0
  21. package/dist/application/evaluation/types.js +522 -0
  22. package/dist/cli/command-definitions.js +19 -3
  23. package/dist/commands/eval.js +1176 -13
  24. package/dist/commands/init.js +4 -1
  25. package/dist/infrastructure/evaluation/alias-store.js +199 -0
  26. package/dist/infrastructure/evaluation/campaign-store.js +154 -0
  27. package/dist/infrastructure/evaluation/corpus-store.js +181 -0
  28. package/dist/infrastructure/evaluation/experiment-store.js +124 -0
  29. package/dist/infrastructure/evaluation/ignition-store.js +82 -0
  30. package/dist/infrastructure/evaluation/private-verifier-store.js +145 -0
  31. package/dist/infrastructure/evaluation/proposer-store.js +78 -0
  32. package/dist/worker/cli.js +6 -3
  33. package/dist/worker/delivery/final-verification.js +96 -8
  34. package/dist/worker/delivery/package.js +23 -4
  35. package/dist/worker/delivery/verification-bundle.js +521 -0
  36. package/dist/worker/feature/fullstack-validate.js +337 -0
  37. package/dist/worker/feature/profile-schema.js +44 -0
  38. package/dist/worker/feature/ready-plan-projection.js +1 -0
  39. package/dist/worker/feature/reducer.js +2 -0
  40. package/dist/worker/feature/review.js +106 -11
  41. package/dist/worker/materialize/harness-task-materializer.js +5 -0
  42. package/dist/worker/observability/read-model.js +7 -0
  43. package/dist/worker/observe/static/views/task.js +1 -0
  44. package/dist/worker/outcomes/adapters.js +144 -0
  45. package/dist/worker/outcomes/evidence-tokens.js +29 -0
  46. package/dist/worker/outcomes/gate.js +40 -0
  47. package/dist/worker/outcomes/projector.js +185 -0
  48. package/dist/worker/outcomes/registry.js +1 -0
  49. package/dist/worker/outcomes/store.js +131 -0
  50. package/dist/worker/outcomes/types.js +79 -0
  51. package/dist/worker/report/morning-report.js +4 -3
  52. package/dist/worker/run-task/run-task.js +85 -2
  53. package/dist/worker/runner/run-ready.js +32 -1
  54. package/dist/worker/task-graph/acceptance-schema.js +12 -0
  55. package/dist/worker/task-graph/ready-planner.js +131 -0
  56. package/dist/worker/task-graph/task-graph-schema.js +31 -0
  57. package/dist/worker/task-graph/validate.js +44 -4
  58. package/dist/worker/task-spec/schema.js +9 -0
  59. package/dist/worker/task-spec/validate.js +39 -0
  60. package/dist/worker/task-spec/workflow-routing.js +149 -0
  61. package/dist/workflows/dag/budget-enforcement.js +67 -0
  62. package/dist/workflows/dag/context-policy.js +137 -0
  63. package/dist/workflows/dag/init-hybrid.js +27 -11
  64. package/dist/workflows/dag/knowledge-curator.js +3 -0
  65. package/dist/workflows/dag/node-execution.js +11 -4
  66. package/dist/workflows/dag/prompt.js +1 -1
  67. package/dist/workflows/dag/runner.js +43 -16
  68. package/dist/workflows/dag/skill-snapshot.js +11 -7
  69. package/dist/workflows/dag/types.js +18 -0
  70. package/docs/README.md +1 -0
  71. package/docs/init-surface.manifest.json +7 -7
  72. package/docs/templates/branch-merge-report.md +0 -1
  73. package/docs/templates/evaluation/campaign-budget-v1.json +12 -0
  74. package/docs/templates/evaluation/campaign-dogfood-v0.json +24 -0
  75. package/docs/templates/evaluation/campaign-evidence-v1.json +44 -0
  76. package/docs/templates/evaluation/context-policy-baseline-v1.json +17 -0
  77. package/docs/templates/evaluation/context-policy-role-specialized-v1.json +28 -0
  78. package/docs/templates/evaluation/corpus-dogfood-v0.manifest.json +118 -0
  79. package/docs/templates/evaluation/matrix-dag-dry-run-v1.json +21 -0
  80. package/docs/templates/evaluation/matrix-fixture-v1.json +10 -0
  81. package/docs/templates/evaluation/private-verifier-dogfood-v0.json +16 -0
  82. package/docs/templates/product-line/AGENTS.md +1 -0
  83. package/docs/templates/product-line/README.md +17 -0
  84. package/docs/templates/product-line/acceptance.yaml +9 -0
  85. package/docs/templates/product-line/feature.yaml +11 -0
  86. package/docs/templates/product-line/task-graph.yaml +8 -0
  87. package/docs/templates/product-line/task.yaml +4 -0
  88. package/package.json +6 -16
  89. package/skills/browser-tools/SKILL.md +2 -2
  90. package/skills/frontend-design-review/references/review-checklist.md +27 -45
  91. package/skills/frontend-implementation/references/node-contracts.md +4 -4
  92. package/skills/frontend-review/SKILL.md +3 -1
  93. package/skills/frontend-review/references/review-findings.md +2 -1
  94. package/skills/frontend-verification/SKILL.md +3 -1
  95. package/skills/frontend-verification/references/verification-checklist.md +13 -22
  96. package/skills/loop-agent/references/hybrid-dag.md +1 -1
  97. package/docs/agent-dag-recovery-playbook.md +0 -195
  98. package/docs/agent-dag-runner.md +0 -67
  99. package/docs/cursor-prompt-sidecar.md +0 -36
  100. package/docs/decisions/README.md +0 -18
  101. package/docs/design/README.md +0 -167
  102. package/docs/development-principles.md +0 -73
  103. package/docs/exec-plans/README.md +0 -6
  104. package/docs/exec-plans/active/README.md +0 -13
  105. package/docs/exec-plans/completed/README.md +0 -108
  106. package/docs/feature-workflow.md +0 -414
  107. package/docs/loop-agent-harness.md +0 -142
  108. package/docs/production-readiness.md +0 -96
  109. package/docs/progress/README.md +0 -81
  110. package/docs/reports/README.md +0 -163
  111. package/docs/verification-matrix.md +0 -70
  112. package/scripts/check-product-line-docs.sh +0 -29
  113. package/scripts/check-task-pool-root.sh +0 -32
  114. package/scripts/kb-graph-incremental-prepare.sh +0 -5
  115. package/scripts/kb-graph-materialize.sh +0 -4
  116. package/scripts/kb-graph-promote.sh +0 -4
  117. 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
- return {
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) => executeDagNode({
414
- nodeId,
415
- tasksById,
416
- state,
417
- spec,
418
- cwd,
419
- runDir,
420
- executeNode,
421
- executeDynamicNode,
422
- observer: input.observer,
423
- persistState,
424
- onPause,
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 { DEFAULT_SKILL_INSTRUCTION_MAX_CHARS, DEFAULT_SKILL_INSTRUCTION_TOTAL_MAX_CHARS, resolveDagSkillInstructions, } from "./skill-instructions.js";
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: [...resolveDagNodeSkills(spec, task)],
122
- includeLearnedPatterns: task.role === "implementer",
123
- perSkillMaxChars: DEFAULT_SKILL_INSTRUCTION_MAX_CHARS,
124
- totalMaxChars: DEFAULT_SKILL_INSTRUCTION_TOTAL_MAX_CHARS,
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 skillNames = resolveDagNodeSkills(input.spec, input.task);
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": "copied",
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,12 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "mode": "hard",
4
+ "limits": {
5
+ "maxTokens": 2000000,
6
+ "maxWallTimeMs": 86400000,
7
+ "maxExecutorCalls": 500,
8
+ "maxRepairPasses": 50,
9
+ "maxConcurrency": 2,
10
+ "maxContextChars": 120000
11
+ }
12
+ }
@@ -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,10 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "defaultVerifyPassed": false,
4
+ "results": [
5
+ {
6
+ "split": "public",
7
+ "verifyPassed": true
8
+ }
9
+ ]
10
+ }
@@ -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