hadara 0.3.3 → 0.3.4-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.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <img alt="Stable npm release" src="https://img.shields.io/badge/npm-0.3.3-blue">
9
- <img alt="Source version" src="https://img.shields.io/badge/source-0.3.3-blue">
9
+ <img alt="Source version" src="https://img.shields.io/badge/source-0.3.4--rc.0-blue">
10
10
  <img alt="Node.js" src="https://img.shields.io/badge/node-%3E%3D22-brightgreen">
11
11
  <img alt="License" src="https://img.shields.io/badge/license-MIT-lightgrey">
12
12
  </p>
@@ -27,19 +27,26 @@ Current stable npm release:
27
27
  hadara@0.3.3
28
28
  ```
29
29
 
30
- Previous release candidate:
30
+ Current source release candidate:
31
+
32
+ ```text
33
+ hadara@0.3.4-rc.0
34
+ ```
35
+
36
+ Previous published release candidate:
31
37
 
32
38
  ```text
33
39
  hadara@0.3.3-rc.0
34
40
  ```
35
41
 
36
- The 0.3.3 line promotes the context-routing and lifecycle release candidate after dogfood hardening. It adds project context graph, context pack/slice/session-start surfaces, explicit context cache warm paths, and makes the finalize-first lifecycle the default agent-facing task loop.
42
+ The 0.3.4 source line is an agent UX hardening release candidate after stable 0.3.3. It keeps the context-routing and finalize-first lifecycle surfaces, then improves the commands agents actually use during HADARA development: session start, context pack, evidence summary, handoff stale-problem checks, release closeout, package recycle, generated init guidance, and close/finalize diagnostics.
37
43
 
38
- Phase labels are internal implementation phases, not npm release-candidate labels. The stable `0.3.3` package is the default install target; `0.3.3-rc.0` remains the previous release candidate.
44
+ Phase labels are internal implementation phases, not npm release-candidate labels. The stable `0.3.3` package is the default install target; `0.3.4-rc.0` is the current source candidate and must not be treated as published until an approval-gated publish capsule completes.
39
45
 
40
46
  | Surface | Status |
41
47
  |---|---|
42
48
  | Current stable | [`hadara@0.3.3`](docs/RELEASE_NOTES.md#033) |
49
+ | Source candidate | [`hadara@0.3.4-rc.0`](docs/RELEASE_NOTES.md#034-rc0) |
43
50
  | Previous RC | [`hadara@0.3.3-rc.0`](docs/RELEASE_NOTES.md#033-rc0) |
44
51
  | Previous stable | [`hadara@0.3.2`](docs/RELEASE_NOTES.md#032) |
45
52
  | Historical RCs | See [Release Notes](docs/RELEASE_NOTES.md). |
@@ -48,7 +55,7 @@ Phase labels are internal implementation phases, not npm release-candidate label
48
55
  | PyPI/Python package | `hadara==0.2.0rc1` published preview bridge. |
49
56
  | Installer scripts / USB launchers | Deferred. |
50
57
 
51
- No release command should publish, create a GitHub Release, build Docker images, upload artifacts, or load token values unless an operator explicitly approves the mutation path for the active release capsule. Stable `0.3.3` is the npm package line; GitHub Release, Docker/PyPI publish, installer execution, and MCP release/package execution remain separate explicit mutations.
58
+ No release command should publish, create a GitHub Release, build Docker images, upload artifacts, or load token values unless an operator explicitly approves the mutation path for the active release capsule. Stable `0.3.3` is the npm package line until the `0.3.4-rc.0` publish capsule completes; GitHub Release, Docker/PyPI publish, installer execution, and MCP release/package execution remain separate explicit mutations.
52
59
 
53
60
  ## Install
54
61
 
@@ -122,10 +129,11 @@ hadara help command task.close
122
129
 
123
130
  ## Primary Capsule Lifecycle
124
131
 
125
- The 0.3.3 primary path is intentionally small. Agents should use `task finalize` as the default lifecycle close path:
132
+ The 0.3.4 primary path is intentionally small. Agents should start with compact context and use `task finalize` as the default lifecycle close path:
126
133
 
127
134
  ```bash
128
135
  hadara task next --json
136
+ hadara session start --task T-XXXX --json
129
137
  hadara task create "implement a focused change" --json
130
138
  hadara task status --task T-XXXX --json
131
139
  hadara evidence add-command --task T-XXXX --summary "..." --result passed --category validation --idempotency-key "command:T-XXXX:check" --json
@@ -260,7 +268,7 @@ Dashboard, TUI, Hermes, MCP, installer, package, release, and run commands stay
260
268
 
261
269
  ## Safety Boundaries
262
270
 
263
- HADARA 0.3.3 is not:
271
+ HADARA 0.3.4-rc.0 is not:
264
272
 
265
273
  - a full agent runtime;
266
274
  - Rack/enterprise behavior;
@@ -300,7 +308,7 @@ Portable/local state is not committed. Project docs, Task Capsules, and reduced
300
308
 
301
309
  `evidence.jsonl` is the canonical Task Capsule evidence source. `EVIDENCE.md` is a non-canonical human summary that can help review validation history, but it must not be treated as the source of truth for rebuild, migration, or resolution logic.
302
310
 
303
- 0.3.3 does not implement `hadara evidence rebuild --json` or an execute mode. Future rebuild work must first define whether a difference is formatting regeneration, managed-section drift, or data inconsistency before reporting `wouldChange`. Any later write-capable rebuild flow must be dry-run-first, reviewed, and before-hash guarded before it rewrites derived Markdown.
311
+ 0.3.4-rc.0 does not implement `hadara evidence rebuild --json` or an execute mode. Future rebuild work must first define whether a difference is formatting regeneration, managed-section drift, or data inconsistency before reporting `wouldChange`. Any later write-capable rebuild flow must be dry-run-first, reviewed, and before-hash guarded before it rewrites derived Markdown.
304
312
 
305
313
  ## Development / Contributing
306
314
 
@@ -313,7 +321,7 @@ hadara init --profile standard
313
321
  hadara init --profile governed
314
322
  ```
315
323
 
316
- Every profile generates `docs/TASK_WORKFLOW_COMMANDS.md` so fresh projects get the current 0.3.3 finalize-first lifecycle loop plus low-level proof-boundary reference commands.
324
+ Every profile generates `docs/TASK_WORKFLOW_COMMANDS.md` so fresh projects get the current 0.3.4 context-aware finalize-first lifecycle loop plus low-level proof-boundary reference commands.
317
325
 
318
326
  | Profile | Use When |
319
327
  |---|---|
@@ -20,7 +20,6 @@ const dashboard_heavy_projection_1 = require("../services/dashboard-heavy-projec
20
20
  const evidence_lint_1 = require("../services/evidence-lint");
21
21
  const evidence_list_1 = require("../services/evidence-list");
22
22
  const dashboard_timeline_1 = require("../services/dashboard-timeline");
23
- const operational_debt_1 = require("../services/operational-debt");
24
23
  const operations_status_service_1 = require("../services/operations-status-service");
25
24
  const task_read_model_1 = require("../services/task-read-model");
26
25
  const task_workbench_1 = require("../services/task-workbench");
@@ -93,8 +92,11 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
93
92
  if (!url || !url.pathname.startsWith('/api/'))
94
93
  return null;
95
94
  const headOnly = normalizedMethod === 'HEAD';
96
- if (url.pathname === '/api/status')
97
- return jsonResponse((0, operations_status_service_1.createOpsStatusReport)(projectRoot), headOnly);
95
+ if (url.pathname === '/api/status') {
96
+ // Dashboard clients fetch operational debt separately; keep the status
97
+ // route cheap so a single slow debt scan cannot block first API paint.
98
+ return jsonResponse((0, operations_status_service_1.createOpsStatusReport)(projectRoot, { includeDebt: false }), headOnly);
99
+ }
98
100
  if (url.pathname === '/api/dashboard/core') {
99
101
  const bypassProjection = url.searchParams.get('cache') === 'bypass';
100
102
  const status = bypassProjection ? null : (0, dashboard_refresh_1.createDashboardProjectionStatusReport)(projectRoot);
@@ -116,7 +118,7 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
116
118
  return jsonResponse((0, dashboard_heavy_projection_1.createProjectedDashboardDebtReport)(projectRoot), headOnly);
117
119
  if (url.pathname === '/api/dashboard/bootstrap') {
118
120
  const selectedTaskId = url.searchParams.get('selectedTaskId')?.trim();
119
- const tier = url.searchParams.get('tier') === 'core' ? 'core' : 'full';
121
+ const tier = url.searchParams.get('tier') === 'full' ? 'full' : 'core';
120
122
  const keyParts = ['bootstrap'];
121
123
  if (tier === 'core')
122
124
  keyParts.push('core');
@@ -140,8 +142,13 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
140
142
  return jsonResponse((0, task_read_model_1.createTaskListReport)(projectRoot), headOnly);
141
143
  if (url.pathname === '/api/active-run')
142
144
  return jsonResponse((0, active_run_state_1.safeCreateActiveRunProjection)(projectRoot), headOnly);
143
- if (url.pathname === '/api/debt')
144
- return jsonResponse((0, operational_debt_1.createOperationalDebtReport)(projectRoot), headOnly);
145
+ if (url.pathname === '/api/debt') {
146
+ // Keep the legacy dashboard debt route on the same fast aggregate path as
147
+ // /api/dashboard/debt. The full operational-debt read model remains
148
+ // available through CLI/MCP surfaces; dashboard request handlers must not
149
+ // perform broad capsule scans on the hot API path.
150
+ return jsonResponse((0, dashboard_heavy_projection_1.createProjectedDashboardDebtReport)(projectRoot), headOnly);
151
+ }
145
152
  if (url.pathname === '/api/timeline') {
146
153
  const taskId = url.searchParams.get('taskId')?.trim();
147
154
  const key = taskId ? (0, dashboard_cache_1.createDashboardCacheKey)(projectRoot, 'timeline', taskId) : (0, dashboard_cache_1.createDashboardCacheKey)(projectRoot, 'timeline');
@@ -11,6 +11,7 @@ const evidence_json_1 = require("./evidence-json");
11
11
  const evidence_lint_1 = require("../services/evidence-lint");
12
12
  const evidence_list_1 = require("../services/evidence-list");
13
13
  const evidence_migration_1 = require("../services/evidence-migration");
14
+ const evidence_summary_1 = require("../services/evidence-summary");
14
15
  const args_1 = require("./args");
15
16
  function handleEvidenceCommand(input) {
16
17
  const sub = input.args[1];
@@ -52,6 +53,32 @@ function handleEvidenceCommand(input) {
52
53
  process.exitCode = 6;
53
54
  return true;
54
55
  }
56
+ if (sub === 'summary') {
57
+ const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
58
+ const report = (0, evidence_summary_1.createEvidenceSummaryReport)(input.projectRoot, {
59
+ taskId,
60
+ limit: (0, args_1.getIntegerOption)(input.args, '--limit', { min: 0, max: 500 }),
61
+ includePrivate: (0, args_1.getFlag)(input.args, '--include-private')
62
+ });
63
+ if (input.jsonOutput) {
64
+ console.log(JSON.stringify(report, null, 2));
65
+ }
66
+ else {
67
+ console.log(`[HADARA] evidence summary ${taskId}: ${report.ok ? 'ok' : 'issues'} | records ${report.summary.count} | durable ${report.summary.durableCount}`);
68
+ if (report.latest)
69
+ console.log(`latest: ${evidenceSummaryDisplay(report.latest)}`);
70
+ if (report.latestCloseEvidence)
71
+ console.log(`latest-close: ${evidenceSummaryDisplay(report.latestCloseEvidence)}`);
72
+ for (const record of report.records)
73
+ console.log(`- ${evidenceSummaryDisplay(record)}`);
74
+ for (const issue of report.issues) {
75
+ console.log(`[${issue.severity}] ${issue.code}: ${issue.message}`);
76
+ }
77
+ }
78
+ if (!report.ok)
79
+ process.exitCode = 6;
80
+ return true;
81
+ }
55
82
  if (sub === 'migrate') {
56
83
  const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
57
84
  const report = (0, evidence_migration_1.createEvidenceMigrationPreviewReport)({
@@ -175,6 +202,21 @@ function evidenceListCategory(record) {
175
202
  function evidenceListOutcome(record) {
176
203
  return record.outcome;
177
204
  }
205
+ function evidenceSummaryDisplay(record) {
206
+ const tags = record.tags.length > 0 ? ` | tags:${record.tags.slice(0, 3).map(compactEvidenceTag).join(',')}${record.tags.length > 3 ? ',...' : ''}` : '';
207
+ return `${record.id} | ${record.time} | ${record.category}/${record.outcome} | ${record.visibility} | ${truncateSummary(record.summary)}${tags}`;
208
+ }
209
+ function truncateSummary(value) {
210
+ const maxLength = 160;
211
+ if (value.length <= maxLength)
212
+ return value;
213
+ return `${value.slice(0, maxLength - 3)}...`;
214
+ }
215
+ function compactEvidenceTag(value) {
216
+ if (value.startsWith('idempotency:'))
217
+ return 'idempotency:*';
218
+ return truncateSummary(value);
219
+ }
178
220
  function parseEvidenceKind(value) {
179
221
  if (['test-log', 'command-log', 'diff-summary', 'screenshot', 'note'].includes(value)) {
180
222
  return value;
@@ -2,11 +2,24 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleHandoffCommand = handleHandoffCommand;
4
4
  const handoff_1 = require("../handoff/handoff");
5
+ const handoff_stale_problems_1 = require("../handoff/handoff-stale-problems");
5
6
  const handoff_suggestion_1 = require("../handoff/handoff-suggestion");
6
7
  const actor_1 = require("./actor");
7
8
  const args_1 = require("./args");
8
9
  function handleHandoffCommand(input) {
9
10
  const sub = input.args[1];
11
+ if (sub === 'stale-problems') {
12
+ const report = (0, handoff_stale_problems_1.createHandoffStaleProblemsReport)(input.projectRoot);
13
+ if (input.jsonOutput) {
14
+ console.log(JSON.stringify(report, null, 2));
15
+ }
16
+ else {
17
+ console.log((0, handoff_stale_problems_1.formatHandoffStaleProblemsReport)(report));
18
+ }
19
+ if (!report.ok)
20
+ process.exitCode = 6;
21
+ return true;
22
+ }
10
23
  if (sub === 'suggest') {
11
24
  const taskId = (0, args_1.getStringOption)(input.args, '--task') ?? input.args[2];
12
25
  if (!taskId || taskId.startsWith('--'))
package/dist/cli/init.js CHANGED
@@ -1104,17 +1104,19 @@ Ownership boundaries follow the lifecycle command model. \`task finalize --execu
1104
1104
 
1105
1105
  ## Standard Task Workflow Loop
1106
1106
 
1107
- The authoritative command semantics live in \`docs/TASK_WORKFLOW_COMMANDS.md\`. From 0.3.3 onward, agents should use this loop for ordinary implementation capsules:
1107
+ The authoritative command semantics live in \`docs/TASK_WORKFLOW_COMMANDS.md\`. From 0.3.4 onward, agents should start from a compact task/context packet, then use lifecycle/finalize for closure instead of starting with low-level proof-boundary commands:
1108
1108
 
1109
1109
  \`\`\`bash
1110
1110
  hadara task next --json
1111
1111
 
1112
1112
  # If a matching capsule already exists:
1113
1113
  hadara task status --task T-XXXX --json
1114
+ hadara session start --task T-XXXX --json
1114
1115
 
1115
1116
  # If no matching capsule exists, create one first:
1116
1117
  hadara task create "task title" --json
1117
1118
  hadara task status --task T-XXXX --json
1119
+ hadara session start --task T-XXXX --json
1118
1120
 
1119
1121
  # Do the scoped work.
1120
1122
 
@@ -1315,17 +1317,19 @@ HADARA task workflow commands are split by responsibility. Similar-looking comma
1315
1317
 
1316
1318
  ## Standard Task Loop
1317
1319
 
1318
- From 0.3.3 onward, agents should use the finalize-first loop for ordinary implementation capsules:
1320
+ From 0.3.4 onward, agents should use the context-aware finalize-first loop for ordinary implementation capsules:
1319
1321
 
1320
1322
  \`\`\`bash
1321
1323
  hadara task next --json
1322
1324
 
1323
1325
  # If a matching capsule already exists:
1324
1326
  hadara task status --task T-XXXX --json
1327
+ hadara session start --task T-XXXX --json
1325
1328
 
1326
1329
  # If no matching capsule exists, create one first:
1327
1330
  hadara task create "task title" --json
1328
1331
  hadara task status --task T-XXXX --json
1332
+ hadara session start --task T-XXXX --json
1329
1333
 
1330
1334
  # Do the scoped work.
1331
1335
 
@@ -1534,6 +1538,20 @@ Use semantic tiers to keep session startup compact:
1534
1538
 
1535
1539
  \`.hadara/context/HADARA_CONTEXT.md\` is the current-state entry point. It should route readers to compact state before task-work or conditional-reference docs. Full historical review of \`docs/PROJECT_STATE.md\` is not mandatory every session; use \`docs/AGENT_HANDOFF.md\` and its Historical Index when older history is needed. Historical and superseded docs are never default required reading.
1536
1540
 
1541
+ ## Default Agent Loop
1542
+
1543
+ For ordinary implementation work, use the current context-aware loop:
1544
+
1545
+ \`\`\`bash
1546
+ hadara task next --json
1547
+ hadara session start --task T-XXXX --json
1548
+ hadara task lifecycle --task T-XXXX --json
1549
+ hadara task finalize --task T-XXXX --json
1550
+ hadara task finalize --task T-XXXX --execute --plan-hash sha256:... --json
1551
+ \`\`\`
1552
+
1553
+ Use low-level \`task finish\`, \`task ready\`, \`task close\`, and \`task audit-close\` only when debugging, repairing, or implementing those proof-boundary commands.
1554
+
1537
1555
  ## Rules
1538
1556
 
1539
1557
  | Rule | Requirement | Evidence / Update Location |
package/dist/cli/main.js CHANGED
@@ -222,6 +222,9 @@ async function main(args = process.argv.slice(2)) {
222
222
  break;
223
223
  }
224
224
  case 'release': {
225
+ const { handleReleaseCloseoutCommand } = await Promise.resolve().then(() => __importStar(require('./release-closeout')));
226
+ if (handleReleaseCloseoutCommand({ args, projectRoot: paths.projectRoot, jsonOutput }))
227
+ return;
225
228
  const { handleReleaseDryRunCommand } = await Promise.resolve().then(() => __importStar(require('./release-dry-run')));
226
229
  if (handleReleaseDryRunCommand({ args, projectRoot: paths.projectRoot, jsonOutput }))
227
230
  return;
@@ -1,10 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handlePackageCommand = handlePackageCommand;
4
+ const package_recycle_1 = require("../services/package-recycle");
4
5
  const package_smoke_1 = require("../services/package-smoke");
5
6
  const args_1 = require("./args");
6
7
  function handlePackageCommand(input) {
7
- if (input.args[0] !== 'package' || input.args[1] !== 'smoke')
8
+ if (input.args[0] !== 'package')
9
+ return false;
10
+ if (input.args[1] === 'recycle') {
11
+ const report = (0, package_recycle_1.createPackageRecycleReport)({
12
+ paths: input.paths,
13
+ execute: (0, args_1.getFlag)(input.args, '--execute'),
14
+ packageSpecifier: (0, args_1.getStringOption)(input.args, '--package'),
15
+ expectedVersion: (0, args_1.getStringOption)(input.args, '--expected-version'),
16
+ workspace: (0, args_1.getStringOption)(input.args, '--workspace'),
17
+ taskId: (0, args_1.getStringOption)(input.args, '--task'),
18
+ attachEvidence: (0, args_1.getFlag)(input.args, '--attach-evidence'),
19
+ noEvidence: (0, args_1.getFlag)(input.args, '--no-evidence'),
20
+ keepTemp: (0, args_1.getFlag)(input.args, '--keep-temp'),
21
+ timeoutSeconds: (0, args_1.getIntegerOption)(input.args, '--timeout', { min: 1 })
22
+ });
23
+ if (input.jsonOutput) {
24
+ console.log(JSON.stringify(report, null, 2));
25
+ }
26
+ else {
27
+ console.log(`${report.ok ? 'passed' : 'failed'} | package recycle | ${report.mode}`);
28
+ for (const step of report.steps) {
29
+ console.log(`${step.status} | ${step.label} | ${step.summary}`);
30
+ }
31
+ for (const issue of report.issues) {
32
+ console.log(`${issue.severity} | ${issue.code} | ${issue.message}`);
33
+ }
34
+ }
35
+ if (!report.ok)
36
+ process.exitCode = 6;
37
+ return true;
38
+ }
39
+ if (input.args[1] !== 'smoke')
8
40
  return false;
9
41
  const options = {
10
42
  paths: input.paths,
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleReleaseCloseoutCommand = handleReleaseCloseoutCommand;
4
+ const release_closeout_1 = require("../services/release-closeout");
5
+ const args_1 = require("./args");
6
+ function handleReleaseCloseoutCommand(input) {
7
+ if (input.args[0] !== 'release' || input.args[1] !== 'closeout')
8
+ return false;
9
+ const report = (0, release_closeout_1.createReleaseCloseoutReport)(input.projectRoot, {
10
+ version: (0, args_1.getStringOption)(input.args, '--version'),
11
+ taskId: (0, args_1.getStringOption)(input.args, '--task')
12
+ });
13
+ if (input.jsonOutput) {
14
+ console.log(JSON.stringify(report, null, 2));
15
+ }
16
+ else {
17
+ console.log((0, release_closeout_1.formatReleaseCloseoutReport)(report));
18
+ }
19
+ if (!report.ok)
20
+ process.exitCode = 6;
21
+ return true;
22
+ }
@@ -108,6 +108,10 @@ function buildContextPackReport(input) {
108
108
  score: 0
109
109
  }, false, input.projectRoot))
110
110
  .sort(compareItems);
111
+ const validateWith = validationSuggestionsForTask(taskId, graphReport);
112
+ const writeBoundaries = writeBoundariesForItems([...readFirst, ...readIfNeeded], graphReport.nodes);
113
+ const sliceCandidates = sliceCandidatesForItems([...readFirst, ...readIfNeeded], graphReport.nodes);
114
+ const agentActions = agentActionsForContextPack(readFirst, sliceCandidates, validateWith, taskId);
111
115
  const cache = input.cache ?? graphReport.cache ?? { used: false, hit: false };
112
116
  return {
113
117
  schemaVersion: exports.CONTEXT_PACK_SCHEMA_ID,
@@ -120,9 +124,10 @@ function buildContextPackReport(input) {
120
124
  readFirst,
121
125
  readIfNeeded,
122
126
  doNotReadByDefault,
123
- validateWith: validationSuggestionsForTask(taskId, graphReport),
124
- writeBoundaries: writeBoundariesForItems([...readFirst, ...readIfNeeded], graphReport.nodes),
125
- sliceCandidates: sliceCandidatesForItems([...readFirst, ...readIfNeeded], graphReport.nodes),
127
+ validateWith,
128
+ writeBoundaries,
129
+ sliceCandidates,
130
+ agentActions,
126
131
  knownProblems,
127
132
  stateProjection: {
128
133
  ...graphReport.stateProjection.summary,
@@ -159,7 +164,7 @@ function rankContextPackNodes(nodes, edges, connectedIds, taskNode) {
159
164
  if (taskNode) {
160
165
  ranked.set(taskNode.id, {
161
166
  node: taskNode,
162
- reason: 'Active task capsule is the first read for task-scoped context routing.',
167
+ reason: 'Read the active task capsule first; it defines scope, acceptance, test expectations, and close handoff for this task.',
163
168
  confidence: 'explicit',
164
169
  score: 1000
165
170
  });
@@ -174,7 +179,7 @@ function rankContextPackNodes(nodes, edges, connectedIds, taskNode) {
174
179
  continue;
175
180
  upsertRankedNode(ranked, {
176
181
  node,
177
- reason: edge.reason,
182
+ reason: concreteRankReason(node, edge.reason),
178
183
  confidence: edge.confidence,
179
184
  score: nodeScore(node, edge.confidence, connectedIds.has(node.id))
180
185
  });
@@ -186,7 +191,7 @@ function rankContextPackNodes(nodes, edges, connectedIds, taskNode) {
186
191
  if (metadataBoolean(node, 'requiredReading')) {
187
192
  upsertRankedNode(ranked, {
188
193
  node,
189
- reason: 'Document registry marks this document as required reading.',
194
+ reason: 'Required current-state document from the docs registry; read after task-local context if the task needs this surface.',
190
195
  confidence: 'explicit',
191
196
  score: nodeScore(node, 'explicit', false) - 25
192
197
  });
@@ -203,6 +208,8 @@ function upsertRankedNode(ranked, candidate) {
203
208
  function nodeScore(node, confidence, connected) {
204
209
  const confidenceScore = confidence === 'explicit' ? 80 : confidence === 'derived' ? 50 : 20;
205
210
  const connectedScore = connected ? 25 : 0;
211
+ const taskLocalScore = node.path?.startsWith('tasks/') ? 140 : 0;
212
+ const sourceScore = ['SourceFile', 'TestFile', 'Symbol'].includes(node.type) ? 40 : 0;
206
213
  const typeScore = {
207
214
  Task: 1000,
208
215
  Document: 700,
@@ -218,7 +225,28 @@ function nodeScore(node, confidence, connected) {
218
225
  FixtureFile: 380,
219
226
  ConfigFile: 360
220
227
  };
221
- return typeScore[node.type] + confidenceScore + connectedScore;
228
+ return typeScore[node.type] + confidenceScore + connectedScore + taskLocalScore + sourceScore;
229
+ }
230
+ function concreteRankReason(node, sourceReason) {
231
+ if (node.path?.startsWith('tasks/')) {
232
+ return `Task-local file connected to the active task; read it before broad project history. ${sourceReason}`;
233
+ }
234
+ if (node.type === 'SourceFile') {
235
+ return `Implementation file connected to the active task; inspect it when changing code. ${sourceReason}`;
236
+ }
237
+ if (node.type === 'TestFile') {
238
+ return `Test file connected to the active task; inspect it when planning or verifying changes. ${sourceReason}`;
239
+ }
240
+ if (node.type === 'Symbol') {
241
+ return `Specific symbol connected to the active task; prefer this bounded location over opening the full file. ${sourceReason}`;
242
+ }
243
+ if (node.type === 'KnownProblem') {
244
+ return `Current handoff problem relevant to this task; check whether it changes implementation or validation. ${sourceReason}`;
245
+ }
246
+ if (node.type === 'Command') {
247
+ return `Command surface connected to this task; use it to choose dry-run/read-only validation before mutation. ${sourceReason}`;
248
+ }
249
+ return sourceReason;
222
250
  }
223
251
  function isReadFirstAllowed(node) {
224
252
  if (isDoNotReadByDefault(node))
@@ -388,6 +416,62 @@ function sliceCandidatesForItems(items, nodes) {
388
416
  };
389
417
  });
390
418
  }
419
+ function agentActionsForContextPack(readFirst, sliceCandidates, validateWith, taskId) {
420
+ const actions = [];
421
+ const seenCommands = new Set();
422
+ const firstSliceableItem = readFirst.find((item) => item.path && item.sourceAccess?.rawSlice === 'sliceable');
423
+ if (firstSliceableItem?.path) {
424
+ const args = suggestedSliceCommandArgs(firstSliceableItem.path, 'explicit-range', undefined, {
425
+ lineStart: firstSliceableItem.lineStart ?? 1,
426
+ lineEnd: firstSliceableItem.lineEnd ?? Math.max(firstSliceableItem.lineStart ?? 1, (firstSliceableItem.lineStart ?? 1) + 80)
427
+ });
428
+ pushAgentAction(actions, seenCommands, {
429
+ id: 'agent-action:read-first:1',
430
+ kind: 'read-first',
431
+ priority: 100,
432
+ sourceItemId: firstSliceableItem.id,
433
+ path: firstSliceableItem.path,
434
+ reason: `Read the top context item ${firstSliceableItem.id} as a bounded raw slice before opening broader files.`,
435
+ command: suggestedSliceCommand(args),
436
+ commandArgs: args,
437
+ writeBoundary: 'read-only'
438
+ });
439
+ }
440
+ for (const [index, candidate] of sliceCandidates.slice(0, 3).entries()) {
441
+ pushAgentAction(actions, seenCommands, {
442
+ id: `agent-action:slice:${index + 1}`,
443
+ kind: 'slice',
444
+ priority: 90 - index,
445
+ sliceCandidateId: candidate.id,
446
+ path: candidate.path,
447
+ reason: `Use this bounded slice candidate before reading the full file: ${candidate.reason}`,
448
+ command: candidate.suggestedCommand,
449
+ commandArgs: candidate.suggestedCommandArgs,
450
+ writeBoundary: 'read-only'
451
+ });
452
+ }
453
+ const requiredValidation = validateWith.find((suggestion) => suggestion.requiredForClose);
454
+ if (requiredValidation) {
455
+ pushAgentAction(actions, seenCommands, {
456
+ id: 'agent-action:validate:required-close',
457
+ kind: 'validate',
458
+ priority: 50,
459
+ reason: taskId
460
+ ? `Run this read-only readiness check before planning close for ${taskId}.`
461
+ : 'Run this read-only readiness check before planning close.',
462
+ command: requiredValidation.command,
463
+ writeBoundary: 'read-only'
464
+ });
465
+ }
466
+ return actions.sort((a, b) => b.priority - a.priority || a.id.localeCompare(b.id));
467
+ }
468
+ function pushAgentAction(actions, seenCommands, action) {
469
+ const key = action.commandArgs ? action.commandArgs.join('\0') : action.command;
470
+ if (seenCommands.has(key))
471
+ return;
472
+ seenCommands.add(key);
473
+ actions.push(action);
474
+ }
391
475
  function explicitRangeForCandidate(item, node) {
392
476
  const lineStart = item.lineStart ?? numberMetadata(node, 'startLine') ?? node?.source.line ?? 1;
393
477
  const metadataEnd = numberMetadata(node, 'endLine');
@@ -134,6 +134,7 @@ function buildWarmCachedContextPackReport(input) {
134
134
  function lifecycleForSessionStart(taskId, contextPack) {
135
135
  const primaryNextCommands = taskId
136
136
  ? [
137
+ `node dist/cli/main.js task lifecycle --task ${taskId} --json`,
137
138
  `node dist/cli/main.js task status --task ${taskId} --json`,
138
139
  `node dist/cli/main.js context pack --task ${taskId} --json`
139
140
  ]
@@ -175,7 +176,17 @@ function guidanceForSessionStart(input) {
175
176
  ? 'Session Start used explicit live context-pack discovery because --live was supplied.'
176
177
  : 'Session Start used the bounded no-live packet and avoided broad live graph discovery.';
177
178
  const commands = [];
179
+ let primaryAction;
178
180
  if (!taskId) {
181
+ primaryAction = {
182
+ id: 'task-next',
183
+ label: 'Select the next task',
184
+ command: 'node dist/cli/main.js task next --json',
185
+ args: ['task', 'next', '--json'],
186
+ reason: 'No task id is available, so the next useful step is to select a concrete task before reading task-scoped context.',
187
+ writeBoundary: 'read-only',
188
+ recommendedActorRole: 'agent-worker'
189
+ };
179
190
  commands.push({
180
191
  id: 'task-next',
181
192
  command: 'node dist/cli/main.js task next --json',
@@ -184,6 +195,21 @@ function guidanceForSessionStart(input) {
184
195
  });
185
196
  }
186
197
  else {
198
+ primaryAction = {
199
+ id: 'task-lifecycle',
200
+ label: 'Inspect task lifecycle phase',
201
+ command: `node dist/cli/main.js task lifecycle --task ${taskId} --json`,
202
+ args: ['task', 'lifecycle', '--task', taskId, '--json'],
203
+ reason: 'A task id is available, so the fastest safe first step is to inspect lifecycle phase and blockers before editing files.',
204
+ writeBoundary: 'read-only',
205
+ recommendedActorRole: 'agent-worker'
206
+ };
207
+ commands.push({
208
+ id: 'task-lifecycle',
209
+ command: `node dist/cli/main.js task lifecycle --task ${taskId} --json`,
210
+ args: ['task', 'lifecycle', '--task', taskId, '--json'],
211
+ reason: 'Inspect the task phase and the one primary lifecycle next action before editing.'
212
+ });
187
213
  commands.push({
188
214
  id: 'task-status',
189
215
  command: `node dist/cli/main.js task status --task ${taskId} --json`,
@@ -216,6 +242,18 @@ function guidanceForSessionStart(input) {
216
242
  return {
217
243
  mode,
218
244
  primaryNextAction,
245
+ primaryAction,
246
+ whyThisNow: primaryAction.reason,
247
+ avoidForNow: taskId
248
+ ? [
249
+ 'Do not run task finalize before reviewing lifecycle blockers and required reads.',
250
+ 'Do not opt into --live context reads unless the bounded or warm packet is insufficient.'
251
+ ]
252
+ : [
253
+ 'Do not infer a task id from broad project files.',
254
+ 'Do not run live context discovery before selecting a task.'
255
+ ],
256
+ nextCommandArgs: primaryAction.args,
219
257
  reason,
220
258
  taskRequired,
221
259
  liveContextPackAvailable: true,
@@ -273,6 +311,7 @@ function buildBoundedContextPackReport(input) {
273
311
  : [],
274
312
  writeBoundaries: [],
275
313
  sliceCandidates: [],
314
+ agentActions: [],
276
315
  knownProblems: [],
277
316
  stateProjection: {
278
317
  ...(input.taskId ? { activeTask: input.taskId } : {}),
@@ -36,12 +36,15 @@ const docs_required_reading_schema_json_1 = __importDefault(require("../schemas/
36
36
  const evidence_lint_schema_json_1 = __importDefault(require("../schemas/evidence-lint.schema.json"));
37
37
  const evidence_list_schema_json_1 = __importDefault(require("../schemas/evidence-list.schema.json"));
38
38
  const evidence_migration_preview_schema_json_1 = __importDefault(require("../schemas/evidence-migration-preview.schema.json"));
39
+ const evidence_summary_schema_json_1 = __importDefault(require("../schemas/evidence-summary.schema.json"));
39
40
  const event_schema_json_1 = __importDefault(require("../schemas/event.schema.json"));
40
41
  const feature_smoke_schema_json_1 = __importDefault(require("../schemas/feature-smoke.schema.json"));
41
42
  const harness_validate_schema_json_1 = __importDefault(require("../schemas/harness-validate.schema.json"));
43
+ const handoff_stale_problems_schema_json_1 = __importDefault(require("../schemas/handoff-stale-problems.schema.json"));
42
44
  const handoff_suggestion_schema_json_1 = __importDefault(require("../schemas/handoff-suggestion.schema.json"));
43
45
  const install_plan_schema_json_1 = __importDefault(require("../schemas/install-plan.schema.json"));
44
46
  const next_action_schema_json_1 = __importDefault(require("../schemas/next-action.schema.json"));
47
+ const package_recycle_schema_json_1 = __importDefault(require("../schemas/package-recycle.schema.json"));
45
48
  const package_smoke_schema_json_1 = __importDefault(require("../schemas/package-smoke.schema.json"));
46
49
  const plan_context_schema_json_1 = __importDefault(require("../schemas/plan-context.schema.json"));
47
50
  const private_evidence_schema_json_1 = __importDefault(require("../schemas/private-evidence.schema.json"));
@@ -52,6 +55,7 @@ const provider_call_schema_json_1 = __importDefault(require("../schemas/provider
52
55
  const provider_config_schema_json_1 = __importDefault(require("../schemas/provider-config.schema.json"));
53
56
  const release_artifact_manifest_schema_json_1 = __importDefault(require("../schemas/release-artifact-manifest.schema.json"));
54
57
  const release_artifact_schema_json_1 = __importDefault(require("../schemas/release-artifact.schema.json"));
58
+ const release_closeout_schema_json_1 = __importDefault(require("../schemas/release-closeout.schema.json"));
55
59
  const release_dry_run_schema_json_1 = __importDefault(require("../schemas/release-dry-run.schema.json"));
56
60
  const release_gate_schema_json_1 = __importDefault(require("../schemas/release-gate.schema.json"));
57
61
  const release_publish_schema_json_1 = __importDefault(require("../schemas/release-publish.schema.json"));
@@ -116,12 +120,15 @@ const registeredSchemas = {
116
120
  'hadara.evidence.lint.v1': evidence_lint_schema_json_1.default,
117
121
  'hadara.evidence.list.v1': evidence_list_schema_json_1.default,
118
122
  'hadara.evidence.migration_preview.v1': evidence_migration_preview_schema_json_1.default,
123
+ 'hadara.evidence.summary.v1': evidence_summary_schema_json_1.default,
119
124
  'hadara.event.v1': event_schema_json_1.default,
120
125
  'hadara.featureSmoke.v1': feature_smoke_schema_json_1.default,
121
126
  'hadara.harness.validate.v1': harness_validate_schema_json_1.default,
127
+ 'hadara.handoff.staleProblems.v1': handoff_stale_problems_schema_json_1.default,
122
128
  'hadara.handoff.suggestion.v1': handoff_suggestion_schema_json_1.default,
123
129
  'hadara.install.plan.v1': install_plan_schema_json_1.default,
124
130
  'hadara.next_action.v1': next_action_schema_json_1.default,
131
+ 'hadara.packageRecycle.v1': package_recycle_schema_json_1.default,
125
132
  'hadara.packageSmoke.v1': package_smoke_schema_json_1.default,
126
133
  'hadara.plan_context.v1': plan_context_schema_json_1.default,
127
134
  'hadara.privateEvidence.v1': private_evidence_schema_json_1.default,
@@ -132,6 +139,7 @@ const registeredSchemas = {
132
139
  'hadara.provider.config.v1': provider_config_schema_json_1.default,
133
140
  'hadara.releaseArtifact.manifest.v1': release_artifact_manifest_schema_json_1.default,
134
141
  'hadara.releaseArtifact.v1': release_artifact_schema_json_1.default,
142
+ 'hadara.releaseCloseout.v1': release_closeout_schema_json_1.default,
135
143
  'hadara.releaseDryRun.v1': release_dry_run_schema_json_1.default,
136
144
  'hadara.releaseGate.v1': release_gate_schema_json_1.default,
137
145
  'hadara.releasePublish.v1': release_publish_schema_json_1.default,