@davesheffer/hunch 1.31.1 → 1.32.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/README.md +1 -0
- package/dist/cli/automaticReviewMemory.d.ts +13 -0
- package/dist/cli/dna.d.ts +2 -0
- package/dist/cli/index.d.ts +16 -0
- package/dist/cli/index.js +602 -43
- package/dist/cli/integrations.d.ts +2 -0
- package/dist/cli/integrations.js +5 -2
- package/dist/cli/invocation.d.ts +35 -0
- package/dist/cli/preflight.d.ts +1 -0
- package/dist/cli/reviewMemory.d.ts +4 -0
- package/dist/cli/reviewMemoryProvider.d.ts +14 -0
- package/dist/cli/serve.d.ts +2 -0
- package/dist/cli/taskReport.d.ts +6 -0
- package/dist/cli/taskReport.js +124 -0
- package/dist/cli/update.d.ts +14 -0
- package/dist/client/state.d.ts +248 -0
- package/dist/constitution/adapters.d.ts +29 -0
- package/dist/constitution/behaviorAttestationBinding.d.ts +3 -0
- package/dist/constitution/behaviorEvaluator.d.ts +8 -0
- package/dist/constitution/behaviorProof.d.ts +9 -0
- package/dist/constitution/behaviorWorkspace.d.ts +25 -0
- package/dist/constitution/bootstrap.d.ts +30 -0
- package/dist/constitution/canonical.d.ts +15 -0
- package/dist/constitution/card.d.ts +58 -0
- package/dist/constitution/compiler.d.ts +52 -0
- package/dist/constitution/composition.d.ts +10 -0
- package/dist/constitution/corpus.d.ts +7 -0
- package/dist/constitution/correctionPolicyMaterializer.d.ts +57 -0
- package/dist/constitution/delta.d.ts +4 -0
- package/dist/constitution/disposition.d.ts +20 -0
- package/dist/constitution/evaluator.d.ts +47 -0
- package/dist/constitution/experiment.d.ts +466 -0
- package/dist/constitution/experimentRunner.d.ts +7 -0
- package/dist/constitution/g2.d.ts +209 -0
- package/dist/constitution/g2BehaviorAttestation.d.ts +45 -0
- package/dist/constitution/g2BehaviorCandidates.d.ts +123 -0
- package/dist/constitution/g2BehaviorDependencies.d.ts +83 -0
- package/dist/constitution/g2BehaviorMaterialization.d.ts +77 -0
- package/dist/constitution/g2BehaviorPolicyMaterializer.d.ts +48 -0
- package/dist/constitution/g2CandidateAttestation.d.ts +43 -0
- package/dist/constitution/g2Candidates.d.ts +80 -0
- package/dist/constitution/g2Drills.d.ts +33 -0
- package/dist/constitution/g3.d.ts +311 -0
- package/dist/constitution/g3Conformance.d.ts +33 -0
- package/dist/constitution/lifecycle.d.ts +25 -0
- package/dist/constitution/mutation.d.ts +15 -0
- package/dist/constitution/nodeTestEvidence.d.ts +13 -0
- package/dist/constitution/plan.d.ts +18 -0
- package/dist/constitution/policyRuntime.d.ts +5 -0
- package/dist/constitution/proof.d.ts +11 -0
- package/dist/constitution/repairPolicies.d.ts +34 -0
- package/dist/constitution/replacementFreeGit.d.ts +14 -0
- package/dist/constitution/replay.d.ts +40 -0
- package/dist/constitution/replayCache.d.ts +14 -0
- package/dist/constitution/replayWorker.d.ts +1 -0
- package/dist/constitution/repository.d.ts +129 -0
- package/dist/constitution/safeCheckout.d.ts +9 -0
- package/dist/constitution/schema.d.ts +1763 -0
- package/dist/constitution/scorecard.d.ts +219 -0
- package/dist/constitution/service.d.ts +398 -0
- package/dist/constitution/shadow.d.ts +54 -0
- package/dist/constitution/sourceMutation.d.ts +15 -0
- package/dist/constitution/staticGraphBaseline.d.ts +13 -0
- package/dist/constitution/structural.d.ts +64 -0
- package/dist/core/agenthook.d.ts +63 -0
- package/dist/core/agenthook.js +1 -0
- package/dist/core/automaticReviewMemory.d.ts +60 -0
- package/dist/core/autoreview.d.ts +66 -0
- package/dist/core/canonicalOrder.d.ts +3 -0
- package/dist/core/capturetoken.d.ts +19 -0
- package/dist/core/changeIdentity.d.ts +22 -0
- package/dist/core/changeProof.d.ts +14 -0
- package/dist/core/checkreport.d.ts +140 -0
- package/dist/core/commitrepair.d.ts +171 -0
- package/dist/core/commitrepair.js +238 -0
- package/dist/core/compare.d.ts +21 -0
- package/dist/core/config.d.ts +20 -0
- package/dist/core/conformance.d.ts +35 -0
- package/dist/core/constraintmatch.d.ts +54 -0
- package/dist/core/correction.d.ts +42 -0
- package/dist/core/correctionStage.d.ts +224 -0
- package/dist/core/declarationClusters.d.ts +149 -0
- package/dist/core/delivery.d.ts +110 -0
- package/dist/core/docanchors.d.ts +32 -0
- package/dist/core/docscan.d.ts +26 -0
- package/dist/core/drift.d.ts +13 -0
- package/dist/core/drift.js +31 -1
- package/dist/core/dupdetect.d.ts +43 -0
- package/dist/core/escalations.d.ts +129 -0
- package/dist/core/escalations.js +165 -0
- package/dist/core/events.d.ts +29 -0
- package/dist/core/evidenceMap.d.ts +164 -0
- package/dist/core/externalImports.d.ts +8 -0
- package/dist/core/format.d.ts +13 -0
- package/dist/core/glob.d.ts +9 -0
- package/dist/core/groundingLag.d.ts +84 -0
- package/dist/core/groundingLag.js +16 -2
- package/dist/core/groundingMerge.d.ts +15 -0
- package/dist/core/groundingMerge.js +95 -0
- package/dist/core/hookcache.d.ts +10 -0
- package/dist/core/hookpolicy.d.ts +47 -0
- package/dist/core/ids.d.ts +36 -0
- package/dist/core/importReview.d.ts +30 -0
- package/dist/core/io.d.ts +23 -0
- package/dist/core/jsonc.d.ts +4 -0
- package/dist/core/landscapeAdoption.d.ts +66 -0
- package/dist/core/landscapeDelivery.d.ts +76 -0
- package/dist/core/memorylog.d.ts +37 -0
- package/dist/core/migrate.d.ts +28 -0
- package/dist/core/outcomeExperience.d.ts +89 -0
- package/dist/core/overlaySafety.d.ts +24 -0
- package/dist/core/overlaySafety.js +7 -1
- package/dist/core/paths.d.ts +31 -0
- package/dist/core/pipeline.d.ts +289 -0
- package/dist/core/premises.d.ts +43 -0
- package/dist/core/provenance.d.ts +21 -0
- package/dist/core/publication.d.ts +54 -0
- package/dist/core/refrepair.d.ts +24 -0
- package/dist/core/refrepair.js +1 -1
- package/dist/core/relativeImports.d.ts +11 -0
- package/dist/core/repair.d.ts +44 -0
- package/dist/core/repairqueue.d.ts +40 -0
- package/dist/core/repairqueue.js +133 -0
- package/dist/core/reviewMemory.d.ts +40 -0
- package/dist/core/reviewqueue.d.ts +54 -0
- package/dist/core/safeRepoFile.d.ts +20 -0
- package/dist/core/served.d.ts +57 -0
- package/dist/core/served.js +14 -0
- package/dist/core/stateContract.d.ts +979 -0
- package/dist/core/stateDelivery.d.ts +78 -0
- package/dist/core/stateRecords.d.ts +383 -0
- package/dist/core/stats.d.ts +88 -0
- package/dist/core/strictgate.d.ts +33 -0
- package/dist/core/taskReport.d.ts +226 -0
- package/dist/core/taskReport.js +514 -0
- package/dist/core/taskReportCapture.d.ts +8 -0
- package/dist/core/taskReportCapture.js +36 -0
- package/dist/core/taskReportEvidence.d.ts +26 -0
- package/dist/core/taskReportEvidence.js +268 -0
- package/dist/core/taskReportHook.d.ts +20 -0
- package/dist/core/taskReportHook.js +76 -0
- package/dist/core/taskReportPaths.d.ts +3 -0
- package/dist/core/taskReportPaths.js +23 -0
- package/dist/core/taskReportPublic.d.ts +31 -0
- package/dist/core/taskReportPublic.js +45 -0
- package/dist/core/taskReportRender.d.ts +6 -0
- package/dist/core/taskReportRender.js +97 -0
- package/dist/core/topics.d.ts +73 -0
- package/dist/core/types.d.ts +1239 -0
- package/dist/core/version.d.ts +10 -0
- package/dist/eval/guards.d.ts +45 -0
- package/dist/eval/harness.d.ts +58 -0
- package/dist/extractors/adrImport.d.ts +59 -0
- package/dist/extractors/comments.d.ts +7 -0
- package/dist/extractors/correctionSources.d.ts +11 -0
- package/dist/extractors/diff.d.ts +40 -0
- package/dist/extractors/git.d.ts +304 -0
- package/dist/extractors/git.js +208 -20
- package/dist/extractors/helm.d.ts +31 -0
- package/dist/extractors/indexer.d.ts +55 -0
- package/dist/extractors/landscapeDiscovery.d.ts +41 -0
- package/dist/extractors/languages.d.ts +77 -0
- package/dist/extractors/nativeTreeSitter.d.ts +16 -0
- package/dist/extractors/parse.d.ts +54 -0
- package/dist/extractors/php.d.ts +26 -0
- package/dist/extractors/repoSource.d.ts +53 -0
- package/dist/extractors/testreport.d.ts +36 -0
- package/dist/integrations/ciAction.d.ts +8 -0
- package/dist/integrations/claudeConfig.d.ts +44 -0
- package/dist/integrations/claudemd.d.ts +13 -0
- package/dist/integrations/claudemd.js +11 -2
- package/dist/integrations/gitignore.d.ts +16 -0
- package/dist/integrations/gitignore.js +7 -0
- package/dist/integrations/health.d.ts +74 -0
- package/dist/integrations/hooks.d.ts +41 -0
- package/dist/integrations/hooks.js +92 -66
- package/dist/integrations/madrExport.d.ts +39 -0
- package/dist/integrations/madrManifest.d.ts +69 -0
- package/dist/integrations/mergeDriver.d.ts +3 -0
- package/dist/integrations/mergeDriver.js +25 -8
- package/dist/integrations/probe.d.ts +4 -0
- package/dist/integrations/providers.d.ts +103 -0
- package/dist/integrations/scaffold.d.ts +29 -0
- package/dist/integrations/sync.d.ts +30 -0
- package/dist/integrations/sync.js +3 -2
- package/dist/integrations/team.d.ts +70 -0
- package/dist/integrations/worktree.d.ts +8 -0
- package/dist/mcp/roots.d.ts +16 -0
- package/dist/mcp/server.d.ts +36 -0
- package/dist/mcp/server.js +74 -22
- package/dist/mcp/taskReportTools.d.ts +3 -0
- package/dist/mcp/taskReportTools.js +106 -0
- package/dist/serve/app.d.ts +36 -0
- package/dist/serve/config.d.ts +100 -0
- package/dist/serve/writelock.d.ts +22 -0
- package/dist/store/changeLedger.d.ts +132 -0
- package/dist/store/compact.d.ts +33 -0
- package/dist/store/db.d.ts +9 -0
- package/dist/store/embedder.d.ts +32 -0
- package/dist/store/hunchStore.d.ts +624 -0
- package/dist/store/hunchStore.js +6 -3
- package/dist/store/jsonStore.d.ts +125 -0
- package/dist/store/merge.d.ts +39 -0
- package/dist/store/privateMigrate.d.ts +24 -0
- package/dist/store/replay.d.ts +56 -0
- package/dist/store/schema.d.ts +17 -0
- package/dist/store/stateBinding.d.ts +145 -0
- package/dist/store/stateCapture.d.ts +15 -0
- package/dist/synthesis/cliAdapter.d.ts +22 -0
- package/dist/synthesis/initiator.d.ts +13 -0
- package/dist/synthesis/provider.d.ts +307 -0
- package/dist/synthesis/synthesize.d.ts +117 -0
- package/dist/synthesis/tripwires.d.ts +9 -0
- package/dist/taskReports.d.ts +76 -0
- package/dist/taskReports.js +67 -0
- package/dist/wiki/adopt.d.ts +22 -0
- package/dist/wiki/graph.d.ts +87 -0
- package/dist/wiki/wiki.d.ts +242 -0
- package/package.json +6 -5
- package/server.json +2 -2
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import { type HunchPaths } from "../core/paths.js";
|
|
2
|
+
import { type Component, type Constraint, type Bug, type Decision, type Symbol, type Edge, type Finding, type EntityKind, type EntityFor } from "../core/types.js";
|
|
3
|
+
import { type DB } from "./db.js";
|
|
4
|
+
import { type Embedder } from "./embedder.js";
|
|
5
|
+
import { JsonStore } from "./jsonStore.js";
|
|
6
|
+
import { type VetoTier } from "../core/strictgate.js";
|
|
7
|
+
import { type DiffAnalysis } from "../extractors/diff.js";
|
|
8
|
+
import type { CheckReport, CausalWhy, ImpactReport } from "../core/checkreport.js";
|
|
9
|
+
import { type ReviewedLandscapeSelection } from "../core/landscapeDelivery.js";
|
|
10
|
+
import { type StateSlice } from "../core/stateDelivery.js";
|
|
11
|
+
export interface SearchHit {
|
|
12
|
+
ref: string;
|
|
13
|
+
kind: string;
|
|
14
|
+
title: string;
|
|
15
|
+
snippet: string;
|
|
16
|
+
score: number;
|
|
17
|
+
}
|
|
18
|
+
export interface HybridSearchOpts {
|
|
19
|
+
embedder?: Embedder | null;
|
|
20
|
+
graphWeight?: number;
|
|
21
|
+
graphDepth?: number;
|
|
22
|
+
graphNodeCap?: number;
|
|
23
|
+
graphTokenCap?: number;
|
|
24
|
+
/** Repository whose HEAD/file history proves decision-anchor freshness. Defaults to this store's root. */
|
|
25
|
+
freshnessRoot?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface RankedSearchOpts {
|
|
28
|
+
/** Repository whose HEAD/file history proves decision-anchor freshness. Defaults to this store's root. */
|
|
29
|
+
freshnessRoot?: string;
|
|
30
|
+
}
|
|
31
|
+
export type OverlayResolutionSource = "environment" | "local-config" | null;
|
|
32
|
+
export interface OverlayOverride {
|
|
33
|
+
/** Repo/worktree-local target that would have won without the environment override. */
|
|
34
|
+
configuredDir: string;
|
|
35
|
+
/** Process-global target selected by HUNCH_PRIVATE_DIR. */
|
|
36
|
+
environmentDir: string;
|
|
37
|
+
}
|
|
38
|
+
export interface WhyResult {
|
|
39
|
+
target: string;
|
|
40
|
+
decisions: Decision[];
|
|
41
|
+
bugs: Bug[];
|
|
42
|
+
constraints: Constraint[];
|
|
43
|
+
symbols: Symbol[];
|
|
44
|
+
components: Component[];
|
|
45
|
+
}
|
|
46
|
+
export interface FragileNode {
|
|
47
|
+
id: string;
|
|
48
|
+
file: string;
|
|
49
|
+
name: string;
|
|
50
|
+
score: number;
|
|
51
|
+
churn_90d: number;
|
|
52
|
+
bug_count: number;
|
|
53
|
+
fan_in: number;
|
|
54
|
+
evidence: string[];
|
|
55
|
+
}
|
|
56
|
+
export declare class HunchStore {
|
|
57
|
+
private readonly paths;
|
|
58
|
+
readonly json: JsonStore;
|
|
59
|
+
/** Optional PRIVATE overlay (HUNCH_PRIVATE_DIR) — a second store in a repo the
|
|
60
|
+
* user controls. Unioned into reads via recs(); never written by public paths. */
|
|
61
|
+
private readonly privateJson?;
|
|
62
|
+
/** The resolved private-overlay hunch dir (from env or .hunch/local.json), or undefined
|
|
63
|
+
* when no overlay is configured. Surfaced so `hunch doctor` reflects the true state. */
|
|
64
|
+
readonly privateDir?: string;
|
|
65
|
+
/** How privateDir was selected. Multi-store consumers can use this instead of
|
|
66
|
+
* inferring process-global routing from process.env. */
|
|
67
|
+
readonly overlaySource: OverlayResolutionSource;
|
|
68
|
+
/** Present only when HUNCH_PRIVATE_DIR redirects this store away from the
|
|
69
|
+
* repo/worktree-local pointer. Precedence is compatibility-sensitive and stays
|
|
70
|
+
* env-first; making the redirection queryable removes the silent footgun. */
|
|
71
|
+
readonly overlayOverride?: OverlayOverride;
|
|
72
|
+
/** Whether captures auto-commit the store they land in — ON by default in EVERY mode;
|
|
73
|
+
* `--no-auto-commit` (hunch init/private/shared) persists `autoCommit: false` in
|
|
74
|
+
* local.json to opt out. Read by the MCP write tools and `hunch sync`. */
|
|
75
|
+
readonly autoCommit: boolean;
|
|
76
|
+
/** How memory is homed: "public" (no overlay — the repo-tracked .hunch/ is the one store),
|
|
77
|
+
* "private" (overlay holds ONLY private:true records; public records stay committed here),
|
|
78
|
+
* or "shared" (the overlay IS the store — every capture routes there, one source of truth
|
|
79
|
+
* across branches, worktrees, teammates, and agents). Absent `mode` in an existing
|
|
80
|
+
* config reads as "private" — no behavior change on upgrade. */
|
|
81
|
+
readonly mode: "public" | "private" | "shared";
|
|
82
|
+
/** mode === "shared" with a configured overlay: ALL captures route to the overlay. */
|
|
83
|
+
readonly unified: boolean;
|
|
84
|
+
/** autoCommit AND a private overlay is configured: private writes auto commit+push the
|
|
85
|
+
* overlay repo. (Public writes auto-commit the repo-tracked .hunch/ WITHOUT pushing —
|
|
86
|
+
* see commitAndPushHunch push:false / bug_overlay_clobber.) */
|
|
87
|
+
readonly privateAutoCommit: boolean;
|
|
88
|
+
/** When true, recs() ignores the private overlay (public-only). Set transiently by
|
|
89
|
+
* buildCheckReport({publicOnly}) so any PUBLICLY-POSTED report (the CI PR comment)
|
|
90
|
+
* can never render a private record — a publicly-posted output is a leak surface
|
|
91
|
+
* equal to a committed file (dec_d7bad4ccb7). */
|
|
92
|
+
private suppressPrivate;
|
|
93
|
+
private _db;
|
|
94
|
+
/** HEAD-keyed, process-local ranking evidence. It never changes record authority or storage. */
|
|
95
|
+
private decisionFreshnessRoot;
|
|
96
|
+
private decisionFreshnessHead;
|
|
97
|
+
private readonly decisionFreshnessScopes;
|
|
98
|
+
private readonly decisionFreshnessChanges;
|
|
99
|
+
constructor(paths: HunchPaths);
|
|
100
|
+
/** Human-facing warning for the compatibility-preserving env-first resolution.
|
|
101
|
+
* Callers decide where it is safe to emit (CLI/MCP stderr, doctor output); the
|
|
102
|
+
* store constructor stays side-effect-free for hooks and embedded consumers. */
|
|
103
|
+
overlayResolutionWarning(teamConfigBypassed?: boolean): string | null;
|
|
104
|
+
/** Where a capture belongs: an explicit private:true always goes to the overlay
|
|
105
|
+
* (putPrivate throws rather than silently landing public when none is configured);
|
|
106
|
+
* otherwise the overlay in unified ("shared") mode, else the public store. ONE home
|
|
107
|
+
* per record — the single-source-of-truth contract. */
|
|
108
|
+
captureHome(isPrivate?: boolean): "private" | "public";
|
|
109
|
+
/** Write a capture to its ONE home (see captureHome). Every capture path — MCP tools,
|
|
110
|
+
* post-commit synthesis, inline intents, record-constraint/conform, runbooks — funnels
|
|
111
|
+
* through here so all modes, branches, worktrees, teams, and agents agree on where
|
|
112
|
+
* memory lives. */
|
|
113
|
+
putCapture<K extends EntityKind>(kind: K, record: EntityFor[K], isPrivate?: boolean): EntityFor[K];
|
|
114
|
+
/** Replace one capture kind in its routed home as a single bulk operation.
|
|
115
|
+
*
|
|
116
|
+
* Snapshot producers use this instead of calling putCapture once per record.
|
|
117
|
+
* That distinction is material for array-backed graph kinds: repeated puts
|
|
118
|
+
* parse, sort, and atomically rewrite the complete index for every symbol or
|
|
119
|
+
* edge, while JsonStore.replaceAll validates the full input before performing
|
|
120
|
+
* one write. Routing and cross-home collision rules remain identical to an
|
|
121
|
+
* ordinary capture, so the optimization cannot create a second source of truth.
|
|
122
|
+
*/
|
|
123
|
+
replaceCaptures<K extends EntityKind>(kind: K, records: EntityFor[K][], isPrivate?: boolean): void;
|
|
124
|
+
/** Read a record by id from wherever it lives (private overlay wins on collision). */
|
|
125
|
+
getRec<K extends EntityKind>(kind: K, id: string): EntityFor[K] | undefined;
|
|
126
|
+
/** Read a record only from the configured private overlay. Callers that must
|
|
127
|
+
* preserve privacy boundaries (for example, an explicit `--private` repair)
|
|
128
|
+
* should use this instead of overlay-first `getRec`. */
|
|
129
|
+
getPrivateRec<K extends EntityKind>(kind: K, id: string): EntityFor[K] | undefined;
|
|
130
|
+
getStateDirect<K extends "derived" | "receipts" | "commitments">(kind: K, id: string, home: "public" | "private"): EntityFor[K] | undefined;
|
|
131
|
+
/** Update an EXISTING record in the store that holds it — an overlay record must never
|
|
132
|
+
* fork a public copy on update (and vice versa). Falls back to captureHome routing for
|
|
133
|
+
* a record that exists nowhere yet. */
|
|
134
|
+
putWhereItLives<K extends EntityKind>(kind: K, record: EntityFor[K]): EntityFor[K];
|
|
135
|
+
/** Delete an existing record from its actual home. The review/curation path
|
|
136
|
+
* uses this so rejecting a private draft cannot silently leave it behind or
|
|
137
|
+
* accidentally target a public record with the same id. */
|
|
138
|
+
deleteWhereItLives<K extends EntityKind>(kind: K, id: string): boolean;
|
|
139
|
+
/** The private-overlay config from the gitignored `.hunch/local.json` (per-machine,
|
|
140
|
+
* never committed). Tolerant: returns {} on missing/invalid so reads never crash.
|
|
141
|
+
* `autoCommit` is tri-state: true/false when the file says so, undefined when unset.
|
|
142
|
+
* `mode` records HOW the overlay was set up ("private" split vs "shared" unified). */
|
|
143
|
+
private localConfig;
|
|
144
|
+
/** Merged read: public ∪ private overlay (private wins on id collision). Every
|
|
145
|
+
* QUERY / REINDEX path uses this so MCP + the guards see private memory. Public-
|
|
146
|
+
* artifact writers keep using this.json.loadAll (public-only) so a private record
|
|
147
|
+
* can never reach a committed file — see dec_d7bad4ccb7. */
|
|
148
|
+
recs<K extends EntityKind>(kind: K): EntityFor[K][];
|
|
149
|
+
/** Records for an AGENT-FACING advisory surface (escalations, orientation).
|
|
150
|
+
*
|
|
151
|
+
* In unified ("shared") mode the overlay IS the one store — the public `.hunch/` is
|
|
152
|
+
* only a routing shell — so reading the public home alone returns NOTHING and the
|
|
153
|
+
* surface reports "all clear" for a store whose every record is elsewhere. That is
|
|
154
|
+
* the worst possible answer for escalations, whose entire job is to raise the
|
|
155
|
+
* questions only a human can settle: a real topic collision came back as an empty
|
|
156
|
+
* list and the agent was affirmatively told there was nothing to escalate.
|
|
157
|
+
*
|
|
158
|
+
* In "private" mode the split is a real privacy boundary, so this stays public-only:
|
|
159
|
+
* private records must not surface on a public advisory surface. Mode-aware, not a
|
|
160
|
+
* blanket union — the distinction is the point. */
|
|
161
|
+
advisoryRecs<K extends EntityKind>(kind: K): EntityFor[K][];
|
|
162
|
+
/** Records from exactly one storage home (no public/private union). Capture
|
|
163
|
+
* paths use this for identity/lineage checks so a private record can never
|
|
164
|
+
* inherit or disclose relationships from an identically-shaped public record. */
|
|
165
|
+
recsInHome<K extends EntityKind>(kind: K, home: "public" | "private"): EntityFor[K][];
|
|
166
|
+
/** Whether a private overlay store is configured (HUNCH_PRIVATE_DIR is set). */
|
|
167
|
+
get hasPrivate(): boolean;
|
|
168
|
+
/** Public project root protected from private-overlay commit/push operations. */
|
|
169
|
+
get publicRoot(): string;
|
|
170
|
+
/** Write a record into the PRIVATE overlay (never the public repo). Throws if no
|
|
171
|
+
* HUNCH_PRIVATE_DIR is configured, so a "private" write can never silently land
|
|
172
|
+
* in the public `.hunch/`. */
|
|
173
|
+
putPrivate<K extends EntityKind>(kind: K, record: EntityFor[K]): EntityFor[K];
|
|
174
|
+
get db(): DB;
|
|
175
|
+
close(): void;
|
|
176
|
+
/** Revision marker for every JSON source currently visible to this store.
|
|
177
|
+
* SQLite/FTS is derived state; long-lived consumers compare this marker with
|
|
178
|
+
* the marker captured only after a successful rebuild. */
|
|
179
|
+
sourceStamp(): string;
|
|
180
|
+
/** Rebuild from fresh disk reads after an out-of-process Git pull or capture.
|
|
181
|
+
* Normal in-process writes invalidate their own kind cache; cross-process
|
|
182
|
+
* refreshes clear both homes explicitly so schema-only changes are included. */
|
|
183
|
+
reindexFresh(): {
|
|
184
|
+
counts: Record<string, number>;
|
|
185
|
+
};
|
|
186
|
+
/** Rebuild the entire SQLite index + FTS from the JSON source of truth. */
|
|
187
|
+
reindex(): {
|
|
188
|
+
counts: Record<string, number>;
|
|
189
|
+
};
|
|
190
|
+
/** FTS5 ranked search — the RAW bm25 primitive, no priors. Callers wanting
|
|
191
|
+
* relevance ordering (liveness/provenance/recency + topic-chain promotion)
|
|
192
|
+
* go through hybridSearch/searchScoped, where rerankByPriors applies.
|
|
193
|
+
* Falls back to LIKE if the query has no FTS-tokenizable terms. */
|
|
194
|
+
search(query: string, limit?: number): SearchHit[];
|
|
195
|
+
/** State-of-record ordering for nuryel.state/1 hits (superseded derived, done/cancelled
|
|
196
|
+
* commitments, failed receipts, retired entities): indexed and findable, but ranked BELOW
|
|
197
|
+
* the live record of the same subject. bm25 is negative (lower = better), so a history
|
|
198
|
+
* hit's score is scaled toward 0 by STATE_HISTORY_SCORE_FACTOR and the pool is re-sorted
|
|
199
|
+
* STABLY by score — a store with no state history returns the exact SQL order, and a
|
|
200
|
+
* LIKE-fallback pool (all scores 0) is partitioned live-first in its existing order.
|
|
201
|
+
* Bounded (a dimmer, not an exclusion) so the previous summary stays reachable one query
|
|
202
|
+
* away; hybridSearch/rankedSearch additionally apply the liveness prior via priorMeta. */
|
|
203
|
+
private demoteHistoricalState;
|
|
204
|
+
/** Liveness + clock for a state hit (null for every non-state kind). */
|
|
205
|
+
private stateMeta;
|
|
206
|
+
/** The bounded "State" slice for a context brief (hunch_context / `hunch context`): the
|
|
207
|
+
* current derived summaries, in-force commitments and latest verified receipts whose
|
|
208
|
+
* subject or text matches `target`. Matching is AND over the target's tokens (every token
|
|
209
|
+
* must appear, prefix-tolerant) so a file path such as src/store/x.ts never drags in a
|
|
210
|
+
* summary that merely mentions "store"; an exact subject match always qualifies. Order is
|
|
211
|
+
* deterministic: score (best first), then observed_at DESC, then id. Caps per kind are
|
|
212
|
+
* STATE_SLICE_CAPS. A store with no state records returns three empty lists. */
|
|
213
|
+
stateSlice(target: string): StateSlice;
|
|
214
|
+
/** AND-shaped FTS over one state kind (every token required, prefix-tolerant); degrades to
|
|
215
|
+
* the kind-scoped LIKE scan (OR-shaped — the caller re-checks AND) without FTS5. */
|
|
216
|
+
private stateFts;
|
|
217
|
+
/** Portable bounded fallback over titles/bodies. Each natural-language token
|
|
218
|
+
* is an OR candidate, mirroring the high-recall FTS query closely enough for
|
|
219
|
+
* runtimes whose SQLite build omits the optional FTS5 module.
|
|
220
|
+
*
|
|
221
|
+
* `_` is BOTH a LIKE single-character wildcard and the dominant character in this
|
|
222
|
+
* codebase's identifiers (dec_/con_/bug_ ids, snake_case symbols). The old code
|
|
223
|
+
* STRIPPED it, so a search for `hunch_record_decision` looked for the literal
|
|
224
|
+
* `hunchrecorddecision` and matched nothing — on precisely the runtimes with no FTS5,
|
|
225
|
+
* where this fallback is the only search there is. Escaping keeps the term literal;
|
|
226
|
+
* leaving `_` unescaped would silently over-match instead. */
|
|
227
|
+
private likeSearch;
|
|
228
|
+
/** The exact (ref, kind, title, body) docs that feed FTS — and thus embeddings.
|
|
229
|
+
* A single source so FTS, the doc_hash, and the stored vectors never disagree. */
|
|
230
|
+
private searchDocs;
|
|
231
|
+
/** Delete embedding rows whose source doc was removed or whose text changed
|
|
232
|
+
* (doc_hash mismatch). Model-free and cheap; run at the end of every reindex()
|
|
233
|
+
* so vectors track the JSON truth without ever being reset. Returns the count. */
|
|
234
|
+
pruneStaleEmbeddings(): number;
|
|
235
|
+
/** Embedding coverage for a model: up-to-date vectors vs total docs (doctor). */
|
|
236
|
+
embeddingStats(model: string): {
|
|
237
|
+
embedded: number;
|
|
238
|
+
total: number;
|
|
239
|
+
};
|
|
240
|
+
/** The SINGLE gate for "can semantic search run right now": an embedder exists and
|
|
241
|
+
* it has at least one stored vector. Used by both hybridSearch and the CLI so the
|
|
242
|
+
* definition can't drift between them. */
|
|
243
|
+
semanticReady(embedder: Embedder | null): embedder is Embedder;
|
|
244
|
+
/** Generate/refresh embeddings for every doc missing an up-to-date vector for
|
|
245
|
+
* this embedder's model. Batched + flushed per batch so a Ctrl-C leaves a
|
|
246
|
+
* coherent partial index that a re-run resumes. Assumes reindex() ran first. */
|
|
247
|
+
embedAll(embedder: Embedder, opts?: {
|
|
248
|
+
batch?: number;
|
|
249
|
+
onProgress?: (done: number, total: number) => void;
|
|
250
|
+
}): Promise<{
|
|
251
|
+
embedded: number;
|
|
252
|
+
skipped: number;
|
|
253
|
+
total: number;
|
|
254
|
+
}>;
|
|
255
|
+
/** Post-fusion rerank by graph PRIORS (dec_25e277f479): relevance ordering, not
|
|
256
|
+
* just reachability. Trust weight w = liveness × provenance × recency: liveness 0.6
|
|
257
|
+
* for superseded/retired/rejected, provenance 1.0 / 0.85 / 0.75 for
|
|
258
|
+
* human_confirmed / llm_draft / extracted-inferred, recency 0.7 + 0.3·½^(age/90d),
|
|
259
|
+
* and proven anchored-file staleness 0.8. Every factor is ranking-only.
|
|
260
|
+
* Runbook trigger phrases matching the query boost ×1.5 (exact intent beats
|
|
261
|
+
* keyword luck). Structural refs (symbols/components/edges) stay neutral.
|
|
262
|
+
*
|
|
263
|
+
* The prior is applied as a BOUNDED POSITIONAL SHIFT, not as a multiplier on a
|
|
264
|
+
* rank-derived score. That is deliberate and load-bearing. The old form —
|
|
265
|
+
* `1/(60+i) × w`, sorted descending, sliced to `limit` — could not keep the
|
|
266
|
+
* "dims, never disappears" promise it made: across a fused pool of 24 the
|
|
267
|
+
* positional term spans only 1/60…1/83 (a 0.72 ratio) while w spans 0.48…1.0 on
|
|
268
|
+
* this repo's own records, so the prior was WIDER than the entire pool's
|
|
269
|
+
* positional spread and simply overrode fusion. Worse, `priorMeta` returns null
|
|
270
|
+
* for structural refs, which left them at w = 1 — a ceiling above what any record
|
|
271
|
+
* that is not both human_confirmed and brand-new can reach. Measured over this
|
|
272
|
+
* repo's 152 committed decisions with the rest of the pool structural (symbols are
|
|
273
|
+
* ~91% of the corpus): 38% of decisions were dropped from the top-12 even when
|
|
274
|
+
* they were the #1 fused hit, and 88% from fused rank 8. Both retrieval layers
|
|
275
|
+
* ranked the record first and the rerank alone threw it away.
|
|
276
|
+
*
|
|
277
|
+
* A shift of at most ±MAX_PRIOR_SHIFT positions restores the intended semantics:
|
|
278
|
+
* a stale or low-provenance record visibly dims, an exact runbook-trigger match
|
|
279
|
+
* visibly promotes, and neither can leapfrog the whole pool. Same measurement
|
|
280
|
+
* after: 0% evicted from fused ranks 0–8. Deterministic; ties keep fused order. */
|
|
281
|
+
private rerankByPriors;
|
|
282
|
+
/**
|
|
283
|
+
* Score only freshness that the existing graph clocks can prove: an anchored path changed after
|
|
284
|
+
* `provenance.last_verified`. One bounded Git pass fills a HEAD-keyed cache for newly encountered
|
|
285
|
+
* scopes; repeated MCP/CLI queries perform no history walk until HEAD changes.
|
|
286
|
+
*/
|
|
287
|
+
private staleDecisionIds;
|
|
288
|
+
/** Fast relevance ranking for task-phrase context fallback: FTS + bounded graph priors, no model. */
|
|
289
|
+
rankedSearch(query: string, limit?: number, opts?: RankedSearchOpts): SearchHit[];
|
|
290
|
+
/** The prior-bearing metadata for a hit: liveness, provenance, effective date,
|
|
291
|
+
* and (runbooks) trigger phrases. null = structural ref, neutral prior. */
|
|
292
|
+
private priorMeta;
|
|
293
|
+
/** Hybrid search (hunch_query / `hunch query --semantic`): FTS bm25 fused with
|
|
294
|
+
* cosine over stored embeddings via Reciprocal Rank Fusion. Degrades to pure
|
|
295
|
+
* sync FTS (zero added latency) when there's no embedder or no vectors yet, so
|
|
296
|
+
* the lean install and fallback regressions are unaffected. Pass
|
|
297
|
+
* `embedder: null` to FORCE FTS-only without auto-selecting. */
|
|
298
|
+
hybridSearch(query: string, limit?: number, opts?: HybridSearchOpts): Promise<SearchHit[]>;
|
|
299
|
+
/** Runbook-scoped retrieval (roadmap #5): the same FTS+graph(+semantic) fusion,
|
|
300
|
+
* restricted to the `runbooks` kind — so a "what's the procedure for X" query
|
|
301
|
+
* competes only with other runbooks, not the whole graph. Measurement showed
|
|
302
|
+
* whole-corpus retrieval buries terse runbooks (33% recall); scoping + semantic
|
|
303
|
+
* lifted recall@5 to 83% (dec_1239efae54 follow-up). Pass an embedder for the
|
|
304
|
+
* semantic leg; omit for keyword+graph. */
|
|
305
|
+
searchRunbooks(query: string, limit?: number, opts?: {
|
|
306
|
+
embedder?: Embedder | null;
|
|
307
|
+
}): Promise<SearchHit[]>;
|
|
308
|
+
/** Kind-SCOPED retrieval: FTS + (optional) semantic fused, but the candidate pool is
|
|
309
|
+
* restricted to one record kind from the START — not over-fetched from a whole-corpus
|
|
310
|
+
* ranking (whose top-50 cap can bury a terse record before any filter). This is what
|
|
311
|
+
* lifted runbook recall@5 from 33% → 83% in the measurement (dec_1239efae54 follow-up). */
|
|
312
|
+
searchScoped(query: string, kind: string, limit?: number, opts?: {
|
|
313
|
+
embedder?: Embedder | null;
|
|
314
|
+
}): Promise<SearchHit[]>;
|
|
315
|
+
/** FTS bm25 over a single kind (the `kind` column is UNINDEXED, so a plain `=`
|
|
316
|
+
* constraint composes with MATCH). Empty when the query has no FTS-able terms. */
|
|
317
|
+
private scopedFts;
|
|
318
|
+
/** Brute-force exact cosine top-n over stored vectors for one model. Vectors are
|
|
319
|
+
* pre-normalized, so cosine == dot product. Scoped to `dim = qvec.length` so a
|
|
320
|
+
* row stored at a different dimension (model id reused at a new dim) can never
|
|
321
|
+
* drive an out-of-bounds BLOB read; any with an unexpected byte length are
|
|
322
|
+
* skipped defensively rather than crashing the query. */
|
|
323
|
+
private cosineRank;
|
|
324
|
+
/** Rank-based Reciprocal Rank Fusion of the FTS, semantic, and graph lists. Ranks
|
|
325
|
+
* (not raw scores) erase the bm25-vs-cosine-vs-graph scale mismatch; a small lexical
|
|
326
|
+
* weight keeps exact symbol/path matches from being displaced by paraphrase or
|
|
327
|
+
* neighbor hits. An empty list contributes nothing, so 2-stream behavior is exactly
|
|
328
|
+
* preserved when graph (or sem) is absent. */
|
|
329
|
+
private rrfFuse;
|
|
330
|
+
/** Bounded relevance traversal over the dependency graph. Lexical/semantic symbol
|
|
331
|
+
* and component hits seed a small number of depth layers; support decays per hop
|
|
332
|
+
* and adds across multiple useful paths. Each frontier and the returned context
|
|
333
|
+
* obey a hard node cap, while hydration obeys a separate token cap. Only records
|
|
334
|
+
* present in the indexed symbol/component tables can enter the frontier, so
|
|
335
|
+
* shared external-package hubs never become context or bridge unrelated symbols. */
|
|
336
|
+
private graphExpand;
|
|
337
|
+
/** All decisions/bugs/constraints/symbols/components touching a file path or
|
|
338
|
+
* symbol name (hunch_why). Pass `{ asOf }` (an ISO instant) to TIME-TRAVEL:
|
|
339
|
+
* return only decisions/constraints whose valid-time window contained that
|
|
340
|
+
* instant — "what did we believe as of commit X?". Omit `asOf` for the full,
|
|
341
|
+
* history-inclusive view (backward-compatible default). */
|
|
342
|
+
why(target: string, opts?: {
|
|
343
|
+
asOf?: string;
|
|
344
|
+
}): WhyResult;
|
|
345
|
+
/** Transitive blast radius: every symbol/component that (in)directly depends on
|
|
346
|
+
* `id`, via a recursive CTE over the edges graph (hunch_get_dependents). We
|
|
347
|
+
* walk edges BACKWARD (edges.to = current) following call/dep/import/contains. */
|
|
348
|
+
getDependents(id: string, maxDepth?: number): Array<{
|
|
349
|
+
id: string;
|
|
350
|
+
depth: number;
|
|
351
|
+
via: string;
|
|
352
|
+
}>;
|
|
353
|
+
/** Symbols/components this id depends ON (forward walk) — used for refactor blast radius. */
|
|
354
|
+
getDependencies(id: string, maxDepth?: number): Array<{
|
|
355
|
+
id: string;
|
|
356
|
+
depth: number;
|
|
357
|
+
via: string;
|
|
358
|
+
}>;
|
|
359
|
+
/** Files whose symbols (in)directly DEPEND ON a symbol defined in `file` — the
|
|
360
|
+
* blast radius of editing `file`, collapsed to file granularity (nearest depth
|
|
361
|
+
* wins per file). Powers `hunch check` near-violation detection and `--blast`. */
|
|
362
|
+
blastRadiusFiles(file: string, maxDepth?: number): Array<{
|
|
363
|
+
file: string;
|
|
364
|
+
via: string;
|
|
365
|
+
depth: number;
|
|
366
|
+
}>;
|
|
367
|
+
/** Shortest undirected path between two graph nodes (symbols/components) over
|
|
368
|
+
* call/dep/import/contains/type-relation edges — "how does A reach B?" (hunch
|
|
369
|
+
* path / hunch_path). An iterative BFS visits each node once; the old recursive
|
|
370
|
+
* SQL enumerated simple paths and grew exponentially on dense real repositories.
|
|
371
|
+
* Returns the node chain in order, or null when no path exists within maxDepth. */
|
|
372
|
+
shortestPath(fromId: string, toId: string, maxDepth?: number): Array<{
|
|
373
|
+
id: string;
|
|
374
|
+
via: string;
|
|
375
|
+
}> | null;
|
|
376
|
+
/** Human label for a graph node id: "name @ file" for a symbol, the component name,
|
|
377
|
+
* or the id itself when unindexed. */
|
|
378
|
+
nodeLabel(id: string): string;
|
|
379
|
+
/** Resolve a free-form target (symbol id / name / file path, component id / name)
|
|
380
|
+
* to graph node ids — symbols win over components, exact file before suffix. */
|
|
381
|
+
resolveNodeIds(target: string): string[];
|
|
382
|
+
/** PR impact (read-only, ADVISORY — never gates): the dependency + memory surface
|
|
383
|
+
* of a change. Composes the SAME primitives as buildCheckReport (blast radius,
|
|
384
|
+
* scope-matched constraints, why) so impact and gating can never disagree. */
|
|
385
|
+
prImpact(files: string[], diff: string): ImpactReport;
|
|
386
|
+
/** Structure view (hunch_structure / hunch structure): serve the indexed shape of
|
|
387
|
+
* the repo so an agent ORIENTS from the graph instead of running grep/glob rounds.
|
|
388
|
+
* Resolution: no target -> repo map; a directory -> its files+symbols; a file ->
|
|
389
|
+
* its outline; a symbol name -> exact definition site(s) with one-hop neighbors.
|
|
390
|
+
* Deterministic, read-only, straight from the derived index. */
|
|
391
|
+
structure(target?: string): StructureView;
|
|
392
|
+
/** Labelled one-hop edge neighbors of a node ("in" = who reaches it, "out" = what it reaches). */
|
|
393
|
+
private edgeNeighbors;
|
|
394
|
+
/** Constraints whose scope glob matches a path/glob (hunch_check_constraints).
|
|
395
|
+
* By default only ACTIVE invariants are returned — a retired constraint is no
|
|
396
|
+
* longer enforced. Pass `{ asOf }` to instead return the invariants in force at
|
|
397
|
+
* that instant (time-travel: "what must I not have broken as of commit X?"). */
|
|
398
|
+
checkConstraints(scope: string, opts?: {
|
|
399
|
+
asOf?: string;
|
|
400
|
+
}): Constraint[];
|
|
401
|
+
/** LIVE findings (observations — audited, no diff yet) concerning a file/scope:
|
|
402
|
+
* triage open / accepted-risk / scheduled; resolved and stale stay silent. The
|
|
403
|
+
* matcher mirrors checkConstraints: an affected entry may be a concrete path or a
|
|
404
|
+
* glob, and the queried scope may be either too. Advisory only — findings never
|
|
405
|
+
* enter any block path. Sorted worst-first, then id for stable output. */
|
|
406
|
+
liveFindingsFor(scope: string): Finding[];
|
|
407
|
+
/** The causal chain behind a constraint — the WHY a diff-only reviewer can't see.
|
|
408
|
+
* Deterministic graph join: constraint → source_decision (the decision that
|
|
409
|
+
* motivated the guard) → the bug whose root cause spawned it (via
|
|
410
|
+
* lineage.spawned_constraint, else the source decision's caused_by_bug). Read-only. */
|
|
411
|
+
causalChain(constraintId: string): CausalWhy;
|
|
412
|
+
/** Assemble a CheckReport from a diff: direct invariant hits, near hits (blast
|
|
413
|
+
* radius), and regressions (re-added retired code), with the hardened strict
|
|
414
|
+
* gate and a causal `why` citation per direct hit. Read-only — shared by
|
|
415
|
+
* `hunch check`, the CI guard, and hunch_merge_verdict so they never drift. */
|
|
416
|
+
buildCheckReport(files: string[], diff: string, opts: {
|
|
417
|
+
strict: boolean;
|
|
418
|
+
lastChange?: (f: string) => string;
|
|
419
|
+
publicOnly?: boolean;
|
|
420
|
+
}): CheckReport;
|
|
421
|
+
/** Sprawl/"this already exists" guard (ADVISORY, never blocks). A symbol the diff
|
|
422
|
+
* ADDS whose name already exists in the indexed graph in a file NOT touched by the
|
|
423
|
+
* diff is a likely re-implementation the agent's local context window couldn't see.
|
|
424
|
+
* Read-only. Heuristic, so noise is controlled: top-level function/class/const only,
|
|
425
|
+
* name length ≥ 4, a stopword list of generic names, deduped, and capped. */
|
|
426
|
+
redundantSymbols(added: Array<{
|
|
427
|
+
name: string;
|
|
428
|
+
kind: string;
|
|
429
|
+
}>, files: string[], opts?: {
|
|
430
|
+
movedFrom?: string[];
|
|
431
|
+
removedNames?: Set<string>;
|
|
432
|
+
}): Array<{
|
|
433
|
+
name: string;
|
|
434
|
+
kind: string;
|
|
435
|
+
existingFile: string;
|
|
436
|
+
}>;
|
|
437
|
+
/** Time-travel: the decision history for a target — every decision touching it,
|
|
438
|
+
* newest-first, with its valid-time window and supersession links. Answers
|
|
439
|
+
* "what did we believe, and when/why did it change?" (hunch_timeline). */
|
|
440
|
+
timeline(target: string): Decision[];
|
|
441
|
+
/** Invalidate, don't delete (Zep edge-invalidation): close `oldId`'s valid-time
|
|
442
|
+
* window at the superseding decision's `valid_from`, mark it superseded + linked,
|
|
443
|
+
* and write a `supersedes` edge. Returns the updated old decision, or null if it
|
|
444
|
+
* doesn't exist. All writes are atomic via json.put (con_902759b3dc). */
|
|
445
|
+
supersede(oldId: string, by: Decision): Decision | null;
|
|
446
|
+
/** Look up a decision by id in a SPECIFIC store — the public store, or the private
|
|
447
|
+
* overlay when `priv` is true — NOT the union. The capture guard uses this to know
|
|
448
|
+
* whether a supersede will actually close its target: `supersede`/`supersedePrivate`
|
|
449
|
+
* each look in only one store, so a cross-store supersede silently no-ops and would
|
|
450
|
+
* leave two live decisions on one topic. Returns undefined if absent (or no overlay). */
|
|
451
|
+
decisionInStore(id: string, priv: boolean): Decision | undefined;
|
|
452
|
+
/** Private-overlay counterpart of `supersede`: close + link the old decision inside
|
|
453
|
+
* the HUNCH_PRIVATE_DIR store, so a PRIVATE decision can supersede another private
|
|
454
|
+
* one (the MCP record path is private→private). A private write never mutates the
|
|
455
|
+
* committed public store. Returns null if no private store is configured or the old
|
|
456
|
+
* record isn't in it. */
|
|
457
|
+
supersedePrivate(oldId: string, by: Decision): Decision | null;
|
|
458
|
+
/** Shared body for supersede / supersedePrivate against a specific store. */
|
|
459
|
+
private supersedeIn;
|
|
460
|
+
/** Regression Guard: detect a change RE-INTRODUCING something an in-force
|
|
461
|
+
* decision deliberately removed. Matches the added symbols/deps of a diff
|
|
462
|
+
* against the `retired` signal of decisions concerning the touched files. A hit
|
|
463
|
+
* is `blocking` when the retiring decision is tied to an ACTIVE blocking
|
|
464
|
+
* constraint (via source_decision) — that's the only case the strict guard
|
|
465
|
+
* fails the commit on; everything else is an advisory warning. */
|
|
466
|
+
regressionHits(added: {
|
|
467
|
+
symbols: string[];
|
|
468
|
+
deps: string[];
|
|
469
|
+
}, files: string[]): RegressionHit[];
|
|
470
|
+
/** Veto Guard: detect a change RE-INTRODUCING an approach an in-force decision
|
|
471
|
+
* deliberately REJECTED (`decision.rejected_tripwires`). The counterpart to
|
|
472
|
+
* regressionHits, which only sees code that once existed — a rejected alternative
|
|
473
|
+
* never did. Precision-first ladder (dep > symbol > pattern); the semantic tier is
|
|
474
|
+
* advisory and lives elsewhere. A hit `blocks` only when isVetoBlocker passes (a
|
|
475
|
+
* human-confirmed tripwire on an in-force, non-stale decision — dec_a466655539).
|
|
476
|
+
* Read-only; shared by buildCheckReport. */
|
|
477
|
+
vetoHits(an: DiffAnalysis, files: string[], staleDecisions?: Set<string>): VetoHit[];
|
|
478
|
+
/** Resolve a veto's causal citation: the bug whose root cause spawned the decision
|
|
479
|
+
* (decision → caused_by_bug). Distinct from causalChain, which is constraint-keyed. */
|
|
480
|
+
private vetoWhy;
|
|
481
|
+
/** Veto check for a LIVE edit (the agent pre-edit hook): no diff exists yet, so
|
|
482
|
+
* synthesize a minimal added-only diff from the proposed new lines and run the
|
|
483
|
+
* same vetoHits ladder. Freshness needs git lastChange (unavailable at edit time),
|
|
484
|
+
* so the staleness gate is skipped here — the commit/CI path applies it. */
|
|
485
|
+
vetoForFileEdit(file: string, addedLines: string[]): VetoHit[];
|
|
486
|
+
/** The symbols/deps an in-force decision deliberately RETIRED from a file — the
|
|
487
|
+
* agent-hook grounding ("don't re-add X here; dec_Y removed it"). No diff is
|
|
488
|
+
* available at edit time, so this surfaces the risk as context, not a block. */
|
|
489
|
+
retiredForFile(file: string): RetiredNote[];
|
|
490
|
+
/** Bugs matching a symptom (FTS over bugs) or a symbol, with lineage (hunch_bug_lineage). */
|
|
491
|
+
bugLineage(symptomOrSymbol: string): Bug[];
|
|
492
|
+
/** Ranked fragility report (hunch fragile). fragility = weighted churn + bugs + fan-in. */
|
|
493
|
+
fragility(limit?: number): FragileNode[];
|
|
494
|
+
/** Convenience: load a single entity from JSON by id (any kind). */
|
|
495
|
+
resolve(id: string): {
|
|
496
|
+
kind: string;
|
|
497
|
+
record: unknown;
|
|
498
|
+
} | undefined;
|
|
499
|
+
/** All edges (for graph export). */
|
|
500
|
+
allEdges(): Edge[];
|
|
501
|
+
/** Drift detection (DESIGN §9 "staleness kills trust"): a decision/constraint
|
|
502
|
+
* is STALE when a file in its scope changed AFTER it was last verified. The
|
|
503
|
+
* caller supplies `lastChange(file) -> ISO date | ""` (git-backed). */
|
|
504
|
+
staleness(lastChange: (file: string) => string): StaleRecord[];
|
|
505
|
+
/** The Context Assembler (DESIGN §2.1/§6): the MINIMAL relevant Hunch slice for
|
|
506
|
+
* a task on `target`, ordered by what matters most — invariants first, then the
|
|
507
|
+
* why, then blast radius and bug history — trimmed to a rough token budget. */
|
|
508
|
+
assembleContext(target: string, budget?: number, opts?: {
|
|
509
|
+
asOf?: string;
|
|
510
|
+
}): AssembledContext;
|
|
511
|
+
}
|
|
512
|
+
/** The graph-served repo shape (hunch_structure) — orient without grep rounds. */
|
|
513
|
+
export type StructureView = {
|
|
514
|
+
kind: "repo";
|
|
515
|
+
components: Array<{
|
|
516
|
+
id: string;
|
|
517
|
+
name: string;
|
|
518
|
+
responsibility: string;
|
|
519
|
+
paths: string[];
|
|
520
|
+
}>;
|
|
521
|
+
dirs: Array<{
|
|
522
|
+
dir: string;
|
|
523
|
+
files: number;
|
|
524
|
+
symbols: number;
|
|
525
|
+
}>;
|
|
526
|
+
} | {
|
|
527
|
+
kind: "dir";
|
|
528
|
+
dir: string;
|
|
529
|
+
files: Array<{
|
|
530
|
+
file: string;
|
|
531
|
+
symbols: Array<{
|
|
532
|
+
name: string;
|
|
533
|
+
kind: string;
|
|
534
|
+
fan_in: number;
|
|
535
|
+
}>;
|
|
536
|
+
}>;
|
|
537
|
+
} | {
|
|
538
|
+
kind: "file";
|
|
539
|
+
file: string;
|
|
540
|
+
symbols: Array<{
|
|
541
|
+
id: string;
|
|
542
|
+
name: string;
|
|
543
|
+
kind: string;
|
|
544
|
+
loc: number;
|
|
545
|
+
fan_in: number;
|
|
546
|
+
fan_out: number;
|
|
547
|
+
callers: string[];
|
|
548
|
+
}>;
|
|
549
|
+
} | {
|
|
550
|
+
kind: "symbol";
|
|
551
|
+
matches: Array<{
|
|
552
|
+
id: string;
|
|
553
|
+
name: string;
|
|
554
|
+
kind: string;
|
|
555
|
+
file: string;
|
|
556
|
+
fan_in: number;
|
|
557
|
+
fan_out: number;
|
|
558
|
+
callers: string[];
|
|
559
|
+
callees: string[];
|
|
560
|
+
}>;
|
|
561
|
+
} | {
|
|
562
|
+
kind: "none";
|
|
563
|
+
target: string;
|
|
564
|
+
};
|
|
565
|
+
export interface StaleRecord {
|
|
566
|
+
kind: string;
|
|
567
|
+
id: string;
|
|
568
|
+
last_verified: string;
|
|
569
|
+
changed_at: string;
|
|
570
|
+
files: string[];
|
|
571
|
+
}
|
|
572
|
+
/** A diff re-introducing something a decision deliberately removed. */
|
|
573
|
+
export interface RegressionHit {
|
|
574
|
+
decision: string;
|
|
575
|
+
title: string;
|
|
576
|
+
kind: "symbol" | "dep";
|
|
577
|
+
name: string;
|
|
578
|
+
/** True only when the retiring decision is tied to an active blocking invariant. */
|
|
579
|
+
blocking: boolean;
|
|
580
|
+
reason: string;
|
|
581
|
+
}
|
|
582
|
+
/** A diff re-introducing an approach an in-force decision deliberately REJECTED. */
|
|
583
|
+
export interface VetoHit {
|
|
584
|
+
decision: string;
|
|
585
|
+
title: string;
|
|
586
|
+
/** The rejected approach's text — the receipt headline. */
|
|
587
|
+
alternative: string;
|
|
588
|
+
/** What was chosen instead (decision.decision). */
|
|
589
|
+
chosen: string;
|
|
590
|
+
tier: VetoTier;
|
|
591
|
+
evidence: string[];
|
|
592
|
+
/** True only when the matched tripwire is human-confirmed, in-force, non-stale. */
|
|
593
|
+
blocks: boolean;
|
|
594
|
+
why?: {
|
|
595
|
+
bug?: {
|
|
596
|
+
id: string;
|
|
597
|
+
title: string;
|
|
598
|
+
root_cause: string;
|
|
599
|
+
};
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
/** What an in-force decision retired from a file (agent-hook grounding). */
|
|
603
|
+
export interface RetiredNote {
|
|
604
|
+
decision: string;
|
|
605
|
+
title: string;
|
|
606
|
+
symbols: string[];
|
|
607
|
+
deps: string[];
|
|
608
|
+
}
|
|
609
|
+
export interface AssembledContext {
|
|
610
|
+
target: string;
|
|
611
|
+
constraints: Constraint[];
|
|
612
|
+
decisions: Decision[];
|
|
613
|
+
bugs: Bug[];
|
|
614
|
+
blast_radius: Array<{
|
|
615
|
+
id: string;
|
|
616
|
+
depth: number;
|
|
617
|
+
via: string;
|
|
618
|
+
}>;
|
|
619
|
+
components: Component[];
|
|
620
|
+
findings: Finding[];
|
|
621
|
+
/** Current, explicitly reviewed Engineering Landscape orientation. */
|
|
622
|
+
landscape?: ReviewedLandscapeSelection;
|
|
623
|
+
budget_tokens: number;
|
|
624
|
+
}
|
package/dist/store/hunchStore.js
CHANGED
|
@@ -1992,9 +1992,12 @@ const DECISION_FRESHNESS_PATH_CACHE_CAP = 4_096;
|
|
|
1992
1992
|
* had correctly injected just below the cut line. This lifts memory records by a
|
|
1993
1993
|
* bounded number of positions; it never EXCLUDES a kind (a symbol-name query still
|
|
1994
1994
|
* returns symbols, and a constraint stays reachable), it only breaks the tie toward
|
|
1995
|
-
* intent.
|
|
1996
|
-
*
|
|
1997
|
-
|
|
1995
|
+
* intent. The task-report additions diluted one previously reachable decision:
|
|
1996
|
+
* on the same memory corpus, 12 positions gave Recall@10 8/11 (MRR .470);
|
|
1997
|
+
* 16 restores 9/11 (.483), preserving the existing floor and exact-symbol controls.
|
|
1998
|
+
* This is bounded calibration, not immunity to arbitrary corpus growth.
|
|
1999
|
+
* Set HUNCH_MEMORY_PRIOR_SHIFT=0 to disable. */
|
|
2000
|
+
const MEMORY_PRIOR_SHIFT = numEnv("HUNCH_MEMORY_PRIOR_SHIFT", 16);
|
|
1998
2001
|
const MEMORY_KINDS = new Set(["decisions", "constraints", "bugs", "runbooks", "policies", ...STATE_KINDS]);
|
|
1999
2002
|
/** State-of-record ordering in the RAW search path: a nuryel.state/1 history hit (superseded
|
|
2000
2003
|
* derived, done/cancelled commitment, failed receipt, retired entity) keeps this fraction of
|