mishkan-harness 0.1.0 → 0.2.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 +75 -24
- package/bin/mishkan.js +145 -22
- package/docs/design/MISHKAN_decisions.md +400 -0
- package/docs/design/MISHKAN_observability.md +1023 -0
- package/docs/usage/01-installation.md +40 -12
- package/docs/usage/10-observability.md +149 -0
- package/docs/usage/README.md +1 -0
- package/package.json +1 -1
- package/payload/install/settings.hooks.json +9 -0
- package/payload/mishkan/hooks/model-route.py +32 -0
- package/payload/mishkan/hooks/post-tool-observe.sh +222 -14
- package/payload/mishkan/hooks/pre-tool-security.sh +16 -0
- package/payload/mishkan/hooks/pre-tool-trace.sh +43 -0
- package/payload/mishkan/observability/README.md +100 -0
- package/payload/mishkan/observability/bus.py +89 -0
- package/payload/mishkan/observability/bus.sh +117 -0
- package/payload/mishkan/observability/schema.json +209 -0
- package/payload/mishkan/observability/usage_parser.py +211 -0
- package/payload/mishkan/observability/watch/README.md +62 -0
- package/payload/mishkan/observability/watch/pyproject.toml +22 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/__init__.py +3 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/__main__.py +29 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/app.py +235 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/client.py +134 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/__init__.py +1 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/activity.py +100 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/agents.py +278 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/knowledge.py +188 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/live.py +307 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/workflows.py +189 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/theme.tcss +254 -0
- package/payload/mishkan/observability/watchd/README.md +59 -0
- package/payload/mishkan/observability/watchd/pyproject.toml +23 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/__init__.py +3 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/__main__.py +230 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/lifecycle.py +68 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/server.py +134 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/__init__.py +6 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/bus_tail.py +97 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/cognee_poll.py +228 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/mcp_probe.py +230 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/session_discover.py +74 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/session_tail.py +161 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/subagent_tail.py +195 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/worktree_poll.py +107 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/state.py +288 -0
- package/payload/mishkan/observability/watchd/tests/test_state.py +77 -0
|
@@ -167,6 +167,406 @@ small extra Neo4j. The curated library is small and regenerable
|
|
|
167
167
|
bulk seeding bursts embedding calls and cloud free-tier embeddings 429
|
|
168
168
|
(RESOURCE_EXHAUSTED).
|
|
169
169
|
|
|
170
|
+
## D-008 — Three-layer memory epistemology: structure / project semantics / curated cross-project (added 2026-06-05)
|
|
171
|
+
|
|
172
|
+
**Decision:** MISHKAN's knowledge surface is split into **three physically
|
|
173
|
+
separate stores**, each owning one epistemic question, with no overlap of write
|
|
174
|
+
authority:
|
|
175
|
+
|
|
176
|
+
| Store | Question it answers | Source of truth | Write authority |
|
|
177
|
+
|---|---|---|---|
|
|
178
|
+
| **Graphify** (per-project, local artifacts) | *How is the code structured?* — call graphs, dependents, god nodes, schema-to-code edges, file-to-symbol provenance | tree-sitter AST + optional LLM enrichment, deterministic, re-derivable from the repo | the build (`graphify` CLI), no agent writes by hand |
|
|
179
|
+
| **Cognee work** (`:7777`, `cognee` MCP) | *Why does this code exist? what did we decide? what did we learn on this project?* | curated project artifacts (PRD, SRS, ADRs, sprint reports, agent learnings) ingested via `mishkan-ingest` | agents, gated by `mishkan: ingest` frontmatter |
|
|
180
|
+
| **Cognee curated** (`:7730`, `cognee-curated` MCP, read-only from projects) | *What have we learned across all projects?* | promoted cross-harness knowledge, reference library | `/sprint-close` + `seed-curated-library.sh`, only |
|
|
181
|
+
|
|
182
|
+
Graphify v0.8.31 (MIT, [github.com/safishamsi/graphify](https://github.com/safishamsi/graphify))
|
|
183
|
+
joins the stack as a **third store**, not as a Cognee feeder, not as a Cognee
|
|
184
|
+
replacement.
|
|
185
|
+
|
|
186
|
+
### Force-tension
|
|
187
|
+
|
|
188
|
+
**What pushes toward a third layer.** Structural questions ("who calls
|
|
189
|
+
`apply_overlay`?", "what depends on `models.User`?", "where are the god nodes?",
|
|
190
|
+
"what tables does this service read?") are answered today by repeated grep and
|
|
191
|
+
file reads, which is exactly the failure mode the engineer profile names: token
|
|
192
|
+
waste, context bloat, and answers that drift because they reconstruct structure
|
|
193
|
+
from prose instead of reading it from the AST. Graphify gives a deterministic
|
|
194
|
+
graph, locally extracted, re-derivable from the repo at any time — the *opposite*
|
|
195
|
+
epistemic shape from Cognee work, which is a curated, lossy, LLM-summarised
|
|
196
|
+
narrative of decisions. Conflating the two in one store has already been rejected
|
|
197
|
+
once (D-007 separated curated from work for the same reason: different write
|
|
198
|
+
discipline, different trust shape).
|
|
199
|
+
|
|
200
|
+
**What pushes back.** Three stores is one more runtime, one more failure mode,
|
|
201
|
+
one more place for an agent to look in the wrong order. Graphify is a young
|
|
202
|
+
project (v0.8.x, ~10 weeks old at this writing) with a credible breaking-change
|
|
203
|
+
risk. Cognee already has a code-extraction notion (`codify`); adding Graphify
|
|
204
|
+
risks duplicating capability the harness already paid for. The boundary between
|
|
205
|
+
"structure" and "semantics" is not always crisp ("why does this function call
|
|
206
|
+
`Y`?" is both).
|
|
207
|
+
|
|
208
|
+
The tension resolves toward the split: the *write discipline* is what matters,
|
|
209
|
+
and the three stores have three different write disciplines (deterministic build
|
|
210
|
+
output / curated agent ingestion / cross-harness promotion). Collapsing any two
|
|
211
|
+
of them collapses one discipline into another and loses the property D-007 was
|
|
212
|
+
introduced to protect.
|
|
213
|
+
|
|
214
|
+
### Alternatives considered
|
|
215
|
+
|
|
216
|
+
1. **Keep only the two Cognee stores (status quo).** *Bad.* Structural questions
|
|
217
|
+
stay grep-shaped — high token cost per question, answers that miss
|
|
218
|
+
transitive edges and god-node patterns by construction. Cognee `codify`
|
|
219
|
+
produces an LLM-summarised view of code, not a deterministic call graph: it
|
|
220
|
+
is the wrong tool for "who depends on X" and was never meant to be that tool.
|
|
221
|
+
Leaves the engineer's documented complaint about token waste unaddressed.
|
|
222
|
+
|
|
223
|
+
2. **Add Graphify as a third store with a hard write-discipline boundary.**
|
|
224
|
+
*Chosen.* Each store answers one question, each has one writer, agents
|
|
225
|
+
consult them in a documented order (Graphify first for structure, Cognee
|
|
226
|
+
work for rationale, Cognee curated for cross-project precedent). Mirrors
|
|
227
|
+
D-007's logic: physical separation when write discipline differs.
|
|
228
|
+
|
|
229
|
+
3. **Make Graphify a pre-processor / feeder into Cognee work.** Rejected.
|
|
230
|
+
Graphify's value is that the graph is *deterministic and re-derivable* —
|
|
231
|
+
pushing it through Cognee's LLM extraction layer destroys both properties.
|
|
232
|
+
The output would be a lossy paraphrase of a graph that was exact, ingested
|
|
233
|
+
into a store optimised for semantics not topology. It would also entangle
|
|
234
|
+
Graphify's update cycle (per-commit) with Cognee's ingestion cycle (curated,
|
|
235
|
+
sparse), forcing one to the cadence of the other.
|
|
236
|
+
|
|
237
|
+
4. **Substitute Cognee with Graphify entirely.** *Bad — door explicitly closed.*
|
|
238
|
+
Graphify does not hold decisions, rationale, sprint reports, agent learnings,
|
|
239
|
+
or the curated cross-project library. Replacing Cognee with Graphify would
|
|
240
|
+
delete the "why" layer to gain a "how" layer. The two are orthogonal, not
|
|
241
|
+
competing.
|
|
242
|
+
|
|
243
|
+
5. **Defer Graphify and re-evaluate after a measurement POC.** Rejected as the
|
|
244
|
+
primary path because the write-discipline argument is independent of any
|
|
245
|
+
token-saving number: even if Graphify saved zero tokens, the deterministic
|
|
246
|
+
structural graph still belongs in its own store. A measurement POC is still
|
|
247
|
+
useful but is out of scope here (see below).
|
|
248
|
+
|
|
249
|
+
### Invariants of boundary (the routing matrix agents follow)
|
|
250
|
+
|
|
251
|
+
- **Structure → Graphify.** "Who calls X", "what depends on Y", "god nodes",
|
|
252
|
+
"files touching table Z", "transitive dependents of module M", "what does
|
|
253
|
+
this file import / export". Deterministic; re-derivable; cite the graph node
|
|
254
|
+
id.
|
|
255
|
+
- **Project rationale / decisions / learnings → Cognee work.** "Why did we
|
|
256
|
+
choose X over Y", "what did sprint S3 conclude on auth", "what did Hizkiah
|
|
257
|
+
learn about the embedding 429 issue". Curated; cite the ingested artifact.
|
|
258
|
+
- **Cross-project reference → Cognee curated.** "How have we handled rate
|
|
259
|
+
limiting elsewhere", "what does the engineer profile say about commit
|
|
260
|
+
format". Read-only from projects; cite the curated node id.
|
|
261
|
+
- **Ambiguous questions ("why does this function call Y?")** decompose into a
|
|
262
|
+
structure half (Graphify: it calls Y at file:line, via path P, in branch B)
|
|
263
|
+
and a semantics half (Cognee work: ADR D-00x decided that B owns Y because
|
|
264
|
+
reasons). Agents answer both halves explicitly; they never fuse them into
|
|
265
|
+
one store.
|
|
266
|
+
- **Write authority is exclusive.** No agent writes to Graphify (only the
|
|
267
|
+
`graphify` CLI does, on its update trigger). No build process writes to
|
|
268
|
+
Cognee work (only agents do, gated by `mishkan: ingest`). No project writes
|
|
269
|
+
to Cognee curated (only `/sprint-close` and the seed script do).
|
|
270
|
+
|
|
271
|
+
### Integration
|
|
272
|
+
|
|
273
|
+
- **Runtime placement.** Graphify runs **per-project**, artifacts live under
|
|
274
|
+
`.graphify/` in the project (`graph.html`, `GRAPH_REPORT.md`, `graph.json`),
|
|
275
|
+
gitignored by default. No shared service, no port. The Neo4j export
|
|
276
|
+
(`--neo4j-push`) targets a **dedicated Graphify Neo4j container** when used —
|
|
277
|
+
it does **not** share the Cognee work store's Neo4j (port 7687 in that
|
|
278
|
+
container is Cognee's; collision would commingle a deterministic AST graph
|
|
279
|
+
with an LLM-summarised semantic graph, the exact conflation D-008 forbids).
|
|
280
|
+
Most agent queries hit `graph.json` directly; Neo4j is opt-in for cross-repo
|
|
281
|
+
graph queries and not part of the default install.
|
|
282
|
+
- **Re-extraction trigger.** Incremental refresh via `graphify --update` runs:
|
|
283
|
+
(a) on a **post-commit hook** when files in `src/`, `lib/`, or
|
|
284
|
+
language-specific source roots change, and (b) on `/sprint-close` as a
|
|
285
|
+
belt-and-braces full re-extract. No cron — the engineer's stateful-operation
|
|
286
|
+
rule applies: AI prepares the command, Y4NN runs it (the post-commit hook is
|
|
287
|
+
local to his machine, run by his shell, not by an agent).
|
|
288
|
+
- **Agent consult order (PreToolUse hook).** Graphify ships a PreToolUse hook
|
|
289
|
+
that nudges Claude Code toward graph-first queries. The MISHKAN integration
|
|
290
|
+
is **deferred to a follow-up ADR** (see Out of Scope) — landing it requires
|
|
291
|
+
threading through the existing Bash PreToolUse validator chain (D-004) and
|
|
292
|
+
per-agent opt-in for the five code-writing specialists (Hizkiah, Salma,
|
|
293
|
+
Oholiab, Nathan, Zadok). The hook is *available* but *not enabled by default*
|
|
294
|
+
in this decision.
|
|
295
|
+
- **Citation discipline.** When an agent answers from Graphify, it cites the
|
|
296
|
+
graph node id and the source `file:line`. When it answers from Cognee work,
|
|
297
|
+
it cites the ingested artifact. No "according to the graph" without an id.
|
|
298
|
+
|
|
299
|
+
### Out of scope (explicitly not decided here)
|
|
300
|
+
|
|
301
|
+
1. **Token-saving measurement POC.** Whether Graphify saves Nx tokens on a
|
|
302
|
+
representative agent workload is a separate, instrumented experiment with
|
|
303
|
+
its own success criteria. The 71.5× figure circulating in third-party write-ups
|
|
304
|
+
is **not** verified by this ADR and must not be cited as a MISHKAN claim
|
|
305
|
+
until measured.
|
|
306
|
+
2. **Refactor of the Explore agent / Hiram's exploration playbook.** Whether
|
|
307
|
+
Hiram should consult Graphify before grep is a downstream agent change,
|
|
308
|
+
not decided here.
|
|
309
|
+
3. **Unified Graph Explorer UI** combining Graphify's `graph.html` and
|
|
310
|
+
Cognee's Neo4j browser. Not decided; each store keeps its own UI.
|
|
311
|
+
4. **Graphify PreToolUse hook enablement and routing.** Whether and how the
|
|
312
|
+
hook fires for the five code-writing specialists, and how it composes with
|
|
313
|
+
the existing Bash PreToolUse chain, is deferred to a follow-up ADR.
|
|
314
|
+
5. **Cognee `codify` deprecation.** Whether Cognee's own code-extraction
|
|
315
|
+
feature is now redundant in MISHKAN given Graphify is not decided here —
|
|
316
|
+
it stays available; `mishkan-ingest` continues to gate its use.
|
|
317
|
+
6. **Cross-project Graphify federation** (one graph across all projects in
|
|
318
|
+
`projects.yaml`). Not in scope; Graphify is per-project for now.
|
|
319
|
+
|
|
320
|
+
### Consequences
|
|
321
|
+
|
|
322
|
+
**Positive.**
|
|
323
|
+
- Code-writing specialists (Hizkiah, Salma, Oholiab, Nathan, Zadok) get a
|
|
324
|
+
deterministic structural answer for "who calls X" / "what depends on Y"
|
|
325
|
+
without grep-shaped token spend.
|
|
326
|
+
- Write discipline stays sharp: three stores, three writers, three citation
|
|
327
|
+
shapes. No agent has to guess where to write.
|
|
328
|
+
- Cognee work stays small and curated — it does not get polluted with
|
|
329
|
+
AST-derived nodes that change on every commit.
|
|
330
|
+
- Graphify artifacts are re-derivable from the repo, so the third store has
|
|
331
|
+
effectively zero backup obligation: delete `.graphify/` and rebuild.
|
|
332
|
+
- The boundary is testable: a CI check can refuse a commit that ingests
|
|
333
|
+
AST-shaped content into Cognee work.
|
|
334
|
+
|
|
335
|
+
**Negative.**
|
|
336
|
+
- One more runtime to install, document, and teach 45 agents to route to.
|
|
337
|
+
- Graphify v0.8.x is young; a breaking change in node schema or CLI flags
|
|
338
|
+
would touch every agent that cites a graph node id. Mitigation: cite by
|
|
339
|
+
`file:line` alongside node id so the answer survives a schema change.
|
|
340
|
+
- The "structure vs semantics" line is not always crisp; some questions
|
|
341
|
+
require both stores and a careful answer. Documented in the routing
|
|
342
|
+
matrix above, but it adds cognitive load.
|
|
343
|
+
- The Neo4j-push path introduces a second Neo4j container if enabled —
|
|
344
|
+
more memory, more secrets to manage. Mitigation: keep it opt-in; default
|
|
345
|
+
is `graph.json` only.
|
|
346
|
+
- The deferred PreToolUse hook means agents must be *told* to consult
|
|
347
|
+
Graphify first; the harness does not enforce it until the follow-up ADR
|
|
348
|
+
lands.
|
|
349
|
+
|
|
350
|
+
**Supersedes / amends:** none. Extends D-001 (Cognee local Docker) and D-007
|
|
351
|
+
(curated vs work split) by adding a third epistemic layer on the same
|
|
352
|
+
discipline.
|
|
353
|
+
|
|
354
|
+
## D-009 — Graph-first PreToolUse gate for the five code-writing specialists (added 2026-06-05)
|
|
355
|
+
|
|
356
|
+
**Decision:** introduce a MISHKAN PreToolUse hook — `pre-tool-graphify-nudge.py` —
|
|
357
|
+
that, for **exactly the five code-writing specialist agents** (Hizkiah, Salma,
|
|
358
|
+
Oholiab, Nathan, Zadok), runs alongside the existing security (D-004) and
|
|
359
|
+
model-routing hooks and **advises** — does not block — when a `Read` or `Grep`
|
|
360
|
+
call on source code looks like a structural query that Graphify (D-008) could
|
|
361
|
+
have answered deterministically. The hook is **advisory** (soft gate), not
|
|
362
|
+
hard-deny: it injects a permissionDecisionReason nudge plus, where supported, a
|
|
363
|
+
concrete `graphify search` command into the tool input metadata, but always
|
|
364
|
+
returns `allow`. Conformance is measured via the existing PostToolUse observer
|
|
365
|
+
chain; the gate **never** refuses the underlying tool call.
|
|
366
|
+
|
|
367
|
+
### Force-tension
|
|
368
|
+
|
|
369
|
+
**What pushes toward enforcement.** The five named agents are the harness's
|
|
370
|
+
heaviest token consumers — Hizkiah and Salma in particular routinely Read 6–10
|
|
371
|
+
files to answer "who calls X" or "what depends on Y", which D-008 just declared
|
|
372
|
+
the wrong store for that question. Without a runtime mechanism, the D-008
|
|
373
|
+
routing matrix is doctrine on paper that drifts the moment an agent is mid-task
|
|
374
|
+
and reaches for the familiar tool. The whole reason D-008 exists is to make
|
|
375
|
+
structural queries deterministic; leaving the enforcement at "we told them to"
|
|
376
|
+
collapses the discipline into a code-review aspiration.
|
|
377
|
+
|
|
378
|
+
**What pushes against a hard gate.** Three failure modes make hard-deny costly:
|
|
379
|
+
(a) the project may not yet be scanned (first run, fresh clone) — denying Read
|
|
380
|
+
would brick the agent until Y4NN runs `graphify`, violating the stateful-
|
|
381
|
+
operation contract that says AI prepares but does not execute scans; (b) the
|
|
382
|
+
graph can be stale (HEAD has moved since the last `--update`), so the agent's
|
|
383
|
+
correct move is to Read the source of truth, not the cached graph; (c) the
|
|
384
|
+
heuristic for "this query is structural" is necessarily fuzzy — a query like
|
|
385
|
+
"how does this function handle errors" is semantic and Graphify is the wrong
|
|
386
|
+
store for it. A hard gate would generate false-positive blocks on cases (a),
|
|
387
|
+
(b), and (c), creating the exact friction the engineer profile names as a
|
|
388
|
+
top complaint with AI tooling.
|
|
389
|
+
|
|
390
|
+
The tension resolves toward **soft gate + telemetry**: the hook nudges and
|
|
391
|
+
records, never blocks. If telemetry shows ≥80% of code-writing Reads now
|
|
392
|
+
preceded by `graphify search` after one sprint, the doctrine is working without
|
|
393
|
+
enforcement. If it does not, a future ADR can revisit hardening.
|
|
394
|
+
|
|
395
|
+
### Alternatives considered
|
|
396
|
+
|
|
397
|
+
1. **Hard gate — deny Read/Grep when no `graphify search` was observed in the
|
|
398
|
+
agent's session within the last N tool calls.** *Bad.* Brittle on first-scan,
|
|
399
|
+
stale-graph, and semantic-query cases; turns a coordination problem into a
|
|
400
|
+
runtime block; violates the asymmetric-delegation contract by effectively
|
|
401
|
+
forcing the agent to ask Y4NN to run a scan mid-task.
|
|
402
|
+
|
|
403
|
+
2. **Soft gate — advisory injection on suspected structural queries; always
|
|
404
|
+
allow.** *Chosen.* Surfaces the doctrine at the exact moment it would
|
|
405
|
+
otherwise be skipped, names the concrete command (`graphify search <symbol>`),
|
|
406
|
+
and degrades gracefully when the graph is absent or stale. Composes cleanly
|
|
407
|
+
with the existing Bash PreToolUse chain (D-004) and the Python model-routing
|
|
408
|
+
hook because all three already follow the fail-open-on-error contract.
|
|
409
|
+
|
|
410
|
+
3. **Silent telemetry only — count the conformance ratio in the PostToolUse
|
|
411
|
+
bus, no advisory in the prompt.** *Useful, but insufficient alone.* Without
|
|
412
|
+
the in-prompt nudge, the agent has no feedback signal mid-task; the ratio
|
|
413
|
+
would document drift rather than reduce it. Adopted as **phase 1**: ship the
|
|
414
|
+
telemetry first (single sprint), then layer the advisory nudge on top once
|
|
415
|
+
the baseline is measured. The full soft-gate behaviour is the **phase 2**
|
|
416
|
+
target documented here.
|
|
417
|
+
|
|
418
|
+
4. **Skill-only doctrine — encode "graphify first on structural queries" in the
|
|
419
|
+
five craft skills (`hizkiah-backend-impl-craft.md` etc.) and rely on agents
|
|
420
|
+
to follow it.** *Insufficient.* The harness already has the precedent
|
|
421
|
+
(D-004 PreToolUse security) that doctrine without a mechanism drifts;
|
|
422
|
+
skills inform behaviour, hooks enforce shape. Adopted **alongside** the
|
|
423
|
+
hook, not as a substitute.
|
|
424
|
+
|
|
425
|
+
5. **Expand the gate to all 45 agents.** Rejected as scope creep. The QA,
|
|
426
|
+
reporter, research, and orchestration agents do not write code and rarely
|
|
427
|
+
ask structural questions; gating them adds noise without changing behaviour.
|
|
428
|
+
Expansion to Hiram (Explore) is the obvious next candidate but is deferred
|
|
429
|
+
to the Explore-refactor ADR (D-008 Out of Scope #2).
|
|
430
|
+
|
|
431
|
+
### Invariants of the gate
|
|
432
|
+
|
|
433
|
+
- **Scope — exactly five agents.** The hook activates only when the invoking
|
|
434
|
+
subagent is one of: **Hizkiah, Salma, Oholiab, Nathan, Zadok**. For any
|
|
435
|
+
other agent (including Hiram, Caleb, all QA/reporters/orchestrators), the
|
|
436
|
+
hook is a no-op. Adding an agent requires a new ADR.
|
|
437
|
+
- **Trigger condition (precise).**
|
|
438
|
+
- **`Read`** on a file whose extension matches the Graphify-supported set:
|
|
439
|
+
`.py .ts .tsx .js .jsx .mjs .cjs .go .rs .java .php .rb`. Configs,
|
|
440
|
+
Markdown, YAML, lockfiles, and dotfiles are **not** triggers.
|
|
441
|
+
- **`Grep`** when the `pattern` is a bare identifier (matches
|
|
442
|
+
`^[A-Za-z_][A-Za-z0-9_]*$`) — i.e. clearly a symbol lookup, not a
|
|
443
|
+
semantic regex. Patterns with `.*`, alternation, multiline, or non-word
|
|
444
|
+
characters do **not** trigger.
|
|
445
|
+
- **Explore** tool calls are out of scope (deferred ADR).
|
|
446
|
+
- **Fallback behaviour (graceful degradation).**
|
|
447
|
+
- *No `.graphify/graph.json` in the project* → emit a single advisory line
|
|
448
|
+
"Graphify not yet scanned for this project; ask Y4NN to run `graphify`
|
|
449
|
+
(stateful op). Falling back to Read is correct for now." and `allow`.
|
|
450
|
+
- *Graph stale* (`graph.json` mtime older than the most recent commit on
|
|
451
|
+
HEAD) → emit "Graphify graph is older than HEAD; structural answer may
|
|
452
|
+
be stale, prefer Read+cite for changes after `<sha>`." and `allow`.
|
|
453
|
+
- *Trigger heuristic likely wrong* (the agent has already issued a
|
|
454
|
+
`graphify search` in this session for a related symbol) → no nudge,
|
|
455
|
+
silent allow. The PostToolUse counter still records the read.
|
|
456
|
+
- **Opt-out path.** Two mechanisms, in order of normalcy:
|
|
457
|
+
1. Per-tool-call: a `tool_input.metadata.skip_graphify_nudge: true` field
|
|
458
|
+
suppresses the advisory for that single call. Used when an agent has
|
|
459
|
+
explicitly decided the query is semantic.
|
|
460
|
+
2. Session-wide: env `MISHKAN_GRAPHIFY_NUDGE=off` disables the hook
|
|
461
|
+
entirely (Y4NN debug escape). Recorded in the session-start observer
|
|
462
|
+
so disablement is visible in sprint reports.
|
|
463
|
+
- **Performance budget.** The hook must add **≤ 50 ms p95** to PreToolUse
|
|
464
|
+
latency. Implementation must avoid invoking `graphify search` itself
|
|
465
|
+
inside the hook — it inspects `graph.json` metadata (mtime, presence)
|
|
466
|
+
and the tool input shape only. If the hook exceeds 200 ms on any call,
|
|
467
|
+
it self-disables for the remainder of the session and logs to the
|
|
468
|
+
PostToolUse observer ("graphify nudge self-disabled: budget exceeded").
|
|
469
|
+
- **Fail-open contract.** Identical to D-004 and to `model-route.py`: any
|
|
470
|
+
parse, IO, or format error → emit nothing, exit 0, never block.
|
|
471
|
+
- **Conformance metric.** Two numbers, recorded by `post-tool-observe.sh`
|
|
472
|
+
per session and aggregated at `/sprint-close`:
|
|
473
|
+
1. **Nudge-respect ratio** — of the Read/Grep calls that triggered an
|
|
474
|
+
advisory, the fraction followed by a `graphify search` within the
|
|
475
|
+
next 3 tool calls of the same agent.
|
|
476
|
+
2. **Pre-Read graph consultation rate** — of all triggering Read/Grep
|
|
477
|
+
calls by the five agents, the fraction preceded by **any**
|
|
478
|
+
`graphify search` in the same session. Target ≥ 80% after one
|
|
479
|
+
sprint of phase-2 operation.
|
|
480
|
+
Both metrics are reported, neither is a gate.
|
|
481
|
+
|
|
482
|
+
### Integration
|
|
483
|
+
|
|
484
|
+
- **Hook file.** `payload/mishkan/hooks/pre-tool-graphify-nudge.py`,
|
|
485
|
+
registered alongside `pre-tool-security.sh` and `model-route.py` in the
|
|
486
|
+
PreToolUse chain. Order: security (deny on violation) → model-route
|
|
487
|
+
(inject model) → graphify-nudge (advise). Each is independent and
|
|
488
|
+
fail-open; chain order is for clarity, not correctness.
|
|
489
|
+
- **Subagent detection.** Reuses the same `subagent_type` field
|
|
490
|
+
`model-route.py` already reads from `tool_input`. When the field is
|
|
491
|
+
absent (top-level Claude Code session, not a subagent), the hook is a
|
|
492
|
+
no-op — Y4NN is not in scope of the gate.
|
|
493
|
+
- **Advisory shape.** The hook returns
|
|
494
|
+
`hookSpecificOutput.permissionDecision = "allow"` with a populated
|
|
495
|
+
`permissionDecisionReason` quoting the exact `graphify search` command
|
|
496
|
+
to try first. The agent sees the reason; nothing is enforced.
|
|
497
|
+
- **Phase 1 (this sprint).** Ship the hook in **telemetry-only mode**:
|
|
498
|
+
trigger detection runs, metrics are recorded, no advisory text emitted.
|
|
499
|
+
Establishes the baseline number for "how often do the five agents
|
|
500
|
+
already consult Graphify before Read?".
|
|
501
|
+
- **Phase 2 (next sprint, conditional on phase-1 baseline).** Enable the
|
|
502
|
+
advisory text. Re-measure. If nudge-respect ratio < 50% after one sprint,
|
|
503
|
+
open a follow-up ADR — do not unilaterally promote to hard gate.
|
|
504
|
+
|
|
505
|
+
### Out of scope (explicitly not decided here)
|
|
506
|
+
|
|
507
|
+
1. **Token-saving measurement POC** (the 71.5× figure) — still separate, as
|
|
508
|
+
in D-008 Out of Scope #1. The conformance metric here is behavioural
|
|
509
|
+
(did the agent consult the graph?), not economic (how many tokens did
|
|
510
|
+
it save?).
|
|
511
|
+
2. **Refactor of the Explore agent / Hiram's exploration playbook** —
|
|
512
|
+
deferred per D-008 Out of Scope #2. The gate explicitly excludes
|
|
513
|
+
Explore tool calls.
|
|
514
|
+
3. **Unified Graph Explorer UI** — out of scope, per D-008.
|
|
515
|
+
4. **Adding further agents to the gate** (e.g. Hiram once refactored, or
|
|
516
|
+
any future code-writing specialist) — requires a new ADR amendment.
|
|
517
|
+
5. **Cognee-work consultation gate** — a symmetric soft-gate for semantic
|
|
518
|
+
queries ("have we decided this before?") that would nudge toward
|
|
519
|
+
Cognee MCP before a freeform reasoning answer. Plausible and consistent
|
|
520
|
+
with the D-008 routing matrix, but not decided here. If pursued, it
|
|
521
|
+
would follow this ADR's shape (advisory, fail-open, telemetry-first).
|
|
522
|
+
6. **Incremental re-extract on commit** — D-008 Out of Scope. The stale-
|
|
523
|
+
graph fallback above accommodates the absence of incremental refresh;
|
|
524
|
+
it does not commit the harness to provide one.
|
|
525
|
+
7. **Hard-deny mode** — explicitly deferred. The phase-2 advisory is the
|
|
526
|
+
strongest enforcement this ADR sanctions. Any future hardening is a
|
|
527
|
+
separate, dated decision.
|
|
528
|
+
|
|
529
|
+
### Consequences
|
|
530
|
+
|
|
531
|
+
**Positive.**
|
|
532
|
+
- The D-008 routing matrix gains a runtime mechanism for its highest-
|
|
533
|
+
traffic edge (structural queries by code-writing specialists), without
|
|
534
|
+
introducing a friction failure mode the engineer profile rejects.
|
|
535
|
+
- Telemetry-first phasing means the harness measures before it constrains;
|
|
536
|
+
the phase-2 decision will be grounded in a baseline, not a guess.
|
|
537
|
+
- Fail-open contract preserves the property all MISHKAN hooks share: a
|
|
538
|
+
broken hook never bricks delegation.
|
|
539
|
+
- Composable with the existing PreToolUse chain (D-004 security,
|
|
540
|
+
model-route) without rewriting any of them; each hook stays small and
|
|
541
|
+
inspectable.
|
|
542
|
+
|
|
543
|
+
**Negative.**
|
|
544
|
+
- One more hook to maintain, with its own heuristic surface (extension
|
|
545
|
+
list, regex for "bare identifier", stale-graph detection). Each is a
|
|
546
|
+
small calibration debt — false-positive nudges on semantic queries
|
|
547
|
+
will accumulate small annoyance until the advisory wording is tuned.
|
|
548
|
+
- The stale-graph fallback depends on file mtime vs. HEAD commit time,
|
|
549
|
+
which is approximate; an agent could pull a graph from a sibling
|
|
550
|
+
checkout and have a wrong mtime [UNKNOWN — to verify in implementation
|
|
551
|
+
whether this corner case warrants a content-hash check].
|
|
552
|
+
- Phase-2 enablement creates a sprint-boundary coordination point: the
|
|
553
|
+
decision to flip the advisory on depends on phase-1 telemetry being
|
|
554
|
+
reviewed, which is a `/sprint-close` agenda item that did not previously
|
|
555
|
+
exist.
|
|
556
|
+
- The 50 ms p95 budget is tight for Python startup on cold cache;
|
|
557
|
+
implementation may need to be a small Bash inspector instead, mirroring
|
|
558
|
+
`pre-tool-security.sh`'s shape. [UNKNOWN — measure cold-start cost of
|
|
559
|
+
the Python interpreter on Y4NN's machine before committing to language.]
|
|
560
|
+
- Documents an opt-out (`MISHKAN_GRAPHIFY_NUDGE=off`) which, like every
|
|
561
|
+
opt-out, can become a habit that erodes the discipline. The session-
|
|
562
|
+
start observer logging is the partial mitigation; review at
|
|
563
|
+
`/sprint-close` is the rest.
|
|
564
|
+
|
|
565
|
+
**Supersedes / amends:** none. Extends D-008 (Graphify as third store) by
|
|
566
|
+
providing the runtime mechanism D-008 explicitly deferred (D-008 Out of
|
|
567
|
+
Scope #4). Composes with D-004 (existing PreToolUse Bash chain) without
|
|
568
|
+
modifying it.
|
|
569
|
+
|
|
170
570
|
---
|
|
171
571
|
|
|
172
572
|
*Decisions locked May 2026. Revisit only with a dated amendment below.*
|