@kentwynn/kgraph 0.2.18 → 0.2.20

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
@@ -121,13 +121,14 @@ After useful AI work, assistants save durable runtime-capture notes into `.kgrap
121
121
  Normal agent flow is intentionally small:
122
122
 
123
123
  ```bash
124
- kgraph pack "topic" --budget 8000 --json
124
+ kgraph "topic"
125
125
  # work normally
126
- # if repo files changed, write an inbox note before the final refresh
127
- kgraph
126
+ kgraph "topic" --final
127
+ # if final check requires capture:
128
+ kgraph "topic" --capture "durable conclusion" --capture-file path/to/file.ts --capture-symbol SymbolName
128
129
  ```
129
130
 
130
- `kgraph "<topic>"` remains the human-readable briefing. Agents should prefer `kgraph pack "<topic>" --budget 8000 --json` because it returns the stable `ContextPack` contract with atoms, source ranges, git changes, omitted items, token estimates, and inclusion reasons.
131
+ `kgraph "<topic>"` is the smart root workflow: it refreshes maps, processes capture notes, reports memory health, and returns focused context. Agents can still use `kgraph pack "<topic>" --budget 8000 --json` when they need the stable machine-readable `ContextPack` contract with atoms, source ranges, git changes, omitted items, token estimates, and inclusion reasons.
131
132
 
132
133
  Use `kgraph doctor` after setup and before trusting a repo's saved intelligence. It checks initialization, maps, pending inbox notes, integration targets, and actionable quality problems. Use `kgraph doctor --quality` and `kgraph repair --dry-run` when stale or noisy atom references start making context harder to trust.
133
134
 
@@ -163,6 +164,18 @@ kgraph "some topic"
163
164
 
164
165
  The normal command. Scans the repo, updates durable memory, and returns focused context for the topic.
165
166
 
167
+ ```bash
168
+ kgraph "some topic" --final
169
+ ```
170
+
171
+ End-of-work check. Refreshes intelligence and fails with `capture-required` when mapped repo files changed but no recent durable atom references those files.
172
+
173
+ ```bash
174
+ kgraph "some topic" --capture "durable conclusion" --capture-file src/auth.ts --capture-symbol refreshSession
175
+ ```
176
+
177
+ Stores durable cognition through the root workflow. Use `--capture-confidence high` only with file or symbol evidence.
178
+
166
179
  ```bash
167
180
  kgraph
168
181
  ```
@@ -33,6 +33,9 @@ export function registerConcludeCommand(program) {
33
33
  console.log(`Stored ${note.kind} cognition: ${note.title}`);
34
34
  console.log(`Confidence: ${note.confidence}`);
35
35
  console.log(`Status: ${note.referencesStatus}`);
36
+ for (const warning of note.warnings) {
37
+ console.error(`Warning: ${warning}`);
38
+ }
36
39
  }));
37
40
  }
38
41
  export function normalizeKind(value) {
@@ -89,6 +89,9 @@ export function registerSessionCommand(program) {
89
89
  if (pendingConclusion) {
90
90
  const note = await concludeTopic(workspace, pendingConclusion);
91
91
  console.log(`Stored session cognition: ${note.title}`);
92
+ for (const warning of note.warnings) {
93
+ console.error(`Warning: ${warning}`);
94
+ }
92
95
  }
93
96
  }));
94
97
  session
@@ -1 +1,11 @@
1
- export declare function runDefaultWorkflow(query?: string): Promise<void>;
1
+ export interface DefaultWorkflowOptions {
2
+ final?: boolean;
3
+ capture?: string;
4
+ type?: string;
5
+ confidence?: string;
6
+ domain?: string;
7
+ tags?: string[];
8
+ files?: string[];
9
+ symbols?: string[];
10
+ }
11
+ export declare function runDefaultWorkflow(query?: string, options?: DefaultWorkflowOptions): Promise<void>;
@@ -1,13 +1,18 @@
1
1
  import { refreshCognitionReferenceStatuses, updateCognition, } from '../../cognition/cognition-updater.js';
2
+ import { concludeTopic } from '../../cognition/conclusion.js';
2
3
  import { loadConfig } from '../../config/config.js';
3
4
  import { queryContext } from '../../context/context-query.js';
5
+ import { refreshKnowledgeAtomStatuses } from '../../knowledge/atom-store.js';
6
+ import { getWorkingTreeChanges } from '../../scanner/git-utils.js';
4
7
  import { scanRepository } from '../../scanner/repo-scanner.js';
8
+ import { listInboxNotes } from '../../storage/cognition-store.js';
5
9
  import { assertWorkspace, pathExists, resolveWorkspace, } from '../../storage/kgraph-paths.js';
6
10
  import { readMaps, writeMaps } from '../../storage/map-store.js';
7
- import { runCommand } from '../errors.js';
11
+ import { KGraphError, runCommand } from '../errors.js';
8
12
  import { renderRootHelp, renderWorkflowBanner } from '../help.js';
13
+ import { normalizeConfidence, normalizeKind } from './conclude.js';
9
14
  import { renderContextMarkdown } from './context.js';
10
- export async function runDefaultWorkflow(query) {
15
+ export async function runDefaultWorkflow(query, options = {}) {
11
16
  await runCommand(async () => {
12
17
  const topic = query?.trim();
13
18
  const candidateWorkspace = resolveWorkspace(process.cwd());
@@ -32,6 +37,47 @@ export async function runDefaultWorkflow(query) {
32
37
  symbols: scan.symbols,
33
38
  });
34
39
  const update = await updateCognition(workspace, { files: scan.files, symbols: scan.symbols }, false);
40
+ if (options.capture) {
41
+ if (!topic) {
42
+ throw new KGraphError('A topic is required when using --capture.');
43
+ }
44
+ const note = await concludeTopic(workspace, {
45
+ topic,
46
+ body: options.capture,
47
+ kind: normalizeKind(options.type),
48
+ confidence: normalizeConfidence(options.confidence),
49
+ domain: options.domain,
50
+ tags: options.tags ?? [],
51
+ relatedFiles: options.files ?? [],
52
+ relatedSymbols: options.symbols ?? [],
53
+ source: 'conclude',
54
+ });
55
+ console.log(`Stored ${note.kind} cognition: ${note.title}`);
56
+ console.log(`Confidence: ${note.confidence}`);
57
+ console.log(`Status: ${note.referencesStatus}`);
58
+ for (const warning of note.warnings) {
59
+ console.error(`Warning: ${warning}`);
60
+ }
61
+ }
62
+ const refreshedAtoms = await refreshKnowledgeAtomStatuses(workspace, {
63
+ fileMap: {
64
+ generatedAt: new Date().toISOString(),
65
+ files: scan.files,
66
+ },
67
+ symbolMap: {
68
+ generatedAt: new Date().toISOString(),
69
+ symbols: scan.symbols,
70
+ },
71
+ });
72
+ const atoms = refreshedAtoms.atoms;
73
+ const pendingInbox = await listInboxNotes(workspace);
74
+ const activeAtoms = atoms.filter((atom) => atom.status === 'active');
75
+ const captureCheck = await buildCaptureCheck(workspace.rootPath, {
76
+ topic,
77
+ previousFiles: previousMaps.fileMap.files,
78
+ files: scan.files,
79
+ atoms,
80
+ });
35
81
  console.log(renderWorkflowBanner({
36
82
  files: scan.files.length,
37
83
  symbols: scan.symbols.length,
@@ -42,11 +88,30 @@ export async function runDefaultWorkflow(query) {
42
88
  mode: integration.mode,
43
89
  enabled: integration.enabled,
44
90
  })),
91
+ memory: {
92
+ atomsProcessed: update.processed.length,
93
+ pendingInbox: pendingInbox.length,
94
+ activeAtoms: activeAtoms.length,
95
+ needsReviewAtoms: atoms.filter((atom) => atom.status === 'needs-review')
96
+ .length,
97
+ staleAtoms: atoms.filter((atom) => atom.status === 'stale').length,
98
+ highConfidenceMissingEvidence: activeAtoms.filter((atom) => atom.confidence === 'high' && atom.evidenceRefs.length === 0).length,
99
+ captureRequired: captureCheck.required,
100
+ changedFiles: captureCheck.changedFiles.length,
101
+ },
45
102
  }));
46
103
  console.log('');
47
104
  for (const warning of [...scan.warnings, ...update.warnings]) {
48
105
  console.warn(`Warning: ${warning}`);
49
106
  }
107
+ if (options.final) {
108
+ console.log('');
109
+ renderFinalCaptureCheck(captureCheck, topic);
110
+ if (captureCheck.required) {
111
+ process.exitCode = 1;
112
+ }
113
+ return;
114
+ }
50
115
  if (!topic) {
51
116
  return;
52
117
  }
@@ -56,3 +121,137 @@ export async function runDefaultWorkflow(query) {
56
121
  console.log(renderContextMarkdown(response));
57
122
  });
58
123
  }
124
+ async function buildCaptureCheck(rootPath, input) {
125
+ const knownFiles = new Set(input.files.map((file) => file.path));
126
+ const previousByPath = new Map(input.previousFiles.map((file) => [file.path, file]));
127
+ const currentPaths = new Set(input.files.map((file) => file.path));
128
+ const mapChangedFiles = input.files
129
+ .filter((file) => {
130
+ const previous = previousByPath.get(file.path);
131
+ return !previous || previous.contentHash !== file.contentHash;
132
+ })
133
+ .map((file) => file.path);
134
+ const deletedFiles = input.previousFiles
135
+ .filter((file) => !currentPaths.has(file.path))
136
+ .map((file) => file.path);
137
+ const gitChangedFiles = (await getWorkingTreeChanges(rootPath)).filter((file) => knownFiles.has(file) || previousByPath.has(file));
138
+ const changedFiles = [
139
+ ...new Set([...mapChangedFiles, ...deletedFiles, ...gitChangedFiles]),
140
+ ];
141
+ const recentCutoff = Date.now() - 24 * 60 * 60 * 1000;
142
+ const recentActiveAtoms = input.atoms.filter((atom) => {
143
+ if (atom.status !== 'active')
144
+ return false;
145
+ const createdAt = Date.parse(atom.provenance.createdAt);
146
+ return Number.isFinite(createdAt) && createdAt >= recentCutoff;
147
+ });
148
+ const invalidatedAtoms = matchingInvalidatedAtoms(input.atoms, input.topic).filter((atom) => !isInvalidatedAtomCovered(atom, recentActiveAtoms));
149
+ const covered = new Set();
150
+ for (const atom of recentActiveAtoms) {
151
+ for (const ref of atom.evidenceRefs) {
152
+ if (ref.type === 'file' && changedFiles.includes(ref.path)) {
153
+ covered.add(ref.path);
154
+ }
155
+ }
156
+ for (const file of atom.scopeRefs.files) {
157
+ if (changedFiles.includes(file)) {
158
+ covered.add(file);
159
+ }
160
+ }
161
+ }
162
+ return {
163
+ required: changedFiles.some((file) => !covered.has(file)) ||
164
+ invalidatedAtoms.length > 0,
165
+ changedFiles,
166
+ coveredFiles: [...covered],
167
+ invalidatedAtoms,
168
+ };
169
+ }
170
+ function isInvalidatedAtomCovered(invalidated, recentActiveAtoms) {
171
+ const invalidatedFiles = new Set(invalidated.scopeRefs.files);
172
+ const invalidatedSymbols = new Set(invalidated.scopeRefs.symbols);
173
+ for (const ref of invalidated.evidenceRefs) {
174
+ if (ref.type === 'file')
175
+ invalidatedFiles.add(ref.path);
176
+ if (ref.type === 'symbol')
177
+ invalidatedSymbols.add(ref.name);
178
+ }
179
+ return recentActiveAtoms.some((atom) => {
180
+ const atomFiles = new Set(atom.scopeRefs.files);
181
+ const atomSymbols = new Set(atom.scopeRefs.symbols);
182
+ for (const ref of atom.evidenceRefs) {
183
+ if (ref.type === 'file')
184
+ atomFiles.add(ref.path);
185
+ if (ref.type === 'symbol')
186
+ atomSymbols.add(ref.name);
187
+ }
188
+ const fileOverlap = [...invalidatedFiles].some((file) => atomFiles.has(file));
189
+ const symbolOverlap = [...invalidatedSymbols].some((symbol) => atomSymbols.has(symbol));
190
+ if (fileOverlap || symbolOverlap)
191
+ return true;
192
+ return tokenOverlap(invalidated.topic, atom.topic);
193
+ });
194
+ }
195
+ function tokenOverlap(a, b) {
196
+ const aTokens = new Set(a
197
+ .toLowerCase()
198
+ .split(/[^a-z0-9]+/)
199
+ .filter(Boolean));
200
+ return b
201
+ .toLowerCase()
202
+ .split(/[^a-z0-9]+/)
203
+ .filter(Boolean)
204
+ .some((token) => aTokens.has(token));
205
+ }
206
+ function matchingInvalidatedAtoms(atoms, topic) {
207
+ const tokens = new Set((topic ?? '')
208
+ .toLowerCase()
209
+ .split(/[^a-z0-9]+/)
210
+ .filter(Boolean));
211
+ return atoms.filter((atom) => {
212
+ if (atom.status !== 'needs-review' && atom.status !== 'stale')
213
+ return false;
214
+ if (tokens.size === 0)
215
+ return true;
216
+ const haystack = [
217
+ atom.topic,
218
+ atom.claim,
219
+ atom.summary,
220
+ ...atom.scopeRefs.files,
221
+ ...atom.scopeRefs.symbols,
222
+ ]
223
+ .filter(Boolean)
224
+ .join(' ')
225
+ .toLowerCase();
226
+ return [...tokens].some((token) => haystack.includes(token));
227
+ });
228
+ }
229
+ function renderFinalCaptureCheck(check, topic) {
230
+ console.log('KGraph Final Check');
231
+ if (check.changedFiles.length === 0) {
232
+ if (check.required) {
233
+ console.log(' status capture-required');
234
+ console.log(' changed files 0');
235
+ console.log(` invalid atoms ${check.invalidatedAtoms.length}`);
236
+ console.log(' conclusion missing for needs-review or stale knowledge');
237
+ console.log(` next kgraph "${topic || '<topic>'}" --capture "<durable conclusion>" --capture-file <path>`);
238
+ return;
239
+ }
240
+ console.log(' status clean');
241
+ console.log(' reason no mapped repo files changed or invalidated matching atoms');
242
+ return;
243
+ }
244
+ if (!check.required) {
245
+ console.log(' status captured');
246
+ console.log(` changed files ${check.changedFiles.length}`);
247
+ console.log(` covered files ${check.coveredFiles.length}`);
248
+ return;
249
+ }
250
+ console.log(' status capture-required');
251
+ console.log(` changed files ${check.changedFiles.length}`);
252
+ if (check.invalidatedAtoms.length > 0) {
253
+ console.log(` invalid atoms ${check.invalidatedAtoms.length}`);
254
+ }
255
+ console.log(' conclusion missing for one or more changed files');
256
+ console.log(` next kgraph "${topic || '<topic>'}" --capture "<durable conclusion>" --capture-file <path>`);
257
+ }
@@ -5,11 +5,22 @@ interface WorkflowBannerStats {
5
5
  cognitionNotes: number;
6
6
  skippedFiles?: number;
7
7
  integrations?: WorkflowBannerIntegration[];
8
+ memory?: WorkflowBannerMemory;
8
9
  }
9
10
  interface WorkflowBannerIntegration {
10
11
  name: string;
11
12
  mode: string;
12
13
  enabled: boolean;
13
14
  }
15
+ interface WorkflowBannerMemory {
16
+ atomsProcessed: number;
17
+ pendingInbox: number;
18
+ activeAtoms: number;
19
+ needsReviewAtoms: number;
20
+ staleAtoms: number;
21
+ highConfidenceMissingEvidence: number;
22
+ changedFiles?: number;
23
+ captureRequired?: boolean;
24
+ }
14
25
  export declare function renderWorkflowBanner(stats: WorkflowBannerStats, useColor?: boolean): string;
15
26
  export {};
package/dist/cli/help.js CHANGED
@@ -25,6 +25,8 @@ export function renderRootHelp(useColor = supportsColor()) {
25
25
  sectionTitle(theme, `${accent} Daily workflow`),
26
26
  command('kgraph', 'Refresh scan maps and process pending capture notes'),
27
27
  command('kgraph "auth token refresh"', 'Refresh everything and return compact context for a topic'),
28
+ command('kgraph "auth token refresh" --final', 'End-of-work check: enforce capture when changed files need memory'),
29
+ command('kgraph "auth token refresh" --capture "..."', 'Store durable knowledge through the smart root workflow'),
28
30
  '',
29
31
  sectionTitle(theme, `${accent} Workflows`),
30
32
  command('scan', 'Optional: refresh only file, symbol, import, and relationship maps'),
@@ -60,10 +62,16 @@ export function renderRootHelp(useColor = supportsColor()) {
60
62
  sectionTitle(theme, `${accent} Options`),
61
63
  command('-V, --version', 'Show version'),
62
64
  command('-h, --help', 'Show this help'),
65
+ command('--final', 'Run final capture enforcement in the root workflow'),
66
+ command('--capture <text>', 'Store a durable conclusion in the root workflow'),
67
+ command('--capture-file <path>', 'Attach file evidence to root capture'),
68
+ command('--capture-symbol <name>', 'Attach symbol evidence to root capture'),
63
69
  '',
64
70
  sectionTitle(theme, `${accent} Examples`),
65
71
  ' kgraph init --integrations codex,copilot,cursor,claude-code,gemini,windsurf,cline',
66
72
  ' kgraph "blog admin token usage"',
73
+ ' kgraph "blog admin token usage" --final',
74
+ ' kgraph "blog admin token usage" --capture "Author filter now uses display names" --capture-file www/app/blog/page.tsx',
67
75
  ' kgraph pack "about page update" --budget 4000',
68
76
  ' kgraph doctor',
69
77
  '',
@@ -97,6 +105,21 @@ export function renderWorkflowBanner(stats, useColor = supportsColor()) {
97
105
  command('symbols', String(stats.symbols)),
98
106
  command('capture notes processed', String(stats.cognitionNotes)),
99
107
  command('integration modes', integrationLine),
108
+ ...(stats.memory
109
+ ? [
110
+ '',
111
+ sectionTitle(theme, `${accent} Memory`),
112
+ command('atoms processed', String(stats.memory.atomsProcessed)),
113
+ command('pending inbox', String(stats.memory.pendingInbox)),
114
+ command('active atoms', String(stats.memory.activeAtoms)),
115
+ command('needs review', String(stats.memory.needsReviewAtoms)),
116
+ command('stale', String(stats.memory.staleAtoms)),
117
+ command('high-confidence missing evidence', String(stats.memory.highConfidenceMissingEvidence)),
118
+ command('capture status', stats.memory.captureRequired
119
+ ? `required (${stats.memory.changedFiles ?? 0} changed file(s))`
120
+ : `ok (${stats.memory.changedFiles ?? 0} changed file(s))`),
121
+ ]
122
+ : []),
100
123
  '',
101
124
  sectionTitle(theme, `${accent} Next`),
102
125
  command('kgraph "auth token refresh"', 'Return compact context for a topic'),
package/dist/cli/index.js CHANGED
@@ -31,10 +31,27 @@ export function createProgram() {
31
31
  .name('kgraph')
32
32
  .description('Persistent repo intelligence for AI coding assistants')
33
33
  .argument('[topic...]', 'Run the default refresh workflow and optionally return context for a topic')
34
+ .option('--final', 'Run end-of-work capture enforcement after refresh')
35
+ .option('--capture <text>', 'Store a durable conclusion through the root workflow')
36
+ .option('--capture-type <type>', 'Capture type: finding, decision, gotcha, summary, or relationship', 'summary')
37
+ .option('--capture-confidence <level>', 'Capture confidence: high, medium, or low', 'medium')
38
+ .option('--capture-domain <name>', 'Capture domain name')
39
+ .option('--capture-tag <tag>', 'Capture tag; repeatable', collect, [])
40
+ .option('--capture-file <path>', 'Capture related repo file; repeatable', collect, [])
41
+ .option('--capture-symbol <name>', 'Capture related symbol; repeatable', collect, [])
34
42
  .version(version)
35
43
  .helpOption(false)
36
- .action(async (topicParts = []) => {
37
- await runDefaultWorkflow(topicParts.join(' '));
44
+ .action(async (topicParts = [], options) => {
45
+ await runDefaultWorkflow(topicParts.join(' '), {
46
+ final: options.final,
47
+ capture: options.capture,
48
+ type: options.captureType,
49
+ confidence: options.captureConfidence,
50
+ domain: options.captureDomain,
51
+ tags: options.captureTag,
52
+ files: options.captureFile,
53
+ symbols: options.captureSymbol,
54
+ });
38
55
  });
39
56
  program.option('-h, --help', 'Show this help');
40
57
  program.hook('preAction', (thisCommand) => {
@@ -63,6 +80,10 @@ export function createProgram() {
63
80
  registerUninstallCommand(program);
64
81
  return program;
65
82
  }
83
+ function collect(value, previous) {
84
+ previous.push(value);
85
+ return previous;
86
+ }
66
87
  if (isCliEntrypoint()) {
67
88
  const program = createProgram();
68
89
  const helpTarget = findExplicitHelpTarget(program, process.argv.slice(2));
@@ -15,6 +15,10 @@ export async function concludeTopic(workspace, input) {
15
15
  ? input.relatedFiles
16
16
  : await inferChangedFiles(workspace, maps);
17
17
  const relatedSymbols = input.relatedSymbols ?? [];
18
+ const hasEvidence = relatedFiles.length > 0 || relatedSymbols.length > 0;
19
+ if ((input.confidence ?? 'medium') === 'high' && !hasEvidence) {
20
+ throw new KGraphError('High-confidence cognition requires evidence. Add --file <path> or --symbol <name>, or use --confidence medium/low.');
21
+ }
18
22
  const note = {
19
23
  title,
20
24
  kind: input.kind ?? 'summary',
@@ -29,7 +33,11 @@ export async function concludeTopic(workspace, input) {
29
33
  },
30
34
  relatedFiles,
31
35
  relatedSymbols,
32
- warnings: [],
36
+ warnings: (input.confidence ?? 'medium') === 'medium' && !hasEvidence
37
+ ? [
38
+ 'Medium-confidence cognition has no file or symbol evidence; add --file or --symbol when possible.',
39
+ ]
40
+ : [],
33
41
  id: `${timestamp}-${slugify(title) || 'conclusion'}`,
34
42
  sourceInboxPath: '',
35
43
  processedPath: `.kgraph/cognition/${timestamp}-${slugify(title) || 'conclusion'}.md`,
@@ -1,12 +1,13 @@
1
+ import { numberedWorkflow } from '../workflow-steps.js';
1
2
  export const claudeCodeAdapter = {
2
3
  name: 'claude-code',
3
4
  label: 'Claude Code',
4
5
  targetPath: 'CLAUDE.md',
5
6
  instructions: `## KGraph Workflow
6
7
 
7
- {{KGRAPH_CONTEXT_POLICY}} Use /kgraph for the full automated workflow. Run \`kgraph pack "<task>" --budget 8000 --json\` for a machine-readable token-budgeted context pack, \`kgraph knowledge list\` or \`kgraph knowledge get <atom-id>\` to inspect durable atoms, \`kgraph stale\` and \`kgraph blame <atom-id>\` when lifecycle/provenance matters, \`kgraph conclude\` for durable typed engineering memory, and \`kgraph compact --dry-run\` when cognition looks duplicated or stale. Run \`kgraph doctor\` when setup or generated maps look wrong. Run \`kgraph scan\`, \`kgraph update\`, and \`kgraph context\` manually only when you need one specific step.
8
-
9
- {{KGRAPH_CAPTURE_POLICY}}
8
+ ${numberedWorkflow('claude-code', {
9
+ sessionQualifier: 'native hooks also report session activity when configured',
10
+ })}
10
11
  `,
11
12
  commandFiles: [
12
13
  {
@@ -20,7 +21,7 @@ export const claudeCodeAdapter = {
20
21
  5. Verify the change actually landed before claiming completion. Prefer a narrow read of the changed range or \`git diff -- <path>\`; if there is no diff or the expected text is missing, say the edit did not apply and fix it before summarizing.
21
22
  6. Do not run \`kgraph\` again, \`kgraph context\`, \`kgraph pack\`, \`kgraph knowledge\`, \`kgraph stale\`, \`kgraph blame\`, \`kgraph scan\`, \`kgraph update\`, \`kgraph compact\`, or \`kgraph repair\` unless the user explicitly asks for that lower-level command.
22
23
  7. Do not continue broad repository search after the target file is identified. If a path must be located, prefer \`rg --files\` and quote paths containing spaces or parentheses.
23
- 8. At the end of repository-file changes, store durable engineering memory with \`kgraph conclude "<topic>" --type <finding|decision|gotcha|summary|relationship> --confidence <high|medium|low>\` only when the work created reusable engineering knowledge.
24
+ 8. At the end of repository-file changes, run \`kgraph "<topic>" --final\`. If KGraph reports capture-required, run \`kgraph "<topic>" --capture "<durable conclusion>" --capture-file <path> --capture-symbol <name>\`, or explicitly say "No durable knowledge created" only when there is genuinely no reusable knowledge.
24
25
  `,
25
26
  },
26
27
  {
@@ -85,7 +86,7 @@ export const claudeCodeAdapter = {
85
86
  },
86
87
  {
87
88
  path: '.claude/commands/kgraph-conclude.md',
88
- content: `Use \`kgraph conclude "$ARGUMENTS"\` when the session produced reusable engineering knowledge. Choose one type from finding, decision, gotcha, summary, relationship, and one confidence from high, medium, low. Store only durable conclusions, not raw chain-of-thought, temporary reasoning, speculative exploration, or low-value observations.
89
+ content: `Prefer \`kgraph "<topic>" --capture "$ARGUMENTS" --capture-file <path> --capture-symbol <name>\` when the session produced reusable engineering knowledge. Use \`kgraph conclude "$ARGUMENTS"\` only when the user explicitly asks for the conclude command. Choose one type from finding, decision, gotcha, summary, relationship, and one confidence from high, medium, low. Add file or symbol evidence whenever possible; high-confidence conclusions require evidence. Store only durable conclusions, not raw chain-of-thought, temporary reasoning, speculative exploration, or low-value observations.
89
90
  `,
90
91
  },
91
92
  {
@@ -1,11 +1,12 @@
1
1
  import { agentSkillFiles } from '../agent-skills.js';
2
+ import { numberedWorkflow } from '../workflow-steps.js';
2
3
  export const codexAdapter = {
3
4
  name: 'codex',
4
5
  label: 'Codex',
5
6
  targetPath: 'AGENTS.md',
6
7
  instructions: `## KGraph Workflow
7
8
 
8
- {{KGRAPH_CONTEXT_POLICY}} The /kgraph skill handles the full automated workflow. Run \`kgraph pack "<task>" --budget 8000 --json\` for a machine-readable token-budgeted context pack, \`kgraph knowledge list\` or \`kgraph knowledge get <atom-id>\` to inspect durable atoms, \`kgraph stale\` and \`kgraph blame <atom-id>\` when lifecycle/provenance matters, \`kgraph conclude\` for durable typed engineering memory, and \`kgraph compact --dry-run\` when cognition looks duplicated or stale. Run \`kgraph doctor\` when setup or generated maps look wrong. Run \`kgraph scan\`, \`kgraph update\`, and \`kgraph context\` manually only when you need one specific step.
9
+ ${numberedWorkflow('codex')}
9
10
  `,
10
11
  commandFiles: agentSkillFiles('codex'),
11
12
  obsoleteCommandFiles: [],
@@ -127,7 +127,7 @@ name: kgraph-conclude
127
127
  description: Store a typed durable KGraph engineering conclusion
128
128
  ---
129
129
 
130
- Use \`kgraph conclude "$ARGUMENTS"\` when the session produced reusable engineering knowledge. Choose one type from finding, decision, gotcha, summary, relationship, and one confidence from high, medium, low. Store only durable conclusions, not raw chain-of-thought, temporary reasoning, speculative exploration, or low-value observations.
130
+ Prefer \`kgraph "<topic>" --capture "$ARGUMENTS" --capture-file <path> --capture-symbol <name>\` when the session produced reusable engineering knowledge. Use low-level \`kgraph conclude "$ARGUMENTS"\` only when the user explicitly asks for the conclude command. Choose one type from finding, decision, gotcha, summary, relationship, and one confidence from high, medium, low. Add file or symbol evidence whenever possible; high-confidence conclusions require evidence. Store only durable conclusions, not raw chain-of-thought, temporary reasoning, speculative exploration, or low-value observations.
131
131
  `,
132
132
  },
133
133
  {
@@ -51,12 +51,15 @@ export function renderContextPolicy(mode) {
51
51
  }
52
52
  export function renderCapturePolicy() {
53
53
  return `Capture policy:
54
- - At the end of any session that changed repository files, store durable engineering memory with \`kgraph conclude "<topic>" --type <finding|decision|gotcha|summary|relationship> --confidence <high|medium|low>\` or \`kgraph session end --agent <agent> --conclude --topic "<topic>"\`.
54
+ - At the end of any session that changed repository files, run \`kgraph "<topic>" --final\` before the final response so KGraph can enforce whether capture is required.
55
+ - If final check reports capture-required, run \`kgraph "<topic>" --capture "<durable conclusion>" --capture-file <path> --capture-symbol <name>\` to store durable engineering memory, or explicitly say "No durable knowledge created" only when there is genuinely no reusable knowledge.
56
+ - Low-level alternatives remain available: \`kgraph conclude "<topic>" --type <finding|decision|gotcha|summary|relationship> --confidence <high|medium|low>\` or \`kgraph session end --agent <agent> --conclude --topic "<topic>"\`.
57
+ - Add evidence when storing cognition. High-confidence conclusions must include at least one \`--file <path>\` or \`--symbol <name>\`; medium-confidence conclusions should include evidence when possible.
55
58
  - Preserve only expensive-to-rediscover findings, decisions, gotchas, summaries, and relationships. Do not store raw chain-of-thought, temporary reasoning, speculative exploration, or low-value observations.
56
59
  - Use \`.kgraph/inbox/<slug>.md\` only when a longer structured note is clearer than a single \`kgraph conclude\` command.
57
60
  - A \`.kgraph/inbox/*.md\` note is KGraph runtime capture, not project documentation. It is allowed by this workflow unless the user explicitly says not to capture to KGraph.
58
61
  - Do not skip capture for meaningful UI text, button, link, route, styling, or small file edits. Skip capture only when no reusable repository knowledge was created.
59
- - Do not run KGraph repeatedly. Run it once at the start with \`kgraph pack "<topic>" --budget 8000 --json\` for agent-readable context. If repo files changed, write the inbox note first, then run \`kgraph\` once at the end.
62
+ - Do not run KGraph repeatedly. Run it once at the start with \`kgraph "<topic>"\` or \`kgraph pack "<topic>" --budget 8000 --json\` for agent-readable context. If repo files changed, run \`kgraph "<topic>" --final\` once before the final answer.
60
63
  - After the final \`kgraph\` run, mention whether durable cognition was stored or processed.
61
64
 
62
65
  When using an inbox note, use this structure:
@@ -9,6 +9,7 @@ const COMPACT_STEP = `Run \`kgraph compact --dry-run\` when cognition looks dupl
9
9
  const HISTORY_STEP = `Run \`kgraph history\` or \`kgraph history "<topic>"\` to review past cognition sessions with git author attribution.`;
10
10
  const KNOWLEDGE_STEP = `Run \`kgraph knowledge list --topic "<topic>"\` or \`kgraph knowledge get <atom-id>\` when the user asks what KGraph remembers or atom provenance/lifecycle matters.`;
11
11
  const PACK_STEP = `Treat \`kgraph pack "<task>" --budget 8000 --json\` as the primary agent contract: use atoms, source ranges, git changes, omitted items, and inclusion reasons from the ContextPack before reading files. Use human \`kgraph "<topic>"\` output only when the user explicitly wants a briefing.`;
12
+ const SMART_ROOT_STEP = `Prefer the root workflow for normal agent work: run \`kgraph "<topic>"\` to refresh maps, process cognition, report memory health, and return context; run \`kgraph "<topic>" --final\` before the final answer when repository files changed; run \`kgraph "<topic>" --capture "<durable conclusion>" --capture-file <path> --capture-symbol <name>\` when the final check requires durable knowledge.`;
12
13
  const STALE_STEP = `Run \`kgraph stale\` when changed or deleted code may have invalidated durable knowledge. Run \`kgraph blame <atom-id>\` when provenance or evidence for a memory matters.`;
13
14
  const EXPLORATION_BOUNDARY_STEP = `Keep exploration bounded by the task. For simple edits, use KGraph to identify the likely file, then read only that file or a narrow range and make the edit. Do not keep searching after the target file is found, do not retry malformed shell commands with broader variants, and do not run broad \`find\`, recursive \`grep\`, or repeated full-file dumps after KGraph already returned candidate files. Use \`rg --files\` and quoted paths when a path must be located.`;
14
15
  const VERIFY_EDIT_STEP = `After editing, verify the change actually landed before claiming completion. Prefer a narrow read of the changed range or \`git diff -- <path>\`; if there is no diff or the expected text is missing, say the edit did not apply and fix it before summarizing.`;
@@ -26,19 +27,20 @@ export function numberedWorkflow(agentName, options = {}) {
26
27
  3. Use the returned files, symbols, relationships, and cognition before broad exploration.
27
28
  4. ${EXPLORATION_BOUNDARY_STEP}
28
29
  5. ${VERIFY_EDIT_STEP}
29
- 6. ${PACK_STEP}
30
- 7. ${KNOWLEDGE_STEP}
31
- 8. ${DOCTOR_STEP}
32
- 9. ${STALE_STEP}
33
- 10. ${sessionStep(agentName, options.sessionQualifier)}
34
- 11. ${IMPACT_STEP}
30
+ 6. ${SMART_ROOT_STEP}
31
+ 7. ${PACK_STEP}
32
+ 8. ${KNOWLEDGE_STEP}
33
+ 9. ${DOCTOR_STEP}
34
+ 10. ${STALE_STEP}
35
+ 11. ${sessionStep(agentName, options.sessionQualifier)}
36
+ 12. ${IMPACT_STEP}
35
37
 
36
38
  {{KGRAPH_CAPTURE_POLICY}}
37
39
 
38
- 12. ${REPAIR_STEP}
39
- 13. ${COMPACT_STEP}
40
- 14. Run \`kgraph visualize\` when the user wants to inspect the dependency graph — opens an interactive graph at http://localhost:4242 with PNG export.
41
- 15. ${HISTORY_STEP}`;
40
+ 13. ${REPAIR_STEP}
41
+ 14. ${COMPACT_STEP}
42
+ 15. Run \`kgraph visualize\` when the user wants to inspect the dependency graph — opens an interactive graph at http://localhost:4242 with PNG export.
43
+ 16. ${HISTORY_STEP}`;
42
44
  }
43
45
  /**
44
46
  * Returns the bullet-list workflow for rules files.
@@ -48,6 +50,7 @@ export function bulletWorkflow(agentName, options = {}) {
48
50
  return `- {{KGRAPH_CONTEXT_POLICY}}
49
51
  - ${EXPLORATION_BOUNDARY_STEP}
50
52
  - ${VERIFY_EDIT_STEP}
53
+ - ${SMART_ROOT_STEP}
51
54
  - ${PACK_STEP}
52
55
  - ${KNOWLEDGE_STEP}
53
56
  - ${DOCTOR_STEP}
@@ -209,6 +209,8 @@ export async function validateKnowledgeStore(workspace, maps) {
209
209
  const symbolNames = new Set(maps.symbolMap.symbols.map((symbol) => symbol.name));
210
210
  const symbolIds = new Set(maps.symbolMap.symbols.map((symbol) => symbol.id));
211
211
  for (const atom of atoms) {
212
+ if (atom.status === 'archived')
213
+ continue;
212
214
  for (const ref of atom.evidenceRefs) {
213
215
  if (ref.type === 'file') {
214
216
  const file = fileByPath.get(ref.path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kentwynn/kgraph",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "Persistent repo intelligence for AI coding assistants.",
5
5
  "type": "module",
6
6
  "bin": {