diff --git a/.orchestration/agent_trace.jsonl b/.orchestration/agent_trace.jsonl
index 48c0a71402..297c6c1735 100644
--- a/.orchestration/agent_trace.jsonl
+++ b/.orchestration/agent_trace.jsonl
@@ -1,3 +1,3 @@
-{"id":"df1a0b04-1993-45f4-b3cb-c0b4fcb2dba2","timestamp":"2026-02-21T08:37:15.794Z","vcs":{"revision_id":"abc123"},"files":[{"relative_path":"src/api/weather.ts","conversations":[{"url":"session-1","contributor":{"entity_type":"AI","model_identifier":"test-model"},"ranges":[{"start_line":1,"end_line":2,"content_hash":"sha256:2e4ec5cf14cfeb55ef32cb663ffd2c4fe6ab22bb2d2fe77ee8e284fe62866cac"}],"related":[{"type":"specification","value":"INT-001"}]}]}]}
-{"id":"7f9541c8-7fd4-4cef-ae44-c51f191b942a","timestamp":"2026-02-21T08:37:15.797Z","files":[{"relative_path":"src/api/forecast.ts","conversations":[{"contributor":{"entity_type":"AI","model_identifier":"unknown"},"ranges":[{"start_line":1,"end_line":1,"content_hash":"sha256:c47272be41ffa9d8a897b81b3f9f6d5e13fbb5d6dd4609a7b20c282950f11842"}],"related":[{"type":"specification","value":"INT-001"}]}]}]}
-{"id":"da60914f-8e16-4c59-8c5d-2c673793287d","timestamp":"2026-02-21T08:37:15.803Z","vcs":{"revision_id":"rev-1"},"files":[{"relative_path":"src/api/demo.ts","conversations":[{"url":"log-1","contributor":{"entity_type":"AI","model_identifier":"model-1"},"ranges":[{"start_line":1,"end_line":1,"content_hash":"sha256:bd994c26b795571c7c1e0357cba348f1d0238b648b748b9ec7a4d0de264d212f"}],"related":[{"type":"specification","value":"INT-001"}]}]}]}
+{"id":"02a7c124-4730-4e49-8783-08f861f640ba","timestamp":"2026-02-21T09:48:48.707Z","vcs":{"revision_id":"abc123"},"files":[{"relative_path":"src/api/weather.ts","conversations":[{"url":"session-1","contributor":{"entity_type":"AI","model_identifier":"test-model"},"ranges":[{"start_line":1,"end_line":2,"content_hash":"sha256:2e4ec5cf14cfeb55ef32cb663ffd2c4fe6ab22bb2d2fe77ee8e284fe62866cac"}],"related":[{"type":"specification","value":"INT-001"}]}]}]}
+{"id":"e55e9867-663a-412e-b341-af61f85dbedd","timestamp":"2026-02-21T09:48:48.716Z","files":[{"relative_path":"src/api/forecast.ts","conversations":[{"contributor":{"entity_type":"AI","model_identifier":"unknown"},"ranges":[{"start_line":1,"end_line":1,"content_hash":"sha256:c47272be41ffa9d8a897b81b3f9f6d5e13fbb5d6dd4609a7b20c282950f11842"}],"related":[{"type":"specification","value":"INT-001"}]}]}]}
+{"id":"3609a24b-418e-4e1a-a554-2e3cf9db1be9","timestamp":"2026-02-21T09:48:48.737Z","vcs":{"revision_id":"rev-1"},"files":[{"relative_path":"src/api/demo.ts","conversations":[{"url":"log-1","contributor":{"entity_type":"AI","model_identifier":"model-1"},"ranges":[{"start_line":1,"end_line":1,"content_hash":"sha256:bd994c26b795571c7c1e0357cba348f1d0238b648b748b9ec7a4d0de264d212f"}],"related":[{"type":"specification","value":"INT-001"}]}]}]}
diff --git a/scripts/test_phase1.js b/scripts/test_phase1.js
new file mode 100644
index 0000000000..0f4c0bb6bd
--- /dev/null
+++ b/scripts/test_phase1.js
@@ -0,0 +1,47 @@
+const fs = require("fs")
+const path = require("path")
+
+// Load active intents
+const intentsPath = path.join(__dirname, "..", ".orchestration", "active_intents.yaml")
+const yaml = require("js-yaml")
+const intents = yaml.load(fs.readFileSync(intentsPath, "utf8")).active_intents
+
+// Simple PreHook simulation
+function preHook(intentId, targetFile) {
+ const intent = intents.find((i) => i.id === intentId)
+ if (!intent) {
+ throw new Error("You must select a valid active Intent ID before writing code.")
+ }
+
+ const allowed = intent.owned_scope.some((scope) => targetFile.startsWith(scope.replace("**", "")))
+ if (!allowed) {
+ throw new Error(`Scope Violation: ${intentId} is not authorized to edit ${targetFile}`)
+ }
+
+ console.log(`PreHook Passed: ${targetFile} is within scope for ${intentId}`)
+}
+
+// Simulate AI writing a file
+function writeFile(intentId, filePath, content) {
+ preHook(intentId, filePath)
+ fs.writeFileSync(filePath, content)
+ console.log(`File written successfully: ${filePath}`)
+}
+
+// === TEST CASES ===
+try {
+ // Valid case
+ writeFile("INT-001", "src/api/weather.ts", "// Weather API code here")
+
+ // Invalid scope
+ writeFile("INT-001", "src/db/db.ts", "// DB code here")
+} catch (err) {
+ console.error("Error:", err.message)
+}
+
+try {
+ // Invalid intent
+ writeFile("INT-999", "src/api/weather.ts", "// Should fail")
+} catch (err) {
+ console.error("Error:", err.message)
+}
diff --git a/src/api/weather.ts b/src/api/weather.ts
new file mode 100644
index 0000000000..a442555ef2
--- /dev/null
+++ b/src/api/weather.ts
@@ -0,0 +1 @@
+// Weather API code here
diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts
index 0d6071644a..de103ea8c3 100644
--- a/src/core/prompts/system.ts
+++ b/src/core/prompts/system.ts
@@ -10,6 +10,7 @@ import { isEmpty } from "../../utils/object"
import { McpHub } from "../../services/mcp/McpHub"
import { CodeIndexManager } from "../../services/code-index/manager"
import { SkillsManager } from "../../services/skills/SkillsManager"
+import { INTENT_DRIVEN_PROMPT_SNIPPET } from "../../hooks/intent-prompt-snippet"
import type { SystemPromptSettings } from "./types"
import {
@@ -84,6 +85,8 @@ async function generatePrompt(
const basePrompt = `${roleDefinition}
+${INTENT_DRIVEN_PROMPT_SNIPPET}
+
${markdownFormattingSection()}
${getSharedToolUseSection()}${toolsCatalog}
diff --git a/src/core/prompts/tools/native-tools/index.ts b/src/core/prompts/tools/native-tools/index.ts
index 758914d2d6..624d1db084 100644
--- a/src/core/prompts/tools/native-tools/index.ts
+++ b/src/core/prompts/tools/native-tools/index.ts
@@ -1,4 +1,5 @@
import type OpenAI from "openai"
+import { selectActiveIntentToolDefinition } from "../../../../hooks/select-active-intent-tool"
import accessMcpResource from "./access_mcp_resource"
import { apply_diff } from "./apply_diff"
import applyPatch from "./apply_patch"
@@ -47,6 +48,7 @@ export function getNativeTools(options: NativeToolsOptions = {}): OpenAI.Chat.Ch
}
return [
+ selectActiveIntentToolDefinition,
accessMcpResource,
apply_diff,
applyPatch,
diff --git a/src/hooks/context-loader.ts b/src/hooks/context-loader.ts
index 62063c4fc8..6c3591cf70 100644
--- a/src/hooks/context-loader.ts
+++ b/src/hooks/context-loader.ts
@@ -34,8 +34,9 @@ export async function loadIntentContext(cwd: string, intentId: string): Promise<
/**
* Build an XML block to inject as the tool result for select_active_intent.
+ * Optionally include related agent trace entries for consolidated context.
*/
-export function buildIntentContextXml(context: IntentContext): string {
+export function buildIntentContextXml(context: IntentContext, relatedTracePaths: string[] = []): string {
const constraintsXml =
context.constraints.length > 0
? context.constraints.map((c) => ` ${escapeXml(c)}`).join("\n")
@@ -48,6 +49,10 @@ export function buildIntentContextXml(context: IntentContext): string {
(context.acceptance_criteria?.length ?? 0) > 0
? context.acceptance_criteria!.map((a) => ` ${escapeXml(a)}`).join("\n")
: " None specified"
+ const traceXml =
+ relatedTracePaths.length > 0
+ ? relatedTracePaths.map((p) => ` ${escapeXml(p)}`).join("\n")
+ : " None yet"
return `
${escapeXml(context.id)}
@@ -62,9 +67,34 @@ ${scopeXml}
${criteriaXml}
+
+${traceXml}
+
`
}
+/**
+ * Load intent from active_intents.yaml, gather related agent_trace entries for that intent,
+ * and return a consolidated XML context block (for Pre-Hook injection).
+ */
+export async function buildConsolidatedIntentContextXml(cwd: string, intentId: string): Promise {
+ const context = await loadIntentContext(cwd, intentId)
+ if (!context) return null
+ const traceLines = await readRecentTraceForIntent(cwd, intentId, 20)
+ const paths = new Set()
+ for (const line of traceLines) {
+ try {
+ const entry = JSON.parse(line) as { files?: Array<{ relative_path?: string }> }
+ for (const f of entry.files ?? []) {
+ if (f.relative_path) paths.add(f.relative_path)
+ }
+ } catch {
+ // skip malformed lines
+ }
+ }
+ return buildIntentContextXml(context, [...paths])
+}
+
function escapeXml(s: string): string {
return s
.replace(/&/g, "&")
diff --git a/src/hooks/pre-hook.ts b/src/hooks/pre-hook.ts
index be650829cf..e8dc7315c6 100644
--- a/src/hooks/pre-hook.ts
+++ b/src/hooks/pre-hook.ts
@@ -2,7 +2,7 @@ import path from "path"
import type { HookResult } from "./types"
import { DESTRUCTIVE_TOOLS } from "./types"
-import { loadIntentContext, buildIntentContextXml } from "./context-loader"
+import { loadIntentContext, buildConsolidatedIntentContextXml } from "./context-loader"
import { pathInScope } from "./scope"
/** Block paths that escape workspace (.. or absolute outside cwd). */
@@ -41,15 +41,14 @@ export class PreHook {
if (!intentId) {
return { blocked: true, error: "You must provide a valid intent_id when calling select_active_intent." }
}
- const context = await loadIntentContext(cwd, intentId)
- if (!context) {
+ const xml = await buildConsolidatedIntentContextXml(cwd, intentId)
+ if (!xml) {
return {
blocked: true,
error: `You must cite a valid active Intent ID. Intent "${intentId}" was not found in .orchestration/active_intents.yaml.`,
}
}
setActiveIntentId(intentId)
- const xml = buildIntentContextXml(context)
return { blocked: false, injectResult: xml }
}