hadara 0.3.0-rc.0 → 0.3.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
- <img alt="Release candidate" src="https://img.shields.io/badge/npm-0.3.0--rc.0-blue">
8
+ <img alt="Published npm release" src="https://img.shields.io/badge/npm-0.3.0--rc.0-blue">
9
9
  <img alt="Node.js" src="https://img.shields.io/badge/node-%3E%3D22-brightgreen">
10
10
  <img alt="License" src="https://img.shields.io/badge/license-MIT-lightgrey">
11
11
  </p>
@@ -20,7 +20,13 @@ This repository is both the HADARA source checkout and the HADARA protocol works
20
20
 
21
21
  ## Release Status
22
22
 
23
- Current release candidate:
23
+ Current release candidate prepared for operator publish:
24
+
25
+ ```text
26
+ hadara@0.3.0-rc.1
27
+ ```
28
+
29
+ Current published npm release:
24
30
 
25
31
  ```text
26
32
  hadara@0.3.0-rc.0
@@ -28,7 +34,7 @@ hadara@0.3.0-rc.0
28
34
 
29
35
  The 0.3.0 line is the Phase 7 Surface Refactor. It organizes HADARA's existing task, evidence, proof, lifecycle, release, and document surfaces so agents can distinguish primary lifecycle commands, diagnostics, advanced surfaces, canonical documents, historical documents, and safe Markdown update boundaries.
30
36
 
31
- Phase 7.x labels are internal implementation phases, not npm release-candidate labels. Publishing `0.3.0-rc.0` or any later release still requires the approval-gated release path and explicit operator confirmation.
37
+ Phase 7.x labels are internal implementation phases, not npm release-candidate labels. Publishing `0.3.0-rc.1` or any later release still requires the approval-gated release path and explicit operator confirmation.
32
38
 
33
39
  | Surface | Status |
34
40
  |---|---|
@@ -36,7 +42,8 @@ Phase 7.x labels are internal implementation phases, not npm release-candidate l
36
42
  | `hadara@0.2.0-rc.1` | Previous published npm RC. |
37
43
  | `hadara@0.2.0-rc.2` | Previous published npm RC. |
38
44
  | `hadara@0.2.0-rc.3` | Previous published npm RC. |
39
- | `hadara@0.3.0-rc.0` | Current release candidate. |
45
+ | `hadara@0.3.0-rc.0` | Current published npm RC; package metadata lacks the intended discovery fields. |
46
+ | `hadara@0.3.0-rc.1` | Current publish candidate; T-0301 prepares the operator-confirmed npm publish path. |
40
47
  | GitHub Release | Secondary target, approval-gated. |
41
48
  | Docker image | Deferred. |
42
49
  | PyPI/Python package | `hadara==0.2.0rc1` published preview bridge. |
@@ -48,7 +55,7 @@ No release command should publish, create a GitHub Release, build Docker images,
48
55
 
49
56
  Requires Node.js 22.
50
57
 
51
- Install this release candidate:
58
+ Install the latest published RC before the T-0301 publish step:
52
59
 
53
60
  ```bash
54
61
  npm install -g hadara@0.3.0-rc.0
@@ -63,6 +70,14 @@ npx hadara@0.3.0-rc.0 help
63
70
  npx hadara@0.3.0-rc.0 doctor --json
64
71
  ```
65
72
 
73
+ After the T-0301 npm publish helper verifies `hadara@0.3.0-rc.1` on the registry, install the new RC:
74
+
75
+ ```bash
76
+ npm install -g hadara@0.3.0-rc.1
77
+ hadara help
78
+ hadara doctor --json
79
+ ```
80
+
66
81
  ## What HADARA Gives You
67
82
 
68
83
  | Capability | Purpose |
@@ -2,10 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleProtocolCommand = handleProtocolCommand;
4
4
  const protocol_consistency_1 = require("../services/protocol-consistency");
5
+ const protocol_migration_1 = require("../services/protocol-migration");
5
6
  const protocol_remediation_1 = require("../services/protocol-remediation");
6
7
  const args_1 = require("./args");
7
8
  function handleProtocolCommand(input) {
8
9
  const sub = input.args[1];
10
+ if (sub === 'migrate')
11
+ return handleProtocolMigrateCommand(input);
9
12
  if (sub === 'remediate')
10
13
  return handleProtocolRemediateCommand(input);
11
14
  if (sub !== 'doctor')
@@ -62,6 +65,40 @@ function handleProtocolCommand(input) {
62
65
  process.exitCode = 6;
63
66
  return true;
64
67
  }
68
+ function handleProtocolMigrateCommand(input) {
69
+ const target = (0, args_1.getStringOption)(input.args, '--target', '0.3.0') ?? '0.3.0';
70
+ if (target !== '0.3.0')
71
+ throw new args_1.CliArgsError('CLI_OPTION_INVALID_VALUE', `unsupported protocol migration target: ${target}`);
72
+ const profile = (0, args_1.getStringOption)(input.args, '--profile');
73
+ if (profile !== undefined && profile !== 'basic' && profile !== 'standard' && profile !== 'governed' && profile !== 'hadara-dev') {
74
+ throw new args_1.CliArgsError('CLI_OPTION_INVALID_VALUE', `unsupported HADARA profile: ${profile}`);
75
+ }
76
+ const report = (0, protocol_migration_1.createProtocolMigrationReport)({
77
+ projectRoot: input.projectRoot,
78
+ target,
79
+ mode: (0, args_1.getFlag)(input.args, '--execute') ? 'execute' : 'dry-run',
80
+ beforeHash: (0, args_1.getStringOption)(input.args, '--before-hash'),
81
+ taskId: (0, args_1.getStringOption)(input.args, '--task'),
82
+ profile
83
+ });
84
+ if (input.jsonOutput) {
85
+ console.log(JSON.stringify(report, null, 2));
86
+ }
87
+ else if (report.ok) {
88
+ console.log(`[HADARA] Protocol migration ${report.mode}: ${target}`);
89
+ for (const action of report.actions) {
90
+ console.log(`- ${action.status}: ${action.summary}${action.path ? ` (${action.path})` : ''}`);
91
+ }
92
+ }
93
+ else {
94
+ console.log(`[HADARA] Protocol migration failed: ${target}`);
95
+ for (const issue of report.issues)
96
+ console.log(`- ${issue.code}: ${issue.message}${issue.path ? ` (${issue.path})` : ''}`);
97
+ }
98
+ if (!report.ok)
99
+ process.exitCode = 6;
100
+ return true;
101
+ }
65
102
  function handleProtocolRemediateCommand(input) {
66
103
  const fix = (0, args_1.getStringOption)(input.args, '--fix');
67
104
  if (!fix)
@@ -37,6 +37,7 @@ const package_smoke_schema_json_1 = __importDefault(require("../schemas/package-
37
37
  const plan_context_schema_json_1 = __importDefault(require("../schemas/plan-context.schema.json"));
38
38
  const private_evidence_schema_json_1 = __importDefault(require("../schemas/private-evidence.schema.json"));
39
39
  const protocol_consistency_schema_json_1 = __importDefault(require("../schemas/protocol-consistency.schema.json"));
40
+ const protocol_migration_schema_json_1 = __importDefault(require("../schemas/protocol-migration.schema.json"));
40
41
  const protocol_remediation_schema_json_1 = __importDefault(require("../schemas/protocol-remediation.schema.json"));
41
42
  const provider_call_schema_json_1 = __importDefault(require("../schemas/provider-call.schema.json"));
42
43
  const provider_config_schema_json_1 = __importDefault(require("../schemas/provider-config.schema.json"));
@@ -101,6 +102,7 @@ const registeredSchemas = {
101
102
  'hadara.plan_context.v1': plan_context_schema_json_1.default,
102
103
  'hadara.privateEvidence.v1': private_evidence_schema_json_1.default,
103
104
  'hadara.protocol.consistency.v1': protocol_consistency_schema_json_1.default,
105
+ 'hadara.protocol.migration.v1': protocol_migration_schema_json_1.default,
104
106
  'hadara.protocol.remediation.v1': protocol_remediation_schema_json_1.default,
105
107
  'hadara.provider.call.v1': provider_call_schema_json_1.default,
106
108
  'hadara.provider.config.v1': provider_config_schema_json_1.default,
@@ -0,0 +1,111 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "hadara.protocol.migration.v1",
4
+ "x-hadara-schema-id": "hadara.protocol.migration.v1",
5
+ "title": "HADARA Protocol Migration Report",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": ["schemaVersion", "command", "ok", "mode", "target", "scope", "projectRoot", "detection", "summary", "actions", "issues"],
9
+ "properties": {
10
+ "schemaVersion": { "const": "hadara.protocol.migration.v1" },
11
+ "command": { "const": "protocol.migrate" },
12
+ "ok": { "type": "boolean" },
13
+ "mode": { "type": "string", "enum": ["dry-run", "execute"] },
14
+ "target": {
15
+ "type": "object",
16
+ "additionalProperties": true,
17
+ "required": ["protocolVersion"],
18
+ "properties": {
19
+ "protocolVersion": { "const": "0.3.0" }
20
+ }
21
+ },
22
+ "scope": {
23
+ "type": "object",
24
+ "additionalProperties": true,
25
+ "required": ["kind", "taskId"],
26
+ "properties": {
27
+ "kind": { "type": "string", "enum": ["project", "task"] },
28
+ "taskId": {
29
+ "anyOf": [
30
+ { "type": "string", "pattern": "^T-[0-9]{4,}$" },
31
+ { "type": "null" }
32
+ ]
33
+ }
34
+ }
35
+ },
36
+ "projectRoot": { "type": "string", "minLength": 1 },
37
+ "detection": {
38
+ "type": "object",
39
+ "additionalProperties": true,
40
+ "required": ["scaffoldGeneration", "profile", "docsRegistryPresent", "docsRegistryValid", "commandSurfaceDocPresent", "requiredReadingManaged", "managedSectionsPresent", "taskCapsulePresent"],
41
+ "properties": {
42
+ "scaffoldGeneration": { "type": "string", "enum": ["pre-0.3", "0.3", "partial-0.3", "unknown"] },
43
+ "profile": { "type": "string", "enum": ["basic", "standard", "governed", "hadara-dev"] },
44
+ "docsRegistryPresent": { "type": "boolean" },
45
+ "docsRegistryValid": { "type": "boolean" },
46
+ "commandSurfaceDocPresent": { "type": "boolean" },
47
+ "requiredReadingManaged": { "type": "boolean" },
48
+ "managedSectionsPresent": { "type": "boolean" },
49
+ "taskCapsulePresent": {
50
+ "anyOf": [
51
+ { "type": "boolean" },
52
+ { "type": "null" }
53
+ ]
54
+ }
55
+ }
56
+ },
57
+ "summary": {
58
+ "type": "object",
59
+ "additionalProperties": true,
60
+ "required": ["planned", "changed", "skipped", "beforeHash"],
61
+ "properties": {
62
+ "planned": { "type": "integer" },
63
+ "changed": { "type": "integer" },
64
+ "skipped": { "type": "integer" },
65
+ "beforeHash": {
66
+ "anyOf": [
67
+ { "type": "string", "pattern": "^[a-f0-9]{64}$" },
68
+ { "type": "null" }
69
+ ]
70
+ }
71
+ }
72
+ },
73
+ "actions": {
74
+ "type": "array",
75
+ "items": { "$ref": "#/$defs/action" }
76
+ },
77
+ "issues": {
78
+ "type": "array",
79
+ "items": { "$ref": "#/$defs/issue" }
80
+ }
81
+ },
82
+ "$defs": {
83
+ "action": {
84
+ "type": "object",
85
+ "additionalProperties": true,
86
+ "required": ["id", "path", "status", "summary"],
87
+ "properties": {
88
+ "id": { "type": "string", "minLength": 1 },
89
+ "path": { "type": "string", "minLength": 1 },
90
+ "status": { "type": "string", "enum": ["planned", "created", "updated", "skipped"] },
91
+ "summary": { "type": "string", "minLength": 1 },
92
+ "before": { "type": "string" },
93
+ "after": { "type": "string" },
94
+ "expectedBeforeExists": { "type": "boolean" },
95
+ "expectedBeforeHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
96
+ "afterHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
97
+ }
98
+ },
99
+ "issue": {
100
+ "type": "object",
101
+ "additionalProperties": true,
102
+ "required": ["severity", "code", "message"],
103
+ "properties": {
104
+ "severity": { "type": "string", "enum": ["error", "warning", "info"] },
105
+ "code": { "type": "string", "minLength": 1, "pattern": "^[A-Z0-9_:-]+$" },
106
+ "message": { "type": "string", "minLength": 1 },
107
+ "path": { "type": "string", "minLength": 1 }
108
+ }
109
+ }
110
+ }
111
+ }
@@ -197,6 +197,13 @@
197
197
  "owner": "protocol/remediation",
198
198
  "notes": "Documents dry-run-first protocol remediation plans, report-level before-hash guards, and bounded execute reports."
199
199
  },
200
+ {
201
+ "id": "hadara.protocol.migration.v1",
202
+ "path": "src/schemas/protocol-migration.schema.json",
203
+ "status": "fixture",
204
+ "owner": "protocol/migration",
205
+ "notes": "Documents dry-run-first 0.3 protocol migration plans for existing project and selected Task Capsule scopes."
206
+ },
200
207
  {
201
208
  "id": "hadara.task.upgrade_scaffold.v1",
202
209
  "path": "src/schemas/task-upgrade-scaffold.schema.json",
@@ -701,6 +701,31 @@ exports.HADARA_COMMAND_REGISTRY = [
701
701
  related: ['protocol.doctor', 'task.upgrade-scaffold'],
702
702
  conflictsWith: []
703
703
  }),
704
+ commandEntry({
705
+ id: 'protocol.migrate',
706
+ command: 'hadara protocol migrate --target 0.3.0 [--task <task-id>] [--profile basic|standard|governed|hadara-dev] [--execute --before-hash <hash>] [--json]',
707
+ summary: 'Preview or apply the 0.3 protocol migration for existing HADARA projects or selected Task Capsules.',
708
+ canonical: true,
709
+ appearsInDefaultHelp: false,
710
+ family: 'docs-governance',
711
+ scope: 'project',
712
+ lifecycleStage: 'work',
713
+ requiredness: 'conditional',
714
+ writeBoundary: 'shared-doc-write',
715
+ readOnly: false,
716
+ risk: 'medium',
717
+ actor: 'operator',
718
+ status: 'stable',
719
+ schemaVersion: 'hadara.protocol.migration.v1',
720
+ since: '0.3.0-rc.1',
721
+ docs: ['docs/specs/0.3.0/rc1/00_Protocol_Migration_for_0_3_Adoption.md', 'docs/IMPLEMENTATION_SOP.md'],
722
+ examples: [
723
+ example('Preview project migration', 'hadara protocol migrate --target 0.3.0 --json', 'When upgrading a pre-0.3 initialized HADARA project.'),
724
+ example('Preview task migration', 'hadara protocol migrate --target 0.3.0 --task T-0001 --json', 'When migrating one selected older Task Capsule.')
725
+ ],
726
+ related: ['protocol.doctor', 'docs.doctor', 'docs.managed.list', 'init.upgrade'],
727
+ conflictsWith: []
728
+ }),
704
729
  commandEntry({
705
730
  id: 'docs.list',
706
731
  command: 'hadara docs list [--status <status>] [--read-when <read-when>] [--json]',
@@ -0,0 +1,396 @@
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.createProtocolMigrationReport = createProtocolMigrationReport;
7
+ const node_crypto_1 = __importDefault(require("node:crypto"));
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const task_capsule_1 = require("../task/task-capsule");
11
+ const capability_registry_1 = require("./capability-registry");
12
+ const docs_registry_1 = require("./docs-registry");
13
+ const managed_sections_1 = require("./managed-sections");
14
+ function createProtocolMigrationReport(input) {
15
+ const issues = [];
16
+ const actions = [];
17
+ const profile = input.profile ?? detectProfile(input.projectRoot);
18
+ const detection = detectMigrationState(input.projectRoot, profile, input.taskId);
19
+ if (input.taskId) {
20
+ planTaskScopedMigration(input, actions, issues);
21
+ }
22
+ else {
23
+ planProjectScopedMigration(input.projectRoot, profile, actions, issues);
24
+ }
25
+ const beforeHash = createPlanHash(actions);
26
+ if (input.mode === 'execute' && beforeHash)
27
+ validateBeforeHash(input.beforeHash, beforeHash, issues);
28
+ if (input.mode === 'execute' && issues.every((issue) => issue.severity !== 'error')) {
29
+ applyMigrationActions(input.projectRoot, actions, issues);
30
+ }
31
+ return {
32
+ schemaVersion: 'hadara.protocol.migration.v1',
33
+ command: 'protocol.migrate',
34
+ ok: issues.every((issue) => issue.severity !== 'error'),
35
+ mode: input.mode,
36
+ target: { protocolVersion: input.target },
37
+ scope: { kind: input.taskId ? 'task' : 'project', taskId: input.taskId ?? null },
38
+ projectRoot: input.projectRoot,
39
+ detection,
40
+ summary: {
41
+ planned: actions.filter((action) => action.status === 'planned').length,
42
+ changed: actions.filter((action) => action.status === 'created' || action.status === 'updated').length,
43
+ skipped: actions.filter((action) => action.status === 'skipped').length,
44
+ beforeHash
45
+ },
46
+ actions,
47
+ issues
48
+ };
49
+ }
50
+ function planProjectScopedMigration(projectRoot, profile, actions, issues) {
51
+ planWrite(projectRoot, actions, 'protocol-version', '.hadara/protocol-version.json', JSON.stringify({
52
+ schemaVersion: 'hadara.protocol.version.v1',
53
+ protocolVersion: '0.3.0',
54
+ source: 'protocol.migrate'
55
+ }, null, 2) + '\n', 'Record the project protocol version marker for 0.3.0 migration.');
56
+ const seed = (0, docs_registry_1.createSeedDocumentRegistry)(profile);
57
+ const registry = mergeExistingRegistry(projectRoot, seed, issues);
58
+ planWrite(projectRoot, actions, 'docs-registry-json', docs_registry_1.DOCS_REGISTRY_PATH, (0, docs_registry_1.registryJson)(registry), 'Insert or update the 0.3 docs registry seed.');
59
+ planWrite(projectRoot, actions, 'doc-registry-markdown', 'docs/DOC_REGISTRY.md', (0, docs_registry_1.renderDocRegistryMarkdown)(registry), 'Create managed docs registry Markdown summary.');
60
+ planWrite(projectRoot, actions, 'command-surface-doc', 'docs/COMMAND_SURFACE.md', renderCommandSurfaceDoc(), 'Create command surface documentation from the command registry.');
61
+ const sopAfterRows = planRequiredReadingRows(projectRoot, actions, issues);
62
+ planSopRequiredReadingMarkers(projectRoot, actions, issues, sopAfterRows);
63
+ }
64
+ function planTaskScopedMigration(input, actions, issues) {
65
+ const task = (0, task_capsule_1.listTaskCapsules)(input.projectRoot).find((candidate) => candidate.id === input.taskId);
66
+ if (!task) {
67
+ issues.push({ severity: 'error', code: 'PROTOCOL_MIGRATION_TASK_NOT_FOUND', message: `Task Capsule not found: ${input.taskId}` });
68
+ return;
69
+ }
70
+ const relativeTaskPath = toPortablePath(node_path_1.default.relative(input.projectRoot, task.dir));
71
+ planEnsureMissingFile(input.projectRoot, actions, 'task-evidence-jsonl', `${relativeTaskPath}/evidence.jsonl`, `Create missing evidence JSONL index for ${task.id}.`, `${relativeTaskPath}/evidence.jsonl already exists; protocol migration preserves existing evidence history.`);
72
+ planTaskStatusHistoryMarkers(input.projectRoot, `${relativeTaskPath}/TASK.md`, actions, issues);
73
+ }
74
+ function mergeExistingRegistry(projectRoot, seed, issues) {
75
+ const registryPath = node_path_1.default.join(projectRoot, docs_registry_1.DOCS_REGISTRY_PATH);
76
+ if (!node_fs_1.default.existsSync(registryPath))
77
+ return seed;
78
+ try {
79
+ const existing = JSON.parse(node_fs_1.default.readFileSync(registryPath, 'utf8'));
80
+ const existingPaths = new Set(existing.documents.map((doc) => doc.path));
81
+ const missing = seed.documents.filter((doc) => !existingPaths.has(doc.path));
82
+ return {
83
+ ...existing,
84
+ schemaVersion: 'hadara.docs.registry.v1',
85
+ registryVersion: Math.max(existing.registryVersion ?? 1, seed.registryVersion),
86
+ projectProfile: seed.projectProfile,
87
+ documents: [...existing.documents, ...missing]
88
+ };
89
+ }
90
+ catch (error) {
91
+ issues.push({
92
+ severity: 'warning',
93
+ code: 'PROTOCOL_MIGRATION_REGISTRY_INVALID_JSON',
94
+ path: docs_registry_1.DOCS_REGISTRY_PATH,
95
+ message: `Existing docs registry could not be parsed and would be replaced by the ${seed.projectProfile} seed: ${error instanceof Error ? error.message : String(error)}`
96
+ });
97
+ return seed;
98
+ }
99
+ }
100
+ function planRequiredReadingRows(projectRoot, actions, issues) {
101
+ const relativePath = 'docs/IMPLEMENTATION_SOP.md';
102
+ const current = readIfExists(node_path_1.default.join(projectRoot, relativePath));
103
+ if (!current) {
104
+ actions.push({ id: 'required-reading-cleanup', path: relativePath, status: 'skipped', summary: `${relativePath} is missing; Required Reading cleanup skipped.` });
105
+ return null;
106
+ }
107
+ if (!current.includes('| Document | When to Read | Purpose |')) {
108
+ issues.push({ severity: 'warning', code: 'PROTOCOL_MIGRATION_REQUIRED_READING_TABLE_MISSING', path: relativePath, message: 'SOP Required Reading table was not found.' });
109
+ actions.push({ id: 'required-reading-cleanup', path: relativePath, status: 'skipped', summary: `${relativePath} has no canonical Required Reading table.` });
110
+ return null;
111
+ }
112
+ let next = current;
113
+ const rows = [
114
+ ['`docs/DOC_REGISTRY.md`', 'Docs governance or migration work', 'Readable projection of the 0.3 document registry.'],
115
+ ['`docs/COMMAND_SURFACE.md`', 'Command surface, lifecycle, or migration work', 'Registry-backed command portfolio and lifecycle entry points.']
116
+ ];
117
+ for (const row of rows) {
118
+ if (!next.includes(row[0]))
119
+ next = insertTableRow(next, '| Document | When to Read | Purpose |', formatTableRow(row));
120
+ }
121
+ if (next === current) {
122
+ actions.push({ id: 'required-reading-cleanup', path: relativePath, status: 'skipped', summary: `${relativePath} already includes 0.3 docs governance rows.` });
123
+ return current;
124
+ }
125
+ addPlannedAction(actions, {
126
+ id: 'required-reading-cleanup',
127
+ path: relativePath,
128
+ before: current,
129
+ after: next,
130
+ expectedBeforeExists: true
131
+ }, 'Add 0.3 docs governance rows to SOP Required Reading.');
132
+ return next;
133
+ }
134
+ function planSopRequiredReadingMarkers(projectRoot, actions, issues, plannedBase) {
135
+ const relativePath = 'docs/IMPLEMENTATION_SOP.md';
136
+ const current = plannedBase ?? readIfExists(node_path_1.default.join(projectRoot, relativePath));
137
+ if (!current || current.includes('hadara:managed:start required-reading')) {
138
+ actions.push({
139
+ id: 'managed-required-reading-marker',
140
+ path: relativePath,
141
+ status: 'skipped',
142
+ summary: current ? `${relativePath} already has a managed Required Reading marker.` : `${relativePath} is missing; managed marker skipped.`
143
+ });
144
+ return;
145
+ }
146
+ const replaced = wrapContiguousTable(current, '| Document | When to Read | Purpose |', 'required-reading', {
147
+ schema: 'hadara.managedSection.v1',
148
+ owner: 'protocol.migrate',
149
+ kind: 'markdown-table',
150
+ mode: 'insert-row',
151
+ version: 1,
152
+ required: true,
153
+ closeSourceRole: 'included'
154
+ });
155
+ if (!replaced) {
156
+ issues.push({ severity: 'warning', code: 'PROTOCOL_MIGRATION_MANAGED_MARKER_UNSUPPORTED', path: relativePath, message: 'Could not safely wrap the SOP Required Reading table in managed markers.' });
157
+ actions.push({ id: 'managed-required-reading-marker', path: relativePath, status: 'skipped', summary: `${relativePath} Required Reading table could not be safely wrapped.` });
158
+ return;
159
+ }
160
+ addPlannedAction(actions, {
161
+ id: 'managed-required-reading-marker',
162
+ path: relativePath,
163
+ before: current,
164
+ after: replaced,
165
+ expectedBeforeExists: true
166
+ }, 'Insert managed section markers around SOP Required Reading.');
167
+ }
168
+ function planTaskStatusHistoryMarkers(projectRoot, relativePath, actions, issues) {
169
+ const current = readIfExists(node_path_1.default.join(projectRoot, relativePath));
170
+ if (!current) {
171
+ actions.push({ id: 'task-status-history-marker', path: relativePath, status: 'skipped', summary: `${relativePath} is missing.` });
172
+ return;
173
+ }
174
+ if (current.includes('hadara:managed:start task-status-history')) {
175
+ actions.push({ id: 'task-status-history-marker', path: relativePath, status: 'skipped', summary: `${relativePath} already has managed task status history markers.` });
176
+ return;
177
+ }
178
+ const replaced = wrapContiguousTable(current, '| Time | Status | Reason | Evidence |', 'task-status-history', {
179
+ schema: 'hadara.managedSection.v1',
180
+ owner: 'task.finish',
181
+ kind: 'markdown-table',
182
+ mode: 'update-row',
183
+ version: 1,
184
+ required: true,
185
+ closeSourceRole: 'included'
186
+ });
187
+ if (!replaced) {
188
+ issues.push({ severity: 'warning', code: 'PROTOCOL_MIGRATION_TASK_STATUS_TABLE_MISSING', path: relativePath, message: 'Could not find the Task Status History table to wrap.' });
189
+ actions.push({ id: 'task-status-history-marker', path: relativePath, status: 'skipped', summary: `${relativePath} has no canonical status-history table.` });
190
+ return;
191
+ }
192
+ addPlannedAction(actions, {
193
+ id: 'task-status-history-marker',
194
+ path: relativePath,
195
+ before: current,
196
+ after: replaced,
197
+ expectedBeforeExists: true
198
+ }, 'Insert managed section markers around task status history.');
199
+ }
200
+ function planWrite(projectRoot, actions, id, relativePath, content, summary) {
201
+ const absolutePath = node_path_1.default.join(projectRoot, relativePath);
202
+ const exists = node_fs_1.default.existsSync(absolutePath);
203
+ const current = readIfExists(absolutePath);
204
+ if (exists && current === content) {
205
+ actions.push({ id, path: relativePath, status: 'skipped', summary: `${relativePath} already matches the 0.3 migration output.` });
206
+ return;
207
+ }
208
+ addPlannedAction(actions, {
209
+ id,
210
+ path: relativePath,
211
+ before: current,
212
+ after: content,
213
+ expectedBeforeExists: exists
214
+ }, summary);
215
+ }
216
+ function planEnsureMissingFile(projectRoot, actions, id, relativePath, createSummary, existsSummary) {
217
+ if (node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, relativePath))) {
218
+ actions.push({ id, path: relativePath, status: 'skipped', summary: existsSummary });
219
+ return;
220
+ }
221
+ addPlannedAction(actions, {
222
+ id,
223
+ path: relativePath,
224
+ before: '',
225
+ after: '',
226
+ expectedBeforeExists: false
227
+ }, createSummary);
228
+ }
229
+ function addPlannedAction(actions, write, summary) {
230
+ actions.push({
231
+ id: write.id,
232
+ path: write.path,
233
+ status: 'planned',
234
+ summary,
235
+ before: write.before,
236
+ after: write.after,
237
+ expectedBeforeExists: write.expectedBeforeExists,
238
+ expectedBeforeHash: hashContent(write.before),
239
+ afterHash: hashContent(write.after)
240
+ });
241
+ }
242
+ function applyMigrationActions(projectRoot, actions, issues) {
243
+ for (const action of actions) {
244
+ if (action.status !== 'planned' || action.after === undefined || action.expectedBeforeHash === undefined)
245
+ continue;
246
+ const absolutePath = node_path_1.default.join(projectRoot, action.path);
247
+ const currentExists = node_fs_1.default.existsSync(absolutePath);
248
+ const current = readIfExists(absolutePath);
249
+ if (currentExists !== (action.expectedBeforeExists ?? false) || hashContent(current) !== action.expectedBeforeHash) {
250
+ action.status = 'skipped';
251
+ issues.push({ severity: 'error', code: 'PROTOCOL_MIGRATION_WRITE_CONFLICT', path: action.path, message: `${action.path} changed after dry-run planning; rerun migration dry-run.` });
252
+ continue;
253
+ }
254
+ const tmp = node_path_1.default.join(node_path_1.default.dirname(absolutePath), `.hadara-migrate-${process.pid}-${Date.now()}-${node_path_1.default.basename(absolutePath)}`);
255
+ try {
256
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(absolutePath), { recursive: true });
257
+ node_fs_1.default.writeFileSync(tmp, action.after, { encoding: 'utf8', flag: 'wx' });
258
+ node_fs_1.default.renameSync(tmp, absolutePath);
259
+ action.status = currentExists ? 'updated' : 'created';
260
+ }
261
+ catch (error) {
262
+ if (node_fs_1.default.existsSync(tmp))
263
+ node_fs_1.default.rmSync(tmp, { force: true });
264
+ action.status = 'skipped';
265
+ issues.push({ severity: 'error', code: 'PROTOCOL_MIGRATION_ATOMIC_WRITE_FAILED', path: action.path, message: `Could not write ${action.path}: ${error instanceof Error ? error.message : String(error)}` });
266
+ }
267
+ }
268
+ }
269
+ function validateBeforeHash(beforeHash, expected, issues) {
270
+ if (!beforeHash) {
271
+ issues.push({ severity: 'error', code: 'PROTOCOL_MIGRATION_BEFORE_HASH_REQUIRED', message: `Execute mode requires --before-hash ${expected} from the reviewed dry-run report.` });
272
+ }
273
+ else if (beforeHash !== expected) {
274
+ issues.push({ severity: 'error', code: 'PROTOCOL_MIGRATION_BEFORE_HASH_MISMATCH', message: 'The supplied --before-hash does not match the current migration plan; rerun dry-run.' });
275
+ }
276
+ }
277
+ function detectMigrationState(projectRoot, profile, taskId) {
278
+ const registryPresent = node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, docs_registry_1.DOCS_REGISTRY_PATH));
279
+ const registryValid = registryPresent && canParseJson(node_path_1.default.join(projectRoot, docs_registry_1.DOCS_REGISTRY_PATH));
280
+ const commandSurfaceDocPresent = node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, 'docs/COMMAND_SURFACE.md'));
281
+ const sop = readIfExists(node_path_1.default.join(projectRoot, 'docs/IMPLEMENTATION_SOP.md'));
282
+ const requiredReadingManaged = sop.includes('hadara:managed:start required-reading');
283
+ const managedSectionsPresent = ['docs/TASK_BOARD.md', 'docs/PROJECT_STATE.md', 'docs/AGENT_HANDOFF.md', 'docs/IMPLEMENTATION_SOP.md', 'docs/DOC_REGISTRY.md']
284
+ .some((target) => readIfExists(node_path_1.default.join(projectRoot, target)).includes('hadara:managed:start'));
285
+ const taskCapsulePresent = taskId ? (0, task_capsule_1.listTaskCapsules)(projectRoot).some((candidate) => candidate.id === taskId) : null;
286
+ const migratedSignals = [registryPresent, registryValid, commandSurfaceDocPresent, requiredReadingManaged, managedSectionsPresent].filter(Boolean).length;
287
+ const scaffoldGeneration = migratedSignals >= 4 ? '0.3' : migratedSignals > 0 ? 'partial-0.3' : node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, 'AGENTS.md')) ? 'pre-0.3' : 'unknown';
288
+ return {
289
+ scaffoldGeneration,
290
+ profile,
291
+ docsRegistryPresent: registryPresent,
292
+ docsRegistryValid: registryValid,
293
+ commandSurfaceDocPresent,
294
+ requiredReadingManaged,
295
+ managedSectionsPresent,
296
+ taskCapsulePresent
297
+ };
298
+ }
299
+ function detectProfile(projectRoot) {
300
+ const state = readIfExists(node_path_1.default.join(projectRoot, 'docs/PROJECT_STATE.md'));
301
+ const declared = state.match(/\|\s*HADARA Profile\s*\|\s*(basic|standard|governed|hadara-dev)\s*\|/)?.[1];
302
+ if (declared === 'basic' || declared === 'standard' || declared === 'governed' || declared === 'hadara-dev')
303
+ return declared;
304
+ if (node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, 'docs/SECURITY_MODEL.md')))
305
+ return 'governed';
306
+ if (node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, 'docs/ARCHITECTURE.md')))
307
+ return 'standard';
308
+ return 'basic';
309
+ }
310
+ function renderCommandSurfaceDoc() {
311
+ const rows = capability_registry_1.HADARA_COMMAND_REGISTRY
312
+ .filter((entry) => entry.canonical && entry.status === 'stable')
313
+ .map((entry) => formatTableRow([
314
+ `\`${entry.id}\``,
315
+ `\`${entry.command.replace(/\|/g, '/')}\``,
316
+ entry.family,
317
+ entry.requiredness,
318
+ entry.writeBoundary
319
+ ]));
320
+ const table = ['| ID | Command | Family | Requiredness | Write Boundary |', '|---|---|---|---|---|', ...rows].join('\n');
321
+ return [
322
+ '# COMMAND_SURFACE',
323
+ '',
324
+ 'This document is generated from the HADARA command registry by `hadara protocol migrate`.',
325
+ '',
326
+ (0, managed_sections_1.managedSectionBlock)('command-surface-registry', {
327
+ schema: 'hadara.managedSection.v1',
328
+ owner: 'protocol.migrate',
329
+ kind: 'markdown-table',
330
+ mode: 'replace',
331
+ version: 1,
332
+ required: true,
333
+ closeSourceRole: 'included'
334
+ }, table),
335
+ ''
336
+ ].join('\n');
337
+ }
338
+ function wrapContiguousTable(content, header, sectionId, metadata) {
339
+ const lines = content.split('\n');
340
+ const start = lines.findIndex((line) => line.trim() === header);
341
+ if (start < 0)
342
+ return null;
343
+ let end = start;
344
+ while (end < lines.length && lines[end].startsWith('|'))
345
+ end += 1;
346
+ const body = lines.slice(start, end).join('\n');
347
+ const block = (0, managed_sections_1.managedSectionBlock)(sectionId, metadata, body);
348
+ lines.splice(start, end - start, ...block.split('\n'));
349
+ return lines.join('\n');
350
+ }
351
+ function insertTableRow(content, header, row) {
352
+ const lines = content.split('\n');
353
+ const headerIndex = lines.findIndex((line) => line.trim() === header);
354
+ if (headerIndex < 0)
355
+ return content;
356
+ let insertAt = headerIndex + 2;
357
+ while (insertAt < lines.length && lines[insertAt].startsWith('|'))
358
+ insertAt += 1;
359
+ lines.splice(insertAt, 0, row);
360
+ return lines.join('\n');
361
+ }
362
+ function formatTableRow(cells) {
363
+ return `| ${cells.map((cell) => cell.replace(/\|/g, '/')).join(' | ')} |`;
364
+ }
365
+ function createPlanHash(actions) {
366
+ const planned = actions
367
+ .filter((action) => action.status === 'planned')
368
+ .map((action) => ({
369
+ id: action.id,
370
+ path: action.path,
371
+ expectedBeforeExists: action.expectedBeforeExists ?? null,
372
+ expectedBeforeHash: action.expectedBeforeHash ?? null,
373
+ afterHash: action.afterHash ?? null
374
+ }));
375
+ if (planned.length === 0)
376
+ return null;
377
+ return hashContent(JSON.stringify(planned));
378
+ }
379
+ function hashContent(content) {
380
+ return node_crypto_1.default.createHash('sha256').update(content).digest('hex');
381
+ }
382
+ function readIfExists(absolutePath) {
383
+ return node_fs_1.default.existsSync(absolutePath) ? node_fs_1.default.readFileSync(absolutePath, 'utf8') : '';
384
+ }
385
+ function canParseJson(absolutePath) {
386
+ try {
387
+ JSON.parse(node_fs_1.default.readFileSync(absolutePath, 'utf8'));
388
+ return true;
389
+ }
390
+ catch {
391
+ return false;
392
+ }
393
+ }
394
+ function toPortablePath(value) {
395
+ return value.replace(/\\/g, '/');
396
+ }
@@ -30,11 +30,11 @@ const RELEASE_PACKAGE_KEYWORDS = [
30
30
  ];
31
31
  const RELEASE_PACKAGE_REPOSITORY = {
32
32
  type: 'git',
33
- url: 'git+https://github.com/ictseoyoungmin/HADARA-dev.git'
33
+ url: 'git+https://github.com/ictseoyoungmin/HADARA.git'
34
34
  };
35
- const RELEASE_PACKAGE_HOMEPAGE = 'https://github.com/ictseoyoungmin/HADARA-dev#readme';
35
+ const RELEASE_PACKAGE_HOMEPAGE = 'https://github.com/ictseoyoungmin/HADARA#readme';
36
36
  const RELEASE_PACKAGE_BUGS = {
37
- url: 'https://github.com/ictseoyoungmin/HADARA-dev/issues'
37
+ url: 'https://github.com/ictseoyoungmin/HADARA/issues'
38
38
  };
39
39
  const requiredFiles = ['package.json', 'README.md', 'LICENSE', 'dist/cli/main.js'];
40
40
  const allowedRoots = ['dist/', 'README.md', 'LICENSE', 'package.json'];
@@ -418,6 +418,13 @@ function appendStatusHistoryDone(content) {
418
418
  const prefix = content.slice(0, start);
419
419
  const suffix = content.slice(end);
420
420
  const row = `| ${new Date().toISOString().slice(0, 10)} | Done | Finished task capsule. | \`hadara task finish --execute\` |`;
421
+ const managedEnd = '<!-- hadara:managed:end task-status-history -->';
422
+ const managedEndIndex = section.indexOf(managedEnd);
423
+ if (managedEndIndex >= 0) {
424
+ const beforeMarker = section.slice(0, managedEndIndex).replace(/[ \t\r\n]+$/, '');
425
+ const afterMarker = section.slice(managedEndIndex);
426
+ return `${prefix}${beforeMarker}\n${row}\n${afterMarker}${suffix}`;
427
+ }
421
428
  const separator = section.endsWith('\n') ? '' : '\n';
422
429
  return `${prefix}${section}${separator}${row}\n${suffix}`;
423
430
  }
package/package.json CHANGED
@@ -1,9 +1,33 @@
1
1
  {
2
2
  "name": "hadara",
3
- "version": "0.3.0-rc.0",
3
+ "version": "0.3.0-rc.1",
4
4
  "private": false,
5
5
  "license": "MIT",
6
- "description": "HADARA: portable agentic development workbench",
6
+ "description": "Portable AI-assisted development workbench for evidence-backed task capsules, handoffs, and release gates.",
7
+ "keywords": [
8
+ "ai",
9
+ "agent",
10
+ "agents",
11
+ "coding-agent",
12
+ "developer-tools",
13
+ "cli",
14
+ "workflow",
15
+ "automation",
16
+ "task-management",
17
+ "evidence",
18
+ "handoff",
19
+ "release-management",
20
+ "mcp",
21
+ "hadara"
22
+ ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/ictseoyoungmin/HADARA.git"
26
+ },
27
+ "homepage": "https://github.com/ictseoyoungmin/HADARA#readme",
28
+ "bugs": {
29
+ "url": "https://github.com/ictseoyoungmin/HADARA/issues"
30
+ },
7
31
  "bin": {
8
32
  "hadara": "./dist/cli/main.js"
9
33
  },