@hone-ai/cli 1.29.0 → 1.30.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/lib/run-report.js +104 -2
- package/package.json +1 -1
package/lib/run-report.js
CHANGED
|
@@ -60,6 +60,102 @@ function buildReport(run, extra = {}) {
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// 1b. HC-GF-044: a STEP can be failed while the RUN says completed.
|
|
64
|
+
//
|
|
65
|
+
// Everything above keys on `run.status`. Nothing read `steps[].status`, so a run
|
|
66
|
+
// reported `completed` that contained a `failed` step produced ZERO items and the
|
|
67
|
+
// empty-state branch then printed the affirmative "nothing outstanding". Observed on
|
|
68
|
+
// run 08c27392: `Status: completed` / `Error: MAX_RETRIES_EXCEEDED: step_5 retried 2
|
|
69
|
+
// times` / `✗ step_5 (code-reviewer) failed`, and then `NEEDS YOU (0)`. The STEPS block
|
|
70
|
+
// below rendered the failure correctly, so the data was present and read — only the
|
|
71
|
+
// actionable layer missed it.
|
|
72
|
+
//
|
|
73
|
+
// This is the exact class HC-GF-016 exists to prevent, and the sharp part is that its
|
|
74
|
+
// empty-state rule was HONOURED: the section rendered and named what it checked. It
|
|
75
|
+
// checked the wrong field. A well-formed "nothing" is only as true as its predicate.
|
|
76
|
+
//
|
|
77
|
+
// DELIBERATELY INDEPENDENT OF HC-GF-043. That story stops the orchestrator writing
|
|
78
|
+
// `completed` over a failed step, which would make THIS run fire here via the branch
|
|
79
|
+
// above. It would not help a `completed` run whose step failed for any other reason, or
|
|
80
|
+
// a report built from a run recorded before that fix shipped. Both are needed; neither
|
|
81
|
+
// substitutes for the other.
|
|
82
|
+
//
|
|
83
|
+
// Keyed on the step's OWN state, and `rejected` counts alongside `failed`: a rejected
|
|
84
|
+
// gate is a human decision awaiting follow-up, not a null result.
|
|
85
|
+
const brokenSteps = steps.filter(s => s.status === 'failed' || s.gateResult === 'rejected');
|
|
86
|
+
for (const s of brokenSteps) {
|
|
87
|
+
// Skip the one the run-level item already names, so one problem is one entry.
|
|
88
|
+
if (run.status === 'failed' && s.stepKey === run.currentStep) continue;
|
|
89
|
+
// HC-GF-036(b): a step that RETURNED AN ADVERSE VERDICT did not break. Saying
|
|
90
|
+
// "code-reviewer FAILED" for a reviewer that ran correctly and said no is the same
|
|
91
|
+
// conflation that was inflating batch failure_rates — and it points the operator at
|
|
92
|
+
// the wrong thing to fix. The verdict kinds name the agent's own vocabulary.
|
|
93
|
+
const kind = (s.diagnostics && s.diagnostics.failure_record || {}).failure_kind;
|
|
94
|
+
const VERDICT = {
|
|
95
|
+
review_reject: 'returned a FAIL review',
|
|
96
|
+
security_high: 'raised a HIGH security finding',
|
|
97
|
+
security_medium: 'raised a MEDIUM security finding',
|
|
98
|
+
test_fail: 'reported failing tests',
|
|
99
|
+
lint_fail: 'reported lint errors',
|
|
100
|
+
perf_finding: 'raised a performance finding',
|
|
101
|
+
validator_rejected: 'produced output the format validator rejected',
|
|
102
|
+
};
|
|
103
|
+
const what = kind && VERDICT[kind]
|
|
104
|
+
? `${s.stepKey} (${s.agent || 'agent'}) ${VERDICT[kind]}`
|
|
105
|
+
: `${s.stepKey} (${s.agent || 'agent'}) ${s.status === 'failed' ? 'FAILED' : 'was REJECTED'}`;
|
|
106
|
+
needsYou.push({
|
|
107
|
+
what: `${what} — the run is recorded ${run.status}, so nothing downstream will revisit it`,
|
|
108
|
+
how: `hone show step ${run.runId} ${s.stepKey}`,
|
|
109
|
+
howLabel: 'inspect',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 1c. HC-GF-041: a retry that came back with LESS than the attempt it replaces.
|
|
114
|
+
//
|
|
115
|
+
// The newest attempt REPLACES the previous one for every downstream consumer, so this
|
|
116
|
+
// silently deletes work. Twice it was caught only because an agent refused to treat
|
|
117
|
+
// absence as evidence — step_5d's `UNVERIFIED-HIGH / source-unavailable`, and a
|
|
118
|
+
// reviewer's "I will not approve a P0 reliability primitive I have not read". Neither
|
|
119
|
+
// is a mechanism; both were luck. This is the mechanism.
|
|
120
|
+
//
|
|
121
|
+
// NEEDS YOU, not WORTH KNOWING: the artifact every later step reads is not the one a
|
|
122
|
+
// human approved, and only a human can say whether that is acceptable.
|
|
123
|
+
for (const s of steps) {
|
|
124
|
+
const reg = s.diagnostics && s.diagnostics.artifact_regression;
|
|
125
|
+
if (!reg) continue;
|
|
126
|
+
needsYou.push({
|
|
127
|
+
what: `${s.stepKey} attempt ${s.attempt} carries less than the attempt it replaces`
|
|
128
|
+
+ ` — and it is the one every later step reads`,
|
|
129
|
+
detail: (reg.reasons || []).map(String),
|
|
130
|
+
how: `hone show step ${run.runId} ${s.stepKey} --attempt ${s.attempt}`,
|
|
131
|
+
howLabel: 'compare',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 1d. HC-GF-041: two diagnostics the orchestrator has stamped since HC-019n-followup-41
|
|
136
|
+
// and -43, which NOTHING has ever read on this path — the run payload carried only
|
|
137
|
+
// token counts until the `diagnostics` projection was added. HC-GF-016's row specifies
|
|
138
|
+
// these be rendered; this is the first build where they can be.
|
|
139
|
+
//
|
|
140
|
+
// Both are written ONLY when non-empty, so an absent key means "checked, none found"
|
|
141
|
+
// and must not be rendered as a reassuring zero — silence here is correct, an invented
|
|
142
|
+
// "none" would be a claim the report cannot support for a run that never ran the check.
|
|
143
|
+
for (const s of steps) {
|
|
144
|
+
const d = s.diagnostics || {};
|
|
145
|
+
if (Array.isArray(d.invented_paths) && d.invented_paths.length) {
|
|
146
|
+
needsYou.push({
|
|
147
|
+
what: `${s.stepKey} named ${d.invented_paths.length} path(s) that do not exist in the repo, and was accepted anyway`,
|
|
148
|
+
detail: d.invented_paths.map(String),
|
|
149
|
+
how: `hone show step ${run.runId} ${s.stepKey}`,
|
|
150
|
+
howLabel: 'inspect',
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (Array.isArray(d.composed_missing) && d.composed_missing.length) {
|
|
154
|
+
worthKnowing.push(
|
|
155
|
+
`${s.stepKey} dropped composed guardrail section(s) on a retry and was accepted (budget spent): ${d.composed_missing.join(', ')}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
63
159
|
// 2. The last verification is ONE event, so it becomes ONE item with sub-lines.
|
|
64
160
|
// Listing "verification failed" and "lint failed" separately gave two entries
|
|
65
161
|
// for one problem resolved by one fix — and NEEDS YOU is a list of DISTINCT
|
|
@@ -92,10 +188,16 @@ function buildReport(run, extra = {}) {
|
|
|
92
188
|
}
|
|
93
189
|
|
|
94
190
|
// ── WORTH KNOWING ──────────────────────────────────────────────────────────
|
|
95
|
-
// Retries: the step
|
|
191
|
+
// Retries: the step ran more than once. HC-GF-041(c): this used to append "(its
|
|
192
|
+
// first output was rejected)", which the report never checked and which is often
|
|
193
|
+
// FALSE — on run 08c27392 step_4's attempt 1 was `completed | approved` and it was
|
|
194
|
+
// attempt 2 that got rejected; a route-back retries a step whose first output was
|
|
195
|
+
// fine. Narrating an unchecked cause inside the tool built to be derived-never-
|
|
196
|
+
// narrated is the defect HC-GF-016 exists to prevent. State the count, which is
|
|
197
|
+
// measured, and let the regression check below say when something is wrong.
|
|
96
198
|
for (const s of steps) {
|
|
97
199
|
if (Number(s.attempts) > 1) {
|
|
98
|
-
worthKnowing.push(`${s.stepKey}
|
|
200
|
+
worthKnowing.push(`${s.stepKey} ran ${s.attempts} times`);
|
|
99
201
|
}
|
|
100
202
|
}
|
|
101
203
|
// The two "blind run" tells, both from HC-GF-006's preflight reasoning.
|