@jinn-network/jinn-layer 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 (131) hide show
  1. package/dist/adapters/contribution-adapter.d.ts +18 -0
  2. package/dist/adapters/contribution-adapter.js +134 -0
  3. package/dist/adapters/corpus-adapter.d.ts +12 -0
  4. package/dist/adapters/corpus-adapter.js +212 -0
  5. package/dist/adapters/index.d.ts +17 -0
  6. package/dist/adapters/index.js +17 -0
  7. package/dist/adapters/json-map-store.d.ts +4 -0
  8. package/dist/adapters/json-map-store.js +31 -0
  9. package/dist/adapters/local-learning-adapter.d.ts +25 -0
  10. package/dist/adapters/local-learning-adapter.js +49 -0
  11. package/dist/adapters/skills-adapter.d.ts +8 -0
  12. package/dist/adapters/skills-adapter.js +55 -0
  13. package/dist/admission-policy.d.ts +38 -0
  14. package/dist/admission-policy.js +17 -0
  15. package/dist/bin/jinn-distill-mcp.d.ts +2 -0
  16. package/dist/bin/jinn-distill-mcp.js +10 -0
  17. package/dist/bin/jinn-layer.d.ts +7 -0
  18. package/dist/bin/jinn-layer.js +15 -0
  19. package/dist/bridge-fetch-evidence.d.ts +102 -0
  20. package/dist/bridge-fetch-evidence.js +677 -0
  21. package/dist/bridge-verdict-source.d.ts +47 -0
  22. package/dist/bridge-verdict-source.js +216 -0
  23. package/dist/bridge.d.ts +247 -0
  24. package/dist/bridge.js +439 -0
  25. package/dist/capture.d.ts +98 -0
  26. package/dist/capture.js +617 -0
  27. package/dist/cli.d.ts +148 -0
  28. package/dist/cli.js +2221 -0
  29. package/dist/cluster-selection.d.ts +34 -0
  30. package/dist/cluster-selection.js +135 -0
  31. package/dist/cluster.d.ts +89 -0
  32. package/dist/cluster.js +142 -0
  33. package/dist/consume.d.ts +139 -0
  34. package/dist/consume.js +280 -0
  35. package/dist/corpus-probes.d.ts +61 -0
  36. package/dist/corpus-probes.js +120 -0
  37. package/dist/corpus-store.d.ts +16 -0
  38. package/dist/corpus-store.js +101 -0
  39. package/dist/distill-captures.d.ts +56 -0
  40. package/dist/distill-captures.js +283 -0
  41. package/dist/distill-feedback.d.ts +20 -0
  42. package/dist/distill-feedback.js +30 -0
  43. package/dist/distill-llm.d.ts +138 -0
  44. package/dist/distill-llm.js +438 -0
  45. package/dist/distill-mcp-server.d.ts +30 -0
  46. package/dist/distill-mcp-server.js +281 -0
  47. package/dist/distill-mode.d.ts +62 -0
  48. package/dist/distill-mode.js +107 -0
  49. package/dist/distill-progress.d.ts +78 -0
  50. package/dist/distill-progress.js +69 -0
  51. package/dist/distill-prompt.d.ts +34 -0
  52. package/dist/distill-prompt.js +85 -0
  53. package/dist/distill-render.d.ts +123 -0
  54. package/dist/distill-render.js +303 -0
  55. package/dist/distill-runs.d.ts +28 -0
  56. package/dist/distill-runs.js +39 -0
  57. package/dist/distill-traces.d.ts +67 -0
  58. package/dist/distill-traces.js +211 -0
  59. package/dist/distill.d.ts +180 -0
  60. package/dist/distill.js +337 -0
  61. package/dist/distiller.d.ts +118 -0
  62. package/dist/distiller.js +142 -0
  63. package/dist/envelope.d.ts +9 -0
  64. package/dist/envelope.js +9 -0
  65. package/dist/eval-prep.d.ts +129 -0
  66. package/dist/eval-prep.js +523 -0
  67. package/dist/execution-publish.d.ts +62 -0
  68. package/dist/execution-publish.js +72 -0
  69. package/dist/gate.d.ts +39 -0
  70. package/dist/gate.js +106 -0
  71. package/dist/index.d.ts +49 -0
  72. package/dist/index.js +50 -0
  73. package/dist/ipfs-cid.d.ts +18 -0
  74. package/dist/ipfs-cid.js +149 -0
  75. package/dist/layer-default.d.ts +3 -0
  76. package/dist/layer-default.js +10 -0
  77. package/dist/ledger.d.ts +62 -0
  78. package/dist/ledger.js +104 -0
  79. package/dist/measurement.d.ts +90 -0
  80. package/dist/measurement.js +134 -0
  81. package/dist/path-safety.d.ts +19 -0
  82. package/dist/path-safety.js +303 -0
  83. package/dist/pipeline.d.ts +94 -0
  84. package/dist/pipeline.js +124 -0
  85. package/dist/plugin-wiring.d.ts +4 -0
  86. package/dist/plugin-wiring.js +54 -0
  87. package/dist/preview.d.ts +31 -0
  88. package/dist/preview.js +28 -0
  89. package/dist/process-contract.d.ts +303 -0
  90. package/dist/process-contract.js +118 -0
  91. package/dist/publish-skill.d.ts +86 -0
  92. package/dist/publish-skill.js +116 -0
  93. package/dist/publish.d.ts +294 -0
  94. package/dist/publish.js +1090 -0
  95. package/dist/scripts/scan-skills-registry.d.ts +24 -0
  96. package/dist/scripts/scan-skills-registry.js +207 -0
  97. package/dist/seed-import/curated-batch.d.ts +36 -0
  98. package/dist/seed-import/curated-batch.js +103 -0
  99. package/dist/seed-import/episode-execute.d.ts +50 -0
  100. package/dist/seed-import/episode-execute.js +235 -0
  101. package/dist/seed-import/episode-fetch.d.ts +119 -0
  102. package/dist/seed-import/episode-fetch.js +137 -0
  103. package/dist/seed-import/episode-plan.d.ts +11 -0
  104. package/dist/seed-import/episode-plan.js +27 -0
  105. package/dist/seed-import/episode-report.d.ts +39 -0
  106. package/dist/seed-import/episode-report.js +36 -0
  107. package/dist/seed-import/execute.d.ts +61 -0
  108. package/dist/seed-import/execute.js +245 -0
  109. package/dist/seed-import/fetch.d.ts +62 -0
  110. package/dist/seed-import/fetch.js +82 -0
  111. package/dist/seed-import/licence.d.ts +17 -0
  112. package/dist/seed-import/licence.js +36 -0
  113. package/dist/seed-import/plan.d.ts +10 -0
  114. package/dist/seed-import/plan.js +21 -0
  115. package/dist/seed-import/report.d.ts +34 -0
  116. package/dist/seed-import/report.js +31 -0
  117. package/dist/seed-import/state.d.ts +72 -0
  118. package/dist/seed-import/state.js +120 -0
  119. package/dist/signal.d.ts +36 -0
  120. package/dist/signal.js +48 -0
  121. package/dist/signing.d.ts +8 -0
  122. package/dist/signing.js +21 -0
  123. package/dist/skill-package.d.ts +70 -0
  124. package/dist/skill-package.js +141 -0
  125. package/dist/skill.d.ts +21 -0
  126. package/dist/skill.js +77 -0
  127. package/dist/snapshot-transcript.d.ts +54 -0
  128. package/dist/snapshot-transcript.js +117 -0
  129. package/dist/three-arm-run.d.ts +27 -0
  130. package/dist/three-arm-run.js +41 -0
  131. package/package.json +60 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Evidence-episode seed source (issue #1771, rescope plan §4.2-§4.4).
3
+ *
4
+ * Stage 1's rescoped retrieval serves evidence records, not skill records
5
+ * (docs/superpowers/plans/2026-07-16-jinn-plugin-stage-1-rescope-plan.md
6
+ * §3.1). The shared testnet corpus had almost no evidence to serve — skills.sh
7
+ * skill seeds only. This source reads a directory of hand-authored
8
+ * seed-episode JSON files (each transformed offline from a REAL completed
9
+ * session per the runbook, docs/runbooks/stage1-evidence-seeding.md) and
10
+ * feeds them through the SAME `capture() -> publish()` path the skill lane
11
+ * uses (`episode-execute.ts`). The resulting envelope remains identifiable
12
+ * as a seed on the wire through `provenance: 'imported'`, the seed importer
13
+ * harness/session identity, the `seed-import` tag, and `seed:*` steps. The
14
+ * selected scrub profile is a local implementation fact proved by code and
15
+ * tests; `TraceEnvelopeV0` does not publish a scrub-component manifest.
16
+ *
17
+ * Step convention (so a future retrieval-side packet projector — rescope
18
+ * plan §3.2 `projectKnowledgePacket`, R1/#1770 — has an obvious, discoverable
19
+ * shape to read): every content step's `attributes` carries
20
+ * 'seed.step.label' — one of the §3.2 excerpt labels
21
+ * ('failure' | 'fix' | 'command' | 'diff' | 'note')
22
+ * 'seed.step.title' — a short human title
23
+ * 'seed.step.text' — the excerpt content itself
24
+ * and a final step carries the episode-level metadata:
25
+ * 'seed.synthesis' — the authored how-it-was-solved text (§3.2 `synthesis`)
26
+ * 'seed.attribution' — { repo, baseCommit?, origin, sourceUrl?, supersedes? }
27
+ * No bespoke fixture format: this is exactly the frozen jinn.trace-envelope.v0
28
+ * step shape (spec §3.1), the same free-form-attribute convention the
29
+ * existing `seed:skill-md` skill-seed step already uses.
30
+ */
31
+ import { z } from 'zod';
32
+ /** Rescope plan §3.2 excerpt labels — the knowledge-packet projection's vocabulary. */
33
+ export declare const SEED_EPISODE_EXCERPT_LABELS: readonly ["failure", "fix", "command", "diff", "note"];
34
+ export declare const SeedEpisodeExcerptLabelSchema: z.ZodEnum<{
35
+ command: "command";
36
+ failure: "failure";
37
+ fix: "fix";
38
+ diff: "diff";
39
+ note: "note";
40
+ }>;
41
+ export type SeedEpisodeExcerptLabel = z.infer<typeof SeedEpisodeExcerptLabelSchema>;
42
+ export declare const SeedEpisodeStepSchema: z.ZodObject<{
43
+ label: z.ZodEnum<{
44
+ command: "command";
45
+ failure: "failure";
46
+ fix: "fix";
47
+ diff: "diff";
48
+ note: "note";
49
+ }>;
50
+ title: z.ZodString;
51
+ text: z.ZodString;
52
+ }, z.core.$strict>;
53
+ export type SeedEpisodeStep = z.infer<typeof SeedEpisodeStepSchema>;
54
+ /**
55
+ * A seed-episode file — the canonical captured-evidence contract (issue
56
+ * #1771): task summary, ordered steps with real commands/outputs (a source
57
+ * episode carries at least one `failure` and its `fix`; distractors are
58
+ * freeform), outcome + verifiability tier, tags, attribution, and an
59
+ * authored synthesis. Strict: an unknown field is a malformed fixture, not
60
+ * silently tolerated.
61
+ */
62
+ export declare const SeedEpisodeSchema: z.ZodObject<{
63
+ id: z.ZodString;
64
+ repo: z.ZodString;
65
+ baseCommit: z.ZodOptional<z.ZodString>;
66
+ taskSummary: z.ZodString;
67
+ tags: z.ZodArray<z.ZodString>;
68
+ steps: z.ZodArray<z.ZodObject<{
69
+ label: z.ZodEnum<{
70
+ command: "command";
71
+ failure: "failure";
72
+ fix: "fix";
73
+ diff: "diff";
74
+ note: "note";
75
+ }>;
76
+ title: z.ZodString;
77
+ text: z.ZodString;
78
+ }, z.core.$strict>>;
79
+ outcome: z.ZodObject<{
80
+ status: z.ZodEnum<{
81
+ completed: "completed";
82
+ failed: "failed";
83
+ abandoned: "abandoned";
84
+ }>;
85
+ verifiabilityTier: z.ZodEnum<{
86
+ "user-accepted": "user-accepted";
87
+ "tests-passed": "tests-passed";
88
+ "evaluator-verified": "evaluator-verified";
89
+ }>;
90
+ }, z.core.$strict>;
91
+ synthesis: z.ZodString;
92
+ attribution: z.ZodObject<{
93
+ origin: z.ZodString;
94
+ sourceUrl: z.ZodOptional<z.ZodString>;
95
+ }, z.core.$strict>;
96
+ }, z.core.$strict>;
97
+ export type SeedEpisode = z.infer<typeof SeedEpisodeSchema>;
98
+ /** Canonical identity and content approved by an episode report row. */
99
+ export declare function episodeContentDigest(episode: SeedEpisode): string;
100
+ /** Parse an untrusted value (a seed-episode JSON file) as a SeedEpisode; throws ZodError. */
101
+ export declare function parseSeedEpisode(input: unknown): SeedEpisode;
102
+ export interface EpisodeSource {
103
+ name: string;
104
+ list(): Promise<SeedEpisode[]>;
105
+ }
106
+ export interface LocalEpisodeSeedSourceOptions {
107
+ maxFiles?: number;
108
+ maxFileBytes?: number;
109
+ rejectSymlinks?: boolean;
110
+ }
111
+ /**
112
+ * Local directory seed source: every `*.episode.json` file in `dir`,
113
+ * validated against `SeedEpisodeSchema`. Sorted by filename for
114
+ * deterministic plan/execute ordering (reproducible report files; the
115
+ * fixture-lint test pins this too). Files not matching the `.episode.json`
116
+ * suffix (e.g. the skill-shaped distractor fixtures) are ignored by
117
+ * construction — episodes and skills are separate lanes.
118
+ */
119
+ export declare function createLocalEpisodeSeedSource(dir: string, options?: LocalEpisodeSeedSourceOptions): EpisodeSource;
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Evidence-episode seed source (issue #1771, rescope plan §4.2-§4.4).
3
+ *
4
+ * Stage 1's rescoped retrieval serves evidence records, not skill records
5
+ * (docs/superpowers/plans/2026-07-16-jinn-plugin-stage-1-rescope-plan.md
6
+ * §3.1). The shared testnet corpus had almost no evidence to serve — skills.sh
7
+ * skill seeds only. This source reads a directory of hand-authored
8
+ * seed-episode JSON files (each transformed offline from a REAL completed
9
+ * session per the runbook, docs/runbooks/stage1-evidence-seeding.md) and
10
+ * feeds them through the SAME `capture() -> publish()` path the skill lane
11
+ * uses (`episode-execute.ts`). The resulting envelope remains identifiable
12
+ * as a seed on the wire through `provenance: 'imported'`, the seed importer
13
+ * harness/session identity, the `seed-import` tag, and `seed:*` steps. The
14
+ * selected scrub profile is a local implementation fact proved by code and
15
+ * tests; `TraceEnvelopeV0` does not publish a scrub-component manifest.
16
+ *
17
+ * Step convention (so a future retrieval-side packet projector — rescope
18
+ * plan §3.2 `projectKnowledgePacket`, R1/#1770 — has an obvious, discoverable
19
+ * shape to read): every content step's `attributes` carries
20
+ * 'seed.step.label' — one of the §3.2 excerpt labels
21
+ * ('failure' | 'fix' | 'command' | 'diff' | 'note')
22
+ * 'seed.step.title' — a short human title
23
+ * 'seed.step.text' — the excerpt content itself
24
+ * and a final step carries the episode-level metadata:
25
+ * 'seed.synthesis' — the authored how-it-was-solved text (§3.2 `synthesis`)
26
+ * 'seed.attribution' — { repo, baseCommit?, origin, sourceUrl?, supersedes? }
27
+ * No bespoke fixture format: this is exactly the frozen jinn.trace-envelope.v0
28
+ * step shape (spec §3.1), the same free-form-attribute convention the
29
+ * existing `seed:skill-md` skill-seed step already uses.
30
+ */
31
+ import { lstatSync, readFileSync, readdirSync } from 'node:fs';
32
+ import { join } from 'node:path';
33
+ import { z } from 'zod';
34
+ import { OutcomeStatusSchema, VerifiabilityTierSchema } from '../envelope.js';
35
+ import { hashSeedContent } from './state.js';
36
+ /** Rescope plan §3.2 excerpt labels — the knowledge-packet projection's vocabulary. */
37
+ export const SEED_EPISODE_EXCERPT_LABELS = ['failure', 'fix', 'command', 'diff', 'note'];
38
+ export const SeedEpisodeExcerptLabelSchema = z.enum(SEED_EPISODE_EXCERPT_LABELS);
39
+ export const SeedEpisodeStepSchema = z.strictObject({
40
+ label: SeedEpisodeExcerptLabelSchema,
41
+ title: z.string().min(1),
42
+ text: z.string().min(1),
43
+ });
44
+ /**
45
+ * A seed-episode file — the canonical captured-evidence contract (issue
46
+ * #1771): task summary, ordered steps with real commands/outputs (a source
47
+ * episode carries at least one `failure` and its `fix`; distractors are
48
+ * freeform), outcome + verifiability tier, tags, attribution, and an
49
+ * authored synthesis. Strict: an unknown field is a malformed fixture, not
50
+ * silently tolerated.
51
+ */
52
+ export const SeedEpisodeSchema = z.strictObject({
53
+ /** Stable seed identity — the idempotency key (state.ts) and filename stem. */
54
+ id: z.string().min(1),
55
+ /** `owner/repo` the episode re-performs work in/against. */
56
+ repo: z.string().min(1),
57
+ /** Full 40-char commit sha the episode was re-performed AT (pre-fix state), when applicable. */
58
+ baseCommit: z.string().regex(/^[0-9a-f]{40}$/, 'full 40-char commit sha').optional(),
59
+ taskSummary: z.string().min(1).max(500),
60
+ tags: z.array(z.string().min(1)).min(1),
61
+ steps: z.array(SeedEpisodeStepSchema).min(1),
62
+ outcome: z.strictObject({
63
+ status: OutcomeStatusSchema,
64
+ verifiabilityTier: VerifiabilityTierSchema,
65
+ }),
66
+ /** 3-6 sentence how-it-was-solved text (rescope plan §3.2 `synthesis`). */
67
+ synthesis: z.string().min(1),
68
+ attribution: z.strictObject({
69
+ /**
70
+ * How this episode entered the corpus — stated honestly. Vocabulary in
71
+ * use (freeform by schema; document new values in
72
+ * docs/runbooks/stage1-evidence-seeding.md):
73
+ * - 'operator-recorded-session' — transformed from a real recorded
74
+ * session; pair with `sourceUrl` when it re-performs a merged fix.
75
+ * - 'synthetic-selection-distractor' — hand-authored negative/contrast
76
+ * material, explicitly synthetic and paired with the unverified
77
+ * `user-accepted` tier (PR #1779 review).
78
+ */
79
+ origin: z.string().min(1),
80
+ /** Link to the real merged fix, when the episode re-performs one. */
81
+ sourceUrl: z.string().min(1).optional(),
82
+ }),
83
+ });
84
+ /** Canonical identity and content approved by an episode report row. */
85
+ export function episodeContentDigest(episode) {
86
+ return hashSeedContent({
87
+ id: episode.id,
88
+ repo: episode.repo,
89
+ baseCommit: episode.baseCommit ?? null,
90
+ taskSummary: episode.taskSummary,
91
+ tags: episode.tags,
92
+ steps: episode.steps,
93
+ outcome: episode.outcome,
94
+ synthesis: episode.synthesis,
95
+ attribution: episode.attribution,
96
+ });
97
+ }
98
+ /** Parse an untrusted value (a seed-episode JSON file) as a SeedEpisode; throws ZodError. */
99
+ export function parseSeedEpisode(input) {
100
+ return SeedEpisodeSchema.parse(input);
101
+ }
102
+ /**
103
+ * Local directory seed source: every `*.episode.json` file in `dir`,
104
+ * validated against `SeedEpisodeSchema`. Sorted by filename for
105
+ * deterministic plan/execute ordering (reproducible report files; the
106
+ * fixture-lint test pins this too). Files not matching the `.episode.json`
107
+ * suffix (e.g. the skill-shaped distractor fixtures) are ignored by
108
+ * construction — episodes and skills are separate lanes.
109
+ */
110
+ export function createLocalEpisodeSeedSource(dir, options = {}) {
111
+ return {
112
+ name: `local-episodes:${dir}`,
113
+ async list() {
114
+ const files = readdirSync(dir)
115
+ .filter((file) => file.endsWith('.episode.json'))
116
+ .sort();
117
+ if (options.maxFiles !== undefined && files.length > options.maxFiles) {
118
+ throw new Error(`episode directory contains ${files.length} candidate files; maximum is ${options.maxFiles}`);
119
+ }
120
+ return files.map((file) => {
121
+ const path = join(dir, file);
122
+ const stats = lstatSync(path);
123
+ if (options.rejectSymlinks && stats.isSymbolicLink()) {
124
+ throw new Error(`episode candidate must not be a symbolic link: ${file}`);
125
+ }
126
+ if (options.rejectSymlinks && !stats.isFile()) {
127
+ throw new Error(`episode candidate must be a regular file: ${file}`);
128
+ }
129
+ if (options.maxFileBytes !== undefined &&
130
+ stats.size > options.maxFileBytes) {
131
+ throw new Error(`episode candidate ${file} exceeds ${options.maxFileBytes} byte limit`);
132
+ }
133
+ return parseSeedEpisode(JSON.parse(readFileSync(path, 'utf-8')));
134
+ });
135
+ },
136
+ };
137
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `planEpisodes()` — list + structural validation, ZERO writes (mirrors
3
+ * `plan.ts` for the evidence-episode source, issue #1771). Episodes are
4
+ * first-party content (no licence gate); the only structural failure this
5
+ * catches is a duplicate `id` within one batch — a same-identity collision
6
+ * would otherwise make idempotency (state.ts) apply to whichever file lists
7
+ * second, silently dropping the first from the report.
8
+ */
9
+ import { type EpisodeSource } from './episode-fetch.js';
10
+ import type { EpisodeImportReport } from './episode-report.js';
11
+ export declare function planEpisodes(source: EpisodeSource): Promise<EpisodeImportReport>;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `planEpisodes()` — list + structural validation, ZERO writes (mirrors
3
+ * `plan.ts` for the evidence-episode source, issue #1771). Episodes are
4
+ * first-party content (no licence gate); the only structural failure this
5
+ * catches is a duplicate `id` within one batch — a same-identity collision
6
+ * would otherwise make idempotency (state.ts) apply to whichever file lists
7
+ * second, silently dropping the first from the report.
8
+ */
9
+ import { episodeContentDigest } from './episode-fetch.js';
10
+ export async function planEpisodes(source) {
11
+ const episodes = await source.list();
12
+ const seen = new Set();
13
+ return episodes.map((episode) => {
14
+ const duplicate = seen.has(episode.id);
15
+ seen.add(episode.id);
16
+ return {
17
+ id: episode.id,
18
+ taskSummary: episode.taskSummary,
19
+ tags: episode.tags,
20
+ contentDigest: episodeContentDigest(episode),
21
+ verdict: duplicate ? 'skip' : 'import',
22
+ reason: duplicate
23
+ ? `duplicate id "${episode.id}" in source — first occurrence wins`
24
+ : 'first-party recorded episode',
25
+ };
26
+ });
27
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The EpisodeImportReport — the evidence-episode analogue of `report.ts`'s
3
+ * ImportReport (issue #1771). Episodes are first-party authored content (no
4
+ * upstream licence to gate on, unlike the skills.sh skill lane) so `verdict`
5
+ * here is structural, not licence-based: 'skip' only for a duplicate `id`
6
+ * within the same batch (see `episode-plan.ts`). `seed execute --episodes-dir`
7
+ * still reads this report as the approval-gate artifact — nothing publishes
8
+ * that was not on the list a human reviewed, and `contentDigest` binds each
9
+ * row to the exact canonical episode content present at planning time.
10
+ */
11
+ import { z } from 'zod';
12
+ export declare const EpisodeImportReportRowSchema: z.ZodObject<{
13
+ id: z.ZodString;
14
+ taskSummary: z.ZodString;
15
+ tags: z.ZodArray<z.ZodString>;
16
+ contentDigest: z.ZodString;
17
+ verdict: z.ZodEnum<{
18
+ import: "import";
19
+ skip: "skip";
20
+ }>;
21
+ reason: z.ZodString;
22
+ }, z.core.$strict>;
23
+ export type EpisodeImportReportRow = z.infer<typeof EpisodeImportReportRowSchema>;
24
+ export declare const EpisodeImportReportSchema: z.ZodArray<z.ZodObject<{
25
+ id: z.ZodString;
26
+ taskSummary: z.ZodString;
27
+ tags: z.ZodArray<z.ZodString>;
28
+ contentDigest: z.ZodString;
29
+ verdict: z.ZodEnum<{
30
+ import: "import";
31
+ skip: "skip";
32
+ }>;
33
+ reason: z.ZodString;
34
+ }, z.core.$strict>>;
35
+ export type EpisodeImportReport = z.infer<typeof EpisodeImportReportSchema>;
36
+ /** Parse an untrusted value (e.g. an edited report file); throws ZodError. */
37
+ export declare function parseEpisodeImportReport(input: unknown): EpisodeImportReport;
38
+ /** Human-readable report table for the terminal. */
39
+ export declare function renderEpisodeImportReport(report: EpisodeImportReport): string;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The EpisodeImportReport — the evidence-episode analogue of `report.ts`'s
3
+ * ImportReport (issue #1771). Episodes are first-party authored content (no
4
+ * upstream licence to gate on, unlike the skills.sh skill lane) so `verdict`
5
+ * here is structural, not licence-based: 'skip' only for a duplicate `id`
6
+ * within the same batch (see `episode-plan.ts`). `seed execute --episodes-dir`
7
+ * still reads this report as the approval-gate artifact — nothing publishes
8
+ * that was not on the list a human reviewed, and `contentDigest` binds each
9
+ * row to the exact canonical episode content present at planning time.
10
+ */
11
+ import { z } from 'zod';
12
+ export const EpisodeImportReportRowSchema = z.strictObject({
13
+ id: z.string().min(1),
14
+ taskSummary: z.string().min(1),
15
+ tags: z.array(z.string().min(1)),
16
+ /** SHA-256 over the canonical episode content the operator approved. */
17
+ contentDigest: z.string().regex(/^[0-9a-f]{64}$/),
18
+ verdict: z.enum(['import', 'skip']),
19
+ reason: z.string().min(1),
20
+ });
21
+ export const EpisodeImportReportSchema = z.array(EpisodeImportReportRowSchema);
22
+ /** Parse an untrusted value (e.g. an edited report file); throws ZodError. */
23
+ export function parseEpisodeImportReport(input) {
24
+ return EpisodeImportReportSchema.parse(input);
25
+ }
26
+ /** Human-readable report table for the terminal. */
27
+ export function renderEpisodeImportReport(report) {
28
+ if (report.length === 0)
29
+ return 'Empty report — no episodes listed.';
30
+ const lines = [`${report.length} episode(s)`, ''];
31
+ for (const row of report) {
32
+ lines.push(`${row.verdict === 'import' ? 'IMPORT' : 'skip '} ${row.id}`, ` summary ${row.taskSummary}`, ` tags ${row.tags.join(', ')}`, ` digest ${row.contentDigest}`, ` reason ${row.reason}`, '');
33
+ }
34
+ lines.pop();
35
+ return lines.join('\n');
36
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * `execute()` — publish the approved import rows through the Task-4 path
3
+ * (plan Task 6, spec §7).
4
+ *
5
+ * Each imported skill becomes a synthetic captured task carrying the
6
+ * SKILL.md as its single step, runs through the SAME mandatory scrub
7
+ * (`capture()`) and the SAME publish/anchor path (`publish()`) as a real
8
+ * contribution — one publish path, per the 2026-07-02 schema review — with
9
+ * `provenance: 'imported'` and attribution metadata in the step attributes.
10
+ * Seeds are thereby excluded from the demand signal and from emissions
11
+ * eligibility by every provenance-aware reader.
12
+ *
13
+ * The licence gate is structural, not advisory: execute() re-checks each
14
+ * row's licence from the freshly-fetched source. A verdict hand-edited to
15
+ * `import` over a non-allowlisted licence is refused — widening the gate is
16
+ * a code change to the disclosed allowlist, reviewable in a PR, not a
17
+ * report-file edit.
18
+ *
19
+ * Idempotent by seed identity (issue #1771, `state.ts`): re-running execute
20
+ * over an unchanged skill publishes nothing new (a `skipped` row instead);
21
+ * re-running over a CHANGED skill (different skillMd/description/licence/
22
+ * source) republishes and sets the new record's
23
+ * `provenance.supersedes` to the prior envelopeRef. `opts.state` defaults to
24
+ * a fresh in-memory store per call, so existing callers that never pass one
25
+ * see unchanged behaviour (every row always publishes) — the CLI wires a
26
+ * file-backed store for real runs (cli.ts, `seed execute`).
27
+ */
28
+ import { type HarnessPublishDeps } from '../publish.js';
29
+ import type { SeedSource } from './fetch.js';
30
+ import type { ImportReport } from './report.js';
31
+ import { type SeedImportStateStore } from './state.js';
32
+ export interface ImportResult {
33
+ imported: Array<{
34
+ skill: string;
35
+ envelopeRef: string;
36
+ anchorTx: string | null;
37
+ /** Publication succeeded, but local lineage state needs operator recovery. */
38
+ stateWarning?: string;
39
+ /** Anchor succeeded, but the local publication ledger needs recovery. */
40
+ ledgerWarning?: string;
41
+ }>;
42
+ skipped: Array<{
43
+ skill: string;
44
+ reason: string;
45
+ }>;
46
+ errors: Array<{
47
+ skill: string;
48
+ error: string;
49
+ }>;
50
+ }
51
+ /**
52
+ * Extract `name:` from a SKILL.md YAML frontmatter block, if present.
53
+ * Deliberately a line-parser, not a YAML dependency — skill frontmatter
54
+ * names are plain scalars; anything more exotic simply yields no tag.
55
+ */
56
+ export declare function frontmatterName(skillMd: string): string | null;
57
+ /** Last path segment of a registry-style skill id (`owner/repo/slug`). */
58
+ export declare function skillSlug(skillId: string): string;
59
+ export declare function execute(report: ImportReport, source: SeedSource, deps: HarnessPublishDeps, opts?: {
60
+ state?: SeedImportStateStore;
61
+ }): Promise<ImportResult>;