create-egregore 0.19.22 → 0.19.24

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.
Files changed (125) hide show
  1. package/assets/egregore-launcher.js +50 -7
  2. package/assets/egregore-launcher.sh +10 -2
  3. package/bin/cli.js +21 -3
  4. package/lib/codex-runtime.js +25 -3
  5. package/lib/connect-runtime.js +329 -0
  6. package/lib/pi-runtime.js +23 -3
  7. package/lib/setup.js +13 -0
  8. package/package.json +3 -3
  9. package/runtime/codex/.codex/skills/handoff/SKILL.md +99 -13
  10. package/runtime/codex/.codex/skills/save/SKILL.md +39 -4
  11. package/runtime/codex/.codex/spec-manifest.json +2 -2
  12. package/runtime/codex/AGENTS.md +18 -1
  13. package/runtime/codex/bin/agent.sh +28 -14
  14. package/runtime/codex/bin/attendant.sh +169 -6
  15. package/runtime/codex/bin/codex-render-spec.mjs +18 -1
  16. package/runtime/codex/bin/codex-session-start.sh +13 -0
  17. package/runtime/codex/bin/codex-skill-render.mjs +5 -0
  18. package/runtime/codex/bin/codex-sync-skills.sh +1 -0
  19. package/runtime/codex/bin/graph.sh +21 -1
  20. package/runtime/codex/bin/handoff-run.sh +88 -84
  21. package/runtime/codex/bin/index-handoff.sh +2 -1
  22. package/runtime/codex/bin/lib/context.sh +220 -172
  23. package/runtime/codex/bin/lib/dashboard-artifact.sh +113 -13
  24. package/runtime/codex/bin/lib/git-sync.sh +44 -8
  25. package/runtime/codex/bin/lib/greeting.sh +34 -11
  26. package/runtime/codex/bin/lib/identity.sh +32 -14
  27. package/runtime/codex/bin/person.py +20 -0
  28. package/runtime/codex/bin/publish-artifact.sh +25 -4
  29. package/runtime/codex/manifest.json +3 -72
  30. package/runtime/pi/.codex/skills/handoff/SKILL.md +99 -13
  31. package/runtime/pi/.codex/skills/save/SKILL.md +39 -4
  32. package/runtime/pi/.codex/spec-manifest.json +2 -2
  33. package/runtime/pi/.pi/APPEND_SYSTEM.md +18 -1
  34. package/runtime/pi/.pi/spec-manifest.json +2 -2
  35. package/runtime/pi/AGENTS.md +18 -1
  36. package/runtime/pi/bin/agent.sh +28 -14
  37. package/runtime/pi/bin/attendant.sh +169 -6
  38. package/runtime/pi/bin/codex-render-spec.mjs +18 -1
  39. package/runtime/pi/bin/codex-session-start.sh +13 -0
  40. package/runtime/pi/bin/codex-skill-render.mjs +5 -0
  41. package/runtime/pi/bin/codex-sync-skills.sh +1 -0
  42. package/runtime/pi/bin/graph.sh +21 -1
  43. package/runtime/pi/bin/handoff-run.sh +88 -84
  44. package/runtime/pi/bin/index-handoff.sh +2 -1
  45. package/runtime/pi/bin/lib/context.sh +220 -172
  46. package/runtime/pi/bin/lib/dashboard-artifact.sh +113 -13
  47. package/runtime/pi/bin/lib/git-sync.sh +44 -8
  48. package/runtime/pi/bin/lib/greeting.sh +34 -11
  49. package/runtime/pi/bin/lib/identity.sh +32 -14
  50. package/runtime/pi/bin/person.py +20 -0
  51. package/runtime/pi/bin/pi-product-workflows.mjs +19 -6
  52. package/runtime/pi/bin/publish-artifact.sh +25 -4
  53. package/runtime/pi/manifest.json +3 -72
  54. package/runtime/codex/.codex/skills/add/SKILL.md +0 -29
  55. package/runtime/codex/.codex/skills/answer/SKILL.md +0 -29
  56. package/runtime/codex/.codex/skills/ask/SKILL.md +0 -67
  57. package/runtime/codex/.codex/skills/audit/SKILL.md +0 -29
  58. package/runtime/codex/.codex/skills/autosave/SKILL.md +0 -29
  59. package/runtime/codex/.codex/skills/branch/SKILL.md +0 -29
  60. package/runtime/codex/.codex/skills/commit/SKILL.md +0 -29
  61. package/runtime/codex/.codex/skills/demo-mode/SKILL.md +0 -29
  62. package/runtime/codex/.codex/skills/deploy-bridge/SKILL.md +0 -29
  63. package/runtime/codex/.codex/skills/design/SKILL.md +0 -29
  64. package/runtime/codex/.codex/skills/egregore-voice/SKILL.md +0 -29
  65. package/runtime/codex/.codex/skills/emissary/SKILL.md +0 -45
  66. package/runtime/codex/.codex/skills/env/SKILL.md +0 -29
  67. package/runtime/codex/.codex/skills/ingest-google/SKILL.md +0 -29
  68. package/runtime/codex/.codex/skills/minion/SKILL.md +0 -29
  69. package/runtime/codex/.codex/skills/mock/SKILL.md +0 -29
  70. package/runtime/codex/.codex/skills/pr/SKILL.md +0 -29
  71. package/runtime/codex/.codex/skills/product-voice/SKILL.md +0 -29
  72. package/runtime/codex/.codex/skills/project/SKILL.md +0 -45
  73. package/runtime/codex/.codex/skills/pull/SKILL.md +0 -29
  74. package/runtime/codex/.codex/skills/push/SKILL.md +0 -29
  75. package/runtime/codex/.codex/skills/quest-suggest/SKILL.md +0 -29
  76. package/runtime/codex/.codex/skills/review-pr/SKILL.md +0 -45
  77. package/runtime/codex/.codex/skills/scroll/SKILL.md +0 -288
  78. package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +0 -4
  79. package/runtime/codex/.codex/skills/scroll/assets/shell.html +0 -871
  80. package/runtime/codex/.codex/skills/search/SKILL.md +0 -79
  81. package/runtime/codex/.codex/skills/session-view/SKILL.md +0 -29
  82. package/runtime/codex/.codex/skills/setup/SKILL.md +0 -29
  83. package/runtime/codex/.codex/skills/summon/SKILL.md +0 -45
  84. package/runtime/codex/.codex/skills/task/SKILL.md +0 -29
  85. package/runtime/codex/.codex/skills/teams-connect/SKILL.md +0 -29
  86. package/runtime/codex/.codex/skills/telegram-connect/SKILL.md +0 -29
  87. package/runtime/codex/.codex/skills/the-spiral/SKILL.md +0 -84
  88. package/runtime/codex/.codex/skills/thread/SKILL.md +0 -29
  89. package/runtime/codex/.codex/skills/todo/SKILL.md +0 -45
  90. package/runtime/pi/.codex/skills/add/SKILL.md +0 -29
  91. package/runtime/pi/.codex/skills/answer/SKILL.md +0 -29
  92. package/runtime/pi/.codex/skills/ask/SKILL.md +0 -67
  93. package/runtime/pi/.codex/skills/audit/SKILL.md +0 -29
  94. package/runtime/pi/.codex/skills/autosave/SKILL.md +0 -29
  95. package/runtime/pi/.codex/skills/branch/SKILL.md +0 -29
  96. package/runtime/pi/.codex/skills/commit/SKILL.md +0 -29
  97. package/runtime/pi/.codex/skills/demo-mode/SKILL.md +0 -29
  98. package/runtime/pi/.codex/skills/deploy-bridge/SKILL.md +0 -29
  99. package/runtime/pi/.codex/skills/design/SKILL.md +0 -29
  100. package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +0 -29
  101. package/runtime/pi/.codex/skills/emissary/SKILL.md +0 -45
  102. package/runtime/pi/.codex/skills/env/SKILL.md +0 -29
  103. package/runtime/pi/.codex/skills/ingest-google/SKILL.md +0 -29
  104. package/runtime/pi/.codex/skills/minion/SKILL.md +0 -29
  105. package/runtime/pi/.codex/skills/mock/SKILL.md +0 -29
  106. package/runtime/pi/.codex/skills/pr/SKILL.md +0 -29
  107. package/runtime/pi/.codex/skills/product-voice/SKILL.md +0 -29
  108. package/runtime/pi/.codex/skills/project/SKILL.md +0 -45
  109. package/runtime/pi/.codex/skills/pull/SKILL.md +0 -29
  110. package/runtime/pi/.codex/skills/push/SKILL.md +0 -29
  111. package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +0 -29
  112. package/runtime/pi/.codex/skills/review-pr/SKILL.md +0 -45
  113. package/runtime/pi/.codex/skills/scroll/SKILL.md +0 -288
  114. package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +0 -4
  115. package/runtime/pi/.codex/skills/scroll/assets/shell.html +0 -871
  116. package/runtime/pi/.codex/skills/search/SKILL.md +0 -79
  117. package/runtime/pi/.codex/skills/session-view/SKILL.md +0 -29
  118. package/runtime/pi/.codex/skills/setup/SKILL.md +0 -29
  119. package/runtime/pi/.codex/skills/summon/SKILL.md +0 -45
  120. package/runtime/pi/.codex/skills/task/SKILL.md +0 -29
  121. package/runtime/pi/.codex/skills/teams-connect/SKILL.md +0 -29
  122. package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +0 -29
  123. package/runtime/pi/.codex/skills/the-spiral/SKILL.md +0 -84
  124. package/runtime/pi/.codex/skills/thread/SKILL.md +0 -29
  125. package/runtime/pi/.codex/skills/todo/SKILL.md +0 -45
@@ -48,7 +48,27 @@ _millis() {
48
48
  # greeting) pay zero network round-trips when warm. TTL in seconds; unset/0
49
49
  # disables. Mutating queries are never cached.
50
50
  _CACHE_TTL="${EGREGORE_GRAPH_CACHE_TTL:-0}"
51
- _CACHE_DIR="$HOME/.egregore/graph-cache/$(echo -n "$SCRIPT_DIR" | cksum | cut -d' ' -f1)"
51
+ # Key the cache by the MAIN checkout so worktree sessions hit the same warm
52
+ # cache the attendant maintains (its replay uses the same derivation).
53
+ # Worktrees have .git as a FILE pointing into the main repo's .git/worktrees/.
54
+ # api_url + slug are part of the key so entries never cross tenants when the
55
+ # same checkout is repointed at a different org/API (cache entries are keyed
56
+ # only by query+params below, so the directory must carry the tenant).
57
+ _CACHE_ROOT="$SCRIPT_DIR"
58
+ if [ -f "$SCRIPT_DIR/.git" ]; then
59
+ _WT_GITDIR=$(sed 's/^gitdir: //' "$SCRIPT_DIR/.git" 2>/dev/null || true)
60
+ if [ -n "$_WT_GITDIR" ]; then
61
+ _CACHE_ROOT=$(cd "$_WT_GITDIR/../../.." 2>/dev/null && pwd || echo "$SCRIPT_DIR")
62
+ fi
63
+ fi
64
+ # The scope is a non-secret fingerprint of the EFFECTIVE endpoint + credential
65
+ # (already resolved above: process env → .env → committed api_url). Committed
66
+ # config alone is not enough — an EGREGORE_API_URL/KEY override or a swapped
67
+ # .env can point this checkout at a different tenant than egregore.json names,
68
+ # and the entry key below carries only query+params. cksum output is a CRC,
69
+ # not a reversible or usable credential.
70
+ _CACHE_SCOPE=$(echo -n "${API_URL:-}|${API_KEY:-}" | cksum | cut -d' ' -f1)
71
+ _CACHE_DIR="$HOME/.egregore/graph-cache/$(echo -n "${_CACHE_ROOT}|${_CACHE_SCOPE}" | cksum | cut -d' ' -f1)"
52
72
 
53
73
  _cache_key() {
54
74
  echo -n "$1|${2:-}" | cksum | cut -d' ' -f1
@@ -12,6 +12,8 @@ set -euo pipefail
12
12
  # [--recipient <name>] \
13
13
  # [--project <name>] \
14
14
  # [--intent <action|feedback|fyi>] \
15
+ # [--content-mode <supplied|generated>] \
16
+ # [--include-session-artifacts] \
15
17
  # <<'HANDOFFEOF'
16
18
  # ...full markdown body of the handoff...
17
19
  # HANDOFFEOF
@@ -19,7 +21,7 @@ set -euo pipefail
19
21
  # The full file body is read from stdin. The script:
20
22
  # 1. Computes the file path: memory/handoffs/YYYY-MM/DD-author-slug.md
21
23
  # 2. Writes the file
22
- # 3. Appends ## Repo State section (from bin/repo-state.sh) if non-empty
24
+ # 3. In generated mode, appends ## Repo State when non-empty
23
25
  # 4. Prepends memory/handoffs/index.md with a new entry
24
26
  # 5. Runs bin/index-handoff.sh (connected mode only) — creates Session node
25
27
  # and returns a subgraph snapshot in the result JSON
@@ -51,6 +53,8 @@ NO_PUSH=0
51
53
  NO_NOTIFY=0
52
54
  NO_PUBLISH=0
53
55
  COMPOSED="" # optional house-kit JSON — the agent-composed render spec
56
+ CONTENT_MODE="generated"
57
+ INCLUDE_SESSION_ARTIFACTS=0
54
58
 
55
59
  while [ $# -gt 0 ]; do
56
60
  case "$1" in
@@ -60,6 +64,8 @@ while [ $# -gt 0 ]; do
60
64
  --project) PROJECT="${2:-}"; shift 2 ;;
61
65
  --intent) INTENT="${2:-}"; shift 2 ;;
62
66
  --composed) COMPOSED="${2:-}"; shift 2 ;;
67
+ --content-mode) CONTENT_MODE="${2:-}"; shift 2 ;;
68
+ --include-session-artifacts) INCLUDE_SESSION_ARTIFACTS=1; shift ;;
63
69
  --no-push) NO_PUSH=1; shift ;;
64
70
  --no-notify) NO_NOTIFY=1; shift ;;
65
71
  --no-publish) NO_PUBLISH=1; shift ;;
@@ -72,7 +78,7 @@ done
72
78
  # markdown file is ALWAYS the memory record + graph index either way.
73
79
  RENDER_TYPE="handoff"
74
80
  RENDER_SRC="" # resolved to $ABS_FILE after the file is written, unless composed
75
- if [ -n "$COMPOSED" ] && [ -f "$COMPOSED" ]; then
81
+ if [ "$CONTENT_MODE" = "generated" ] && [ -n "$COMPOSED" ] && [ -f "$COMPOSED" ]; then
76
82
  RENDER_TYPE="composed"
77
83
  RENDER_SRC="$COMPOSED"
78
84
  fi
@@ -83,6 +89,14 @@ case "$INTENT" in
83
89
  action|feedback|fyi) ;;
84
90
  *) echo "Invalid --intent: $INTENT (action|feedback|fyi)" >&2; exit 1 ;;
85
91
  esac
92
+ case "$CONTENT_MODE" in
93
+ supplied|generated) ;;
94
+ *) echo "Invalid --content-mode: $CONTENT_MODE (supplied|generated)" >&2; exit 1 ;;
95
+ esac
96
+ if [ "$CONTENT_MODE" = "supplied" ] && [ -n "$COMPOSED" ]; then
97
+ echo "--composed cannot be used with --content-mode supplied" >&2
98
+ exit 1
99
+ fi
86
100
 
87
101
  MODE=$(jq -r '.mode // "connected"' "$CONFIG" 2>/dev/null || echo "connected")
88
102
  TODAY=$(date +%Y-%m-%d)
@@ -122,92 +136,73 @@ if [ ! -s "$ABS_FILE" ]; then
122
136
  exit 1
123
137
  fi
124
138
 
125
- # --- Canonical capture contract -----------------------------------------
126
- # /handoff is the addressed door into the shared capture engine. Persist the
127
- # same schema/mode fields used by /wrap and SessionEnd so filesystem and graph
128
- # retrieval can distinguish captures without inferring from directory names.
129
- if ! grep -qiE '^(capture_schema:|\*\*[Cc]apture [Ss]chema\*\*:)' "$ABS_FILE"; then
130
- CAPTURE_KIND=""
131
- if ! grep -qiE '^(kind:|\*\*[Kk]ind\*\*:)' "$ABS_FILE"; then
132
- CAPTURE_KIND="addressed"
133
- fi
134
- if [ "$(head -1 "$ABS_FILE")" = "---" ]; then
135
- awk -v kind="$CAPTURE_KIND" '
136
- NR == 1 {
137
- print
138
- print "capture_schema: egregore-capture/v1"
139
- print "capture_mode: addressed"
140
- if (kind != "") print "kind: " kind
141
- next
142
- }
143
- { print }
144
- ' "$ABS_FILE" > "$ABS_FILE.new" && mv "$ABS_FILE.new" "$ABS_FILE"
145
- else
146
- awk -v kind="$CAPTURE_KIND" '
147
- !injected && /^\*\*[Aa]uthor\*\*:/ {
148
- print
149
- print "**Capture Schema**: egregore-capture/v1"
150
- print "**Capture Mode**: addressed"
151
- if (kind != "") print "**Kind**: " kind
152
- injected=1
153
- next
154
- }
155
- { print }
156
- END {
157
- if (!injected) {
158
- print "**Capture Schema**: egregore-capture/v1"
159
- print "**Capture Mode**: addressed"
160
- if (kind != "") print "**Kind**: " kind
161
- }
162
- }
163
- ' "$ABS_FILE" > "$ABS_FILE.new" && mv "$ABS_FILE.new" "$ABS_FILE"
164
- fi
165
- fi
139
+ # --- Canonical metadata boundary ----------------------------------------
140
+ # Callers are allowed to send prose-only markdown, but persisted handoffs are
141
+ # not. Normalize every input to the same frontmatter contract before indexing
142
+ # or rendering so title/author/recipient cannot disappear at the adapter seam.
143
+ HAS_FRONTMATTER=0
144
+ [ "$(head -1 "$ABS_FILE")" = "---" ] && HAS_FRONTMATTER=1
166
145
 
167
- # --- Inject **To**: line if --recipient given and body omits it -----
168
- # index-handoff.sh parses recipients from the file body. Skills
169
- # normally write the To line themselves, but callers that invoke
170
- # handoff-run.sh directly often skip it. Inject after the Author
171
- # line so the graph gets the HANDED_TO edge.
172
- if [ -n "$RECIPIENT" ] && ! grep -qi "^\*\*[Tt]o\*\*:" "$ABS_FILE"; then
173
- awk -v r="$RECIPIENT" '
174
- !injected && /^\*\*[Aa]uthor\*\*:/ {
175
- print
176
- print "**To**: " r
177
- injected=1
178
- next
179
- }
180
- { print }
181
- ' "$ABS_FILE" > "$ABS_FILE.new" && mv "$ABS_FILE.new" "$ABS_FILE"
182
- fi
146
+ _has_frontmatter_key() {
147
+ [ "$HAS_FRONTMATTER" = "1" ] && sed -n '2,/^---$/p' "$ABS_FILE" | grep -qiE "^${1}:"
148
+ }
183
149
 
184
- # --- Persist lifecycle intent in the canonical handoff file -------------
185
- if ! grep -qiE '^(intent:|\*\*[Ii]ntent\*\*:)' "$ABS_FILE"; then
186
- if [ "$(head -1 "$ABS_FILE")" = "---" ]; then
187
- awk -v intent="$INTENT" '
188
- NR == 1 { print; print "intent: " intent; next }
189
- { print }
190
- ' "$ABS_FILE" > "$ABS_FILE.new" && mv "$ABS_FILE.new" "$ABS_FILE"
191
- else
192
- awk -v intent="$INTENT" '
193
- !injected && /^\*\*[Aa]uthor\*\*:/ {
194
- print
195
- print "**Intent**: " intent
196
- injected=1
197
- next
198
- }
199
- { print }
200
- END {
201
- if (!injected) print "**Intent**: " intent
202
- }
203
- ' "$ABS_FILE" > "$ABS_FILE.new" && mv "$ABS_FILE.new" "$ABS_FILE"
204
- fi
150
+ MISSING_SCHEMA=0; _has_frontmatter_key capture_schema || MISSING_SCHEMA=1
151
+ MISSING_MODE=0; _has_frontmatter_key capture_mode || MISSING_MODE=1
152
+ MISSING_KIND=0; _has_frontmatter_key kind || MISSING_KIND=1
153
+ MISSING_FROM=0; _has_frontmatter_key from || _has_frontmatter_key author || MISSING_FROM=1
154
+ MISSING_DATE=0; _has_frontmatter_key date || MISSING_DATE=1
155
+ MISSING_TOPIC=0; _has_frontmatter_key topic || MISSING_TOPIC=1
156
+ MISSING_INTENT=0; _has_frontmatter_key intent || MISSING_INTENT=1
157
+ MISSING_CONTENT_MODE=0; _has_frontmatter_key content_mode || MISSING_CONTENT_MODE=1
158
+ MISSING_RECIPIENT=0
159
+ if [ -n "$RECIPIENT" ]; then
160
+ _has_frontmatter_key addressed_to || _has_frontmatter_key to || MISSING_RECIPIENT=1
205
161
  fi
206
162
 
163
+ awk \
164
+ -v has_frontmatter="$HAS_FRONTMATTER" \
165
+ -v schema="$MISSING_SCHEMA" -v mode="$MISSING_MODE" -v kind="$MISSING_KIND" \
166
+ -v from="$MISSING_FROM" -v date_missing="$MISSING_DATE" \
167
+ -v topic_missing="$MISSING_TOPIC" -v intent_missing="$MISSING_INTENT" \
168
+ -v content_mode_missing="$MISSING_CONTENT_MODE" \
169
+ -v recipient_missing="$MISSING_RECIPIENT" \
170
+ -v author="$AUTHOR" -v date="$TODAY" -v topic="$TOPIC" \
171
+ -v intent="$INTENT" -v recipient="$RECIPIENT" -v content_mode="$CONTENT_MODE" '
172
+ function metadata() {
173
+ if (schema) print "capture_schema: egregore-capture/v1"
174
+ if (mode) print "capture_mode: addressed"
175
+ if (kind) print "kind: addressed"
176
+ if (from) print "from: " author
177
+ if (recipient_missing) print "addressed_to: " recipient
178
+ if (date_missing) print "date: " date
179
+ if (topic_missing) print "topic: " topic
180
+ if (intent_missing) print "intent: " intent
181
+ if (content_mode_missing) print "content_mode: " content_mode
182
+ }
183
+ BEGIN {
184
+ if (!has_frontmatter) {
185
+ print "---"
186
+ metadata()
187
+ print "---"
188
+ if (content_mode != "supplied") print ""
189
+ }
190
+ }
191
+ has_frontmatter && NR == 1 {
192
+ print
193
+ metadata()
194
+ next
195
+ }
196
+ { print }
197
+ ' "$ABS_FILE" > "$ABS_FILE.new" && mv "$ABS_FILE.new" "$ABS_FILE"
198
+
207
199
  # --- Append repo state ---------------------------------------------------
208
200
  # --no-pr skips `gh pr list` (~400–600ms per repo). A detached PR backfill
209
201
  # after the parallel phase rewrites `—` → `#N` and re-pushes memory.
210
- REPO_STATE=$(bash "$SCRIPT_DIR/bin/repo-state.sh" --no-pr 2>/dev/null || true)
202
+ REPO_STATE=""
203
+ if [ "$CONTENT_MODE" = "generated" ] || [ "$INCLUDE_SESSION_ARTIFACTS" = "1" ]; then
204
+ REPO_STATE=$(bash "$SCRIPT_DIR/bin/repo-state.sh" --no-pr 2>/dev/null || true)
205
+ fi
211
206
  if [ -n "$REPO_STATE" ]; then
212
207
  printf '\n%s\n' "$REPO_STATE" >> "$ABS_FILE"
213
208
  fi
@@ -401,12 +396,16 @@ PID_MEMORY=$!
401
396
  RENDERED_HTML="$TMPD/handoff.html"
402
397
  RENDER_OK=0
403
398
  LOCAL_CLI="$SCRIPT_DIR/packages/egregore-artifacts/bin/cli.js"
399
+ FIDELITY_ARGS=(--verify-fidelity)
400
+ if [ "$RENDER_TYPE" = "composed" ]; then
401
+ FIDELITY_ARGS+=(--source "$ABS_FILE")
402
+ fi
404
403
  if [ "${EGREGORE_USE_PUBLISHED:-0}" != "1" ] && [ -f "$LOCAL_CLI" ] && [ -d "$SCRIPT_DIR/packages/egregore-artifacts/node_modules/react" ]; then
405
- if node "$LOCAL_CLI" "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" --output "$RENDERED_HTML" >/dev/null 2>&1; then
404
+ if node "$LOCAL_CLI" "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" "${FIDELITY_ARGS[@]}" --output "$RENDERED_HTML" >/dev/null 2>"$TMPD/render-error"; then
406
405
  RENDER_OK=1
407
406
  fi
408
407
  else
409
- if npx egregore-artifacts "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" --output "$RENDERED_HTML" >/dev/null 2>&1; then
408
+ if npx -y egregore-artifacts@latest "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" "${FIDELITY_ARGS[@]}" --output "$RENDERED_HTML" >/dev/null 2>"$TMPD/render-error"; then
410
409
  RENDER_OK=1
411
410
  fi
412
411
  fi
@@ -481,9 +480,11 @@ PID_MEMORY=$!
481
480
  PUBLISH_RC=0
482
481
  if [ -z "$V1URL" ] && [ "$NO_PUBLISH" = "0" ] && [ -x "$SCRIPT_DIR/bin/publish-artifact.sh" ]; then
483
482
  AURL=$(bash "$SCRIPT_DIR/bin/publish-artifact.sh" "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" \
483
+ --verify-fidelity \
484
+ --source "$ABS_FILE" \
484
485
  --title "$TOPIC" \
485
486
  --author "$AUTHOR" \
486
- --description "$BRIEFING_LEAD" 2>/dev/null) || PUBLISH_RC=$?
487
+ --description "$BRIEFING_LEAD" 2>"$TMPD/publish-error") || PUBLISH_RC=$?
487
488
  fi
488
489
 
489
490
  PS="published"
@@ -495,6 +496,8 @@ PID_MEMORY=$!
495
496
  PS="relay-off"
496
497
  elif [ "$PUBLISH_RC" = "3" ]; then
497
498
  PS="hosting-off"
499
+ elif grep -q 'Handoff fidelity check failed' "$TMPD/render-error" "$TMPD/publish-error" 2>/dev/null; then
500
+ PS="fidelity-failed"
498
501
  else
499
502
  PS="failed"
500
503
  fi
@@ -614,6 +617,7 @@ STATUS_BITS=("saved")
614
617
  [ "$NOTIFY_STATUS" = "approval_required" ] && STATUS_BITS+=("notify approval pending")
615
618
  [ -n "$ARTIFACT_URL" ] && STATUS_BITS+=("published")
616
619
  [ "$PUBLISH_STATUS" = "relay-off" ] && STATUS_BITS+=("not published")
620
+ [ "$PUBLISH_STATUS" = "fidelity-failed" ] && STATUS_BITS+=("artifact fidelity failed")
617
621
 
618
622
  STATUS_LINE=""
619
623
  for bit in "${STATUS_BITS[@]}"; do
@@ -119,11 +119,12 @@ AUTHOR="$(md_field 'Author')"
119
119
  AUTHOR_HANDLE="$(echo "$AUTHOR" | sed 's/[[:space:]]*→.*//' | awk '{print tolower($1)}')"
120
120
  [ -z "$AUTHOR_HANDLE" ] && AUTHOR_HANDLE="unknown"
121
121
 
122
- # Recipients: try markdown header (To or For), then YAML (to or for)
122
+ # Recipients: try markdown header, then canonical/legacy YAML names.
123
123
  RECIPIENTS_RAW="$(md_field 'To')"
124
124
  [ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(md_field 'For')"
125
125
  [ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(yaml_extract 'to')"
126
126
  [ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(yaml_extract 'for')"
127
+ [ -z "$RECIPIENTS_RAW" ] && RECIPIENTS_RAW="$(yaml_extract 'addressed_to')"
127
128
  # Also extract from "From: author → recipient" pattern
128
129
  if [ -z "$RECIPIENTS_RAW" ] && echo "$AUTHOR" | grep -q '→' 2>/dev/null; then
129
130
  RECIPIENTS_RAW="$(echo "$AUTHOR" | sed 's/.*→[[:space:]]*//')"