feat(trekresearch): add Phase 4.5 dimension discovery and amend Independence rule
This commit is contained in:
parent
3e8af75015
commit
d9cba9c6ea
3 changed files with 121 additions and 5 deletions
|
|
@ -7,12 +7,20 @@ tools: ["Read", "Glob", "Grep", "Write", "Edit", "Bash"]
|
|||
---
|
||||
|
||||
<!-- Phase mapping: orchestrator → command
|
||||
Corrected in v5.10: every row below was off by one, and the old last row
|
||||
pointed at a ninth command phase that does not exist — the command ends
|
||||
at Phase 8.
|
||||
Orchestrator Phase 1 = Command Phase 4 (Agent group selection)
|
||||
Orchestrator Phase 2 = Command Phase 5 (Parallel research)
|
||||
Orchestrator Phase 3 = Command Phase 6 (Targeted follow-ups)
|
||||
Orchestrator Phase 4 = Command Phase 7 (Triangulation)
|
||||
Orchestrator Phase 5 = Command Phase 8 (Synthesis + write brief)
|
||||
Orchestrator Phase 6 = Command Phase 9 (Completion)
|
||||
Orchestrator Phase 2 = Command Phase 4 (Parallel research — same
|
||||
command phase; the orchestrator
|
||||
splits selection from launch)
|
||||
(no orchestrator phase)= Command Phase 4.5 (Dimension discovery,
|
||||
high effort only — v5.10)
|
||||
Orchestrator Phase 3 = Command Phase 5 (Targeted follow-ups; bounded
|
||||
conversation loop at high effort)
|
||||
Orchestrator Phase 4 = Command Phase 6 (Triangulation)
|
||||
Orchestrator Phase 5 = Command Phase 7 (Synthesis + write brief)
|
||||
Orchestrator Phase 6 = Command Phase 8 (Present and track / completion)
|
||||
As of v2.4.0, /trekresearch runs these phases inline in main
|
||||
context instead of spawning this agent. Keep this file as the canonical
|
||||
reference for what those phases do. -->
|
||||
|
|
|
|||
|
|
@ -391,6 +391,34 @@ other agents — the value of Gemini is independence.
|
|||
small = halved, medium/large = default
|
||||
- convention-scanner: medium+ codebases only (50+ files)
|
||||
|
||||
## Phase 4.5 — Dimension discovery
|
||||
|
||||
**Skip this phase entirely unless `phase_signal_result.effort == 'high'`.**
|
||||
|
||||
Phase 4 retrieves more than the interview knew to ask for. This phase mines
|
||||
that surplus: findings that were **retrieved but unintegrated** — material an
|
||||
agent surfaced that no interview dimension claims.
|
||||
|
||||
1. **Mine.** Walk the Phase-4 agent results and collect findings that map to
|
||||
no existing dimension.
|
||||
2. **Rerank.** Order candidates by relevance to the research question **and**
|
||||
dissimilarity to the dimensions already on the list. A candidate that
|
||||
restates an existing dimension is not a discovery.
|
||||
3. **Augment under the existing ceiling.** Append candidates to the dimension
|
||||
list only while the **whole** list (interview + discovered) stays at or
|
||||
below `maxDimensions: 8` (`settings.json:16`). The ceiling is **not**
|
||||
raised here, so the documented 3–8 dimension range stays true and the
|
||||
README prose about it stays untouched. If the interview already produced 8
|
||||
dimensions, this phase discovers nothing and says so.
|
||||
4. **Record the baseline.** Keep the interview-derived count as
|
||||
`dimensions_baseline` so the discovered delta is machine-readable against
|
||||
the final `dimensions` (Phase 8 stats).
|
||||
|
||||
Every outbound query generated from a discovered dimension passes
|
||||
`query-privacy-gate.mjs` before it leaves the machine — see the per-turn
|
||||
protocol in Phase 5. That gate is the compensating control for the
|
||||
Independence crossing this phase makes (see Hard rules → Independence).
|
||||
|
||||
## Phase 5 — Targeted follow-ups
|
||||
|
||||
Review all agent results. Identify knowledge gaps — dimensions where findings
|
||||
|
|
@ -727,6 +755,16 @@ Low effort: inline research only, no agent swarm (existing
|
|||
- **Sources required:** Every claim must cite a source. No unsourced findings.
|
||||
- **Independence:** Do not pre-bias external agents with local findings or vice versa.
|
||||
Triangulate AFTER independent research.
|
||||
**Amended (v5.10) for Phase 4.5:** dimension discovery deliberately crosses this
|
||||
rule. Its candidate dimensions are mined from the Phase-4 result set, which
|
||||
contains output from the five local codebase agents, so a discovered dimension
|
||||
can carry local context into an external query. The crossing is bounded to
|
||||
Phase 4.5 and the Phase 5 loop it feeds, it never applies to the initial
|
||||
external swarm (which stays blind to local findings), and the compensating
|
||||
control is `query-privacy-gate.mjs`: every outbound query is inspected before
|
||||
it leaves the machine, with a hard-block tier for secret-shaped strings that no
|
||||
operator flag can override. Triangulation still happens AFTER independent
|
||||
research.
|
||||
- **Graceful degradation:** If MCP tools are unavailable (Tavily, Gemini, MS Learn),
|
||||
proceed with available tools and note limitations in brief metadata.
|
||||
- **Cost:** Model resolution at Agent-spawn sites is a three-layer fallback:
|
||||
|
|
|
|||
|
|
@ -166,6 +166,76 @@ test('trekresearch — High-effort behavior keeps the standard/low effort senten
|
|||
);
|
||||
});
|
||||
|
||||
// --- Step 8: Phase 4.5 dimension discovery + Independence amendment ---
|
||||
|
||||
const ORCHESTRATOR_FILE = join(ROOT, 'agents', 'research-orchestrator.md');
|
||||
function readOrchestrator() { return readFileSync(ORCHESTRATOR_FILE, 'utf8'); }
|
||||
|
||||
test('trekresearch — Phase 4.5 exists between Phase 4 and Phase 5 with the effort skip-guard', () => {
|
||||
const text = read();
|
||||
const p45 = text.indexOf('## Phase 4.5 —');
|
||||
assert.ok(p45 >= 0, 'Phase 4.5 heading missing');
|
||||
const p4 = text.indexOf('## Phase 4 —');
|
||||
const p5 = text.indexOf('## Phase 5 —');
|
||||
assert.ok(p4 >= 0 && p5 > p45 && p45 > p4, 'Phase 4.5 must sit between Phase 4 and Phase 5');
|
||||
|
||||
const slice = text.slice(p45, p5);
|
||||
assert.match(
|
||||
slice,
|
||||
/\*\*Skip this phase entirely unless `phase_signal_result\.effort == 'high'`\.\*\*/,
|
||||
'Phase 4.5 must carry the bolded skip-guard in the Phase 3.5 form',
|
||||
);
|
||||
assert.match(slice, /query-privacy-gate\.mjs/,
|
||||
'Phase 4.5 must name the privacy gate as its compensating control');
|
||||
assert.match(slice, /maxDimensions: 8|maxDimensions` *: *8/,
|
||||
'Phase 4.5 must augment under the existing maxDimensions ceiling, not raise it');
|
||||
});
|
||||
|
||||
test('trekresearch — Independence hard rule carries an explicit Phase 4.5 amendment', () => {
|
||||
const text = read();
|
||||
const rulesIdx = text.indexOf('## Hard rules');
|
||||
assert.ok(rulesIdx >= 0, 'Hard rules section missing');
|
||||
const rules = text.slice(rulesIdx);
|
||||
const indIdx = rules.indexOf('**Independence:**');
|
||||
assert.ok(indIdx >= 0, 'Independence hard rule missing');
|
||||
// Bound the rule at the next bullet so the amendment must live inside it.
|
||||
const nextBullet = rules.indexOf('\n- **', indIdx);
|
||||
const independence = nextBullet > indIdx ? rules.slice(indIdx, nextBullet) : rules.slice(indIdx);
|
||||
assert.match(independence, /Amend(ed|ment)/i,
|
||||
'Independence must be explicitly amended, not silently contradicted');
|
||||
assert.match(independence, /Phase 4\.5/, 'the amendment must name Phase 4.5 as the crossing');
|
||||
assert.match(independence, /query-privacy-gate\.mjs/,
|
||||
'the amendment must name the compensating control');
|
||||
});
|
||||
|
||||
test('trekresearch — orchestrator phase map is correct, has no Phase 9, and carries Phase 4.5', () => {
|
||||
const doc = readOrchestrator();
|
||||
const start = doc.indexOf('<!-- Phase mapping');
|
||||
assert.ok(start >= 0, 'phase mapping comment missing');
|
||||
const end = doc.indexOf('-->', start);
|
||||
assert.ok(end > start, 'phase mapping comment not terminated');
|
||||
const map = doc.slice(start, end);
|
||||
|
||||
assert.doesNotMatch(map, /Command Phase 9/,
|
||||
'the command ends at Phase 8 — a Command Phase 9 row is a fiction');
|
||||
|
||||
// Six orchestrator rows, each pointing at the phase the command actually has.
|
||||
const expected = [
|
||||
[1, '4'],
|
||||
[2, '4'],
|
||||
[3, '5'],
|
||||
[4, '6'],
|
||||
[5, '7'],
|
||||
[6, '8'],
|
||||
];
|
||||
for (const [orch, cmd] of expected) {
|
||||
const re = new RegExp(`Orchestrator Phase ${orch}\\s+= Command Phase ${cmd.replace('.', '\\.')}\\b`);
|
||||
assert.match(map, re, `map row for Orchestrator Phase ${orch} must point at Command Phase ${cmd}`);
|
||||
}
|
||||
|
||||
assert.match(map, /Command Phase 4\.5/, 'the map must carry the new Phase 4.5 row');
|
||||
});
|
||||
|
||||
// --- v5.1.1 runtime SC4 + SC7 ---
|
||||
|
||||
test('trekresearch — SC4: low-effort fixture → resolver returns {effort: low, model: sonnet}', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue