@davesheffer/hunch 1.19.0 → 1.20.0-rc.1

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.
@@ -28,6 +28,7 @@ import { collectCorrectionStageSources } from "../extractors/correctionSources.j
28
28
  import { buildDeliveryEnvelope } from "../core/delivery.js";
29
29
  import { armExecutionObligations, loadPipelineState, savePipelineState } from "../core/pipeline.js";
30
30
  import { recordServed } from "../core/served.js";
31
+ import { EdgeSchema, ResourceSchema } from "../core/types.js";
31
32
  import { compareCandidates } from "../core/compare.js";
32
33
  import { checkConformance } from "../core/conformance.js";
33
34
  import { ConstitutionService, policyEvaluationEnvelope } from "../constitution/service.js";
@@ -144,13 +145,45 @@ const EXECUTION_OBLIGATION_SCHEMA = z.object({
144
145
  output_excludes: z.array(z.string()).optional(),
145
146
  }),
146
147
  });
148
+ const LANDSCAPE_SELECTION_RECEIPT_SCHEMA = z.object({
149
+ selectionReason: z.enum(["exact-target", "task-match", "orientation-root", "graph-neighbor", "graph-connection"]),
150
+ selectionRank: z.number().int().positive(),
151
+ rank: z.number().int().positive(),
152
+ deliveryReason: z.literal("ranked"),
153
+ required: z.literal(false),
154
+ blocking: z.literal(false),
155
+ provenanceStatus: z.literal("current"),
156
+ tokenCost: z.number().int().positive(),
157
+ });
158
+ const LANDSCAPE_FRAGMENT_SCHEMA = z.object({
159
+ schema: z.literal("hunch.landscape-fragment/1"),
160
+ authority: z.literal("human_confirmed"),
161
+ target: z.string(),
162
+ resources: z.array(LANDSCAPE_SELECTION_RECEIPT_SCHEMA.extend({ record: ResourceSchema })),
163
+ relationships: z.array(LANDSCAPE_SELECTION_RECEIPT_SCHEMA.extend({
164
+ selectionReason: z.literal("graph-connection"),
165
+ record: EdgeSchema,
166
+ })),
167
+ omitted: z.array(z.object({
168
+ kind: z.enum(["resources", "relationships"]),
169
+ recordId: z.string(),
170
+ reason: z.enum(["budget", "stale-provenance", "endpoint-not-delivered", "landscape-cap"]),
171
+ detail: z.string(),
172
+ })),
173
+ reviewIds: z.array(z.string()),
174
+ discoveryHashes: z.array(z.string()),
175
+ sourceRevisions: z.array(z.string()),
176
+ fragmentHash: z.string(),
177
+ });
147
178
  /** Public MCP shape for the canonical delivery envelope. Keeping the schema on
148
179
  * the tool means orchestrators can consume receipt facts without scraping the
149
180
  * backward-compatible text block. */
150
181
  const DELIVERY_OUTPUT_SCHEMA = z.object({
182
+ schema_version: z.literal("hunch.delivery-envelope/1"),
183
+ receipt_id: z.string().regex(/^hdr_[a-f0-9]{24}$/),
151
184
  text: z.string(),
152
185
  delivered: z.array(z.object({
153
- kind: z.enum(["constraints", "decisions", "bugs", "findings"]),
186
+ kind: z.enum(["constraints", "decisions", "bugs", "findings", "resources", "relationships"]),
154
187
  record_id: z.string(),
155
188
  rank: z.number().int().positive(),
156
189
  delivery_reason: z.enum(["ranked", "blocking-reserved"]),
@@ -178,13 +211,15 @@ const DELIVERY_OUTPUT_SCHEMA = z.object({
178
211
  token_cost: z.number().int().nonnegative(),
179
212
  })),
180
213
  omitted: z.array(z.object({
181
- kind: z.enum(["constraints", "decisions", "bugs", "findings"]),
214
+ kind: z.enum(["constraints", "decisions", "bugs", "findings", "resources", "relationships"]),
182
215
  record_id: z.string(),
183
- reason: z.enum(["budget", "stale-provenance", "retired", "actionability-cap", "low-confidence", "insufficient-context", "low-relevance"]),
216
+ reason: z.enum(["budget", "stale-provenance", "retired", "actionability-cap", "endpoint-not-delivered", "landscape-cap", "low-confidence", "insufficient-context", "low-relevance"]),
184
217
  detail: z.string(),
185
218
  })),
219
+ landscape: LANDSCAPE_FRAGMENT_SCHEMA.nullable(),
186
220
  budget_tokens: z.number().int().nonnegative(),
187
221
  used_chars: z.number().int().nonnegative(),
222
+ accounted_chars: z.number().int().nonnegative(),
188
223
  blocking_overflow: z.boolean(),
189
224
  abstention: z.object({
190
225
  active: z.boolean(),
@@ -775,7 +810,13 @@ export function buildServerWithRootControl(initialRoot) {
775
810
  // Task-phrase input ("improve retrieval ranking") resolves no file/symbol and
776
811
  // used to return an empty brief while the graph held the answer — fall back to
777
812
  // FTS so the assistant always leaves with the closest matches, not a shrug.
778
- const empty = !ctx.constraints.length && !ctx.decisions.length && !ctx.bugs.length && !ctx.blast_radius.length;
813
+ const empty = !ctx.constraints.length &&
814
+ !ctx.decisions.length &&
815
+ !ctx.bugs.length &&
816
+ !ctx.blast_radius.length &&
817
+ !ctx.findings.length &&
818
+ !ctx.landscape?.resources.length &&
819
+ !ctx.landscape?.relationships.length;
779
820
  if (empty && !asOf) {
780
821
  const hits = store.search(target, 8);
781
822
  if (hits.length) {
@@ -25,6 +25,7 @@ import { edgeId } from "../core/ids.js";
25
25
  import { isStrictBlocker, isVetoBlocker } from "../core/strictgate.js";
26
26
  import { effectiveForbids, matchForbids } from "../core/constraintmatch.js";
27
27
  import { analyzeDiff } from "../extractors/diff.js";
28
+ import { selectReviewedLandscape, } from "../core/landscapeDelivery.js";
28
29
  /** Git cannot resolve repository identity from a cwd that does not exist yet.
29
30
  * Probe the nearest real directory so a planned nested overlay cannot evade the
30
31
  * public-repository boundary merely by deferring mkdir until its first write. */
@@ -1666,6 +1667,12 @@ export class HunchStore {
1666
1667
  blast_radius: [...blast.values()].sort((a, b) => a.depth - b.depth).slice(0, 12),
1667
1668
  components: w.components,
1668
1669
  findings: this.liveFindingsFor(target).slice(0, 8),
1670
+ // Landscape records do not yet carry a valid-time window. A historical
1671
+ // query therefore withholds them instead of mixing current graph state
1672
+ // into an as-of memory envelope.
1673
+ landscape: opts.asOf
1674
+ ? undefined
1675
+ : selectReviewedLandscape(this.recs("resources"), this.recs("edges"), target),
1669
1676
  budget_tokens: budget,
1670
1677
  };
1671
1678
  return ctx;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davesheffer/hunch",
3
- "version": "1.19.0",
3
+ "version": "1.20.0-rc.1",
4
4
  "mcpName": "io.github.davesheffer/hunch",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dave Sheffer <dave.sheffer1@gmail.com>",
@@ -58,7 +58,7 @@
58
58
  "build": "npm run clean && tsc -p tsconfig.json",
59
59
  "dev": "tsx src/cli/index.ts",
60
60
  "hunch": "tsx src/cli/index.ts",
61
- "test": "tsx --test --test-concurrency=2 test/*.test.ts",
61
+ "test": "tsx --test --test-concurrency=4 test/*.test.ts",
62
62
  "test:e2e:md1": "tsx --test test/md1-e2e.test.ts",
63
63
  "test:e2e:team-matrix": "tsx --test test/team-matrix-e2e.test.ts",
64
64
  "verify:matrix": "node tooling/matrix-release-verification.mjs",
@@ -68,6 +68,7 @@
68
68
  "site:proof": "npm run build && node tooling/generate-public-proof.mjs",
69
69
  "bench:md1": "node tooling/md1-benchmark.mjs",
70
70
  "research:competitors": "node tooling/competitive-watch.mjs",
71
+ "outreach": "node tooling/outreach-pipeline.mjs",
71
72
  "prepublishOnly": "npm run build"
72
73
  },
73
74
  "dependencies": {
package/server.json CHANGED
@@ -7,13 +7,13 @@
7
7
  "source": "github"
8
8
  },
9
9
  "websiteUrl": "https://hunch-pi.vercel.app",
10
- "version": "1.19.0",
10
+ "version": "1.20.0-rc.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "registryBaseUrl": "https://registry.npmjs.org",
15
15
  "identifier": "@davesheffer/hunch",
16
- "version": "1.19.0",
16
+ "version": "1.20.0-rc.1",
17
17
  "runtimeHint": "npx",
18
18
  "packageArguments": [
19
19
  {