@miadi/inquiry-weave 0.1.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/artefact.d.ts +33 -0
  4. package/dist/artefact.d.ts.map +1 -0
  5. package/dist/artefact.js +59 -0
  6. package/dist/artefact.js.map +1 -0
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +4 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/cli.d.ts +21 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +339 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/env.d.ts +22 -0
  16. package/dist/env.d.ts.map +1 -0
  17. package/dist/env.js +26 -0
  18. package/dist/env.js.map +1 -0
  19. package/dist/episode.d.ts +38 -0
  20. package/dist/episode.d.ts.map +1 -0
  21. package/dist/episode.js +109 -0
  22. package/dist/episode.js.map +1 -0
  23. package/dist/index.d.ts +12 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +12 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/issue.d.ts +27 -0
  28. package/dist/issue.d.ts.map +1 -0
  29. package/dist/issue.js +55 -0
  30. package/dist/issue.js.map +1 -0
  31. package/dist/legacy-hch.d.ts +28 -0
  32. package/dist/legacy-hch.d.ts.map +1 -0
  33. package/dist/legacy-hch.js +120 -0
  34. package/dist/legacy-hch.js.map +1 -0
  35. package/dist/relate.d.ts +31 -0
  36. package/dist/relate.d.ts.map +1 -0
  37. package/dist/relate.js +79 -0
  38. package/dist/relate.js.map +1 -0
  39. package/dist/status.d.ts +47 -0
  40. package/dist/status.d.ts.map +1 -0
  41. package/dist/status.js +142 -0
  42. package/dist/status.js.map +1 -0
  43. package/dist/sync.d.ts +28 -0
  44. package/dist/sync.d.ts.map +1 -0
  45. package/dist/sync.js +80 -0
  46. package/dist/sync.js.map +1 -0
  47. package/dist/tree.d.ts +19 -0
  48. package/dist/tree.d.ts.map +1 -0
  49. package/dist/tree.js +44 -0
  50. package/dist/tree.js.map +1 -0
  51. package/dist/weave.d.ts +83 -0
  52. package/dist/weave.d.ts.map +1 -0
  53. package/dist/weave.js +101 -0
  54. package/dist/weave.js.map +1 -0
  55. package/package.json +51 -0
  56. package/rispecs/medicine-wheel-registration.spec.md +72 -0
package/dist/sync.js ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * One-way artefact → episode mirror sync.
3
+ *
4
+ * The inquiry root (IAIP tree) is the system-of-record: the episode copy is a
5
+ * snapshot, deletions propagate, and local edits to the copy are flagged as
6
+ * divergence (a warning that sync will overwrite them).
7
+ */
8
+ import { cp, rename, rm } from "node:fs/promises";
9
+ import { existsSync } from "node:fs";
10
+ import { basename, join } from "node:path";
11
+ import { hashTree } from "./tree.js";
12
+ import { readEpisodeWeave, writeEpisodeWeave, WeaveError, } from "./weave.js";
13
+ function requireEntry(weave, artefactName) {
14
+ const entry = weave?.inquiries.find((inquiry) => inquiry.artefact === artefactName);
15
+ if (!weave || !entry) {
16
+ throw new WeaveError(`artefact ${artefactName} is not related to this episode yet — run 'inquiry-weave relate' (or 'promote') first`);
17
+ }
18
+ return entry;
19
+ }
20
+ export async function syncArtefact(artefactDir, episodeDir, options = {}) {
21
+ const artefactName = basename(artefactDir);
22
+ const weave = await readEpisodeWeave(episodeDir);
23
+ const entry = requireEntry(weave, artefactName);
24
+ const copyDir = join(episodeDir, "inquiry", artefactName);
25
+ const manifest = await hashTree(artefactDir);
26
+ if (entry.last_sync?.tree_sha256 === manifest.tree_sha256 && existsSync(copyDir)) {
27
+ return { status: "up-to-date", artefact: artefactName, episodeDir, copyDir, manifest };
28
+ }
29
+ if (options.dryRun) {
30
+ return { status: "dry-run", artefact: artefactName, episodeDir, copyDir, manifest };
31
+ }
32
+ const tmpDir = join(episodeDir, "inquiry", `.${artefactName}.tmp-${process.pid}`);
33
+ try {
34
+ await cp(artefactDir, tmpDir, { recursive: true });
35
+ if (existsSync(copyDir))
36
+ await rm(copyDir, { recursive: true });
37
+ await rename(tmpDir, copyDir);
38
+ }
39
+ finally {
40
+ if (existsSync(tmpDir))
41
+ await rm(tmpDir, { recursive: true, force: true });
42
+ }
43
+ entry.last_sync = {
44
+ at: (options.now?.() ?? new Date()).toISOString(),
45
+ tree_sha256: manifest.tree_sha256,
46
+ file_count: manifest.file_count,
47
+ bytes_total: manifest.bytes_total,
48
+ };
49
+ await writeEpisodeWeave(episodeDir, weave);
50
+ return { status: "synced", artefact: artefactName, episodeDir, copyDir, manifest };
51
+ }
52
+ export async function classifyFreshness(artefactDir, episodeDir, entry) {
53
+ const diagnostics = [];
54
+ if (!entry.last_sync)
55
+ return { state: "never-synced", diagnostics };
56
+ const copyDir = join(episodeDir, "inquiry", entry.artefact);
57
+ if (!existsSync(copyDir)) {
58
+ diagnostics.push("episode copy is missing; run sync to restore it");
59
+ return { state: "stale", diagnostics };
60
+ }
61
+ const sourceHash = existsSync(artefactDir)
62
+ ? (await hashTree(artefactDir)).tree_sha256
63
+ : undefined;
64
+ const copyHash = (await hashTree(copyDir)).tree_sha256;
65
+ if (copyHash !== entry.last_sync.tree_sha256) {
66
+ diagnostics.push("episode copy was edited after the last sync; sync will overwrite it");
67
+ if (sourceHash === entry.last_sync.tree_sha256) {
68
+ return { state: "episode-copy-diverged", diagnostics };
69
+ }
70
+ }
71
+ if (sourceHash === undefined) {
72
+ diagnostics.push("source artefact directory is missing under the inquiry root");
73
+ return { state: "stale", diagnostics };
74
+ }
75
+ if (sourceHash !== entry.last_sync.tree_sha256) {
76
+ return { state: "stale", diagnostics };
77
+ }
78
+ return { state: "in-sync", diagnostics };
79
+ }
80
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
package/dist/tree.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Deterministic tree hashing for sync freshness.
3
+ *
4
+ * tree_sha256 = sha256 over the sorted `relpath:filehash:size` manifest —
5
+ * robust to mtime drift across filesystems, unlike timestamp comparison.
6
+ */
7
+ export interface TreeFileEntry {
8
+ path: string;
9
+ sha256: string;
10
+ size: number;
11
+ }
12
+ export interface TreeManifest {
13
+ tree_sha256: string;
14
+ file_count: number;
15
+ bytes_total: number;
16
+ files: TreeFileEntry[];
17
+ }
18
+ export declare function hashTree(dir: string): Promise<TreeManifest>;
19
+ //# sourceMappingURL=tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,aAAa,EAAE,CAAA;CACvB;AAwBD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAWjE"}
package/dist/tree.js ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Deterministic tree hashing for sync freshness.
3
+ *
4
+ * tree_sha256 = sha256 over the sorted `relpath:filehash:size` manifest —
5
+ * robust to mtime drift across filesystems, unlike timestamp comparison.
6
+ */
7
+ import { createHash } from "node:crypto";
8
+ import { readFile, readdir, readlink } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ function sha256(data) {
11
+ return createHash("sha256").update(data).digest("hex");
12
+ }
13
+ async function walk(dir, prefix, files) {
14
+ const entries = await readdir(dir, { withFileTypes: true });
15
+ for (const entry of entries) {
16
+ const relPath = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
17
+ const absPath = join(dir, entry.name);
18
+ if (entry.isDirectory()) {
19
+ await walk(absPath, relPath, files);
20
+ }
21
+ else if (entry.isSymbolicLink()) {
22
+ const target = await readlink(absPath);
23
+ const content = `symlink:${target}`;
24
+ files.push({ path: relPath, sha256: sha256(content), size: content.length });
25
+ }
26
+ else if (entry.isFile()) {
27
+ const content = await readFile(absPath);
28
+ files.push({ path: relPath, sha256: sha256(content), size: content.length });
29
+ }
30
+ }
31
+ }
32
+ export async function hashTree(dir) {
33
+ const files = [];
34
+ await walk(dir, "", files);
35
+ files.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
36
+ const manifest = files.map((file) => `${file.path}:${file.sha256}:${file.size}`).join("\n");
37
+ return {
38
+ tree_sha256: sha256(manifest),
39
+ file_count: files.length,
40
+ bytes_total: files.reduce((total, file) => total + file.size, 0),
41
+ files,
42
+ };
43
+ }
44
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAehC,SAAS,MAAM,CAAC,IAAqB;IACnC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACxD,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,GAAW,EAAE,MAAc,EAAE,KAAsB;IACrE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAA;QACtE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACrC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;QACrC,CAAC;aAAM,IAAI,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;YACtC,MAAM,OAAO,GAAG,WAAW,MAAM,EAAE,CAAA;YACnC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAC9E,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;YACvC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAC9E,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAW;IACxC,MAAM,KAAK,GAAoB,EAAE,CAAA;IACjC,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,CAAA;IAC1B,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACtE,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3F,OAAO;QACL,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC7B,UAAU,EAAE,KAAK,CAAC,MAAM;QACxB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAChE,KAAK;KACN,CAAA;AACH,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The weave contract — the package's own explicit inquiry-metadata files.
3
+ *
4
+ * Episode side: <episode>/inquiry/weave.yaml (sync state lives here only)
5
+ * Artefact side: <artefact>/.weave.yaml (durable relations only, so a
6
+ * sync can copy it verbatim without a self-invalidating hash loop)
7
+ *
8
+ * `.hch/` is best-effort legacy input (see legacy-hch.ts), never written here.
9
+ */
10
+ export declare const WEAVE_VERSION = 1;
11
+ export interface LastSync {
12
+ at: string;
13
+ tree_sha256: string;
14
+ file_count: number;
15
+ bytes_total: number;
16
+ }
17
+ export interface InquiryEntry {
18
+ artefact: string;
19
+ issue?: string;
20
+ issue_url?: string;
21
+ related_at: string;
22
+ last_sync?: LastSync;
23
+ }
24
+ export interface EpisodeIdentityBlock {
25
+ number?: number;
26
+ slug?: string;
27
+ path: string;
28
+ }
29
+ export interface EpisodeWeave {
30
+ weave: number;
31
+ episode: EpisodeIdentityBlock;
32
+ inquiries: InquiryEntry[];
33
+ }
34
+ export interface ArtefactEpisodeLink {
35
+ path: string;
36
+ number?: number;
37
+ related_at: string;
38
+ }
39
+ export interface ArtefactWeave {
40
+ weave: number;
41
+ artefact: string;
42
+ issue?: string;
43
+ issue_url?: string;
44
+ episodes: ArtefactEpisodeLink[];
45
+ }
46
+ /**
47
+ * Projection of one woven relation — the registration seam for the future
48
+ * `inquiry-weave register` toward medicine-wheel (see rispecs/). v0 exports
49
+ * the shape only; nothing is transmitted anywhere.
50
+ */
51
+ export interface WeaveRecord {
52
+ weaveVersion: number;
53
+ artefact: {
54
+ name: string;
55
+ path: string;
56
+ };
57
+ issue?: {
58
+ ref: string;
59
+ url?: string;
60
+ };
61
+ episode: {
62
+ number?: number;
63
+ slug?: string;
64
+ path: string;
65
+ };
66
+ lastSync?: LastSync;
67
+ }
68
+ export declare class WeaveError extends Error {
69
+ }
70
+ export declare function episodeWeavePath(episodeDir: string): string;
71
+ export declare function artefactWeavePath(artefactDir: string): string;
72
+ export declare function readEpisodeWeave(episodeDir: string): Promise<EpisodeWeave | undefined>;
73
+ export declare function writeEpisodeWeave(episodeDir: string, weave: EpisodeWeave): Promise<void>;
74
+ export declare function readArtefactWeave(artefactDir: string): Promise<ArtefactWeave | undefined>;
75
+ export declare function writeArtefactWeave(artefactDir: string, weave: ArtefactWeave): Promise<void>;
76
+ /** Merge an inquiry entry by artefact name, preserving fields already recorded. */
77
+ export declare function upsertInquiry(weave: EpisodeWeave, entry: InquiryEntry): InquiryEntry;
78
+ /** Merge an episode link by path, preserving the original related_at. */
79
+ export declare function upsertEpisodeLink(weave: ArtefactWeave, link: ArtefactEpisodeLink): ArtefactEpisodeLink;
80
+ export declare function toWeaveRecords(episodeWeave: EpisodeWeave, inquiryRoot: string): WeaveRecord[];
81
+ export declare function newEpisodeWeave(episode: EpisodeIdentityBlock): EpisodeWeave;
82
+ export declare function newArtefactWeave(artefactDir: string): ArtefactWeave;
83
+ //# sourceMappingURL=weave.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weave.d.ts","sourceRoot":"","sources":["../src/weave.ts"],"names":[],"mappings":"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"}
package/dist/weave.js ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The weave contract — the package's own explicit inquiry-metadata files.
3
+ *
4
+ * Episode side: <episode>/inquiry/weave.yaml (sync state lives here only)
5
+ * Artefact side: <artefact>/.weave.yaml (durable relations only, so a
6
+ * sync can copy it verbatim without a self-invalidating hash loop)
7
+ *
8
+ * `.hch/` is best-effort legacy input (see legacy-hch.ts), never written here.
9
+ */
10
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
11
+ import { existsSync } from "node:fs";
12
+ import { basename, join } from "node:path";
13
+ import { parse, stringify } from "yaml";
14
+ export const WEAVE_VERSION = 1;
15
+ export class WeaveError extends Error {
16
+ }
17
+ export function episodeWeavePath(episodeDir) {
18
+ return join(episodeDir, "inquiry", "weave.yaml");
19
+ }
20
+ export function artefactWeavePath(artefactDir) {
21
+ return join(artefactDir, ".weave.yaml");
22
+ }
23
+ async function readWeaveFile(filePath) {
24
+ if (!existsSync(filePath))
25
+ return undefined;
26
+ let parsed;
27
+ try {
28
+ parsed = parse(await readFile(filePath, "utf8"));
29
+ }
30
+ catch (error) {
31
+ throw new WeaveError(`unreadable weave file ${filePath}: ${error.message}`);
32
+ }
33
+ if (parsed === null || parsed === undefined)
34
+ return undefined;
35
+ if (typeof parsed !== "object") {
36
+ throw new WeaveError(`weave file ${filePath} is not a mapping`);
37
+ }
38
+ const version = parsed.weave;
39
+ if (typeof version !== "number" || version > WEAVE_VERSION) {
40
+ throw new WeaveError(`weave file ${filePath} has unsupported version ${String(version)} (this package supports <= ${WEAVE_VERSION})`);
41
+ }
42
+ return parsed;
43
+ }
44
+ export async function readEpisodeWeave(episodeDir) {
45
+ const weave = await readWeaveFile(episodeWeavePath(episodeDir));
46
+ if (weave && !Array.isArray(weave.inquiries))
47
+ weave.inquiries = [];
48
+ return weave;
49
+ }
50
+ export async function writeEpisodeWeave(episodeDir, weave) {
51
+ const filePath = episodeWeavePath(episodeDir);
52
+ await mkdir(join(episodeDir, "inquiry"), { recursive: true });
53
+ await writeFile(filePath, stringify(weave), "utf8");
54
+ }
55
+ export async function readArtefactWeave(artefactDir) {
56
+ const weave = await readWeaveFile(artefactWeavePath(artefactDir));
57
+ if (weave && !Array.isArray(weave.episodes))
58
+ weave.episodes = [];
59
+ return weave;
60
+ }
61
+ export async function writeArtefactWeave(artefactDir, weave) {
62
+ await writeFile(artefactWeavePath(artefactDir), stringify(weave), "utf8");
63
+ }
64
+ /** Merge an inquiry entry by artefact name, preserving fields already recorded. */
65
+ export function upsertInquiry(weave, entry) {
66
+ const existing = weave.inquiries.find((inquiry) => inquiry.artefact === entry.artefact);
67
+ if (!existing) {
68
+ weave.inquiries.push(entry);
69
+ return entry;
70
+ }
71
+ existing.issue = entry.issue ?? existing.issue;
72
+ existing.issue_url = entry.issue_url ?? existing.issue_url;
73
+ existing.last_sync = entry.last_sync ?? existing.last_sync;
74
+ return existing;
75
+ }
76
+ /** Merge an episode link by path, preserving the original related_at. */
77
+ export function upsertEpisodeLink(weave, link) {
78
+ const existing = weave.episodes.find((episode) => episode.path === link.path);
79
+ if (!existing) {
80
+ weave.episodes.push(link);
81
+ return link;
82
+ }
83
+ existing.number = link.number ?? existing.number;
84
+ return existing;
85
+ }
86
+ export function toWeaveRecords(episodeWeave, inquiryRoot) {
87
+ return episodeWeave.inquiries.map((entry) => ({
88
+ weaveVersion: episodeWeave.weave,
89
+ artefact: { name: entry.artefact, path: join(inquiryRoot, entry.artefact) },
90
+ issue: entry.issue ? { ref: entry.issue, url: entry.issue_url } : undefined,
91
+ episode: episodeWeave.episode,
92
+ lastSync: entry.last_sync,
93
+ }));
94
+ }
95
+ export function newEpisodeWeave(episode) {
96
+ return { weave: WEAVE_VERSION, episode, inquiries: [] };
97
+ }
98
+ export function newArtefactWeave(artefactDir) {
99
+ return { weave: WEAVE_VERSION, artefact: basename(artefactDir), episodes: [] };
100
+ }
101
+ //# sourceMappingURL=weave.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weave.js","sourceRoot":"","sources":["../src/weave.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@miadi/inquiry-weave",
3
+ "version": "0.1.0",
4
+ "description": "Relate and sync IAIP inquiry artefacts, their GitHub issues, and miadi-chronicle episodes. Creates vessels and relations only — never inquiry content.",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "type": "module",
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "bin": {
19
+ "inquiry-weave": "dist/bin.js"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "rispecs"
25
+ ],
26
+ "dependencies": {
27
+ "yaml": "^2.5.0",
28
+ "@miadi/episodic-memory-schema": "0.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^5.4.0"
32
+ },
33
+ "keywords": [
34
+ "miadi",
35
+ "inquiry",
36
+ "iaip",
37
+ "miadi-chronicle",
38
+ "episodic-memory",
39
+ "etuaptmumk"
40
+ ],
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/jgwill/Miadi.git",
44
+ "directory": "packages/inquiry-weave"
45
+ },
46
+ "license": "MIT",
47
+ "scripts": {
48
+ "build": "../../node_modules/.bin/tsc -p tsconfig.build.json",
49
+ "test": "npm run build && node --test test/*.test.mjs"
50
+ }
51
+ }
@@ -0,0 +1,72 @@
1
+ # RISE Spec: Inquiry Weave → Medicine-Wheel Registration
2
+
3
+ Companion specs (the other two lanterns of this workstream):
4
+ - `medicine-wheel:rispecs/inquiry-weave-registration.spec.md` — the storage side
5
+ - `forgewright:rispecs/09-inquiry-weave-visibility.spec.md` — the rendering side (branch `4-plan-episode-visibility`, kin to `08-medicine-wheel-integration.spec.md`)
6
+
7
+ Status: **spec-only**. Nothing here is implemented in @miadi/inquiry-weave v0.
8
+
9
+ ## Reverse Engineering (current reality)
10
+
11
+ - Inquiry Weave relations exist only as files on disk: `<episode>/inquiry/weave.yaml`
12
+ (episode side, incl. sync state) and `<artefact>/.weave.yaml` (artefact side, durable
13
+ relations). Schema version key `weave: 1`.
14
+ - @miadi/inquiry-weave v0 already exports the projection seam: `WeaveRecord`
15
+ (`src/weave.ts`) — the three identities (artefact name+path, issue ref+url, episode
16
+ number/slug/path) plus `lastSync` — and `toWeaveRecords(episodeWeave, inquiryRoot)`.
17
+ - A medicine-wheel MCP server is already configured for chronicle work:
18
+ `/a/src/Miadi/etc/mcp-config-mw-ilex.json`, server `medicine-wheel-miadi-chronicle`
19
+ (`npx ${MWCV}`, `MW_API_URL=http://192.168.2.30:3940`). It is the candidate storage
20
+ interface; nothing registers weaves into it today.
21
+ - forgewright renders episode data from medicine-wheel but has no notion of an Inquiry
22
+ Weave.
23
+ - Legacy `.hch/` is a per-artefact issue cache with an unsettled fate (formalize / map /
24
+ retire — see README). Registration must not depend on it.
25
+
26
+ ## Intent
27
+
28
+ Inquiry Weaves become *visible relations*: once an artefact, its issue, and its episode
29
+ are woven, that weave can be seen wherever episodes are seen — without anyone re-deriving
30
+ it from the filesystem. The weave files on disk remain the source of truth; medicine-wheel
31
+ holds a registered projection; forgewright renders it. The mystery boundary is untouched:
32
+ registration moves metadata, never inquiry content.
33
+
34
+ ## Specifications (desired state)
35
+
36
+ - A new command `inquiry-weave register (--episode <ref> | --artefact <ref>) [--dry-run]`
37
+ projects each relation as a `WeaveRecord` and submits it to medicine-wheel.
38
+ - Transport: the medicine-wheel MCP tool surface (preferred) or `MW_API_URL` HTTP —
39
+ decided in the medicine-wheel spec; this package only guarantees the payload shape:
40
+
41
+ ```json
42
+ {
43
+ "weaveVersion": 1,
44
+ "artefact": { "name": "ep126-mila-ai-event-…", "path": "/src/IAIP/prototypes/artefacts/…" },
45
+ "issue": { "ref": "miadisabelle/Etuaptmumk-RSM#245", "url": "https://github.com/…/issues/245" },
46
+ "episode": { "number": 126, "slug": "mila-ai-indigenous-gathering", "path": "2026-07-13-episode-126-…" },
47
+ "lastSync": { "at": "…", "tree_sha256": "…", "file_count": 4, "bytes_total": 341001 }
48
+ }
49
+ ```
50
+
51
+ - Registration is idempotent: re-registering the same weave upserts by
52
+ (episode.path, artefact.name).
53
+ - Structural tension held: current reality = weave files on disk, invisible beyond the
54
+ filesystem; desired state = every woven relation queryable from medicine-wheel and
55
+ rendered by forgewright. `register` is the action step that resolves this tension.
56
+ - Failure to reach medicine-wheel degrades gracefully: the weave files remain
57
+ authoritative; `register` reports and exits 1 without touching them.
58
+
59
+ ## Exportation (implementation-ready notes)
60
+
61
+ - Implement as `src/register.ts` consuming `toWeaveRecords()`; add a `register`
62
+ subcommand in `cli.ts`; keep the transport injectable (test seam like
63
+ `INQUIRY_WEAVE_ISSUE_LIB`).
64
+ - Do not implement until the medicine-wheel spec lands its storage contract; then pin the
65
+ agreed endpoint/tool name here.
66
+ - Companion agent sessions (copilot/codex panes in the medicine-wheel and forgewright
67
+ repos) own the other two specs; keep the payload above as the shared contract between
68
+ all three.
69
+
70
+ ---
71
+ 🌸: The weave learns to be seen — first it held three threads together in the dark; now a
72
+ lantern is being shaped so the whole circle can watch them shine.