Skip to content

Commit 369c233

Browse files
committed
feat: architecture upgrade Level 3→7 — composable scoring, structured diagnostics, lifecycle hooks, convergence loop, content-hash cache, cancellation tokens
- shared-types: add PackDiagnostic, PackScorer, ActivationContext, ActivationResult, CancellationTokens, OrchestratorHooks, HookRunner, ActivationEventEmitter, result factories - orchestrator/matcher: decompose monolithic scorePack into 6 composable scorers + createCompositeScorer - orchestrator: rewrite with content-hash cache, convergence-based conflict resolution, HookRunner lifecycle events, ActivationEventEmitter, CancellationToken support, new activate() pipeline - policy-service: emit PackDiagnostic with severity/tag/suggestion alongside existing reasons[] - hub-api: surface evaluation.diagnostics in all activation responses - mcp-gateway: surface diagnostics in activatePackSet + startProjectFromSpec - docs: add DESIGN-ESSENCES.md (30 patterns from 10 repos) + IMPACT-ASSESSMENT.md - README: update architecture diagram, key features, project structure descriptions
1 parent 4381ab5 commit 369c233

9 files changed

Lines changed: 2189 additions & 103 deletions

File tree

README.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
Point PackForge at any project and it auto-detects the stack, domain, project phase, and risk profile — no config files needed. It reads `package.json`, file trees, CI workflows, and existing tooling to build a full picture in seconds.
2626

2727
### Context-Aware Pack Activation
28-
Based on the analysis, PackForge selects the right instruction packs (system prompts, constraints, tool permissions) from a registry of **18 packs across 5 categories**. Packs that don't match your context are filtered out — your AI agent only gets relevant instructions.
28+
Based on the analysis, PackForge selects the right instruction packs (system prompts, constraints, tool permissions) from a registry of **18 packs across 5 categories**. Six composable scorers (stack, taxonomy, keyword, tool-awareness, feedback, work-mode) are combined via `createCompositeScorer()` — swap or weight individual scorers without touching the pipeline. Packs that don't match your context are filtered out; every decision emits structured diagnostics with severity, tags, and actionable suggestions.
2929

3030
### Smart Tool Onboarding
3131
PackForge detects whether GitNexus, MemPalace, and Obsidian are installed — both globally and per-project. Missing tools surface with install guides, concrete benefits, and a list of packs waiting for them. Users can permanently decline suggestions they don't want.
@@ -51,6 +51,13 @@ The core workflow is two commands: `start_project_from_spec` → `confirm_activa
5151
### Key Features
5252

5353
- **Context-Aware Recommendations** — analyzes `package.json`, file tree, GitNexus graph, and MemPalace memory to understand your project
54+
- **Composable Scoring** — six individual `PackScorer` functions combined via `createCompositeScorer()`; swap, weight, or extend scorers without touching the pipeline
55+
- **Structured Diagnostics** — every policy check and conflict resolution emits `PackDiagnostic` with severity, tags, and actionable suggestions
56+
- **Lifecycle Hooks** — typed `HookRunner` emits events at each pipeline stage (`context:analyzed`, `scoring:complete`, `policy:evaluated`, `activation:before/after/error`)
57+
- **Event Subscribers** — decoupled `ActivationSubscriber` pattern for telemetry, logging, or external integrations
58+
- **Content-Hash Cache** — pack registry invalidates only when YAML files actually change (SHA-256 hash of directory contents), replacing TTL-based expiration
59+
- **Convergence-Based Conflict Resolution** — iterative conflict loop that stabilizes the kept set instead of single-pass filtering
60+
- **Cancellation Tokens**`abort` (hard error) + `cancel` (graceful shutdown) propagated through `ActivationContext`
5461
- **18 Instruction Packs** — covering engineering, quality, ops, product, and documentation workflows
5562
- **2-Tool UX**`start_project_from_spec``confirm_activation`. That's it.
5663
- **Staged Activation** — packs requiring unavailable tools (GitNexus, MemPalace) are held as pending; `reload_activation` promotes them once tools are set up
@@ -71,17 +78,19 @@ The core workflow is two commands: `start_project_from_spec` → `confirm_activa
7178
┌────────────────────────────▼──────────────────────────────┐
7279
│ MCP Gateway │
7380
│ (stdio MCP server, 10 tools) │
81+
│ + diagnostics in every response │
7482
└──┬──────────┬──────────┬──────────┬───────────────────────┘
7583
│ │ │ │
7684
┌──▼───────┐ ┌▼────────┐ ┌▼────────┐ ┌▼──────────────┐
7785
│ Context │ │Orchestr.│ │ Policy │ │ Memory │
78-
│ Analyzer │ │(match + │ │ Service │ │ Service │
79-
│ │ │ score) │ │(govern.)│ │(JSON persist.)│
86+
│ Analyzer │ │ hooks + │ │ Service │ │ Service │
87+
│ │ │ events +│ │ struct. │ │(JSON persist.)│
88+
│ │ │ converg.│ │ diags. │ │ │
8089
└──┬───┬───┘ └────┬────┘ └─────────┘ └───────────────┘
8190
│ │ │
8291
┌──▼┐ ┌▼────┐ ┌──▼─────────────────────────────────┐
8392
│GN │ │ MP │ │ Pack Registry (YAML) │
84-
│ │ │ │ │ 18 packs across 5 categories
93+
│ │ │ │ │ 18 packs · content-hash cache
8594
└───┘ └─────┘ └─────────────────────────────────────┘
8695
8796
GN = GitNexus (code graph) MP = MemPalace (persistent memory)
@@ -220,15 +229,15 @@ apps/
220229
knowledge-compiler/ # Compiles Obsidian vault blueprints to YAML packs
221230
mcp-gateway/ # MCP stdio server (primary entry point, 10 tools)
222231
memory-service/ # Activation state persistence (JSON file storage)
223-
orchestrator/ # Matches and scores packs against context
224-
policy-service/ # Governance, approval, and risk evaluation
232+
orchestrator/ # Composable scoring, lifecycle hooks, convergence loop, content-hash cache
233+
policy-service/ # Governance, approval, risk evaluation, structured diagnostics
225234
packages/
226235
pack-validator/ # YAML pack validation and registry builder
227236
shared-auth/ # JWT / JWKS auth primitives
228237
shared-config/ # Centralized config schemas
229238
shared-otel/ # OpenTelemetry instrumentation
230239
shared-policy/ # Policy domain models
231-
shared-types/ # Canonical Zod schemas (context, packs, activation)
240+
shared-types/ # Canonical Zod schemas, ActivationContext, HookRunner, diagnostics, result factories
232241
packs/ # 18 YAML instruction packs across 5 categories
233242
vault/ # Obsidian blueprints for pack authoring
234243
scripts/ # Validation and export utilities

apps/hub-api/src/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
ProjectContextSchema,
1616
RuntimeHandoffContractSchema,
1717
type InstructionPack,
18+
type PackDiagnostic,
1819
type ProjectContext,
1920
type RuntimeHandoffContract,
2021
} from '@hub/shared-types'
@@ -224,13 +225,13 @@ export function createHubApiApp(options?: { packsDir?: string; memoryFilePath?:
224225
if (evaluation.decision === 'deny') {
225226
const activation = await memoryService.recordActivation({ status: 'denied', plan })
226227
reply.code(403)
227-
return { activation }
228+
return { activation, diagnostics: evaluation.diagnostics }
228229
}
229230

230231
if (evaluation.decision === 'confirm' && !body.autoApprove) {
231232
const activation = await memoryService.recordActivation({ status: 'pending_confirmation', plan })
232233
reply.code(202)
233-
return { activation }
234+
return { activation, diagnostics: evaluation.diagnostics }
234235
}
235236

236237
const activationId = randomUUID()
@@ -244,7 +245,7 @@ export function createHubApiApp(options?: { packsDir?: string; memoryFilePath?:
244245
})
245246
const activation = await memoryService.recordActivation({ status: 'active', plan, handoff })
246247

247-
return { activation }
248+
return { activation, diagnostics: evaluation.diagnostics }
248249
})
249250

250251
app.get('/activations/:id', async (request, reply) => {

apps/mcp-gateway/src/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
type BootstrapStep,
1818
type InstructionPack,
1919
type MissingTool,
20+
type PackDiagnostic,
2021
type PendingPack,
2122
type ProjectContext,
2223
type RuntimeHandoffContract,
@@ -323,7 +324,14 @@ export function createGatewayHandlers(options?: { packsDir?: string; memoryFileP
323324
const handoff =
324325
status !== 'denied' ? buildHandoffContract(activationId, plan, packs, evaluation, declinedTools) : undefined
325326

326-
return memoryService.recordActivation({ id: activationId, status, plan, ...(handoff ? { handoff } : {}) })
327+
const activation = await memoryService.recordActivation({
328+
id: activationId,
329+
status,
330+
plan,
331+
...(handoff ? { handoff } : {}),
332+
})
333+
334+
return { ...activation, diagnostics: evaluation.diagnostics }
327335
},
328336
async getActivationStatus(input: { activationId: string }) {
329337
return memoryService.getActivation(input.activationId)
@@ -401,6 +409,7 @@ export function createGatewayHandlers(options?: { packsDir?: string; memoryFileP
401409
blockedPacks: result.blockedPacks,
402410
policyDecision: plan.policyDecision,
403411
policyReasons: plan.policyReasons,
412+
diagnostics: evaluation.diagnostics,
404413
}
405414
},
406415
async confirmActivation(input: { activationId: string }) {

0 commit comments

Comments
 (0)