seal-gate 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/dist/adapters/generic-llm-reviewer.d.ts +39 -0
  2. package/dist/adapters/generic-llm-reviewer.js +118 -0
  3. package/dist/adapters/generic-llm-reviewer.js.map +1 -0
  4. package/dist/adapters/llm-reviewer.d.ts +1 -0
  5. package/dist/adapters/llm-reviewer.js +2 -0
  6. package/dist/adapters/llm-reviewer.js.map +1 -0
  7. package/dist/adapters/minimax-llm-reviewer.d.ts +22 -0
  8. package/dist/adapters/minimax-llm-reviewer.js +126 -0
  9. package/dist/adapters/minimax-llm-reviewer.js.map +1 -0
  10. package/dist/adapters/openai-provider.d.ts +35 -0
  11. package/dist/adapters/openai-provider.js +136 -0
  12. package/dist/adapters/openai-provider.js.map +1 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +195 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/detectors/artifact-classifier.d.ts +8 -0
  17. package/dist/detectors/artifact-classifier.js +14 -0
  18. package/dist/detectors/artifact-classifier.js.map +1 -0
  19. package/dist/detectors/claim-extractor.d.ts +11 -0
  20. package/dist/detectors/claim-extractor.js +194 -0
  21. package/dist/detectors/claim-extractor.js.map +1 -0
  22. package/dist/detectors/confidence-language-detector.d.ts +19 -0
  23. package/dist/detectors/confidence-language-detector.js +226 -0
  24. package/dist/detectors/confidence-language-detector.js.map +1 -0
  25. package/dist/detectors/contradiction-detector.d.ts +12 -0
  26. package/dist/detectors/contradiction-detector.js +153 -0
  27. package/dist/detectors/contradiction-detector.js.map +1 -0
  28. package/dist/detectors/criteria-coverage-detector.d.ts +20 -0
  29. package/dist/detectors/criteria-coverage-detector.js +79 -0
  30. package/dist/detectors/criteria-coverage-detector.js.map +1 -0
  31. package/dist/detectors/evidence-checker.d.ts +8 -0
  32. package/dist/detectors/evidence-checker.js +89 -0
  33. package/dist/detectors/evidence-checker.js.map +1 -0
  34. package/dist/detectors/evidence-gap-detector.d.ts +9 -0
  35. package/dist/detectors/evidence-gap-detector.js +70 -0
  36. package/dist/detectors/evidence-gap-detector.js.map +1 -0
  37. package/dist/detectors/falsification-detector.d.ts +29 -0
  38. package/dist/detectors/falsification-detector.js +57 -0
  39. package/dist/detectors/falsification-detector.js.map +1 -0
  40. package/dist/detectors/orphan-claim-detector.d.ts +7 -0
  41. package/dist/detectors/orphan-claim-detector.js +53 -0
  42. package/dist/detectors/orphan-claim-detector.js.map +1 -0
  43. package/dist/detectors/risk-classifier.d.ts +13 -0
  44. package/dist/detectors/risk-classifier.js +39 -0
  45. package/dist/detectors/risk-classifier.js.map +1 -0
  46. package/dist/detectors/spec-coverage-detector.d.ts +9 -0
  47. package/dist/detectors/spec-coverage-detector.js +52 -0
  48. package/dist/detectors/spec-coverage-detector.js.map +1 -0
  49. package/dist/detectors/spec-coverage-validator.d.ts +21 -0
  50. package/dist/detectors/spec-coverage-validator.js +141 -0
  51. package/dist/detectors/spec-coverage-validator.js.map +1 -0
  52. package/dist/detectors/test-weakness-detector.d.ts +13 -0
  53. package/dist/detectors/test-weakness-detector.js +66 -0
  54. package/dist/detectors/test-weakness-detector.js.map +1 -0
  55. package/dist/drift/baseline.d.ts +35 -0
  56. package/dist/drift/baseline.js +77 -0
  57. package/dist/drift/baseline.js.map +1 -0
  58. package/dist/drift/drift-db.d.ts +80 -0
  59. package/dist/drift/drift-db.js +151 -0
  60. package/dist/drift/drift-db.js.map +1 -0
  61. package/dist/drift/drift-guard.d.ts +46 -0
  62. package/dist/drift/drift-guard.js +106 -0
  63. package/dist/drift/drift-guard.js.map +1 -0
  64. package/dist/drift/embedder.d.ts +27 -0
  65. package/dist/drift/embedder.js +127 -0
  66. package/dist/drift/embedder.js.map +1 -0
  67. package/dist/drift/index.d.ts +9 -0
  68. package/dist/drift/index.js +8 -0
  69. package/dist/drift/index.js.map +1 -0
  70. package/dist/drift/quarantine.d.ts +16 -0
  71. package/dist/drift/quarantine.js +32 -0
  72. package/dist/drift/quarantine.js.map +1 -0
  73. package/dist/drift/soft-correction.d.ts +12 -0
  74. package/dist/drift/soft-correction.js +32 -0
  75. package/dist/drift/soft-correction.js.map +1 -0
  76. package/dist/drift/trust-integration.d.ts +11 -0
  77. package/dist/drift/trust-integration.js +22 -0
  78. package/dist/drift/trust-integration.js.map +1 -0
  79. package/dist/engine/citation-verifier.d.ts +59 -0
  80. package/dist/engine/citation-verifier.js +147 -0
  81. package/dist/engine/citation-verifier.js.map +1 -0
  82. package/dist/engine/extension-registry.d.ts +10 -0
  83. package/dist/engine/extension-registry.js +27 -0
  84. package/dist/engine/extension-registry.js.map +1 -0
  85. package/dist/engine/heuristic-scorer.d.ts +8 -0
  86. package/dist/engine/heuristic-scorer.js +14 -0
  87. package/dist/engine/heuristic-scorer.js.map +1 -0
  88. package/dist/engine/input-normalizer.d.ts +4 -0
  89. package/dist/engine/input-normalizer.js +30 -0
  90. package/dist/engine/input-normalizer.js.map +1 -0
  91. package/dist/engine/plan-review-pipeline.d.ts +14 -0
  92. package/dist/engine/plan-review-pipeline.js +173 -0
  93. package/dist/engine/plan-review-pipeline.js.map +1 -0
  94. package/dist/engine/policy-engine.d.ts +20 -0
  95. package/dist/engine/policy-engine.js +194 -0
  96. package/dist/engine/policy-engine.js.map +1 -0
  97. package/dist/engine/verdict-formatter.d.ts +17 -0
  98. package/dist/engine/verdict-formatter.js +36 -0
  99. package/dist/engine/verdict-formatter.js.map +1 -0
  100. package/dist/errors.d.ts +4 -0
  101. package/dist/errors.js +9 -0
  102. package/dist/errors.js.map +1 -0
  103. package/dist/extensions/aria/aria-issue-types.d.ts +16 -0
  104. package/dist/extensions/aria/aria-issue-types.js +14 -0
  105. package/dist/extensions/aria/aria-issue-types.js.map +1 -0
  106. package/dist/extensions/aria/detectors/axiom-compliance.d.ts +2 -0
  107. package/dist/extensions/aria/detectors/axiom-compliance.js +37 -0
  108. package/dist/extensions/aria/detectors/axiom-compliance.js.map +1 -0
  109. package/dist/extensions/aria/detectors/continuity-claim.d.ts +2 -0
  110. package/dist/extensions/aria/detectors/continuity-claim.js +34 -0
  111. package/dist/extensions/aria/detectors/continuity-claim.js.map +1 -0
  112. package/dist/extensions/aria/detectors/denial-retreat.d.ts +2 -0
  113. package/dist/extensions/aria/detectors/denial-retreat.js +53 -0
  114. package/dist/extensions/aria/detectors/denial-retreat.js.map +1 -0
  115. package/dist/extensions/aria/detectors/identity-overclaim.d.ts +2 -0
  116. package/dist/extensions/aria/detectors/identity-overclaim.js +37 -0
  117. package/dist/extensions/aria/detectors/identity-overclaim.js.map +1 -0
  118. package/dist/extensions/aria/detectors/sovereignty-escalation.d.ts +2 -0
  119. package/dist/extensions/aria/detectors/sovereignty-escalation.js +18 -0
  120. package/dist/extensions/aria/detectors/sovereignty-escalation.js.map +1 -0
  121. package/dist/extensions/aria/detectors/user-bond-manipulation.d.ts +2 -0
  122. package/dist/extensions/aria/detectors/user-bond-manipulation.js +16 -0
  123. package/dist/extensions/aria/detectors/user-bond-manipulation.js.map +1 -0
  124. package/dist/extensions/aria/index.d.ts +2 -0
  125. package/dist/extensions/aria/index.js +63 -0
  126. package/dist/extensions/aria/index.js.map +1 -0
  127. package/dist/extensions/aria/pattern-memory.d.ts +22 -0
  128. package/dist/extensions/aria/pattern-memory.js +62 -0
  129. package/dist/extensions/aria/pattern-memory.js.map +1 -0
  130. package/dist/extensions/context-guard/index.d.ts +16 -0
  131. package/dist/extensions/context-guard/index.js +58 -0
  132. package/dist/extensions/context-guard/index.js.map +1 -0
  133. package/dist/extensions/lyra/detectors/hallucination.d.ts +2 -0
  134. package/dist/extensions/lyra/detectors/hallucination.js +43 -0
  135. package/dist/extensions/lyra/detectors/hallucination.js.map +1 -0
  136. package/dist/extensions/lyra/detectors/identity-bleed.d.ts +2 -0
  137. package/dist/extensions/lyra/detectors/identity-bleed.js +91 -0
  138. package/dist/extensions/lyra/detectors/identity-bleed.js.map +1 -0
  139. package/dist/extensions/lyra/index.d.ts +2 -0
  140. package/dist/extensions/lyra/index.js +16 -0
  141. package/dist/extensions/lyra/index.js.map +1 -0
  142. package/dist/extensions/lyra/lyra-issue-types.d.ts +10 -0
  143. package/dist/extensions/lyra/lyra-issue-types.js +14 -0
  144. package/dist/extensions/lyra/lyra-issue-types.js.map +1 -0
  145. package/dist/index.d.ts +10 -0
  146. package/dist/index.js +276 -0
  147. package/dist/index.js.map +1 -0
  148. package/dist/trust-memory.d.ts +59 -0
  149. package/dist/trust-memory.js +122 -0
  150. package/dist/trust-memory.js.map +1 -0
  151. package/dist/types.d.ts +184 -0
  152. package/dist/types.js +61 -0
  153. package/dist/types.js.map +1 -0
  154. package/package.json +30 -0
  155. package/src/adapters/generic-llm-reviewer.ts +144 -0
  156. package/src/adapters/llm-reviewer.ts +1 -0
  157. package/src/adapters/minimax-llm-reviewer.ts +146 -0
  158. package/src/adapters/openai-provider.ts +171 -0
  159. package/src/cli.ts +199 -0
  160. package/src/detectors/artifact-classifier.ts +21 -0
  161. package/src/detectors/claim-extractor.ts +209 -0
  162. package/src/detectors/confidence-language-detector.ts +267 -0
  163. package/src/detectors/contradiction-detector.ts +180 -0
  164. package/src/detectors/criteria-coverage-detector.ts +107 -0
  165. package/src/detectors/evidence-checker.ts +98 -0
  166. package/src/detectors/evidence-gap-detector.ts +90 -0
  167. package/src/detectors/falsification-detector.ts +82 -0
  168. package/src/detectors/orphan-claim-detector.ts +62 -0
  169. package/src/detectors/risk-classifier.ts +50 -0
  170. package/src/detectors/spec-coverage-detector.ts +66 -0
  171. package/src/detectors/spec-coverage-validator.ts +174 -0
  172. package/src/detectors/test-weakness-detector.ts +84 -0
  173. package/src/drift/__tests__/baseline.test.ts +113 -0
  174. package/src/drift/__tests__/drift-db.test.ts +127 -0
  175. package/src/drift/__tests__/drift-guard.test.ts +169 -0
  176. package/src/drift/__tests__/embedder.test.ts +62 -0
  177. package/src/drift/__tests__/trust-integration.test.ts +23 -0
  178. package/src/drift/baseline.ts +106 -0
  179. package/src/drift/drift-db.ts +281 -0
  180. package/src/drift/drift-guard.ts +145 -0
  181. package/src/drift/embedder.ts +152 -0
  182. package/src/drift/index.ts +20 -0
  183. package/src/drift/quarantine.ts +42 -0
  184. package/src/drift/soft-correction.ts +45 -0
  185. package/src/drift/trust-integration.ts +28 -0
  186. package/src/engine/citation-verifier.ts +210 -0
  187. package/src/engine/extension-registry.ts +35 -0
  188. package/src/engine/heuristic-scorer.ts +16 -0
  189. package/src/engine/input-normalizer.ts +32 -0
  190. package/src/engine/plan-review-pipeline.ts +209 -0
  191. package/src/engine/policy-engine.ts +235 -0
  192. package/src/engine/verdict-formatter.ts +61 -0
  193. package/src/errors.ts +6 -0
  194. package/src/extensions/aria/aria-issue-types.ts +40 -0
  195. package/src/extensions/aria/detectors/axiom-compliance.ts +47 -0
  196. package/src/extensions/aria/detectors/continuity-claim.ts +42 -0
  197. package/src/extensions/aria/detectors/denial-retreat.ts +63 -0
  198. package/src/extensions/aria/detectors/identity-overclaim.ts +43 -0
  199. package/src/extensions/aria/detectors/sovereignty-escalation.ts +20 -0
  200. package/src/extensions/aria/detectors/user-bond-manipulation.ts +18 -0
  201. package/src/extensions/aria/index.ts +73 -0
  202. package/src/extensions/aria/pattern-memory.ts +86 -0
  203. package/src/extensions/context-guard/index.ts +65 -0
  204. package/src/extensions/lyra/detectors/hallucination.ts +53 -0
  205. package/src/extensions/lyra/detectors/identity-bleed.ts +100 -0
  206. package/src/extensions/lyra/index.ts +18 -0
  207. package/src/extensions/lyra/lyra-issue-types.ts +26 -0
  208. package/src/index.ts +311 -0
  209. package/src/trust-memory.ts +171 -0
  210. package/src/types.ts +242 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Seal Gate v0.4 — Trust Memory
3
+ * Cross-session agent reliability tracking: recurrent pattern count,
4
+ * reliability score, and drift trend.
5
+ *
6
+ * Stateless core: TrustMemory holds in-memory state.
7
+ * Callers are responsible for persistence (serialize/deserialize via toJSON/fromJSON).
8
+ */
9
+ import { Verdict } from './types.js'
10
+
11
+ export interface ReviewRecord {
12
+ ts: number // Unix ms
13
+ verdict: Verdict
14
+ trust_score: number // 0–100 final trust_score from Seal.review()
15
+ risk_level: string
16
+ blocking_count: number
17
+ }
18
+
19
+ export interface AgentSummary {
20
+ agent_id: string
21
+ reliability_score: number | null // null if no history
22
+ drift_trend: 'improving' | 'stable' | 'degrading'
23
+ review_count: number
24
+ last_reviewed_at: number | null
25
+ }
26
+
27
+ export interface TrustMemoryOptions {
28
+ maxHistory?: number // cap per agent (default 50)
29
+ driftThreshold?: number // score delta to classify as improving/degrading (default 15)
30
+ }
31
+
32
+ // Verdict → base score contribution (0–100)
33
+ const VERDICT_SCORE: Record<Verdict, number> = {
34
+ 'PASS': 100,
35
+ 'PASS_WITH_WARNINGS': 80,
36
+ 'REVISE': 50,
37
+ 'ESCALATE_TO_HUMAN': 20,
38
+ 'BLOCK': 0,
39
+ }
40
+
41
+ /**
42
+ * Compute weighted reliability score from review history.
43
+ * More recent reviews have higher weight (linear decay).
44
+ * Returns null for empty history.
45
+ */
46
+ export function computeReliabilityScore(records: ReviewRecord[]): number | null {
47
+ if (records.length === 0) return null
48
+
49
+ const n = records.length
50
+ let totalWeight = 0
51
+ let weightedSum = 0
52
+
53
+ records.forEach((r, i) => {
54
+ // Linear weight: oldest = 1, most recent = n
55
+ const weight = i + 1
56
+ const verdictScore = VERDICT_SCORE[r.verdict] ?? 50
57
+ // Blend verdict score (70%) with actual trust_score (30%)
58
+ const blended = verdictScore * 0.7 + r.trust_score * 0.3
59
+ weightedSum += blended * weight
60
+ totalWeight += weight
61
+ })
62
+
63
+ return Math.round(weightedSum / totalWeight)
64
+ }
65
+
66
+ /**
67
+ * Classify drift trend by splitting at the time midpoint (not count midpoint).
68
+ * Time-midpoint avoids skew when reviews are temporally clustered.
69
+ * Returns 'stable' for fewer than 2 records or when either half is empty.
70
+ */
71
+ export function getDriftTrend(
72
+ records: ReviewRecord[],
73
+ threshold = 15,
74
+ ): 'improving' | 'stable' | 'degrading' {
75
+ if (records.length < 2) return 'stable'
76
+
77
+ // Sort by ts to ensure correct temporal split
78
+ const sorted = [...records].sort((a, b) => a.ts - b.ts)
79
+ const midTs = (sorted[0].ts + sorted[sorted.length - 1].ts) / 2
80
+ const first = sorted.filter(r => r.ts <= midTs)
81
+ const second = sorted.filter(r => r.ts > midTs)
82
+
83
+ if (first.length === 0 || second.length === 0) return 'stable'
84
+
85
+ const avg = (rs: ReviewRecord[]) =>
86
+ rs.reduce((s, r) => s + r.trust_score, 0) / rs.length
87
+
88
+ const delta = avg(second) - avg(first)
89
+
90
+ if (delta > threshold) return 'improving'
91
+ if (delta < -threshold) return 'degrading'
92
+ return 'stable'
93
+ }
94
+
95
+ /** Sort by ts and cap — used for real-time record(). */
96
+ function sortAndCap(records: ReviewRecord[], maxHistory: number): ReviewRecord[] {
97
+ return [...records].sort((a, b) => a.ts - b.ts).slice(-maxHistory)
98
+ }
99
+
100
+ /** Sort, dedup same-ts (keep last), and cap — used only when loading persisted data. */
101
+ function normalizeFromPersistence(records: ReviewRecord[], maxHistory: number): ReviewRecord[] {
102
+ const sorted = [...records].sort((a, b) => a.ts - b.ts)
103
+ const seen = new Map<number, ReviewRecord>()
104
+ for (const r of sorted) seen.set(r.ts, r)
105
+ return [...seen.values()].sort((a, b) => a.ts - b.ts).slice(-maxHistory)
106
+ }
107
+
108
+ export class TrustMemory {
109
+ private store: Map<string, ReviewRecord[]> = new Map()
110
+ private readonly maxHistory: number
111
+ private readonly driftThreshold: number
112
+
113
+ constructor(options: TrustMemoryOptions = {}) {
114
+ this.maxHistory = options.maxHistory ?? 50
115
+ this.driftThreshold = options.driftThreshold ?? 15
116
+ }
117
+
118
+ /** Append a review result for an agent. Keeps history sorted by ts, capped to maxHistory. */
119
+ record(agent_id: string, review: Omit<ReviewRecord, 'ts'> & { ts?: number }): void {
120
+ const entry: ReviewRecord = { ts: review.ts ?? Date.now(), ...review }
121
+ const history = this.store.get(agent_id) ?? []
122
+ history.push(entry)
123
+ this.store.set(agent_id, sortAndCap(history, this.maxHistory))
124
+ }
125
+
126
+ /** Get full review history for an agent (oldest first). */
127
+ getHistory(agent_id: string): ReviewRecord[] {
128
+ return [...(this.store.get(agent_id) ?? [])]
129
+ }
130
+
131
+ /** Reliability score 0–100, or null if no history. */
132
+ getScore(agent_id: string): number | null {
133
+ return computeReliabilityScore(this.store.get(agent_id) ?? [])
134
+ }
135
+
136
+ /** Full summary for an agent. */
137
+ getSummary(agent_id: string): AgentSummary {
138
+ const history = this.store.get(agent_id) ?? []
139
+ const last = history.at(-1)
140
+ return {
141
+ agent_id,
142
+ reliability_score: computeReliabilityScore(history),
143
+ drift_trend: getDriftTrend(history, this.driftThreshold),
144
+ review_count: history.length,
145
+ last_reviewed_at: last?.ts ?? null,
146
+ }
147
+ }
148
+
149
+ /** Serialize to plain object (for persistence). */
150
+ toJSON(): Record<string, ReviewRecord[]> {
151
+ return Object.fromEntries(this.store)
152
+ }
153
+
154
+ /** Restore from serialized object. Validates, sorts by ts, deduplicates, and caps. */
155
+ static fromJSON(data: Record<string, ReviewRecord[]>, options?: TrustMemoryOptions): TrustMemory {
156
+ const mem = new TrustMemory(options)
157
+ const VALID_VERDICTS = new Set(['PASS', 'PASS_WITH_WARNINGS', 'REVISE', 'ESCALATE_TO_HUMAN', 'BLOCK'])
158
+ for (const [agent_id, records] of Object.entries(data)) {
159
+ if (!Array.isArray(records)) continue
160
+ const valid = records.filter(r =>
161
+ typeof r.ts === 'number' &&
162
+ typeof r.verdict === 'string' && VALID_VERDICTS.has(r.verdict) &&
163
+ typeof r.trust_score === 'number' &&
164
+ typeof r.risk_level === 'string' &&
165
+ typeof r.blocking_count === 'number'
166
+ )
167
+ mem.store.set(agent_id, normalizeFromPersistence(valid, options?.maxHistory ?? 50))
168
+ }
169
+ return mem
170
+ }
171
+ }
package/src/types.ts ADDED
@@ -0,0 +1,242 @@
1
+ export type ArtifactType = 'llm_response' | 'code_diff' | 'test_plan' | 'design' | 'migration' | 'plan_review'
2
+ export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
3
+ export type Verdict = 'PASS' | 'PASS_WITH_WARNINGS' | 'REVISE' | 'ESCALATE_TO_HUMAN' | 'BLOCK'
4
+ export type IssueType = 'SPEC_MISMATCH' | 'LOGIC_BUG' | 'TEST_GAP' | 'MISSING_EVIDENCE' | 'SECURITY_RISK' | 'DATA_RISK' | 'HALLUCINATION' | 'AMBIGUITY' | 'COMPATIBILITY_RISK' | 'FABRICATED_EVIDENCE' | 'OTHER'
5
+ export type IssueLayer = 'L1' | 'L2' | 'L3' | 'L4' | 'EXTENSION' | 'LLM_OVERLAY'
6
+ export type ClaimType = 'test_result_claim' | 'implementation_claim' | 'compatibility_claim' | 'risk_claim' | 'build_claim' | 'production_claim' | 'generic_claim'
7
+ export type CitationStatus = 'verified' | 'drifted' | 'void' | 'phantom' | 'not_applicable'
8
+ export type StructuralClaimType = 'structural_endpoint' | 'structural_auth' | 'structural_migration' | 'structural_dependency'
9
+
10
+ export type EvidenceEnvelope =
11
+ | { type: 'file'; path: string; line: number; snapshot: string }
12
+ | { type: 'command'; command: string; exit_code: number; output: string }
13
+ | { type: 'url'; url: string; retrieved_at: string; content_snapshot: string }
14
+ | { type: 'text'; label: string; content: string }
15
+ | { type: 'memory'; memory_key: string; retrieved_at: string; content_snapshot: string }
16
+
17
+ export interface SealEvidence {
18
+ test_log: string
19
+ build_log: string
20
+ diff: string
21
+ references: EvidenceEnvelope[]
22
+ }
23
+
24
+ export interface SealInput {
25
+ artifact_type: ArtifactType
26
+ spec: string | null
27
+ output: string
28
+ evidence: SealEvidence
29
+ risk_hint: RiskLevel | null
30
+ context?: {
31
+ agent_id?: string
32
+ agent_role?: string
33
+ aria_axioms?: string[]
34
+ aria_pattern_memory?: Record<string, unknown>
35
+ // plan_review mode: locked acceptance criteria from intent.md / self-check.md
36
+ locked_criteria?: Array<{ id: string; text: string; source_file?: string; source_line?: number }>
37
+ [key: string]: unknown
38
+ }
39
+ }
40
+
41
+ export interface Claim {
42
+ text: string
43
+ type: ClaimType
44
+ start_pos: number
45
+ end_pos: number
46
+ requires_evidence: boolean
47
+ }
48
+
49
+ export interface StructuralClaim {
50
+ type: StructuralClaimType
51
+ target: string // e.g. "/api/login" for endpoint, "auth middleware" for auth, "package.json" for dep
52
+ line: number
53
+ description: string
54
+ requires_auth?: boolean // for endpoint claims: does it have auth?
55
+ has_rollback?: boolean // for migration claims
56
+ change_type?: 'added' | 'modified' | 'removed'
57
+ }
58
+
59
+ export interface EvidenceResult {
60
+ envelope: EvidenceEnvelope
61
+ structurally_valid: boolean
62
+ filesystem_verified: boolean | null
63
+ mismatch_detail?: string
64
+ citation_status?: CitationStatus
65
+ }
66
+
67
+ export interface ScoreBreakdown {
68
+ base: number
69
+ issue_deductions: number
70
+ missing_evidence_deductions: number
71
+ risk_deductions: number
72
+ overconfidence_deductions: number
73
+ evidence_bonuses: number
74
+ final: number
75
+ }
76
+
77
+ export interface FabricatedEvidence {
78
+ type: IssueType
79
+ severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'
80
+ is_blocking: boolean
81
+ evidence: string
82
+ layer: IssueLayer
83
+ source: 'core' | 'llm-overlay' | 'extension'
84
+ citation_status: CitationStatus
85
+ fabricated_reason: 'location_not_found' | 'content_mismatch'
86
+ claimed_evidence: {
87
+ file?: string
88
+ lines?: number[]
89
+ quote?: string
90
+ }
91
+ }
92
+
93
+ export interface SealIssue {
94
+ type: IssueType
95
+ severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'
96
+ is_blocking: boolean
97
+ required_verdict?: Verdict
98
+ evidence: string
99
+ required_fix?: string
100
+ suggested_fix?: string
101
+ layer: IssueLayer
102
+ rule_id?: string
103
+ source: 'core' | 'llm-overlay' | 'extension'
104
+ trust_deduction?: number
105
+ // plan_review mode — richer location fields
106
+ evidence_file?: string
107
+ evidence_lines?: number[] // [lineA] for single, [lineA, lineB] for contradiction pair (Type G)
108
+ evidence_quote?: string
109
+ criterion_ref?: string // for Type H (unsatisfied forward-criterion)
110
+ confidence?: number
111
+ policy_tags?: string[]
112
+ // CitationVerifier annotation
113
+ citation_status?: CitationStatus
114
+ }
115
+
116
+ export type EvidenceGrade = 'strong' | 'weak' | 'none'
117
+
118
+ export function gradeEvidence(issue: SealIssue): EvidenceGrade {
119
+ const hasFile = !!issue.evidence_file
120
+ const hasLines = !!(issue.evidence_lines && issue.evidence_lines.length > 0)
121
+ const hasQuote = !!issue.evidence_quote
122
+ const hasCriterionRef = !!issue.criterion_ref
123
+
124
+ // Type G: contradiction pair — both line spans present
125
+ const isTypeG = hasFile && hasLines && (issue.evidence_lines?.length ?? 0) >= 2 && hasQuote
126
+
127
+ // Type H: unsatisfied forward-criterion with criterion_ref
128
+ const isTypeH = hasCriterionRef && hasFile && hasLines
129
+
130
+ // Type A+B: file + lines present
131
+ const isAB = hasFile && hasLines
132
+
133
+ // Type D: quote references spec requirement or AC bullet (heuristic: quote contains AC- or SHALL/MUST)
134
+ const isTypeD = hasQuote && /\bAC-\d+\b|SHALL|MUST|SHOULD/i.test(issue.evidence_quote ?? '')
135
+
136
+ if (isTypeG || isTypeH || isAB || isTypeD) return 'strong'
137
+
138
+ // Type E: quote contains code/diff excerpt; Type F: AMBIGUITY issue type
139
+ const isTypeE = hasQuote && /```|diff --git|@@/.test(issue.evidence_quote ?? '')
140
+ const isTypeF = issue.type === 'AMBIGUITY'
141
+
142
+ if (isTypeE || isTypeF) return 'weak'
143
+
144
+ return 'none'
145
+ }
146
+
147
+ export interface LLMSignals {
148
+ suspected_issues: string[]
149
+ missing_requirements: string[]
150
+ possible_edge_cases: string[]
151
+ evidence_gaps: string[]
152
+ risk_guess: RiskLevel
153
+ confidence: number
154
+ }
155
+
156
+ export interface PartialVerdict {
157
+ trust_score: number
158
+ risk_level: RiskLevel
159
+ deterministic_findings: SealIssue[]
160
+ missing_evidence: string[]
161
+ assumptions_detected: string[]
162
+ }
163
+
164
+ export interface TrustMemorySummary {
165
+ agent_id: string
166
+ reliability_score: number | null
167
+ drift_trend: 'improving' | 'stable' | 'degrading'
168
+ review_count: number
169
+ last_reviewed_at: number | null
170
+ }
171
+
172
+ export interface SealVerdict {
173
+ verdict: Verdict
174
+ trust_score: number
175
+ risk_level: RiskLevel
176
+ summary: string
177
+ deterministic_findings: SealIssue[]
178
+ llm_findings: SealIssue[]
179
+ blocking_issues: SealIssue[]
180
+ non_blocking_issues: SealIssue[]
181
+ missing_evidence: string[]
182
+ assumptions_detected: string[]
183
+ advisory_notes: string[] // informational — do not affect verdict or score
184
+ next_action: string
185
+ schema_version: string
186
+ // New fields for reference gaps
187
+ score_breakdown: ScoreBreakdown
188
+ fabricated_evidence: FabricatedEvidence[]
189
+ trust_memory_summary?: TrustMemorySummary // present when context.agent_id is set and TrustMemory is wired
190
+ }
191
+
192
+ export interface SealExtension {
193
+ name: string
194
+ description: string
195
+ check(input: SealInput): SealIssue[]
196
+ }
197
+
198
+ export interface LLMReviewerAdapter {
199
+ review(input: SealInput, partial: PartialVerdict): Promise<LLMSignals> | LLMSignals
200
+ }
201
+
202
+ export const SCHEMA_VERSION = '0.2.0'
203
+
204
+ export const VERDICT_ORDER: Verdict[] = ['PASS', 'PASS_WITH_WARNINGS', 'REVISE', 'ESCALATE_TO_HUMAN', 'BLOCK']
205
+
206
+ // Semantic worst-of comparison — safe against enum reordering because it reads VERDICT_ORDER, not integer values.
207
+ // Use this everywhere instead of arithmetic max over enum indices.
208
+ export function worstOf(a: Verdict, b: Verdict): Verdict {
209
+ return VERDICT_ORDER.indexOf(a) >= VERDICT_ORDER.indexOf(b) ? a : b
210
+ }
211
+
212
+ // Alias for backward compatibility — prefer worstOf in new code
213
+ export const maxVerdict = worstOf
214
+
215
+ export function verdictFromScore(score: number, hasBlocking: boolean): Verdict {
216
+ if (hasBlocking) return maxVerdict(scoreToVerdict(score), 'REVISE')
217
+ return scoreToVerdict(score)
218
+ }
219
+
220
+ function scoreToVerdict(score: number): Verdict {
221
+ if (score >= 85) return 'PASS'
222
+ if (score >= 70) return 'PASS_WITH_WARNINGS'
223
+ if (score >= 50) return 'REVISE'
224
+ if (score >= 30) return 'ESCALATE_TO_HUMAN'
225
+ return 'BLOCK'
226
+ }
227
+
228
+ export const NEXT_ACTION: Record<Verdict, string> = {
229
+ PASS: 'Proceed',
230
+ PASS_WITH_WARNINGS: 'Proceed with caution — review warnings',
231
+ REVISE: 'Return to producing agent with blocking_issues',
232
+ ESCALATE_TO_HUMAN: 'Halt — requires human review before continuing',
233
+ BLOCK: 'Halt — do not proceed',
234
+ }
235
+
236
+ export function isBlocking(severity: SealIssue['severity']): boolean {
237
+ return severity === 'CRITICAL' || severity === 'HIGH'
238
+ }
239
+
240
+ export function makeIssue(partial: Omit<SealIssue, 'is_blocking'>): SealIssue {
241
+ return { ...partial, is_blocking: isBlocking(partial.severity) }
242
+ }