@rallycry/conveyor-agent 10.3.0 → 10.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -183,6 +183,13 @@ done
183
183
  # as the literal string "null", which then flows into git URLs and env vars.
184
184
  export CONVEYOR_TASK_TOKEN=$(echo "${BOOTSTRAP_JSON}" | jq -r '.sessionJwt // empty')
185
185
  export CONVEYOR_GITHUB_TOKEN=$(echo "${BOOTSTRAP_JSON}" | jq -r '.githubToken // empty')
186
+ # gh CLI auth (baked into the base image). GH_TOKEN (not GITHUB_TOKEN — that
187
+ # name leaks into too many third-party tools) points gh at the same role-scoped
188
+ # installation token git uses. Caveat: installation tokens live ~1h and the
189
+ # agent's refresh path updates the git remote URL, not this env — a long-lived
190
+ # session's gh calls can 401 after expiry; agents should treat that as
191
+ # "re-check via MCP tools", not an auth bug to debug.
192
+ export GH_TOKEN="${CONVEYOR_GITHUB_TOKEN}"
186
193
  REPO_OWNER=$(echo "${BOOTSTRAP_JSON}" | jq -r '.gitPlan.repoOwner // empty')
187
194
  REPO_NAME=$(echo "${BOOTSTRAP_JSON}" | jq -r '.gitPlan.repoName // empty')
188
195
  BRANCH=$(echo "${BOOTSTRAP_JSON}" | jq -r '.gitPlan.branch // empty')
@@ -318,6 +325,14 @@ seed_claude_json() {
318
325
  # back to PROJECT_ID from the pod env.
319
326
  USER_HOME_MOUNT="/mnt/conveyor-users"
320
327
  USER_HOME_PROJECT_ID="${CONVEYOR_PROJECT_ID_FROM_BUNDLE:-${PROJECT_ID:-}}"
328
+
329
+ # Bake steps that run as root while ENV HOME points at /home/conveyor can leave
330
+ # root-owned $HOME entries (a uv install receipt in ~/.config once bricked every
331
+ # pod boot fleet-wide); reclaim them before the symlink work below so a baked
332
+ # ownership slip cannot EACCES the entrypoint under `set -e`. Runs before any
333
+ # symlinks exist, so there is nothing to follow into the FUSE mount.
334
+ sudo -n chown -R conveyor:conveyor /home/conveyor/.config /home/conveyor/.local 2>/dev/null || true
335
+
321
336
  if [ -n "${CONVEYOR_USER_ID}" ] && [ -n "${USER_HOME_PROJECT_ID}" ] && [ -d "${USER_HOME_MOUNT}" ]; then
322
337
  USER_HOME_ROOT="${USER_HOME_MOUNT}/users/${CONVEYOR_USER_ID}/${USER_HOME_PROJECT_ID}"
323
338
  echo "[pool] Linking Claude state to ${USER_HOME_ROOT}"
@@ -419,8 +434,69 @@ bind_graphify_bundle() {
419
434
  echo "[pool] Bound graphify bundle '${CONVEYOR_GRAPHIFY_SLUG}' into ${target_dir}"
420
435
  }
421
436
 
437
+ # ── Link shared Grimoire skills into the repo's project skill dir ──
438
+ # The prebake links grimoire skills into the BAKED image's ~/.claude/skills,
439
+ # but the user-home wiring above replaces /home/conveyor/.claude with the
440
+ # per-user GCS mount on every mounted pod boot — wiping those links before any
441
+ # agent runs (observed fleet-wide 2026-07-10: pods saw only repo-tracked
442
+ # skills, none of the shared rc-* set). Link repo-locally instead: Claude
443
+ # loads project skills from <workspace>/.claude/skills regardless of where
444
+ # $HOME points, the links are pod-local (no writes to the shared mount, no
445
+ # cross-pod races), and .gitignore covers them so `git status` stays clean.
446
+ # Runs inside mark_git_ready — after checkout is final, before the agent may
447
+ # spawn Claude — alongside the graphify bundle bind. Both helpers are
448
+ # best-effort: a grimoire failure must never block the git-ready gate.
449
+ ensure_grimoire_submodule() {
450
+ local workspace="${CONVEYOR_WORKSPACE:-/workspaces/repo}"
451
+ [ -f "${workspace}/.gitmodules" ] || return 0
452
+ git -C "${workspace}" config --file .gitmodules --get-regexp 'submodule\..*\.path' 2>/dev/null \
453
+ | grep -q '\.claude/grimoire$' || return 0
454
+ # Already materialized (pod-image bake ran conveyor-prebake successfully).
455
+ [ -d "${workspace}/.claude/grimoire/skills" ] && return 0
456
+ if [ -z "${CONVEYOR_GITHUB_TOKEN:-}" ]; then
457
+ echo "[pool] WARN: grimoire submodule absent and no token to fetch it"
458
+ return 0
459
+ fi
460
+ # insteadOf injects the installation token for the submodule's https URL the
461
+ # same way sync_task_branch_to_repo authenticates the main repo remote.
462
+ if _grim_out=$(git -C "${workspace}" \
463
+ -c url."https://x-access-token:${CONVEYOR_GITHUB_TOKEN}@github.com/".insteadOf="https://github.com/" \
464
+ submodule update --init .claude/grimoire 2>&1); then
465
+ echo "[pool] Initialized grimoire submodule"
466
+ else
467
+ echo "[pool] WARN: grimoire submodule init failed (baked pods may lack rc-* skills): ${_grim_out}"
468
+ fi
469
+ return 0
470
+ }
471
+
472
+ link_grimoire_skills() {
473
+ local workspace="${CONVEYOR_WORKSPACE:-/workspaces/repo}"
474
+ local source_dir="${workspace}/.claude/grimoire/skills"
475
+ local target_dir="${workspace}/.claude/skills"
476
+ [ -d "${source_dir}" ] || return 0
477
+ if ! mkdir -p "${target_dir}" 2>/dev/null; then
478
+ echo "[pool] WARN: unable to create ${target_dir}; skipping grimoire skill links"
479
+ return 0
480
+ fi
481
+ local skill_dir name linked=0
482
+ for skill_dir in "${source_dir}"/*/; do
483
+ [ -d "${skill_dir}" ] || continue
484
+ name="$(basename "${skill_dir}")"
485
+ # Never clobber a real (repo-tracked) skill dir with a link.
486
+ if [ -e "${target_dir}/${name}" ] && [ ! -L "${target_dir}/${name}" ]; then
487
+ echo "[pool] WARN: ${target_dir}/${name} exists and is not a symlink; skipping"
488
+ continue
489
+ fi
490
+ ln -sfn "../grimoire/skills/${name}" "${target_dir}/${name}" 2>/dev/null && linked=$((linked + 1))
491
+ done
492
+ echo "[pool] Linked ${linked} grimoire skills into ${target_dir}"
493
+ return 0
494
+ }
495
+
422
496
  mark_git_ready() {
423
497
  bind_graphify_bundle
498
+ ensure_grimoire_submodule
499
+ link_grimoire_skills
424
500
  : > "$GIT_READY_MARKER"
425
501
  }
426
502