Skip to content

Commit 5577799

Browse files
neo-opus-adatobiuAda
authored
fix(golden-path): current-focus fallback rows round-trip through parseGoldenPath (#15092) (#15093)
The never-empty focus-as-route floor rendered `N. **issue-N**:` rows without the indented `- *desc*` continuation line that AgentOrchestrator.parseGoldenPath() requires, so the autonomous orchestrator extracted ZERO directives in the no-survivor-contradiction state — the floor was silently unroutable. A false-green test (asserted the render substring + claimed 'parseGoldenPath-compatible' but never ran the parser) masked it. - render: emit the continuation line per routed focus row (mirrors the canonical row shape) - test: real render->parseGoldenPath round-trip in AgentOrchestrator.spec (red-before-green verified) + honest render-shape assertion replacing the false-green substring check - test: lock the declared-intent fallback as advisory/non-executed (zero directives, by design) Co-authored-by: tobiu <tobiasuhlig78@gmail.com> Co-authored-by: Ada <ada@neo-opus-ada.ai>
1 parent be4aa02 commit 5577799

3 files changed

Lines changed: 75 additions & 4 deletions

File tree

ai/services/graph/computedGoldenPathRouting.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,13 @@ export function renderComputedGoldenPathContradictionSection({
240240
const label = candidate.title || candidate.name ||
241241
(Array.isArray(candidate.reasons) ? candidate.reasons.join(', ') : 'Current Release / Incident Focus');
242242

243-
lines.push(`${index + 1}. **issue-${candidate.number}**: Current Release / Incident Focus (${label})`)
243+
// Emit the indented `- *…*` continuation line the AgentOrchestrator route parser requires:
244+
// a bare `N. **issue-N**:` row without it renders for humans but extracts ZERO directives,
245+
// leaving the never-empty floor silently unroutable. Mirrors the canonical route row shape.
246+
lines.push(
247+
`${index + 1}. **issue-${candidate.number}**: Current Release / Incident Focus`,
248+
` - *${label}*`
249+
)
244250
});
245251
} else {
246252
// Epic-only / no-actionable-focus: the honest state is NO immediate route — an epic umbrella or

test/playwright/unit/ai/AgentOrchestrator.spec.mjs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import Neo from '../../../../src/Neo.mjs';
55
import * as core from '../../../../src/core/_export.mjs';
66
import AgentOrchestrator from '../../../../ai/agent/AgentOrchestrator.mjs';
77

8+
import {findComputedFocusContradiction, renderComputedGoldenPathContradictionSection} from '../../../../ai/services/graph/computedGoldenPathRouting.mjs';
9+
import {rankByDeclaredIntent, renderDeclaredIntentFallback} from '../../../../ai/services/graph/goldenPathPickupBridge.mjs';
10+
811
const createTestHandoff = (filename, content) => {
912
const filePath = path.resolve(process.cwd(), filename);
1013
fs.writeFileSync(filePath, content, 'utf-8');
@@ -127,6 +130,67 @@ Based on priorities, the following tasks are mathematically recommended:
127130
}
128131
});
129132

133+
test('the current-focus contradiction fallback render round-trips through parseGoldenPath (the never-empty floor actually routes)', async () => {
134+
// The producer render (routing module) and the consumer parser (this class) form a byte-format
135+
// contract. A render-substring assertion is NOT proof of routability — only feeding the real
136+
// render through the real parser is. This guards the regression where the focus-as-route rows
137+
// lacked the `- *…*` continuation line and silently extracted ZERO directives.
138+
const contradiction = findComputedFocusContradiction({
139+
currentFocusCandidates: [{number: 14988, reasons: ['incident'], labels: ['bug'], title: 'Fleet auth restart supervised'}],
140+
topNodes : [{
141+
node : {id: 'issue-200', type: 'ISSUE', properties: {labels: ['documentation'], title: 'docs: release notes'}},
142+
score: 1, semantic: 1, structural: 0
143+
}]
144+
});
145+
146+
test.expect(contradiction).not.toBeNull();
147+
148+
const section = renderComputedGoldenPathContradictionSection({contradiction, stats: {}, renderLimit: 5}),
149+
content = `# Autonomous Handoff\n${section}`,
150+
handoff = createTestHandoff('.neo-test-handoff-contradiction.md', content);
151+
152+
try {
153+
const orchestrator = Neo.create(AgentOrchestrator, {handoffPath: handoff}),
154+
directives = orchestrator.parseGoldenPath();
155+
156+
test.expect(directives).not.toBeNull();
157+
test.expect(directives.length).toBe(1);
158+
test.expect(directives[0].issueId).toBe('14988');
159+
// the blocked content candidate is diagnostic-only, never a routed directive
160+
test.expect(directives.map(directive => directive.issueId)).not.toContain('200');
161+
} finally {
162+
if (fs.existsSync(handoff)) {
163+
fs.unlinkSync(handoff);
164+
}
165+
}
166+
});
167+
168+
test('the declared-intent frontier-empty fallback render yields ZERO parseGoldenPath directives (advisory, not executed route — by design)', async () => {
169+
// The declared-intent fallback is a provisional cold-cache rescue, explicitly "not the semantic
170+
// ranking" and additive-never-gating per the direction contract. It renders as a separate `### …`
171+
// section with `#N` rows so the executed-route parser deliberately does NOT consume it. Elevating
172+
// it into the executed route would make declared intent gate execution — this locks the boundary.
173+
const section = renderDeclaredIntentFallback(
174+
rankByDeclaredIntent([{id: '14620', inOpenEpic: true, epicActivity: 3, blocked: false, filedAt: '2026-07-10'}]),
175+
5,
176+
{code: 'COLD_START', phrase: 'cold cache'}
177+
),
178+
content = `# Autonomous Handoff\n${section}`,
179+
handoff = createTestHandoff('.neo-test-handoff-declared-intent.md', content);
180+
181+
try {
182+
const orchestrator = Neo.create(AgentOrchestrator, {handoffPath: handoff}),
183+
directives = orchestrator.parseGoldenPath();
184+
185+
// section-shaped but non-executable: no `**issue-N**:` rows → an empty directive list, never a route
186+
test.expect(directives).toEqual([]);
187+
} finally {
188+
if (fs.existsSync(handoff)) {
189+
fs.unlinkSync(handoff);
190+
}
191+
}
192+
});
193+
130194
test('execute records completed outcomes for exhausted Golden Path directives', async () => {
131195
const content = `
132196
# Autonomous Handoff

test/playwright/unit/ai/services/graph/computedGoldenPathRouting.spec.mjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,10 @@ test.describe('computedGoldenPathRouting — the contradiction guard (routing-de
103103

104104
const section = renderComputedGoldenPathContradictionSection({contradiction, stats: {selectedTopNodes: 0}});
105105

106-
// never empty: the live Current Focus item IS the numbered route (parseGoldenPath-compatible)
107-
expect(section).toContain('1. **issue-100**');
108-
expect(section).toContain('incident: cockpit auth relaunch');
106+
// never empty: the live Current Focus item IS the numbered route. Parser-SHAPED at the render
107+
// level — the `**issue-N**:` row is followed by the `- *…*` continuation line the route parser
108+
// requires (the full render→parseGoldenPath round-trip is asserted in AgentOrchestrator.spec).
109+
expect(section).toMatch(/1\. \*\*issue-100\*\*:[^\n]*\n\s+-\s\*incident: cockpit auth relaunch\*/);
109110
// the blocked content is filtered-only — it appears in the diagnostic, never as a numbered route
110111
expect(section).toMatch(/Contradictory computed candidates filtered:.*issue-200/);
111112
expect(section).not.toMatch(/^\d+\.\s+\*\*issue-20[01]\*\*/m);

0 commit comments

Comments
 (0)