kc-beta 0.7.3 → 0.8.1

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 (109) hide show
  1. package/README.md +57 -4
  2. package/bin/kc-beta.js +20 -6
  3. package/package.json +3 -2
  4. package/src/agent/engine.js +493 -132
  5. package/src/agent/pipelines/_advance-hints.js +92 -0
  6. package/src/agent/pipelines/_milestone-derive.js +387 -17
  7. package/src/agent/pipelines/initializer.js +4 -1
  8. package/src/agent/pipelines/skill-authoring.js +30 -1
  9. package/src/agent/skill-loader.js +433 -111
  10. package/src/agent/tools/agent-tool.js +2 -2
  11. package/src/agent/tools/consult-skill.js +127 -0
  12. package/src/agent/tools/copy-to-workspace.js +4 -3
  13. package/src/agent/tools/dashboard-render.js +48 -1
  14. package/src/agent/tools/document-parse.js +31 -2
  15. package/src/agent/tools/phase-advance.js +17 -13
  16. package/src/agent/tools/release.js +378 -8
  17. package/src/agent/tools/sandbox-exec.js +65 -8
  18. package/src/agent/tools/worker-llm-call.js +95 -15
  19. package/src/agent/tools/workspace-file.js +7 -7
  20. package/src/agent/workspace.js +25 -4
  21. package/src/cli/components.js +4 -1
  22. package/src/cli/index.js +97 -1
  23. package/src/config.js +20 -3
  24. package/src/marathon/driver.js +217 -0
  25. package/src/marathon/prompts.js +93 -0
  26. package/template/.env.template +16 -0
  27. package/template/AGENT.md +182 -7
  28. package/template/skills/en/{meta-meta/auto-model-selection → auto-model-selection}/SKILL.md +1 -0
  29. package/template/skills/en/{meta-meta/bootstrap-workspace → bootstrap-workspace}/SKILL.md +15 -0
  30. package/template/skills/{zh/meta → en}/compliance-judgment/SKILL.md +1 -0
  31. package/template/skills/en/{meta/confidence-system → confidence-system}/SKILL.md +1 -0
  32. package/template/skills/en/{meta/corner-case-management → corner-case-management}/SKILL.md +1 -0
  33. package/template/skills/en/{meta/cross-document-verification → cross-document-verification}/SKILL.md +1 -0
  34. package/template/skills/en/{meta-meta/dashboard-reporting → dashboard-reporting}/SKILL.md +1 -0
  35. package/template/skills/en/{meta/data-sensibility → data-sensibility}/SKILL.md +1 -0
  36. package/template/skills/{zh/meta → en}/document-chunking/SKILL.md +1 -0
  37. package/template/skills/en/{meta/document-parsing → document-parsing}/SKILL.md +1 -0
  38. package/template/skills/{zh/meta → en}/entity-extraction/SKILL.md +1 -0
  39. package/template/skills/en/{meta-meta/evolution-loop → evolution-loop}/SKILL.md +1 -0
  40. package/template/skills/en/{meta-meta/pdf-review-dashboard → pdf-review-dashboard}/SKILL.md +1 -0
  41. package/template/skills/en/{meta-meta/quality-control → quality-control}/SKILL.md +10 -0
  42. package/template/skills/en/{meta-meta/rule-extraction → rule-extraction}/SKILL.md +1 -0
  43. package/template/skills/en/{meta-meta/rule-graph → rule-graph}/SKILL.md +1 -0
  44. package/template/skills/en/{meta-meta/skill-authoring → skill-authoring}/SKILL.md +40 -0
  45. package/template/skills/en/skill-creator/SKILL.md +2 -1
  46. package/template/skills/en/{meta-meta/skill-to-workflow → skill-to-workflow}/SKILL.md +58 -4
  47. package/template/skills/en/{meta-meta/task-decomposition → task-decomposition}/SKILL.md +1 -0
  48. package/template/skills/en/{meta/tree-processing → tree-processing}/SKILL.md +1 -0
  49. package/template/skills/en/{meta-meta/version-control → version-control}/SKILL.md +1 -0
  50. package/template/skills/en/{meta-meta/work-decomposition → work-decomposition}/SKILL.md +51 -6
  51. package/template/skills/phase_skills.yaml +112 -0
  52. package/template/skills/zh/{meta-meta/auto-model-selection → auto-model-selection}/SKILL.md +1 -0
  53. package/template/skills/zh/{meta-meta/bootstrap-workspace → bootstrap-workspace}/SKILL.md +15 -0
  54. package/template/skills/zh/compliance-judgment/SKILL.md +83 -0
  55. package/template/skills/zh/{meta/confidence-system → confidence-system}/SKILL.md +1 -0
  56. package/template/skills/zh/{meta/corner-case-management → corner-case-management}/SKILL.md +1 -0
  57. package/template/skills/zh/{meta/cross-document-verification → cross-document-verification}/SKILL.md +1 -0
  58. package/template/skills/zh/{meta-meta/dashboard-reporting → dashboard-reporting}/SKILL.md +1 -0
  59. package/template/skills/zh/{meta/data-sensibility → data-sensibility}/SKILL.md +1 -0
  60. package/template/skills/zh/document-chunking/SKILL.md +40 -0
  61. package/template/skills/zh/document-parsing/SKILL.md +102 -0
  62. package/template/skills/zh/entity-extraction/SKILL.md +121 -0
  63. package/template/skills/zh/{meta-meta/evolution-loop → evolution-loop}/SKILL.md +1 -0
  64. package/template/skills/zh/{meta-meta/pdf-review-dashboard → pdf-review-dashboard}/SKILL.md +1 -0
  65. package/template/skills/zh/{meta-meta/quality-control → quality-control}/SKILL.md +10 -0
  66. package/template/skills/zh/{meta-meta/rule-extraction → rule-extraction}/SKILL.md +1 -0
  67. package/template/skills/zh/{meta-meta/rule-graph → rule-graph}/SKILL.md +1 -0
  68. package/template/skills/zh/{meta-meta/skill-authoring → skill-authoring}/SKILL.md +40 -0
  69. package/template/skills/zh/skill-creator/SKILL.md +205 -200
  70. package/template/skills/zh/skill-to-workflow/SKILL.md +243 -0
  71. package/template/skills/zh/{meta-meta/task-decomposition → task-decomposition}/SKILL.md +1 -0
  72. package/template/skills/zh/tree-processing/SKILL.md +126 -0
  73. package/template/skills/zh/{meta-meta/version-control → version-control}/SKILL.md +1 -0
  74. package/template/skills/zh/{meta-meta/work-decomposition → work-decomposition}/SKILL.md +49 -4
  75. package/template/workflows/common/llm_client.py +168 -0
  76. package/template/workflows/common/utils.py +132 -0
  77. package/template/CLAUDE.md +0 -150
  78. package/template/skills/en/meta/compliance-judgment/SKILL.md +0 -82
  79. package/template/skills/en/meta/document-chunking/SKILL.md +0 -32
  80. package/template/skills/en/meta/entity-extraction/SKILL.md +0 -120
  81. package/template/skills/zh/meta/document-parsing/SKILL.md +0 -101
  82. package/template/skills/zh/meta/tree-processing/SKILL.md +0 -121
  83. package/template/skills/zh/meta-meta/skill-to-workflow/SKILL.md +0 -188
  84. /package/template/skills/en/{meta/compliance-judgment → compliance-judgment}/references/output-format.md +0 -0
  85. /package/template/skills/en/{meta/cross-document-verification → cross-document-verification}/references/contradiction-taxonomy.md +0 -0
  86. /package/template/skills/en/{meta-meta/dashboard-reporting → dashboard-reporting}/scripts/generate_dashboard.py +0 -0
  87. /package/template/skills/en/{meta/document-parsing → document-parsing}/references/parser-catalog.md +0 -0
  88. /package/template/skills/en/{meta-meta/evolution-loop → evolution-loop}/references/convergence-guide.md +0 -0
  89. /package/template/skills/en/{meta-meta/pdf-review-dashboard → pdf-review-dashboard}/scripts/generate_review.js +0 -0
  90. /package/template/skills/en/{meta-meta/quality-control → quality-control}/references/qa-layers.md +0 -0
  91. /package/template/skills/en/{meta-meta/quality-control → quality-control}/references/sampling-strategies.md +0 -0
  92. /package/template/skills/en/{meta-meta/rule-extraction → rule-extraction}/references/chunking-strategies.md +0 -0
  93. /package/template/skills/en/{meta-meta/skill-authoring → skill-authoring}/references/skill-format-spec.md +0 -0
  94. /package/template/skills/en/{meta-meta/skill-to-workflow → skill-to-workflow}/references/worker-llm-catalog.md +0 -0
  95. /package/template/skills/en/{meta-meta/task-decomposition → task-decomposition}/references/decision-matrix.md +0 -0
  96. /package/template/skills/en/{meta-meta/version-control → version-control}/references/trace-id-spec.md +0 -0
  97. /package/template/skills/zh/{meta/compliance-judgment → compliance-judgment}/references/output-format.md +0 -0
  98. /package/template/skills/zh/{meta/cross-document-verification → cross-document-verification}/references/contradiction-taxonomy.md +0 -0
  99. /package/template/skills/zh/{meta-meta/dashboard-reporting → dashboard-reporting}/scripts/generate_dashboard.py +0 -0
  100. /package/template/skills/zh/{meta/document-parsing → document-parsing}/references/parser-catalog.md +0 -0
  101. /package/template/skills/zh/{meta-meta/evolution-loop → evolution-loop}/references/convergence-guide.md +0 -0
  102. /package/template/skills/zh/{meta-meta/pdf-review-dashboard → pdf-review-dashboard}/scripts/generate_review.js +0 -0
  103. /package/template/skills/zh/{meta-meta/quality-control → quality-control}/references/qa-layers.md +0 -0
  104. /package/template/skills/zh/{meta-meta/quality-control → quality-control}/references/sampling-strategies.md +0 -0
  105. /package/template/skills/zh/{meta-meta/rule-extraction → rule-extraction}/references/chunking-strategies.md +0 -0
  106. /package/template/skills/zh/{meta-meta/skill-authoring → skill-authoring}/references/skill-format-spec.md +0 -0
  107. /package/template/skills/zh/{meta-meta/skill-to-workflow → skill-to-workflow}/references/worker-llm-catalog.md +0 -0
  108. /package/template/skills/zh/{meta-meta/task-decomposition → task-decomposition}/references/decision-matrix.md +0 -0
  109. /package/template/skills/zh/{meta-meta/version-control → version-control}/references/trace-id-spec.md +0 -0
@@ -0,0 +1,127 @@
1
+ import { BaseTool, ToolResult } from "./base.js";
2
+
3
+ /**
4
+ * v0.7.5: load a methodology skill's body into the agent's conversation
5
+ * history as a tool result. Pairs with the always-loaded body injection
6
+ * in SkillLoader.formatForContext — that handles the 1-2 architecturally-
7
+ * required skills per phase; consult_skill handles the rest on demand.
8
+ *
9
+ * Validation:
10
+ * - Skill name must be in the current phase's available set (per
11
+ * template/skills/phase_skills.yaml).
12
+ * - Already-always-loaded skills return a hint pointing the agent at the
13
+ * system prompt (don't double-load).
14
+ * - Missing bodies return an error result.
15
+ *
16
+ * Emits `skill_invoked` event with proper skill name on success — replaces
17
+ * the older path-matching regex at engine.js:1297-1313 that produced
18
+ * "(unknown)" spam from rule_skills/<id>/SKILL.md writes.
19
+ */
20
+ export class ConsultSkillTool extends BaseTool {
21
+ /**
22
+ * @param {import('../workspace.js').Workspace} workspace
23
+ * @param {import('../skill-loader.js').SkillLoader} skillLoader
24
+ * @param {() => string} getCurrentPhase — returns the engine's current phase
25
+ * @param {import('../event-log.js').EventLog} [eventLog] — for skill_invoked emission
26
+ */
27
+ constructor(workspace, skillLoader, getCurrentPhase, eventLog) {
28
+ super();
29
+ this._workspace = workspace;
30
+ this._skillLoader = skillLoader;
31
+ this._getCurrentPhase = getCurrentPhase;
32
+ this._eventLog = eventLog;
33
+ }
34
+
35
+ get name() { return "consult_skill"; }
36
+
37
+ get description() {
38
+ return (
39
+ "Load the full body of a methodology skill into your context for the " +
40
+ "current turn. Use when the description tease in the system prompt's " +
41
+ "'Available Methodology Skills' section isn't enough detail to proceed. " +
42
+ "The body lands in your conversation history; subsequent turns can " +
43
+ "reference it via context, or you can re-consult if it ages out. " +
44
+ "Skills already in the 'Loaded Into Your Context' section don't need " +
45
+ "consulting — they're already in your prompt."
46
+ );
47
+ }
48
+
49
+ get inputSchema() {
50
+ return {
51
+ type: "object",
52
+ properties: {
53
+ name: {
54
+ type: "string",
55
+ description: "Skill name as listed in the system prompt (e.g., 'work-decomposition', 'evolution-loop').",
56
+ },
57
+ },
58
+ required: ["name"],
59
+ };
60
+ }
61
+
62
+ async execute(input) {
63
+ const name = (input?.name || "").trim();
64
+ if (!name) return new ToolResult("name required (e.g. consult_skill({name: 'work-decomposition'}))", true);
65
+
66
+ // v0.8 P0-A: defensive null-check. v0.7.5 shipped with an init-order bug
67
+ // where ConsultSkillTool received undefined skillLoader and threw
68
+ // "Cannot read properties of undefined (reading 'getPhaseSkillSet')"
69
+ // on every invocation (资管 audit § 9.1, 5/5 failure rate). The init-order
70
+ // fix is in engine.js:238; this guard prevents an uncaught exception if
71
+ // the bug recurs from any future constructor reorder.
72
+ if (!this._skillLoader || typeof this._skillLoader.getPhaseSkillSet !== "function") {
73
+ return new ToolResult(
74
+ "consult_skill is misconfigured: skillLoader unavailable. This is an engine-side bug — " +
75
+ "surface to the developer user. The agent should fall back to reading skill bodies " +
76
+ "directly from <workspace>/skills/<name>/SKILL.md or the system prompt's always-loaded section.",
77
+ true,
78
+ );
79
+ }
80
+
81
+ const phase = this._getCurrentPhase ? this._getCurrentPhase() : null;
82
+ const { alwaysLoaded, available } = this._skillLoader.getPhaseSkillSet(phase);
83
+
84
+ const alwaysSet = new Set(alwaysLoaded);
85
+ const availableSet = new Set(available);
86
+
87
+ if (alwaysSet.has(name)) {
88
+ return new ToolResult(
89
+ `Skill '${name}' is already always-loaded in your system prompt for phase '${phase}'. ` +
90
+ `Re-read the system prompt's 'Methodology Skills — Loaded Into Your Context' section ` +
91
+ `— the body is there. No separate consult needed.`,
92
+ );
93
+ }
94
+
95
+ if (!availableSet.has(name)) {
96
+ const sorted = [...availableSet].sort();
97
+ return new ToolResult(
98
+ `Skill '${name}' is not available in phase '${phase}'. ` +
99
+ `Available for this phase: ${sorted.join(", ")}. ` +
100
+ `If you genuinely need this skill, either advance/retreat to a phase ` +
101
+ `where it's available, or check the spelling.`,
102
+ true,
103
+ );
104
+ }
105
+
106
+ const body = this._skillLoader.loadSkillBody(name);
107
+ if (!body) {
108
+ return new ToolResult(
109
+ `Skill '${name}' is declared available for phase '${phase}' but its body could not be loaded. ` +
110
+ `This is an engine/template inconsistency — surface to the developer user.`,
111
+ true,
112
+ );
113
+ }
114
+
115
+ // Emit skill_invoked event with the real skill name (replaces the
116
+ // old path-matching regex that produced "(unknown)" spam).
117
+ try {
118
+ this._eventLog?.append?.("skill_invoked", {
119
+ skill: name,
120
+ via_tool: "consult_skill",
121
+ phase,
122
+ });
123
+ } catch { /* event logging is best-effort */ }
124
+
125
+ return new ToolResult(body);
126
+ }
127
+ }
@@ -114,9 +114,10 @@ export class CopyToWorkspaceTool extends BaseTool {
114
114
  }
115
115
 
116
116
  async _appendManifest(entry) {
117
- // v0.7.3: refs/manifest.json is a shared coordination path — wrap the
118
- // whole read-modify-write under the workspace lock so two parallel
119
- // copy_to_workspace calls (main agent + subagent) don't lose entries.
117
+ // v0.7.4 (re-applied from v0.7.3 G1a): refs/manifest.json is a
118
+ // shared coordination path — wrap the whole read-modify-write
119
+ // under the workspace lock so two parallel copy_to_workspace
120
+ // calls (main agent + subagent) don't lose entries.
120
121
  return await this._workspace.withSharedLockIfApplicable(MANIFEST_REL, () => {
121
122
  const manifestAbs = this._workspace.resolvePath(MANIFEST_REL);
122
123
  fs.mkdirSync(path.dirname(manifestAbs), { recursive: true });
@@ -81,11 +81,57 @@ export class DashboardRenderTool extends BaseTool {
81
81
  metrics.evolution_iterations = fs.readdirSync(evoDir).filter((f) => f.endsWith(".json")).length;
82
82
  }
83
83
 
84
+ // v0.8 P1-G: QC counter now reads from multiple known agent-write
85
+ // locations + counts per-doc reviews. Pre-v0.8 read only output/qc/*.json
86
+ // top-level; 资管 v0.7.5 wrote output/results/production_qc_results.json
87
+ // so the dashboard showed `QC Batches: 0` despite 126 pairs of data.
88
+ let qcBatches = 0;
89
+ let qcDocsReviewed = 0;
90
+
91
+ // (a) Top-level batch files in output/qc/ (贷款 v0.7.5 shape)
84
92
  const qcDir = path.join(ws, "output", "qc");
85
93
  if (fs.existsSync(qcDir)) {
86
- metrics.qc_batches = fs.readdirSync(qcDir).filter((f) => f.endsWith(".json")).length;
94
+ for (const f of fs.readdirSync(qcDir).filter((f) => f.endsWith(".json"))) {
95
+ qcBatches++;
96
+ try {
97
+ const data = JSON.parse(fs.readFileSync(path.join(qcDir, f), "utf-8"));
98
+ const n = Number(data?.documents_reviewed);
99
+ if (Number.isFinite(n) && n > qcDocsReviewed) qcDocsReviewed = n;
100
+ } catch { /* skip malformed */ }
101
+ }
87
102
  }
88
103
 
104
+ // (b) Per-doc reviews at output/qc/reviews/ (贷款 detail shape)
105
+ const reviewsDir = path.join(ws, "output", "qc", "reviews");
106
+ if (fs.existsSync(reviewsDir)) {
107
+ const reviewFiles = fs.readdirSync(reviewsDir).filter((f) => f.endsWith(".json"));
108
+ qcDocsReviewed = Math.max(qcDocsReviewed, reviewFiles.length);
109
+ }
110
+
111
+ // (c) production_qc_results.json shape (资管 v0.7.5)
112
+ const productionQc = path.join(ws, "output", "results", "production_qc_results.json");
113
+ if (fs.existsSync(productionQc)) {
114
+ qcBatches++;
115
+ try {
116
+ const data = JSON.parse(fs.readFileSync(productionQc, "utf-8"));
117
+ const totalDocs = Number(data?.total_docs);
118
+ if (Number.isFinite(totalDocs)) qcDocsReviewed = Math.max(qcDocsReviewed, totalDocs);
119
+ // Otherwise, dedup doc keys from nested results
120
+ if (!Number.isFinite(totalDocs) && data?.results && typeof data.results === "object") {
121
+ const docSet = new Set();
122
+ for (const docs of Object.values(data.results)) {
123
+ if (docs && typeof docs === "object") {
124
+ for (const k of Object.keys(docs)) docSet.add(k);
125
+ }
126
+ }
127
+ if (docSet.size > 0) qcDocsReviewed = Math.max(qcDocsReviewed, docSet.size);
128
+ }
129
+ } catch { /* skip */ }
130
+ }
131
+
132
+ metrics.qc_batches = qcBatches;
133
+ metrics.qc_docs_reviewed = qcDocsReviewed;
134
+
89
135
  return metrics;
90
136
  }
91
137
 
@@ -126,6 +172,7 @@ th { color: #737373; font-size: 0.85em; }
126
172
  <div class="metric"><span class="value">${total}</span><br><span class="label">Results</span></div>
127
173
  <div class="metric"><span class="value">${metrics.evolution_iterations}</span><br><span class="label">Evolution Cycles</span></div>
128
174
  <div class="metric"><span class="value">${metrics.qc_batches}</span><br><span class="label">QC Batches</span></div>
175
+ <div class="metric"><span class="value">${metrics.qc_docs_reviewed || 0}</span><br><span class="label">Docs Reviewed</span></div>
129
176
  </div>
130
177
  <h2>Confidence Distribution</h2>
131
178
  <div class="card">
@@ -12,14 +12,43 @@ const MIN_CHARS_PER_PAGE = 50;
12
12
  * Level 3: OCR models via SiliconFlow — fallback via vision models
13
13
  */
14
14
  export class DocumentParseTool extends BaseTool {
15
- constructor(workspace, { mineruApiUrl, mineruApiKey, llmApiKey, llmBaseUrl, ocrModel } = {}) {
15
+ /**
16
+ * @param {object} workspace
17
+ * @param {object} opts
18
+ * @param {string} [opts.mineruApiUrl]
19
+ * @param {string} [opts.mineruApiKey]
20
+ * @param {string} [opts.llmApiKey]
21
+ * @param {string} [opts.llmBaseUrl]
22
+ * @param {string} [opts.ocrModel] — static fallback (legacy)
23
+ * @param {() => string} [opts.getOcrModel] — v0.8.1 P9-B: live-read
24
+ * callback. If provided, takes precedence over `ocrModel`. The
25
+ * constructor used to capture vlmTier1 once at engine startup, but
26
+ * workspace_env_overlay (P1-B) fires AFTER tool construction in
27
+ * some flows (e.g. agent edits .env mid-run, OR overlay applies on
28
+ * a subagent's engine but parent already cached the gc default).
29
+ * E2E #11 资管 v0.8 audit found document_parse errors quoting
30
+ * Qwen3-VL-235B-A22B-Instruct (gc default) even though .env set
31
+ * OCR_MODEL_TIER1=zai-org/GLM-4.6V — the overlay applied 5 min
32
+ * after first failed call. Live-read fixes the race.
33
+ */
34
+ constructor(workspace, { mineruApiUrl, mineruApiKey, llmApiKey, llmBaseUrl, ocrModel, getOcrModel } = {}) {
16
35
  super();
17
36
  this._workspace = workspace;
18
37
  this._mineruApiUrl = mineruApiUrl || "";
19
38
  this._mineruApiKey = mineruApiKey || "";
20
39
  this._vlmApiKey = llmApiKey || "";
21
40
  this._vlmBaseUrl = (llmBaseUrl || "").replace(/\/+$/, "");
22
- this._ocrModel = ocrModel || "";
41
+ this._ocrModelStatic = ocrModel || "";
42
+ this._getOcrModel = typeof getOcrModel === "function" ? getOcrModel : null;
43
+ }
44
+
45
+ /** Read ocrModel live (P9-B) or fall back to the static value captured at construction. */
46
+ get _ocrModel() {
47
+ if (this._getOcrModel) {
48
+ try { return this._getOcrModel() || this._ocrModelStatic; }
49
+ catch { return this._ocrModelStatic; }
50
+ }
51
+ return this._ocrModelStatic;
23
52
  }
24
53
 
25
54
  get name() { return "document_parse"; }
@@ -1,5 +1,6 @@
1
1
  import { BaseTool, ToolResult } from "./base.js";
2
2
  import { Phase } from "../pipelines/index.js";
3
+ import { getPrescriptiveHint } from "../pipelines/_advance-hints.js";
3
4
 
4
5
  const VALID_PHASES = new Set(Object.values(Phase));
5
6
 
@@ -72,12 +73,12 @@ export class PhaseAdvanceTool extends BaseTool {
72
73
 
73
74
  const beforePhase = this._getCurrentPhase();
74
75
  // H1: short-circuit the "already in target" case with an informational
75
- // message — the agent was trying to advance correctly, engine just
76
- // auto-advanced ahead of it (common when _maybeAutoAdvance fires on a
77
- // criteria flip). Treat as success, not refusal.
76
+ // message — agent was trying to advance correctly, engine was already
77
+ // there (from a prior pipeline_event-driven advance or an earlier
78
+ // explicit call). Treat as success, not refusal.
78
79
  if (beforePhase && beforePhase === to) {
79
80
  return new ToolResult(
80
- `Already in phase ${to} (engine auto-advanced earlier via criteria flip or prior explicit call). Proceed with phase-appropriate work.`,
81
+ `Already in phase ${to} (engine was already there from a prior advance). Proceed with phase-appropriate work.`,
81
82
  );
82
83
  }
83
84
 
@@ -126,18 +127,21 @@ export class PhaseAdvanceTool extends BaseTool {
126
127
  // exactly which milestones the gate is reading and can satisfy them.
127
128
  // E2E #6 v070 showed the generic "check /status" hint wasn't concrete
128
129
  // enough — agents forced through. Naming the gap inline reduces that.
129
- const engineCountsLine = advanceResult?.engineCounts
130
- ? `\nEngine telemetry: ${advanceResult.engineCounts}`
131
- : "";
130
+ // v0.8 P0-E: prescriptive refusal hint — name the artifacts the agent
131
+ // needs to produce, derived from the same paths _milestone-derive.js
132
+ // walks. Replaces the v0.7.x descriptive "check /status" message that
133
+ // 资管 + 贷款 v0.7.5 audits showed agents force-bypassing.
134
+ const prescriptive = getPrescriptiveHint(
135
+ beforePhase,
136
+ advanceResult?.engineCounts,
137
+ advanceResult?.engineCounts || "",
138
+ );
132
139
 
133
140
  return new ToolResult(
134
141
  `Did not advance to ${to} (currently in ${beforePhase || "?"}). ` +
135
- `Likely cause: source-phase exit criteria not met.${engineCountsLine}\n\n` +
136
- `Run /status (or read the phase describeState block in this turn's system reminder) ` +
137
- `to see which milestones are missing, then produce the disk artifacts that satisfy them ` +
138
- `the engine derives milestones from filesystem facts (rule_skills/<id>/SKILL.md, check.py, ` +
139
- `workflows/<id>/*.py, output/results/*.json, etc.). ` +
140
- `If the transition is non-adjacent or this phase truly is done despite the gate, ` +
142
+ `Likely cause: source-phase exit criteria not met.\n\n` +
143
+ prescriptive +
144
+ `\n\nIf the transition is non-adjacent or this phase truly is done despite the gate, ` +
141
145
  `re-call with the documented schema flag. The engine logged the precise reason in ` +
142
146
  `events.jsonl as 'phase_advance_refused'.`,
143
147
  false,