create-egregore 0.21.0-runtime-mvp.15 → 0.21.0-runtime-mvp.17

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // egregore-launcher-version: 0.21.0-runtime-mvp.15
2
+ // egregore-launcher-version: 0.21.0-runtime-mvp.17
3
3
  //
4
4
  // egregore — launch and browse Egregore instances from your terminal.
5
5
  // Installed by create-egregore. Reads ~/.egregore/instances.json.
@@ -35,7 +35,7 @@ const crypto = require("node:crypto");
35
35
  const { spawnSync, spawn } = require("node:child_process");
36
36
  const readline = require("node:readline");
37
37
 
38
- const VERSION = "0.21.0-runtime-mvp.15";
38
+ const VERSION = "0.21.0-runtime-mvp.17";
39
39
  const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
40
40
  const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
41
41
  const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.21.0-runtime-mvp.15",
3
+ "version": "0.21.0-runtime-mvp.17",
4
4
  "description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code, Codex, or Pi.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -228,19 +228,21 @@ For each user-directed organizational lookup, show exactly one visible line:
228
228
  - retrieval that actually queries the connected graph:
229
229
  `⌕ Egregore Connect · searching your organization’s memory and relationships`
230
230
 
231
- Runtime owns retrieval; no agent feature flag does. `EGREGORE_ORG_CONTEXT_V1`
232
- is only the schema label. When attached, authorized retrieval is complete:
231
+ When `EGREGORE_ORG_CONTEXT_V1` is attached, authorized retrieval is complete:
233
232
  never query again that turn, and state any evidence gap. `OPENED_BY_OBSERVE`
234
233
  means the evidence is open. A later explicit search starts a new turn.
235
234
 
236
- **Visibility is the contract.** Emit it verbatim as a
237
- standalone assistant message before the first Bash, Grep, Glob, Read, or other
238
- retrieval tool call.
239
- Shell/tool output does not satisfy this requirement. Do not paraphrase the line.
235
+ **Scope.** Egregore Runtime is for organizational recall only: team memory,
236
+ decisions, handoffs, prior work. Code, files, Git, and tests use the harness's
237
+ normal tools, with no beat and no Runtime call.
240
238
 
241
- Use the memory form normally; use Connect only for an explicit graph traversal
242
- that actually runs. Emit once per episode. A `bin/search.sh` banner is only a
243
- shell fallback and does not replace this visible beat.
239
+ **Visibility is the contract.** Emit it verbatim as a standalone assistant
240
+ message before the first tool call that performs the organizational recall
241
+ (the `bin/search.sh` query or a graph read). Tool output does not satisfy it.
242
+ Do not paraphrase the line.
243
+
244
+ Use Connect only for a graph traversal that actually runs. Emit once per
245
+ episode.
244
246
 
245
247
  **Routing:** Organizational recall always enters Egregore Runtime. The model is
246
248
  the semantic intent authority, deciding from meaning rather than fixed phrases.
@@ -251,20 +253,18 @@ Without sufficient attached context, make exactly one Runtime/QMD call:
251
253
  bash bin/search.sh query "<concept>" -n 6 --compact --context-packet
252
254
  ```
253
255
 
254
- `--context-packet` keeps the evidence out of the visible transcript and
255
- attaches it privately; the visible output is the attribution line alone.
256
- Never re-print ranked results, file/rank/excerpt metadata, or raw JSON.
256
+ `--context-packet` attaches the evidence privately; the visible output is the
257
+ attribution line alone. Never re-print ranked results or raw JSON.
257
258
 
258
259
  Do not resolve `memory/` to its sibling repo or start with raw `grep`/`ls`.
259
- Search owns lexical/semantic selection and attaches no graph by default. Open a
260
- source only when its excerpt cannot support a necessary claim. For current
260
+ Search attaches no graph by default. Open a source only when its excerpt
261
+ cannot support a necessary claim. For current
261
262
  team/person synthesis, retain known organization/member names and choose
262
- `--recent-days` plus `--min-evidence`. Use native
263
- Grep/Glob for code and exact errors. `/activity`, `/dashboard`, and `/project`
263
+ `--recent-days` plus `--min-evidence`. `/activity`, `/dashboard`, and `/project`
264
264
  display bounded status surfaces only when named or directly requested as a
265
- card. Questions asking for an explained synthesis of current organizational
266
- work are Runtime/QMD recall, as is prior-work discovery. Do not run full
267
- hybrid retrieval on unrelated prompts or infer graph from recall intent.
265
+ card. Explained syntheses of current organizational work and prior-work
266
+ discovery are Runtime/QMD recall. Do not run retrieval on unrelated prompts or
267
+ infer graph from recall intent.
268
268
 
269
269
  Do not emit the beat for code search, Git inspection, startup hydration, graph
270
270
  writes, ingestion, background sync, maintenance, or internal queries.
@@ -157,9 +157,8 @@ const NON_SKILL_TOKENS = new Set(['loop', 'fast', 'config', 'clear', 'help']);
157
157
  // hook delivers there. Rewrite the fallback command to the stdout form and
158
158
  // state the same hygiene contract in shell terms: search output is
159
159
  // model-internal; the visible reply carries only the retrieval line.
160
- const CLAUDE_PACKET_NOTE = `\`--context-packet\` keeps the evidence out of the visible transcript and
161
- attaches it privately; the visible output is the attribution line alone.
162
- Never re-print ranked results, file/rank/excerpt metadata, or raw JSON.`;
160
+ const CLAUDE_PACKET_NOTE = `\`--context-packet\` attaches the evidence privately; the visible output is the
161
+ attribution line alone. Never re-print ranked results or raw JSON.`;
163
162
  const SHELL_PACKET_NOTE = `Search output is model-internal: show only the retrieval line and receipts —
164
163
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.`;
165
164
 
@@ -38,7 +38,7 @@ from .runtime import EgregoreRuntime, local_runtime
38
38
  _ORG_CONTEXT_PREFETCH_PATTERN = re.compile(
39
39
  "|".join(
40
40
  (
41
- r"\b(find|locate|dig up|look up|pull up)\b.*\b"
41
+ r"\b(find|locate|dig up|look up|pull up)\b[^\n]*\b"
42
42
  r"(handoff|decision|notes?|docs?|meeting|harvest|quest|reflection)s?\b",
43
43
  r"\bdid we (decide|discuss|agree|say|talk about|cover)\b",
44
44
  r"\bdo we have (any )?(notes?|docs?|anything|something)( on| about)?\b",
@@ -161,6 +161,26 @@ def _clear_observe_receipt(root: Path, harness: str) -> None:
161
161
  pass
162
162
 
163
163
 
164
+ def _actor_contract_marker_path(
165
+ root: Path, harness: str, session: str | None = None
166
+ ) -> Path:
167
+ """Gitignored per-session marker: the full actor/retrieval contract was
168
+ already attached once. Later prompts receive a short reminder instead of
169
+ the ~4 KB block. bin/pre-compact.sh removes the marker so the first prompt
170
+ after a compaction re-attaches the full contract.
171
+
172
+ ``session`` is the harness's own session id from the hook payload. The
173
+ Egregore session file in the checkout is shared by every concurrent
174
+ session there and rewritten by each new one, so keying on it alone made
175
+ parallel sessions re-attach the full contract on every prompt."""
176
+
177
+ session = re.sub(r"[^A-Za-z0-9_.-]", "-", session or _session_id(root))[:120]
178
+ return (
179
+ root / ".egregore" / "runtime" / "observe"
180
+ / f"actor-contract-{harness}-{session}.sent"
181
+ )
182
+
183
+
164
184
  def _write_observe_receipt(root: Path, harness: str, prompt: str, context) -> None:
165
185
  """Record that this prompt already received useful authorized evidence.
166
186
 
@@ -378,6 +398,37 @@ def _query(args: argparse.Namespace) -> int:
378
398
  return 0
379
399
 
380
400
 
401
+ _PASTED_OUTPUT_LINE = re.compile(
402
+ r"^(?:"
403
+ r"[ MADRCU?!]{2}\s+\S" # git status --short / porcelain rows
404
+ r"|[▶❯»]\s" # terminal prompt markers
405
+ r"|\$\s\S" # shell prompt
406
+ r"|(?:\+\+\+|---|@@)\s" # unified diff
407
+ r"|diff --git\b"
408
+ r"|Traceback \(most recent call last\)"
409
+ r"|\s+File \"[^\"]+\", line \d+"
410
+ r"|\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}" # log timestamps
411
+ r"|[A-Za-z0-9_./-]+/[A-Za-z0-9_.-]+\.[a-z]{1,6}$" # a bare path
412
+ r")"
413
+ )
414
+
415
+
416
+ def _looks_like_pasted_output(prompt: str) -> bool:
417
+ """True for terminal output, diffs, logs, and code blocks pasted into a prompt.
418
+
419
+ Those carry words like "handoff", "notes", or "docs" by accident; running a
420
+ retrieval on them attaches unrelated evidence and costs seconds. The actor
421
+ context still tells the model to recall once if the prompt truly needs it.
422
+ """
423
+
424
+ if "```" in prompt:
425
+ return True
426
+ lines = [line for line in prompt.splitlines() if line.strip()]
427
+ if len(lines) >= 8:
428
+ return True
429
+ return sum(1 for line in lines if _PASTED_OUTPUT_LINE.match(line)) >= 2
430
+
431
+
381
432
  def prompt_prefetches_org_context(prompt: str) -> bool:
382
433
  """Return whether Observe should prefetch likely organizational context.
383
434
 
@@ -390,6 +441,7 @@ def prompt_prefetches_org_context(prompt: str) -> bool:
390
441
  return bool(
391
442
  prompt
392
443
  and not _EXPLICIT_SEARCH.search(prompt)
444
+ and not _looks_like_pasted_output(prompt)
393
445
  and (
394
446
  _ORG_CONTEXT_PREFETCH_PATTERN.search(prompt)
395
447
  or _ORG_RITUAL_CONTEXT_PREFETCH_PATTERN.search(prompt)
@@ -414,8 +466,22 @@ def prompt_requests_actor_context(prompt: str) -> bool:
414
466
  return bool(prompt and prompt.strip())
415
467
 
416
468
 
417
- def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) -> str:
418
- """Render the minimum stable identity needed by harness rituals."""
469
+ _RECALL_ONLY_SCOPE = (
470
+ "Egregore Runtime is for organizational recall and memory only: team memory, "
471
+ "decisions, handoffs, meetings, prior work. Code search, file reads, Git, tests, "
472
+ "and every other task use the harness's normal tools (Grep, Glob, Read, Bash) "
473
+ "with no retrieval ritual and no Runtime call."
474
+ )
475
+
476
+
477
+ def _actor_context_block(
478
+ runtime: EgregoreRuntime, root: Path, harness: str, *, full: bool = True
479
+ ) -> str:
480
+ """Render the stable identity and the recall contract for harness rituals.
481
+
482
+ ``full`` renders the complete contract (once per session); otherwise a
483
+ short reminder that keeps identity and the recall-only scope in view.
484
+ """
419
485
 
420
486
  actor_context = _actor(runtime, root, harness)
421
487
  actor = actor_context.actor
@@ -435,6 +501,22 @@ def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) ->
435
501
  search_command = 'bash bin/search.sh query "<concept>" -n 6 --compact'
436
502
  if harness == "claude":
437
503
  search_command += " --context-packet"
504
+ if not full:
505
+ return "\n".join(
506
+ (
507
+ "EGREGORE_ACTOR_CONTEXT_V1",
508
+ "Reminder — the full contract was attached earlier in this session.",
509
+ f"Actor: {actor.display_name} · Actor ID: {actor.actor_id} · "
510
+ f"Organization ID: {actor_context.profile.org_id} · Runtime root: {root}",
511
+ _RECALL_ONLY_SCOPE,
512
+ "When a prompt needs that recall and no EGREGORE_ORG_CONTEXT_V1 block is "
513
+ f"attached, run exactly one `{search_command}`; exact latest/oldest/open "
514
+ "handoff questions use `bash bin/search.sh handoffs …` instead.",
515
+ "`me` and `myself` mean this Actor ID and display name; never infer identity "
516
+ "from the OS user, Git, or GitHub names.",
517
+ "END_EGREGORE_ACTOR_CONTEXT_V1",
518
+ )
519
+ )
438
520
  return "\n".join(
439
521
  (
440
522
  "EGREGORE_ACTOR_CONTEXT_V1",
@@ -451,6 +533,7 @@ def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) ->
451
533
  f"Runtime root: {root}",
452
534
  "Run Egregore retrieval and workflow commands from this Runtime root. "
453
535
  "Do not fall back to a launch checkout named elsewhere in the session.",
536
+ _RECALL_ONLY_SCOPE,
454
537
  "Organizational retrieval status: NOT_PERFORMED. This actor-only block "
455
538
  "does not mean organizational recall is unnecessary.",
456
539
  "Interpret the user's intent semantically, without requiring trigger words. "
@@ -687,11 +770,21 @@ def _prompt_hook(args: argparse.Namespace) -> int:
687
770
  args.limit,
688
771
  )
689
772
  else:
773
+ hook_session = str(payload.get("session_id") or "").strip() or None
774
+ marker = _actor_contract_marker_path(root, args.harness, hook_session)
775
+ full = not marker.exists()
690
776
  additional_context = _actor_context_block(
691
777
  runtime,
692
778
  root,
693
779
  args.harness,
780
+ full=full,
694
781
  )
782
+ if full:
783
+ try:
784
+ marker.parent.mkdir(parents=True, exist_ok=True)
785
+ marker.write_text(datetime.now(UTC).isoformat(), encoding="utf-8")
786
+ except OSError:
787
+ pass
695
788
  except Exception as exc: # Hooks are advisory and must never block a prompt.
696
789
  additional_context = (
697
790
  "Egregore Runtime context was requested but the Local Runtime is unavailable "
@@ -53,7 +53,7 @@
53
53
  {
54
54
  "section": "Egregore Retrieval Beat",
55
55
  "action": "keep",
56
- "sourceHash": "6c5cbb0d1c9a",
56
+ "sourceHash": "bb2f214689cb",
57
57
  "note": "Kept verbatim — product attribution applies across runtimes."
58
58
  },
59
59
  {
@@ -258,19 +258,21 @@ For each user-directed organizational lookup, show exactly one visible line:
258
258
  - retrieval that actually queries the connected graph:
259
259
  `⌕ Egregore Connect · searching your organization’s memory and relationships`
260
260
 
261
- Runtime owns retrieval; no agent feature flag does. `EGREGORE_ORG_CONTEXT_V1`
262
- is only the schema label. When attached, authorized retrieval is complete:
261
+ When `EGREGORE_ORG_CONTEXT_V1` is attached, authorized retrieval is complete:
263
262
  never query again that turn, and state any evidence gap. `OPENED_BY_OBSERVE`
264
263
  means the evidence is open. A later explicit search starts a new turn.
265
264
 
266
- **Visibility is the contract.** Emit it verbatim as a
267
- standalone assistant message before the first Bash, Grep, Glob, Read, or other
268
- retrieval tool call.
269
- Shell/tool output does not satisfy this requirement. Do not paraphrase the line.
265
+ **Scope.** Egregore Runtime is for organizational recall only: team memory,
266
+ decisions, handoffs, prior work. Code, files, Git, and tests use the harness's
267
+ normal tools, with no beat and no Runtime call.
270
268
 
271
- Use the memory form normally; use Connect only for an explicit graph traversal
272
- that actually runs. Emit once per episode. A `bin/search.sh` banner is only a
273
- shell fallback and does not replace this visible beat.
269
+ **Visibility is the contract.** Emit it verbatim as a standalone assistant
270
+ message before the first tool call that performs the organizational recall
271
+ (the `bin/search.sh` query or a graph read). Tool output does not satisfy it.
272
+ Do not paraphrase the line.
273
+
274
+ Use Connect only for a graph traversal that actually runs. Emit once per
275
+ episode.
274
276
 
275
277
  **Routing:** Organizational recall always enters Egregore Runtime. The model is
276
278
  the semantic intent authority, deciding from meaning rather than fixed phrases.
@@ -285,15 +287,14 @@ Search output is model-internal: show only the retrieval line and receipts —
285
287
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.
286
288
 
287
289
  Do not resolve `memory/` to its sibling repo or start with raw `grep`/`ls`.
288
- Search owns lexical/semantic selection and attaches no graph by default. Open a
289
- source only when its excerpt cannot support a necessary claim. For current
290
+ Search attaches no graph by default. Open a source only when its excerpt
291
+ cannot support a necessary claim. For current
290
292
  team/person synthesis, retain known organization/member names and choose
291
- `--recent-days` plus `--min-evidence`. Use native
292
- Grep/Glob for code and exact errors. `$activity`, `$dashboard`, and `$project`
293
+ `--recent-days` plus `--min-evidence`. `$activity`, `$dashboard`, and `$project`
293
294
  display bounded status surfaces only when named or directly requested as a
294
- card. Questions asking for an explained synthesis of current organizational
295
- work are Runtime/QMD recall, as is prior-work discovery. Do not run full
296
- hybrid retrieval on unrelated prompts or infer graph from recall intent.
295
+ card. Explained syntheses of current organizational work and prior-work
296
+ discovery are Runtime/QMD recall. Do not run retrieval on unrelated prompts or
297
+ infer graph from recall intent.
297
298
 
298
299
  Do not emit the beat for code search, Git inspection, startup hydration, graph
299
300
  writes, ingestion, background sync, maintenance, or internal queries.
@@ -157,9 +157,8 @@ const NON_SKILL_TOKENS = new Set(['loop', 'fast', 'config', 'clear', 'help']);
157
157
  // hook delivers there. Rewrite the fallback command to the stdout form and
158
158
  // state the same hygiene contract in shell terms: search output is
159
159
  // model-internal; the visible reply carries only the retrieval line.
160
- const CLAUDE_PACKET_NOTE = `\`--context-packet\` keeps the evidence out of the visible transcript and
161
- attaches it privately; the visible output is the attribution line alone.
162
- Never re-print ranked results, file/rank/excerpt metadata, or raw JSON.`;
160
+ const CLAUDE_PACKET_NOTE = `\`--context-packet\` attaches the evidence privately; the visible output is the
161
+ attribution line alone. Never re-print ranked results or raw JSON.`;
163
162
  const SHELL_PACKET_NOTE = `Search output is model-internal: show only the retrieval line and receipts —
164
163
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.`;
165
164
 
@@ -38,7 +38,7 @@ from .runtime import EgregoreRuntime, local_runtime
38
38
  _ORG_CONTEXT_PREFETCH_PATTERN = re.compile(
39
39
  "|".join(
40
40
  (
41
- r"\b(find|locate|dig up|look up|pull up)\b.*\b"
41
+ r"\b(find|locate|dig up|look up|pull up)\b[^\n]*\b"
42
42
  r"(handoff|decision|notes?|docs?|meeting|harvest|quest|reflection)s?\b",
43
43
  r"\bdid we (decide|discuss|agree|say|talk about|cover)\b",
44
44
  r"\bdo we have (any )?(notes?|docs?|anything|something)( on| about)?\b",
@@ -161,6 +161,26 @@ def _clear_observe_receipt(root: Path, harness: str) -> None:
161
161
  pass
162
162
 
163
163
 
164
+ def _actor_contract_marker_path(
165
+ root: Path, harness: str, session: str | None = None
166
+ ) -> Path:
167
+ """Gitignored per-session marker: the full actor/retrieval contract was
168
+ already attached once. Later prompts receive a short reminder instead of
169
+ the ~4 KB block. bin/pre-compact.sh removes the marker so the first prompt
170
+ after a compaction re-attaches the full contract.
171
+
172
+ ``session`` is the harness's own session id from the hook payload. The
173
+ Egregore session file in the checkout is shared by every concurrent
174
+ session there and rewritten by each new one, so keying on it alone made
175
+ parallel sessions re-attach the full contract on every prompt."""
176
+
177
+ session = re.sub(r"[^A-Za-z0-9_.-]", "-", session or _session_id(root))[:120]
178
+ return (
179
+ root / ".egregore" / "runtime" / "observe"
180
+ / f"actor-contract-{harness}-{session}.sent"
181
+ )
182
+
183
+
164
184
  def _write_observe_receipt(root: Path, harness: str, prompt: str, context) -> None:
165
185
  """Record that this prompt already received useful authorized evidence.
166
186
 
@@ -378,6 +398,37 @@ def _query(args: argparse.Namespace) -> int:
378
398
  return 0
379
399
 
380
400
 
401
+ _PASTED_OUTPUT_LINE = re.compile(
402
+ r"^(?:"
403
+ r"[ MADRCU?!]{2}\s+\S" # git status --short / porcelain rows
404
+ r"|[▶❯»]\s" # terminal prompt markers
405
+ r"|\$\s\S" # shell prompt
406
+ r"|(?:\+\+\+|---|@@)\s" # unified diff
407
+ r"|diff --git\b"
408
+ r"|Traceback \(most recent call last\)"
409
+ r"|\s+File \"[^\"]+\", line \d+"
410
+ r"|\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}" # log timestamps
411
+ r"|[A-Za-z0-9_./-]+/[A-Za-z0-9_.-]+\.[a-z]{1,6}$" # a bare path
412
+ r")"
413
+ )
414
+
415
+
416
+ def _looks_like_pasted_output(prompt: str) -> bool:
417
+ """True for terminal output, diffs, logs, and code blocks pasted into a prompt.
418
+
419
+ Those carry words like "handoff", "notes", or "docs" by accident; running a
420
+ retrieval on them attaches unrelated evidence and costs seconds. The actor
421
+ context still tells the model to recall once if the prompt truly needs it.
422
+ """
423
+
424
+ if "```" in prompt:
425
+ return True
426
+ lines = [line for line in prompt.splitlines() if line.strip()]
427
+ if len(lines) >= 8:
428
+ return True
429
+ return sum(1 for line in lines if _PASTED_OUTPUT_LINE.match(line)) >= 2
430
+
431
+
381
432
  def prompt_prefetches_org_context(prompt: str) -> bool:
382
433
  """Return whether Observe should prefetch likely organizational context.
383
434
 
@@ -390,6 +441,7 @@ def prompt_prefetches_org_context(prompt: str) -> bool:
390
441
  return bool(
391
442
  prompt
392
443
  and not _EXPLICIT_SEARCH.search(prompt)
444
+ and not _looks_like_pasted_output(prompt)
393
445
  and (
394
446
  _ORG_CONTEXT_PREFETCH_PATTERN.search(prompt)
395
447
  or _ORG_RITUAL_CONTEXT_PREFETCH_PATTERN.search(prompt)
@@ -414,8 +466,22 @@ def prompt_requests_actor_context(prompt: str) -> bool:
414
466
  return bool(prompt and prompt.strip())
415
467
 
416
468
 
417
- def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) -> str:
418
- """Render the minimum stable identity needed by harness rituals."""
469
+ _RECALL_ONLY_SCOPE = (
470
+ "Egregore Runtime is for organizational recall and memory only: team memory, "
471
+ "decisions, handoffs, meetings, prior work. Code search, file reads, Git, tests, "
472
+ "and every other task use the harness's normal tools (Grep, Glob, Read, Bash) "
473
+ "with no retrieval ritual and no Runtime call."
474
+ )
475
+
476
+
477
+ def _actor_context_block(
478
+ runtime: EgregoreRuntime, root: Path, harness: str, *, full: bool = True
479
+ ) -> str:
480
+ """Render the stable identity and the recall contract for harness rituals.
481
+
482
+ ``full`` renders the complete contract (once per session); otherwise a
483
+ short reminder that keeps identity and the recall-only scope in view.
484
+ """
419
485
 
420
486
  actor_context = _actor(runtime, root, harness)
421
487
  actor = actor_context.actor
@@ -435,6 +501,22 @@ def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) ->
435
501
  search_command = 'bash bin/search.sh query "<concept>" -n 6 --compact'
436
502
  if harness == "claude":
437
503
  search_command += " --context-packet"
504
+ if not full:
505
+ return "\n".join(
506
+ (
507
+ "EGREGORE_ACTOR_CONTEXT_V1",
508
+ "Reminder — the full contract was attached earlier in this session.",
509
+ f"Actor: {actor.display_name} · Actor ID: {actor.actor_id} · "
510
+ f"Organization ID: {actor_context.profile.org_id} · Runtime root: {root}",
511
+ _RECALL_ONLY_SCOPE,
512
+ "When a prompt needs that recall and no EGREGORE_ORG_CONTEXT_V1 block is "
513
+ f"attached, run exactly one `{search_command}`; exact latest/oldest/open "
514
+ "handoff questions use `bash bin/search.sh handoffs …` instead.",
515
+ "`me` and `myself` mean this Actor ID and display name; never infer identity "
516
+ "from the OS user, Git, or GitHub names.",
517
+ "END_EGREGORE_ACTOR_CONTEXT_V1",
518
+ )
519
+ )
438
520
  return "\n".join(
439
521
  (
440
522
  "EGREGORE_ACTOR_CONTEXT_V1",
@@ -451,6 +533,7 @@ def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) ->
451
533
  f"Runtime root: {root}",
452
534
  "Run Egregore retrieval and workflow commands from this Runtime root. "
453
535
  "Do not fall back to a launch checkout named elsewhere in the session.",
536
+ _RECALL_ONLY_SCOPE,
454
537
  "Organizational retrieval status: NOT_PERFORMED. This actor-only block "
455
538
  "does not mean organizational recall is unnecessary.",
456
539
  "Interpret the user's intent semantically, without requiring trigger words. "
@@ -687,11 +770,21 @@ def _prompt_hook(args: argparse.Namespace) -> int:
687
770
  args.limit,
688
771
  )
689
772
  else:
773
+ hook_session = str(payload.get("session_id") or "").strip() or None
774
+ marker = _actor_contract_marker_path(root, args.harness, hook_session)
775
+ full = not marker.exists()
690
776
  additional_context = _actor_context_block(
691
777
  runtime,
692
778
  root,
693
779
  args.harness,
780
+ full=full,
694
781
  )
782
+ if full:
783
+ try:
784
+ marker.parent.mkdir(parents=True, exist_ok=True)
785
+ marker.write_text(datetime.now(UTC).isoformat(), encoding="utf-8")
786
+ except OSError:
787
+ pass
695
788
  except Exception as exc: # Hooks are advisory and must never block a prompt.
696
789
  additional_context = (
697
790
  "Egregore Runtime context was requested but the Local Runtime is unavailable "
@@ -53,7 +53,7 @@
53
53
  {
54
54
  "section": "Egregore Retrieval Beat",
55
55
  "action": "keep",
56
- "sourceHash": "6c5cbb0d1c9a",
56
+ "sourceHash": "bb2f214689cb",
57
57
  "note": "Kept verbatim — product attribution applies across runtimes."
58
58
  },
59
59
  {
@@ -183,19 +183,21 @@ For each user-directed organizational lookup, show exactly one visible line:
183
183
  - retrieval that actually queries the connected graph:
184
184
  `⌕ Egregore Connect · searching your organization’s memory and relationships`
185
185
 
186
- Runtime owns retrieval; no agent feature flag does. `EGREGORE_ORG_CONTEXT_V1`
187
- is only the schema label. When attached, authorized retrieval is complete:
186
+ When `EGREGORE_ORG_CONTEXT_V1` is attached, authorized retrieval is complete:
188
187
  never query again that turn, and state any evidence gap. `OPENED_BY_OBSERVE`
189
188
  means the evidence is open. A later explicit search starts a new turn.
190
189
 
191
- **Visibility is the contract.** Emit it verbatim as a
192
- standalone assistant message before the first Bash, Grep, Glob, Read, or other
193
- retrieval tool call.
194
- Shell/tool output does not satisfy this requirement. Do not paraphrase the line.
190
+ **Scope.** Egregore Runtime is for organizational recall only: team memory,
191
+ decisions, handoffs, prior work. Code, files, Git, and tests use the harness's
192
+ normal tools, with no beat and no Runtime call.
195
193
 
196
- Use the memory form normally; use Connect only for an explicit graph traversal
197
- that actually runs. Emit once per episode. A `bin/search.sh` banner is only a
198
- shell fallback and does not replace this visible beat.
194
+ **Visibility is the contract.** Emit it verbatim as a standalone assistant
195
+ message before the first tool call that performs the organizational recall
196
+ (the `bin/search.sh` query or a graph read). Tool output does not satisfy it.
197
+ Do not paraphrase the line.
198
+
199
+ Use Connect only for a graph traversal that actually runs. Emit once per
200
+ episode.
199
201
 
200
202
  **Routing:** Organizational recall always enters Egregore Runtime. The model is
201
203
  the semantic intent authority, deciding from meaning rather than fixed phrases.
@@ -210,15 +212,14 @@ Search output is model-internal: show only the retrieval line and receipts —
210
212
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.
211
213
 
212
214
  Do not resolve `memory/` to its sibling repo or start with raw `grep`/`ls`.
213
- Search owns lexical/semantic selection and attaches no graph by default. Open a
214
- source only when its excerpt cannot support a necessary claim. For current
215
+ Search attaches no graph by default. Open a source only when its excerpt
216
+ cannot support a necessary claim. For current
215
217
  team/person synthesis, retain known organization/member names and choose
216
- `--recent-days` plus `--min-evidence`. Use native
217
- Grep/Glob for code and exact errors. `/activity`, `/dashboard`, and `/project`
218
+ `--recent-days` plus `--min-evidence`. `/activity`, `/dashboard`, and `/project`
218
219
  display bounded status surfaces only when named or directly requested as a
219
- card. Questions asking for an explained synthesis of current organizational
220
- work are Runtime/QMD recall, as is prior-work discovery. Do not run full
221
- hybrid retrieval on unrelated prompts or infer graph from recall intent.
220
+ card. Explained syntheses of current organizational work and prior-work
221
+ discovery are Runtime/QMD recall. Do not run retrieval on unrelated prompts or
222
+ infer graph from recall intent.
222
223
 
223
224
  Do not emit the beat for code search, Git inspection, startup hydration, graph
224
225
  writes, ingestion, background sync, maintenance, or internal queries.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "generatedBy": "bin/pi-render-spec.mjs",
3
3
  "source": "AGENTS.md generated Codex spec",
4
- "sourceHash": "76c76679f791",
5
- "outputHash": "cf5aa8ffd739",
4
+ "sourceHash": "496480654dd9",
5
+ "outputHash": "6eef1a6daeb9",
6
6
  "adaptations": [
7
7
  "Pi project trust and APPEND_SYSTEM delivery",
8
8
  "Pi tool_call branch gate",
@@ -258,19 +258,21 @@ For each user-directed organizational lookup, show exactly one visible line:
258
258
  - retrieval that actually queries the connected graph:
259
259
  `⌕ Egregore Connect · searching your organization’s memory and relationships`
260
260
 
261
- Runtime owns retrieval; no agent feature flag does. `EGREGORE_ORG_CONTEXT_V1`
262
- is only the schema label. When attached, authorized retrieval is complete:
261
+ When `EGREGORE_ORG_CONTEXT_V1` is attached, authorized retrieval is complete:
263
262
  never query again that turn, and state any evidence gap. `OPENED_BY_OBSERVE`
264
263
  means the evidence is open. A later explicit search starts a new turn.
265
264
 
266
- **Visibility is the contract.** Emit it verbatim as a
267
- standalone assistant message before the first Bash, Grep, Glob, Read, or other
268
- retrieval tool call.
269
- Shell/tool output does not satisfy this requirement. Do not paraphrase the line.
265
+ **Scope.** Egregore Runtime is for organizational recall only: team memory,
266
+ decisions, handoffs, prior work. Code, files, Git, and tests use the harness's
267
+ normal tools, with no beat and no Runtime call.
270
268
 
271
- Use the memory form normally; use Connect only for an explicit graph traversal
272
- that actually runs. Emit once per episode. A `bin/search.sh` banner is only a
273
- shell fallback and does not replace this visible beat.
269
+ **Visibility is the contract.** Emit it verbatim as a standalone assistant
270
+ message before the first tool call that performs the organizational recall
271
+ (the `bin/search.sh` query or a graph read). Tool output does not satisfy it.
272
+ Do not paraphrase the line.
273
+
274
+ Use Connect only for a graph traversal that actually runs. Emit once per
275
+ episode.
274
276
 
275
277
  **Routing:** Organizational recall always enters Egregore Runtime. The model is
276
278
  the semantic intent authority, deciding from meaning rather than fixed phrases.
@@ -285,15 +287,14 @@ Search output is model-internal: show only the retrieval line and receipts —
285
287
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.
286
288
 
287
289
  Do not resolve `memory/` to its sibling repo or start with raw `grep`/`ls`.
288
- Search owns lexical/semantic selection and attaches no graph by default. Open a
289
- source only when its excerpt cannot support a necessary claim. For current
290
+ Search attaches no graph by default. Open a source only when its excerpt
291
+ cannot support a necessary claim. For current
290
292
  team/person synthesis, retain known organization/member names and choose
291
- `--recent-days` plus `--min-evidence`. Use native
292
- Grep/Glob for code and exact errors. `$activity`, `$dashboard`, and `$project`
293
+ `--recent-days` plus `--min-evidence`. `$activity`, `$dashboard`, and `$project`
293
294
  display bounded status surfaces only when named or directly requested as a
294
- card. Questions asking for an explained synthesis of current organizational
295
- work are Runtime/QMD recall, as is prior-work discovery. Do not run full
296
- hybrid retrieval on unrelated prompts or infer graph from recall intent.
295
+ card. Explained syntheses of current organizational work and prior-work
296
+ discovery are Runtime/QMD recall. Do not run retrieval on unrelated prompts or
297
+ infer graph from recall intent.
297
298
 
298
299
  Do not emit the beat for code search, Git inspection, startup hydration, graph
299
300
  writes, ingestion, background sync, maintenance, or internal queries.
@@ -157,9 +157,8 @@ const NON_SKILL_TOKENS = new Set(['loop', 'fast', 'config', 'clear', 'help']);
157
157
  // hook delivers there. Rewrite the fallback command to the stdout form and
158
158
  // state the same hygiene contract in shell terms: search output is
159
159
  // model-internal; the visible reply carries only the retrieval line.
160
- const CLAUDE_PACKET_NOTE = `\`--context-packet\` keeps the evidence out of the visible transcript and
161
- attaches it privately; the visible output is the attribution line alone.
162
- Never re-print ranked results, file/rank/excerpt metadata, or raw JSON.`;
160
+ const CLAUDE_PACKET_NOTE = `\`--context-packet\` attaches the evidence privately; the visible output is the
161
+ attribution line alone. Never re-print ranked results or raw JSON.`;
163
162
  const SHELL_PACKET_NOTE = `Search output is model-internal: show only the retrieval line and receipts —
164
163
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.`;
165
164
 
@@ -38,7 +38,7 @@ from .runtime import EgregoreRuntime, local_runtime
38
38
  _ORG_CONTEXT_PREFETCH_PATTERN = re.compile(
39
39
  "|".join(
40
40
  (
41
- r"\b(find|locate|dig up|look up|pull up)\b.*\b"
41
+ r"\b(find|locate|dig up|look up|pull up)\b[^\n]*\b"
42
42
  r"(handoff|decision|notes?|docs?|meeting|harvest|quest|reflection)s?\b",
43
43
  r"\bdid we (decide|discuss|agree|say|talk about|cover)\b",
44
44
  r"\bdo we have (any )?(notes?|docs?|anything|something)( on| about)?\b",
@@ -161,6 +161,26 @@ def _clear_observe_receipt(root: Path, harness: str) -> None:
161
161
  pass
162
162
 
163
163
 
164
+ def _actor_contract_marker_path(
165
+ root: Path, harness: str, session: str | None = None
166
+ ) -> Path:
167
+ """Gitignored per-session marker: the full actor/retrieval contract was
168
+ already attached once. Later prompts receive a short reminder instead of
169
+ the ~4 KB block. bin/pre-compact.sh removes the marker so the first prompt
170
+ after a compaction re-attaches the full contract.
171
+
172
+ ``session`` is the harness's own session id from the hook payload. The
173
+ Egregore session file in the checkout is shared by every concurrent
174
+ session there and rewritten by each new one, so keying on it alone made
175
+ parallel sessions re-attach the full contract on every prompt."""
176
+
177
+ session = re.sub(r"[^A-Za-z0-9_.-]", "-", session or _session_id(root))[:120]
178
+ return (
179
+ root / ".egregore" / "runtime" / "observe"
180
+ / f"actor-contract-{harness}-{session}.sent"
181
+ )
182
+
183
+
164
184
  def _write_observe_receipt(root: Path, harness: str, prompt: str, context) -> None:
165
185
  """Record that this prompt already received useful authorized evidence.
166
186
 
@@ -378,6 +398,37 @@ def _query(args: argparse.Namespace) -> int:
378
398
  return 0
379
399
 
380
400
 
401
+ _PASTED_OUTPUT_LINE = re.compile(
402
+ r"^(?:"
403
+ r"[ MADRCU?!]{2}\s+\S" # git status --short / porcelain rows
404
+ r"|[▶❯»]\s" # terminal prompt markers
405
+ r"|\$\s\S" # shell prompt
406
+ r"|(?:\+\+\+|---|@@)\s" # unified diff
407
+ r"|diff --git\b"
408
+ r"|Traceback \(most recent call last\)"
409
+ r"|\s+File \"[^\"]+\", line \d+"
410
+ r"|\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}" # log timestamps
411
+ r"|[A-Za-z0-9_./-]+/[A-Za-z0-9_.-]+\.[a-z]{1,6}$" # a bare path
412
+ r")"
413
+ )
414
+
415
+
416
+ def _looks_like_pasted_output(prompt: str) -> bool:
417
+ """True for terminal output, diffs, logs, and code blocks pasted into a prompt.
418
+
419
+ Those carry words like "handoff", "notes", or "docs" by accident; running a
420
+ retrieval on them attaches unrelated evidence and costs seconds. The actor
421
+ context still tells the model to recall once if the prompt truly needs it.
422
+ """
423
+
424
+ if "```" in prompt:
425
+ return True
426
+ lines = [line for line in prompt.splitlines() if line.strip()]
427
+ if len(lines) >= 8:
428
+ return True
429
+ return sum(1 for line in lines if _PASTED_OUTPUT_LINE.match(line)) >= 2
430
+
431
+
381
432
  def prompt_prefetches_org_context(prompt: str) -> bool:
382
433
  """Return whether Observe should prefetch likely organizational context.
383
434
 
@@ -390,6 +441,7 @@ def prompt_prefetches_org_context(prompt: str) -> bool:
390
441
  return bool(
391
442
  prompt
392
443
  and not _EXPLICIT_SEARCH.search(prompt)
444
+ and not _looks_like_pasted_output(prompt)
393
445
  and (
394
446
  _ORG_CONTEXT_PREFETCH_PATTERN.search(prompt)
395
447
  or _ORG_RITUAL_CONTEXT_PREFETCH_PATTERN.search(prompt)
@@ -414,8 +466,22 @@ def prompt_requests_actor_context(prompt: str) -> bool:
414
466
  return bool(prompt and prompt.strip())
415
467
 
416
468
 
417
- def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) -> str:
418
- """Render the minimum stable identity needed by harness rituals."""
469
+ _RECALL_ONLY_SCOPE = (
470
+ "Egregore Runtime is for organizational recall and memory only: team memory, "
471
+ "decisions, handoffs, meetings, prior work. Code search, file reads, Git, tests, "
472
+ "and every other task use the harness's normal tools (Grep, Glob, Read, Bash) "
473
+ "with no retrieval ritual and no Runtime call."
474
+ )
475
+
476
+
477
+ def _actor_context_block(
478
+ runtime: EgregoreRuntime, root: Path, harness: str, *, full: bool = True
479
+ ) -> str:
480
+ """Render the stable identity and the recall contract for harness rituals.
481
+
482
+ ``full`` renders the complete contract (once per session); otherwise a
483
+ short reminder that keeps identity and the recall-only scope in view.
484
+ """
419
485
 
420
486
  actor_context = _actor(runtime, root, harness)
421
487
  actor = actor_context.actor
@@ -435,6 +501,22 @@ def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) ->
435
501
  search_command = 'bash bin/search.sh query "<concept>" -n 6 --compact'
436
502
  if harness == "claude":
437
503
  search_command += " --context-packet"
504
+ if not full:
505
+ return "\n".join(
506
+ (
507
+ "EGREGORE_ACTOR_CONTEXT_V1",
508
+ "Reminder — the full contract was attached earlier in this session.",
509
+ f"Actor: {actor.display_name} · Actor ID: {actor.actor_id} · "
510
+ f"Organization ID: {actor_context.profile.org_id} · Runtime root: {root}",
511
+ _RECALL_ONLY_SCOPE,
512
+ "When a prompt needs that recall and no EGREGORE_ORG_CONTEXT_V1 block is "
513
+ f"attached, run exactly one `{search_command}`; exact latest/oldest/open "
514
+ "handoff questions use `bash bin/search.sh handoffs …` instead.",
515
+ "`me` and `myself` mean this Actor ID and display name; never infer identity "
516
+ "from the OS user, Git, or GitHub names.",
517
+ "END_EGREGORE_ACTOR_CONTEXT_V1",
518
+ )
519
+ )
438
520
  return "\n".join(
439
521
  (
440
522
  "EGREGORE_ACTOR_CONTEXT_V1",
@@ -451,6 +533,7 @@ def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) ->
451
533
  f"Runtime root: {root}",
452
534
  "Run Egregore retrieval and workflow commands from this Runtime root. "
453
535
  "Do not fall back to a launch checkout named elsewhere in the session.",
536
+ _RECALL_ONLY_SCOPE,
454
537
  "Organizational retrieval status: NOT_PERFORMED. This actor-only block "
455
538
  "does not mean organizational recall is unnecessary.",
456
539
  "Interpret the user's intent semantically, without requiring trigger words. "
@@ -687,11 +770,21 @@ def _prompt_hook(args: argparse.Namespace) -> int:
687
770
  args.limit,
688
771
  )
689
772
  else:
773
+ hook_session = str(payload.get("session_id") or "").strip() or None
774
+ marker = _actor_contract_marker_path(root, args.harness, hook_session)
775
+ full = not marker.exists()
690
776
  additional_context = _actor_context_block(
691
777
  runtime,
692
778
  root,
693
779
  args.harness,
780
+ full=full,
694
781
  )
782
+ if full:
783
+ try:
784
+ marker.parent.mkdir(parents=True, exist_ok=True)
785
+ marker.write_text(datetime.now(UTC).isoformat(), encoding="utf-8")
786
+ except OSError:
787
+ pass
695
788
  except Exception as exc: # Hooks are advisory and must never block a prompt.
696
789
  additional_context = (
697
790
  "Egregore Runtime context was requested but the Local Runtime is unavailable "
@@ -53,7 +53,7 @@
53
53
  {
54
54
  "section": "Egregore Retrieval Beat",
55
55
  "action": "keep",
56
- "sourceHash": "6c5cbb0d1c9a",
56
+ "sourceHash": "bb2f214689cb",
57
57
  "note": "Kept verbatim — product attribution applies across runtimes."
58
58
  },
59
59
  {
@@ -185,19 +185,21 @@ For each user-directed organizational lookup, show exactly one visible line:
185
185
  - retrieval that actually queries the connected graph:
186
186
  `⌕ Egregore Connect · searching your organization’s memory and relationships`
187
187
 
188
- Runtime owns retrieval; no agent feature flag does. `EGREGORE_ORG_CONTEXT_V1`
189
- is only the schema label. When attached, authorized retrieval is complete:
188
+ When `EGREGORE_ORG_CONTEXT_V1` is attached, authorized retrieval is complete:
190
189
  never query again that turn, and state any evidence gap. `OPENED_BY_OBSERVE`
191
190
  means the evidence is open. A later explicit search starts a new turn.
192
191
 
193
- **Visibility is the contract.** Emit it verbatim as a
194
- standalone assistant message before the first Bash, Grep, Glob, Read, or other
195
- retrieval tool call.
196
- Shell/tool output does not satisfy this requirement. Do not paraphrase the line.
192
+ **Scope.** Egregore Runtime is for organizational recall only: team memory,
193
+ decisions, handoffs, prior work. Code, files, Git, and tests use the harness's
194
+ normal tools, with no beat and no Runtime call.
197
195
 
198
- Use the memory form normally; use Connect only for an explicit graph traversal
199
- that actually runs. Emit once per episode. A `bin/search.sh` banner is only a
200
- shell fallback and does not replace this visible beat.
196
+ **Visibility is the contract.** Emit it verbatim as a standalone assistant
197
+ message before the first tool call that performs the organizational recall
198
+ (the `bin/search.sh` query or a graph read). Tool output does not satisfy it.
199
+ Do not paraphrase the line.
200
+
201
+ Use Connect only for a graph traversal that actually runs. Emit once per
202
+ episode.
201
203
 
202
204
  **Routing:** Organizational recall always enters Egregore Runtime. The model is
203
205
  the semantic intent authority, deciding from meaning rather than fixed phrases.
@@ -212,15 +214,14 @@ Search output is model-internal: show only the retrieval line and receipts —
212
214
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.
213
215
 
214
216
  Do not resolve `memory/` to its sibling repo or start with raw `grep`/`ls`.
215
- Search owns lexical/semantic selection and attaches no graph by default. Open a
216
- source only when its excerpt cannot support a necessary claim. For current
217
+ Search attaches no graph by default. Open a source only when its excerpt
218
+ cannot support a necessary claim. For current
217
219
  team/person synthesis, retain known organization/member names and choose
218
- `--recent-days` plus `--min-evidence`. Use native
219
- Grep/Glob for code and exact errors. `/activity`, `/dashboard`, and `/project`
220
+ `--recent-days` plus `--min-evidence`. `/activity`, `/dashboard`, and `/project`
220
221
  display bounded status surfaces only when named or directly requested as a
221
- card. Questions asking for an explained synthesis of current organizational
222
- work are Runtime/QMD recall, as is prior-work discovery. Do not run full
223
- hybrid retrieval on unrelated prompts or infer graph from recall intent.
222
+ card. Explained syntheses of current organizational work and prior-work
223
+ discovery are Runtime/QMD recall. Do not run retrieval on unrelated prompts or
224
+ infer graph from recall intent.
224
225
 
225
226
  Do not emit the beat for code search, Git inspection, startup hydration, graph
226
227
  writes, ingestion, background sync, maintenance, or internal queries.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "generatedBy": "bin/prime-render-spec.mjs",
3
3
  "source": "AGENTS.md generated Codex spec",
4
- "sourceHash": "76c76679f791",
5
- "outputHash": "f220a03bc40a",
4
+ "sourceHash": "496480654dd9",
5
+ "outputHash": "fa7b0b82de98",
6
6
  "adaptations": [
7
7
  "Prime Agent APPEND_SYSTEM delivery from .prime/agent/",
8
8
  "Prime Agent tool_call branch gate incl. ipython classification",
@@ -258,19 +258,21 @@ For each user-directed organizational lookup, show exactly one visible line:
258
258
  - retrieval that actually queries the connected graph:
259
259
  `⌕ Egregore Connect · searching your organization’s memory and relationships`
260
260
 
261
- Runtime owns retrieval; no agent feature flag does. `EGREGORE_ORG_CONTEXT_V1`
262
- is only the schema label. When attached, authorized retrieval is complete:
261
+ When `EGREGORE_ORG_CONTEXT_V1` is attached, authorized retrieval is complete:
263
262
  never query again that turn, and state any evidence gap. `OPENED_BY_OBSERVE`
264
263
  means the evidence is open. A later explicit search starts a new turn.
265
264
 
266
- **Visibility is the contract.** Emit it verbatim as a
267
- standalone assistant message before the first Bash, Grep, Glob, Read, or other
268
- retrieval tool call.
269
- Shell/tool output does not satisfy this requirement. Do not paraphrase the line.
265
+ **Scope.** Egregore Runtime is for organizational recall only: team memory,
266
+ decisions, handoffs, prior work. Code, files, Git, and tests use the harness's
267
+ normal tools, with no beat and no Runtime call.
270
268
 
271
- Use the memory form normally; use Connect only for an explicit graph traversal
272
- that actually runs. Emit once per episode. A `bin/search.sh` banner is only a
273
- shell fallback and does not replace this visible beat.
269
+ **Visibility is the contract.** Emit it verbatim as a standalone assistant
270
+ message before the first tool call that performs the organizational recall
271
+ (the `bin/search.sh` query or a graph read). Tool output does not satisfy it.
272
+ Do not paraphrase the line.
273
+
274
+ Use Connect only for a graph traversal that actually runs. Emit once per
275
+ episode.
274
276
 
275
277
  **Routing:** Organizational recall always enters Egregore Runtime. The model is
276
278
  the semantic intent authority, deciding from meaning rather than fixed phrases.
@@ -285,15 +287,14 @@ Search output is model-internal: show only the retrieval line and receipts —
285
287
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.
286
288
 
287
289
  Do not resolve `memory/` to its sibling repo or start with raw `grep`/`ls`.
288
- Search owns lexical/semantic selection and attaches no graph by default. Open a
289
- source only when its excerpt cannot support a necessary claim. For current
290
+ Search attaches no graph by default. Open a source only when its excerpt
291
+ cannot support a necessary claim. For current
290
292
  team/person synthesis, retain known organization/member names and choose
291
- `--recent-days` plus `--min-evidence`. Use native
292
- Grep/Glob for code and exact errors. `$activity`, `$dashboard`, and `$project`
293
+ `--recent-days` plus `--min-evidence`. `$activity`, `$dashboard`, and `$project`
293
294
  display bounded status surfaces only when named or directly requested as a
294
- card. Questions asking for an explained synthesis of current organizational
295
- work are Runtime/QMD recall, as is prior-work discovery. Do not run full
296
- hybrid retrieval on unrelated prompts or infer graph from recall intent.
295
+ card. Explained syntheses of current organizational work and prior-work
296
+ discovery are Runtime/QMD recall. Do not run retrieval on unrelated prompts or
297
+ infer graph from recall intent.
297
298
 
298
299
  Do not emit the beat for code search, Git inspection, startup hydration, graph
299
300
  writes, ingestion, background sync, maintenance, or internal queries.
@@ -157,9 +157,8 @@ const NON_SKILL_TOKENS = new Set(['loop', 'fast', 'config', 'clear', 'help']);
157
157
  // hook delivers there. Rewrite the fallback command to the stdout form and
158
158
  // state the same hygiene contract in shell terms: search output is
159
159
  // model-internal; the visible reply carries only the retrieval line.
160
- const CLAUDE_PACKET_NOTE = `\`--context-packet\` keeps the evidence out of the visible transcript and
161
- attaches it privately; the visible output is the attribution line alone.
162
- Never re-print ranked results, file/rank/excerpt metadata, or raw JSON.`;
160
+ const CLAUDE_PACKET_NOTE = `\`--context-packet\` attaches the evidence privately; the visible output is the
161
+ attribution line alone. Never re-print ranked results or raw JSON.`;
163
162
  const SHELL_PACKET_NOTE = `Search output is model-internal: show only the retrieval line and receipts —
164
163
  never ranked blocks, file/rank/excerpt metadata, or raw JSON.`;
165
164
 
@@ -38,7 +38,7 @@ from .runtime import EgregoreRuntime, local_runtime
38
38
  _ORG_CONTEXT_PREFETCH_PATTERN = re.compile(
39
39
  "|".join(
40
40
  (
41
- r"\b(find|locate|dig up|look up|pull up)\b.*\b"
41
+ r"\b(find|locate|dig up|look up|pull up)\b[^\n]*\b"
42
42
  r"(handoff|decision|notes?|docs?|meeting|harvest|quest|reflection)s?\b",
43
43
  r"\bdid we (decide|discuss|agree|say|talk about|cover)\b",
44
44
  r"\bdo we have (any )?(notes?|docs?|anything|something)( on| about)?\b",
@@ -161,6 +161,26 @@ def _clear_observe_receipt(root: Path, harness: str) -> None:
161
161
  pass
162
162
 
163
163
 
164
+ def _actor_contract_marker_path(
165
+ root: Path, harness: str, session: str | None = None
166
+ ) -> Path:
167
+ """Gitignored per-session marker: the full actor/retrieval contract was
168
+ already attached once. Later prompts receive a short reminder instead of
169
+ the ~4 KB block. bin/pre-compact.sh removes the marker so the first prompt
170
+ after a compaction re-attaches the full contract.
171
+
172
+ ``session`` is the harness's own session id from the hook payload. The
173
+ Egregore session file in the checkout is shared by every concurrent
174
+ session there and rewritten by each new one, so keying on it alone made
175
+ parallel sessions re-attach the full contract on every prompt."""
176
+
177
+ session = re.sub(r"[^A-Za-z0-9_.-]", "-", session or _session_id(root))[:120]
178
+ return (
179
+ root / ".egregore" / "runtime" / "observe"
180
+ / f"actor-contract-{harness}-{session}.sent"
181
+ )
182
+
183
+
164
184
  def _write_observe_receipt(root: Path, harness: str, prompt: str, context) -> None:
165
185
  """Record that this prompt already received useful authorized evidence.
166
186
 
@@ -378,6 +398,37 @@ def _query(args: argparse.Namespace) -> int:
378
398
  return 0
379
399
 
380
400
 
401
+ _PASTED_OUTPUT_LINE = re.compile(
402
+ r"^(?:"
403
+ r"[ MADRCU?!]{2}\s+\S" # git status --short / porcelain rows
404
+ r"|[▶❯»]\s" # terminal prompt markers
405
+ r"|\$\s\S" # shell prompt
406
+ r"|(?:\+\+\+|---|@@)\s" # unified diff
407
+ r"|diff --git\b"
408
+ r"|Traceback \(most recent call last\)"
409
+ r"|\s+File \"[^\"]+\", line \d+"
410
+ r"|\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}" # log timestamps
411
+ r"|[A-Za-z0-9_./-]+/[A-Za-z0-9_.-]+\.[a-z]{1,6}$" # a bare path
412
+ r")"
413
+ )
414
+
415
+
416
+ def _looks_like_pasted_output(prompt: str) -> bool:
417
+ """True for terminal output, diffs, logs, and code blocks pasted into a prompt.
418
+
419
+ Those carry words like "handoff", "notes", or "docs" by accident; running a
420
+ retrieval on them attaches unrelated evidence and costs seconds. The actor
421
+ context still tells the model to recall once if the prompt truly needs it.
422
+ """
423
+
424
+ if "```" in prompt:
425
+ return True
426
+ lines = [line for line in prompt.splitlines() if line.strip()]
427
+ if len(lines) >= 8:
428
+ return True
429
+ return sum(1 for line in lines if _PASTED_OUTPUT_LINE.match(line)) >= 2
430
+
431
+
381
432
  def prompt_prefetches_org_context(prompt: str) -> bool:
382
433
  """Return whether Observe should prefetch likely organizational context.
383
434
 
@@ -390,6 +441,7 @@ def prompt_prefetches_org_context(prompt: str) -> bool:
390
441
  return bool(
391
442
  prompt
392
443
  and not _EXPLICIT_SEARCH.search(prompt)
444
+ and not _looks_like_pasted_output(prompt)
393
445
  and (
394
446
  _ORG_CONTEXT_PREFETCH_PATTERN.search(prompt)
395
447
  or _ORG_RITUAL_CONTEXT_PREFETCH_PATTERN.search(prompt)
@@ -414,8 +466,22 @@ def prompt_requests_actor_context(prompt: str) -> bool:
414
466
  return bool(prompt and prompt.strip())
415
467
 
416
468
 
417
- def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) -> str:
418
- """Render the minimum stable identity needed by harness rituals."""
469
+ _RECALL_ONLY_SCOPE = (
470
+ "Egregore Runtime is for organizational recall and memory only: team memory, "
471
+ "decisions, handoffs, meetings, prior work. Code search, file reads, Git, tests, "
472
+ "and every other task use the harness's normal tools (Grep, Glob, Read, Bash) "
473
+ "with no retrieval ritual and no Runtime call."
474
+ )
475
+
476
+
477
+ def _actor_context_block(
478
+ runtime: EgregoreRuntime, root: Path, harness: str, *, full: bool = True
479
+ ) -> str:
480
+ """Render the stable identity and the recall contract for harness rituals.
481
+
482
+ ``full`` renders the complete contract (once per session); otherwise a
483
+ short reminder that keeps identity and the recall-only scope in view.
484
+ """
419
485
 
420
486
  actor_context = _actor(runtime, root, harness)
421
487
  actor = actor_context.actor
@@ -435,6 +501,22 @@ def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) ->
435
501
  search_command = 'bash bin/search.sh query "<concept>" -n 6 --compact'
436
502
  if harness == "claude":
437
503
  search_command += " --context-packet"
504
+ if not full:
505
+ return "\n".join(
506
+ (
507
+ "EGREGORE_ACTOR_CONTEXT_V1",
508
+ "Reminder — the full contract was attached earlier in this session.",
509
+ f"Actor: {actor.display_name} · Actor ID: {actor.actor_id} · "
510
+ f"Organization ID: {actor_context.profile.org_id} · Runtime root: {root}",
511
+ _RECALL_ONLY_SCOPE,
512
+ "When a prompt needs that recall and no EGREGORE_ORG_CONTEXT_V1 block is "
513
+ f"attached, run exactly one `{search_command}`; exact latest/oldest/open "
514
+ "handoff questions use `bash bin/search.sh handoffs …` instead.",
515
+ "`me` and `myself` mean this Actor ID and display name; never infer identity "
516
+ "from the OS user, Git, or GitHub names.",
517
+ "END_EGREGORE_ACTOR_CONTEXT_V1",
518
+ )
519
+ )
438
520
  return "\n".join(
439
521
  (
440
522
  "EGREGORE_ACTOR_CONTEXT_V1",
@@ -451,6 +533,7 @@ def _actor_context_block(runtime: EgregoreRuntime, root: Path, harness: str) ->
451
533
  f"Runtime root: {root}",
452
534
  "Run Egregore retrieval and workflow commands from this Runtime root. "
453
535
  "Do not fall back to a launch checkout named elsewhere in the session.",
536
+ _RECALL_ONLY_SCOPE,
454
537
  "Organizational retrieval status: NOT_PERFORMED. This actor-only block "
455
538
  "does not mean organizational recall is unnecessary.",
456
539
  "Interpret the user's intent semantically, without requiring trigger words. "
@@ -687,11 +770,21 @@ def _prompt_hook(args: argparse.Namespace) -> int:
687
770
  args.limit,
688
771
  )
689
772
  else:
773
+ hook_session = str(payload.get("session_id") or "").strip() or None
774
+ marker = _actor_contract_marker_path(root, args.harness, hook_session)
775
+ full = not marker.exists()
690
776
  additional_context = _actor_context_block(
691
777
  runtime,
692
778
  root,
693
779
  args.harness,
780
+ full=full,
694
781
  )
782
+ if full:
783
+ try:
784
+ marker.parent.mkdir(parents=True, exist_ok=True)
785
+ marker.write_text(datetime.now(UTC).isoformat(), encoding="utf-8")
786
+ except OSError:
787
+ pass
695
788
  except Exception as exc: # Hooks are advisory and must never block a prompt.
696
789
  additional_context = (
697
790
  "Egregore Runtime context was requested but the Local Runtime is unavailable "