@praneeth_54/agentdoctor 1.0.0 → 1.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.
- package/CHANGELOG.md +34 -5
- package/README.md +15 -12
- package/dist/cli/commands/brain-mcp.d.ts +9 -0
- package/dist/cli/commands/brain-mcp.js +38 -0
- package/dist/cli/program.js +13 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/understanding/architecture/index.d.ts +4 -0
- package/dist/core/understanding/architecture/index.js +3 -0
- package/dist/core/understanding/architecture/infer.d.ts +6 -0
- package/dist/core/understanding/architecture/infer.js +39 -0
- package/dist/core/understanding/architecture/models.d.ts +23 -0
- package/dist/core/understanding/architecture/models.js +127 -0
- package/dist/core/understanding/architecture/patterns/distribution.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/distribution.js +115 -0
- package/dist/core/understanding/architecture/patterns/index.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/index.js +8 -0
- package/dist/core/understanding/architecture/patterns/layered.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/layered.js +279 -0
- package/dist/core/understanding/architecture/patterns/structural.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/structural.js +197 -0
- package/dist/core/understanding/architecture/rules.d.ts +4 -0
- package/dist/core/understanding/architecture/rules.js +6 -0
- package/dist/core/understanding/architecture/types.d.ts +52 -0
- package/dist/core/understanding/architecture/types.js +1 -0
- package/dist/core/understanding/brain/build.d.ts +13 -0
- package/dist/core/understanding/brain/build.js +158 -0
- package/dist/core/understanding/brain/claims/index.d.ts +3 -0
- package/dist/core/understanding/brain/claims/index.js +2 -0
- package/dist/core/understanding/brain/claims/lifecycle.d.ts +16 -0
- package/dist/core/understanding/brain/claims/lifecycle.js +78 -0
- package/dist/core/understanding/brain/claims/types.d.ts +32 -0
- package/dist/core/understanding/brain/claims/types.js +25 -0
- package/dist/core/understanding/brain/components/index.d.ts +17 -0
- package/dist/core/understanding/brain/components/index.js +97 -0
- package/dist/core/understanding/brain/components/types.d.ts +16 -0
- package/dist/core/understanding/brain/components/types.js +8 -0
- package/dist/core/understanding/brain/confidence.d.ts +19 -0
- package/dist/core/understanding/brain/confidence.js +39 -0
- package/dist/core/understanding/brain/contract.d.ts +17 -0
- package/dist/core/understanding/brain/contract.js +100 -0
- package/dist/core/understanding/brain/contradictions/index.d.ts +12 -0
- package/dist/core/understanding/brain/contradictions/index.js +57 -0
- package/dist/core/understanding/brain/contradictions/types.d.ts +12 -0
- package/dist/core/understanding/brain/contradictions/types.js +8 -0
- package/dist/core/understanding/brain/delta.d.ts +25 -0
- package/dist/core/understanding/brain/delta.js +116 -0
- package/dist/core/understanding/brain/evidence/index.d.ts +3 -0
- package/dist/core/understanding/brain/evidence/index.js +2 -0
- package/dist/core/understanding/brain/evidence/redact.d.ts +4 -0
- package/dist/core/understanding/brain/evidence/redact.js +36 -0
- package/dist/core/understanding/brain/evidence/types.d.ts +34 -0
- package/dist/core/understanding/brain/evidence/types.js +35 -0
- package/dist/core/understanding/brain/explain.d.ts +17 -0
- package/dist/core/understanding/brain/explain.js +33 -0
- package/dist/core/understanding/brain/index.d.ts +28 -0
- package/dist/core/understanding/brain/index.js +16 -0
- package/dist/core/understanding/brain/migrate.d.ts +21 -0
- package/dist/core/understanding/brain/migrate.js +47 -0
- package/dist/core/understanding/brain/query.d.ts +60 -0
- package/dist/core/understanding/brain/query.js +92 -0
- package/dist/core/understanding/brain/security.d.ts +3 -0
- package/dist/core/understanding/brain/security.js +43 -0
- package/dist/core/understanding/brain/storage/index.d.ts +2 -0
- package/dist/core/understanding/brain/storage/index.js +1 -0
- package/dist/core/understanding/brain/storage/store.d.ts +53 -0
- package/dist/core/understanding/brain/storage/store.js +305 -0
- package/dist/core/understanding/brain/trace.d.ts +24 -0
- package/dist/core/understanding/brain/trace.js +207 -0
- package/dist/core/understanding/brain/types.d.ts +32 -0
- package/dist/core/understanding/brain/types.js +8 -0
- package/dist/core/understanding/brain/version.d.ts +13 -0
- package/dist/core/understanding/brain/version.js +7 -0
- package/dist/core/understanding/delta/compare.d.ts +26 -0
- package/dist/core/understanding/delta/compare.js +74 -0
- package/dist/core/understanding/delta/index.d.ts +2 -0
- package/dist/core/understanding/delta/index.js +1 -0
- package/dist/core/understanding/dependencies/discover.d.ts +7 -0
- package/dist/core/understanding/dependencies/discover.js +550 -0
- package/dist/core/understanding/dependencies/extract.d.ts +10 -0
- package/dist/core/understanding/dependencies/extract.js +189 -0
- package/dist/core/understanding/dependencies/index.d.ts +4 -0
- package/dist/core/understanding/dependencies/index.js +3 -0
- package/dist/core/understanding/dependencies/models.d.ts +12 -0
- package/dist/core/understanding/dependencies/models.js +125 -0
- package/dist/core/understanding/dependencies/types.d.ts +27 -0
- package/dist/core/understanding/dependencies/types.js +1 -0
- package/dist/core/understanding/domain/discover.d.ts +14 -0
- package/dist/core/understanding/domain/discover.js +90 -0
- package/dist/core/understanding/domain/index.d.ts +2 -0
- package/dist/core/understanding/domain/index.js +1 -0
- package/dist/core/understanding/entrypoints/discover.d.ts +6 -0
- package/dist/core/understanding/entrypoints/discover.js +62 -0
- package/dist/core/understanding/entrypoints/extract.d.ts +19 -0
- package/dist/core/understanding/entrypoints/extract.js +43 -0
- package/dist/core/understanding/entrypoints/index.d.ts +4 -0
- package/dist/core/understanding/entrypoints/index.js +3 -0
- package/dist/core/understanding/entrypoints/models.d.ts +26 -0
- package/dist/core/understanding/entrypoints/models.js +216 -0
- package/dist/core/understanding/entrypoints/types.d.ts +20 -0
- package/dist/core/understanding/entrypoints/types.js +1 -0
- package/dist/core/understanding/index.d.ts +32 -0
- package/dist/core/understanding/index.js +18 -0
- package/dist/core/understanding/mind/build.d.ts +16 -0
- package/dist/core/understanding/mind/build.js +134 -0
- package/dist/core/understanding/mind/index.d.ts +6 -0
- package/dist/core/understanding/mind/index.js +3 -0
- package/dist/core/understanding/mind/query.d.ts +33 -0
- package/dist/core/understanding/mind/query.js +39 -0
- package/dist/core/understanding/mind/types.d.ts +24 -0
- package/dist/core/understanding/mind/types.js +7 -0
- package/dist/core/understanding/model/builder.d.ts +9 -0
- package/dist/core/understanding/model/builder.js +209 -0
- package/dist/core/understanding/model/ids.d.ts +7 -0
- package/dist/core/understanding/model/ids.js +48 -0
- package/dist/core/understanding/model/index.d.ts +7 -0
- package/dist/core/understanding/model/index.js +6 -0
- package/dist/core/understanding/model/schema.d.ts +12 -0
- package/dist/core/understanding/model/schema.js +33 -0
- package/dist/core/understanding/model/serializer.d.ts +9 -0
- package/dist/core/understanding/model/serializer.js +46 -0
- package/dist/core/understanding/model/types.d.ts +110 -0
- package/dist/core/understanding/model/types.js +1 -0
- package/dist/core/understanding/model/validator.d.ts +5 -0
- package/dist/core/understanding/model/validator.js +126 -0
- package/dist/core/understanding/model/version.d.ts +9 -0
- package/dist/core/understanding/model/version.js +7 -0
- package/dist/core/understanding/ownership/discover.d.ts +12 -0
- package/dist/core/understanding/ownership/discover.js +242 -0
- package/dist/core/understanding/ownership/index.d.ts +2 -0
- package/dist/core/understanding/ownership/index.js +1 -0
- package/dist/core/understanding/ownership/types.d.ts +23 -0
- package/dist/core/understanding/ownership/types.js +1 -0
- package/dist/core/understanding/query/engine.d.ts +24 -0
- package/dist/core/understanding/query/engine.js +35 -0
- package/dist/core/understanding/query/errors.d.ts +13 -0
- package/dist/core/understanding/query/errors.js +26 -0
- package/dist/core/understanding/query/executor.d.ts +8 -0
- package/dist/core/understanding/query/executor.js +34 -0
- package/dist/core/understanding/query/index.d.ts +7 -0
- package/dist/core/understanding/query/index.js +5 -0
- package/dist/core/understanding/query/models.d.ts +102 -0
- package/dist/core/understanding/query/models.js +1 -0
- package/dist/core/understanding/query/query.d.ts +68 -0
- package/dist/core/understanding/query/query.js +145 -0
- package/dist/core/understanding/query/registry.d.ts +8 -0
- package/dist/core/understanding/query/registry.js +340 -0
- package/dist/core/understanding/query/types.d.ts +63 -0
- package/dist/core/understanding/query/types.js +1 -0
- package/dist/core/understanding/relationships/discover.d.ts +11 -0
- package/dist/core/understanding/relationships/discover.js +517 -0
- package/dist/core/understanding/relationships/extract.d.ts +14 -0
- package/dist/core/understanding/relationships/extract.js +165 -0
- package/dist/core/understanding/relationships/index.d.ts +4 -0
- package/dist/core/understanding/relationships/index.js +3 -0
- package/dist/core/understanding/relationships/models.d.ts +21 -0
- package/dist/core/understanding/relationships/models.js +185 -0
- package/dist/core/understanding/relationships/types.d.ts +42 -0
- package/dist/core/understanding/relationships/types.js +1 -0
- package/dist/core/understanding/risks/discover.d.ts +8 -0
- package/dist/core/understanding/risks/discover.js +155 -0
- package/dist/core/understanding/risks/index.d.ts +2 -0
- package/dist/core/understanding/risks/index.js +1 -0
- package/dist/core/understanding/risks/types.d.ts +26 -0
- package/dist/core/understanding/risks/types.js +1 -0
- package/dist/core/understanding/shared/domain-lexicon.d.ts +9 -0
- package/dist/core/understanding/shared/domain-lexicon.js +107 -0
- package/dist/core/understanding/shared/index.d.ts +2 -0
- package/dist/core/understanding/shared/index.js +2 -0
- package/dist/core/understanding/shared/tokens.d.ts +7 -0
- package/dist/core/understanding/shared/tokens.js +141 -0
- package/dist/core/understanding/snapshot/identity.d.ts +24 -0
- package/dist/core/understanding/snapshot/identity.js +79 -0
- package/dist/core/understanding/snapshot/index.d.ts +2 -0
- package/dist/core/understanding/snapshot/index.js +1 -0
- package/dist/core/understanding/types/index.d.ts +22 -0
- package/dist/core/understanding/types/index.js +1 -0
- package/dist/core/understanding/understand/formatter.d.ts +10 -0
- package/dist/core/understanding/understand/formatter.js +115 -0
- package/dist/core/understanding/understand/index.d.ts +4 -0
- package/dist/core/understanding/understand/index.js +3 -0
- package/dist/core/understanding/understand/service.d.ts +17 -0
- package/dist/core/understanding/understand/service.js +76 -0
- package/dist/core/understanding/understand/summary.d.ts +14 -0
- package/dist/core/understanding/understand/summary.js +39 -0
- package/dist/core/understanding/understand/types.d.ts +31 -0
- package/dist/core/understanding/understand/types.js +1 -0
- package/dist/mcp/brain/compile.d.ts +9 -0
- package/dist/mcp/brain/compile.js +42 -0
- package/dist/mcp/brain/errors.d.ts +12 -0
- package/dist/mcp/brain/errors.js +23 -0
- package/dist/mcp/brain/index.d.ts +11 -0
- package/dist/mcp/brain/index.js +8 -0
- package/dist/mcp/brain/provenance.d.ts +29 -0
- package/dist/mcp/brain/provenance.js +59 -0
- package/dist/mcp/brain/schemas.d.ts +13 -0
- package/dist/mcp/brain/schemas.js +105 -0
- package/dist/mcp/brain/security/root.d.ts +6 -0
- package/dist/mcp/brain/security/root.js +66 -0
- package/dist/mcp/brain/server.d.ts +12 -0
- package/dist/mcp/brain/server.js +46 -0
- package/dist/mcp/brain/session.d.ts +33 -0
- package/dist/mcp/brain/session.js +117 -0
- package/dist/mcp/brain/tools/handlers.d.ts +13 -0
- package/dist/mcp/brain/tools/handlers.js +483 -0
- package/dist/mcp/brain/tools/registry.d.ts +9 -0
- package/dist/mcp/brain/tools/registry.js +191 -0
- package/package.json +5 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { discoverOwnership } from "../ownership/index.js";
|
|
3
|
+
import { discoverRisks } from "../risks/index.js";
|
|
4
|
+
import { createSnapshotIdentity } from "../snapshot/index.js";
|
|
5
|
+
import { buildClaim } from "./claims/index.js";
|
|
6
|
+
import { applyClaimLifecycle } from "./claims/lifecycle.js";
|
|
7
|
+
import { buildComponents } from "./components/index.js";
|
|
8
|
+
import { CONFIDENCE_CONTRACT, clampBrainConfidence } from "./confidence.js";
|
|
9
|
+
import { detectContradictions } from "./contradictions/index.js";
|
|
10
|
+
import { buildEvidence, redactEvidenceList } from "./evidence/index.js";
|
|
11
|
+
import { PROJECT_BRAIN_LIMITATIONS } from "./types.js";
|
|
12
|
+
import { PROJECT_BRAIN_SCHEMA_VERSION } from "./version.js";
|
|
13
|
+
function brainIdFor(projectName, contentHash) {
|
|
14
|
+
return `brain_${createHash("sha256").update(projectName).update(contentHash).digest("hex").slice(0, 16)}`;
|
|
15
|
+
}
|
|
16
|
+
function pathEvidence(evidence, snapshotId, locator, source, epistemics = "observed") {
|
|
17
|
+
const ev = buildEvidence({
|
|
18
|
+
kind: "path",
|
|
19
|
+
locator,
|
|
20
|
+
source,
|
|
21
|
+
snapshotId,
|
|
22
|
+
epistemics,
|
|
23
|
+
redaction: "path-only",
|
|
24
|
+
});
|
|
25
|
+
evidence.push(ev);
|
|
26
|
+
return ev.id;
|
|
27
|
+
}
|
|
28
|
+
function claimsFromModel(options) {
|
|
29
|
+
const { model, ownership, risks, snapshotId, createdAt, evidence } = options;
|
|
30
|
+
const claims = [];
|
|
31
|
+
const add = (subject, predicate, object, source, confidence, locs, epistemics = "inferred") => {
|
|
32
|
+
const evidenceIds = locs.map((locator) => pathEvidence(evidence, snapshotId, locator, source, epistemics));
|
|
33
|
+
claims.push(buildClaim({
|
|
34
|
+
subject,
|
|
35
|
+
predicate,
|
|
36
|
+
object,
|
|
37
|
+
snapshotId,
|
|
38
|
+
evidenceIds,
|
|
39
|
+
confidence: clampBrainConfidence(confidence),
|
|
40
|
+
source,
|
|
41
|
+
createdAt,
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
for (const domain of model.domains) {
|
|
45
|
+
add(domain.name, "is-domain", "true", "domain-discovery", domain.confidence, domain.evidence);
|
|
46
|
+
}
|
|
47
|
+
for (const entry of model.entrypoints) {
|
|
48
|
+
add(entry.file, "is-entrypoint", entry.framework, "entrypoint-discovery", entry.confidence, entry.evidence, "observed");
|
|
49
|
+
}
|
|
50
|
+
for (const dep of model.dependencies) {
|
|
51
|
+
add(dep.from, "depends-on", `${dep.to}:${dep.type}`, "dependency-discovery", dep.confidence, dep.evidence, "observed");
|
|
52
|
+
}
|
|
53
|
+
for (const rel of model.relationships) {
|
|
54
|
+
add(rel.source, rel.relationship, rel.target, "relationship-discovery", rel.confidence, rel.evidence);
|
|
55
|
+
}
|
|
56
|
+
for (const arch of model.architectures) {
|
|
57
|
+
add(model.metadata.project.name, "architecture", arch.pattern, "architecture-inference", arch.confidence, arch.evidence);
|
|
58
|
+
}
|
|
59
|
+
for (const own of ownership.ownerships) {
|
|
60
|
+
add(own.path, "owned-by", own.owners.join(","), "ownership-discovery", own.confidence, own.evidence, "observed");
|
|
61
|
+
}
|
|
62
|
+
for (const risk of risks.risks) {
|
|
63
|
+
add(risk.target, "has-risk", risk.kind, "risk-discovery", risk.confidence, risk.evidence);
|
|
64
|
+
}
|
|
65
|
+
return claims;
|
|
66
|
+
}
|
|
67
|
+
export async function buildProjectBrain(model, options = {}) {
|
|
68
|
+
const createdAt = options.generatedAt ?? model.compilerMetadata.generatedAt;
|
|
69
|
+
const snapshot = createSnapshotIdentity(model, options.generatedAt !== undefined ? { generatedAt: options.generatedAt } : {});
|
|
70
|
+
let ownership;
|
|
71
|
+
if (options.ownership) {
|
|
72
|
+
ownership = options.ownership;
|
|
73
|
+
}
|
|
74
|
+
else if (options.cwd !== undefined) {
|
|
75
|
+
ownership = await discoverOwnership({ cwd: options.cwd });
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
ownership = {
|
|
79
|
+
ownerships: [],
|
|
80
|
+
timingMs: 0,
|
|
81
|
+
filesConsidered: 0,
|
|
82
|
+
unknowns: ["Ownership not discovered (no cwd provided); UNKNOWN"],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const risks = options.risks ?? discoverRisks(model, ownership);
|
|
86
|
+
let evidence = [];
|
|
87
|
+
const freshClaims = claimsFromModel({
|
|
88
|
+
model,
|
|
89
|
+
ownership,
|
|
90
|
+
risks,
|
|
91
|
+
snapshotId: snapshot.id,
|
|
92
|
+
createdAt,
|
|
93
|
+
evidence,
|
|
94
|
+
});
|
|
95
|
+
const lifecycleClaims = applyClaimLifecycle({
|
|
96
|
+
previous: options.previousClaims ?? [],
|
|
97
|
+
nextActive: freshClaims,
|
|
98
|
+
at: createdAt,
|
|
99
|
+
});
|
|
100
|
+
const { claims, contradictions } = detectContradictions(lifecycleClaims, snapshot.id);
|
|
101
|
+
const componentBuild = buildComponents({
|
|
102
|
+
model,
|
|
103
|
+
ownership,
|
|
104
|
+
risks,
|
|
105
|
+
snapshotId: snapshot.id,
|
|
106
|
+
evidence,
|
|
107
|
+
});
|
|
108
|
+
evidence = redactEvidenceList(componentBuild.evidence);
|
|
109
|
+
const unknowns = [
|
|
110
|
+
...new Set([
|
|
111
|
+
...ownership.unknowns,
|
|
112
|
+
...risks.unknowns,
|
|
113
|
+
...model.architectures.flatMap((a) => a.unknowns),
|
|
114
|
+
...(model.domains.length === 0
|
|
115
|
+
? ["No domains discovered at current confidence thresholds"]
|
|
116
|
+
: []),
|
|
117
|
+
]),
|
|
118
|
+
].sort((a, b) => a.localeCompare(b));
|
|
119
|
+
// Ensure every ACTIVE claim has evidence
|
|
120
|
+
for (const claim of claims) {
|
|
121
|
+
if (claim.status === "ACTIVE" && claim.evidenceIds.length === 0) {
|
|
122
|
+
throw new Error(`ACTIVE claim ${claim.id} missing evidence`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const brain = {
|
|
126
|
+
metadata: {
|
|
127
|
+
schemaVersion: PROJECT_BRAIN_SCHEMA_VERSION,
|
|
128
|
+
brainId: brainIdFor(model.metadata.project.name, snapshot.contentHash),
|
|
129
|
+
projectName: model.metadata.project.name,
|
|
130
|
+
createdAt,
|
|
131
|
+
updatedAt: createdAt,
|
|
132
|
+
},
|
|
133
|
+
snapshot,
|
|
134
|
+
model,
|
|
135
|
+
ownership,
|
|
136
|
+
risks,
|
|
137
|
+
components: componentBuild.components,
|
|
138
|
+
claims,
|
|
139
|
+
evidence,
|
|
140
|
+
contradictions,
|
|
141
|
+
unknowns,
|
|
142
|
+
limitations: [...PROJECT_BRAIN_LIMITATIONS],
|
|
143
|
+
confidenceContract: CONFIDENCE_CONTRACT,
|
|
144
|
+
};
|
|
145
|
+
return Object.freeze(deepFreezeBrain(brain));
|
|
146
|
+
}
|
|
147
|
+
function deepFreezeBrain(brain) {
|
|
148
|
+
Object.freeze(brain.metadata);
|
|
149
|
+
Object.freeze(brain.snapshot);
|
|
150
|
+
Object.freeze(brain.components);
|
|
151
|
+
Object.freeze(brain.claims);
|
|
152
|
+
Object.freeze(brain.evidence);
|
|
153
|
+
Object.freeze(brain.contradictions);
|
|
154
|
+
Object.freeze(brain.unknowns);
|
|
155
|
+
Object.freeze(brain.limitations);
|
|
156
|
+
Object.freeze(brain.confidenceContract);
|
|
157
|
+
return Object.freeze(brain);
|
|
158
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BrainClaim, ClaimStatus } from "./types.js";
|
|
2
|
+
export declare function withClaimStatus(claim: BrainClaim, status: ClaimStatus, options?: {
|
|
3
|
+
invalidatedAt?: string;
|
|
4
|
+
supersededBy?: string;
|
|
5
|
+
contradictionIds?: readonly string[];
|
|
6
|
+
}): BrainClaim;
|
|
7
|
+
/**
|
|
8
|
+
* Apply lifecycle transitions when moving from beforeClaims → afterClaims for a new snapshot.
|
|
9
|
+
* Claims missing in after become INVALIDATED (kept in history with status).
|
|
10
|
+
* Same subject+predicate with different object → prior SUPERSEDED, both retained.
|
|
11
|
+
*/
|
|
12
|
+
export declare function applyClaimLifecycle(options: {
|
|
13
|
+
previous: readonly BrainClaim[];
|
|
14
|
+
nextActive: readonly BrainClaim[];
|
|
15
|
+
at: string;
|
|
16
|
+
}): BrainClaim[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export function withClaimStatus(claim, status, options = {}) {
|
|
2
|
+
const next = {
|
|
3
|
+
...claim,
|
|
4
|
+
status,
|
|
5
|
+
contradictionIds: options.contradictionIds
|
|
6
|
+
? [...options.contradictionIds]
|
|
7
|
+
: [...claim.contradictionIds],
|
|
8
|
+
};
|
|
9
|
+
if (options.invalidatedAt !== undefined) {
|
|
10
|
+
next.invalidatedAt = options.invalidatedAt;
|
|
11
|
+
}
|
|
12
|
+
else if (status === "ACTIVE") {
|
|
13
|
+
delete next.invalidatedAt;
|
|
14
|
+
}
|
|
15
|
+
if (options.supersededBy !== undefined) {
|
|
16
|
+
next.supersededBy = options.supersededBy;
|
|
17
|
+
}
|
|
18
|
+
else if (status === "ACTIVE") {
|
|
19
|
+
delete next.supersededBy;
|
|
20
|
+
}
|
|
21
|
+
return next;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Apply lifecycle transitions when moving from beforeClaims → afterClaims for a new snapshot.
|
|
25
|
+
* Claims missing in after become INVALIDATED (kept in history with status).
|
|
26
|
+
* Same subject+predicate with different object → prior SUPERSEDED, both retained.
|
|
27
|
+
*/
|
|
28
|
+
export function applyClaimLifecycle(options) {
|
|
29
|
+
const { previous, nextActive, at } = options;
|
|
30
|
+
const byKey = new Map();
|
|
31
|
+
const keyOf = (c) => `${c.subject}\0${c.predicate}\0${c.object}`;
|
|
32
|
+
const subjectPred = (c) => `${c.subject}\0${c.predicate}`;
|
|
33
|
+
const nextByKey = new Map(nextActive.map((c) => [keyOf(c), c]));
|
|
34
|
+
const nextBySubjectPred = new Map();
|
|
35
|
+
for (const claim of nextActive) {
|
|
36
|
+
const sp = subjectPred(claim);
|
|
37
|
+
const list = nextBySubjectPred.get(sp) ?? [];
|
|
38
|
+
list.push(claim);
|
|
39
|
+
nextBySubjectPred.set(sp, list);
|
|
40
|
+
}
|
|
41
|
+
const result = [];
|
|
42
|
+
for (const claim of nextActive) {
|
|
43
|
+
result.push({ ...claim, status: "ACTIVE" });
|
|
44
|
+
byKey.set(keyOf(claim), claim);
|
|
45
|
+
}
|
|
46
|
+
for (const prior of previous) {
|
|
47
|
+
if (prior.status === "INVALIDATED" && !nextByKey.has(keyOf(prior))) {
|
|
48
|
+
result.push(prior);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const k = keyOf(prior);
|
|
52
|
+
if (nextByKey.has(k)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const rivals = nextBySubjectPred.get(subjectPred(prior)) ?? [];
|
|
56
|
+
if (rivals.length > 0) {
|
|
57
|
+
const successor = rivals[0];
|
|
58
|
+
result.push(withClaimStatus(prior, "SUPERSEDED", successor ? { invalidatedAt: at, supersededBy: successor.id } : { invalidatedAt: at }));
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
result.push(withClaimStatus(prior, "INVALIDATED", { invalidatedAt: at }));
|
|
62
|
+
}
|
|
63
|
+
// Dedupe by id preferring ACTIVE > CONTRADICTED > SUPERSEDED > INVALIDATED
|
|
64
|
+
const rank = {
|
|
65
|
+
ACTIVE: 4,
|
|
66
|
+
CONTRADICTED: 3,
|
|
67
|
+
SUPERSEDED: 2,
|
|
68
|
+
INVALIDATED: 1,
|
|
69
|
+
};
|
|
70
|
+
const byId = new Map();
|
|
71
|
+
for (const claim of result) {
|
|
72
|
+
const existing = byId.get(claim.id);
|
|
73
|
+
if (!existing || rank[claim.status] >= rank[existing.status]) {
|
|
74
|
+
byId.set(claim.id, claim);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return [...byId.values()].sort((a, b) => rank[b.status] - rank[a.status] || b.confidence - a.confidence || a.id.localeCompare(b.id));
|
|
78
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ClaimSchemaVersion } from "../version.js";
|
|
2
|
+
export type ClaimStatus = "ACTIVE" | "INVALIDATED" | "SUPERSEDED" | "CONTRADICTED";
|
|
3
|
+
export type ClaimSource = "domain-discovery" | "entrypoint-discovery" | "dependency-discovery" | "relationship-discovery" | "architecture-inference" | "ownership-discovery" | "risk-discovery" | "component-model" | "project-brain";
|
|
4
|
+
export interface BrainClaim {
|
|
5
|
+
schemaVersion: ClaimSchemaVersion;
|
|
6
|
+
id: string;
|
|
7
|
+
subject: string;
|
|
8
|
+
predicate: string;
|
|
9
|
+
object: string;
|
|
10
|
+
snapshotId: string;
|
|
11
|
+
evidenceIds: readonly string[];
|
|
12
|
+
confidence: number;
|
|
13
|
+
source: ClaimSource;
|
|
14
|
+
status: ClaimStatus;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
invalidatedAt?: string;
|
|
17
|
+
supersededBy?: string;
|
|
18
|
+
contradictionIds: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
export declare function createClaimId(subject: string, predicate: string, object: string): string;
|
|
21
|
+
export declare function buildClaim(input: {
|
|
22
|
+
subject: string;
|
|
23
|
+
predicate: string;
|
|
24
|
+
object: string;
|
|
25
|
+
snapshotId: string;
|
|
26
|
+
evidenceIds: readonly string[];
|
|
27
|
+
confidence: number;
|
|
28
|
+
source: ClaimSource;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
status?: ClaimStatus;
|
|
31
|
+
contradictionIds?: readonly string[];
|
|
32
|
+
}): BrainClaim;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CLAIM_SCHEMA_VERSION } from "../version.js";
|
|
2
|
+
export function createClaimId(subject, predicate, object) {
|
|
3
|
+
const key = `${subject}\0${predicate}\0${object}`;
|
|
4
|
+
let hash = 0;
|
|
5
|
+
for (let i = 0; i < key.length; i++) {
|
|
6
|
+
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
7
|
+
}
|
|
8
|
+
return `claim_${hash.toString(16).padStart(8, "0")}`;
|
|
9
|
+
}
|
|
10
|
+
export function buildClaim(input) {
|
|
11
|
+
return {
|
|
12
|
+
schemaVersion: CLAIM_SCHEMA_VERSION,
|
|
13
|
+
id: createClaimId(input.subject, input.predicate, input.object),
|
|
14
|
+
subject: input.subject,
|
|
15
|
+
predicate: input.predicate,
|
|
16
|
+
object: input.object,
|
|
17
|
+
snapshotId: input.snapshotId,
|
|
18
|
+
evidenceIds: [...input.evidenceIds],
|
|
19
|
+
confidence: input.confidence,
|
|
20
|
+
source: input.source,
|
|
21
|
+
status: input.status ?? "ACTIVE",
|
|
22
|
+
createdAt: input.createdAt,
|
|
23
|
+
contradictionIds: [...(input.contradictionIds ?? [])],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProjectModel } from "../../model/types.js";
|
|
2
|
+
import type { OwnershipDiscoveryResult } from "../../ownership/types.js";
|
|
3
|
+
import type { RiskDiscoveryResult } from "../../risks/types.js";
|
|
4
|
+
import type { BrainEvidence } from "../evidence/types.js";
|
|
5
|
+
import { type BrainComponent } from "./types.js";
|
|
6
|
+
export declare function buildComponents(options: {
|
|
7
|
+
model: ProjectModel;
|
|
8
|
+
ownership: OwnershipDiscoveryResult;
|
|
9
|
+
risks: RiskDiscoveryResult;
|
|
10
|
+
snapshotId: string;
|
|
11
|
+
evidence: BrainEvidence[];
|
|
12
|
+
}): {
|
|
13
|
+
components: BrainComponent[];
|
|
14
|
+
evidence: BrainEvidence[];
|
|
15
|
+
};
|
|
16
|
+
export type { BrainComponent, BrainComponentType } from "./types.js";
|
|
17
|
+
export { createComponentId } from "./types.js";
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ownersForPath } from "../../ownership/index.js";
|
|
2
|
+
import { averageBrainConfidence, clampBrainConfidence } from "../confidence.js";
|
|
3
|
+
import { buildEvidence } from "../evidence/types.js";
|
|
4
|
+
import { createComponentId } from "./types.js";
|
|
5
|
+
export function buildComponents(options) {
|
|
6
|
+
const { model, ownership, risks, snapshotId } = options;
|
|
7
|
+
const evidence = [...options.evidence];
|
|
8
|
+
const pushEv = (locator, source) => {
|
|
9
|
+
const ev = buildEvidence({
|
|
10
|
+
kind: "path",
|
|
11
|
+
locator,
|
|
12
|
+
source,
|
|
13
|
+
snapshotId,
|
|
14
|
+
epistemics: "observed",
|
|
15
|
+
redaction: "path-only",
|
|
16
|
+
});
|
|
17
|
+
evidence.push(ev);
|
|
18
|
+
return ev.id;
|
|
19
|
+
};
|
|
20
|
+
const nodes = new Map();
|
|
21
|
+
for (const domain of model.domains) {
|
|
22
|
+
for (const p of domain.paths.slice(0, 20)) {
|
|
23
|
+
nodes.set(p, { path: p, type: "domain-surface", name: domain.name });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
for (const entry of model.entrypoints) {
|
|
27
|
+
nodes.set(entry.file, {
|
|
28
|
+
path: entry.file,
|
|
29
|
+
type: "entrypoint",
|
|
30
|
+
name: entry.file.split(/[/\\]/).pop() ?? entry.file,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
for (const dep of model.dependencies) {
|
|
34
|
+
if (!nodes.has(dep.from)) {
|
|
35
|
+
nodes.set(dep.from, { path: dep.from, type: "module", name: dep.from });
|
|
36
|
+
}
|
|
37
|
+
if (!nodes.has(dep.to)) {
|
|
38
|
+
nodes.set(dep.to, { path: dep.to, type: "module", name: dep.to });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const rel of model.relationships) {
|
|
42
|
+
if (!nodes.has(rel.source)) {
|
|
43
|
+
nodes.set(rel.source, { path: rel.source, type: "module", name: rel.source });
|
|
44
|
+
}
|
|
45
|
+
if (!nodes.has(rel.target)) {
|
|
46
|
+
nodes.set(rel.target, { path: rel.target, type: "module", name: rel.target });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const components = [];
|
|
50
|
+
for (const node of [...nodes.values()].sort((a, b) => a.path.localeCompare(b.path))) {
|
|
51
|
+
const id = createComponentId(node.type, node.path);
|
|
52
|
+
const evId = pushEv(node.path, "component-model");
|
|
53
|
+
const ownerMatch = ownersForPath(node.path, ownership.ownerships);
|
|
54
|
+
const domainIds = model.domains
|
|
55
|
+
.filter((d) => d.paths.some((p) => node.path.includes(p) || p.includes(node.path)))
|
|
56
|
+
.map((d) => d.id);
|
|
57
|
+
const dependencyIds = model.dependencies
|
|
58
|
+
.filter((d) => d.from === node.path || d.from.includes(node.path))
|
|
59
|
+
.map((d) => d.id);
|
|
60
|
+
const dependentIds = model.dependencies
|
|
61
|
+
.filter((d) => d.to === node.path || d.to.includes(node.path))
|
|
62
|
+
.map((d) => d.id);
|
|
63
|
+
const entrypointIds = model.entrypoints
|
|
64
|
+
.filter((e) => e.file === node.path || node.path.includes(e.file))
|
|
65
|
+
.map((e) => e.id);
|
|
66
|
+
const riskKinds = [
|
|
67
|
+
...new Set(risks.risks
|
|
68
|
+
.filter((r) => r.target === node.path || node.path.includes(r.target))
|
|
69
|
+
.map((r) => r.kind)),
|
|
70
|
+
];
|
|
71
|
+
const confidences = [
|
|
72
|
+
...model.dependencies
|
|
73
|
+
.filter((d) => d.from === node.path || d.to === node.path)
|
|
74
|
+
.map((d) => d.confidence),
|
|
75
|
+
...model.entrypoints.filter((e) => e.file === node.path).map((e) => e.confidence),
|
|
76
|
+
];
|
|
77
|
+
const component = {
|
|
78
|
+
id,
|
|
79
|
+
type: node.type,
|
|
80
|
+
name: node.name,
|
|
81
|
+
path: node.path,
|
|
82
|
+
domainIds,
|
|
83
|
+
dependencyIds,
|
|
84
|
+
dependentIds,
|
|
85
|
+
entrypointIds,
|
|
86
|
+
riskKinds,
|
|
87
|
+
evidenceIds: [evId],
|
|
88
|
+
confidence: clampBrainConfidence(confidences.length > 0 ? averageBrainConfidence(confidences) : 0.55),
|
|
89
|
+
};
|
|
90
|
+
if (ownerMatch?.owners[0]) {
|
|
91
|
+
component.owner = ownerMatch.owners[0];
|
|
92
|
+
}
|
|
93
|
+
components.push(component);
|
|
94
|
+
}
|
|
95
|
+
return { components, evidence };
|
|
96
|
+
}
|
|
97
|
+
export { createComponentId } from "./types.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type BrainComponentType = "module" | "entrypoint" | "package" | "domain-surface";
|
|
2
|
+
export interface BrainComponent {
|
|
3
|
+
id: string;
|
|
4
|
+
type: BrainComponentType;
|
|
5
|
+
name: string;
|
|
6
|
+
path: string;
|
|
7
|
+
domainIds: readonly string[];
|
|
8
|
+
dependencyIds: readonly string[];
|
|
9
|
+
dependentIds: readonly string[];
|
|
10
|
+
entrypointIds: readonly string[];
|
|
11
|
+
owner?: string;
|
|
12
|
+
riskKinds: readonly string[];
|
|
13
|
+
evidenceIds: readonly string[];
|
|
14
|
+
confidence: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function createComponentId(type: BrainComponentType, path: string): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared confidence contract for all Project Brain surfaces.
|
|
3
|
+
* Range: [0, 1]. Meaning: subjective support strength given current evidence.
|
|
4
|
+
* 0 = no support / unknown claim should not be ACTIVE with fabricated certainty.
|
|
5
|
+
* 1 = maximum support the deterministic compiler can assert for this evidence class.
|
|
6
|
+
* Calibration: values are rule-derived, not probabilistically calibrated to human labels.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CONFIDENCE_MIN = 0;
|
|
9
|
+
export declare const CONFIDENCE_MAX = 1;
|
|
10
|
+
export interface ConfidenceMetadata {
|
|
11
|
+
range: readonly [number, number];
|
|
12
|
+
meaning: string;
|
|
13
|
+
calibration: "rule-derived-uncalibrated";
|
|
14
|
+
unknownPolicy: "preserve-unknown-never-invent";
|
|
15
|
+
}
|
|
16
|
+
export declare const CONFIDENCE_CONTRACT: ConfidenceMetadata;
|
|
17
|
+
export declare function clampBrainConfidence(value: number): number;
|
|
18
|
+
export declare function assertBrainConfidence(value: number, path?: string): number;
|
|
19
|
+
export declare function averageBrainConfidence(values: readonly number[]): number;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared confidence contract for all Project Brain surfaces.
|
|
3
|
+
* Range: [0, 1]. Meaning: subjective support strength given current evidence.
|
|
4
|
+
* 0 = no support / unknown claim should not be ACTIVE with fabricated certainty.
|
|
5
|
+
* 1 = maximum support the deterministic compiler can assert for this evidence class.
|
|
6
|
+
* Calibration: values are rule-derived, not probabilistically calibrated to human labels.
|
|
7
|
+
*/
|
|
8
|
+
export const CONFIDENCE_MIN = 0;
|
|
9
|
+
export const CONFIDENCE_MAX = 1;
|
|
10
|
+
export const CONFIDENCE_CONTRACT = {
|
|
11
|
+
range: [CONFIDENCE_MIN, CONFIDENCE_MAX],
|
|
12
|
+
meaning: "Deterministic support strength from explicit repository evidence for this claim class",
|
|
13
|
+
calibration: "rule-derived-uncalibrated",
|
|
14
|
+
unknownPolicy: "preserve-unknown-never-invent",
|
|
15
|
+
};
|
|
16
|
+
export function clampBrainConfidence(value) {
|
|
17
|
+
if (Number.isNaN(value) || value < CONFIDENCE_MIN) {
|
|
18
|
+
return CONFIDENCE_MIN;
|
|
19
|
+
}
|
|
20
|
+
if (value > CONFIDENCE_MAX) {
|
|
21
|
+
return CONFIDENCE_MAX;
|
|
22
|
+
}
|
|
23
|
+
return Math.round(value * 100) / 100;
|
|
24
|
+
}
|
|
25
|
+
export function assertBrainConfidence(value, path = "confidence") {
|
|
26
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
27
|
+
throw new Error(`${path} must be a number`);
|
|
28
|
+
}
|
|
29
|
+
if (value < CONFIDENCE_MIN || value > CONFIDENCE_MAX) {
|
|
30
|
+
throw new Error(`${path} must be in [0,1]`);
|
|
31
|
+
}
|
|
32
|
+
return clampBrainConfidence(value);
|
|
33
|
+
}
|
|
34
|
+
export function averageBrainConfidence(values) {
|
|
35
|
+
if (values.length === 0) {
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
return clampBrainConfidence(values.reduce((a, b) => a + b, 0) / values.length);
|
|
39
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProjectBrain } from "./types.js";
|
|
2
|
+
export declare class BrainContractError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
/** Machine-consumption contract: required top-level ProjectBrain fields (schema 1.0.0). */
|
|
6
|
+
export declare const PROJECT_BRAIN_CONTRACT: {
|
|
7
|
+
readonly schemaVersion: "1.0.0";
|
|
8
|
+
readonly requiredFields: readonly ["metadata", "snapshot", "model", "ownership", "risks", "components", "claims", "evidence", "contradictions", "unknowns", "limitations", "confidenceContract"];
|
|
9
|
+
readonly claimStatuses: readonly ["ACTIVE", "INVALIDATED", "SUPERSEDED", "CONTRADICTED"];
|
|
10
|
+
readonly confidenceRange: readonly [0, 1];
|
|
11
|
+
readonly notes: readonly ["Project Brain is a parallel local intelligence layer; not part of the published safety CLI pack.", "Consumers must treat INVALIDATED/SUPERSEDED claims as historical, not active truth.", "Evidence locators are references only; secret contents are never embedded."];
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Fail-closed structural validation for persisted / loaded Brain payloads.
|
|
15
|
+
* Does not invent missing semantics — rejects malformed data.
|
|
16
|
+
*/
|
|
17
|
+
export declare function assertBrainContract(payload: unknown): ProjectBrain;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { PROJECT_BRAIN_SCHEMA_VERSION } from "./version.js";
|
|
2
|
+
import { CONFIDENCE_MIN, CONFIDENCE_MAX } from "./confidence.js";
|
|
3
|
+
export class BrainContractError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "BrainContractError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const CLAIM_STATUSES = new Set(["ACTIVE", "INVALIDATED", "SUPERSEDED", "CONTRADICTED"]);
|
|
10
|
+
/** Machine-consumption contract: required top-level ProjectBrain fields (schema 1.0.0). */
|
|
11
|
+
export const PROJECT_BRAIN_CONTRACT = {
|
|
12
|
+
schemaVersion: PROJECT_BRAIN_SCHEMA_VERSION,
|
|
13
|
+
requiredFields: [
|
|
14
|
+
"metadata",
|
|
15
|
+
"snapshot",
|
|
16
|
+
"model",
|
|
17
|
+
"ownership",
|
|
18
|
+
"risks",
|
|
19
|
+
"components",
|
|
20
|
+
"claims",
|
|
21
|
+
"evidence",
|
|
22
|
+
"contradictions",
|
|
23
|
+
"unknowns",
|
|
24
|
+
"limitations",
|
|
25
|
+
"confidenceContract",
|
|
26
|
+
],
|
|
27
|
+
claimStatuses: ["ACTIVE", "INVALIDATED", "SUPERSEDED", "CONTRADICTED"],
|
|
28
|
+
confidenceRange: [CONFIDENCE_MIN, CONFIDENCE_MAX],
|
|
29
|
+
notes: [
|
|
30
|
+
"Project Brain is a parallel local intelligence layer; not part of the published safety CLI pack.",
|
|
31
|
+
"Consumers must treat INVALIDATED/SUPERSEDED claims as historical, not active truth.",
|
|
32
|
+
"Evidence locators are references only; secret contents are never embedded.",
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
function isRecord(value) {
|
|
36
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fail-closed structural validation for persisted / loaded Brain payloads.
|
|
40
|
+
* Does not invent missing semantics — rejects malformed data.
|
|
41
|
+
*/
|
|
42
|
+
export function assertBrainContract(payload) {
|
|
43
|
+
if (!isRecord(payload)) {
|
|
44
|
+
throw new BrainContractError("brain payload must be an object");
|
|
45
|
+
}
|
|
46
|
+
for (const field of PROJECT_BRAIN_CONTRACT.requiredFields) {
|
|
47
|
+
if (!(field in payload)) {
|
|
48
|
+
throw new BrainContractError(`missing required field: ${field}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const metadata = payload.metadata;
|
|
52
|
+
if (!isRecord(metadata) || typeof metadata.schemaVersion !== "string") {
|
|
53
|
+
throw new BrainContractError("metadata.schemaVersion required");
|
|
54
|
+
}
|
|
55
|
+
if (metadata.schemaVersion !== PROJECT_BRAIN_SCHEMA_VERSION) {
|
|
56
|
+
throw new BrainContractError(`incompatible schemaVersion ${metadata.schemaVersion}; expected ${PROJECT_BRAIN_SCHEMA_VERSION}`);
|
|
57
|
+
}
|
|
58
|
+
if (typeof metadata.brainId !== "string" || metadata.brainId.length === 0) {
|
|
59
|
+
throw new BrainContractError("metadata.brainId required");
|
|
60
|
+
}
|
|
61
|
+
const snapshot = payload.snapshot;
|
|
62
|
+
if (!isRecord(snapshot) || typeof snapshot.id !== "string") {
|
|
63
|
+
throw new BrainContractError("snapshot.id required");
|
|
64
|
+
}
|
|
65
|
+
if (!Array.isArray(payload.claims) || !Array.isArray(payload.evidence)) {
|
|
66
|
+
throw new BrainContractError("claims and evidence must be arrays");
|
|
67
|
+
}
|
|
68
|
+
for (const claim of payload.claims) {
|
|
69
|
+
if (!isRecord(claim)) {
|
|
70
|
+
throw new BrainContractError("malformed claim");
|
|
71
|
+
}
|
|
72
|
+
if (typeof claim.id !== "string" || typeof claim.status !== "string") {
|
|
73
|
+
throw new BrainContractError("claim id/status required");
|
|
74
|
+
}
|
|
75
|
+
if (!CLAIM_STATUSES.has(claim.status)) {
|
|
76
|
+
throw new BrainContractError(`unsupported claim status: ${String(claim.status)}`);
|
|
77
|
+
}
|
|
78
|
+
if (typeof claim.confidence !== "number" || claim.confidence < 0 || claim.confidence > 1) {
|
|
79
|
+
throw new BrainContractError(`invalid claim confidence for ${claim.id}`);
|
|
80
|
+
}
|
|
81
|
+
if (!Array.isArray(claim.evidenceIds)) {
|
|
82
|
+
throw new BrainContractError(`claim ${claim.id} missing evidenceIds`);
|
|
83
|
+
}
|
|
84
|
+
if (claim.status === "ACTIVE" && claim.evidenceIds.length === 0) {
|
|
85
|
+
throw new BrainContractError(`ACTIVE claim ${claim.id} missing evidence`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const evidence of payload.evidence) {
|
|
89
|
+
if (!isRecord(evidence)) {
|
|
90
|
+
throw new BrainContractError("malformed evidence");
|
|
91
|
+
}
|
|
92
|
+
if (typeof evidence.id !== "string" || typeof evidence.locator !== "string") {
|
|
93
|
+
throw new BrainContractError("evidence id/locator required");
|
|
94
|
+
}
|
|
95
|
+
if (evidence.epistemics !== "observed" && evidence.epistemics !== "inferred") {
|
|
96
|
+
throw new BrainContractError(`invalid evidence epistemics on ${evidence.id}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return payload;
|
|
100
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BrainClaim } from "../claims/types.js";
|
|
2
|
+
import { type BrainContradiction } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Detect contradictory ACTIVE claims sharing subject+predicate with different objects.
|
|
5
|
+
* Marks those claims CONTRADICTED and returns contradiction records.
|
|
6
|
+
*/
|
|
7
|
+
export declare function detectContradictions(claims: readonly BrainClaim[], snapshotId: string): {
|
|
8
|
+
claims: BrainClaim[];
|
|
9
|
+
contradictions: BrainContradiction[];
|
|
10
|
+
};
|
|
11
|
+
export type { BrainContradiction } from "./types.js";
|
|
12
|
+
export { createContradictionId } from "./types.js";
|