@rubric-protocol/evidence 1.0.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.
@@ -0,0 +1,6 @@
1
+ import type { EvidenceIndex, StorePort } from "./ports.js";
2
+ /** Index reader backed by the read-only SQLite day-shards under `indexDir`. */
3
+ export declare function openIndexReader(indexDir: string): EvidenceIndex;
4
+ /** Store reader over `storeDir`; a row's bundlePath is relative to it. */
5
+ export declare function openStoreReader(storeDir: string): StorePort;
6
+ //# sourceMappingURL=adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAoB,MAAM,YAAY,CAAC;AAE7E,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAM/D;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAkB3D"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Offline adapters (tasks/P5.md): read the SQLite index shards (read-only) and
3
+ * the bundle-store files directly — no network, no HCS, no payment.
4
+ */
5
+ import { readFileSync } from "node:fs";
6
+ import { isAbsolute, relative, resolve, sep } from "node:path";
7
+ import { Index } from "@rubric-protocol/attest-index";
8
+ /** Index reader backed by the read-only SQLite day-shards under `indexDir`. */
9
+ export function openIndexReader(indexDir) {
10
+ const index = new Index(indexDir, { readonly: true });
11
+ return {
12
+ byAgentRange: (agentId, from, to) => index.byAgentRange(agentId, from, to),
13
+ close: () => index.close(),
14
+ };
15
+ }
16
+ /** Store reader over `storeDir`; a row's bundlePath is relative to it. */
17
+ export function openStoreReader(storeDir) {
18
+ const root = resolve(storeDir);
19
+ return {
20
+ get(bundlePath) {
21
+ // Contain reads within storeDir — a hostile bundlePath ("../…") must not
22
+ // escape the store (path-traversal / arbitrary file read).
23
+ const full = resolve(root, bundlePath);
24
+ const rel = relative(root, full);
25
+ if (rel === ".." || rel.startsWith(".." + sep) || isAbsolute(rel)) {
26
+ return undefined;
27
+ }
28
+ try {
29
+ return JSON.parse(readFileSync(full, "utf8"));
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ },
35
+ };
36
+ }
37
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAGtD,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,OAAO;QACL,YAAY,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAC1E,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE;KAC3B,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/B,OAAO;QACL,GAAG,CAAC,UAAkB;YACpB,yEAAyE;YACzE,2DAA2D;YAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YACvC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACjC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClE,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAqB,CAAC;YACpE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { EvidenceBundle, EvidenceIndex, IndexRow, SchemaEpoch, StorePort } from "./ports.js";
2
+ /** Consecutive-run epochs of schemaHash across ordered rows. */
3
+ export declare function buildSchemaChangeLog(rows: IndexRow[]): SchemaEpoch[];
4
+ export interface AssembleOptions {
5
+ agent: string;
6
+ from: string;
7
+ to: string;
8
+ generatedAt: string;
9
+ }
10
+ export declare function assembleEvidence(index: EvidenceIndex, store: StorePort, opts: AssembleOptions): EvidenceBundle;
11
+ //# sourceMappingURL=bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,cAAc,EACd,aAAa,EACb,QAAQ,EACR,WAAW,EACX,SAAS,EACV,MAAM,YAAY,CAAC;AAEpB,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,WAAW,EAAE,CAWpE;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,eAAe,GACpB,cAAc,CAqBhB"}
package/dist/bundle.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Assemble an evidence bundle (tasks/P5.md): DARs + proofs + anchor refs for an
3
+ * agent over a ts range, a continuity report (reusing @rubric-protocol/verify chainCheck,
4
+ * so forks render as branches and gaps as gaps), and a schema-change log.
5
+ */
6
+ import { chainCheck } from "@rubric-protocol/verify";
7
+ /** Consecutive-run epochs of schemaHash across ordered rows. */
8
+ export function buildSchemaChangeLog(rows) {
9
+ const log = [];
10
+ for (const r of rows) {
11
+ const last = log[log.length - 1];
12
+ if (last && last.schemaHash === r.schemaHash) {
13
+ last.count++;
14
+ }
15
+ else {
16
+ log.push({ schemaHash: r.schemaHash, fromDecisionId: r.decisionId, fromTs: r.ts, count: 1 });
17
+ }
18
+ }
19
+ return log;
20
+ }
21
+ export function assembleEvidence(index, store, opts) {
22
+ const rows = index.byAgentRange(opts.agent, opts.from, opts.to);
23
+ const decisions = rows.map((r) => {
24
+ const bundle = store.get(r.bundlePath);
25
+ if (!bundle) {
26
+ throw new Error(`evidence: bundle missing for ${r.decisionId} at ${r.bundlePath}`);
27
+ }
28
+ return bundle;
29
+ });
30
+ return {
31
+ version: "rubric-evidence/0.1",
32
+ agent: opts.agent,
33
+ range: { from: opts.from, to: opts.to },
34
+ generatedAt: opts.generatedAt,
35
+ decisionCount: decisions.length,
36
+ decisions,
37
+ continuity: chainCheck(opts.agent, rows),
38
+ schemaChangeLog: buildSchemaChangeLog(rows),
39
+ };
40
+ }
41
+ //# sourceMappingURL=bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAUrD,gEAAgE;AAChE,MAAM,UAAU,oBAAoB,CAAC,IAAgB;IACnD,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,IAAI,IAAI,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU,EAAE,CAAC;YAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/F,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AASD,MAAM,UAAU,gBAAgB,CAC9B,KAAoB,EACpB,KAAgB,EAChB,IAAqB;IAErB,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAEhE,MAAM,SAAS,GAAuB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACnD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,UAAU,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QACrF,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE,qBAAqB;QAC9B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;QACvC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,aAAa,EAAE,SAAS,CAAC,MAAM;QAC/B,SAAS;QACT,UAAU,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC;QACxC,eAAe,EAAE,oBAAoB,CAAC,IAAI,CAAC;KAC5C,CAAC;AACJ,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI (tasks/P5.md):
4
+ * rubric-evidence export --agent <id> --index <dir> --store <dir> \
5
+ * [--from <ts>] [--to <ts>] [--out <file>] [--summary <file>]
6
+ *
7
+ * Writes the JSON evidence bundle (to --out or stdout) and the plain-text
8
+ * summary (to --summary or stderr). Runs offline against the index shards.
9
+ */
10
+ import { writeFileSync } from "node:fs";
11
+ import { parseArgs } from "node:util";
12
+ import { openIndexReader, openStoreReader } from "./adapters.js";
13
+ import { exportEvidence } from "./export.js";
14
+ const MIN_TS = "0000-01-01T00:00:00.000Z";
15
+ const MAX_TS = "9999-12-31T23:59:59.999Z";
16
+ function usage() {
17
+ process.stderr.write("usage: rubric-evidence export --agent <id> --index <dir> --store <dir> " +
18
+ "[--from <ts>] [--to <ts>] [--out <file>] [--summary <file>]\n");
19
+ process.exit(2);
20
+ }
21
+ function main(argv) {
22
+ const { values } = parseArgs({
23
+ args: argv,
24
+ options: {
25
+ agent: { type: "string" },
26
+ from: { type: "string" },
27
+ to: { type: "string" },
28
+ index: { type: "string" },
29
+ store: { type: "string" },
30
+ out: { type: "string" },
31
+ summary: { type: "string" },
32
+ "generated-at": { type: "string" },
33
+ },
34
+ });
35
+ if (!values.agent || !values.index || !values.store)
36
+ usage();
37
+ const index = openIndexReader(values.index);
38
+ try {
39
+ const { bundle, summary } = exportEvidence(index, openStoreReader(values.store), {
40
+ agent: values.agent,
41
+ from: values.from ?? MIN_TS,
42
+ to: values.to ?? MAX_TS,
43
+ generatedAt: values["generated-at"] ?? new Date().toISOString(),
44
+ });
45
+ const json = JSON.stringify(bundle, null, 2) + "\n";
46
+ if (values.out)
47
+ writeFileSync(values.out, json);
48
+ else
49
+ process.stdout.write(json);
50
+ if (values.summary)
51
+ writeFileSync(values.summary, summary);
52
+ else
53
+ process.stderr.write("\n" + summary);
54
+ }
55
+ finally {
56
+ index.close?.();
57
+ }
58
+ }
59
+ // Support the `export` subcommand verb: `rubric-evidence export --agent ...`.
60
+ const args = process.argv.slice(2);
61
+ main(args[0] === "export" ? args.slice(1) : args);
62
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,GAAG,0BAA0B,CAAC;AAC1C,MAAM,MAAM,GAAG,0BAA0B,CAAC;AAE1C,SAAS,KAAK;IACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,yEAAyE;QACvE,+DAA+D,CAClE,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,IAAI,CAAC,IAAc;IAC1B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC3B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACP,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACtB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACvB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC3B,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,KAAK,EAAE,CAAC;IAE7D,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC/E,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,MAAM;YAC3B,EAAE,EAAE,MAAM,CAAC,EAAE,IAAI,MAAM;YACvB,WAAW,EAAE,MAAM,CAAC,cAAc,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAChE,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;QACpD,IAAI,MAAM,CAAC,GAAG;YAAE,aAAa,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;;YAC3C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEhC,IAAI,MAAM,CAAC,OAAO;YAAE,aAAa,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;;YACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC;IAC5C,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAClB,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Export orchestration: assemble the evidence bundle and render its plain-text
3
+ * summary together (tasks/P5.md).
4
+ */
5
+ import { type AssembleOptions } from "./bundle.js";
6
+ import type { EvidenceBundle, EvidenceIndex, StorePort } from "./ports.js";
7
+ export interface ExportResult {
8
+ bundle: EvidenceBundle;
9
+ summary: string;
10
+ }
11
+ export declare function exportEvidence(index: EvidenceIndex, store: StorePort, opts: AssembleOptions): ExportResult;
12
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../src/export.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAErE,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE3E,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,eAAe,GACpB,YAAY,CAGd"}
package/dist/export.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Export orchestration: assemble the evidence bundle and render its plain-text
3
+ * summary together (tasks/P5.md).
4
+ */
5
+ import { assembleEvidence } from "./bundle.js";
6
+ import { renderSummary } from "./summary.js";
7
+ export function exportEvidence(index, store, opts) {
8
+ const bundle = assembleEvidence(index, store, opts);
9
+ return { bundle, summary: renderSummary(bundle) };
10
+ }
11
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../src/export.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAQ7C,MAAM,UAAU,cAAc,CAC5B,KAAoB,EACpB,KAAgB,EAChB,IAAqB;IAErB,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACpD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;AACpD,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @rubric-protocol/evidence — offline evidence export (tasks/P5.md).
3
+ *
4
+ * Assembles a JSON evidence bundle (DARs, proofs, anchor refs, continuity
5
+ * report, schema-change log) plus a plain-text summary, reading the SQLite index
6
+ * shards and bundle store directly (no network). Exposed as the CLI
7
+ * `rubric-evidence export`.
8
+ */
9
+ export { exportEvidence, type ExportResult } from "./export.js";
10
+ export { assembleEvidence, buildSchemaChangeLog, type AssembleOptions } from "./bundle.js";
11
+ export { renderSummary } from "./summary.js";
12
+ export { openIndexReader, openStoreReader } from "./adapters.js";
13
+ export type { EvidenceBundle, EvidenceIndex, StorePort, DecisionEvidence, SchemaEpoch, ContinuityReport, IndexRow, VerifiableBundle, } from "./ports.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EACV,cAAc,EACd,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @rubric-protocol/evidence — offline evidence export (tasks/P5.md).
3
+ *
4
+ * Assembles a JSON evidence bundle (DARs, proofs, anchor refs, continuity
5
+ * report, schema-change log) plus a plain-text summary, reading the SQLite index
6
+ * shards and bundle store directly (no network). Exposed as the CLI
7
+ * `rubric-evidence export`.
8
+ */
9
+ export { exportEvidence } from "./export.js";
10
+ export { assembleEvidence, buildSchemaChangeLog } from "./bundle.js";
11
+ export { renderSummary } from "./summary.js";
12
+ export { openIndexReader, openStoreReader } from "./adapters.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAqB,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAwB,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Ports for evidence export (tasks/P5.md). The assembly logic depends only on
3
+ * these, so tests inject mocks and the CLI injects real adapters that read the
4
+ * SQLite index shards + bundle-store files offline.
5
+ */
6
+ import type { IndexRow, StorePort, VerifiableBundle, ContinuityReport } from "@rubric-protocol/verify";
7
+ export type { IndexRow, StorePort, VerifiableBundle, ContinuityReport };
8
+ /** Index reader: an agent's rows within a ts range, ordered (ts, decisionId). */
9
+ export interface EvidenceIndex {
10
+ byAgentRange(agentId: string, fromTs: string, toTs: string): IndexRow[];
11
+ close?(): void;
12
+ }
13
+ /** One decision's evidence: the DAR plus its proof, anchor ref, and signature. */
14
+ export type DecisionEvidence = VerifiableBundle;
15
+ /** A schema epoch: a run of consecutive decisions under one schemaHash. */
16
+ export interface SchemaEpoch {
17
+ schemaHash: string;
18
+ fromDecisionId: string;
19
+ fromTs: string;
20
+ count: number;
21
+ }
22
+ export interface EvidenceBundle {
23
+ version: "rubric-evidence/0.1";
24
+ agent: string;
25
+ range: {
26
+ from: string;
27
+ to: string;
28
+ };
29
+ generatedAt: string;
30
+ decisionCount: number;
31
+ decisions: DecisionEvidence[];
32
+ continuity: ContinuityReport;
33
+ schemaChangeLog: SchemaEpoch[];
34
+ }
35
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEvG,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;AAExE,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC;IACxE,KAAK,CAAC,IAAI,IAAI,CAAC;CAChB;AAED,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAEhD,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,eAAe,EAAE,WAAW,EAAE,CAAC;CAChC"}
package/dist/ports.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Plain-text summary of an evidence bundle (tasks/P5.md). Deterministic — every
3
+ * line is derived from bundle fields, so it is stable for golden comparison.
4
+ */
5
+ import type { EvidenceBundle } from "./ports.js";
6
+ export declare function renderSummary(bundle: EvidenceBundle): string;
7
+ //# sourceMappingURL=summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../src/summary.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAwC5D"}
@@ -0,0 +1,38 @@
1
+ export function renderSummary(bundle) {
2
+ const lines = [];
3
+ lines.push("Rubric Decision Evidence");
4
+ lines.push(` version: ${bundle.version}`);
5
+ lines.push(` agent: ${bundle.agent}`);
6
+ lines.push(` range: ${bundle.range.from} .. ${bundle.range.to}`);
7
+ lines.push(` generated: ${bundle.generatedAt}`);
8
+ lines.push(` decisions: ${bundle.decisionCount}`);
9
+ const c = bundle.continuity;
10
+ const shape = c.linear
11
+ ? "linear"
12
+ : [
13
+ c.branches.length ? `${c.branches.length} fork(s)` : "",
14
+ c.gaps.length ? `${c.gaps.length} gap(s)` : "",
15
+ ]
16
+ .filter(Boolean)
17
+ .join(", ") || "non-linear";
18
+ lines.push("");
19
+ lines.push(`Continuity: ${shape} (tampering: ${c.tampering})`);
20
+ lines.push(` genesis: ${c.genesis.join(", ") || "(none)"}`);
21
+ lines.push(` heads: ${c.heads.join(", ") || "(none)"}`);
22
+ for (const b of c.branches) {
23
+ lines.push(` fork at ${b.parent} -> ${b.children.join(", ")}`);
24
+ }
25
+ for (const g of c.gaps) {
26
+ lines.push(` gap: ${g.decisionId} references missing ${g.missingPrev}`);
27
+ }
28
+ lines.push("");
29
+ lines.push(`Schema epochs: ${bundle.schemaChangeLog.length}`);
30
+ for (const e of bundle.schemaChangeLog) {
31
+ lines.push(` ${e.schemaHash} from ${e.fromDecisionId} @ ${e.fromTs} (${e.count} decision(s))`);
32
+ }
33
+ const anchors = new Set(bundle.decisions.map((d) => `${d.anchorRef.network}:${d.anchorRef.topicId}`));
34
+ lines.push("");
35
+ lines.push(`Anchors: ${[...anchors].sort().join(", ") || "(none)"}`);
36
+ return lines.join("\n") + "\n";
37
+ }
38
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../src/summary.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@rubric-protocol/evidence",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Rubric evidence export CLI: offline evidence bundles from index shards + bundle store",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/0xsims/rubric-attest.git",
10
+ "directory": "packages/evidence"
11
+ },
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "development": "./src/index.ts",
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "bin": {
22
+ "rubric-evidence": "./dist/cli.js"
23
+ },
24
+ "files": ["dist", "src"],
25
+ "publishConfig": { "access": "public" },
26
+ "scripts": {
27
+ "prepublishOnly": "npm run build",
28
+ "build": "tsc -p tsconfig.build.json",
29
+ "test": "vitest run"
30
+ },
31
+ "dependencies": {
32
+ "@rubric-protocol/attest-decision": "^1.0.0",
33
+ "@rubric-protocol/attest-index": "^1.0.0",
34
+ "@rubric-protocol/verify": "^1.0.0"
35
+ }
36
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Offline adapters (tasks/P5.md): read the SQLite index shards (read-only) and
3
+ * the bundle-store files directly — no network, no HCS, no payment.
4
+ */
5
+ import { readFileSync } from "node:fs";
6
+ import { isAbsolute, relative, resolve, sep } from "node:path";
7
+ import { Index } from "@rubric-protocol/attest-index";
8
+ import type { EvidenceIndex, StorePort, VerifiableBundle } from "./ports.js";
9
+
10
+ /** Index reader backed by the read-only SQLite day-shards under `indexDir`. */
11
+ export function openIndexReader(indexDir: string): EvidenceIndex {
12
+ const index = new Index(indexDir, { readonly: true });
13
+ return {
14
+ byAgentRange: (agentId, from, to) => index.byAgentRange(agentId, from, to),
15
+ close: () => index.close(),
16
+ };
17
+ }
18
+
19
+ /** Store reader over `storeDir`; a row's bundlePath is relative to it. */
20
+ export function openStoreReader(storeDir: string): StorePort {
21
+ const root = resolve(storeDir);
22
+ return {
23
+ get(bundlePath: string): VerifiableBundle | undefined {
24
+ // Contain reads within storeDir — a hostile bundlePath ("../…") must not
25
+ // escape the store (path-traversal / arbitrary file read).
26
+ const full = resolve(root, bundlePath);
27
+ const rel = relative(root, full);
28
+ if (rel === ".." || rel.startsWith(".." + sep) || isAbsolute(rel)) {
29
+ return undefined;
30
+ }
31
+ try {
32
+ return JSON.parse(readFileSync(full, "utf8")) as VerifiableBundle;
33
+ } catch {
34
+ return undefined;
35
+ }
36
+ },
37
+ };
38
+ }
package/src/bundle.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Assemble an evidence bundle (tasks/P5.md): DARs + proofs + anchor refs for an
3
+ * agent over a ts range, a continuity report (reusing @rubric-protocol/verify chainCheck,
4
+ * so forks render as branches and gaps as gaps), and a schema-change log.
5
+ */
6
+ import { chainCheck } from "@rubric-protocol/verify";
7
+ import type {
8
+ DecisionEvidence,
9
+ EvidenceBundle,
10
+ EvidenceIndex,
11
+ IndexRow,
12
+ SchemaEpoch,
13
+ StorePort,
14
+ } from "./ports.js";
15
+
16
+ /** Consecutive-run epochs of schemaHash across ordered rows. */
17
+ export function buildSchemaChangeLog(rows: IndexRow[]): SchemaEpoch[] {
18
+ const log: SchemaEpoch[] = [];
19
+ for (const r of rows) {
20
+ const last = log[log.length - 1];
21
+ if (last && last.schemaHash === r.schemaHash) {
22
+ last.count++;
23
+ } else {
24
+ log.push({ schemaHash: r.schemaHash, fromDecisionId: r.decisionId, fromTs: r.ts, count: 1 });
25
+ }
26
+ }
27
+ return log;
28
+ }
29
+
30
+ export interface AssembleOptions {
31
+ agent: string;
32
+ from: string;
33
+ to: string;
34
+ generatedAt: string;
35
+ }
36
+
37
+ export function assembleEvidence(
38
+ index: EvidenceIndex,
39
+ store: StorePort,
40
+ opts: AssembleOptions,
41
+ ): EvidenceBundle {
42
+ const rows = index.byAgentRange(opts.agent, opts.from, opts.to);
43
+
44
+ const decisions: DecisionEvidence[] = rows.map((r) => {
45
+ const bundle = store.get(r.bundlePath);
46
+ if (!bundle) {
47
+ throw new Error(`evidence: bundle missing for ${r.decisionId} at ${r.bundlePath}`);
48
+ }
49
+ return bundle;
50
+ });
51
+
52
+ return {
53
+ version: "rubric-evidence/0.1",
54
+ agent: opts.agent,
55
+ range: { from: opts.from, to: opts.to },
56
+ generatedAt: opts.generatedAt,
57
+ decisionCount: decisions.length,
58
+ decisions,
59
+ continuity: chainCheck(opts.agent, rows),
60
+ schemaChangeLog: buildSchemaChangeLog(rows),
61
+ };
62
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI (tasks/P5.md):
4
+ * rubric-evidence export --agent <id> --index <dir> --store <dir> \
5
+ * [--from <ts>] [--to <ts>] [--out <file>] [--summary <file>]
6
+ *
7
+ * Writes the JSON evidence bundle (to --out or stdout) and the plain-text
8
+ * summary (to --summary or stderr). Runs offline against the index shards.
9
+ */
10
+ import { writeFileSync } from "node:fs";
11
+ import { parseArgs } from "node:util";
12
+ import { openIndexReader, openStoreReader } from "./adapters.js";
13
+ import { exportEvidence } from "./export.js";
14
+
15
+ const MIN_TS = "0000-01-01T00:00:00.000Z";
16
+ const MAX_TS = "9999-12-31T23:59:59.999Z";
17
+
18
+ function usage(): never {
19
+ process.stderr.write(
20
+ "usage: rubric-evidence export --agent <id> --index <dir> --store <dir> " +
21
+ "[--from <ts>] [--to <ts>] [--out <file>] [--summary <file>]\n",
22
+ );
23
+ process.exit(2);
24
+ }
25
+
26
+ function main(argv: string[]): void {
27
+ const { values } = parseArgs({
28
+ args: argv,
29
+ options: {
30
+ agent: { type: "string" },
31
+ from: { type: "string" },
32
+ to: { type: "string" },
33
+ index: { type: "string" },
34
+ store: { type: "string" },
35
+ out: { type: "string" },
36
+ summary: { type: "string" },
37
+ "generated-at": { type: "string" },
38
+ },
39
+ });
40
+
41
+ if (!values.agent || !values.index || !values.store) usage();
42
+
43
+ const index = openIndexReader(values.index);
44
+ try {
45
+ const { bundle, summary } = exportEvidence(index, openStoreReader(values.store), {
46
+ agent: values.agent,
47
+ from: values.from ?? MIN_TS,
48
+ to: values.to ?? MAX_TS,
49
+ generatedAt: values["generated-at"] ?? new Date().toISOString(),
50
+ });
51
+
52
+ const json = JSON.stringify(bundle, null, 2) + "\n";
53
+ if (values.out) writeFileSync(values.out, json);
54
+ else process.stdout.write(json);
55
+
56
+ if (values.summary) writeFileSync(values.summary, summary);
57
+ else process.stderr.write("\n" + summary);
58
+ } finally {
59
+ index.close?.();
60
+ }
61
+ }
62
+
63
+ // Support the `export` subcommand verb: `rubric-evidence export --agent ...`.
64
+ const args = process.argv.slice(2);
65
+ main(args[0] === "export" ? args.slice(1) : args);
package/src/export.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Export orchestration: assemble the evidence bundle and render its plain-text
3
+ * summary together (tasks/P5.md).
4
+ */
5
+ import { assembleEvidence, type AssembleOptions } from "./bundle.js";
6
+ import { renderSummary } from "./summary.js";
7
+ import type { EvidenceBundle, EvidenceIndex, StorePort } from "./ports.js";
8
+
9
+ export interface ExportResult {
10
+ bundle: EvidenceBundle;
11
+ summary: string;
12
+ }
13
+
14
+ export function exportEvidence(
15
+ index: EvidenceIndex,
16
+ store: StorePort,
17
+ opts: AssembleOptions,
18
+ ): ExportResult {
19
+ const bundle = assembleEvidence(index, store, opts);
20
+ return { bundle, summary: renderSummary(bundle) };
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @rubric-protocol/evidence — offline evidence export (tasks/P5.md).
3
+ *
4
+ * Assembles a JSON evidence bundle (DARs, proofs, anchor refs, continuity
5
+ * report, schema-change log) plus a plain-text summary, reading the SQLite index
6
+ * shards and bundle store directly (no network). Exposed as the CLI
7
+ * `rubric-evidence export`.
8
+ */
9
+ export { exportEvidence, type ExportResult } from "./export.js";
10
+ export { assembleEvidence, buildSchemaChangeLog, type AssembleOptions } from "./bundle.js";
11
+ export { renderSummary } from "./summary.js";
12
+ export { openIndexReader, openStoreReader } from "./adapters.js";
13
+ export type {
14
+ EvidenceBundle,
15
+ EvidenceIndex,
16
+ StorePort,
17
+ DecisionEvidence,
18
+ SchemaEpoch,
19
+ ContinuityReport,
20
+ IndexRow,
21
+ VerifiableBundle,
22
+ } from "./ports.js";
package/src/ports.ts ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Ports for evidence export (tasks/P5.md). The assembly logic depends only on
3
+ * these, so tests inject mocks and the CLI injects real adapters that read the
4
+ * SQLite index shards + bundle-store files offline.
5
+ */
6
+ import type { IndexRow, StorePort, VerifiableBundle, ContinuityReport } from "@rubric-protocol/verify";
7
+
8
+ export type { IndexRow, StorePort, VerifiableBundle, ContinuityReport };
9
+
10
+ /** Index reader: an agent's rows within a ts range, ordered (ts, decisionId). */
11
+ export interface EvidenceIndex {
12
+ byAgentRange(agentId: string, fromTs: string, toTs: string): IndexRow[];
13
+ close?(): void;
14
+ }
15
+
16
+ /** One decision's evidence: the DAR plus its proof, anchor ref, and signature. */
17
+ export type DecisionEvidence = VerifiableBundle;
18
+
19
+ /** A schema epoch: a run of consecutive decisions under one schemaHash. */
20
+ export interface SchemaEpoch {
21
+ schemaHash: string;
22
+ fromDecisionId: string;
23
+ fromTs: string;
24
+ count: number;
25
+ }
26
+
27
+ export interface EvidenceBundle {
28
+ version: "rubric-evidence/0.1";
29
+ agent: string;
30
+ range: { from: string; to: string };
31
+ generatedAt: string;
32
+ decisionCount: number;
33
+ decisions: DecisionEvidence[];
34
+ continuity: ContinuityReport;
35
+ schemaChangeLog: SchemaEpoch[];
36
+ }
package/src/summary.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Plain-text summary of an evidence bundle (tasks/P5.md). Deterministic — every
3
+ * line is derived from bundle fields, so it is stable for golden comparison.
4
+ */
5
+ import type { EvidenceBundle } from "./ports.js";
6
+
7
+ export function renderSummary(bundle: EvidenceBundle): string {
8
+ const lines: string[] = [];
9
+ lines.push("Rubric Decision Evidence");
10
+ lines.push(` version: ${bundle.version}`);
11
+ lines.push(` agent: ${bundle.agent}`);
12
+ lines.push(` range: ${bundle.range.from} .. ${bundle.range.to}`);
13
+ lines.push(` generated: ${bundle.generatedAt}`);
14
+ lines.push(` decisions: ${bundle.decisionCount}`);
15
+
16
+ const c = bundle.continuity;
17
+ const shape = c.linear
18
+ ? "linear"
19
+ : [
20
+ c.branches.length ? `${c.branches.length} fork(s)` : "",
21
+ c.gaps.length ? `${c.gaps.length} gap(s)` : "",
22
+ ]
23
+ .filter(Boolean)
24
+ .join(", ") || "non-linear";
25
+ lines.push("");
26
+ lines.push(`Continuity: ${shape} (tampering: ${c.tampering})`);
27
+ lines.push(` genesis: ${c.genesis.join(", ") || "(none)"}`);
28
+ lines.push(` heads: ${c.heads.join(", ") || "(none)"}`);
29
+ for (const b of c.branches) {
30
+ lines.push(` fork at ${b.parent} -> ${b.children.join(", ")}`);
31
+ }
32
+ for (const g of c.gaps) {
33
+ lines.push(` gap: ${g.decisionId} references missing ${g.missingPrev}`);
34
+ }
35
+
36
+ lines.push("");
37
+ lines.push(`Schema epochs: ${bundle.schemaChangeLog.length}`);
38
+ for (const e of bundle.schemaChangeLog) {
39
+ lines.push(` ${e.schemaHash} from ${e.fromDecisionId} @ ${e.fromTs} (${e.count} decision(s))`);
40
+ }
41
+
42
+ const anchors = new Set(bundle.decisions.map((d) => `${d.anchorRef.network}:${d.anchorRef.topicId}`));
43
+ lines.push("");
44
+ lines.push(`Anchors: ${[...anchors].sort().join(", ") || "(none)"}`);
45
+
46
+ return lines.join("\n") + "\n";
47
+ }