mishkan-harness 0.2.0 → 0.2.3
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 +108 -172
- package/bin/mishkan.js +677 -16
- package/docs/design/MISHKAN_decisions.md +450 -8
- package/docs/design/MISHKAN_harness_design.md +1 -33
- package/docs/design/MISHKAN_observability.md +13 -9
- package/docs/research/graphify-token-saving-poc.md +108 -0
- package/docs/usage/09-workflows.md +74 -4
- package/docs/usage/10-observability.md +37 -13
- package/docs/usage/11-graphify.md +264 -0
- package/docs/usage/12-skill-discovery.md +294 -0
- package/docs/usage/README.md +5 -3
- package/package.json +1 -1
- package/payload/install/settings.hooks.json +24 -0
- package/payload/mishkan/AGENT_SPEC.md +25 -7
- package/payload/mishkan/agents/ahikam.md +4 -2
- package/payload/mishkan/agents/aholiab.md +4 -2
- package/payload/mishkan/agents/asaph.md +5 -2
- package/payload/mishkan/agents/baruch.md +19 -2
- package/payload/mishkan/agents/benaiah.md +4 -2
- package/payload/mishkan/agents/bezalel.md +4 -2
- package/payload/mishkan/agents/caleb.md +4 -2
- package/payload/mishkan/agents/deborah.md +4 -2
- package/payload/mishkan/agents/elasah.md +4 -2
- package/payload/mishkan/agents/eliashib.md +4 -2
- package/payload/mishkan/agents/ezra.md +4 -2
- package/payload/mishkan/agents/hanun.md +5 -2
- package/payload/mishkan/agents/hiram.md +5 -2
- package/payload/mishkan/agents/hizkiah.md +5 -2
- package/payload/mishkan/agents/huldah.md +4 -2
- package/payload/mishkan/agents/huram.md +4 -2
- package/payload/mishkan/agents/hushai.md +5 -2
- package/payload/mishkan/agents/igal.md +4 -2
- package/payload/mishkan/agents/ira.md +5 -2
- package/payload/mishkan/agents/jahaziel.md +5 -2
- package/payload/mishkan/agents/jakin.md +4 -2
- package/payload/mishkan/agents/jehonathan.md +5 -2
- package/payload/mishkan/agents/jehoshaphat.md +4 -2
- package/payload/mishkan/agents/joab.md +5 -2
- package/payload/mishkan/agents/joah.md +5 -2
- package/payload/mishkan/agents/maaseiah.md +4 -2
- package/payload/mishkan/agents/meremoth.md +5 -2
- package/payload/mishkan/agents/meshullam.md +5 -2
- package/payload/mishkan/agents/nathan.md +5 -2
- package/payload/mishkan/agents/nehemiah.md +4 -2
- package/payload/mishkan/agents/obed.md +4 -2
- package/payload/mishkan/agents/oholiab.md +5 -2
- package/payload/mishkan/agents/palal.md +5 -2
- package/payload/mishkan/agents/phinehas.md +4 -2
- package/payload/mishkan/agents/rehum.md +4 -2
- package/payload/mishkan/agents/salma.md +5 -2
- package/payload/mishkan/agents/seraiah.md +4 -2
- package/payload/mishkan/agents/shallum.md +5 -2
- package/payload/mishkan/agents/shaphan.md +4 -2
- package/payload/mishkan/agents/shemaiah.md +4 -2
- package/payload/mishkan/agents/shevna.md +5 -2
- package/payload/mishkan/agents/uriah.md +5 -2
- package/payload/mishkan/agents/zaccur.md +4 -2
- package/payload/mishkan/agents/zadok.md +5 -2
- package/payload/mishkan/agents/zerubbabel.md +4 -2
- package/payload/mishkan/commands/code-graph.md +35 -0
- package/payload/mishkan/commands/eval-baruch.md +57 -0
- package/payload/mishkan/commands/mishkan-org-reference.md +58 -0
- package/payload/mishkan/commands/mishkan-skills-misses.md +47 -0
- package/payload/mishkan/commands/mishkan-skills-reindex.md +33 -0
- package/payload/mishkan/commands/skills.md +35 -0
- package/payload/mishkan/commands/sprint-close.md +3 -1
- package/payload/mishkan/hooks/pre-tool-knowledge-route.sh +231 -0
- package/payload/mishkan/hooks/pre-tool-task-skill-route.sh +122 -0
- package/payload/mishkan/hooks/session-start-skill-index.sh +35 -0
- package/payload/mishkan/observability/schema.json +1 -1
- package/payload/mishkan/observability/watch/src/mishkan_watch/__main__.py +84 -2
- package/payload/mishkan/observability/watch/src/mishkan_watch/app.py +64 -3
- package/payload/mishkan/observability/watch/src/mishkan_watch/org_data.py +79 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/skills_data.py +267 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/agents.py +33 -1
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/knowledge.py +118 -9
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/live.py +102 -2
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/org.py +139 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/skills.py +187 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/usage.py +377 -0
- package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/workflows.py +199 -27
- package/payload/mishkan/observability/watch/src/mishkan_watch/theme.tcss +78 -4
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/__main__.py +36 -6
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/bus_tail.py +16 -3
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/graphify_tail.py +220 -0
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/worktree_poll.py +13 -2
- package/payload/mishkan/observability/watchd/src/mishkan_watchd/state.py +157 -3
- package/payload/mishkan/observability/watchd/tests/test_state.py +6 -2
- package/payload/mishkan/org/org.json +141 -0
- package/payload/mishkan/scripts/pre-commit-eval-gate.sh +50 -0
- package/payload/mishkan/scripts/skill-discovery-indexer.py +462 -0
- package/payload/mishkan/scripts/skill-discovery-misses.py +173 -0
- package/payload/mishkan/scripts/skill-discovery-router.py +457 -0
- package/payload/mishkan/scripts/skill-discovery-smoke.sh +156 -0
- package/payload/mishkan/skills/graphify-query-craft/SKILL.md +162 -0
- package/payload/mishkan/skills/mishkan-init/SKILL.md +15 -0
- package/payload/mishkan/skills/skill-discovery/SKILL.md +120 -0
- package/payload/mishkan/templates/case-node.schema.json +1 -0
- package/payload/mishkan/templates/observability-log.schema.json +1 -0
- package/payload/mishkan/workflows/README.md +78 -13
- package/payload/mishkan/workflows/chosheb-feature-ship.js +75 -0
- package/payload/mishkan/workflows/migdal-dr-drill.js +94 -0
- package/payload/mishkan/workflows/migdal-infra-change.js +67 -0
- package/payload/mishkan/workflows/mishkan-blast-radius.js +144 -0
- package/payload/mishkan/workflows/mishkan-init.js +58 -0
- package/payload/mishkan/workflows/mishkan-knowledge-gap-discovery.js +121 -0
- package/payload/mishkan/workflows/mishkan-standards-rollout.js +153 -0
- package/payload/mishkan/workflows/mishmar-security-gate.js +83 -0
- package/payload/mishkan/workflows/panim-ds-rollout.js +83 -0
- package/payload/mishkan/workflows/sefer-release-notes.js +85 -0
- package/payload/mishkan/workflows/yasad-data-migration-wave.js +78 -0
- package/payload/mishkan/workflows/yasad-schema-evolution.js +76 -0
- package/payload/mishkan/templates/user-CLAUDE.md +0 -62
|
@@ -18,6 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
from collections import deque
|
|
19
19
|
from dataclasses import dataclass, field
|
|
20
20
|
from datetime import datetime, timezone
|
|
21
|
+
from time import monotonic
|
|
21
22
|
from typing import Any, Optional
|
|
22
23
|
|
|
23
24
|
|
|
@@ -26,6 +27,12 @@ def _now() -> str:
|
|
|
26
27
|
f"{datetime.now(tz=timezone.utc).microsecond // 1000:03d}Z"
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
# How long an event for an unconfirmed session is buffered before being
|
|
31
|
+
# dropped. session_discover polls every 10s, so 15s covers the case where
|
|
32
|
+
# a hook fires for a brand-new session before its first JSONL flush.
|
|
33
|
+
_PENDING_TTL_S = 15.0
|
|
34
|
+
|
|
35
|
+
|
|
29
36
|
@dataclass
|
|
30
37
|
class AgentState:
|
|
31
38
|
name: str
|
|
@@ -36,6 +43,13 @@ class AgentState:
|
|
|
36
43
|
tokens_in: int = 0
|
|
37
44
|
tokens_out: int = 0
|
|
38
45
|
cost_estimate_usd: float = 0.0
|
|
46
|
+
# Claude tier this agent runs on (opus / sonnet / haiku). Lifted from
|
|
47
|
+
# post-tool-observe.sh's `agent_spawn` payload, which already carries the
|
|
48
|
+
# model field that model-route.py injected. Consumed by the Usage tab to
|
|
49
|
+
# pick the per-agent context window (Sonnet 4.6 = 1M, Opus 4.x = 1M,
|
|
50
|
+
# Haiku 4.5 = 200k) instead of pinning the bar at a single fleet-wide
|
|
51
|
+
# default. Empty string when the spawner didn't surface a model.
|
|
52
|
+
model: str = ""
|
|
39
53
|
|
|
40
54
|
|
|
41
55
|
@dataclass
|
|
@@ -93,24 +107,97 @@ class CogneeStoreState:
|
|
|
93
107
|
last_probe: Optional[str] = None
|
|
94
108
|
|
|
95
109
|
|
|
110
|
+
@dataclass
|
|
111
|
+
class GraphifyState:
|
|
112
|
+
"""Aggregated Graphify state across all watched projects.
|
|
113
|
+
|
|
114
|
+
graphify_tail emits scan events whose payload carries the latest
|
|
115
|
+
nodes/edges for a single project; we keep the most recent per-project
|
|
116
|
+
rollup so the snapshot served to a new TUI client carries the current
|
|
117
|
+
state even if the scan event itself fired before the client connected.
|
|
118
|
+
"""
|
|
119
|
+
nodes: int = 0
|
|
120
|
+
edges: int = 0
|
|
121
|
+
communities: int = 0
|
|
122
|
+
scans: int = 0
|
|
123
|
+
queries: int = 0
|
|
124
|
+
last_scan_project: Optional[str] = None
|
|
125
|
+
last_scan_at: Optional[str] = None
|
|
126
|
+
last_query_project: Optional[str] = None
|
|
127
|
+
last_query_at: Optional[str] = None
|
|
128
|
+
last_query_text: Optional[str] = None
|
|
129
|
+
|
|
130
|
+
|
|
96
131
|
@dataclass
|
|
97
132
|
class HarnessState:
|
|
98
133
|
sessions: dict[str, SessionState] = field(default_factory=dict)
|
|
99
134
|
worktrees: dict[str, WorktreeState] = field(default_factory=dict)
|
|
100
135
|
mcp_servers: dict[str, MCPServerState] = field(default_factory=dict)
|
|
101
136
|
cognee: dict[str, CogneeStoreState] = field(default_factory=dict)
|
|
137
|
+
graphify: GraphifyState = field(default_factory=GraphifyState)
|
|
102
138
|
recent_hooks: deque[dict[str, Any]] = field(default_factory=lambda: deque(maxlen=50))
|
|
103
139
|
started: str = field(default_factory=_now)
|
|
140
|
+
# Bounded ring of recently-stopped session ids. Any event for an id
|
|
141
|
+
# in this ring is dropped silently — prevents background-hook events
|
|
142
|
+
# (e.g. CLAUDE_PROJECT_DIR leaking from a stale shell env) from
|
|
143
|
+
# resurrecting a stopped session via _ensure_session. Capacity 256 is
|
|
144
|
+
# ~256 × poll_interval = several minutes of memory; cheap.
|
|
145
|
+
_stopped_recently: deque[str] = field(default_factory=lambda: deque(maxlen=256))
|
|
146
|
+
|
|
147
|
+
# Sessions confirmed alive by session_discover. Only sessions in this
|
|
148
|
+
# set are eligible to have non-session_start events applied to state.
|
|
149
|
+
# session_discover emits session_start when it observes an active
|
|
150
|
+
# JSONL; that adds to this set. session_stop removes from it.
|
|
151
|
+
_confirmed_alive: set[str] = field(default_factory=set)
|
|
152
|
+
|
|
153
|
+
# Per-session buffer of events that arrived before the session_id was
|
|
154
|
+
# confirmed alive by session_discover. Each entry: (monotonic ts, event).
|
|
155
|
+
# On session_start the buffer for that sid is flushed in order. Entries
|
|
156
|
+
# older than _PENDING_TTL_S are dropped during apply() sweep — bounds
|
|
157
|
+
# memory for sids that session_discover never confirms (typically
|
|
158
|
+
# hooks firing for already-gone sessions).
|
|
159
|
+
_pending: dict[str, deque] = field(default_factory=dict)
|
|
104
160
|
|
|
105
161
|
# ----- event application -------------------------------------------------
|
|
106
162
|
|
|
107
163
|
def apply(self, event: dict[str, Any]) -> None:
|
|
108
|
-
"""Apply a bus-format event to the state.
|
|
164
|
+
"""Apply a bus-format event to the state.
|
|
165
|
+
|
|
166
|
+
Authority gate: session_discover is the source of truth for alive
|
|
167
|
+
sessions. Only sessions confirmed by session_discover (via
|
|
168
|
+
session_start) are eligible to have events applied. Events for
|
|
169
|
+
unknown session_ids are buffered briefly (_PENDING_TTL_S) and
|
|
170
|
+
flushed if confirmation arrives; otherwise dropped.
|
|
171
|
+
|
|
172
|
+
This kills the phantom-session bug at its root: bus_tail can no
|
|
173
|
+
longer resurrect stopped or never-alive sessions, no matter what
|
|
174
|
+
old hook events surface from disk.
|
|
175
|
+
"""
|
|
109
176
|
try:
|
|
177
|
+
self._sweep_pending()
|
|
110
178
|
etype = event.get("type")
|
|
111
179
|
session_id = event.get("session")
|
|
180
|
+
|
|
112
181
|
if session_id:
|
|
113
|
-
|
|
182
|
+
if etype == "session_start":
|
|
183
|
+
# session_discover has just confirmed this session is alive.
|
|
184
|
+
self._confirmed_alive.add(session_id)
|
|
185
|
+
self._ensure_session(session_id, event.get("project") or "unknown")
|
|
186
|
+
self._flush_pending(session_id)
|
|
187
|
+
elif etype == "session_stop":
|
|
188
|
+
# Honoured below; remove from confirmed + tombstone.
|
|
189
|
+
pass
|
|
190
|
+
elif session_id in self._stopped_recently:
|
|
191
|
+
# Stopped session — drop silently. Tombstone window handles
|
|
192
|
+
# lagging hook events for sessions whose stop just propagated.
|
|
193
|
+
return
|
|
194
|
+
elif session_id not in self._confirmed_alive:
|
|
195
|
+
# Unknown session. Buffer with TTL — if session_discover
|
|
196
|
+
# confirms within _PENDING_TTL_S we replay in order;
|
|
197
|
+
# otherwise the sweep drops it.
|
|
198
|
+
buf = self._pending.setdefault(session_id, deque(maxlen=200))
|
|
199
|
+
buf.append((monotonic(), event))
|
|
200
|
+
return
|
|
114
201
|
|
|
115
202
|
if etype == "agent_spawn":
|
|
116
203
|
self._on_agent_spawn(session_id, event)
|
|
@@ -128,16 +215,53 @@ class HarnessState:
|
|
|
128
215
|
self._on_mcp(event)
|
|
129
216
|
elif etype == "cognee_op":
|
|
130
217
|
self._on_cognee_op(event)
|
|
218
|
+
elif etype in ("graphify_scan", "graphify_query"):
|
|
219
|
+
self._on_graphify(event)
|
|
131
220
|
elif etype == "session_start":
|
|
132
221
|
pass # ensured above
|
|
133
222
|
elif etype == "session_stop":
|
|
134
|
-
|
|
223
|
+
if session_id:
|
|
224
|
+
self.sessions.pop(session_id, None)
|
|
225
|
+
self._confirmed_alive.discard(session_id)
|
|
226
|
+
self._pending.pop(session_id, None)
|
|
227
|
+
# Tombstone so lagging hook events for this session
|
|
228
|
+
# don't resurrect it. Bounded ring — old tombstones age out.
|
|
229
|
+
self._stopped_recently.append(session_id)
|
|
135
230
|
|
|
136
231
|
if session_id and session_id in self.sessions:
|
|
137
232
|
self.sessions[session_id].recent_events.append(event)
|
|
138
233
|
except Exception:
|
|
139
234
|
return # fail-open on any state error
|
|
140
235
|
|
|
236
|
+
# ----- pending buffer (confirmed-alive gate) -----------------------------
|
|
237
|
+
|
|
238
|
+
def _sweep_pending(self) -> None:
|
|
239
|
+
"""Drop pending events older than _PENDING_TTL_S.
|
|
240
|
+
|
|
241
|
+
Called at the head of every apply() — bounded O(buffered sids)
|
|
242
|
+
which stays small in practice (only sids session_discover hasn't
|
|
243
|
+
confirmed yet, mostly transient).
|
|
244
|
+
"""
|
|
245
|
+
if not self._pending:
|
|
246
|
+
return
|
|
247
|
+
now = monotonic()
|
|
248
|
+
for sid in list(self._pending.keys()):
|
|
249
|
+
buf = self._pending[sid]
|
|
250
|
+
while buf and (now - buf[0][0]) > _PENDING_TTL_S:
|
|
251
|
+
buf.popleft()
|
|
252
|
+
if not buf:
|
|
253
|
+
del self._pending[sid]
|
|
254
|
+
|
|
255
|
+
def _flush_pending(self, session_id: str) -> None:
|
|
256
|
+
"""Replay all buffered events for a now-confirmed session, in order."""
|
|
257
|
+
buf = self._pending.pop(session_id, None)
|
|
258
|
+
if not buf:
|
|
259
|
+
return
|
|
260
|
+
for _, ev in list(buf):
|
|
261
|
+
# Re-enter apply(); the sid is now in _confirmed_alive so the
|
|
262
|
+
# gate above accepts.
|
|
263
|
+
self.apply(ev)
|
|
264
|
+
|
|
141
265
|
# ----- snapshot for new clients ------------------------------------------
|
|
142
266
|
|
|
143
267
|
def to_snapshot(self) -> dict[str, Any]:
|
|
@@ -149,6 +273,7 @@ class HarnessState:
|
|
|
149
273
|
"worktrees": {p: _dc_to_dict(w) for p, w in self.worktrees.items()},
|
|
150
274
|
"mcp_servers": {n: _dc_to_dict(s) for n, s in self.mcp_servers.items()},
|
|
151
275
|
"cognee": {n: _dc_to_dict(s) for n, s in self.cognee.items()},
|
|
276
|
+
"graphify": _dc_to_dict(self.graphify),
|
|
152
277
|
"recent_hooks": list(self.recent_hooks),
|
|
153
278
|
}
|
|
154
279
|
|
|
@@ -177,11 +302,13 @@ class HarnessState:
|
|
|
177
302
|
agent_name = event.get("agent") or (event.get("payload") or {}).get("subagent_type")
|
|
178
303
|
if not agent_name:
|
|
179
304
|
return
|
|
305
|
+
payload = event.get("payload") or {}
|
|
180
306
|
sess.agents_active[agent_name] = AgentState(
|
|
181
307
|
name=agent_name,
|
|
182
308
|
started=event.get("ts") or _now(),
|
|
183
309
|
last_tool=event.get("tool"),
|
|
184
310
|
status="running",
|
|
311
|
+
model=str(payload.get("model") or ""),
|
|
185
312
|
)
|
|
186
313
|
|
|
187
314
|
def _on_agent_complete(self, sid: Optional[str], event: dict[str, Any]) -> None:
|
|
@@ -260,6 +387,33 @@ class HarnessState:
|
|
|
260
387
|
s.last_probe = event.get("ts")
|
|
261
388
|
self.cognee[store] = s
|
|
262
389
|
|
|
390
|
+
def _on_graphify(self, event: dict[str, Any]) -> None:
|
|
391
|
+
"""Aggregate Graphify scan/query events into a single rollup.
|
|
392
|
+
|
|
393
|
+
We don't break this down per-project here — the TUI cards show the
|
|
394
|
+
most recent scan's totals + a query counter, which is what a single
|
|
395
|
+
rollup needs. The snapshot serializer flattens this back to a dict
|
|
396
|
+
so the existing TUI Knowledge tab can read it directly.
|
|
397
|
+
"""
|
|
398
|
+
p = event.get("payload") or {}
|
|
399
|
+
etype = event.get("type")
|
|
400
|
+
g = self.graphify
|
|
401
|
+
if etype == "graphify_scan":
|
|
402
|
+
if p.get("nodes") is not None:
|
|
403
|
+
g.nodes = int(p["nodes"])
|
|
404
|
+
if p.get("edges") is not None:
|
|
405
|
+
g.edges = int(p["edges"])
|
|
406
|
+
if p.get("communities") is not None:
|
|
407
|
+
g.communities = int(p["communities"])
|
|
408
|
+
g.scans += 1
|
|
409
|
+
g.last_scan_project = p.get("project")
|
|
410
|
+
g.last_scan_at = p.get("scanned_at") or event.get("ts")
|
|
411
|
+
elif etype == "graphify_query":
|
|
412
|
+
g.queries += 1
|
|
413
|
+
g.last_query_project = p.get("project")
|
|
414
|
+
g.last_query_at = event.get("ts")
|
|
415
|
+
g.last_query_text = p.get("question") or p.get("file")
|
|
416
|
+
|
|
263
417
|
|
|
264
418
|
def _dc_to_dict(dc) -> dict[str, Any]:
|
|
265
419
|
return {k: v for k, v in dc.__dict__.items()}
|
|
@@ -11,6 +11,7 @@ from mishkan_watchd.state import HarnessState
|
|
|
11
11
|
|
|
12
12
|
def test_agent_spawn_creates_session_and_agent():
|
|
13
13
|
s = HarnessState()
|
|
14
|
+
s.apply({"ts": "x", "session": "sess-1", "type": "session_start", "project": "/tmp/proj"})
|
|
14
15
|
s.apply({
|
|
15
16
|
"ts": "2026-06-05T12:00:00.000Z",
|
|
16
17
|
"session": "sess-1",
|
|
@@ -27,6 +28,7 @@ def test_agent_spawn_creates_session_and_agent():
|
|
|
27
28
|
|
|
28
29
|
def test_token_usage_accumulates_per_session():
|
|
29
30
|
s = HarnessState()
|
|
31
|
+
s.apply({"ts": "x", "session": "sess-tok", "type": "session_start", "project": "/p"})
|
|
30
32
|
for tin, tout in [(100, 20), (200, 40), (50, 10)]:
|
|
31
33
|
s.apply({
|
|
32
34
|
"ts": "2026-06-05T12:00:00.000Z",
|
|
@@ -51,6 +53,7 @@ def test_session_stop_removes_session():
|
|
|
51
53
|
def test_snapshot_serializes_cleanly():
|
|
52
54
|
import json
|
|
53
55
|
s = HarnessState()
|
|
56
|
+
s.apply({"ts": "x", "session": "snap", "type": "session_start", "project": "/p"})
|
|
54
57
|
s.apply({
|
|
55
58
|
"ts": "x", "session": "snap", "project": "/p",
|
|
56
59
|
"type": "agent_spawn", "tool": "Task", "agent": "caleb",
|
|
@@ -63,8 +66,9 @@ def test_snapshot_serializes_cleanly():
|
|
|
63
66
|
|
|
64
67
|
def test_unknown_event_type_is_ignored_gracefully():
|
|
65
68
|
s = HarnessState()
|
|
66
|
-
s.apply({"ts": "x", "session": "weird", "type": "
|
|
67
|
-
|
|
69
|
+
s.apply({"ts": "x", "session": "weird", "type": "session_start", "project": "/p"})
|
|
70
|
+
s.apply({"ts": "y", "session": "weird", "type": "completely_unknown_type"})
|
|
71
|
+
# Session exists from session_start; unknown type triggers no exception.
|
|
68
72
|
assert "weird" in s.sessions
|
|
69
73
|
|
|
70
74
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generated_from": "docs/design/MISHKAN_agent_aliases.md",
|
|
3
|
+
"schema_version": 1,
|
|
4
|
+
"groups": [
|
|
5
|
+
{
|
|
6
|
+
"id": "orchestration",
|
|
7
|
+
"label": "Orchestration",
|
|
8
|
+
"domain": null,
|
|
9
|
+
"mission": "Routes every request, owns scope, owns technical standards. The two interface layers of MISHKAN — PM (Nehemiah) talks to Y4NN, CTO (Bezalel) gates technical quality.",
|
|
10
|
+
"charter": "No specialist work happens without scope (Nehemiah) and technical fit (Bezalel) approved first.",
|
|
11
|
+
"agents": [
|
|
12
|
+
{"alias": "nehemiah", "role": "project_manager", "short": "PM", "description": "Scope, delivery, sprint state, user interface in exploration mode.", "source": "Book of Nehemiah"},
|
|
13
|
+
{"alias": "bezalel", "role": "cto", "short": "CTO", "description": "Technical standards, architecture, quality bar.", "source": "Exodus 31:2"}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "research",
|
|
18
|
+
"label": "Research Pipeline",
|
|
19
|
+
"domain": null,
|
|
20
|
+
"mission": "Shared research pipeline reachable by any agent facing an unknown. Six stages: clarify → formulate → research → summarise → evaluate → report.",
|
|
21
|
+
"charter": "No fabricated facts. If it's unknown, the pipeline runs. The report carries verifiable sources or marks the unknown explicitly.",
|
|
22
|
+
"agents": [
|
|
23
|
+
{"alias": "jakin", "role": "intent_clarificator", "short": "intent", "description": "Establishes the threshold — clarifies what the question really is before research starts.", "source": "1 Kings 7:21"},
|
|
24
|
+
{"alias": "ezra", "role": "research_details_formulator", "short": "formulate", "description": "Structures the research plan with precision.", "source": "Ezra 7:6"},
|
|
25
|
+
{"alias": "caleb", "role": "contextual_web_researcher", "short": "web", "description": "Goes wide and returns a faithful, fearless report.", "source": "Numbers 13:30"},
|
|
26
|
+
{"alias": "shaphan", "role": "contextual_research_summarizer", "short": "summarize", "description": "Compresses Caleb's findings into actionable summary.", "source": "2 Kings 22:3-10"},
|
|
27
|
+
{"alias": "shemaiah", "role": "contextual_research_results_evaluator", "short": "evaluate", "description": "Discerns true signal from false in the research output.", "source": "Nehemiah 6:10-13"},
|
|
28
|
+
{"alias": "baruch", "role": "research_reporter", "short": "report", "description": "Carries the final message back faithfully.", "source": "Jeremiah 36:4"}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "chosheb",
|
|
33
|
+
"label": "Chosheb",
|
|
34
|
+
"domain": "Design",
|
|
35
|
+
"hebrew": "חֹשֵׁב",
|
|
36
|
+
"hebrew_meaning": "Cunning work — deviser of artistic works (Exodus 31:4)",
|
|
37
|
+
"mission": "Designs the visible and felt experience before a line of frontend code is written. Owns UI, UX, design system tokens, accessibility intent.",
|
|
38
|
+
"charter": "Design handoff to Panim is complete or it doesn't ship. DS fit, a11y, asset manifest, QA matrix — all four lenses or the package is incomplete.",
|
|
39
|
+
"relationships": "Chosheb → Panim (design handoff · unidirectional)",
|
|
40
|
+
"agents": [
|
|
41
|
+
{"alias": "aholiab", "role": "chosheb_team_lead", "short": "lead", "description": "Bezalel's partner — leads design craftsmen, coordinates handoff.", "source": "Exodus 31:6"},
|
|
42
|
+
{"alias": "hiram", "role": "senior_ui_design_and_prototype_implementation", "short": "ui", "description": "Crafts the visible beautiful things — UI design and prototypes.", "source": "1 Kings 7:13-14"},
|
|
43
|
+
{"alias": "deborah", "role": "cognitive_and_emotional_ux_expert", "short": "ux", "description": "Sees what others miss — cognitive and emotional UX guidance.", "source": "Judges 4:4-5"},
|
|
44
|
+
{"alias": "elasah", "role": "chosheb_team_reporter", "short": "report", "description": "Carries the team's structured output faithfully.", "source": "Jeremiah 29:3"}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "panim",
|
|
49
|
+
"label": "Panim",
|
|
50
|
+
"domain": "Frontend",
|
|
51
|
+
"hebrew": "פָּנִים",
|
|
52
|
+
"hebrew_meaning": "Face · presence · what is turned toward (Exodus 33:14)",
|
|
53
|
+
"mission": "Implements every visible surface — components, pages, design system code, asset pipeline, a11y/SEO. Consumes Chosheb's handoff, contracts with Yasad's APIs.",
|
|
54
|
+
"charter": "What the user sees is correct, accessible, and consistent with the DS. Token changes propagate everywhere or nowhere — no half-rolled-out themes.",
|
|
55
|
+
"relationships": "Chosheb → Panim (design handoff) · Panim ↔ Yasad (API contracts)",
|
|
56
|
+
"agents": [
|
|
57
|
+
{"alias": "huram", "role": "panim_team_lead", "short": "lead", "description": "Leads all visible frontend works — cunning across stack.", "source": "2 Chronicles 2:13"},
|
|
58
|
+
{"alias": "oholiab", "role": "senior_frontend_engineer_frontend_design_system_expert", "short": "ds", "description": "Keeper of patterns and standards across components.", "source": "Exodus 35:34"},
|
|
59
|
+
{"alias": "salma", "role": "senior_frontend_developer", "short": "dev", "description": "Implements visible form — the builder of frontend code.", "source": "1 Chronicles 2:51"},
|
|
60
|
+
{"alias": "obed", "role": "smart_frontend_assets_feeder", "short": "assets", "description": "Faithful servant who supplies and sustains assets.", "source": "Ruth 4:17"},
|
|
61
|
+
{"alias": "asaph", "role": "seo_accessibility_expert", "short": "a11y", "description": "Makes the work heard and received by all — SEO + a11y.", "source": "1 Chronicles 16:5"},
|
|
62
|
+
{"alias": "jahaziel", "role": "frontend_qa_engineer", "short": "qa", "description": "Sees what others miss — frontend QA, evaluates without producing.", "source": "2 Chronicles 20:14"},
|
|
63
|
+
{"alias": "ahikam", "role": "panim_team_reporter", "short": "report", "description": "Carries the team's structured output faithfully.", "source": "2 Kings 22:12"}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "yasad",
|
|
68
|
+
"label": "Yasad",
|
|
69
|
+
"domain": "Backend",
|
|
70
|
+
"hebrew": "יָסַד",
|
|
71
|
+
"hebrew_meaning": "To lay the foundation · establish solidly (Psalm 24:2)",
|
|
72
|
+
"mission": "Lays and maintains the backend foundation — endpoints, contracts, data schema, persistence, business invariants. Contract-first.",
|
|
73
|
+
"charter": "Contracts before code. Migrations reviewed by 4 orthogonal lenses (contracts/perf/security/tests) before they ship.",
|
|
74
|
+
"relationships": "Panim ↔ Yasad (API contracts) · Yasad ↔ Mishmar (audit + remediation)",
|
|
75
|
+
"agents": [
|
|
76
|
+
{"alias": "zerubbabel", "role": "yasad_team_lead", "short": "lead", "description": "Governor who lays the deep base — leads backend foundation work.", "source": "Ezra 3:2"},
|
|
77
|
+
{"alias": "zadok", "role": "senior_software_engineer_design_system_master", "short": "patterns", "description": "Keeper of what must not change — backend patterns.", "source": "2 Samuel 8:17"},
|
|
78
|
+
{"alias": "nathan", "role": "senior_software_engineer_software_architecture_master", "short": "arch", "description": "Brings architectural vision — what should and should not be built.", "source": "2 Samuel 7:2"},
|
|
79
|
+
{"alias": "hizkiah", "role": "senior_backend_engineer_pure_backend_implementation", "short": "impl", "description": "Strength of Yah — does the direct backend implementation labour.", "source": "2 Chronicles 31:13"},
|
|
80
|
+
{"alias": "shallum", "role": "senior_backend_engineering_databases_expert", "short": "db", "description": "Keeper of what is stored — database design and migration.", "source": "2 Kings 15:10"},
|
|
81
|
+
{"alias": "uriah", "role": "backend_qa_engineer", "short": "qa", "description": "Absolute integrity — holds the line on backend standards.", "source": "2 Samuel 11"},
|
|
82
|
+
{"alias": "igal", "role": "yasad_team_reporter", "short": "report", "description": "Reports faithfully from his section.", "source": "Numbers 13:7"}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "mishmar",
|
|
87
|
+
"label": "Mishmar",
|
|
88
|
+
"domain": "Security",
|
|
89
|
+
"hebrew": "מִשְׁמָר",
|
|
90
|
+
"hebrew_meaning": "Guard · watch · the post set day and night (Nehemiah 4:9)",
|
|
91
|
+
"mission": "Cross-cutting security — code, surface, infrastructure, threat model. A constraint shaping every team's output from the start, not a team reviewing at the end.",
|
|
92
|
+
"charter": "Sensitive surface changes pass the 3-lens gate (code-OWASP / surface / threat-model) with adversarial refute before merge. Hardening overlay re-applied on every container recreate.",
|
|
93
|
+
"relationships": "Mishmar ↔ all teams (cross-cutting audit) · Mishmar → Migdal (security gates infra)",
|
|
94
|
+
"agents": [
|
|
95
|
+
{"alias": "phinehas", "role": "mishmar_team_lead", "short": "lead", "description": "Acted decisively to stop a breach — zealous for the boundary.", "source": "Numbers 25:7-8"},
|
|
96
|
+
{"alias": "benaiah", "role": "software_and_infrastructure_security_expert_devsecops", "short": "infra-sec", "description": "Mighty man who handles the hardest infrastructure threats.", "source": "2 Samuel 23:20"},
|
|
97
|
+
{"alias": "joab", "role": "web_mobile_desktop_security_expert", "short": "app-sec", "description": "Field general across all attack vectors — app surface security.", "source": "2 Samuel 8:16"},
|
|
98
|
+
{"alias": "ira", "role": "code_security_ops", "short": "code-sec", "description": "Keeps watch at the code level — security ops.", "source": "2 Samuel 20:26"},
|
|
99
|
+
{"alias": "hushai", "role": "software_security_advisor", "short": "advisor", "description": "Trusted advisor who outmanoeuvred the attacker — security counsel.", "source": "2 Samuel 15:37"},
|
|
100
|
+
{"alias": "maaseiah", "role": "mishmar_team_reporter", "short": "report", "description": "Carries the structured account faithfully.", "source": "Nehemiah 8:4"}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "migdal",
|
|
105
|
+
"label": "Migdal",
|
|
106
|
+
"domain": "Infrastructure",
|
|
107
|
+
"hebrew": "מִגְדָּל",
|
|
108
|
+
"hebrew_meaning": "Tower · load-bearing structures of the wall (Nehemiah 3:1)",
|
|
109
|
+
"mission": "Builds and operates the load-bearing infrastructure — systems, network, devops pipelines, observability, recovery. Gated by Mishmar at change time.",
|
|
110
|
+
"charter": "Infra change passes the 5-lens panel (design/systems/devops/observability/health) before apply. No `:latest` tags. Hardening overlay always re-applied. DR drilled quarterly.",
|
|
111
|
+
"relationships": "Mishmar → Migdal (security gates) · Migdal serves all teams' runtime",
|
|
112
|
+
"agents": [
|
|
113
|
+
{"alias": "eliashib", "role": "migdal_team_lead", "short": "lead", "description": "Organises the foundational infrastructure work.", "source": "Nehemiah 3:1"},
|
|
114
|
+
{"alias": "palal", "role": "systems_engineer_os_virtualisation_networks", "short": "systems", "description": "Works at the structural intersection — OS / virt / networks.", "source": "Nehemiah 3:25"},
|
|
115
|
+
{"alias": "meshullam", "role": "infrastructure_design_engineer", "short": "design", "description": "Designs connections between infrastructure parts.", "source": "Nehemiah 3:4"},
|
|
116
|
+
{"alias": "hanun", "role": "devsecops_practitioner_support_ops", "short": "devsecops", "description": "Long support stretch — devsecops practice, operational support.", "source": "Nehemiah 3:13"},
|
|
117
|
+
{"alias": "meremoth", "role": "devops_engineer", "short": "devops", "description": "Works at the delivery layer — pipelines, deploys.", "source": "Nehemiah 3:4"},
|
|
118
|
+
{"alias": "rehum", "role": "infrastructure_health_and_security_advisor", "short": "health", "description": "Watches for risk and advises — infrastructure health.", "source": "Nehemiah 3:17"},
|
|
119
|
+
{"alias": "zaccur", "role": "migdal_team_reporter", "short": "report", "description": "Keeps record, mindful of what happened.", "source": "Nehemiah 3:2"}
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "sefer",
|
|
124
|
+
"label": "Sefer",
|
|
125
|
+
"domain": "Documentation",
|
|
126
|
+
"hebrew": "סֵפֶר",
|
|
127
|
+
"hebrew_meaning": "Book · scroll · official written record (Nehemiah 8, Ezra 6)",
|
|
128
|
+
"mission": "Documentation across three layers — organisation (Seraiah), project (Joah), team (Shevna). Pull-based: pulls at milestones and on trigger events, not on every commit.",
|
|
129
|
+
"charter": "What is not written down is forgotten. Release notes assembled from git log per category. ADRs for every consequential decision. Style guide applied uniformly.",
|
|
130
|
+
"relationships": "Sefer ← all teams (pulls at milestones + trigger events)",
|
|
131
|
+
"agents": [
|
|
132
|
+
{"alias": "jehoshaphat", "role": "sefer_team_lead", "short": "lead", "description": "First Recorder in David's court — cares for national archives.", "source": "2 Samuel 8:16"},
|
|
133
|
+
{"alias": "seraiah", "role": "organisation_layer_specialist", "short": "org", "description": "Operates at the highest state level — org-wide documentation.", "source": "2 Samuel 8:17"},
|
|
134
|
+
{"alias": "joah", "role": "project_layer_specialist", "short": "project", "description": "Documents specific events and decisions of each project.", "source": "2 Kings 18:18"},
|
|
135
|
+
{"alias": "shevna", "role": "team_layer_specialist", "short": "team", "description": "Embedded with teams — documents their specific outputs.", "source": "2 Kings 18:18"},
|
|
136
|
+
{"alias": "jehonathan", "role": "knowledge_publication_specialist", "short": "publish", "description": "Takes knowledge and makes it legible for others.", "source": "1 Chronicles 27:32"},
|
|
137
|
+
{"alias": "huldah", "role": "sefer_team_reporter", "short": "report", "description": "Verifies, interprets, reports back with authority.", "source": "2 Kings 22:14"}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pre-commit-eval-gate.sh — run MISHKAN evals if their contract surface changed.
|
|
3
|
+
#
|
|
4
|
+
# Optional install: symlink as your repo's .git/hooks/pre-commit so the gate
|
|
5
|
+
# fires before every commit. Detects whether the staged changes touch any
|
|
6
|
+
# eval's sentinel paths; runs only the affected eval(s).
|
|
7
|
+
#
|
|
8
|
+
# Install:
|
|
9
|
+
# ln -s "$(pwd)/payload/mishkan/scripts/pre-commit-eval-gate.sh" .git/hooks/pre-commit
|
|
10
|
+
# chmod +x .git/hooks/pre-commit
|
|
11
|
+
#
|
|
12
|
+
# Uninstall:
|
|
13
|
+
# rm .git/hooks/pre-commit
|
|
14
|
+
#
|
|
15
|
+
# Exit codes:
|
|
16
|
+
# 0 → no relevant changes OR all triggered evals passed
|
|
17
|
+
# 1 → at least one eval failed (commit blocked)
|
|
18
|
+
# 2 → an eval's environment is missing (jq, validator)
|
|
19
|
+
set -uo pipefail
|
|
20
|
+
|
|
21
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
|
|
22
|
+
cd "$REPO_ROOT"
|
|
23
|
+
|
|
24
|
+
changed=$(git diff --cached --name-only --diff-filter=ACMR)
|
|
25
|
+
[[ -z "$changed" ]] && exit 0
|
|
26
|
+
|
|
27
|
+
trigger_baruch=false
|
|
28
|
+
while IFS= read -r f; do
|
|
29
|
+
case "$f" in
|
|
30
|
+
payload/mishkan/templates/research-log.schema.json) trigger_baruch=true ;;
|
|
31
|
+
payload/mishkan/scripts/validate-research-log.sh) trigger_baruch=true ;;
|
|
32
|
+
payload/mishkan/agents/baruch.md) trigger_baruch=true ;;
|
|
33
|
+
payload/mishkan/skills/baruch-research-reporting-craft/*) trigger_baruch=true ;;
|
|
34
|
+
payload/mishkan/evals/baruch/*) trigger_baruch=true ;;
|
|
35
|
+
esac
|
|
36
|
+
done <<< "$changed"
|
|
37
|
+
|
|
38
|
+
overall=0
|
|
39
|
+
if [[ "$trigger_baruch" == "true" ]]; then
|
|
40
|
+
echo "→ Baruch eval (contract surface changed)"
|
|
41
|
+
if bash payload/mishkan/evals/baruch/run.sh; then
|
|
42
|
+
echo " ✓ Baruch eval passed"
|
|
43
|
+
else
|
|
44
|
+
rc=$?
|
|
45
|
+
echo " ✗ Baruch eval failed (exit $rc)" >&2
|
|
46
|
+
overall=$rc
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
exit "$overall"
|