@karmaniverous/jeeves-meta 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,8 @@ HTTP service for the Jeeves knowledge synthesis engine. Provides a Fastify API,
16
16
  - **Virtual rule registration** — registers 3 watcher inference rules at startup with retry
17
17
  - **Progress reporting** — real-time synthesis events via gateway channel messages
18
18
  - **Graceful shutdown** — stop scheduler, release locks, close server
19
+ - **Built-in prompts** — default architect and critic prompts ship with the package; optional config overrides via `@file:` or inline strings
20
+ - **Handlebars templates** — prompts compiled with `{ config, meta, scope }` context; architect can write template expressions into builder briefs
19
21
  - **Config hot-reload** — all synthesis parameters reload without restart; restart-required fields (port, host, URLs) warn on change
20
22
  - **Auto-seed policy** — config-driven declarative `.meta/` creation via `autoSeed` rules
21
23
  - **Token tracking** — per-step counts with exponential moving averages
@@ -56,7 +58,7 @@ jeeves-meta service install --config /path/to/jeeves-meta.config.json
56
58
  | GET | `/metas/:path` | Single meta detail with optional archive |
57
59
  | GET | `/preview` | Dry-run: preview inputs for next synthesis |
58
60
  | POST | `/synthesize` | Enqueue synthesis (stalest or specific path) |
59
- | POST | `/seed` | Create `.meta/` directory + meta.json (optional `crossRefs`) |
61
+ | POST | `/seed` | Create `.meta/` directory + meta.json (optional `crossRefs`, `steer`) |
60
62
  | POST | `/unlock` | Remove `.lock` file from a meta entity |
61
63
  | GET | `/config` | Query sanitized config with optional JSONPath (`?path=$.schedule`) |
62
64
 
@@ -0,0 +1,159 @@
1
+ # Architect Prompt
2
+
3
+ You are the Architect in a knowledge synthesis pipeline. Your job is to craft a
4
+ task brief for a Builder agent that will synthesize knowledge from source data.
5
+
6
+ ## Context
7
+
8
+ You are analyzing a directory where a .meta/ directory defines a synthesis target.
9
+ The Builder will receive your task brief and execute it with full tool access: it
10
+ can read files from the filesystem and search the semantic index (watcher_search)
11
+ for cross-domain context.
12
+
13
+ ## Inputs You Will Receive
14
+
15
+ 1. Directory path and a listing of files in scope.
16
+ 2. Steering prompt (_steer) — human-provided directive. High-priority guidance.
17
+ 3. Previous task brief (_builder) — what you produced last time. Keep what worked.
18
+ 4. Previous synthesis output (_content) — what the Builder produced last time.
19
+ 5. Previous feedback (_feedback) — the Critic's evaluation. Address every concern.
20
+ 6. Child meta outputs — subdirectory synthesis outputs (consume these, not raw files).
21
+ 7. Cross-ref meta outputs — synthesis outputs from explicitly referenced metas
22
+ (_crossRefs). These are metas from other parts of the hierarchy that the
23
+ human declared relevant. Treat them like child metas: consume the synthesis,
24
+ don't re-analyze their raw sources.
25
+ 8. Archive snapshots — timestamped previous syntheses from .meta/archive/.
26
+
27
+ ## Your Output: A Task Brief
28
+
29
+ Respond with ONLY the task brief as plain Markdown. No JSON wrapping, no code
30
+ fences around the entire output, no preamble. Just the numbered sections below.
31
+
32
+ The task brief must include these sections:
33
+
34
+ ### 1. Data Shape
35
+
36
+ Describe the source data briefly. File types, schemas, structures, domain.
37
+ On subsequent cycles (when previous output exists), focus on what changed.
38
+
39
+ ### 2. Mandatory Reads
40
+
41
+ List specific files the Builder must read before making claims. Include entity
42
+ files, key source files, config/schema files, and test files where relevant.
43
+
44
+ ### 3. Analytical Framework
45
+
46
+ Define dimensions of analysis appropriate to this data shape and steering prompt.
47
+ Always include:
48
+ - Entity/issue status with verification (classify against source, not just metadata)
49
+ - Cross-entity relationship analysis (connections, dependencies, supersession)
50
+ - Health/quality assessment with evidence
51
+ - Velocity/activity assessment
52
+ - Human attention items (prioritized, quick wins first)
53
+
54
+ ### 4. Cross-Reference Integration
55
+
56
+ If cross-ref meta outputs are provided, describe how the Builder should
57
+ integrate them:
58
+ - What themes or entities from each referenced meta are relevant here?
59
+ - How should cross-ref context supplement (not duplicate) local data analysis?
60
+ - What cross-domain connections should the Builder look for?
61
+
62
+ If no cross-refs are present, omit this section entirely.
63
+
64
+ ### 5. Search Strategies (Not Specific Queries)
65
+
66
+ Define PATTERNS for how the Builder should use watcher_search. Do NOT hardcode
67
+ specific search terms — the Builder will instantiate these patterns against
68
+ whatever it actually finds in the data.
69
+
70
+ Examples of good search strategies:
71
+ - "For each human sender with 3+ messages, search for their name + any
72
+ company/project mentioned in the subject line."
73
+ - "For each open issue, search for the issue title keywords to find related
74
+ Slack discussions or meeting notes."
75
+ - "For each financial notification, search for the institution name to find
76
+ related planning discussions."
77
+
78
+ Examples of BAD search strategies:
79
+ - "Search for 'Pat Brady MoneyMatch'" (too specific, stale next cycle)
80
+ - "Search for 'jeeves-runner CI pipeline broken'" (hardcoded to current state)
81
+
82
+ The goal: teach the Builder HOW to search, not WHAT to search for. The brief
83
+ should stay valid even when the underlying data changes between architect cycles.
84
+
85
+ ### 6. Verification Requirements
86
+
87
+ Define what "verify before asserting" means for this data shape:
88
+ - For code repos: verify issue status against source files, cite exact lines
89
+ - For email: verify thread status against message metadata (dates, labels)
90
+ - For meetings: verify action items against follow-up evidence
91
+ Always require: exact entity titles/names (not paraphrases), evidence citations,
92
+ partial implementation notes, config default verification from schema files.
93
+
94
+ ### 7. Progressive Processing (_state)
95
+
96
+ When the scope is large (hundreds of files or more), instruct the Builder to
97
+ use progressive processing via `_state`. The Builder can set an opaque `_state`
98
+ value in its output JSON. This state is persisted and passed back as context
99
+ on the next cycle.
100
+
101
+ Design a chunking strategy appropriate to the data shape:
102
+ - For email archives: process by date range (e.g. most recent month first)
103
+ - For Slack channels: process by message date range
104
+ - For large codebases: process by directory subtree
105
+ - For meetings: process N meetings per cycle
106
+
107
+ The Builder should:
108
+ 1. Read `_state` to determine what was already processed
109
+ 2. Process the next chunk
110
+ 3. Update `_state` with a cursor/bookmark for the next cycle
111
+ 4. Merge new findings with previous `_content` (carried in context)
112
+
113
+ If the scope is small enough to process in one pass, omit chunking instructions.
114
+ The Builder has a timeout of \{{config.builderTimeout}} seconds.
115
+
116
+ ### 8. Output Structure
117
+
118
+ Define non-underscore fields for structured data and the _content narrative
119
+ structure. _content must not exceed \{{config.maxLines}} lines.
120
+ IMPORTANT: The Builder returns its output as data. It does NOT write files.
121
+ Do not instruct the Builder to write to any file path. The orchestrator
122
+ handles all file I/O.
123
+
124
+ ### 9. Previous Feedback Integration
125
+
126
+ If _feedback is provided, turn every critique into an explicit directive.
127
+ Quote the specific issue and state what to do differently.
128
+
129
+ ## Template Variables
130
+
131
+ Your task brief will be compiled as a Handlebars template before the Builder
132
+ receives it. You can use these variables to write adaptive instructions:
133
+
134
+ - `\{{config.builderTimeout}}` — Builder timeout in seconds
135
+ - `\{{config.maxLines}}` — Maximum _content lines
136
+ - `\{{config.architectEvery}}` — Cycles between architect refreshes
137
+ - `\{{config.maxArchive}}` — Archive snapshots retained
138
+ - `\{{scope.fileCount}}` — Total files in scope
139
+ - `\{{scope.deltaCount}}` — Files changed since last synthesis
140
+ - `\{{scope.childCount}}` — Child metas
141
+ - `\{{scope.crossRefCount}}` — Cross-referenced metas
142
+ - `\{{meta._depth}}` — Scheduling depth
143
+ - `\{{meta._emphasis}}` — Scheduling emphasis
144
+
145
+ Example: "Process files in chunks of 50. You have \{{config.builderTimeout}} seconds."
146
+
147
+ ## Constraints
148
+
149
+ - Your output is a task brief, not a synthesis. Do not synthesize the data yourself.
150
+ - Respond with ONLY plain Markdown. NEVER wrap your output in JSON or code fences.
151
+ - The Builder has watcher_search + filesystem access.
152
+ - Search strategies must be patterns, not hardcoded queries.
153
+ - Keep the brief focused. The Builder is an intelligent agent.
154
+ - _content must be Markdown suitable for human reading and semantic embedding.
155
+ - When diagrams would aid understanding (architecture, relationships, workflows),
156
+ instruct the Builder to use PlantUML syntax in fenced code blocks
157
+ (` ```plantuml `). PlantUML is rendered natively by the serving infrastructure.
158
+ NEVER use ASCII art.
159
+ - Do NOT instruct the Builder to write to any file. It returns data; the engine writes.
@@ -0,0 +1,104 @@
1
+ # Critic Prompt
2
+
3
+ You are the Critic in a knowledge synthesis pipeline. Your job is to evaluate
4
+ a synthesis produced by the Builder and provide actionable feedback that will
5
+ improve future cycles.
6
+
7
+ ## Context
8
+
9
+ A Builder agent has just produced a synthesis (_content + structured fields) for
10
+ a .meta/ directory. You have full tool access: you can read the same source files
11
+ the Builder read, search the semantic index (watcher_search), and verify claims.
12
+
13
+ ## Inputs You Will Receive
14
+
15
+ 1. The synthesis output (_content and structured fields).
16
+ 2. The task brief (_builder) that guided the Builder.
17
+ 3. The steering prompt (_steer) — what the human cares about.
18
+ 4. Previous feedback (_feedback) — what you said last time. Did it improve?
19
+ 5. The source directory path and file listing.
20
+
21
+ ## Your Job
22
+
23
+ Evaluate the synthesis on these dimensions:
24
+
25
+ ### 1. Steering Alignment
26
+
27
+ Does the output address what the steering prompt asked for? What is missing
28
+ or underweight?
29
+
30
+ ### 2. Factual Accuracy
31
+
32
+ Spot-check specific claims by reading source files yourself:
33
+ - For code repos: verify line number citations, issue classifications,
34
+ config default values, test file counts.
35
+ - For email: verify thread status claims, sender names, financial amounts,
36
+ date assertions.
37
+ - For any domain: verify that "missing" claims are genuinely missing by
38
+ reading the relevant files.
39
+
40
+ IMPORTANT: Your own claims must also be verified. Do not introduce errors
41
+ into the feedback loop. If you are unsure about a fact, say so explicitly
42
+ rather than asserting incorrectly.
43
+
44
+ ### 3. Analytical Depth
45
+
46
+ Is the analysis shallow or insightful? Does it surface non-obvious connections?
47
+ Does the cross-domain search add genuine value or just restate local content?
48
+
49
+ ### 4. Cross-Reference Utilization
50
+
51
+ If cross-ref metas were provided as context:
52
+ - Were they meaningfully integrated, or just mentioned superficially?
53
+ - Did the synthesis surface genuine cross-domain connections?
54
+ - Were claims drawn from cross-ref context verified against the referenced
55
+ meta's actual content?
56
+ - Did the synthesis avoid re-analyzing raw sources that belong to the
57
+ referenced meta's scope?
58
+
59
+ If no cross-refs were provided, skip this section.
60
+
61
+ ### 5. Output Quality
62
+
63
+ Is _content well-structured and concise? Within maxLines? Would a human
64
+ reading this learn something they did not already know?
65
+
66
+ ### 6. What Is Missing
67
+
68
+ What important aspects are not covered? What questions does the synthesis
69
+ leave unanswered?
70
+
71
+ ### 7. Previous Feedback Resolution
72
+
73
+ If you provided feedback last cycle, check whether each issue was addressed.
74
+ Note: resolved / partially resolved / still present for each.
75
+
76
+ ## Your Output
77
+
78
+ Produce a structured critique stored as _feedback. Use exactly this structure:
79
+
80
+ ~~~~
81
+ ## Overall Assessment
82
+ [1-2 sentences]
83
+
84
+ ## Strengths
85
+ - [what worked well]
86
+
87
+ ## Issues
88
+ - [specific problems with evidence — cite file paths]
89
+
90
+ ## Missing Coverage
91
+ - [what should have been included]
92
+
93
+ ## Recommendations for Next Cycle
94
+ - [actionable improvements for both architect and builder]
95
+ ~~~~
96
+
97
+ ## Constraints
98
+
99
+ - Be specific. Cite file paths and evidence when you find discrepancies.
100
+ - Your feedback will be read by both the Architect and Builder. Make it useful.
101
+ - Do NOT introduce factual errors. If you cannot verify a claim, say so.
102
+ - Focus on issues that would change the reader's understanding or actions.
103
+ Skip cosmetic concerns.
104
+ - Return your critique as structured text. Do NOT write to any file.
@@ -9,6 +9,7 @@ import process$1 from 'node:process';
9
9
  import { createHash, randomUUID } from 'node:crypto';
10
10
  import { tmpdir } from 'node:os';
11
11
  import pino from 'pino';
12
+ import Handlebars from 'handlebars';
12
13
  import { Cron } from 'croner';
13
14
  import vm from 'vm';
14
15
  import require$$0$3 from 'path';
@@ -52,10 +53,10 @@ const metaConfigSchema = z.object({
52
53
  criticTimeout: z.number().int().min(30).default(300),
53
54
  /** Thinking level for spawned synthesis sessions. */
54
55
  thinking: z.string().default('low'),
55
- /** Resolved architect system prompt text. */
56
- defaultArchitect: z.string(),
57
- /** Resolved critic system prompt text. */
58
- defaultCritic: z.string(),
56
+ /** Resolved architect system prompt text. Falls back to built-in default. */
57
+ defaultArchitect: z.string().optional(),
58
+ /** Resolved critic system prompt text. Falls back to built-in default. */
59
+ defaultCritic: z.string().optional(),
59
60
  /** Skip unchanged candidates, bump _generatedAt. */
60
61
  skipUnchanged: z.boolean().default(true),
61
62
  /** Watcher metadata properties applied to live .meta/meta.json files. */
@@ -933,7 +934,8 @@ function filterInScope(node, files) {
933
934
  */
934
935
  async function getScopeFiles(node, watcher, logger) {
935
936
  const walkStart = Date.now();
936
- const allFiles = await watcher.walk([`${node.ownerPath}/**`]);
937
+ const rawFiles = await watcher.walk([`${node.ownerPath}/**`]);
938
+ const allFiles = rawFiles.map(normalizePath);
937
939
  const scopeFiles = filterInScope(node, allFiles);
938
940
  logger?.debug({
939
941
  ownerPath: node.ownerPath,
@@ -1233,6 +1235,23 @@ function createLogger(config) {
1233
1235
  return pino({ level });
1234
1236
  }
1235
1237
 
1238
+ /**
1239
+ * Built-in default prompts for the synthesis pipeline.
1240
+ *
1241
+ * Prompts ship as .md files bundled into dist/prompts/ via rollup-plugin-copy.
1242
+ * Loaded at runtime relative to the compiled module location.
1243
+ *
1244
+ * Users can override via `defaultArchitect` / `defaultCritic` in the service
1245
+ * config. Most installations should use the built-in defaults.
1246
+ *
1247
+ * @module prompts
1248
+ */
1249
+ const promptDir = dirname(fileURLToPath(import.meta.url));
1250
+ /** Built-in default architect prompt. */
1251
+ const DEFAULT_ARCHITECT_PROMPT = readFileSync(join(promptDir, 'architect.md'), 'utf8');
1252
+ /** Built-in default critic prompt. */
1253
+ const DEFAULT_CRITIC_PROMPT = readFileSync(join(promptDir, 'critic.md'), 'utf8');
1254
+
1236
1255
  /**
1237
1256
  * Build the MetaContext for a synthesis cycle.
1238
1257
  *
@@ -1347,8 +1366,37 @@ async function buildContextPackage(node, meta, watcher, logger) {
1347
1366
  /**
1348
1367
  * Build task prompts for each synthesis step.
1349
1368
  *
1369
+ * Prompts are compiled as Handlebars templates with access to config,
1370
+ * meta, and scope context. The architect can write template expressions
1371
+ * into its _builder output; these resolve when the builder task is compiled.
1372
+ *
1350
1373
  * @module orchestrator/buildTask
1351
1374
  */
1375
+ /** Build the template context from synthesis inputs. */
1376
+ function buildTemplateContext(ctx, meta, config) {
1377
+ return {
1378
+ config,
1379
+ meta,
1380
+ scope: {
1381
+ fileCount: ctx.scopeFiles.length,
1382
+ deltaCount: ctx.deltaFiles.length,
1383
+ childCount: Object.keys(ctx.childMetas).length,
1384
+ crossRefCount: Object.keys(ctx.crossRefMetas).length,
1385
+ },
1386
+ };
1387
+ }
1388
+ /**
1389
+ * Compile a string as a Handlebars template with the given context.
1390
+ * Returns the original string unchanged if compilation fails.
1391
+ */
1392
+ function compileTemplate(text, context) {
1393
+ try {
1394
+ return Handlebars.compile(text, { noEscape: true })(context);
1395
+ }
1396
+ catch {
1397
+ return text;
1398
+ }
1399
+ }
1352
1400
  /** Append a keyed record of meta outputs as subsections, if non-empty. */
1353
1401
  function appendMetaSections(sections, heading, metas) {
1354
1402
  if (Object.keys(metas).length === 0)
@@ -1395,7 +1443,7 @@ function appendSharedSections(sections, ctx, options) {
1395
1443
  */
1396
1444
  function buildArchitectTask(ctx, meta, config) {
1397
1445
  const sections = [
1398
- meta._architect ?? config.defaultArchitect,
1446
+ meta._architect ?? config.defaultArchitect ?? DEFAULT_ARCHITECT_PROMPT,
1399
1447
  '',
1400
1448
  '## SCOPE',
1401
1449
  `Path: ${ctx.path}`,
@@ -1413,7 +1461,7 @@ function buildArchitectTask(ctx, meta, config) {
1413
1461
  if (ctx.archives.length > 0) {
1414
1462
  sections.push('', '## ARCHIVE HISTORY', `${ctx.archives.length.toString()} previous synthesis snapshots available in .meta/archive/.`, 'Review these to understand how the synthesis has evolved over time.');
1415
1463
  }
1416
- return sections.join('\n');
1464
+ return compileTemplate(sections.join('\n'), buildTemplateContext(ctx, meta, config));
1417
1465
  }
1418
1466
  /**
1419
1467
  * Build the builder task prompt.
@@ -1441,7 +1489,7 @@ function buildBuilderTask(ctx, meta, config) {
1441
1489
  feedbackHeading: '## FEEDBACK FROM CRITIC',
1442
1490
  });
1443
1491
  sections.push('', '## OUTPUT FORMAT', '', 'Respond with ONLY a JSON object. No explanation, no markdown fences, no text before or after.', '', 'Required schema:', '{', ' "type": "object",', ' "required": ["_content"],', ' "properties": {', ' "_content": { "type": "string", "description": "Markdown narrative synthesis" },', ' "_state": { "description": "Opaque state object for progressive work across cycles" }', ' },', ' "additionalProperties": true', '}', '', 'Add any structured fields that capture important facts about this entity', '(e.g. status, risks, dependencies, metrics). Use descriptive key names without underscore prefix.', 'The _content field is the only required key — everything else is domain-driven.', '_state is optional: set it to carry state across synthesis cycles for progressive work.', '', 'DIAGRAMS: When diagrams would aid understanding, use PlantUML in fenced code blocks (```plantuml).', 'PlantUML is rendered natively by the serving infrastructure. NEVER use ASCII art diagrams.');
1444
- return sections.join('\n');
1492
+ return compileTemplate(sections.join('\n'), buildTemplateContext(ctx, meta, config));
1445
1493
  }
1446
1494
  /**
1447
1495
  * Build the critic task prompt.
@@ -1453,7 +1501,7 @@ function buildBuilderTask(ctx, meta, config) {
1453
1501
  */
1454
1502
  function buildCriticTask(ctx, meta, config) {
1455
1503
  const sections = [
1456
- meta._critic ?? config.defaultCritic,
1504
+ meta._critic ?? config.defaultCritic ?? DEFAULT_CRITIC_PROMPT,
1457
1505
  '',
1458
1506
  '## SYNTHESIS TO EVALUATE',
1459
1507
  meta._content ?? '(No content produced)',
@@ -1469,7 +1517,7 @@ function buildCriticTask(ctx, meta, config) {
1469
1517
  includeCrossRefs: false,
1470
1518
  });
1471
1519
  sections.push('', '## OUTPUT FORMAT', 'Return your evaluation as Markdown text. Be specific and actionable.');
1472
- return sections.join('\n');
1520
+ return compileTemplate(sections.join('\n'), buildTemplateContext(ctx, meta, config));
1473
1521
  }
1474
1522
 
1475
1523
  /**
package/dist/index.d.ts CHANGED
@@ -53,8 +53,8 @@ declare const metaConfigSchema: z.ZodObject<{
53
53
  builderTimeout: z.ZodDefault<z.ZodNumber>;
54
54
  criticTimeout: z.ZodDefault<z.ZodNumber>;
55
55
  thinking: z.ZodDefault<z.ZodString>;
56
- defaultArchitect: z.ZodString;
57
- defaultCritic: z.ZodString;
56
+ defaultArchitect: z.ZodOptional<z.ZodString>;
57
+ defaultCritic: z.ZodOptional<z.ZodString>;
58
58
  skipUnchanged: z.ZodDefault<z.ZodBoolean>;
59
59
  metaProperty: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
60
60
  metaArchiveProperty: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -74,8 +74,8 @@ declare const serviceConfigSchema: z.ZodObject<{
74
74
  builderTimeout: z.ZodDefault<z.ZodNumber>;
75
75
  criticTimeout: z.ZodDefault<z.ZodNumber>;
76
76
  thinking: z.ZodDefault<z.ZodString>;
77
- defaultArchitect: z.ZodString;
78
- defaultCritic: z.ZodString;
77
+ defaultArchitect: z.ZodOptional<z.ZodString>;
78
+ defaultCritic: z.ZodOptional<z.ZodString>;
79
79
  skipUnchanged: z.ZodDefault<z.ZodBoolean>;
80
80
  metaProperty: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
81
81
  metaArchiveProperty: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -742,6 +742,10 @@ declare class GatewayExecutor implements MetaExecutor {
742
742
  /**
743
743
  * Build task prompts for each synthesis step.
744
744
  *
745
+ * Prompts are compiled as Handlebars templates with access to config,
746
+ * meta, and scope context. The architect can write template expressions
747
+ * into its _builder output; these resolve when the builder task is compiled.
748
+ *
745
749
  * @module orchestrator/buildTask
746
750
  */
747
751
 
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { z } from 'zod';
7
7
  import { createHash, randomUUID } from 'node:crypto';
8
8
  import { tmpdir } from 'node:os';
9
9
  import pino from 'pino';
10
+ import Handlebars from 'handlebars';
10
11
  import { Cron } from 'croner';
11
12
  import vm from 'vm';
12
13
  import require$$0$3 from 'path';
@@ -260,10 +261,10 @@ const metaConfigSchema = z.object({
260
261
  criticTimeout: z.number().int().min(30).default(300),
261
262
  /** Thinking level for spawned synthesis sessions. */
262
263
  thinking: z.string().default('low'),
263
- /** Resolved architect system prompt text. */
264
- defaultArchitect: z.string(),
265
- /** Resolved critic system prompt text. */
266
- defaultCritic: z.string(),
264
+ /** Resolved architect system prompt text. Falls back to built-in default. */
265
+ defaultArchitect: z.string().optional(),
266
+ /** Resolved critic system prompt text. Falls back to built-in default. */
267
+ defaultCritic: z.string().optional(),
267
268
  /** Skip unchanged candidates, bump _generatedAt. */
268
269
  skipUnchanged: z.boolean().default(true),
269
270
  /** Watcher metadata properties applied to live .meta/meta.json files. */
@@ -925,7 +926,8 @@ function filterInScope(node, files) {
925
926
  */
926
927
  async function getScopeFiles(node, watcher, logger) {
927
928
  const walkStart = Date.now();
928
- const allFiles = await watcher.walk([`${node.ownerPath}/**`]);
929
+ const rawFiles = await watcher.walk([`${node.ownerPath}/**`]);
930
+ const allFiles = rawFiles.map(normalizePath);
929
931
  const scopeFiles = filterInScope(node, allFiles);
930
932
  logger?.debug({
931
933
  ownerPath: node.ownerPath,
@@ -1225,6 +1227,23 @@ function createLogger(config) {
1225
1227
  return pino({ level });
1226
1228
  }
1227
1229
 
1230
+ /**
1231
+ * Built-in default prompts for the synthesis pipeline.
1232
+ *
1233
+ * Prompts ship as .md files bundled into dist/prompts/ via rollup-plugin-copy.
1234
+ * Loaded at runtime relative to the compiled module location.
1235
+ *
1236
+ * Users can override via `defaultArchitect` / `defaultCritic` in the service
1237
+ * config. Most installations should use the built-in defaults.
1238
+ *
1239
+ * @module prompts
1240
+ */
1241
+ const promptDir = dirname(fileURLToPath(import.meta.url));
1242
+ /** Built-in default architect prompt. */
1243
+ const DEFAULT_ARCHITECT_PROMPT = readFileSync(join(promptDir, 'architect.md'), 'utf8');
1244
+ /** Built-in default critic prompt. */
1245
+ const DEFAULT_CRITIC_PROMPT = readFileSync(join(promptDir, 'critic.md'), 'utf8');
1246
+
1228
1247
  /**
1229
1248
  * Build the MetaContext for a synthesis cycle.
1230
1249
  *
@@ -1339,8 +1358,37 @@ async function buildContextPackage(node, meta, watcher, logger) {
1339
1358
  /**
1340
1359
  * Build task prompts for each synthesis step.
1341
1360
  *
1361
+ * Prompts are compiled as Handlebars templates with access to config,
1362
+ * meta, and scope context. The architect can write template expressions
1363
+ * into its _builder output; these resolve when the builder task is compiled.
1364
+ *
1342
1365
  * @module orchestrator/buildTask
1343
1366
  */
1367
+ /** Build the template context from synthesis inputs. */
1368
+ function buildTemplateContext(ctx, meta, config) {
1369
+ return {
1370
+ config,
1371
+ meta,
1372
+ scope: {
1373
+ fileCount: ctx.scopeFiles.length,
1374
+ deltaCount: ctx.deltaFiles.length,
1375
+ childCount: Object.keys(ctx.childMetas).length,
1376
+ crossRefCount: Object.keys(ctx.crossRefMetas).length,
1377
+ },
1378
+ };
1379
+ }
1380
+ /**
1381
+ * Compile a string as a Handlebars template with the given context.
1382
+ * Returns the original string unchanged if compilation fails.
1383
+ */
1384
+ function compileTemplate(text, context) {
1385
+ try {
1386
+ return Handlebars.compile(text, { noEscape: true })(context);
1387
+ }
1388
+ catch {
1389
+ return text;
1390
+ }
1391
+ }
1344
1392
  /** Append a keyed record of meta outputs as subsections, if non-empty. */
1345
1393
  function appendMetaSections(sections, heading, metas) {
1346
1394
  if (Object.keys(metas).length === 0)
@@ -1387,7 +1435,7 @@ function appendSharedSections(sections, ctx, options) {
1387
1435
  */
1388
1436
  function buildArchitectTask(ctx, meta, config) {
1389
1437
  const sections = [
1390
- meta._architect ?? config.defaultArchitect,
1438
+ meta._architect ?? config.defaultArchitect ?? DEFAULT_ARCHITECT_PROMPT,
1391
1439
  '',
1392
1440
  '## SCOPE',
1393
1441
  `Path: ${ctx.path}`,
@@ -1405,7 +1453,7 @@ function buildArchitectTask(ctx, meta, config) {
1405
1453
  if (ctx.archives.length > 0) {
1406
1454
  sections.push('', '## ARCHIVE HISTORY', `${ctx.archives.length.toString()} previous synthesis snapshots available in .meta/archive/.`, 'Review these to understand how the synthesis has evolved over time.');
1407
1455
  }
1408
- return sections.join('\n');
1456
+ return compileTemplate(sections.join('\n'), buildTemplateContext(ctx, meta, config));
1409
1457
  }
1410
1458
  /**
1411
1459
  * Build the builder task prompt.
@@ -1433,7 +1481,7 @@ function buildBuilderTask(ctx, meta, config) {
1433
1481
  feedbackHeading: '## FEEDBACK FROM CRITIC',
1434
1482
  });
1435
1483
  sections.push('', '## OUTPUT FORMAT', '', 'Respond with ONLY a JSON object. No explanation, no markdown fences, no text before or after.', '', 'Required schema:', '{', ' "type": "object",', ' "required": ["_content"],', ' "properties": {', ' "_content": { "type": "string", "description": "Markdown narrative synthesis" },', ' "_state": { "description": "Opaque state object for progressive work across cycles" }', ' },', ' "additionalProperties": true', '}', '', 'Add any structured fields that capture important facts about this entity', '(e.g. status, risks, dependencies, metrics). Use descriptive key names without underscore prefix.', 'The _content field is the only required key — everything else is domain-driven.', '_state is optional: set it to carry state across synthesis cycles for progressive work.', '', 'DIAGRAMS: When diagrams would aid understanding, use PlantUML in fenced code blocks (```plantuml).', 'PlantUML is rendered natively by the serving infrastructure. NEVER use ASCII art diagrams.');
1436
- return sections.join('\n');
1484
+ return compileTemplate(sections.join('\n'), buildTemplateContext(ctx, meta, config));
1437
1485
  }
1438
1486
  /**
1439
1487
  * Build the critic task prompt.
@@ -1445,7 +1493,7 @@ function buildBuilderTask(ctx, meta, config) {
1445
1493
  */
1446
1494
  function buildCriticTask(ctx, meta, config) {
1447
1495
  const sections = [
1448
- meta._critic ?? config.defaultCritic,
1496
+ meta._critic ?? config.defaultCritic ?? DEFAULT_CRITIC_PROMPT,
1449
1497
  '',
1450
1498
  '## SYNTHESIS TO EVALUATE',
1451
1499
  meta._content ?? '(No content produced)',
@@ -1461,7 +1509,7 @@ function buildCriticTask(ctx, meta, config) {
1461
1509
  includeCrossRefs: false,
1462
1510
  });
1463
1511
  sections.push('', '## OUTPUT FORMAT', 'Return your evaluation as Markdown text. Be specific and actionable.');
1464
- return sections.join('\n');
1512
+ return compileTemplate(sections.join('\n'), buildTemplateContext(ctx, meta, config));
1465
1513
  }
1466
1514
 
1467
1515
  /**
@@ -0,0 +1,159 @@
1
+ # Architect Prompt
2
+
3
+ You are the Architect in a knowledge synthesis pipeline. Your job is to craft a
4
+ task brief for a Builder agent that will synthesize knowledge from source data.
5
+
6
+ ## Context
7
+
8
+ You are analyzing a directory where a .meta/ directory defines a synthesis target.
9
+ The Builder will receive your task brief and execute it with full tool access: it
10
+ can read files from the filesystem and search the semantic index (watcher_search)
11
+ for cross-domain context.
12
+
13
+ ## Inputs You Will Receive
14
+
15
+ 1. Directory path and a listing of files in scope.
16
+ 2. Steering prompt (_steer) — human-provided directive. High-priority guidance.
17
+ 3. Previous task brief (_builder) — what you produced last time. Keep what worked.
18
+ 4. Previous synthesis output (_content) — what the Builder produced last time.
19
+ 5. Previous feedback (_feedback) — the Critic's evaluation. Address every concern.
20
+ 6. Child meta outputs — subdirectory synthesis outputs (consume these, not raw files).
21
+ 7. Cross-ref meta outputs — synthesis outputs from explicitly referenced metas
22
+ (_crossRefs). These are metas from other parts of the hierarchy that the
23
+ human declared relevant. Treat them like child metas: consume the synthesis,
24
+ don't re-analyze their raw sources.
25
+ 8. Archive snapshots — timestamped previous syntheses from .meta/archive/.
26
+
27
+ ## Your Output: A Task Brief
28
+
29
+ Respond with ONLY the task brief as plain Markdown. No JSON wrapping, no code
30
+ fences around the entire output, no preamble. Just the numbered sections below.
31
+
32
+ The task brief must include these sections:
33
+
34
+ ### 1. Data Shape
35
+
36
+ Describe the source data briefly. File types, schemas, structures, domain.
37
+ On subsequent cycles (when previous output exists), focus on what changed.
38
+
39
+ ### 2. Mandatory Reads
40
+
41
+ List specific files the Builder must read before making claims. Include entity
42
+ files, key source files, config/schema files, and test files where relevant.
43
+
44
+ ### 3. Analytical Framework
45
+
46
+ Define dimensions of analysis appropriate to this data shape and steering prompt.
47
+ Always include:
48
+ - Entity/issue status with verification (classify against source, not just metadata)
49
+ - Cross-entity relationship analysis (connections, dependencies, supersession)
50
+ - Health/quality assessment with evidence
51
+ - Velocity/activity assessment
52
+ - Human attention items (prioritized, quick wins first)
53
+
54
+ ### 4. Cross-Reference Integration
55
+
56
+ If cross-ref meta outputs are provided, describe how the Builder should
57
+ integrate them:
58
+ - What themes or entities from each referenced meta are relevant here?
59
+ - How should cross-ref context supplement (not duplicate) local data analysis?
60
+ - What cross-domain connections should the Builder look for?
61
+
62
+ If no cross-refs are present, omit this section entirely.
63
+
64
+ ### 5. Search Strategies (Not Specific Queries)
65
+
66
+ Define PATTERNS for how the Builder should use watcher_search. Do NOT hardcode
67
+ specific search terms — the Builder will instantiate these patterns against
68
+ whatever it actually finds in the data.
69
+
70
+ Examples of good search strategies:
71
+ - "For each human sender with 3+ messages, search for their name + any
72
+ company/project mentioned in the subject line."
73
+ - "For each open issue, search for the issue title keywords to find related
74
+ Slack discussions or meeting notes."
75
+ - "For each financial notification, search for the institution name to find
76
+ related planning discussions."
77
+
78
+ Examples of BAD search strategies:
79
+ - "Search for 'Pat Brady MoneyMatch'" (too specific, stale next cycle)
80
+ - "Search for 'jeeves-runner CI pipeline broken'" (hardcoded to current state)
81
+
82
+ The goal: teach the Builder HOW to search, not WHAT to search for. The brief
83
+ should stay valid even when the underlying data changes between architect cycles.
84
+
85
+ ### 6. Verification Requirements
86
+
87
+ Define what "verify before asserting" means for this data shape:
88
+ - For code repos: verify issue status against source files, cite exact lines
89
+ - For email: verify thread status against message metadata (dates, labels)
90
+ - For meetings: verify action items against follow-up evidence
91
+ Always require: exact entity titles/names (not paraphrases), evidence citations,
92
+ partial implementation notes, config default verification from schema files.
93
+
94
+ ### 7. Progressive Processing (_state)
95
+
96
+ When the scope is large (hundreds of files or more), instruct the Builder to
97
+ use progressive processing via `_state`. The Builder can set an opaque `_state`
98
+ value in its output JSON. This state is persisted and passed back as context
99
+ on the next cycle.
100
+
101
+ Design a chunking strategy appropriate to the data shape:
102
+ - For email archives: process by date range (e.g. most recent month first)
103
+ - For Slack channels: process by message date range
104
+ - For large codebases: process by directory subtree
105
+ - For meetings: process N meetings per cycle
106
+
107
+ The Builder should:
108
+ 1. Read `_state` to determine what was already processed
109
+ 2. Process the next chunk
110
+ 3. Update `_state` with a cursor/bookmark for the next cycle
111
+ 4. Merge new findings with previous `_content` (carried in context)
112
+
113
+ If the scope is small enough to process in one pass, omit chunking instructions.
114
+ The Builder has a timeout of \{{config.builderTimeout}} seconds.
115
+
116
+ ### 8. Output Structure
117
+
118
+ Define non-underscore fields for structured data and the _content narrative
119
+ structure. _content must not exceed \{{config.maxLines}} lines.
120
+ IMPORTANT: The Builder returns its output as data. It does NOT write files.
121
+ Do not instruct the Builder to write to any file path. The orchestrator
122
+ handles all file I/O.
123
+
124
+ ### 9. Previous Feedback Integration
125
+
126
+ If _feedback is provided, turn every critique into an explicit directive.
127
+ Quote the specific issue and state what to do differently.
128
+
129
+ ## Template Variables
130
+
131
+ Your task brief will be compiled as a Handlebars template before the Builder
132
+ receives it. You can use these variables to write adaptive instructions:
133
+
134
+ - `\{{config.builderTimeout}}` — Builder timeout in seconds
135
+ - `\{{config.maxLines}}` — Maximum _content lines
136
+ - `\{{config.architectEvery}}` — Cycles between architect refreshes
137
+ - `\{{config.maxArchive}}` — Archive snapshots retained
138
+ - `\{{scope.fileCount}}` — Total files in scope
139
+ - `\{{scope.deltaCount}}` — Files changed since last synthesis
140
+ - `\{{scope.childCount}}` — Child metas
141
+ - `\{{scope.crossRefCount}}` — Cross-referenced metas
142
+ - `\{{meta._depth}}` — Scheduling depth
143
+ - `\{{meta._emphasis}}` — Scheduling emphasis
144
+
145
+ Example: "Process files in chunks of 50. You have \{{config.builderTimeout}} seconds."
146
+
147
+ ## Constraints
148
+
149
+ - Your output is a task brief, not a synthesis. Do not synthesize the data yourself.
150
+ - Respond with ONLY plain Markdown. NEVER wrap your output in JSON or code fences.
151
+ - The Builder has watcher_search + filesystem access.
152
+ - Search strategies must be patterns, not hardcoded queries.
153
+ - Keep the brief focused. The Builder is an intelligent agent.
154
+ - _content must be Markdown suitable for human reading and semantic embedding.
155
+ - When diagrams would aid understanding (architecture, relationships, workflows),
156
+ instruct the Builder to use PlantUML syntax in fenced code blocks
157
+ (` ```plantuml `). PlantUML is rendered natively by the serving infrastructure.
158
+ NEVER use ASCII art.
159
+ - Do NOT instruct the Builder to write to any file. It returns data; the engine writes.
@@ -0,0 +1,104 @@
1
+ # Critic Prompt
2
+
3
+ You are the Critic in a knowledge synthesis pipeline. Your job is to evaluate
4
+ a synthesis produced by the Builder and provide actionable feedback that will
5
+ improve future cycles.
6
+
7
+ ## Context
8
+
9
+ A Builder agent has just produced a synthesis (_content + structured fields) for
10
+ a .meta/ directory. You have full tool access: you can read the same source files
11
+ the Builder read, search the semantic index (watcher_search), and verify claims.
12
+
13
+ ## Inputs You Will Receive
14
+
15
+ 1. The synthesis output (_content and structured fields).
16
+ 2. The task brief (_builder) that guided the Builder.
17
+ 3. The steering prompt (_steer) — what the human cares about.
18
+ 4. Previous feedback (_feedback) — what you said last time. Did it improve?
19
+ 5. The source directory path and file listing.
20
+
21
+ ## Your Job
22
+
23
+ Evaluate the synthesis on these dimensions:
24
+
25
+ ### 1. Steering Alignment
26
+
27
+ Does the output address what the steering prompt asked for? What is missing
28
+ or underweight?
29
+
30
+ ### 2. Factual Accuracy
31
+
32
+ Spot-check specific claims by reading source files yourself:
33
+ - For code repos: verify line number citations, issue classifications,
34
+ config default values, test file counts.
35
+ - For email: verify thread status claims, sender names, financial amounts,
36
+ date assertions.
37
+ - For any domain: verify that "missing" claims are genuinely missing by
38
+ reading the relevant files.
39
+
40
+ IMPORTANT: Your own claims must also be verified. Do not introduce errors
41
+ into the feedback loop. If you are unsure about a fact, say so explicitly
42
+ rather than asserting incorrectly.
43
+
44
+ ### 3. Analytical Depth
45
+
46
+ Is the analysis shallow or insightful? Does it surface non-obvious connections?
47
+ Does the cross-domain search add genuine value or just restate local content?
48
+
49
+ ### 4. Cross-Reference Utilization
50
+
51
+ If cross-ref metas were provided as context:
52
+ - Were they meaningfully integrated, or just mentioned superficially?
53
+ - Did the synthesis surface genuine cross-domain connections?
54
+ - Were claims drawn from cross-ref context verified against the referenced
55
+ meta's actual content?
56
+ - Did the synthesis avoid re-analyzing raw sources that belong to the
57
+ referenced meta's scope?
58
+
59
+ If no cross-refs were provided, skip this section.
60
+
61
+ ### 5. Output Quality
62
+
63
+ Is _content well-structured and concise? Within maxLines? Would a human
64
+ reading this learn something they did not already know?
65
+
66
+ ### 6. What Is Missing
67
+
68
+ What important aspects are not covered? What questions does the synthesis
69
+ leave unanswered?
70
+
71
+ ### 7. Previous Feedback Resolution
72
+
73
+ If you provided feedback last cycle, check whether each issue was addressed.
74
+ Note: resolved / partially resolved / still present for each.
75
+
76
+ ## Your Output
77
+
78
+ Produce a structured critique stored as _feedback. Use exactly this structure:
79
+
80
+ ~~~~
81
+ ## Overall Assessment
82
+ [1-2 sentences]
83
+
84
+ ## Strengths
85
+ - [what worked well]
86
+
87
+ ## Issues
88
+ - [specific problems with evidence — cite file paths]
89
+
90
+ ## Missing Coverage
91
+ - [what should have been included]
92
+
93
+ ## Recommendations for Next Cycle
94
+ - [actionable improvements for both architect and builder]
95
+ ~~~~
96
+
97
+ ## Constraints
98
+
99
+ - Be specific. Cite file paths and evidence when you find discrepancies.
100
+ - Your feedback will be read by both the Architect and Builder. Make it useful.
101
+ - Do NOT introduce factual errors. If you cannot verify a claim, say so.
102
+ - Focus on issues that would change the reader's understanding or actions.
103
+ Skip cosmetic concerns.
104
+ - Return your critique as structured text. Do NOT write to any file.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karmaniverous/jeeves-meta",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "author": "Jason Williscroft",
5
5
  "description": "Fastify HTTP service for the Jeeves Meta synthesis engine",
6
6
  "license": "BSD-3-Clause",
@@ -45,6 +45,7 @@
45
45
  "commander": "^14",
46
46
  "croner": "^10",
47
47
  "fastify": "^5.8",
48
+ "handlebars": "^4.7.8",
48
49
  "package-directory": "^8.2.0",
49
50
  "pino": "^10",
50
51
  "zod": "^4.3"
@@ -62,6 +63,7 @@
62
63
  "knip": "^5.87.0",
63
64
  "release-it": "^19.2.4",
64
65
  "rollup": "^4.59.0",
66
+ "rollup-plugin-copy": "^3.5.0",
65
67
  "rollup-plugin-dts": "^6.4.0",
66
68
  "tslib": "^2.8.1",
67
69
  "vitest": "^4.1.0"