@davesheffer/hunch 1.31.0 → 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 +3 -6
- 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/client/state.js +2 -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/stateContract.js +87 -4
- package/dist/core/stateDelivery.d.ts +78 -0
- package/dist/core/stateRecords.d.ts +383 -0
- package/dist/core/stateRecords.js +16 -1
- 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 +114 -25
- 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/app.js +20 -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/changeLedger.js +40 -9
- 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 +13 -5
- package/dist/store/jsonStore.d.ts +125 -0
- package/dist/store/jsonStore.js +15 -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/stateBinding.js +110 -15
- package/dist/store/stateCapture.d.ts +15 -0
- package/dist/store/stateCapture.js +145 -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,13 @@
|
|
|
1
|
+
/** Canonical committed identity for the deterministic static graph.
|
|
2
|
+
*
|
|
3
|
+
* Public Hunch pumping creates clone-local commits containing `.hunch/` JSON and
|
|
4
|
+
* sometimes refreshed grounding docs. Those commits do not change anything the
|
|
5
|
+
* indexer parses, so binding a shared static receipt to their local SHA makes the
|
|
6
|
+
* same graph produce different artifact ids in otherwise identical clones. Walk
|
|
7
|
+
* first-parent until the newest indexed-code or checkout-attribute change instead.
|
|
8
|
+
* Attributes are a boundary because proof replay validates them before materializing
|
|
9
|
+
* exact source bytes. A merge is always a boundary: its resolution can change the
|
|
10
|
+
* effective tree even when a simple diff listing is incomplete. Reverts touch code
|
|
11
|
+
* and therefore remain distinct. */
|
|
12
|
+
export declare function canonicalStaticGraphBaseline(root: string, ref?: string): string;
|
|
13
|
+
export declare function isAncestorOrSame(root: string, ancestor: string, descendant: string): boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Decision } from "../core/types.js";
|
|
2
|
+
import type { HunchStore } from "../store/hunchStore.js";
|
|
3
|
+
import { type BootstrapOptions, type BootstrapReport } from "./bootstrap.js";
|
|
4
|
+
import type { PolicyRepository } from "./repository.js";
|
|
5
|
+
import { type PolicyAssertion, type PolicySpec, type StructuralDelta } from "./schema.js";
|
|
6
|
+
export interface StructuralCandidate {
|
|
7
|
+
id: string;
|
|
8
|
+
assertion: PolicyAssertion;
|
|
9
|
+
scope: PolicySpec["scope"];
|
|
10
|
+
basis: "added-call" | "removed-call" | "added-symbol" | "removed-import" | "added-relative-import" | "removed-relative-import";
|
|
11
|
+
reason: string;
|
|
12
|
+
}
|
|
13
|
+
export interface StructuralInspection {
|
|
14
|
+
decision: string;
|
|
15
|
+
commit: string;
|
|
16
|
+
kind: "revert" | "bug_fix" | "decision";
|
|
17
|
+
delta: StructuralDelta;
|
|
18
|
+
candidates: StructuralCandidate[];
|
|
19
|
+
unsupported: string[];
|
|
20
|
+
private: boolean;
|
|
21
|
+
}
|
|
22
|
+
interface Enumerated {
|
|
23
|
+
candidates: StructuralCandidate[];
|
|
24
|
+
unsupported: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface ExternalImportBoundaryCandidate {
|
|
27
|
+
id: string;
|
|
28
|
+
assertion: PolicyAssertion;
|
|
29
|
+
scope: PolicySpec["scope"];
|
|
30
|
+
basis: "correction-forbidden-import";
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
33
|
+
export type ExternalImportBoundaryInspection = {
|
|
34
|
+
candidate: ExternalImportBoundaryCandidate;
|
|
35
|
+
reason: null;
|
|
36
|
+
} | {
|
|
37
|
+
candidate: null;
|
|
38
|
+
reason: string;
|
|
39
|
+
code: "unsupported_dependency" | "unsupported_file" | "missing_anchor" | "baseline_violated";
|
|
40
|
+
};
|
|
41
|
+
/** Bind one exact file/package boundary to the existing static import graph.
|
|
42
|
+
* The file-level fact is anchored to the first stable symbol because the
|
|
43
|
+
* indexer attaches every external import in a file to every symbol in it. */
|
|
44
|
+
export declare function inspectExternalImportBoundary(store: HunchStore, file: string, specifier: string, opts?: {
|
|
45
|
+
publicOnly?: boolean;
|
|
46
|
+
}): ExternalImportBoundaryInspection;
|
|
47
|
+
export declare function structuralKey(policy: Pick<PolicySpec, "assertion" | "scope" | "data_class">): string;
|
|
48
|
+
export declare function directConflict(candidate: PolicySpec, incumbent: PolicySpec): boolean;
|
|
49
|
+
/** Enumerate every supported assertion that the current graph can bind exactly.
|
|
50
|
+
* Ambiguous or missing bindings are reported; they are never guessed through. */
|
|
51
|
+
export declare function enumerateStructuralCandidates(store: HunchStore, delta: StructuralDelta, opts?: {
|
|
52
|
+
publicOnly?: boolean;
|
|
53
|
+
judgment?: Decision;
|
|
54
|
+
retiredDependenciesOnly?: boolean;
|
|
55
|
+
}): Enumerated;
|
|
56
|
+
export declare function inspectStructuralDecision(store: HunchStore, root: string, decisionId: string, opts?: {
|
|
57
|
+
publicOnly?: boolean;
|
|
58
|
+
privateOnly?: boolean;
|
|
59
|
+
}): StructuralInspection;
|
|
60
|
+
/** Phase-2B opt-in bootstrap: only human-confirmed fix/revert or explicit
|
|
61
|
+
* dependency-retirement decisions, exact
|
|
62
|
+
* first-parent deltas, and exactly one bindable candidate may compile. */
|
|
63
|
+
export declare function bootstrapStructuralPolicies(store: HunchStore, root: string, repository: PolicyRepository, opts?: BootstrapOptions): BootstrapReport;
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider hook dialects → Hunch's one internal event shape.
|
|
3
|
+
*
|
|
4
|
+
* Hook payloads are an integration boundary: every provider is free to rename
|
|
5
|
+
* fields or tools. Keep that variability here so the policy engine receives
|
|
6
|
+
* the same small, fail-open shape regardless of the assistant that emitted it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const HOOK_PROVIDERS: readonly ["claude", "vscode", "windsurf", "antigravity", "cursor"];
|
|
9
|
+
export type HookProvider = (typeof HOOK_PROVIDERS)[number];
|
|
10
|
+
export type HunchHookEvent = "PreToolUse" | "PostToolUse" | "PostToolUseFailure" | "UserPromptSubmit" | "SessionStart" | "SubagentStart" | "PreCompact" | "Stop";
|
|
11
|
+
export interface HunchToolInput {
|
|
12
|
+
file_path?: string;
|
|
13
|
+
new_string?: string;
|
|
14
|
+
content?: string;
|
|
15
|
+
edits?: Array<{
|
|
16
|
+
new_string?: string;
|
|
17
|
+
}>;
|
|
18
|
+
command?: string;
|
|
19
|
+
skill?: string;
|
|
20
|
+
}
|
|
21
|
+
/** A provider-neutral observation of the tool result. Output is ephemeral: the
|
|
22
|
+
* pipeline compares it with bounded expectations but never persists it. */
|
|
23
|
+
export interface HunchToolOutcome {
|
|
24
|
+
status: "success" | "failure" | "unknown";
|
|
25
|
+
output: string;
|
|
26
|
+
}
|
|
27
|
+
export interface HunchHookInput {
|
|
28
|
+
hook_event_name: HunchHookEvent;
|
|
29
|
+
session_id?: string;
|
|
30
|
+
/** Native Claude correlation; never synthesize these from prompt text/time. */
|
|
31
|
+
prompt_id?: string;
|
|
32
|
+
cwd?: string;
|
|
33
|
+
agent_id?: string;
|
|
34
|
+
tool_name?: string;
|
|
35
|
+
tool_input?: HunchToolInput;
|
|
36
|
+
tool_outcome?: HunchToolOutcome;
|
|
37
|
+
prompt?: string;
|
|
38
|
+
/** SessionStart origin ("startup" | "resume" | "clear" | "compact") — compaction
|
|
39
|
+
* summarizes away injected grounding, so "compact" resets injection dedup. */
|
|
40
|
+
source?: string;
|
|
41
|
+
/** SubagentStart: the delegated agent's type (e.g. "Explore", "Plan"). */
|
|
42
|
+
agent_type?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Parse a provider name supplied by a hook config. Unknown values intentionally
|
|
45
|
+
* return null so a bad config cannot make an edit fail. */
|
|
46
|
+
export declare function hookProvider(value: unknown): HookProvider | null;
|
|
47
|
+
/** Normalize a hook stdin payload. Unknown/malformed events return null and the
|
|
48
|
+
* CLI exits successfully without output — the Never Block on Hook Failure rule. */
|
|
49
|
+
export declare function normalizeHookEvent(raw: unknown, provider: HookProvider): HunchHookInput | null;
|
|
50
|
+
/** Provider-aware hook output. Context output is intentionally omitted for
|
|
51
|
+
* Windsurf because its documented hook protocol has no agent-context channel;
|
|
52
|
+
* its always-on project rule + MCP server remain the grounding delivery path. */
|
|
53
|
+
export declare function contextHookOutput(provider: HookProvider, event: HunchHookEvent, text: string): object | null;
|
|
54
|
+
/** Strict-deny response in each native dialect. Windsurf uses documented exit
|
|
55
|
+
* code 2; the caller writes this error to stderr and preserves exit success for
|
|
56
|
+
* every accidental/malformed invocation. */
|
|
57
|
+
export declare function denyHookOutput(provider: HookProvider, reason: string): {
|
|
58
|
+
output: object | null;
|
|
59
|
+
exitCode?: number;
|
|
60
|
+
stderr?: string;
|
|
61
|
+
};
|
|
62
|
+
/** Stop-gate output in each native dialect. */
|
|
63
|
+
export declare function stopHookOutput(provider: HookProvider, reason: string): object;
|
package/dist/core/agenthook.js
CHANGED
|
@@ -194,6 +194,7 @@ export function normalizeHookEvent(raw, provider) {
|
|
|
194
194
|
return {
|
|
195
195
|
hook_event_name: event,
|
|
196
196
|
session_id: stringAt(input, "session_id", "sessionId", "conversation_id", "conversationId"),
|
|
197
|
+
...(provider === "claude" ? Object.fromEntries(["prompt_id", "cwd", "agent_id"].filter(key => input[key] !== undefined).map(key => [key, typeof input[key] === "string" ? input[key] : ""])) : {}),
|
|
197
198
|
tool_name: hunchToolName(stringAt(input, "tool_name", "toolName"), toolInput ?? {}),
|
|
198
199
|
tool_input: toolInput,
|
|
199
200
|
...(toolOutcome ? { tool_outcome: toolOutcome } : {}),
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Constraint } from "./types.js";
|
|
2
|
+
export interface AutomaticReviewEntry {
|
|
3
|
+
candidate_id: string;
|
|
4
|
+
evidence_hash: string;
|
|
5
|
+
file: string;
|
|
6
|
+
status: "ready" | "saved" | "skipped" | "review" | "deferred";
|
|
7
|
+
reason: string;
|
|
8
|
+
code_hash?: string;
|
|
9
|
+
rule_id?: string;
|
|
10
|
+
cached?: boolean;
|
|
11
|
+
retryable?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Bounded sequential analysis with a separate skeptical pass. The caller owns I/O and persistence. */
|
|
14
|
+
export declare function automateReviewMemory(options: {
|
|
15
|
+
packet: unknown;
|
|
16
|
+
existing: Constraint[];
|
|
17
|
+
readCurrent: (file: string) => string;
|
|
18
|
+
generate: (prompt: string) => Promise<string>;
|
|
19
|
+
provider: string;
|
|
20
|
+
limit: number;
|
|
21
|
+
now: string;
|
|
22
|
+
progress?: (candidate: string, position: number) => void;
|
|
23
|
+
previous?: AutomaticReviewEntry[];
|
|
24
|
+
providersUsed?: () => string[];
|
|
25
|
+
}): Promise<{
|
|
26
|
+
schema: "hunch.automatic-review-memory/1";
|
|
27
|
+
repository: string;
|
|
28
|
+
provider: string;
|
|
29
|
+
providers_used: string[];
|
|
30
|
+
authority: "advisory";
|
|
31
|
+
applied: boolean;
|
|
32
|
+
analyzed: number;
|
|
33
|
+
entries: AutomaticReviewEntry[];
|
|
34
|
+
rules: {
|
|
35
|
+
id: string;
|
|
36
|
+
type: "security" | "performance" | "correctness" | "architecture" | "compliance";
|
|
37
|
+
statement: string;
|
|
38
|
+
scope: string[];
|
|
39
|
+
severity: "advisory" | "warning" | "blocking";
|
|
40
|
+
enforcement: "ci" | "advisory_v1" | "manual";
|
|
41
|
+
match: string | null;
|
|
42
|
+
forbids: {
|
|
43
|
+
deps: string[];
|
|
44
|
+
symbols: string[];
|
|
45
|
+
patterns: string[];
|
|
46
|
+
} | null;
|
|
47
|
+
rationale: string;
|
|
48
|
+
source_decision: string | null;
|
|
49
|
+
violations: string[];
|
|
50
|
+
status: "active" | "retired";
|
|
51
|
+
valid_to: string | null;
|
|
52
|
+
provenance: {
|
|
53
|
+
source: string;
|
|
54
|
+
confidence: number;
|
|
55
|
+
evidence: string[];
|
|
56
|
+
last_verified?: string | undefined;
|
|
57
|
+
};
|
|
58
|
+
valid_from?: string | undefined;
|
|
59
|
+
}[];
|
|
60
|
+
}>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-review planner — the pure decision core behind `hunch auto-review`.
|
|
3
|
+
*
|
|
4
|
+
* It takes the draft set + a per-draft harness RelevanceVerdict (the "delegate to
|
|
5
|
+
* the harness" judgment) and folds in the two DETERMINISTIC signals the store
|
|
6
|
+
* already produces — near-duplicate detection (dupdetect) and grounding/Critic
|
|
7
|
+
* verification (reviewqueue) — into four disjoint buckets:
|
|
8
|
+
*
|
|
9
|
+
* accept — confirm to human_confirmed. GATED: only Critic-verified,
|
|
10
|
+
* well-grounded drafts the harness judged relevant + non-dup.
|
|
11
|
+
* This is the dec_a466655539 line — a machine never confirms an
|
|
12
|
+
* UN-audited draft; the harness verdict can only ADD a veto, not
|
|
13
|
+
* substitute for the Critic's grounding.
|
|
14
|
+
* rejectDuplicate — reject: a near-duplicate of an accepted record (deterministic
|
|
15
|
+
* dupdetect, OR the harness named an existing decision it restates).
|
|
16
|
+
* rejectIrrelevant— reject: the harness judged it not worth keeping, confidently.
|
|
17
|
+
* keep — everything else stays for a human (the safe default).
|
|
18
|
+
*
|
|
19
|
+
* No I/O, no LLM calls here — verdicts are passed in — so it's fully unit-testable
|
|
20
|
+
* and deterministic given its inputs.
|
|
21
|
+
*/
|
|
22
|
+
import type { Decision } from "./types.js";
|
|
23
|
+
import type { RelevanceVerdict } from "../synthesis/provider.js";
|
|
24
|
+
export type AutoReviewAction = "accept" | "rejectDuplicate" | "rejectIrrelevant" | "keep";
|
|
25
|
+
export interface AutoReviewEntry {
|
|
26
|
+
d: Decision;
|
|
27
|
+
action: AutoReviewAction;
|
|
28
|
+
/** human-readable justification for the action (shown in the plan). */
|
|
29
|
+
reason: string;
|
|
30
|
+
/** the harness verdict for this draft, when one was obtained. */
|
|
31
|
+
verdict?: RelevanceVerdict;
|
|
32
|
+
/** grounded-ness parsed from synth telemetry, when present. */
|
|
33
|
+
grounded?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface AutoReviewPlan {
|
|
36
|
+
accept: AutoReviewEntry[];
|
|
37
|
+
rejectDuplicate: AutoReviewEntry[];
|
|
38
|
+
rejectIrrelevant: AutoReviewEntry[];
|
|
39
|
+
keep: AutoReviewEntry[];
|
|
40
|
+
}
|
|
41
|
+
export interface AutoReviewConfig {
|
|
42
|
+
/** grounded-ness threshold for the accept gate. */
|
|
43
|
+
minGrounded?: number;
|
|
44
|
+
/** minimum harness confidence to act on an "irrelevant" verdict (lifecycle rejection).
|
|
45
|
+
* Below this the draft is kept for a human — we never reject on a shaky judgment. */
|
|
46
|
+
minRejectConfidence?: number;
|
|
47
|
+
}
|
|
48
|
+
/** Build the plan. `verdicts` maps draft id → harness verdict (absent → the draft
|
|
49
|
+
* was not judged, e.g. no CLI available; the pure plan can still flag a
|
|
50
|
+
* deterministic duplicate or keep it). The CLI refuses `--apply` on an incomplete
|
|
51
|
+
* requested harness batch; explicit `--no-llm` triage intentionally has no batch. */
|
|
52
|
+
export declare function planAutoReview(drafts: Decision[], allDecisions: Decision[], verdicts: Map<string, RelevanceVerdict>, cfg?: AutoReviewConfig): AutoReviewPlan;
|
|
53
|
+
/** Total drafts the plan would mutate (accept + both rejection buckets). */
|
|
54
|
+
export declare function planMutations(plan: AutoReviewPlan): number;
|
|
55
|
+
/** A caller's EXPLICIT accept/delete choice (from the Review Console's per-card
|
|
56
|
+
* override), resolved against the live draft set. Ids that aren't a current draft
|
|
57
|
+
* — already accepted/superseded, or plain unknown — land in `unknown` so an
|
|
58
|
+
* apply-by-id can never mutate a non-draft record by a stale id (the console holds
|
|
59
|
+
* a snapshot; the store may have moved). An id claimed by BOTH lists resolves to
|
|
60
|
+
* accept (the safer, non-destructive verb) and is reported as unknown for delete. */
|
|
61
|
+
export interface ResolvedSelection {
|
|
62
|
+
accept: Decision[];
|
|
63
|
+
delete: Decision[];
|
|
64
|
+
unknown: string[];
|
|
65
|
+
}
|
|
66
|
+
export declare function resolveSelection(drafts: Decision[], acceptIds: string[], deleteIds: string[]): ResolvedSelection;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture-session tokens (decision-grounding, DESIGN §5 Stage 1 / §9.3).
|
|
3
|
+
*
|
|
4
|
+
* hunch_capture_decision issues a short-lived token; the commit path consumes it, so a
|
|
5
|
+
* decision written through the capture front door is provably the tail of an interview
|
|
6
|
+
* — the identity-principle guard against a silent, un-interviewed write. In-memory (the
|
|
7
|
+
* MCP server is long-lived); tokens are one-time-use and expire so an abandoned
|
|
8
|
+
* interview can't leak. Absence of a token never BLOCKS a write yet (staged
|
|
9
|
+
* deprecation §9.3) — the caller decides how to treat an un-gated write.
|
|
10
|
+
*/
|
|
11
|
+
declare const CAPTURE_TOKEN_TTL_MS: number;
|
|
12
|
+
/** Issue a token stamped `now` (epoch ms). Prunes expired tokens first so the map can't
|
|
13
|
+
* grow unbounded across a long server life. `mint` supplies the random id (injectable
|
|
14
|
+
* for tests); the call site passes crypto.randomUUID. */
|
|
15
|
+
export declare function issueCaptureToken(mint: () => string, now: number): string;
|
|
16
|
+
/** Consume a token iff it is a live, unexpired capture session. One-time use: a second
|
|
17
|
+
* consume of the same token returns false. */
|
|
18
|
+
export declare function consumeCaptureToken(token: string | undefined, now: number): boolean;
|
|
19
|
+
export { CAPTURE_TOKEN_TTL_MS };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const CHANGE_IDENTITY_SCHEMA_VERSION: "hunch.change-identity/1";
|
|
2
|
+
export declare const CHANGE_IDENTITY_ALGORITHM: "git-raw-tree-delta-sha256/1";
|
|
3
|
+
export interface ChangeIdentity {
|
|
4
|
+
schema: typeof CHANGE_IDENTITY_SCHEMA_VERSION;
|
|
5
|
+
algorithm: typeof CHANGE_IDENTITY_ALGORITHM;
|
|
6
|
+
change_id: string;
|
|
7
|
+
base_revision: string;
|
|
8
|
+
head_revision: string;
|
|
9
|
+
base_tree: string;
|
|
10
|
+
head_tree: string;
|
|
11
|
+
delta_hash: string;
|
|
12
|
+
/** Git's looser interoperability identifier; never the Hunch authority. */
|
|
13
|
+
patch_id: string | null;
|
|
14
|
+
file_count: number;
|
|
15
|
+
paths_hash: string;
|
|
16
|
+
content_hash: string;
|
|
17
|
+
}
|
|
18
|
+
/** Derive one deterministic receipt from two commit-ish references. */
|
|
19
|
+
export declare function deriveChangeIdentity(root: string, baseRef: string, headRef?: string): ChangeIdentity;
|
|
20
|
+
/** Validate an identity without trusting any caller-supplied seal. */
|
|
21
|
+
export declare function assertChangeIdentity(value: unknown): asserts value is ChangeIdentity;
|
|
22
|
+
export declare function changesAreEquivalent(left: unknown, right: unknown): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HunchStore } from "../store/hunchStore.js";
|
|
2
|
+
import { type ChangeProof } from "./changeProofContract.js";
|
|
3
|
+
export interface DeriveChangeProofOptions {
|
|
4
|
+
/** Exclude a configured private overlay from the artifact. Required before publication. */
|
|
5
|
+
publicOnly?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Produce Hunch's standalone semantic proof for one exact committed tree transition.
|
|
9
|
+
*
|
|
10
|
+
* The proof is read-only and timestamp-free. It binds source bytes, parsed graph,
|
|
11
|
+
* current memory records and every explicit gap; it never grants execution, CI,
|
|
12
|
+
* deployment, merge, ranking, promotion or policy authority.
|
|
13
|
+
*/
|
|
14
|
+
export declare function deriveChangeProof(root: string, store: HunchStore, baseRef: string, resultRef?: string, options?: DeriveChangeProofOptions): ChangeProof;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/** Rendering for `hunch check` — pure, so the terminal output and the CI/PR
|
|
2
|
+
* comment share one source of truth and stay unit-testable. The CLI builds a
|
|
3
|
+
* CheckReport from the store (direct/near/regression + the hardened strict gate),
|
|
4
|
+
* then renders it as text (terminal, unchanged) or markdown (a PR comment posted
|
|
5
|
+
* by the GitHub Action). The exit-code decision lives with the caller. */
|
|
6
|
+
/** The causal chain behind an invariant — the WHY a diff-only reviewer can't see:
|
|
7
|
+
* the decision that motivated the guard, and the bug whose root cause spawned it.
|
|
8
|
+
* Resolved deterministically from the graph (constraint→source_decision→bug). */
|
|
9
|
+
export interface CausalWhy {
|
|
10
|
+
constraint_id: string;
|
|
11
|
+
decision?: {
|
|
12
|
+
id: string;
|
|
13
|
+
title: string;
|
|
14
|
+
decision: string;
|
|
15
|
+
};
|
|
16
|
+
bug?: {
|
|
17
|
+
id: string;
|
|
18
|
+
title: string;
|
|
19
|
+
root_cause: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface CheckDirect {
|
|
23
|
+
id: string;
|
|
24
|
+
severity: string;
|
|
25
|
+
statement: string;
|
|
26
|
+
rationale: string;
|
|
27
|
+
files: string[];
|
|
28
|
+
/** Would this invariant FAIL the commit under --strict (direct + high-confidence + non-stale)? */
|
|
29
|
+
strictBlocks: boolean;
|
|
30
|
+
/** If a blocking invariant is downgraded to advisory under strict, why. */
|
|
31
|
+
downgrade?: "stale" | "low-confidence";
|
|
32
|
+
/** The causal citation (the "why this guard exists") — present when the graph links it. */
|
|
33
|
+
why?: CausalWhy;
|
|
34
|
+
}
|
|
35
|
+
export interface CheckNear {
|
|
36
|
+
id: string;
|
|
37
|
+
severity: string;
|
|
38
|
+
statement: string;
|
|
39
|
+
via: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface CheckRegression {
|
|
42
|
+
kind: string;
|
|
43
|
+
name: string;
|
|
44
|
+
decision: string;
|
|
45
|
+
title: string;
|
|
46
|
+
reason: string;
|
|
47
|
+
blocking: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** A diff re-introducing an approach an in-force decision deliberately REJECTED. */
|
|
50
|
+
export interface CheckVeto {
|
|
51
|
+
decision: string;
|
|
52
|
+
title: string;
|
|
53
|
+
alternative: string;
|
|
54
|
+
chosen: string;
|
|
55
|
+
tier: string;
|
|
56
|
+
evidence: string[];
|
|
57
|
+
blocking: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** A diff that ADDS a symbol already defined elsewhere in the graph — likely a
|
|
60
|
+
* re-implementation the local-context-window agent couldn't see. Advisory only. */
|
|
61
|
+
export interface CheckRedundant {
|
|
62
|
+
name: string;
|
|
63
|
+
kind: string;
|
|
64
|
+
existingFile: string;
|
|
65
|
+
}
|
|
66
|
+
export interface CheckReport {
|
|
67
|
+
fileCount: number;
|
|
68
|
+
strict: boolean;
|
|
69
|
+
direct: CheckDirect[];
|
|
70
|
+
near: CheckNear[];
|
|
71
|
+
regressions: CheckRegression[];
|
|
72
|
+
vetoes: CheckVeto[];
|
|
73
|
+
/** Advisory: symbols this diff adds that already exist elsewhere (possible sprawl). Never blocks. */
|
|
74
|
+
redundant: CheckRedundant[];
|
|
75
|
+
/** Count of direct invariants that pass the hardened strict gate. */
|
|
76
|
+
strictBlockers: number;
|
|
77
|
+
/** Count of blocking-linked regressions. */
|
|
78
|
+
regBlocking: number;
|
|
79
|
+
/** Count of vetoes that pass the veto gate (human-confirmed, in-force, non-stale). */
|
|
80
|
+
vetoBlocking: number;
|
|
81
|
+
}
|
|
82
|
+
export declare function reportIsClean(r: CheckReport): boolean;
|
|
83
|
+
/** PR impact (read-only, advisory): the dependency + memory surface of a change.
|
|
84
|
+
* Composes the SAME primitives as CheckReport so impact and gating never disagree. */
|
|
85
|
+
export interface ImpactReport {
|
|
86
|
+
files: string[];
|
|
87
|
+
/** Files whose code (in)directly depends on the changed files — nearest depth wins. */
|
|
88
|
+
blast: Array<{
|
|
89
|
+
file: string;
|
|
90
|
+
via: string;
|
|
91
|
+
depth: number;
|
|
92
|
+
}>;
|
|
93
|
+
report: CheckReport;
|
|
94
|
+
/** In-force decisions concerning the touched files. */
|
|
95
|
+
decisions: Array<{
|
|
96
|
+
id: string;
|
|
97
|
+
title: string;
|
|
98
|
+
status: string;
|
|
99
|
+
}>;
|
|
100
|
+
}
|
|
101
|
+
/** Terminal/markdown-lite rendering of an ImpactReport (hunch impact / hunch_pr_impact). */
|
|
102
|
+
export declare function renderImpact(im: ImpactReport, scope: string): string;
|
|
103
|
+
/** True when --strict should FAIL the commit/PR. */
|
|
104
|
+
export declare function reportFailsStrict(r: CheckReport): boolean;
|
|
105
|
+
/** The deterministic VERDICT for a merge: block (a hard gate fired), warn (touches
|
|
106
|
+
* memory but nothing hard-blocks), or pass (touches no recorded memory at all). */
|
|
107
|
+
export declare function verdict(r: CheckReport): "block" | "warn" | "pass";
|
|
108
|
+
export declare function renderText(r: CheckReport): string;
|
|
109
|
+
export declare function renderMarkdown(r: CheckReport): string;
|
|
110
|
+
/** Conformance/policy/scan findings arrive from the CLI already flattened, so this
|
|
111
|
+
* renderer stays pure and every output channel shares one gate semantics. */
|
|
112
|
+
export interface SarifExtras {
|
|
113
|
+
conformance?: Array<{
|
|
114
|
+
decision: string;
|
|
115
|
+
title: string;
|
|
116
|
+
detail: string;
|
|
117
|
+
why?: string;
|
|
118
|
+
bug?: string;
|
|
119
|
+
}>;
|
|
120
|
+
policies?: Array<{
|
|
121
|
+
id: string;
|
|
122
|
+
result: string;
|
|
123
|
+
explanation: string;
|
|
124
|
+
blocks: boolean;
|
|
125
|
+
receipt: string;
|
|
126
|
+
gateError?: string;
|
|
127
|
+
}>;
|
|
128
|
+
scanIssues?: Array<{
|
|
129
|
+
path: string;
|
|
130
|
+
detail: string;
|
|
131
|
+
code: string;
|
|
132
|
+
}>;
|
|
133
|
+
}
|
|
134
|
+
/** Render the full check verdict as a SARIF 2.1.0 document. Level mapping is
|
|
135
|
+
* severity-truth, independent of the exit code: `error` = would fail --strict
|
|
136
|
+
* (strict-blocking invariants, blocking-linked regressions/vetoes, conformance
|
|
137
|
+
* violations, authorized policy blocks, incomplete scans), `warning` = blocking
|
|
138
|
+
* records the strict gate downgraded plus warning-severity hits, `note` = advisory
|
|
139
|
+
* (near, redundant, non-blocking receipts). */
|
|
140
|
+
export declare function renderSarif(r: CheckReport, version: string, extras?: SarifExtras): string;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { Decision } from "./types.js";
|
|
2
|
+
import type { CommitCandidate, CommitRepairStatus } from "../extractors/git.js";
|
|
3
|
+
export interface CommitRewrite {
|
|
4
|
+
id: string;
|
|
5
|
+
from: string;
|
|
6
|
+
to: string;
|
|
7
|
+
}
|
|
8
|
+
/** Bind review to the ordered queue and which targets are currently resolvable.
|
|
9
|
+
* Queue order matters: duplicate decision ids use first-match selection. */
|
|
10
|
+
export declare function commitRepairReviewHash(queued: readonly CommitRewrite[], withheld?: ReadonlySet<CommitRewrite>): string;
|
|
11
|
+
/** A tombstone: the exact {id, from, to} rewrite a human explicitly rejected
|
|
12
|
+
* via `--drop`. Keyed on the full triple, not just {id, from} — the human's
|
|
13
|
+
* "no" answers the specific question the commit-repair-pending escalation
|
|
14
|
+
* asked ("apply THIS proposed replacement, from -> to?"), not a blanket
|
|
15
|
+
* refusal of every future replacement for this decision's still-orphaned
|
|
16
|
+
* commit. A later merge proposing a genuinely different `to` for the same
|
|
17
|
+
* {id, from} is a NEW proposal — not a repeat of the one that was rejected —
|
|
18
|
+
* and surfaces normally. */
|
|
19
|
+
export interface DroppedRewrite {
|
|
20
|
+
id: string;
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CommitRepairPlan {
|
|
25
|
+
rewrites: CommitRewrite[];
|
|
26
|
+
records: string[];
|
|
27
|
+
}
|
|
28
|
+
/** Live decisions whose cited commit still exists here but is no longer an
|
|
29
|
+
* ancestor of the current tip. Only "orphaned" qualifies: a commit that
|
|
30
|
+
* doesn't resolve at all ("unresolvable") is out of repair scope, and a git
|
|
31
|
+
* failure ("unknown") must never be mistaken for repair-eligible. */
|
|
32
|
+
export declare function orphanedCommitDecisions(decisions: readonly Decision[], status: (sha: string) => CommitRepairStatus): Decision[];
|
|
33
|
+
/** Match each orphaned decision to exactly one candidate whose changed files are
|
|
34
|
+
* a superset of the decision's related_files (non-empty, no globs). */
|
|
35
|
+
export declare function planCommitRepair(orphaned: readonly Decision[], candidates: readonly CommitCandidate[]): CommitRepairPlan;
|
|
36
|
+
/** Whether a queued rewrite is still worth ASKING about: the decision it
|
|
37
|
+
* names still exists, still cites the commit the entry expects to replace (a
|
|
38
|
+
* decision that already moved on would make repairDecisionCommit's own
|
|
39
|
+
* stale-plan bail refuse it), and hasn't since been superseded/rejected —
|
|
40
|
+
* same exclusion as orphanedCommitDecisions, since repairing dead history's
|
|
41
|
+
* provenance isn't worth asking about. Backs the read-only escalation
|
|
42
|
+
* surface only (commitRepairEscalations) — absence-means-not-askable is
|
|
43
|
+
* safe there because it self-heals the moment visibility returns.
|
|
44
|
+
* repair-provenance itself does NOT use this: it reads the full store and
|
|
45
|
+
* may legitimately list (dry-run) or leave queued (apply) an entry this
|
|
46
|
+
* predicate would call not-live, so the escalation surface and the CLI can
|
|
47
|
+
* disagree about the same entry — see deadRewrites and resolvedRewriteIds
|
|
48
|
+
* for the CLI's own, looser-on-purpose reasoning. */
|
|
49
|
+
export declare function liveRewrites(queued: readonly CommitRewrite[], decisions: readonly Decision[]): CommitRewrite[];
|
|
50
|
+
/** Entries a decision has demonstrably outgrown: the decision IS present in
|
|
51
|
+
* `decisions`, and it either moved on from `from` or has since been
|
|
52
|
+
* superseded/rejected. An id this list can't see is deliberately NOT
|
|
53
|
+
* included — absence is a property of the READER (a branch checkout that
|
|
54
|
+
* predates the decision, an unmounted private overlay), not proof the match
|
|
55
|
+
* is stale. This predicate backs a DESTRUCTIVE prune (repair-provenance
|
|
56
|
+
* deletes what it returns from the queue file); repairqueue.ts's own
|
|
57
|
+
* docstring is why that matters — the queue is the one durable record of a
|
|
58
|
+
* match once ORIG_HEAD moves on and the matched-away commit can be gc'd, so
|
|
59
|
+
* deleting an entry on a merely-absent id would destroy it unrecoverably.
|
|
60
|
+
* Contrast liveRewrites, whose absence-means-not-askable rule is safe on
|
|
61
|
+
* the read-only escalation surface, which self-heals once visibility
|
|
62
|
+
* returns.
|
|
63
|
+
*
|
|
64
|
+
* Caveat: "demonstrably" is only as strong as this run's view of
|
|
65
|
+
* `decisions`, which comes from the same git-tracked, branch-dependent
|
|
66
|
+
* files that motivate treating ABSENCE as inconclusive. A decision whose
|
|
67
|
+
* `commit`/`status` genuinely differ between branches (e.g. a repair
|
|
68
|
+
* already applied and committed on one branch, checked out here from a
|
|
69
|
+
* branch that predates it) could still read as "moved on" when it hasn't,
|
|
70
|
+
* from this branch's perspective. Far narrower than the absence case this
|
|
71
|
+
* function exists to fix, and not addressed here. */
|
|
72
|
+
export declare function deadRewrites(queued: readonly CommitRewrite[], decisions: readonly Decision[]): CommitRewrite[];
|
|
73
|
+
/** Among `toApply` (the queued/matched candidates this run is about to act
|
|
74
|
+
* on), the ids repair-provenance may treat as RESOLVED once it's done: the
|
|
75
|
+
* decision is present in `decisions` this run, whether or not
|
|
76
|
+
* repairDecisionCommit actually changed it (a present decision that already
|
|
77
|
+
* moved on is "resolved" too — repairDecisionCommit's own bail refused it,
|
|
78
|
+
* and there is nothing more this run can do about it). An id whose decision
|
|
79
|
+
* is NOT present is never resolved — same reasoning as deadRewrites:
|
|
80
|
+
* absence is the reader's transient view, not proof the match is settled,
|
|
81
|
+
* so it must stay queued for a run where the decision is visible again. */
|
|
82
|
+
export declare function resolvedRewriteIds(toApply: readonly CommitRewrite[], decisions: readonly Decision[]): Set<string>;
|
|
83
|
+
/** Remove any match whose exact {id, from, to} triple was already tombstoned
|
|
84
|
+
* by an earlier `--drop` — otherwise a later merge that re-derives the
|
|
85
|
+
* identical candidate would re-queue exactly what the human rejected. A
|
|
86
|
+
* match sharing {id, from} but proposing a different `to` is unaffected: it
|
|
87
|
+
* is a different proposal, not the rejected one.
|
|
88
|
+
*
|
|
89
|
+
* Preserves object identity for every surviving entry (a plain filter, never
|
|
90
|
+
* a clone or a rebuild) — repair-provenance's own action relies on this to
|
|
91
|
+
* diff its swept-vs-active queue by reference (`queue.filter(r =>
|
|
92
|
+
* !active.includes(r))`) rather than re-spelling the {id, from, to} key a
|
|
93
|
+
* third time. Do not change this to normalize or reconstruct entries
|
|
94
|
+
* without updating that call site too. */
|
|
95
|
+
export declare function withoutDropped(rewrites: readonly CommitRewrite[], dropped: readonly DroppedRewrite[]): CommitRewrite[];
|
|
96
|
+
/** Append newly-dropped {id, from, to} triples to the tombstone list,
|
|
97
|
+
* deduped — same append-only, dedupe-by-key shape as mergeRewrites. */
|
|
98
|
+
export declare function addDropped(newly: readonly DroppedRewrite[], existing: readonly DroppedRewrite[]): DroppedRewrite[];
|
|
99
|
+
/** Combine a freshly-computed plan's rewrites with anything already queued
|
|
100
|
+
* (src/core/repairqueue.ts) from an earlier detection. A fresh match —
|
|
101
|
+
* computed just now, against the current range — overrides a queued one for
|
|
102
|
+
* the same decision; queued entries no longer reachable are simply not
|
|
103
|
+
* repeated by the fresh scan.
|
|
104
|
+
*
|
|
105
|
+
* Overrides by object identity, targeting exactly the one queued entry
|
|
106
|
+
* firstFor (id) would pick — the same first-match rule --drop applies, and
|
|
107
|
+
* the same identity-not-id targeting the dead-entry prune applies (#53; the
|
|
108
|
+
* prune itself removes every provably-dead entry, not just a first match).
|
|
109
|
+
* A corrupted queue file, a hand edit, or a bug upstream could otherwise
|
|
110
|
+
* carry a SECOND entry sharing that id; overriding by id alone would
|
|
111
|
+
* silently destroy that still-queued sibling too, with no tombstone and no
|
|
112
|
+
* way to recover it, even though it was never the entry that would have
|
|
113
|
+
* been applied. Any such sibling is left queued and untouched.
|
|
114
|
+
*
|
|
115
|
+
* How a survivor resolves: the dead-entry prune (deadRewrites), once
|
|
116
|
+
* --apply has moved the decision past the sibling's `from`; or a --drop
|
|
117
|
+
* whose tombstone matches the sibling's exact {id, from, to} — the same
|
|
118
|
+
* --drop that dropped the fresh entry, when the sibling happens to share
|
|
119
|
+
* its triple (the drop's own re-sweep, withoutDropped, catches it too), a
|
|
120
|
+
* second explicit --drop otherwise. NOT a later fresh detection: by this
|
|
121
|
+
* same first-match rule it always overrides the fresh entry sitting ahead
|
|
122
|
+
* of it, never the sibling. */
|
|
123
|
+
export declare function mergeRewrites(fresh: readonly CommitRewrite[], queued: readonly CommitRewrite[]): CommitRewrite[];
|
|
124
|
+
/** Partition the entries --apply is about to act on by whether their proposed
|
|
125
|
+
* `to` commit actually exists in this repository. `existing` is
|
|
126
|
+
* `commitsExist`'s result over every `to` in `toApply`: `null` means the
|
|
127
|
+
* check itself failed to run (not a git repo, git missing, timeout) and is
|
|
128
|
+
* treated as fail-open, same discipline as drift.ts's own use of
|
|
129
|
+
* commitsExist — nothing is withheld just because the check couldn't run.
|
|
130
|
+
*
|
|
131
|
+
* A corrupted queue file, a hand-edited entry, or a bug upstream could
|
|
132
|
+
* otherwise get a `to` that never resolves to a real commit written
|
|
133
|
+
* straight into a decision's `commit` field and provenance evidence, then
|
|
134
|
+
* auto-committed into shared team memory. `withheld` entries must stay
|
|
135
|
+
* queued (never deleted, never passed to repairDecisionCommit) — the queue
|
|
136
|
+
* file is the one durable record of the match, same reasoning as
|
|
137
|
+
* deadRewrites/resolvedRewriteIds.
|
|
138
|
+
*
|
|
139
|
+
* Both returned arrays preserve object identity (never clones or rebuilds
|
|
140
|
+
* an entry) — the CLI's queue sweep and its "still queued" reporting both
|
|
141
|
+
* key a `Set` off the exact objects in `withheld`, by reference, precisely
|
|
142
|
+
* because a corrupted queue file can carry two entries sharing an id (one
|
|
143
|
+
* resolvable, one not): an id alone can't tell them apart. Do not change
|
|
144
|
+
* this to normalize or reconstruct entries without updating those call
|
|
145
|
+
* sites too — same caveat withoutDropped's own docstring carries. */
|
|
146
|
+
export declare function withheldForUnresolvableTo(toApply: readonly CommitRewrite[], existing: Set<string> | null): {
|
|
147
|
+
applicable: CommitRewrite[];
|
|
148
|
+
withheld: CommitRewrite[];
|
|
149
|
+
};
|
|
150
|
+
/** The one rule for which entry wins when more than one shares a decision id
|
|
151
|
+
* (a corrupted queue file, a hand edit, or a bug upstream): first match, by
|
|
152
|
+
* construction. Every caller goes through here so no two can independently
|
|
153
|
+
* drift on which entry "won" — this file's own duplicate-id queue handling
|
|
154
|
+
* has drifted between call sites three times already (see withoutDropped's,
|
|
155
|
+
* withheldForUnresolvableTo's, and mergeRewrites' own docstrings). */
|
|
156
|
+
export declare function firstFor(entries: readonly CommitRewrite[], id: string): CommitRewrite | undefined;
|
|
157
|
+
/** pickRewrite is firstFor scoped to a plan's own rewrites (planCommitRepair
|
|
158
|
+
* itself never produces same-id duplicates, but a plan carrying them isn't
|
|
159
|
+
* ruled out here — see firstFor). repairDecisionCommit uses this to decide
|
|
160
|
+
* what to WRITE; a caller that needs to know which entry WOULD be picked
|
|
161
|
+
* without writing anything (the CLI's --apply summary and queue sweep)
|
|
162
|
+
* calls this same function, so the two can never independently drift on
|
|
163
|
+
* which entry "won". */
|
|
164
|
+
export declare function pickRewrite(plan: CommitRepairPlan, id: string): CommitRewrite | undefined;
|
|
165
|
+
/** Pure: rewrite `commit` for one decision, and its matching `commit:<sha>`
|
|
166
|
+
* evidence entry too if one is present (replaceExact is a no-op when the
|
|
167
|
+
* evidence array never cited the old sha) — or return the same reference
|
|
168
|
+
* when the plan doesn't touch it. If the record moved on since the plan was
|
|
169
|
+
* built (its commit no longer equals the plan's `from`), bail entirely
|
|
170
|
+
* rather than applying a now-stale match. */
|
|
171
|
+
export declare function repairDecisionCommit(d: Decision, plan: CommitRepairPlan): Decision;
|