@davesheffer/hunch 1.20.0-rc.6 → 1.20.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 CHANGED
@@ -171,7 +171,7 @@ repository, separate from the code repository. Hunch does not host it. Create a
171
171
  that every teammate can access, install Hunch on team machines and CI, then have one maintainer run:
172
172
 
173
173
  ```bash
174
- npm i -g @davesheffer/hunch@1.19.0
174
+ npm i -g @davesheffer/hunch@1.20.0
175
175
  hunch shared --repo git@github.com:acme/project-hunch-memory.git
176
176
  git add .gitignore .hunch/team.json
177
177
  git commit -m "chore: connect shared Hunch memory"
@@ -186,7 +186,7 @@ printed by Hunch. Omit `--migrate` for a new setup.
186
186
  After the pointer commit lands, teammates need Hunch installed and Git access to the memory repo:
187
187
 
188
188
  ```bash
189
- npm i -g @davesheffer/hunch@1.19.0
189
+ npm i -g @davesheffer/hunch@1.20.0
190
190
  git pull
191
191
  hunch init
192
192
  hunch doctor
@@ -234,7 +234,7 @@ but stops automatic memory commits and pushes. As a team-coordinated rollback, r
234
234
  commit to stop discovery after teammates pull the revert. Existing machines retain their ignored
235
235
  local overlay until they are deliberately disconnected; do not delete the memory repo as part of a
236
236
  rollback. For this rollout, reinstall the previous published package with
237
- `npm i -g @davesheffer/hunch@1.16.0`; the release receipt resolves and records the verified rollback
237
+ `npm i -g @davesheffer/hunch@1.19.0`; the release receipt resolves and records the verified rollback
238
238
  target from the npm registry instead of trusting Git tags. Pause enforcement first as shown above,
239
239
  and keep every team client on the same release before resuming Matrix policy workflows.
240
240
 
@@ -46,7 +46,7 @@ export function renderHunchSection(store, root) {
46
46
  lines.push("**Consult Hunch via the `hunch_*` MCP tools — pick by MOMENT, not from memory:**");
47
47
  lines.push("");
48
48
  lines.push("**Orient (session/task start):**");
49
- lines.push("- `hunch_context(target_or_task)` — the minimal relevant slice for what you're about to do; a task phrase falls back to the closest graph matches. **Call FIRST.**");
49
+ lines.push("- `hunch_context(target)` — the minimal relevant slice for what you're about to do; a task phrase falls back to the closest graph matches. **Call FIRST.**");
50
50
  lines.push("- `hunch_structure(target?)` — the indexed shape of the repo/dir/file/symbol — orient from the graph, not grep rounds.");
51
51
  lines.push("- `hunch_runbook(task)` — the proven steps for a recurring task, before re-deriving them.");
52
52
  lines.push("- `hunch_escalations()` — the decisions only the HUMAN can make (including one exact imported ADR at a time, topic conflicts, and policy calls). Normally empty; when it isn't, ASK the user inline — an entry is a question, silence is never approval. Apply an ADR answer only through `hunch_review_imported_adr` with its printed source and review hashes.");
@@ -477,14 +477,22 @@ export function buildServerWithRootControl(initialRoot) {
477
477
  };
478
478
  const pullTeamMemory = (force = false) => {
479
479
  if (!store.privateDir)
480
- return;
480
+ return "not_shared";
481
481
  const now = Date.now();
482
482
  if (!force && now < nextRemotePullAt)
483
- return;
484
- notePull(pullHunchStatus(store.privateDir, {
485
- timeoutMs: 5_000,
486
- remote: startupTeamRoute ?? advertisedTeamRemoteContract(root, join(store.privateDir, "..")),
487
- }), Date.now());
483
+ return "cooldown";
484
+ let status;
485
+ try {
486
+ status = pullHunchStatus(store.privateDir, {
487
+ timeoutMs: 5_000,
488
+ remote: startupTeamRoute ?? advertisedTeamRemoteContract(root, join(store.privateDir, "..")),
489
+ });
490
+ }
491
+ catch {
492
+ status = "failed";
493
+ }
494
+ notePull(status, Date.now());
495
+ return status;
488
496
  };
489
497
  // A source stamp is acknowledged ONLY after a stable, successful rebuild. If
490
498
  // another process changes the atomic JSON tree during the rebuild, retry once;
@@ -626,10 +634,7 @@ export function buildServerWithRootControl(initialRoot) {
626
634
  return err("The committed team memory destination is invalid or no longer matches this process. Refusing the stale graph; reconnect Hunch first.");
627
635
  }
628
636
  if (store.mode === "shared" && store.privateDir) {
629
- try {
630
- pullTeamMemory();
631
- }
632
- catch { /* offline / lock held / invalid remote — use local */ }
637
+ pullTeamMemory();
633
638
  // Recompute the full semantic + physical snapshot after the synchronous
634
639
  // network seam. A paired team.json/origin change can occur while fetch is
635
640
  // blocked; serving after that race would attach the old checkout to a new
@@ -1087,11 +1092,31 @@ export function buildServerWithRootControl(initialRoot) {
1087
1092
  // -- hunch_current_decision (decision-grounding: current(topic)) ----------
1088
1093
  server.registerTool("hunch_current_decision", {
1089
1094
  title: "Current decision for a topic",
1090
- description: "Decision-grounding: return the single CURRENT (accepted, non-superseded) decision anchored to a topic — the authoritative answer a doc or diff is checked against, plus what it rejected. If a topic has NO current decision, or an unresolved collision (>1 live), it says so and injects nothing (fail-safe).",
1095
+ description: "Decision-grounding: return the single CURRENT (accepted, non-superseded) decision anchored to a topic — the authoritative answer a doc or diff is checked against, plus what it rejected. A shared-store miss is confirmed against fresh team memory before Hunch says the topic has no current decision. If freshness is unavailable, or a topic has an unresolved collision (>1 live), it injects nothing (fail-safe).",
1091
1096
  inputSchema: { topic: z.string().describe("the decision anchor, e.g. 'auth-transport'") },
1092
1097
  }, async ({ topic }) => {
1093
- const decs = store.recs("decisions");
1094
- const live = liveForTopic(decs, topic);
1098
+ let decs = store.recs("decisions");
1099
+ let live = liveForTopic(decs, topic);
1100
+ // A prior transient fetch failure may have placed ordinary tool traffic in
1101
+ // backoff. That is acceptable for cached positive reads, but an exact topic
1102
+ // miss is an authority claim: local absence must never be presented as team
1103
+ // absence. Bypass the cooldown once, then reread only after a successful
1104
+ // bounded convergence. An unavailable remote produces an explicit abstention.
1105
+ if (live.length === 0 && store.mode === "shared" && store.privateDir) {
1106
+ const status = pullTeamMemory(true);
1107
+ if (status !== "updated" && status !== "current") {
1108
+ return err(`Shared team memory refresh is ${status}; Hunch cannot confirm that topic "${topic}" has no current decision. Retry when the shared store is available.`);
1109
+ }
1110
+ try {
1111
+ if (store.sourceStamp() !== indexedSourceStamp)
1112
+ refreshIndex();
1113
+ }
1114
+ catch {
1115
+ return err(`Shared team memory refreshed, but its derived index could not be rebuilt; Hunch cannot confirm that topic "${topic}" is absent. Retry this read.`);
1116
+ }
1117
+ decs = store.recs("decisions");
1118
+ live = liveForTopic(decs, topic);
1119
+ }
1095
1120
  if (live.length === 0)
1096
1121
  return ok(`No current decision for topic "${topic}". (Un-anchored, or never captured.)`);
1097
1122
  if (live.length > 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davesheffer/hunch",
3
- "version": "1.20.0-rc.6",
3
+ "version": "1.20.0",
4
4
  "mcpName": "io.github.davesheffer/hunch",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dave Sheffer <dave.sheffer1@gmail.com>",
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.20.0-rc.6",
10
+ "version": "1.20.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "registryBaseUrl": "https://registry.npmjs.org",
15
15
  "identifier": "@davesheffer/hunch",
16
- "version": "1.20.0-rc.6",
16
+ "version": "1.20.0",
17
17
  "runtimeHint": "npx",
18
18
  "packageArguments": [
19
19
  {