daftari 1.20.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +89 -0
- package/README.md +4 -1
- package/dist/access/rbac.d.ts +1 -0
- package/dist/access/rbac.d.ts.map +1 -1
- package/dist/access/rbac.js +7 -0
- package/dist/access/rbac.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/consolidate/birth.d.ts +74 -0
- package/dist/consolidate/birth.d.ts.map +1 -0
- package/dist/consolidate/birth.js +253 -0
- package/dist/consolidate/birth.js.map +1 -0
- package/dist/consolidate/clocks.d.ts +15 -0
- package/dist/consolidate/clocks.d.ts.map +1 -0
- package/dist/consolidate/clocks.js +127 -0
- package/dist/consolidate/clocks.js.map +1 -0
- package/dist/consolidate/constants.d.ts +26 -0
- package/dist/consolidate/constants.d.ts.map +1 -0
- package/dist/consolidate/constants.js +83 -0
- package/dist/consolidate/constants.js.map +1 -0
- package/dist/consolidate/decorrelation.d.ts +69 -0
- package/dist/consolidate/decorrelation.d.ts.map +1 -0
- package/dist/consolidate/decorrelation.js +287 -0
- package/dist/consolidate/decorrelation.js.map +1 -0
- package/dist/consolidate/derivation-prompt.d.ts +28 -0
- package/dist/consolidate/derivation-prompt.d.ts.map +1 -0
- package/dist/consolidate/derivation-prompt.js +72 -0
- package/dist/consolidate/derivation-prompt.js.map +1 -0
- package/dist/consolidate/edge-write.d.ts +10 -0
- package/dist/consolidate/edge-write.d.ts.map +1 -0
- package/dist/consolidate/edge-write.js +76 -0
- package/dist/consolidate/edge-write.js.map +1 -0
- package/dist/consolidate/index.d.ts +2 -0
- package/dist/consolidate/index.d.ts.map +1 -0
- package/dist/consolidate/index.js +477 -0
- package/dist/consolidate/index.js.map +1 -0
- package/dist/consolidate/priority.d.ts +25 -0
- package/dist/consolidate/priority.d.ts.map +1 -0
- package/dist/consolidate/priority.js +103 -0
- package/dist/consolidate/priority.js.map +1 -0
- package/dist/consolidate/revision.d.ts +75 -0
- package/dist/consolidate/revision.d.ts.map +1 -0
- package/dist/consolidate/revision.js +232 -0
- package/dist/consolidate/revision.js.map +1 -0
- package/dist/consolidate/state.d.ts +10 -0
- package/dist/consolidate/state.d.ts.map +1 -0
- package/dist/consolidate/state.js +54 -0
- package/dist/consolidate/state.js.map +1 -0
- package/dist/curation/edges.d.ts +5 -0
- package/dist/curation/edges.d.ts.map +1 -1
- package/dist/curation/edges.js +82 -9
- package/dist/curation/edges.js.map +1 -1
- package/dist/curation/lint.d.ts +2 -0
- package/dist/curation/lint.d.ts.map +1 -1
- package/dist/curation/lint.js +5 -0
- package/dist/curation/lint.js.map +1 -1
- package/dist/curation/provenance.d.ts +1 -0
- package/dist/curation/provenance.d.ts.map +1 -1
- package/dist/curation/provenance.js +1 -0
- package/dist/curation/provenance.js.map +1 -1
- package/dist/curation/shadow.d.ts +58 -0
- package/dist/curation/shadow.d.ts.map +1 -0
- package/dist/curation/shadow.js +221 -0
- package/dist/curation/shadow.js.map +1 -0
- package/dist/eval/llm.d.ts +3 -1
- package/dist/eval/llm.d.ts.map +1 -1
- package/dist/eval/llm.js +6 -2
- package/dist/eval/llm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/storage/index-db.d.ts +1 -0
- package/dist/storage/index-db.d.ts.map +1 -1
- package/dist/storage/index-db.js +14 -9
- package/dist/storage/index-db.js.map +1 -1
- package/dist/tools/curation.d.ts +2 -0
- package/dist/tools/curation.d.ts.map +1 -1
- package/dist/tools/curation.js +5 -1
- package/dist/tools/curation.js.map +1 -1
- package/dist/tools/edges.d.ts.map +1 -1
- package/dist/tools/edges.js +14 -10
- package/dist/tools/edges.js.map +1 -1
- package/dist/tools/staged-actions.d.ts +1 -0
- package/dist/tools/staged-actions.d.ts.map +1 -1
- package/dist/tools/staged-actions.js +18 -5
- package/dist/tools/staged-actions.js.map +1 -1
- package/dist/tools/write.d.ts +1 -0
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +93 -7
- package/dist/tools/write.js.map +1 -1
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +17 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/git.d.ts +1 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +19 -0
- package/dist/utils/git.js.map +1 -1
- package/package.json +1 -1
- package/templates/config.yaml +3 -0
- package/templates/reviewer-vault/.daftari/config.yaml +3 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type ContestEdgeInput, type DerivesFromEdge, type ObserveEdgeInput } from "../curation/edges.js";
|
|
2
|
+
import type { LlmClient } from "../eval/llm.js";
|
|
3
|
+
import { type Result } from "../frontmatter/types.js";
|
|
4
|
+
import { type ConsolidatePromptTemplate } from "./constants.js";
|
|
5
|
+
export interface RevisionDeps {
|
|
6
|
+
llm: LlmClient;
|
|
7
|
+
loadDoc: (path: string) => Promise<Result<{
|
|
8
|
+
path: string;
|
|
9
|
+
content: string;
|
|
10
|
+
}, Error>>;
|
|
11
|
+
observe: (input: ObserveEdgeInput) => Promise<Result<DerivesFromEdge, Error>>;
|
|
12
|
+
contest: (input: ContestEdgeInput) => Promise<Result<DerivesFromEdge, Error>>;
|
|
13
|
+
recordRevisionTrace: (row: RevisionTraceRow) => Promise<Result<void, Error>>;
|
|
14
|
+
}
|
|
15
|
+
export interface RevisionOpts {
|
|
16
|
+
vaultRoot: string;
|
|
17
|
+
agent: string;
|
|
18
|
+
panelSize: number;
|
|
19
|
+
budgetRemaining: number;
|
|
20
|
+
model: string;
|
|
21
|
+
}
|
|
22
|
+
export interface RevisionVote {
|
|
23
|
+
axis: ConsolidatePromptTemplate;
|
|
24
|
+
verdict: "survives" | "fails";
|
|
25
|
+
reason: string;
|
|
26
|
+
}
|
|
27
|
+
export interface RevisionVoteError {
|
|
28
|
+
axis: ConsolidatePromptTemplate;
|
|
29
|
+
error: string;
|
|
30
|
+
}
|
|
31
|
+
export type RevisionDecision = "survives" | "fails" | "tie" | "no-vote";
|
|
32
|
+
export interface RevisionTraceRow {
|
|
33
|
+
at: string;
|
|
34
|
+
fromPath: string;
|
|
35
|
+
toPath: string;
|
|
36
|
+
edgeStrengthAtStart: number;
|
|
37
|
+
kSurvivedAtStart: number;
|
|
38
|
+
lastRederivedAtStart: string;
|
|
39
|
+
model: string;
|
|
40
|
+
votes: Array<RevisionVote | RevisionVoteError>;
|
|
41
|
+
decision: RevisionDecision;
|
|
42
|
+
observedCount: number;
|
|
43
|
+
contestedCount: number;
|
|
44
|
+
}
|
|
45
|
+
export interface RevisionOutcome {
|
|
46
|
+
fromPath: string;
|
|
47
|
+
toPath: string;
|
|
48
|
+
votes: Array<RevisionVote | RevisionVoteError>;
|
|
49
|
+
decision: RevisionDecision;
|
|
50
|
+
observedCount: number;
|
|
51
|
+
contestedCount: number;
|
|
52
|
+
writeErrors: Array<{
|
|
53
|
+
axis: ConsolidatePromptTemplate;
|
|
54
|
+
error: string;
|
|
55
|
+
}>;
|
|
56
|
+
llmCalls: number;
|
|
57
|
+
inputTokens: number;
|
|
58
|
+
outputTokens: number;
|
|
59
|
+
traceWritten: boolean;
|
|
60
|
+
traceError?: string;
|
|
61
|
+
}
|
|
62
|
+
export declare function parseRevisionVerdict(raw: unknown): Result<{
|
|
63
|
+
verdict: "survives" | "fails";
|
|
64
|
+
reason: string;
|
|
65
|
+
}, Error>;
|
|
66
|
+
export declare function revisionPanel(edge: {
|
|
67
|
+
fromPath: string;
|
|
68
|
+
toPath: string;
|
|
69
|
+
strength: number;
|
|
70
|
+
kSurvived: number;
|
|
71
|
+
lastRederived: string;
|
|
72
|
+
}, deps: RevisionDeps, opts: RevisionOpts): Promise<Result<RevisionOutcome, Error>>;
|
|
73
|
+
export declare function revisionTracePath(vaultRoot: string): string;
|
|
74
|
+
export declare function appendRevisionTrace(vaultRoot: string, row: RevisionTraceRow): Promise<Result<void, Error>>;
|
|
75
|
+
//# sourceMappingURL=revision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revision.d.ts","sourceRoot":"","sources":["../../src/consolidate/revision.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAgC,KAAK,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAI9F,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,SAAS,CAAC;IAGf,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,KAAK,CAAC,CAAC,CAAC;IACrF,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC,CAAC;IAC9E,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC,CAAC;IAC9E,mBAAmB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;CAC9E;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,yBAAyB,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAID,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,YAAY,GAAG,iBAAiB,CAAC,CAAC;IAG/C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC,YAAY,GAAG,iBAAiB,CAAC,CAAC;IAC/C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IAKvB,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,yBAAyB,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,GACX,MAAM,CAAC;IAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,CAAC,CAgBlE;AA6DD,wBAAsB,aAAa,CACjC,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,EACD,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC,CA8HzC;AAID,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAU9B"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// Revision mode (spec §4.1, brief item 2). Cast a panel of M independent votes
|
|
2
|
+
// on a due edge — each vote uses a distinct prompt template so §11.3's replay
|
|
3
|
+
// guard counts them all as independent in one sitting. Each vote either
|
|
4
|
+
// survives (→ edge_observe, strength accrues) or fails-case-2 (→ edge_contest,
|
|
5
|
+
// revoke + tension). The §11.3 replay guard handles "same (observer, axis)
|
|
6
|
+
// pair within EDGE_REPLAY_GAP_DAYS"; this code uses distinct axes within the
|
|
7
|
+
// panel so the gap never trips.
|
|
8
|
+
//
|
|
9
|
+
// Case-1 vs case-2 simplification (v1): the LLM is asked to produce
|
|
10
|
+
// survives | fails — we treat every "fails" as case-2 (contest). The spec
|
|
11
|
+
// distinguishes case-1 (an endpoint *changed*, so failure isn't a fault of the
|
|
12
|
+
// derivation) but C's event clock already marks such edges due via git-diff;
|
|
13
|
+
// running the panel on them is the same retry path. If shadow data shows we're
|
|
14
|
+
// contesting edges whose endpoints had recent commits, add a case-1 detector
|
|
15
|
+
// (`git log --since=<lastRederived>` on each endpoint) and skip the contest.
|
|
16
|
+
// Tracked for Stage 5 calibration.
|
|
17
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
18
|
+
import { join, posix } from "node:path";
|
|
19
|
+
import { EDGE_AXES, } from "../curation/edges.js";
|
|
20
|
+
import { err, ok } from "../frontmatter/types.js";
|
|
21
|
+
import { CONSOLIDATE_PROMPT_TEMPLATES } from "./constants.js";
|
|
22
|
+
// --- canon -------------------------------------------------------------------
|
|
23
|
+
function canon(p) {
|
|
24
|
+
return posix.normalize(p).replace(/^\.\//, "");
|
|
25
|
+
}
|
|
26
|
+
// --- verdict parsing ---------------------------------------------------------
|
|
27
|
+
const VALID_REVISION_VERDICTS = new Set(["survives", "fails"]);
|
|
28
|
+
export function parseRevisionVerdict(raw) {
|
|
29
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
30
|
+
return err(new Error("revision verdict: expected object"));
|
|
31
|
+
}
|
|
32
|
+
const obj = raw;
|
|
33
|
+
const v = obj.verdict;
|
|
34
|
+
const r = obj.reason;
|
|
35
|
+
if (typeof v !== "string" || !VALID_REVISION_VERDICTS.has(v)) {
|
|
36
|
+
return err(new Error(`revision verdict: expected 'survives' | 'fails', got ${JSON.stringify(v)}`));
|
|
37
|
+
}
|
|
38
|
+
if (typeof r !== "string" || r.trim().length === 0) {
|
|
39
|
+
return err(new Error("revision verdict: 'reason' is required"));
|
|
40
|
+
}
|
|
41
|
+
return ok({ verdict: v, reason: r });
|
|
42
|
+
}
|
|
43
|
+
// --- prompt construction -----------------------------------------------------
|
|
44
|
+
const VERDICT_SCHEMA = {
|
|
45
|
+
type: "object",
|
|
46
|
+
required: ["verdict", "reason"],
|
|
47
|
+
properties: {
|
|
48
|
+
verdict: { enum: ["survives", "fails"] },
|
|
49
|
+
reason: { type: "string", minLength: 1 },
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
const SYSTEM_BASE = "You re-evaluate whether one document's central claim still derives from another. " +
|
|
53
|
+
"You have re-derived this edge before — your job is to do it independently now, " +
|
|
54
|
+
"from the documents alone. Be conservative: only return 'survives' if the dependence is " +
|
|
55
|
+
"clear and load-bearing in the current text. Return 'fails' if the premise no longer " +
|
|
56
|
+
"supports the conclusion as previously claimed.";
|
|
57
|
+
const MAX_DOC_CHARS = 1500;
|
|
58
|
+
function truncate(s) {
|
|
59
|
+
return s.length <= MAX_DOC_CHARS ? s : `${s.slice(0, MAX_DOC_CHARS)}\n…[truncated]`;
|
|
60
|
+
}
|
|
61
|
+
// User body. The leading `[template:NAME]` marker is the post-hoc audit hook —
|
|
62
|
+
// it's how the trace + the decorrelation report (brief item 8) reconstruct
|
|
63
|
+
// which template each vote used without parsing prose. Tests rely on it too.
|
|
64
|
+
function userBody(axis, from, fromContent, to, toContent, edge) {
|
|
65
|
+
const tag = `[template:${axis}]`;
|
|
66
|
+
const meta = `Edge: ${from} derives_from ${to}. k_survived=${edge.kSurvived}, last_rederived=${edge.lastRederived}.`;
|
|
67
|
+
const fromBlock = `DOC FROM (${from}):\n${truncate(fromContent)}`;
|
|
68
|
+
const toBlock = `DOC TO (${to}):\n${truncate(toContent)}`;
|
|
69
|
+
switch (axis) {
|
|
70
|
+
case "forward":
|
|
71
|
+
return `${tag}\n${meta}\n\n${fromBlock}\n\n${toBlock}\n\nDoes DOC FROM still derive its central claim from DOC TO? Return JSON.`;
|
|
72
|
+
case "reverse":
|
|
73
|
+
return `${tag}\n${meta}\n\n${toBlock}\n\n${fromBlock}\n\nIs DOC FROM's central claim still load-bearing on DOC TO? Return JSON.`;
|
|
74
|
+
case "contrast":
|
|
75
|
+
return `${tag}\n${meta}\n\n${fromBlock}\n\n${toBlock}\n\nIf you had to defend or refute the edge "FROM derives_from TO" today, which would you do? Return JSON: 'survives' to defend, 'fails' to refute.`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// --- panel -------------------------------------------------------------------
|
|
79
|
+
// Pick the first N distinct prompt templates so M votes get M distinct
|
|
80
|
+
// (observer, axis) pairs. Validated by the constants test: panelSize cannot
|
|
81
|
+
// exceed CONSOLIDATE_PROMPT_TEMPLATES.length. We still defend at runtime in
|
|
82
|
+
// case a caller passes a runaway value.
|
|
83
|
+
function axesForPanel(panelSize) {
|
|
84
|
+
const k = Math.max(1, Math.min(panelSize, CONSOLIDATE_PROMPT_TEMPLATES.length));
|
|
85
|
+
return CONSOLIDATE_PROMPT_TEMPLATES.slice(0, k);
|
|
86
|
+
}
|
|
87
|
+
export async function revisionPanel(edge, deps, opts) {
|
|
88
|
+
const fromPath = canon(edge.fromPath);
|
|
89
|
+
const toPath = canon(edge.toPath);
|
|
90
|
+
const fromRes = await deps.loadDoc(fromPath);
|
|
91
|
+
if (!fromRes.ok)
|
|
92
|
+
return fromRes;
|
|
93
|
+
const toRes = await deps.loadDoc(toPath);
|
|
94
|
+
if (!toRes.ok)
|
|
95
|
+
return toRes;
|
|
96
|
+
const axes = axesForPanel(opts.panelSize);
|
|
97
|
+
const votes = [];
|
|
98
|
+
const writeErrors = [];
|
|
99
|
+
let llmCalls = 0;
|
|
100
|
+
let inputTokens = 0;
|
|
101
|
+
let outputTokens = 0;
|
|
102
|
+
// --- Phase 1: ELICIT all M votes (no writes). Aggregation must see the whole
|
|
103
|
+
// panel before deciding — applying writes per-vote let a single dissent revoke
|
|
104
|
+
// a healthy edge and a later survive re-seed it (order-dependent churn). ---
|
|
105
|
+
const surviving = [];
|
|
106
|
+
let failsCount = 0;
|
|
107
|
+
let firstFailReason = "re-derivation failed";
|
|
108
|
+
for (const axis of axes) {
|
|
109
|
+
if (llmCalls >= opts.budgetRemaining)
|
|
110
|
+
break;
|
|
111
|
+
const r = await deps.llm.completeJson({
|
|
112
|
+
model: opts.model,
|
|
113
|
+
system: SYSTEM_BASE,
|
|
114
|
+
user: userBody(axis, fromPath, fromRes.value.content, toPath, toRes.value.content, edge),
|
|
115
|
+
schema: VERDICT_SCHEMA,
|
|
116
|
+
});
|
|
117
|
+
llmCalls++;
|
|
118
|
+
if (!r.ok) {
|
|
119
|
+
votes.push({ axis, error: r.error.message });
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
inputTokens += r.value.input_tokens;
|
|
123
|
+
outputTokens += r.value.output_tokens;
|
|
124
|
+
const parsed = parseRevisionVerdict(r.value.parsed);
|
|
125
|
+
if (!parsed.ok) {
|
|
126
|
+
votes.push({ axis, error: parsed.error.message });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
votes.push({ axis, verdict: parsed.value.verdict, reason: parsed.value.reason });
|
|
130
|
+
if (parsed.value.verdict === "survives") {
|
|
131
|
+
surviving.push({ axis, reason: parsed.value.reason });
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
if (failsCount === 0)
|
|
135
|
+
firstFailReason = parsed.value.reason;
|
|
136
|
+
failsCount++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// --- Phase 2: AGGREGATE, then apply ONE decision (majority-decides). ---
|
|
140
|
+
const survivesCount = surviving.length;
|
|
141
|
+
let decision;
|
|
142
|
+
let observedCount = 0;
|
|
143
|
+
let contestedCount = 0;
|
|
144
|
+
if (survivesCount === 0 && failsCount === 0) {
|
|
145
|
+
decision = "no-vote"; // all errored / budget-starved — surface, write nothing
|
|
146
|
+
}
|
|
147
|
+
else if (failsCount > survivesCount) {
|
|
148
|
+
// Majority fails ⇒ ONE contest (revoke + tension). Satisfies the spec's
|
|
149
|
+
// multi-pass-agreement-for-contests: a lone dissent can no longer revoke.
|
|
150
|
+
decision = "fails";
|
|
151
|
+
const con = await deps.contest({
|
|
152
|
+
fromPath,
|
|
153
|
+
toPath,
|
|
154
|
+
contestedBy: opts.agent,
|
|
155
|
+
reason: `revision panel (${failsCount}/${survivesCount + failsCount} fail): ${firstFailReason}`,
|
|
156
|
+
});
|
|
157
|
+
if (con.ok)
|
|
158
|
+
contestedCount = 1;
|
|
159
|
+
else
|
|
160
|
+
writeErrors.push({ axis: axes[0], error: `contest failed: ${con.error.message}` });
|
|
161
|
+
}
|
|
162
|
+
else if (survivesCount > failsCount) {
|
|
163
|
+
// Majority survives ⇒ accrue strength: each surviving vote observes with a
|
|
164
|
+
// DISTINCT store axis (EDGE_AXES), so the §11.3 replay guard counts them as
|
|
165
|
+
// independent in one sitting (panelSize ≤ EDGE_AXES.length ⇒ no collision).
|
|
166
|
+
decision = "survives";
|
|
167
|
+
for (let i = 0; i < surviving.length; i++) {
|
|
168
|
+
const storeAxis = EDGE_AXES[i % EDGE_AXES.length];
|
|
169
|
+
const obs = await deps.observe({
|
|
170
|
+
fromPath,
|
|
171
|
+
toPath,
|
|
172
|
+
observedBy: opts.agent,
|
|
173
|
+
blind: true,
|
|
174
|
+
axis: storeAxis,
|
|
175
|
+
note: `revision/${surviving[i].axis}: ${surviving[i].reason}`,
|
|
176
|
+
});
|
|
177
|
+
if (obs.ok)
|
|
178
|
+
observedCount++;
|
|
179
|
+
else
|
|
180
|
+
writeErrors.push({
|
|
181
|
+
axis: surviving[i].axis,
|
|
182
|
+
error: `observe failed: ${obs.error.message}`,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// Tie (survives === fails, both > 0): no majority ⇒ surface, write nothing.
|
|
188
|
+
decision = "tie";
|
|
189
|
+
}
|
|
190
|
+
const traceRes = await deps.recordRevisionTrace({
|
|
191
|
+
at: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
192
|
+
fromPath,
|
|
193
|
+
toPath,
|
|
194
|
+
edgeStrengthAtStart: edge.strength,
|
|
195
|
+
kSurvivedAtStart: edge.kSurvived,
|
|
196
|
+
lastRederivedAtStart: edge.lastRederived,
|
|
197
|
+
model: opts.model,
|
|
198
|
+
votes,
|
|
199
|
+
decision,
|
|
200
|
+
observedCount,
|
|
201
|
+
contestedCount,
|
|
202
|
+
});
|
|
203
|
+
return ok({
|
|
204
|
+
fromPath,
|
|
205
|
+
toPath,
|
|
206
|
+
votes,
|
|
207
|
+
decision,
|
|
208
|
+
observedCount,
|
|
209
|
+
contestedCount,
|
|
210
|
+
writeErrors,
|
|
211
|
+
llmCalls,
|
|
212
|
+
inputTokens,
|
|
213
|
+
outputTokens,
|
|
214
|
+
traceWritten: traceRes.ok,
|
|
215
|
+
...(traceRes.ok ? {} : { traceError: traceRes.error.message }),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// --- the CLI-wired trace recorder --------------------------------------------
|
|
219
|
+
export function revisionTracePath(vaultRoot) {
|
|
220
|
+
return join(vaultRoot, ".daftari", "revision-trace.jsonl");
|
|
221
|
+
}
|
|
222
|
+
export async function appendRevisionTrace(vaultRoot, row) {
|
|
223
|
+
try {
|
|
224
|
+
mkdirSync(join(vaultRoot, ".daftari"), { recursive: true });
|
|
225
|
+
appendFileSync(revisionTracePath(vaultRoot), `${JSON.stringify(row)}\n`);
|
|
226
|
+
return ok(undefined);
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
return err(new Error(`cannot record revision trace: ${e instanceof Error ? e.message : String(e)}`));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=revision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revision.js","sourceRoot":"","sources":["../../src/consolidate/revision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Result } from "../frontmatter/types.js";
|
|
2
|
+
export interface ConsolidateState {
|
|
3
|
+
lastConsolidationCommit: string | null;
|
|
4
|
+
birthProcessed: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
export declare function consolidateStatePath(vaultRoot: string): string;
|
|
7
|
+
export declare function docContentHash(content: string): string;
|
|
8
|
+
export declare function readConsolidateState(vaultRoot: string): ConsolidateState;
|
|
9
|
+
export declare function writeConsolidateState(vaultRoot: string, state: ConsolidateState): Result<void, Error>;
|
|
10
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/consolidate/state.ts"],"names":[],"mappings":"AASA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAMD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAID,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAYxE;AAKD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,gBAAgB,GACtB,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAarB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// .daftari/consolidate-state.json — the loop's ephemeral cross-session memory:
|
|
2
|
+
// the last consolidation commit (the event clock's baseline) and the
|
|
3
|
+
// birth-processed doc hashes (so an edited doc re-births, spec §4.0). Git-ignored
|
|
4
|
+
// and rebuildable: absent OR corrupt ⇒ the empty default, and the next session
|
|
5
|
+
// treats HEAD as its baseline (the nil path, §3.1/§7).
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { err, ok } from "../frontmatter/types.js";
|
|
10
|
+
function emptyState() {
|
|
11
|
+
return { lastConsolidationCommit: null, birthProcessed: {} };
|
|
12
|
+
}
|
|
13
|
+
export function consolidateStatePath(vaultRoot) {
|
|
14
|
+
return join(vaultRoot, ".daftari", "consolidate-state.json");
|
|
15
|
+
}
|
|
16
|
+
export function docContentHash(content) {
|
|
17
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
18
|
+
}
|
|
19
|
+
// Absent OR corrupt ⇒ the empty default. The state is ephemeral and rebuildable
|
|
20
|
+
// (an empty lastCommit just means the next session uses HEAD as its baseline).
|
|
21
|
+
export function readConsolidateState(vaultRoot) {
|
|
22
|
+
const p = consolidateStatePath(vaultRoot);
|
|
23
|
+
if (!existsSync(p))
|
|
24
|
+
return emptyState();
|
|
25
|
+
try {
|
|
26
|
+
const raw = JSON.parse(readFileSync(p, "utf-8"));
|
|
27
|
+
return {
|
|
28
|
+
lastConsolidationCommit: raw.lastConsolidationCommit ?? null,
|
|
29
|
+
birthProcessed: raw.birthProcessed ?? {},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return emptyState();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Returns a Result rather than throwing (CLAUDE.md house style): a full disk or
|
|
37
|
+
// a read-only .daftari is a recoverable degrade — the loop's state is ephemeral,
|
|
38
|
+
// so a failed write just means the next session re-baselines, not a crash.
|
|
39
|
+
export function writeConsolidateState(vaultRoot, state) {
|
|
40
|
+
if (typeof vaultRoot !== "string" || vaultRoot.trim().length === 0) {
|
|
41
|
+
return err(new Error("writeConsolidateState requires a non-empty vaultRoot"));
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const p = consolidateStatePath(vaultRoot);
|
|
45
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
46
|
+
writeFileSync(p, `${JSON.stringify(state, null, 2)}\n`);
|
|
47
|
+
return ok(undefined);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
51
|
+
return err(new Error(`cannot write consolidate state: ${reason}`));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/consolidate/state.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,qEAAqE;AACrE,kFAAkF;AAClF,+EAA+E;AAC/E,uDAAuD;AAEvD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,yBAAyB,CAAC;AAQ/D,SAAS,UAAU;IACjB,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,OAAO,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,wBAAwB,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,gFAAgF;AAChF,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,MAAM,CAAC,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,UAAU,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAA8B,CAAC;QAC9E,OAAO;YACL,uBAAuB,EAAE,GAAG,CAAC,uBAAuB,IAAI,IAAI;YAC5D,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,EAAE;SACzC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,iFAAiF;AACjF,2EAA2E;AAC3E,MAAM,UAAU,qBAAqB,CACnC,SAAiB,EACjB,KAAuB;IAEvB,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnE,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAC1C,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,aAAa,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACxD,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1D,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,mCAAmC,MAAM,EAAE,CAAC,CAAC,CAAC;IACrE,CAAC;AACH,CAAC"}
|
package/dist/curation/edges.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export declare const EDGE_AXES: readonly ["prompt", "input-neighborhood", "model
|
|
|
8
8
|
export type EdgeAxis = (typeof EDGE_AXES)[number];
|
|
9
9
|
export declare const EDGE_STATUSES: readonly ["candidate", "trigger-bearing", "revoked"];
|
|
10
10
|
export type EdgeStatus = (typeof EDGE_STATUSES)[number];
|
|
11
|
+
export declare const PREMISE_VOTES: readonly ["from", "to", "symmetric"];
|
|
12
|
+
export type PremiseVote = (typeof PREMISE_VOTES)[number];
|
|
13
|
+
export type DirectionVerdict = "directed" | "symmetric";
|
|
11
14
|
export interface DerivesFromEdge {
|
|
12
15
|
fromPath: string;
|
|
13
16
|
toPath: string;
|
|
@@ -16,6 +19,7 @@ export interface DerivesFromEdge {
|
|
|
16
19
|
firstObserved: string;
|
|
17
20
|
lastRederived: string;
|
|
18
21
|
status: EdgeStatus;
|
|
22
|
+
directionVerdict: DirectionVerdict;
|
|
19
23
|
observations: number;
|
|
20
24
|
contestedAt: string | null;
|
|
21
25
|
contestReason: string | null;
|
|
@@ -27,6 +31,7 @@ export interface ObserveEdgeInput {
|
|
|
27
31
|
blind: boolean;
|
|
28
32
|
axis?: EdgeAxis;
|
|
29
33
|
note?: string;
|
|
34
|
+
premiseVote?: PremiseVote;
|
|
30
35
|
at?: string;
|
|
31
36
|
}
|
|
32
37
|
export interface ContestEdgeInput {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../../src/curation/edges.ts"],"names":[],"mappings":"AA2CA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EAGL,KAAK,OAAO,EAGb,MAAM,wBAAwB,CAAC;AAWhC,eAAO,MAAM,UAAU,IAAI,CAAC;AAK5B,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAKtC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAOzC,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAGtC,eAAO,MAAM,SAAS,oDAAqD,CAAC;AAC5E,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAElD,eAAO,MAAM,aAAa,sDAAuD,CAAC;AAClF,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAOxD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../../src/curation/edges.ts"],"names":[],"mappings":"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"}
|
package/dist/curation/edges.js
CHANGED
|
@@ -69,6 +69,10 @@ export const EDGE_REPLAY_GAP_DAYS = 1;
|
|
|
69
69
|
// Q3: the axes a re-derivation can vary to count as an independent vote.
|
|
70
70
|
export const EDGE_AXES = ["prompt", "input-neighborhood", "model"];
|
|
71
71
|
export const EDGE_STATUSES = ["candidate", "trigger-bearing", "revoked"];
|
|
72
|
+
// Which endpoint an observation judged the load-bearing premise. "to" is the
|
|
73
|
+
// normal directed case (birth orients premise on `to`); a "from"/"to" split or
|
|
74
|
+
// an explicit "symmetric" collapses the edge's direction to unconfirmed.
|
|
75
|
+
export const PREMISE_VOTES = ["from", "to", "symmetric"];
|
|
72
76
|
export function edgesPath(vaultRoot) {
|
|
73
77
|
return join(vaultRoot, ".daftari", "edges.jsonl");
|
|
74
78
|
}
|
|
@@ -132,6 +136,30 @@ function edgeKey(from, to) {
|
|
|
132
136
|
// Paths are vault-relative and never contain a newline; "\n" cannot collide.
|
|
133
137
|
return `${from}\n${to}`;
|
|
134
138
|
}
|
|
139
|
+
// The DURABLE key is canonical (sorted), so the two orientations of a pair —
|
|
140
|
+
// `(x,y)` and `(y,x)` — collapse to ONE edge. Direction is then a derived
|
|
141
|
+
// property of the premise votes, not of key order: this is what makes a
|
|
142
|
+
// post-edit orientation flip a from/to *split* (⇒ symmetric) instead of a
|
|
143
|
+
// second contradictory directed twin. Output orientation is re-derived in
|
|
144
|
+
// deriveEdge.
|
|
145
|
+
function canonPair(from, to) {
|
|
146
|
+
return from <= to ? [from, to] : [to, from];
|
|
147
|
+
}
|
|
148
|
+
// Collapse the cycle's premise votes into a direction verdict (review C1):
|
|
149
|
+
// empty (legacy) or unanimous from/to ⇒ directed; a from/to split or any
|
|
150
|
+
// explicit symmetric vote ⇒ symmetric.
|
|
151
|
+
function directionVerdictOf(votes) {
|
|
152
|
+
if (votes.has("symmetric"))
|
|
153
|
+
return "symmetric";
|
|
154
|
+
if (votes.has("from") && votes.has("to"))
|
|
155
|
+
return "symmetric";
|
|
156
|
+
return "directed";
|
|
157
|
+
}
|
|
158
|
+
function validPremiseVote(v) {
|
|
159
|
+
return typeof v === "string" && PREMISE_VOTES.includes(v)
|
|
160
|
+
? v
|
|
161
|
+
: null;
|
|
162
|
+
}
|
|
135
163
|
// Collapses the append-only log to one current state per (from, to), applying
|
|
136
164
|
// the strength-model rules in FILE order (the order events were appended —
|
|
137
165
|
// the tools only ever append at the current instant, so file order is time
|
|
@@ -156,8 +184,22 @@ function collapse(records) {
|
|
|
156
184
|
const from = rec.from;
|
|
157
185
|
const to = rec.to;
|
|
158
186
|
const at = rec.at;
|
|
159
|
-
const
|
|
187
|
+
const [cFrom, cTo] = canonPair(from, to);
|
|
188
|
+
const key = edgeKey(cFrom, cTo);
|
|
160
189
|
const existing = byKey.get(key);
|
|
190
|
+
// Resolve this record's premise vote (relative to the record's own from/to)
|
|
191
|
+
// to a CANONICAL endpoint vote: "from" = canonical-first is the premise,
|
|
192
|
+
// "to" = canonical-second is the premise. Opposite orientations of the same
|
|
193
|
+
// pair therefore vote on the same canonical axis and a flip becomes a split.
|
|
194
|
+
const rawVote = validPremiseVote(rec.premiseVote);
|
|
195
|
+
let canonVote = null;
|
|
196
|
+
if (rawVote === "symmetric") {
|
|
197
|
+
canonVote = "symmetric";
|
|
198
|
+
}
|
|
199
|
+
else if (rawVote === "from" || rawVote === "to") {
|
|
200
|
+
const premisePath = rawVote === "to" ? to : from;
|
|
201
|
+
canonVote = premisePath === cFrom ? "from" : "to";
|
|
202
|
+
}
|
|
161
203
|
if (rec.kind === "contest") {
|
|
162
204
|
// A contest of an edge that was never observed cannot stand alone.
|
|
163
205
|
if (!existing || existing.revoked)
|
|
@@ -183,8 +225,10 @@ function collapse(records) {
|
|
|
183
225
|
? [`${rec.by}\n${rec.axis}`]
|
|
184
226
|
: [];
|
|
185
227
|
byKey.set(key, {
|
|
186
|
-
fromPath:
|
|
187
|
-
toPath:
|
|
228
|
+
fromPath: cFrom,
|
|
229
|
+
toPath: cTo,
|
|
230
|
+
seedFrom: from,
|
|
231
|
+
seedTo: to,
|
|
188
232
|
kSurvived: 0,
|
|
189
233
|
firstObserved: at,
|
|
190
234
|
lastRederived: at,
|
|
@@ -193,10 +237,13 @@ function collapse(records) {
|
|
|
193
237
|
contestedAt: null,
|
|
194
238
|
contestReason: null,
|
|
195
239
|
votedPairs: new Set(seedPair),
|
|
240
|
+
premiseVotes: new Set(canonVote ? [canonVote] : []),
|
|
196
241
|
});
|
|
197
242
|
continue;
|
|
198
243
|
}
|
|
199
244
|
existing.observations += 1;
|
|
245
|
+
if (canonVote)
|
|
246
|
+
existing.premiseVotes.add(canonVote);
|
|
200
247
|
const qualifying = rec.blind === true &&
|
|
201
248
|
typeof rec.axis === "string" &&
|
|
202
249
|
EDGE_AXES.includes(rec.axis) &&
|
|
@@ -226,14 +273,34 @@ function deriveEdge(state, now) {
|
|
|
226
273
|
: strength >= EDGE_TRIGGER_STRENGTH
|
|
227
274
|
? "trigger-bearing"
|
|
228
275
|
: "candidate";
|
|
276
|
+
// Re-derive the OUTPUT orientation (from=dependent, to=premise). The state's
|
|
277
|
+
// fromPath/toPath are canonical; direction lives in the votes:
|
|
278
|
+
// - symmetric ⇒ keep canonical order (direction unconfirmed, won't propagate)
|
|
279
|
+
// - directed with votes ⇒ premise = the voted canonical endpoint
|
|
280
|
+
// - directed without votes (legacy) ⇒ preserve the seed's original orientation
|
|
281
|
+
const verdict = directionVerdictOf(state.premiseVotes);
|
|
282
|
+
let fromPath = state.fromPath;
|
|
283
|
+
let toPath = state.toPath;
|
|
284
|
+
if (verdict === "directed") {
|
|
285
|
+
if (state.premiseVotes.size === 0) {
|
|
286
|
+
fromPath = state.seedFrom;
|
|
287
|
+
toPath = state.seedTo;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
const premiseIsFrom = state.premiseVotes.has("from"); // unanimous when directed
|
|
291
|
+
fromPath = premiseIsFrom ? state.toPath : state.fromPath; // dependent
|
|
292
|
+
toPath = premiseIsFrom ? state.fromPath : state.toPath; // premise
|
|
293
|
+
}
|
|
294
|
+
}
|
|
229
295
|
return {
|
|
230
|
-
fromPath
|
|
231
|
-
toPath
|
|
296
|
+
fromPath,
|
|
297
|
+
toPath,
|
|
232
298
|
strength,
|
|
233
299
|
kSurvived: state.kSurvived,
|
|
234
300
|
firstObserved: state.firstObserved,
|
|
235
301
|
lastRederived: state.lastRederived,
|
|
236
302
|
status,
|
|
303
|
+
directionVerdict: verdict,
|
|
237
304
|
observations: state.observations,
|
|
238
305
|
contestedAt: state.contestedAt,
|
|
239
306
|
contestReason: state.contestReason,
|
|
@@ -261,6 +328,10 @@ export async function observeEdge(vaultRoot, input) {
|
|
|
261
328
|
if (input.at !== undefined && !Number.isFinite(Date.parse(input.at))) {
|
|
262
329
|
return err(new Error("observeEdge 'at' must be a parseable timestamp"));
|
|
263
330
|
}
|
|
331
|
+
if (input.premiseVote !== undefined &&
|
|
332
|
+
!PREMISE_VOTES.includes(input.premiseVote)) {
|
|
333
|
+
return err(new Error(`observeEdge 'premiseVote' must be one of: ${PREMISE_VOTES.join(", ")}`));
|
|
334
|
+
}
|
|
264
335
|
const record = {
|
|
265
336
|
kind: "observe",
|
|
266
337
|
from: input.fromPath.trim(),
|
|
@@ -270,6 +341,7 @@ export async function observeEdge(vaultRoot, input) {
|
|
|
270
341
|
blind: input.blind,
|
|
271
342
|
axis: input.axis ?? null,
|
|
272
343
|
...(input.note ? { note: input.note } : {}),
|
|
344
|
+
...(input.premiseVote ? { premiseVote: input.premiseVote } : {}),
|
|
273
345
|
};
|
|
274
346
|
try {
|
|
275
347
|
mkdirSync(join(vaultRoot, ".daftari"), { recursive: true });
|
|
@@ -279,7 +351,7 @@ export async function observeEdge(vaultRoot, input) {
|
|
|
279
351
|
const reason = e instanceof Error ? e.message : String(e);
|
|
280
352
|
return err(new Error(`cannot record edge observation: ${reason}`));
|
|
281
353
|
}
|
|
282
|
-
const after = collapse(readRawRecords(vaultRoot)).get(edgeKey(record.from, record.to));
|
|
354
|
+
const after = collapse(readRawRecords(vaultRoot)).get(edgeKey(...canonPair(record.from, record.to)));
|
|
283
355
|
if (!after)
|
|
284
356
|
return err(new Error("edge not found after write"));
|
|
285
357
|
return ok(deriveEdge(after, new Date()));
|
|
@@ -300,7 +372,7 @@ export async function contestEdge(vaultRoot, input) {
|
|
|
300
372
|
}
|
|
301
373
|
const from = input.fromPath.trim();
|
|
302
374
|
const to = input.toPath.trim();
|
|
303
|
-
const current = collapse(readRawRecords(vaultRoot)).get(edgeKey(from, to));
|
|
375
|
+
const current = collapse(readRawRecords(vaultRoot)).get(edgeKey(...canonPair(from, to)));
|
|
304
376
|
if (!current) {
|
|
305
377
|
return err(new Error(`contestEdge: no such edge: ${from} derives_from ${to}`));
|
|
306
378
|
}
|
|
@@ -323,7 +395,7 @@ export async function contestEdge(vaultRoot, input) {
|
|
|
323
395
|
const reason = e instanceof Error ? e.message : String(e);
|
|
324
396
|
return err(new Error(`cannot record edge contest: ${reason}`));
|
|
325
397
|
}
|
|
326
|
-
const after = collapse(readRawRecords(vaultRoot)).get(edgeKey(from, to));
|
|
398
|
+
const after = collapse(readRawRecords(vaultRoot)).get(edgeKey(...canonPair(from, to)));
|
|
327
399
|
if (!after)
|
|
328
400
|
return err(new Error("edge not found after write"));
|
|
329
401
|
return ok(deriveEdge(after, new Date()));
|
|
@@ -351,7 +423,7 @@ export async function listEdges(vaultRoot, filter = {}, now = new Date()) {
|
|
|
351
423
|
}
|
|
352
424
|
export async function getEdge(vaultRoot, fromPath, toPath, now = new Date()) {
|
|
353
425
|
try {
|
|
354
|
-
const state = collapse(readRawRecords(vaultRoot)).get(edgeKey(fromPath, toPath));
|
|
426
|
+
const state = collapse(readRawRecords(vaultRoot)).get(edgeKey(...canonPair(fromPath, toPath)));
|
|
355
427
|
return ok(state ? deriveEdge(state, now) : null);
|
|
356
428
|
}
|
|
357
429
|
catch (e) {
|
|
@@ -378,6 +450,7 @@ export function rebuildEdgesIndex(db, vaultRoot, now = new Date()) {
|
|
|
378
450
|
last_rederived: e.lastRederived,
|
|
379
451
|
last_age_decay: at,
|
|
380
452
|
status: e.status,
|
|
453
|
+
direction_verdict: e.directionVerdict,
|
|
381
454
|
}));
|
|
382
455
|
const write = db.transaction(() => {
|
|
383
456
|
clearDerivesFromEdges(db);
|