hadara 0.3.2 → 0.3.3-rc.0

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.
Files changed (51) hide show
  1. package/README.md +38 -23
  2. package/dist/cli/context.js +110 -0
  3. package/dist/cli/help.js +6 -7
  4. package/dist/cli/init.js +56 -58
  5. package/dist/cli/main.js +12 -0
  6. package/dist/cli/session.js +37 -0
  7. package/dist/cli/task.js +52 -0
  8. package/dist/context/code-graph-extractor.js +123 -0
  9. package/dist/context/code-index.js +1154 -0
  10. package/dist/context/context-cache-store.js +925 -0
  11. package/dist/context/context-graph-builder.js +341 -0
  12. package/dist/context/context-graph.js +42 -0
  13. package/dist/context/context-pack.js +457 -0
  14. package/dist/context/context-slice-boundary.js +37 -0
  15. package/dist/context/context-slice.js +487 -0
  16. package/dist/context/document-extractors.js +343 -0
  17. package/dist/context/evidence-extractors.js +179 -0
  18. package/dist/context/extractor-contract.js +166 -0
  19. package/dist/context/registry-extractors.js +177 -0
  20. package/dist/context/release-extractors.js +175 -0
  21. package/dist/context/session-start.js +297 -0
  22. package/dist/context/source-manifest.js +566 -0
  23. package/dist/context/state-projection.js +196 -0
  24. package/dist/context/task-extractors.js +168 -0
  25. package/dist/core/schema.js +26 -0
  26. package/dist/harness/validate.js +7 -8
  27. package/dist/schemas/code-index.schema.json +173 -0
  28. package/dist/schemas/context-cache-record.schema.json +56 -0
  29. package/dist/schemas/context-cache-status.schema.json +167 -0
  30. package/dist/schemas/context-cache-warm.schema.json +222 -0
  31. package/dist/schemas/context-graph.schema.json +286 -0
  32. package/dist/schemas/context-pack.schema.json +246 -0
  33. package/dist/schemas/context-slice.schema.json +94 -0
  34. package/dist/schemas/context-source-manifest.schema.json +147 -0
  35. package/dist/schemas/schema-index.json +91 -0
  36. package/dist/schemas/session-start.schema.json +158 -0
  37. package/dist/schemas/task-close-repair-plan.schema.json +67 -0
  38. package/dist/schemas/task-context.schema.json +125 -0
  39. package/dist/schemas/task-finalize.schema.json +98 -0
  40. package/dist/schemas/task-lifecycle.schema.json +84 -0
  41. package/dist/services/capability-registry.js +266 -9
  42. package/dist/services/lifecycle-guide.js +23 -29
  43. package/dist/services/protocol-consistency.js +6 -8
  44. package/dist/task/acceptance.js +171 -0
  45. package/dist/task/task-close-repair-plan.js +190 -0
  46. package/dist/task/task-close.js +34 -35
  47. package/dist/task/task-finalize.js +377 -0
  48. package/dist/task/task-lifecycle.js +210 -0
  49. package/dist/task/task-next.js +10 -1
  50. package/dist/task/task-ready.js +4 -0
  51. package/package.json +1 -1
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.extractDocsRegistry = extractDocsRegistry;
7
+ exports.extractCommandRegistry = extractCommandRegistry;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const extractor_contract_1 = require("./extractor-contract");
11
+ const docs_registry_1 = require("../services/docs-registry");
12
+ const capability_registry_1 = require("../services/capability-registry");
13
+ const COMMAND_REGISTRY_SOURCE_PATH = 'src/services/capability-registry.ts';
14
+ function extractDocsRegistry(projectRoot) {
15
+ const absolutePath = node_path_1.default.join(projectRoot, docs_registry_1.DOCS_REGISTRY_PATH);
16
+ const content = readOptionalText(absolutePath);
17
+ const result = (0, extractor_contract_1.createEmptyExtractionResult)('extractDocsRegistry', [{ path: docs_registry_1.DOCS_REGISTRY_PATH, content }]);
18
+ if (content == null) {
19
+ result.issues.push({
20
+ severity: 'warning',
21
+ code: 'CONTEXT_GRAPH_DOC_REGISTRY_MISSING',
22
+ path: docs_registry_1.DOCS_REGISTRY_PATH,
23
+ message: '.hadara/docs-registry.json is missing; Document nodes cannot be extracted from registry metadata.',
24
+ fixHint: 'Restore the docs registry artifact or run the scoped docs registry workflow before relying on document context routing.'
25
+ });
26
+ return result;
27
+ }
28
+ let registry;
29
+ try {
30
+ registry = JSON.parse(content);
31
+ }
32
+ catch (error) {
33
+ result.issues.push(parseFailedIssue(docs_registry_1.DOCS_REGISTRY_PATH, `.hadara/docs-registry.json could not be parsed: ${error instanceof Error ? error.message : String(error)}`));
34
+ return result;
35
+ }
36
+ const sourceHash = (0, extractor_contract_1.hashContextGraphText)(content);
37
+ const source = (0, extractor_contract_1.createContextGraphSourceRef)({
38
+ path: docs_registry_1.DOCS_REGISTRY_PATH,
39
+ extractor: 'extractDocsRegistry',
40
+ line: 1,
41
+ hash: sourceHash
42
+ });
43
+ const documents = Array.isArray(registry.documents) ? registry.documents : [];
44
+ result.nodes.push(...documents.map((doc) => documentNode(doc, source)));
45
+ result.edges.push(...documents.flatMap((doc) => documentEdges(doc, source)));
46
+ result.stateSources?.push(docsRegistryStateSource(documents, sourceHash));
47
+ return result;
48
+ }
49
+ function extractCommandRegistry(projectRoot) {
50
+ const absolutePath = node_path_1.default.join(projectRoot, COMMAND_REGISTRY_SOURCE_PATH);
51
+ const content = readOptionalText(absolutePath);
52
+ const result = (0, extractor_contract_1.createEmptyExtractionResult)('extractCommandRegistry', [{ path: COMMAND_REGISTRY_SOURCE_PATH, content }]);
53
+ const sourceHash = content == null ? undefined : (0, extractor_contract_1.hashContextGraphText)(content);
54
+ if (content == null) {
55
+ result.issues.push({
56
+ severity: 'warning',
57
+ code: 'CONTEXT_GRAPH_COMMAND_REGISTRY_MISSING',
58
+ path: COMMAND_REGISTRY_SOURCE_PATH,
59
+ message: 'Command registry source file is missing; command metadata is still available from the loaded runtime registry but source hash is unavailable.',
60
+ fixHint: 'Restore src/services/capability-registry.ts in source checkouts before relying on source-addressed command context.'
61
+ });
62
+ }
63
+ const source = (0, extractor_contract_1.createContextGraphSourceRef)({
64
+ path: COMMAND_REGISTRY_SOURCE_PATH,
65
+ extractor: 'extractCommandRegistry',
66
+ line: 1,
67
+ ...(sourceHash ? { hash: sourceHash } : {})
68
+ });
69
+ const commands = (0, capability_registry_1.listCommandRegistryEntries)();
70
+ result.nodes.push(...commands.map((entry) => commandNode(entry, source)));
71
+ result.edges.push(...commands.flatMap((entry) => commandDocEdges(entry, source)));
72
+ return result;
73
+ }
74
+ function documentNode(doc, source) {
75
+ return {
76
+ id: (0, extractor_contract_1.createDocumentNodeId)(doc.path),
77
+ type: 'Document',
78
+ label: doc.title || doc.path,
79
+ path: (0, extractor_contract_1.normalizeContextGraphPath)(doc.path),
80
+ status: doc.status,
81
+ kind: doc.kind,
82
+ owner: doc.owner,
83
+ metadata: {
84
+ requiredReading: doc.requiredReading,
85
+ readWhen: doc.readWhen,
86
+ scope: doc.scope,
87
+ updateOwner: doc.updateOwner,
88
+ closeSourceRole: doc.closeSourceRole,
89
+ supersedes: doc.supersedes,
90
+ supersededBy: doc.supersededBy ?? null
91
+ },
92
+ source
93
+ };
94
+ }
95
+ function documentEdges(doc, source) {
96
+ const from = (0, extractor_contract_1.createDocumentNodeId)(doc.path);
97
+ const edges = [];
98
+ for (const targetPath of doc.supersedes ?? []) {
99
+ const to = (0, extractor_contract_1.createDocumentNodeId)(targetPath);
100
+ edges.push(edge('SUPERSEDES', from, to, source, `${doc.path} supersedes ${targetPath}.`));
101
+ }
102
+ if (doc.supersededBy) {
103
+ const to = (0, extractor_contract_1.createDocumentNodeId)(doc.supersededBy);
104
+ edges.push(edge('SUPERSEDES', to, from, source, `${doc.supersededBy} supersedes ${doc.path}.`));
105
+ }
106
+ return edges;
107
+ }
108
+ function commandNode(entry, source) {
109
+ return {
110
+ id: (0, extractor_contract_1.createCommandNodeId)(entry.id),
111
+ type: 'Command',
112
+ label: entry.id,
113
+ status: entry.status,
114
+ kind: entry.family,
115
+ owner: entry.actor,
116
+ metadata: {
117
+ command: entry.command,
118
+ summary: entry.summary,
119
+ canonical: entry.canonical,
120
+ aliasFor: entry.aliasFor ?? null,
121
+ scope: entry.scope,
122
+ lifecycleStage: entry.lifecycleStage,
123
+ requiredness: entry.requiredness,
124
+ writeBoundary: entry.writeBoundary,
125
+ readOnly: entry.readOnly,
126
+ risk: entry.risk,
127
+ schemaVersion: entry.schemaVersion ?? null,
128
+ implementationFiles: entry.implementationFiles ?? [],
129
+ testFiles: entry.testFiles ?? [],
130
+ docs: entry.docs,
131
+ related: entry.related
132
+ },
133
+ source
134
+ };
135
+ }
136
+ function commandDocEdges(entry, source) {
137
+ return entry.docs.map((docPath) => edge('DESCRIBES_COMMAND', (0, extractor_contract_1.createDocumentNodeId)(docPath), (0, extractor_contract_1.createCommandNodeId)(entry.id), source, `${docPath} documents command ${entry.id}.`));
138
+ }
139
+ function docsRegistryStateSource(documents, sourceHash) {
140
+ return {
141
+ id: 'state-source:docs-registry',
142
+ path: docs_registry_1.DOCS_REGISTRY_PATH,
143
+ kind: 'docs-registry',
144
+ hash: sourceHash,
145
+ extracted: {
146
+ documents: documents.length,
147
+ requiredReading: documents.filter((doc) => doc.requiredReading).length,
148
+ statusCounts: documents.reduce((counts, doc) => {
149
+ counts[doc.status] = (counts[doc.status] ?? 0) + 1;
150
+ return counts;
151
+ }, {})
152
+ }
153
+ };
154
+ }
155
+ function edge(type, from, to, source, reason) {
156
+ return {
157
+ id: (0, extractor_contract_1.createContextGraphEdgeId)({ type, from, to, source, reason }),
158
+ from,
159
+ to,
160
+ type,
161
+ confidence: 'explicit',
162
+ reason,
163
+ source
164
+ };
165
+ }
166
+ function readOptionalText(absolutePath) {
167
+ return node_fs_1.default.existsSync(absolutePath) ? node_fs_1.default.readFileSync(absolutePath, 'utf8') : null;
168
+ }
169
+ function parseFailedIssue(relativePath, message) {
170
+ return {
171
+ severity: 'warning',
172
+ code: 'CONTEXT_GRAPH_PARSE_FAILED',
173
+ path: relativePath,
174
+ message,
175
+ fixHint: `Repair ${relativePath} before relying on context graph extraction for this source.`
176
+ };
177
+ }
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.extractReleaseReadiness = extractReleaseReadiness;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const extractor_contract_1 = require("./extractor-contract");
10
+ const capability_registry_1 = require("../services/capability-registry");
11
+ const RELEASE_READINESS_PATH = 'docs/RELEASE_READINESS.md';
12
+ const EVIDENCE_ID_PATTERN = /\bev:T-\d{4}:[a-f0-9]{24,64}\b/g;
13
+ function extractReleaseReadiness(projectRoot) {
14
+ const content = readOptionalText(node_path_1.default.join(projectRoot, RELEASE_READINESS_PATH));
15
+ const result = (0, extractor_contract_1.createEmptyExtractionResult)('extractReleaseReadiness', [{ path: RELEASE_READINESS_PATH, content }]);
16
+ if (content == null) {
17
+ result.issues.push({
18
+ severity: 'warning',
19
+ code: 'CONTEXT_GRAPH_SOURCE_MISSING',
20
+ path: RELEASE_READINESS_PATH,
21
+ message: 'docs/RELEASE_READINESS.md is missing; ReleaseCheck nodes cannot be extracted.',
22
+ fixHint: 'Restore docs/RELEASE_READINESS.md before relying on release-readiness context routing.'
23
+ });
24
+ return result;
25
+ }
26
+ const sourceHash = (0, extractor_contract_1.hashContextGraphText)(content);
27
+ const commandEntries = (0, capability_registry_1.listCommandRegistryEntries)();
28
+ const sections = parseReleaseReadinessSections(content);
29
+ for (const section of sections) {
30
+ const source = (0, extractor_contract_1.createContextGraphSourceRef)({
31
+ path: RELEASE_READINESS_PATH,
32
+ extractor: 'extractReleaseReadiness',
33
+ line: section.startLine,
34
+ hash: sourceHash
35
+ });
36
+ const checkNodeId = (0, extractor_contract_1.createReleaseCheckNodeId)(section.title);
37
+ const commandMentions = findCommandMentions(section.text, commandEntries);
38
+ const evidenceIds = findEvidenceIds(section.text);
39
+ result.nodes.push(releaseCheckNode(section, source, commandMentions, evidenceIds));
40
+ result.edges.push(edge('BELONGS_TO_DOCUMENT', checkNodeId, (0, extractor_contract_1.createDocumentNodeId)(RELEASE_READINESS_PATH), source, `${section.title} is defined in ${RELEASE_READINESS_PATH}.`));
41
+ for (const mention of commandMentions) {
42
+ result.edges.push(edge('CHECKS_COMMAND', checkNodeId, (0, extractor_contract_1.createCommandNodeId)(mention.commandId), source, `${section.title} explicitly references command ${mention.commandId}.`));
43
+ }
44
+ for (const evidenceId of evidenceIds) {
45
+ result.edges.push(edge('DEPENDS_ON_EVIDENCE', checkNodeId, (0, extractor_contract_1.createEvidenceNodeId)(evidenceId), source, `${section.title} explicitly references evidence ${evidenceId}.`));
46
+ }
47
+ }
48
+ result.stateSources?.push(releaseReadinessStateSource(sourceHash, sections, result.nodes, result.edges));
49
+ return result;
50
+ }
51
+ function releaseCheckNode(section, source, commandMentions, evidenceIds) {
52
+ const status = deriveSectionStatus(section.text);
53
+ return {
54
+ id: (0, extractor_contract_1.createReleaseCheckNodeId)(section.title),
55
+ type: 'ReleaseCheck',
56
+ label: section.title,
57
+ path: (0, extractor_contract_1.normalizeContextGraphPath)(RELEASE_READINESS_PATH),
58
+ status,
59
+ kind: 'release-readiness-section',
60
+ metadata: {
61
+ startLine: section.startLine,
62
+ endLine: section.endLine,
63
+ commandIds: commandMentions.map((mention) => mention.commandId),
64
+ commandMentions: commandMentions.map((mention) => mention.mention),
65
+ evidenceIds,
66
+ summary: firstMeaningfulLine(section.text)
67
+ },
68
+ source
69
+ };
70
+ }
71
+ function parseReleaseReadinessSections(content) {
72
+ const lines = content.split(/\r?\n/);
73
+ const headingIndexes = [];
74
+ lines.forEach((line, index) => {
75
+ const match = line.match(/^##\s+(.+?)\s*#*\s*$/);
76
+ if (match?.[1])
77
+ headingIndexes.push({ index, title: match[1].trim() });
78
+ });
79
+ return headingIndexes.map((heading, position) => {
80
+ const nextHeading = headingIndexes[position + 1]?.index ?? lines.length;
81
+ const bodyLines = lines.slice(heading.index + 1, nextHeading);
82
+ return {
83
+ title: heading.title,
84
+ startLine: heading.index + 1,
85
+ endLine: nextHeading,
86
+ text: bodyLines.join('\n').trim()
87
+ };
88
+ });
89
+ }
90
+ function findCommandMentions(text, entries) {
91
+ const spans = codeSpans(text).filter((span) => /\bhadara\b/.test(span));
92
+ const mentions = new Map();
93
+ for (const span of spans) {
94
+ const normalizedSpan = normalizeCommandText(span);
95
+ for (const entry of entries) {
96
+ const prefix = commandPrefix(entry.command);
97
+ if (!prefix || !normalizedSpan.startsWith(prefix))
98
+ continue;
99
+ mentions.set(entry.id, {
100
+ commandId: entry.id,
101
+ command: entry.command,
102
+ mention: span
103
+ });
104
+ }
105
+ }
106
+ return Array.from(mentions.values()).sort((a, b) => a.commandId.localeCompare(b.commandId));
107
+ }
108
+ function findEvidenceIds(text) {
109
+ return Array.from(new Set(text.match(EVIDENCE_ID_PATTERN) ?? [])).sort();
110
+ }
111
+ function codeSpans(text) {
112
+ const spans = [];
113
+ for (const match of text.matchAll(/`([^`]+)`/g)) {
114
+ if (match[1])
115
+ spans.push(match[1].trim());
116
+ }
117
+ return spans;
118
+ }
119
+ function commandPrefix(command) {
120
+ return normalizeCommandText(command)
121
+ .replace(/\s*\[[^\]]+\]/g, '')
122
+ .replace(/\s*<[^>]+>/g, '')
123
+ .trim();
124
+ }
125
+ function normalizeCommandText(command) {
126
+ return command.replace(/\s+/g, ' ').trim();
127
+ }
128
+ function deriveSectionStatus(text) {
129
+ const lowered = text.toLowerCase();
130
+ if (/\b(deferred|reserved)\b/.test(lowered))
131
+ return 'deferred';
132
+ if (/\b(blocked|blocking)\b/.test(lowered))
133
+ return 'blocked';
134
+ if (/\b(complete|completed|passed|published)\b/.test(lowered))
135
+ return 'current';
136
+ return 'documented';
137
+ }
138
+ function firstMeaningfulLine(text) {
139
+ return text.split(/\r?\n/)
140
+ .map((line) => line.trim().replace(/^-\s+/, ''))
141
+ .find((line) => line.length > 0) ?? null;
142
+ }
143
+ function releaseReadinessStateSource(sourceHash, sections, nodes, edges) {
144
+ return {
145
+ id: 'state-source:release-readiness',
146
+ path: RELEASE_READINESS_PATH,
147
+ kind: 'release-readiness',
148
+ hash: sourceHash,
149
+ extracted: {
150
+ checks: sections.length,
151
+ headings: sections.map((section) => section.title),
152
+ statusCounts: nodes.reduce((counts, node) => {
153
+ const status = node.status ?? 'unknown';
154
+ counts[status] = (counts[status] ?? 0) + 1;
155
+ return counts;
156
+ }, {}),
157
+ commandReferences: edges.filter((edgeItem) => edgeItem.type === 'CHECKS_COMMAND').length,
158
+ evidenceReferences: edges.filter((edgeItem) => edgeItem.type === 'DEPENDS_ON_EVIDENCE').length
159
+ }
160
+ };
161
+ }
162
+ function edge(type, from, to, source, reason) {
163
+ return {
164
+ id: (0, extractor_contract_1.createContextGraphEdgeId)({ type, from, to, source, reason }),
165
+ from,
166
+ to,
167
+ type,
168
+ confidence: 'explicit',
169
+ reason,
170
+ source
171
+ };
172
+ }
173
+ function readOptionalText(absolutePath) {
174
+ return node_fs_1.default.existsSync(absolutePath) ? node_fs_1.default.readFileSync(absolutePath, 'utf8') : null;
175
+ }
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SESSION_START_COMMAND = exports.SESSION_START_SCHEMA_ID = void 0;
4
+ exports.buildSessionStartReport = buildSessionStartReport;
5
+ const context_pack_1 = require("./context-pack");
6
+ const context_graph_builder_1 = require("./context-graph-builder");
7
+ const context_cache_store_1 = require("./context-cache-store");
8
+ const source_manifest_1 = require("./source-manifest");
9
+ const code_graph_extractor_1 = require("./code-graph-extractor");
10
+ exports.SESSION_START_SCHEMA_ID = 'hadara.sessionStart.v1';
11
+ exports.SESSION_START_COMMAND = 'session.start';
12
+ function buildSessionStartReport(input) {
13
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
14
+ const contextPack = input.contextPack ?? (input.allowLiveContextPack
15
+ ? (0, context_pack_1.buildContextPackReport)({
16
+ projectRoot: input.projectRoot,
17
+ generatedAt,
18
+ includeCode: input.includeCode,
19
+ ...(input.taskId ? { taskId: input.taskId } : {}),
20
+ ...(input.budget ? { budget: input.budget } : {})
21
+ })
22
+ : buildWarmCachedContextPackReport({
23
+ projectRoot: input.projectRoot,
24
+ generatedAt,
25
+ taskId: input.taskId,
26
+ includeCode: input.includeCode,
27
+ budget: input.budget
28
+ }) ?? buildBoundedContextPackReport({
29
+ projectRoot: input.projectRoot,
30
+ generatedAt,
31
+ taskId: input.taskId,
32
+ budget: input.budget
33
+ }));
34
+ const stateProjection = contextPack.stateProjection;
35
+ const taskId = contextPack.taskId ?? input.taskId ?? stateProjection.activeTask;
36
+ const issues = [...contextPack.issues];
37
+ const lifecycle = lifecycleForSessionStart(taskId, contextPack);
38
+ const guidance = guidanceForSessionStart({
39
+ taskId,
40
+ contextPack,
41
+ lifecycle,
42
+ allowLiveContextPack: Boolean(input.allowLiveContextPack)
43
+ });
44
+ return {
45
+ schemaVersion: exports.SESSION_START_SCHEMA_ID,
46
+ command: exports.SESSION_START_COMMAND,
47
+ ok: contextPack.ok && issues.every((issue) => issue.severity !== 'error'),
48
+ generatedAt,
49
+ projectRoot: input.projectRoot,
50
+ currentState: {
51
+ ...(stateProjection.activeTask ? { activeTask: stateProjection.activeTask } : {}),
52
+ ...(stateProjection.latestCompletedTask ? { latestCompletedTask: stateProjection.latestCompletedTask } : {}),
53
+ ...(taskId ? { recommendedNextTask: taskId } : {}),
54
+ ...(stateProjection.releaseState ? { releaseState: stateProjection.releaseState } : {})
55
+ },
56
+ contextPack,
57
+ lifecycle,
58
+ guidance,
59
+ knownProblems: contextPack.knownProblems,
60
+ sourceSummary: contextPack.sourceSummary,
61
+ cache: contextPack.cache,
62
+ summary: {
63
+ degraded: contextPack.sourceSummary.degraded || issues.some((issue) => issue.severity !== 'info'),
64
+ readFirstCount: contextPack.readFirst.length,
65
+ readIfNeededCount: contextPack.readIfNeeded.length,
66
+ sliceCandidateCount: contextPack.sliceCandidates.length,
67
+ knownProblemCount: contextPack.knownProblems.length,
68
+ issueCount: issues.length
69
+ },
70
+ issues
71
+ };
72
+ }
73
+ function buildWarmCachedContextPackReport(input) {
74
+ const cachedManifest = (0, context_cache_store_1.readContextSourceManifestCache)(input.projectRoot);
75
+ if (cachedManifest.status !== 'valid' || !cachedManifest.manifest)
76
+ return undefined;
77
+ const fastFreshness = (0, source_manifest_1.checkContextSourceManifestFastFreshness)(input.projectRoot, cachedManifest.manifest);
78
+ if (!fastFreshness.ok)
79
+ return undefined;
80
+ const graphCore = (0, context_cache_store_1.readContextGraphCoreShard)({
81
+ projectRoot: input.projectRoot,
82
+ manifest: cachedManifest.manifest
83
+ });
84
+ if (!graphCore.hit || !graphCore.result)
85
+ return undefined;
86
+ const extractionResults = [graphCore.result];
87
+ const code = input.includeCode
88
+ ? (0, context_cache_store_1.readContextCodeIndexShard)({
89
+ projectRoot: input.projectRoot,
90
+ manifest: cachedManifest.manifest
91
+ })
92
+ : undefined;
93
+ if (code?.hit && code.result) {
94
+ extractionResults.push((0, code_graph_extractor_1.codeIndexReportToGraphExtraction)(code.result));
95
+ }
96
+ const cache = {
97
+ used: true,
98
+ hit: !input.includeCode || Boolean(code?.hit),
99
+ mode: input.includeCode
100
+ ? code?.hit ? 'graph-core+code-index' : `graph-core+code-index-${code?.status ?? 'missing'}`
101
+ : 'graph-core',
102
+ manifestHash: cachedManifest.manifest.manifestHash,
103
+ readShardCount: 1 + (input.includeCode ? 1 : 0),
104
+ hitShardCount: 1 + (code?.hit ? 1 : 0),
105
+ missShardCount: code?.status === 'missing' ? 1 : 0,
106
+ staleShardCount: code?.status === 'stale' ? 1 : 0,
107
+ corruptShardCount: code?.status === 'corrupt' ? 1 : 0,
108
+ schemaMismatchShardCount: code?.status === 'schema-mismatch' ? 1 : 0,
109
+ shardPaths: [graphCore.path, ...(code ? [code.path] : [])].sort(),
110
+ staleExtractorKeys: code?.status === 'stale' ? ['codeIndex'] : [],
111
+ ...(graphCore.record ? { createdAt: graphCore.record.createdAt, cachePath: graphCore.path } : {}),
112
+ sourceManifestCacheFresh: true,
113
+ sourceManifestFastPath: 'hit',
114
+ sourceManifestFastPathReason: fastFreshness.reason,
115
+ ...(fastFreshness.strategy ? { sourceManifestFastPathStrategy: fastFreshness.strategy } : {})
116
+ };
117
+ const graphReport = (0, context_graph_builder_1.buildContextGraphReport)({
118
+ projectRoot: input.projectRoot,
119
+ generatedAt: input.generatedAt,
120
+ ...(input.taskId ? { taskId: input.taskId, mode: 'task' } : { mode: 'full' }),
121
+ extractionResults,
122
+ cache
123
+ });
124
+ return (0, context_pack_1.buildContextPackReport)({
125
+ projectRoot: input.projectRoot,
126
+ generatedAt: input.generatedAt,
127
+ ...(input.taskId ? { taskId: input.taskId } : {}),
128
+ ...(input.budget ? { budget: input.budget } : {}),
129
+ includeCode: input.includeCode,
130
+ graphReport,
131
+ cache
132
+ });
133
+ }
134
+ function lifecycleForSessionStart(taskId, contextPack) {
135
+ const primaryNextCommands = taskId
136
+ ? [
137
+ `node dist/cli/main.js task status --task ${taskId} --json`,
138
+ `node dist/cli/main.js context pack --task ${taskId} --json`
139
+ ]
140
+ : ['node dist/cli/main.js task next --json'];
141
+ for (const suggestion of contextPack.validateWith) {
142
+ if (suggestion.requiredForClose && !primaryNextCommands.includes(suggestion.command)) {
143
+ primaryNextCommands.push(suggestion.command);
144
+ }
145
+ }
146
+ const diagnosticCommands = [
147
+ 'node dist/cli/main.js context cache status --json',
148
+ taskId
149
+ ? `node dist/cli/main.js context graph --task ${taskId} --json`
150
+ : 'node dist/cli/main.js context graph --json',
151
+ taskId
152
+ ? `node dist/cli/main.js session start --task ${taskId} --live --json`
153
+ : 'node dist/cli/main.js session start --live --json',
154
+ 'node dist/cli/main.js state verify --json'
155
+ ];
156
+ return {
157
+ primaryNextCommands,
158
+ diagnosticCommands
159
+ };
160
+ }
161
+ function guidanceForSessionStart(input) {
162
+ const mode = input.allowLiveContextPack
163
+ ? 'live-context-pack'
164
+ : input.contextPack.cache.used
165
+ ? 'warm-cache'
166
+ : 'bounded-no-live';
167
+ const taskId = input.taskId;
168
+ const taskRequired = !taskId;
169
+ const primaryNextAction = taskRequired ? 'select-task' : 'inspect-task';
170
+ const reason = taskRequired
171
+ ? 'No task id was supplied, so bounded Session Start returned task-selection guidance without running live graph discovery.'
172
+ : mode === 'warm-cache'
173
+ ? 'Session Start used proven-fresh warm cache and preserved read-only behavior.'
174
+ : mode === 'live-context-pack'
175
+ ? 'Session Start used explicit live context-pack discovery because --live was supplied.'
176
+ : 'Session Start used the bounded no-live packet and avoided broad live graph discovery.';
177
+ const commands = [];
178
+ if (!taskId) {
179
+ commands.push({
180
+ id: 'task-next',
181
+ command: 'node dist/cli/main.js task next --json',
182
+ args: ['task', 'next', '--json'],
183
+ reason: 'Choose the next task before requesting task-scoped context.'
184
+ });
185
+ }
186
+ else {
187
+ commands.push({
188
+ id: 'task-status',
189
+ command: `node dist/cli/main.js task status --task ${taskId} --json`,
190
+ args: ['task', 'status', '--task', taskId, '--json'],
191
+ reason: 'Inspect task readiness, evidence, and lifecycle state.'
192
+ });
193
+ commands.push({
194
+ id: 'context-pack',
195
+ command: `node dist/cli/main.js context pack --task ${taskId} --json`,
196
+ args: ['context', 'pack', '--task', taskId, '--json'],
197
+ reason: 'Inspect the bounded task read plan without slicing raw source text.'
198
+ });
199
+ }
200
+ commands.push({
201
+ id: 'cache-warm',
202
+ command: 'node dist/cli/main.js context cache warm --json',
203
+ args: ['context', 'cache', 'warm', '--json'],
204
+ reason: 'Preview stale cache shards without writing cache.'
205
+ });
206
+ commands.push({
207
+ id: 'session-start-live',
208
+ command: taskId
209
+ ? `node dist/cli/main.js session start --task ${taskId} --live --json`
210
+ : 'node dist/cli/main.js session start --live --json',
211
+ args: taskId
212
+ ? ['session', 'start', '--task', taskId, '--live', '--json']
213
+ : ['session', 'start', '--live', '--json'],
214
+ reason: 'Opt into slower live context-pack discovery only when broad graph reads are acceptable.'
215
+ });
216
+ return {
217
+ mode,
218
+ primaryNextAction,
219
+ reason,
220
+ taskRequired,
221
+ liveContextPackAvailable: true,
222
+ commands
223
+ };
224
+ }
225
+ function buildBoundedContextPackReport(input) {
226
+ const budget = {
227
+ ...(input.budget?.targetTokens !== undefined ? { targetTokens: input.budget.targetTokens } : {}),
228
+ ...(input.budget?.maxItems !== undefined ? { maxItems: input.budget.maxItems } : {}),
229
+ maxReadFirstItems: input.budget?.maxReadFirstItems ?? 7,
230
+ mode: input.budget?.mode ?? 'bounded'
231
+ };
232
+ const issues = input.taskId
233
+ ? [{
234
+ severity: 'warning',
235
+ code: 'CONTEXT_PACK_DEGRADED',
236
+ message: 'Session start used the bounded no-live context pack envelope. Run session start --live or context pack explicitly when a full graph read is acceptable.',
237
+ fixHint: 'Run hadara context cache warm --execute --json before relying on broad graph-backed session context.'
238
+ }]
239
+ : [{
240
+ severity: 'warning',
241
+ code: 'CONTEXT_PACK_TASK_NOT_FOUND',
242
+ message: 'No task id was supplied. Bounded session start returned task-selection guidance without running live project discovery.',
243
+ fixHint: 'Run hadara task next --json, then rerun hadara session start --task <task-id> --json.'
244
+ }];
245
+ const readFirst = input.taskId
246
+ ? [{
247
+ id: `task:${input.taskId}`,
248
+ type: 'Task',
249
+ title: input.taskId,
250
+ reason: 'Explicit task id supplied to bounded session start.',
251
+ confidence: 'explicit',
252
+ required: true
253
+ }]
254
+ : [];
255
+ return {
256
+ schemaVersion: context_pack_1.CONTEXT_PACK_SCHEMA_ID,
257
+ command: context_pack_1.CONTEXT_PACK_COMMAND,
258
+ ok: issues.every((issue) => issue.severity !== 'error'),
259
+ generatedAt: input.generatedAt,
260
+ ...(input.taskId ? { taskId: input.taskId } : {}),
261
+ projectRoot: input.projectRoot,
262
+ budget,
263
+ readFirst: readFirst.slice(0, budget.maxReadFirstItems),
264
+ readIfNeeded: [],
265
+ doNotReadByDefault: [],
266
+ validateWith: input.taskId
267
+ ? [{
268
+ command: `node dist/cli/main.js task ready --task ${input.taskId} --level done --json`,
269
+ reason: 'Done-level readiness is required before closing this task.',
270
+ requiredForClose: true,
271
+ source: 'evidence-history'
272
+ }]
273
+ : [],
274
+ writeBoundaries: [],
275
+ sliceCandidates: [],
276
+ knownProblems: [],
277
+ stateProjection: {
278
+ ...(input.taskId ? { activeTask: input.taskId } : {}),
279
+ stateConsistency: 'unknown',
280
+ issues: []
281
+ },
282
+ sourceSummary: {
283
+ graphAvailable: false,
284
+ codeIndexAvailable: false,
285
+ stateProjectionAvailable: false,
286
+ docsRegistryAvailable: false,
287
+ commandRegistryAvailable: false,
288
+ degraded: true
289
+ },
290
+ cache: {
291
+ used: false,
292
+ hit: false,
293
+ mode: 'session-start-bounded-no-live'
294
+ },
295
+ issues
296
+ };
297
+ }