瀏覽代碼

Merge branch 'main' into feature/oac-package-refactor-v2

darrenhinde 4 月之前
父節點
當前提交
31bc8d37cb

+ 140 - 84
.opencode/context/core/workflows/session-context-pattern.md

@@ -1,4 +1,5 @@
 <!-- Context: workflows/session-context | Priority: critical | Version: 1.0 | Updated: 2026-02-15 -->
 <!-- Context: workflows/session-context | Priority: critical | Version: 1.0 | Updated: 2026-02-15 -->
+
 # Session Context Pattern
 # Session Context Pattern
 
 
 ## Problem
 ## Problem
@@ -13,6 +14,7 @@ When orchestrating complex features across multiple agents (TaskManager → Code
 - **Orchestrator** has to manually pass context in every delegation
 - **Orchestrator** has to manually pass context in every delegation
 
 
 This leads to:
 This leads to:
+
 - ❌ Repeated context discovery (inefficient)
 - ❌ Repeated context discovery (inefficient)
 - ❌ Inconsistent decisions (agents don't see previous choices)
 - ❌ Inconsistent decisions (agents don't see previous choices)
 - ❌ Lost architectural context (bounded contexts, contracts, ADRs)
 - ❌ Lost architectural context (bounded contexts, contracts, ADRs)
@@ -54,57 +56,71 @@ Created: {timestamp}
 Status: in_progress | completed | blocked
 Status: in_progress | completed | blocked
 
 
 ## Current Request
 ## Current Request
+
 {Original user request - what we're building}
 {Original user request - what we're building}
 
 
 ## Context Files to Load
 ## Context Files to Load
+
 - {Standards paths - coding conventions, patterns, security rules}
 - {Standards paths - coding conventions, patterns, security rules}
 
 
 ## Reference Files
 ## Reference Files
+
 - {Source material - existing project files to look at}
 - {Source material - existing project files to look at}
 
 
 ## Architecture
 ## Architecture
+
 - Bounded Context: {DDD context from ArchitectureAnalyzer}
 - Bounded Context: {DDD context from ArchitectureAnalyzer}
 - Module: {Package/module name}
 - Module: {Package/module name}
 - Vertical Slice: {Feature slice from StoryMapper}
 - Vertical Slice: {Feature slice from StoryMapper}
 
 
 ## User Stories
 ## User Stories
+
 - {Story 1 from StoryMapper}
 - {Story 1 from StoryMapper}
 - {Story 2}
 - {Story 2}
 
 
 ## Priorities
 ## Priorities
+
 - RICE Score: {score from PrioritizationEngine}
 - RICE Score: {score from PrioritizationEngine}
 - WSJF Score: {score}
 - WSJF Score: {score}
 - Release Slice: {v1.0.0, Q1-2026, MVP}
 - Release Slice: {v1.0.0, Q1-2026, MVP}
 
 
 ## Contracts
 ## Contracts
+
 - {type}: {name} ({status})
 - {type}: {name} ({status})
   Path: {contract file path}
   Path: {contract file path}
 
 
 ## Architectural Decision Records
 ## Architectural Decision Records
+
 - {ADR-ID}: {title}
 - {ADR-ID}: {title}
   Path: {adr file path}
   Path: {adr file path}
 
 
 ## Progress
 ## Progress
+
 Current Stage: {Stage N: Name}
 Current Stage: {Stage N: Name}
 
 
 Completed Stages:
 Completed Stages:
+
 - {Stage 0: Context Loading}
 - {Stage 0: Context Loading}
 - {Stage 1: Planning}
 - {Stage 1: Planning}
 
 
 Stage Outputs:
 Stage Outputs:
+
 - {Stage 0}:
 - {Stage 0}:
   - {Output 1}
   - {Output 1}
   - {Output 2}
   - {Output 2}
 
 
 ## Key Decisions
 ## Key Decisions
+
 - [{timestamp}] {decision}
 - [{timestamp}] {decision}
   Rationale: {why this choice was made}
   Rationale: {why this choice was made}
 
 
 ## Files Created
 ## Files Created
+
 - {file path 1}
 - {file path 1}
 - {file path 2}
 - {file path 2}
 
 
 ## Exit Criteria
 ## Exit Criteria
+
 - [ ] {criterion 1}
 - [ ] {criterion 1}
 - [ ] {criterion 2}
 - [ ] {criterion 2}
 - [x] {completed criterion}
 - [x] {completed criterion}
@@ -132,16 +148,16 @@ Stage Outputs:
 **Stage 0: Initialize Session**
 **Stage 0: Initialize Session**
 
 
 ```typescript
 ```typescript
-import { createSession } from '.opencode/skill/task-management/scripts/session-context-manager';
+import { createSession } from ".opencode/skill/task-management/scripts/session-context-manager";
 
 
 const result = createSession(feature, request, {
 const result = createSession(feature, request, {
   contextFiles: [], // Will be populated by ContextScout
   contextFiles: [], // Will be populated by ContextScout
   referenceFiles: [],
   referenceFiles: [],
   exitCriteria: [
   exitCriteria: [
-    'All subtasks completed',
-    'Tests passing',
-    'Documentation updated'
-  ]
+    "All subtasks completed",
+    "Tests passing",
+    "Documentation updated",
+  ],
 });
 });
 
 
 const sessionId = result.sessionId;
 const sessionId = result.sessionId;
@@ -151,35 +167,35 @@ const sessionId = result.sessionId;
 **Between Stages: Update Context**
 **Between Stages: Update Context**
 
 
 ```typescript
 ```typescript
-import { updateSession, markStageComplete } from './session-context-manager';
+import { updateSession, markStageComplete } from "./session-context-manager";
 
 
 // After ContextScout completes
 // After ContextScout completes
 updateSession(sessionId, {
 updateSession(sessionId, {
   contextFiles: [
   contextFiles: [
-    '.opencode/context/core/standards/code-quality.md',
-    '.opencode/context/core/standards/security-patterns.md'
-  ]
+    ".opencode/context/core/standards/code-quality.md",
+    ".opencode/context/core/standards/security-patterns.md",
+  ],
 });
 });
 
 
 // After ArchitectureAnalyzer completes
 // After ArchitectureAnalyzer completes
 updateSession(sessionId, {
 updateSession(sessionId, {
   architecture: {
   architecture: {
-    boundedContext: 'authentication',
-    module: '@app/auth'
-  }
+    boundedContext: "authentication",
+    module: "@app/auth",
+  },
 });
 });
 
 
 // Mark stage complete
 // Mark stage complete
-markStageComplete(sessionId, 'Stage 1: Planning', [
-  '.tmp/tasks/auth-system/task.json',
-  '.tmp/tasks/auth-system/subtask_01.json'
+markStageComplete(sessionId, "Stage 1: Planning", [
+  ".tmp/tasks/auth-system/task.json",
+  ".tmp/tasks/auth-system/subtask_01.json",
 ]);
 ]);
 ```
 ```
 
 
 **Final Stage: Complete Session**
 **Final Stage: Complete Session**
 
 
 ```typescript
 ```typescript
-updateSession(sessionId, { status: 'completed' });
+updateSession(sessionId, { status: "completed" });
 ```
 ```
 
 
 ### TaskManager
 ### TaskManager
@@ -187,7 +203,7 @@ updateSession(sessionId, { status: 'completed' });
 **Stage 0: Load Session Context**
 **Stage 0: Load Session Context**
 
 
 ```typescript
 ```typescript
-import { loadSession } from '.opencode/skill/task-management/scripts/session-context-manager';
+import { loadSession } from ".opencode/skill/task-management/scripts/session-context-manager";
 
 
 const result = loadSession(sessionId);
 const result = loadSession(sessionId);
 if (!result.success) {
 if (!result.success) {
@@ -222,11 +238,11 @@ const adrs = session.adrs; // Architectural decisions
 **Stage 3: Update Progress**
 **Stage 3: Update Progress**
 
 
 ```typescript
 ```typescript
-import { addDecision } from './session-context-manager';
+import { addDecision } from "./session-context-manager";
 
 
 addDecision(sessionId, {
 addDecision(sessionId, {
-  decision: 'Split authentication into 3 subtasks: schema, service, middleware',
-  rationale: 'Each subtask is atomic (1-2 hours) and has clear dependencies'
+  decision: "Split authentication into 3 subtasks: schema, service, middleware",
+  rationale: "Each subtask is atomic (1-2 hours) and has clear dependencies",
 });
 });
 ```
 ```
 
 
@@ -235,18 +251,18 @@ addDecision(sessionId, {
 **Before Coding: Load Session Context**
 **Before Coding: Load Session Context**
 
 
 ```typescript
 ```typescript
-import { loadSession } from '.opencode/skill/task-management/scripts/session-context-manager';
+import { loadSession } from ".opencode/skill/task-management/scripts/session-context-manager";
 
 
 const result = loadSession(sessionId);
 const result = loadSession(sessionId);
 const session = result.session;
 const session = result.session;
 
 
 // Read context files (standards)
 // Read context files (standards)
-session.contextFiles.forEach(file => {
+session.contextFiles.forEach((file) => {
   // Load coding standards, security patterns
   // Load coding standards, security patterns
 });
 });
 
 
 // Read reference files (existing code)
 // Read reference files (existing code)
-session.referenceFiles.forEach(file => {
+session.referenceFiles.forEach((file) => {
   // Study existing patterns
   // Study existing patterns
 });
 });
 
 
@@ -259,10 +275,10 @@ const adrs = session.adrs; // Architectural decisions to follow
 **After Coding: Track Files Created**
 **After Coding: Track Files Created**
 
 
 ```typescript
 ```typescript
-import { addFile } from './session-context-manager';
+import { addFile } from "./session-context-manager";
 
 
-addFile(sessionId, 'src/auth/jwt.service.ts');
-addFile(sessionId, 'src/auth/jwt.service.test.ts');
+addFile(sessionId, "src/auth/jwt.service.ts");
+addFile(sessionId, "src/auth/jwt.service.test.ts");
 ```
 ```
 
 
 ### ContextScout
 ### ContextScout
@@ -270,18 +286,18 @@ addFile(sessionId, 'src/auth/jwt.service.test.ts');
 **After Discovery: Update Session**
 **After Discovery: Update Session**
 
 
 ```typescript
 ```typescript
-import { updateSession } from './session-context-manager';
+import { updateSession } from "./session-context-manager";
 
 
 updateSession(sessionId, {
 updateSession(sessionId, {
   contextFiles: [
   contextFiles: [
-    '.opencode/context/core/standards/code-quality.md',
-    '.opencode/context/core/standards/security-patterns.md',
-    '(example: .opencode/context/core/standards/naming-conventions.md)'
+    ".opencode/context/core/standards/code-quality.md",
+    ".opencode/context/core/standards/security-patterns.md",
+    "(example: .opencode/context/core/standards/naming-conventions.md)",
   ],
   ],
   referenceFiles: [
   referenceFiles: [
-    'src/middleware/auth.middleware.ts',
-    'src/config/jwt.config.ts'
-  ]
+    "src/middleware/auth.middleware.ts",
+    "src/config/jwt.config.ts",
+  ],
 });
 });
 ```
 ```
 
 
@@ -290,19 +306,20 @@ updateSession(sessionId, {
 **After Analysis: Update Session**
 **After Analysis: Update Session**
 
 
 ```typescript
 ```typescript
-import { updateSession, addDecision } from './session-context-manager';
+import { updateSession, addDecision } from "./session-context-manager";
 
 
 updateSession(sessionId, {
 updateSession(sessionId, {
   architecture: {
   architecture: {
-    boundedContext: 'authentication',
-    module: '@app/auth',
-    verticalSlice: 'user-login'
-  }
+    boundedContext: "authentication",
+    module: "@app/auth",
+    verticalSlice: "user-login",
+  },
 });
 });
 
 
 addDecision(sessionId, {
 addDecision(sessionId, {
-  decision: 'Place authentication in separate bounded context',
-  rationale: 'Auth is a core domain with clear boundaries, used by multiple features'
+  decision: "Place authentication in separate bounded context",
+  rationale:
+    "Auth is a core domain with clear boundaries, used by multiple features",
 });
 });
 ```
 ```
 
 
@@ -311,23 +328,23 @@ addDecision(sessionId, {
 **After Contract Definition: Update Session**
 **After Contract Definition: Update Session**
 
 
 ```typescript
 ```typescript
-import { updateSession } from './session-context-manager';
+import { updateSession } from "./session-context-manager";
 
 
 updateSession(sessionId, {
 updateSession(sessionId, {
   contracts: [
   contracts: [
     {
     {
-      type: 'api',
-      name: 'AuthAPI',
-      path: 'src/api/auth.contract.ts',
-      status: 'defined'
+      type: "api",
+      name: "AuthAPI",
+      path: "src/api/auth.contract.ts",
+      status: "defined",
     },
     },
     {
     {
-      type: 'interface',
-      name: 'JWTService',
-      path: 'src/auth/jwt.service.ts',
-      status: 'draft'
-    }
-  ]
+      type: "interface",
+      name: "JWTService",
+      path: "src/auth/jwt.service.ts",
+      status: "draft",
+    },
+  ],
 });
 });
 ```
 ```
 
 
@@ -338,6 +355,7 @@ updateSession(sessionId, {
 Initialize a new session with context.md file.
 Initialize a new session with context.md file.
 
 
 **Parameters:**
 **Parameters:**
+
 - `feature` (string) - Feature name (kebab-case)
 - `feature` (string) - Feature name (kebab-case)
 - `request` (string) - Original user request
 - `request` (string) - Original user request
 - `options` (object) - Optional configuration
 - `options` (object) - Optional configuration
@@ -351,14 +369,16 @@ Initialize a new session with context.md file.
   - `adrs` (array) - Architectural decision records
   - `adrs` (array) - Architectural decision records
 
 
 **Returns:**
 **Returns:**
+
 ```typescript
 ```typescript
 { success: boolean; sessionId?: string; error?: string }
 { success: boolean; sessionId?: string; error?: string }
 ```
 ```
 
 
 **Example:**
 **Example:**
+
 ```typescript
 ```typescript
-const result = createSession('auth-system', 'Implement JWT authentication', {
-  exitCriteria: ['All tests passing', 'JWT tokens signed with RS256']
+const result = createSession("auth-system", "Implement JWT authentication", {
+  exitCriteria: ["All tests passing", "JWT tokens signed with RS256"],
 });
 });
 // result.sessionId = "auth-system-2026-02-15T10-30-00-000Z"
 // result.sessionId = "auth-system-2026-02-15T10-30-00-000Z"
 ```
 ```
@@ -368,16 +388,19 @@ const result = createSession('auth-system', 'Implement JWT authentication', {
 Read session context from context.md.
 Read session context from context.md.
 
 
 **Parameters:**
 **Parameters:**
+
 - `sessionId` (string) - Session identifier
 - `sessionId` (string) - Session identifier
 
 
 **Returns:**
 **Returns:**
+
 ```typescript
 ```typescript
 { success: boolean; session?: SessionContext; error?: string }
 { success: boolean; session?: SessionContext; error?: string }
 ```
 ```
 
 
 **Example:**
 **Example:**
+
 ```typescript
 ```typescript
-const result = loadSession('auth-system-2026-02-15T10-30-00-000Z');
+const result = loadSession("auth-system-2026-02-15T10-30-00-000Z");
 if (result.success) {
 if (result.success) {
   const contextFiles = result.session.contextFiles;
   const contextFiles = result.session.contextFiles;
   const architecture = result.session.architecture;
   const architecture = result.session.architecture;
@@ -389,6 +412,7 @@ if (result.success) {
 Append new information to session context.
 Append new information to session context.
 
 
 **Parameters:**
 **Parameters:**
+
 - `sessionId` (string) - Session identifier
 - `sessionId` (string) - Session identifier
 - `updates` (object) - Fields to update
 - `updates` (object) - Fields to update
   - `status` - 'in_progress' | 'completed' | 'blocked'
   - `status` - 'in_progress' | 'completed' | 'blocked'
@@ -401,20 +425,27 @@ Append new information to session context.
   - `adrs` - Add ADRs
   - `adrs` - Add ADRs
 
 
 **Returns:**
 **Returns:**
+
 ```typescript
 ```typescript
 { success: boolean; error?: string }
 { success: boolean; error?: string }
 ```
 ```
 
 
 **Example:**
 **Example:**
+
 ```typescript
 ```typescript
 updateSession(sessionId, {
 updateSession(sessionId, {
   architecture: {
   architecture: {
-    boundedContext: 'authentication',
-    module: '@app/auth'
+    boundedContext: "authentication",
+    module: "@app/auth",
   },
   },
   contracts: [
   contracts: [
-    { type: 'api', name: 'AuthAPI', path: 'src/api/auth.contract.ts', status: 'defined' }
-  ]
+    {
+      type: "api",
+      name: "AuthAPI",
+      path: "src/api/auth.contract.ts",
+      status: "defined",
+    },
+  ],
 });
 });
 ```
 ```
 
 
@@ -423,21 +454,24 @@ updateSession(sessionId, {
 Mark a workflow stage as complete and record outputs.
 Mark a workflow stage as complete and record outputs.
 
 
 **Parameters:**
 **Parameters:**
+
 - `sessionId` (string) - Session identifier
 - `sessionId` (string) - Session identifier
 - `stage` (string) - Stage name (e.g., "Stage 1: Planning")
 - `stage` (string) - Stage name (e.g., "Stage 1: Planning")
 - `outputs` (string[]) - Files/artifacts created in this stage
 - `outputs` (string[]) - Files/artifacts created in this stage
 
 
 **Returns:**
 **Returns:**
+
 ```typescript
 ```typescript
 { success: boolean; error?: string }
 { success: boolean; error?: string }
 ```
 ```
 
 
 **Example:**
 **Example:**
+
 ```typescript
 ```typescript
-markStageComplete(sessionId, 'Stage 1: Planning', [
-  '.tmp/tasks/auth-system/task.json',
-  '.tmp/tasks/auth-system/subtask_01.json',
-  '.tmp/tasks/auth-system/subtask_02.json'
+markStageComplete(sessionId, "Stage 1: Planning", [
+  ".tmp/tasks/auth-system/task.json",
+  ".tmp/tasks/auth-system/subtask_01.json",
+  ".tmp/tasks/auth-system/subtask_02.json",
 ]);
 ]);
 ```
 ```
 
 
@@ -446,21 +480,25 @@ markStageComplete(sessionId, 'Stage 1: Planning', [
 Log a key decision with rationale.
 Log a key decision with rationale.
 
 
 **Parameters:**
 **Parameters:**
+
 - `sessionId` (string) - Session identifier
 - `sessionId` (string) - Session identifier
 - `decision` (object)
 - `decision` (object)
   - `decision` (string) - What was decided
   - `decision` (string) - What was decided
   - `rationale` (string) - Why this choice was made
   - `rationale` (string) - Why this choice was made
 
 
 **Returns:**
 **Returns:**
+
 ```typescript
 ```typescript
 { success: boolean; error?: string }
 { success: boolean; error?: string }
 ```
 ```
 
 
 **Example:**
 **Example:**
+
 ```typescript
 ```typescript
 addDecision(sessionId, {
 addDecision(sessionId, {
-  decision: 'Use RS256 for JWT signing instead of HS256',
-  rationale: 'RS256 (asymmetric) is more secure for distributed systems where tokens are verified by multiple services'
+  decision: "Use RS256 for JWT signing instead of HS256",
+  rationale:
+    "RS256 (asymmetric) is more secure for distributed systems where tokens are verified by multiple services",
 });
 });
 ```
 ```
 
 
@@ -469,18 +507,21 @@ addDecision(sessionId, {
 Track a file created during the session.
 Track a file created during the session.
 
 
 **Parameters:**
 **Parameters:**
+
 - `sessionId` (string) - Session identifier
 - `sessionId` (string) - Session identifier
 - `filePath` (string) - Path to created file
 - `filePath` (string) - Path to created file
 
 
 **Returns:**
 **Returns:**
+
 ```typescript
 ```typescript
 { success: boolean; error?: string }
 { success: boolean; error?: string }
 ```
 ```
 
 
 **Example:**
 **Example:**
+
 ```typescript
 ```typescript
-addFile(sessionId, 'src/auth/jwt.service.ts');
-addFile(sessionId, 'src/auth/jwt.service.test.ts');
+addFile(sessionId, "src/auth/jwt.service.ts");
+addFile(sessionId, "src/auth/jwt.service.test.ts");
 ```
 ```
 
 
 ### getSessionSummary(sessionId)
 ### getSessionSummary(sessionId)
@@ -488,9 +529,11 @@ addFile(sessionId, 'src/auth/jwt.service.test.ts');
 Get current session state summary.
 Get current session state summary.
 
 
 **Parameters:**
 **Parameters:**
+
 - `sessionId` (string) - Session identifier
 - `sessionId` (string) - Session identifier
 
 
 **Returns:**
 **Returns:**
+
 ```typescript
 ```typescript
 {
 {
   success: boolean;
   success: boolean;
@@ -510,11 +553,14 @@ Get current session state summary.
 ```
 ```
 
 
 **Example:**
 **Example:**
+
 ```typescript
 ```typescript
 const result = getSessionSummary(sessionId);
 const result = getSessionSummary(sessionId);
 console.log(`Progress: ${result.summary.completedStages} stages complete`);
 console.log(`Progress: ${result.summary.completedStages} stages complete`);
 console.log(`Files: ${result.summary.filesCreated} created`);
 console.log(`Files: ${result.summary.filesCreated} created`);
-console.log(`Exit Criteria: ${result.summary.exitCriteriaMet}/${result.summary.exitCriteriaTotal}`);
+console.log(
+  `Exit Criteria: ${result.summary.exitCriteriaMet}/${result.summary.exitCriteriaTotal}`,
+);
 ```
 ```
 
 
 ## CLI Usage
 ## CLI Usage
@@ -542,70 +588,80 @@ npx ts-node session-context-manager.ts summary auth-system-2026-02-15T10-30-00-0
 ## Best Practices
 ## Best Practices
 
 
 ### 1. Initialize Early
 ### 1. Initialize Early
+
 Create session at the start of orchestration, before any agent work begins.
 Create session at the start of orchestration, before any agent work begins.
 
 
 ### 2. Update After Each Stage
 ### 2. Update After Each Stage
+
 Every agent that completes work should update the session context.
 Every agent that completes work should update the session context.
 
 
 ### 3. Read Before Acting
 ### 3. Read Before Acting
+
 Every agent should load session context before starting work.
 Every agent should load session context before starting work.
 
 
 ### 4. Track Decisions
 ### 4. Track Decisions
+
 Use `addDecision()` for any architectural or design choice.
 Use `addDecision()` for any architectural or design choice.
 
 
 ### 5. Track Files
 ### 5. Track Files
+
 Use `addFile()` for every file created (helps with cleanup, rollback).
 Use `addFile()` for every file created (helps with cleanup, rollback).
 
 
 ### 6. Use Exit Criteria
 ### 6. Use Exit Criteria
+
 Define clear, binary exit criteria at session creation.
 Define clear, binary exit criteria at session creation.
 
 
 ## Example: Full Orchestration Flow
 ## Example: Full Orchestration Flow
 
 
 ```typescript
 ```typescript
 // Orchestrator: Initialize
 // Orchestrator: Initialize
-const { sessionId } = createSession('auth-system', 'Implement JWT authentication', {
-  exitCriteria: ['All tests passing', 'JWT tokens signed with RS256']
-});
+const { sessionId } = createSession(
+  "auth-system",
+  "Implement JWT authentication",
+  {
+    exitCriteria: ["All tests passing", "JWT tokens signed with RS256"],
+  },
+);
 
 
 // Stage 0: ContextScout discovers context
 // Stage 0: ContextScout discovers context
 updateSession(sessionId, {
 updateSession(sessionId, {
-  contextFiles: ['.opencode/context/core/standards/code-quality.md'],
-  referenceFiles: ['src/middleware/auth.middleware.ts']
+  contextFiles: [".opencode/context/core/standards/code-quality.md"],
+  referenceFiles: ["src/middleware/auth.middleware.ts"],
 });
 });
-markStageComplete(sessionId, 'Stage 0: Context Loading', []);
+markStageComplete(sessionId, "Stage 0: Context Loading", []);
 
 
 // Stage 1: ArchitectureAnalyzer analyzes
 // Stage 1: ArchitectureAnalyzer analyzes
 updateSession(sessionId, {
 updateSession(sessionId, {
-  architecture: { boundedContext: 'authentication', module: '@app/auth' }
+  architecture: { boundedContext: "authentication", module: "@app/auth" },
 });
 });
 addDecision(sessionId, {
 addDecision(sessionId, {
-  decision: 'Separate bounded context for auth',
-  rationale: 'Core domain with clear boundaries'
+  decision: "Separate bounded context for auth",
+  rationale: "Core domain with clear boundaries",
 });
 });
-markStageComplete(sessionId, 'Stage 1: Architecture Analysis', []);
+markStageComplete(sessionId, "Stage 1: Architecture Analysis", []);
 
 
 // Stage 2: TaskManager creates tasks
 // Stage 2: TaskManager creates tasks
 const { session } = loadSession(sessionId);
 const { session } = loadSession(sessionId);
 // Use session.contextFiles, session.architecture in task.json
 // Use session.contextFiles, session.architecture in task.json
-markStageComplete(sessionId, 'Stage 2: Task Planning', [
-  '.tmp/tasks/auth-system/task.json'
+markStageComplete(sessionId, "Stage 2: Task Planning", [
+  ".tmp/tasks/auth-system/task.json",
 ]);
 ]);
 
 
 // Stage 3: CoderAgent implements
 // Stage 3: CoderAgent implements
 const { session } = loadSession(sessionId);
 const { session } = loadSession(sessionId);
 // Read session.contextFiles, session.contracts
 // Read session.contextFiles, session.contracts
-addFile(sessionId, 'src/auth/jwt.service.ts');
-markStageComplete(sessionId, 'Stage 3: Implementation', [
-  'src/auth/jwt.service.ts'
+addFile(sessionId, "src/auth/jwt.service.ts");
+markStageComplete(sessionId, "Stage 3: Implementation", [
+  "src/auth/jwt.service.ts",
 ]);
 ]);
 
 
 // Stage 4: Complete
 // Stage 4: Complete
-updateSession(sessionId, { status: 'completed' });
+updateSession(sessionId, { status: "completed" });
 ```
 ```
 
 
 ## Related
 ## Related
 
 
 - `.opencode/skill/task-management/scripts/session-context-manager.ts` - Implementation
 - `.opencode/skill/task-management/scripts/session-context-manager.ts` - Implementation
 - `.opencode/context/core/task-management/standards/task-schema.md` - Task JSON schema
 - `.opencode/context/core/task-management/standards/task-schema.md` - Task JSON schema
-- `.opencode/context/core/workflows/task-delegation.md` - Multi-agent orchestration
+- `.opencode/context/core/workflows/task-delegation-basics.md` - Multi-agent orchestration
 - `.tmp/sessions/test-task-manager/context.md` - Example session context
 - `.tmp/sessions/test-task-manager/context.md` - Example session context

+ 8 - 1
install.sh

@@ -313,7 +313,14 @@ resolve_component_path() {
     registry_key=$(get_registry_key "$component_type")
     registry_key=$(get_registry_key "$component_type")
 
 
     if [ "$component_type" = "context" ] && [[ "$component_id" == */* ]]; then
     if [ "$component_type" = "context" ] && [[ "$component_id" == */* ]]; then
-        jq_exec "first(.components.contexts[]? | select(.path == \".opencode/context/${component_id}.md\") | .path)" "$TEMP_DIR/registry.json"
+        # Try .md extension first (most context files), then fall back to the
+        # path as-is for non-markdown files (e.g. paths.json). Fixes #251.
+        local result
+        result=$(jq_exec "first(.components.contexts[]? | select(.path == \".opencode/context/${component_id}.md\") | .path)" "$TEMP_DIR/registry.json")
+        if [ -z "$result" ] || [ "$result" = "null" ]; then
+            result=$(jq_exec "first(.components.contexts[]? | select(.path == \".opencode/context/${component_id}\") | .path)" "$TEMP_DIR/registry.json")
+        fi
+        echo "$result"
         return
         return
     fi
     fi
 
 

+ 72 - 0
packages/plugin-abilities/src/context/discovery.ts

@@ -0,0 +1,72 @@
+import { glob } from 'glob';
+import path from 'path';
+import fs from 'fs/promises';
+import { parse as parseYaml } from 'yaml';
+import { ContextDefinitionSchema, type ContextDefinition, type LoadedContext } from './types.js';
+
+export interface DiscoveryOptions {
+  rootDir?: string;
+  contextDir?: string;
+}
+
+export class ContextDiscovery {
+  private rootDir: string;
+  private contextDir: string;
+
+  constructor(options: DiscoveryOptions = {}) {
+    this.rootDir = options.rootDir || process.cwd();
+    this.contextDir = options.contextDir || path.join(this.rootDir, '.opencode', 'context');
+  }
+
+  async discover(): Promise<ContextDefinition[]> {
+    const files = await glob('**/*.{yaml,yml,json}', {
+      cwd: this.contextDir,
+      ignore: ['node_modules/**'],
+    });
+
+    const definitions: ContextDefinition[] = [];
+
+    for (const file of files) {
+      const filePath = path.join(this.contextDir, file);
+      try {
+        const content = await fs.readFile(filePath, 'utf-8');
+        const parsed = file.endsWith('.json') ? JSON.parse(content) : parseYaml(content);
+        
+        // Handle array of definitions or single definition
+        const items = Array.isArray(parsed) ? parsed : [parsed];
+        
+        for (const item of items) {
+          const result = ContextDefinitionSchema.safeParse(item);
+          if (result.success) {
+            definitions.push(result.data);
+          } else {
+            console.warn(`Invalid context definition in ${file}:`, result.error.format());
+          }
+        }
+      } catch (error) {
+        console.warn(`Failed to load context file ${file}:`, error);
+      }
+    }
+
+    return definitions;
+  }
+
+  async loadContext(definition: ContextDefinition): Promise<LoadedContext | null> {
+    if (definition.type === 'file') {
+      const filePath = path.resolve(this.rootDir, definition.path);
+      try {
+        const content = await fs.readFile(filePath, 'utf-8');
+        return {
+          definition,
+          content,
+          source: filePath,
+        };
+      } catch (error) {
+        console.warn(`Failed to read context file ${definition.path}:`, error);
+        return null;
+      }
+    }
+    // TODO: Handle URL and API types
+    return null;
+  }
+}

+ 35 - 0
packages/plugin-abilities/src/context/types.ts

@@ -0,0 +1,35 @@
+import { z } from 'zod';
+
+export const ContextTypeSchema = z.enum(['file', 'url', 'api', 'snippet']);
+
+export const ContextDefinitionSchema = z.object({
+  id: z.string(),
+  type: ContextTypeSchema,
+  path: z.string(),
+  description: z.string().optional(),
+  priority: z.number().optional(),
+  metadata: z.record(z.unknown()).optional(),
+});
+
+export type ContextDefinition = z.infer<typeof ContextDefinitionSchema>;
+
+export interface LoadedContext {
+  definition: ContextDefinition;
+  content: string;
+  source: string;
+}
+
+export const SkillPermissionSchema = z.object({
+  skill: z.string(),
+  tools: z.array(z.string()).optional(),
+  resources: z.array(z.string()).optional(),
+  description: z.string().optional(),
+});
+
+export const AgentPermissionsSchema = z.object({
+  agent: z.string(),
+  permissions: z.array(SkillPermissionSchema),
+});
+
+export type SkillPermission = z.infer<typeof SkillPermissionSchema>;
+export type AgentPermissions = z.infer<typeof AgentPermissionsSchema>;

+ 5 - 0
packages/plugin-abilities/src/index.ts

@@ -24,6 +24,11 @@ export { loadAbilities, loadAbility } from './loader/index.js'
 
 
 // Validator
 // Validator
 export { validateAbility, validateInputs } from './validator/index.js'
 export { validateAbility, validateInputs } from './validator/index.js'
+export { PermissionValidator } from './validator/permissions.js'
+
+// Context Discovery
+export { ContextDiscovery } from './context/discovery.js'
+export type { ContextDefinition, LoadedContext, AgentPermissions } from './context/types.js'
 
 
 // Executor
 // Executor
 export { executeAbility, formatExecutionResult } from './executor/index.js'
 export { executeAbility, formatExecutionResult } from './executor/index.js'

+ 45 - 0
packages/plugin-abilities/src/validator/permissions.ts

@@ -0,0 +1,45 @@
+import { AgentPermissionsSchema, type AgentPermissions, type SkillPermission } from '../context/types.js';
+
+export interface PermissionValidationResult {
+  valid: boolean;
+  errors: string[];
+}
+
+export class PermissionValidator {
+  validateAgentPermissions(data: unknown): PermissionValidationResult {
+    const result = AgentPermissionsSchema.safeParse(data);
+    
+    if (!result.success) {
+      return {
+        valid: false,
+        errors: result.error.errors.map(e => `${e.path.join('.')}: ${e.message}`),
+      };
+    }
+
+    return {
+      valid: true,
+      errors: [],
+    };
+  }
+
+  checkSkillAccess(agentPermissions: AgentPermissions, skillName: string, toolName: string): boolean {
+    const permission = agentPermissions.permissions.find(p => p.skill === skillName);
+    
+    if (!permission) {
+      // Default deny if no explicit permission for skill
+      return false;
+    }
+
+    if (!permission.tools) {
+      // If tools not specified, assume strict/deny or allow all?
+      // Security best practice: Default deny.
+      return false;
+    }
+
+    if (permission.tools.includes('*') || permission.tools.includes(toolName)) {
+      return true;
+    }
+
+    return false;
+  }
+}

+ 1 - 1
plugins/claude-code/.claude-plugin/plugin.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "oac",
   "name": "oac",
   "description": "OpenAgentsControl — multi-agent orchestration for Claude Code. Context-aware development with skills, subagents, parallel execution, and automated code review.",
   "description": "OpenAgentsControl — multi-agent orchestration for Claude Code. Context-aware development with skills, subagents, parallel execution, and automated code review.",
-  "version": "1.0.1",
+  "version": "1.0.2",
   "author": {
   "author": {
     "name": "darrenhinde",
     "name": "darrenhinde",
     "url": "https://github.com/darrenhinde"
     "url": "https://github.com/darrenhinde"

+ 16 - 1
plugins/claude-code/README.md

@@ -320,7 +320,21 @@ Stage 4: Execute with loaded context → No nested discovery needed
 
 
 ## 🔧 Configuration
 ## 🔧 Configuration
 
 
-The plugin uses context files from the main OpenAgents Control repository.
+### Model: opusplan
+
+The plugin ships with `settings.json` at the plugin root:
+
+```json
+{
+  "model": "opusplan"
+}
+```
+
+`opusplan` uses **Opus for planning/orchestration** (the main agent) and **Sonnet for execution** (subagents). This matches OAC's plan-first workflow and gives you Opus-quality reasoning without paying Opus rates for every tool call.
+
+Subagents that need a lighter model override this at the agent level (e.g. `external-scout` uses `haiku`). The root setting only affects the main orchestrating agent.
+
+To reload after any settings change: `/reload-plugins` (no restart needed).
 
 
 ### Context Structure
 ### Context Structure
 
 
@@ -328,6 +342,7 @@ The plugin uses context files from the main OpenAgents Control repository.
 plugins/claude-code/
 plugins/claude-code/
 ├── .claude-plugin/
 ├── .claude-plugin/
 │   └── plugin.json              # Plugin metadata
 │   └── plugin.json              # Plugin metadata
+├── settings.json                # Model config: opusplan
 ├── agents/                      # Custom subagents (7 files)
 ├── agents/                      # Custom subagents (7 files)
 │   ├── task-manager.md
 │   ├── task-manager.md
 │   ├── context-scout.md
 │   ├── context-scout.md

+ 4 - 2
plugins/claude-code/scripts/install-context.sh

@@ -72,7 +72,8 @@ check_dependencies() {
 }
 }
 
 
 download_context() {
 download_context() {
-  local categories=($1)
+  local categories
+  read -ra categories <<< "$1"
   local temp_dir
   local temp_dir
   temp_dir="$(mktemp -d)"
   temp_dir="$(mktemp -d)"
   # shellcheck disable=SC2064
   # shellcheck disable=SC2064
@@ -113,7 +114,8 @@ download_context() {
 
 
 write_manifest() {
 write_manifest() {
   local profile="$1"
   local profile="$1"
-  local categories=($2)
+  local categories
+  read -ra categories <<< "$2"
   local commit="$3"
   local commit="$3"
   local timestamp
   local timestamp
   timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
   timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

+ 3 - 0
plugins/claude-code/settings.json

@@ -0,0 +1,3 @@
+{
+  "model": "opusplan"
+}