chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:20:01 +08:00
commit e65605f012
669 changed files with 128771 additions and 0 deletions
@@ -0,0 +1,220 @@
#!/usr/bin/env bash
# cross-model-adversarial-review.sh
#
# Runs the adversarial review through a DIFFERENT model family (the "peer") in a
# separate, read-only process, and writes its findings as JSON into the run dir.
# The peer gets the same canonical adversarial brief the in-process reviewer uses
# (references/personas/adversarial-reviewer.md) so it is genuinely "the adversarial
# persona, on a different model."
#
# Usage: cross-model-adversarial-review.sh <peer: codex|claude> <base-ref> <run-dir>
# <peer> codex -> use Codex (when the host is Claude or Cursor)
# claude -> use Claude (when the host is Codex)
# <base-ref> the diff base (e.g. a merge-base SHA or branch); the peer reviews
# only `git diff <base-ref>` in the current repository
# <run-dir> an existing dir; output is written to <run-dir>/adversarial-<peer>.json
#
# Self-locates its sibling reference files via BASH_SOURCE (NOT the CWD, which is
# the user's project on every host), and derives the repo root from git. The agent
# only has to pass the three values above.
#
# NON-BLOCKING BY DESIGN: every failure logs to stderr and exits 0 without an output
# file. The cross-model pass is additive and must never fail the review; the caller
# detects success purely by the presence of <run-dir>/adversarial-<peer>.json.
set -uo pipefail
PEER="${1:-}"
BASE="${2:-}"
RUN_DIR="${3:-}"
log() { printf '[cross-model] %s\n' "$*" >&2; }
skip() { log "$*"; exit 0; } # non-blocking: announce reason, exit clean, no output
# --- validate inputs -------------------------------------------------------
case "$PEER" in codex|claude) ;; *) skip "invalid peer '${PEER:-<empty>}' (want codex|claude); skipping cross-model pass" ;; esac
[ -n "$BASE" ] || skip "no base ref given; skipping"
[ -n "$RUN_DIR" ] && [ -d "$RUN_DIR" ] || skip "run-dir '${RUN_DIR:-<empty>}' is not a directory; skipping"
command -v "$PEER" >/dev/null 2>&1 || skip "$PEER CLI not installed; skipping"
command -v jq >/dev/null 2>&1 || skip "jq not installed; skipping"
# --- self-locate skill root + canonical sibling files ----------------------
SKILL_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" || skip "cannot resolve skill root; skipping"
PERSONA="$SKILL_ROOT/references/personas/adversarial-reviewer.md"
SCHEMA="$SKILL_ROOT/references/findings-schema.json"
[ -f "$PERSONA" ] || skip "persona brief not found at $PERSONA; skipping"
[ -f "$SCHEMA" ] || skip "findings schema not found at $SCHEMA; skipping"
# --- derive repo root (read-only) ------------------------------------------
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || skip "not inside a git repository; skipping"
OUT="$RUN_DIR/adversarial-$PEER.json"
PROMPT_FILE="$(mktemp "${TMPDIR:-/tmp}/xmodel-prompt-XXXXXX")"
PEERLOG="$(mktemp "${TMPDIR:-/tmp}/xmodel-log-XXXXXX")"
trap 'rm -f "$PROMPT_FILE" "$PEERLOG"' EXIT
# --- compose the peer prompt from the canonical persona (single source) ----
# The full findings schema is embedded so BOTH peers know every required field
# (why_it_matters, confidence, evidence, routing) -- Codex gets no --output-schema
# (its strict mode rejects the permissive draft-07 schema), so the prompt is its
# only schema signal. Verified to produce complete, schema-shaped findings.
{
cat "$PERSONA"
printf '\n\n---\n\n'
printf 'This is an authorized review of the maintainer\047s own repository.\n'
printf 'Think like an attacker and a chaos engineer: find the ways this change fails in production.\n'
printf 'Return ONE JSON object and nothing else (no prose, no code fence) matching this schema:\n\n'
cat "$SCHEMA"
printf '\n\nSet the top-level "reviewer" field to "adversarial-%s".\n' "$PEER"
} > "$PROMPT_FILE"
# Per-peer diff delivery (composed below): codex fetches its own diff inside its
# read-only sandbox; claude is hard-denied shell (see below), so it gets the diff
# embedded and needs no git.
if [ "$PEER" = codex ]; then
printf '\nRun: git diff %q — review ONLY the changes in that diff, in this repository (read-only).\n' "$BASE" >> "$PROMPT_FILE"
else
{ printf '\nReview ONLY the change below (the output of `git diff %q`). You may Read repository files for context but cannot run shell commands.\n' "$BASE"
printf '\n=== BEGIN DIFF ===\n'; git -C "$REPO_ROOT" diff "$BASE"; printf '\n=== END DIFF ===\n'; } >> "$PROMPT_FILE"
fi
# --- run the peer: idle-timeout for streaming codex, hard cap for claude ----
# codex exec streams its reasoning to stdout, so a productive long run is allowed to
# continue and is killed only when its output STALLS for IDLE_SECS (the cross-model
# "second opinion" idle-timeout pattern), with HARD_SECS as an absolute backstop.
# claude's --output-format json is single-shot, so it gets a hard cap only.
#
# Orphan safety: codex runs in its OWN process group (set -m) and the watchdog reaps the
# whole group (TERM then KILL) on idle/hard -- we do NOT signal a (g)timeout wrapper for
# this, because an external kill of (g)timeout forwards only TERM (its -k escalates only
# on gtimeout's OWN expiry), so a peer that defers SIGTERM could survive `wait` and write
# $OUT after Stage 5 skipped it. claude keeps the (g)timeout wrapper: it is single-shot
# and gtimeout's own timeout (with -k) escalates to KILL correctly; perl(alarm) is the
# fallback when neither (g)timeout exists.
IDLE_SECS="${CROSS_MODEL_IDLE_SECS:-180}" # reap codex if its streamed output stalls this long
HARD_SECS="${CROSS_MODEL_HARD_SECS:-600}" # absolute ceiling (backstop) for either peer
TO_BIN="$(command -v gtimeout || command -v timeout || true)"
# Reap a backgrounded job's whole process group: TERM, then KILL after a short grace if
# anything is still alive. The grace loop tests GROUP liveness (kill -0 on the negative
# pgid), not just the leader pid -- otherwise a leader that exits while a child defers TERM
# would let reap() return before the group KILL, leaking the child. Falls back to the bare
# pid only when group signaling isn't accepted at all.
reap() {
local pid="$1" grp
if kill -TERM -- -"$pid" 2>/dev/null; then grp=1; else kill -TERM "$pid" 2>/dev/null; grp=0; fi
for _ in 1 2 3 4 5; do
if [ "$grp" = 1 ]; then kill -0 -- -"$pid" 2>/dev/null || return 0
else kill -0 "$pid" 2>/dev/null || return 0; fi
sleep 1
done
if [ "$grp" = 1 ]; then kill -KILL -- -"$pid" 2>/dev/null; else kill -KILL "$pid" 2>/dev/null; fi
}
# Run codex in its own process group; stream to PEERLOG; reap the group on idle stall or
# hard cap. This watchdog owns both bounds and the kill -- no (g)timeout wrapper to signal.
run_codex() {
local prev; case "$-" in *m*) prev=1;; *) prev=0;; esac
set -m # background job becomes a process-group leader (pgid == pid) so reap() kills the tree
# Force reasoning output on for THIS subprocess (overriding a user's hide_agent_reasoning
# = true), so the streamed reasoning keeps PEERLOG growing and gives the idle watchdog a
# liveness signal -- otherwise a long, quiet reasoning phase on a big diff could be
# misread as a stall and reaped.
# `command codex` bypasses any interactive shell function/alias a user has wrapped
# around codex, so we hit the real binary.
command codex exec - -C "$REPO_ROOT" -s read-only -o "$OUT" \
-c 'model_reasoning_effort="high"' -c 'hide_agent_reasoning=false' < "$PROMPT_FILE" > "$PEERLOG" 2>&1 &
local pid=$!
[ "$prev" = 0 ] && set +m # group is already assigned; restoring silences job-control noise
local start last=-1 lastchg now size
start="$(date +%s)"; lastchg="$start"
while kill -0 "$pid" 2>/dev/null; do
sleep 5; now="$(date +%s)"; size="$(wc -c <"$PEERLOG" 2>/dev/null || echo 0)"
[ "$size" != "$last" ] && { last="$size"; lastchg="$now"; }
if [ $(( now - lastchg )) -ge "$IDLE_SECS" ]; then
log "codex output idle ${IDLE_SECS}s; reaping peer process group"; reap "$pid"; break
fi
if [ $(( now - start )) -ge "$HARD_SECS" ]; then
log "codex exceeded hard cap ${HARD_SECS}s; reaping peer process group"; reap "$pid"; break
fi
done
wait "$pid" 2>/dev/null || true
}
log "running $PEER adversarial review against base $BASE (read-only; idle ${IDLE_SECS}s / hard ${HARD_SECS}s)"
case "$PEER" in
codex)
run_codex
# Fallback: codex's -o write is CLI-level and works under -s read-only, but if it
# ever fails to materialize, recover the same JSON from the stdout we already
# captured (codex prints the final message to stdout too). Belt-and-suspenders.
if { [ ! -s "$OUT" ] || ! jq -e . "$OUT" >/dev/null 2>&1; } && [ -s "$PEERLOG" ] && command -v python3 >/dev/null 2>&1; then
python3 - "$PEERLOG" "$OUT" <<'PY' 2>/dev/null && [ -s "$OUT" ] && log "recovered codex JSON from stdout (-o file unavailable)"
import sys, json
txt = open(sys.argv[1], encoding="utf-8", errors="replace").read()
best, depth, start = None, 0, None
for i, ch in enumerate(txt):
if ch == '{':
if depth == 0: start = i
depth += 1
elif ch == '}' and depth > 0:
depth -= 1
if depth == 0 and start is not None:
try:
obj = json.loads(txt[start:i+1])
if isinstance(obj, dict) and "findings" in obj: best = obj
except Exception: pass
if best is not None: open(sys.argv[2], "w").write(json.dumps(best))
PY
fi
;;
claude)
# Single-shot output -> hard cap only. Disallowed tools as SEPARATE variadic args
# (unambiguous; a single quoted "Edit Write NotebookEdit" is risky since tool names
# can contain spaces). We deny the built-in mutators (Edit/Write/NotebookEdit/Bash)
# AND `mcp__*` (a user's pre-approved MCP write/deploy tools would otherwise run under
# dontAsk) AND `Task` (a subagent would bypass this deny list) -- so the peer stays
# read-only even with MCP servers configured. claude can't write a file under those
# perms, so it emits the JSON envelope on stdout (captured to PEERLOG); we extract it.
if [ -n "$TO_BIN" ]; then
"$TO_BIN" -k 10 "$HARD_SECS" claude -p --model opus --permission-mode dontAsk \
--disallowedTools Edit Write NotebookEdit Bash Task 'mcp__*' --max-turns 15 --no-session-persistence \
--json-schema "$(cat "$SCHEMA")" --output-format json \
< "$PROMPT_FILE" > "$PEERLOG" 2>/dev/null \
|| log "claude exited non-zero or timed out"
else
perl -e 'alarm shift; exec @ARGV' "$HARD_SECS" claude -p --model opus --permission-mode dontAsk \
--disallowedTools Edit Write NotebookEdit Bash Task 'mcp__*' --max-turns 15 --no-session-persistence \
--json-schema "$(cat "$SCHEMA")" --output-format json \
< "$PROMPT_FILE" > "$PEERLOG" 2>/dev/null \
|| log "claude exited non-zero or timed out"
fi
jq -e '.structured_output' "$PEERLOG" > "$OUT" 2>/dev/null \
|| jq -r '.result // empty' "$PEERLOG" | jq -e '.' > "$OUT" 2>/dev/null \
|| { log "could not parse Claude output"; rm -f "$OUT"; }
;;
esac
# --- normalize the reviewer name -------------------------------------------
# The persona's example JSON uses reviewer:"adversarial"; if the peer echoed that
# instead of "adversarial-<peer>", Stage 5 would fold it as the in-process reviewer
# and lose the cross-model agreement signal. Force the distinct name.
if [ -s "$OUT" ]; then
_norm="$(mktemp "${TMPDIR:-/tmp}/xmodel-norm-XXXXXX")"
# Force the distinct reviewer name AND satisfy Stage 5's full top-level contract
# (reviewer string + findings/residual_risks/testing_gaps arrays). Backfill the two
# soft arrays if the peer omitted them; drop the return entirely if findings is not
# an array (empty output -> the validation below removes the file -> clean skip).
if jq --arg r "adversarial-$PEER" \
'if (.findings|type)=="array" then {reviewer:$r, findings, residual_risks:(.residual_risks // []), testing_gaps:(.testing_gaps // [])} else empty end' \
"$OUT" > "$_norm" 2>/dev/null; then mv "$_norm" "$OUT"; else rm -f "$_norm"; fi
fi
# --- validate the output against the Stage 5 reviewer-return contract -------
if [ -s "$OUT" ] && jq -e '(.reviewer|type=="string") and (.findings|type=="array") and (.residual_risks|type=="array") and (.testing_gaps|type=="array")' "$OUT" >/dev/null 2>&1; then
n="$(jq '.findings | length' "$OUT" 2>/dev/null || echo '?')"
log "wrote $n finding(s) to $OUT (reviewer adversarial-$PEER)"
else
log "$PEER produced no usable schema-shaped output; skipping fold-in"
rm -f "$OUT"
fi
exit 0
@@ -0,0 +1,418 @@
#!/usr/bin/env python3
"""Shared repo-grounding project-profile cache: deterministic get/put.
This helper owns the *deterministic* cache I/O for the question-agnostic
project profile that repo-grounding skills reuse. The non-deterministic
derivation (reading manifests, summarizing conventions) is done by the
`repo-profiler` persona only on a miss — never here.
Usage:
python3 repo-profile-cache.py get
python3 repo-profile-cache.py put <profile-json-file>
`get` prints exactly one of:
HIT\\n<profile-json> a valid entry exists for the current repo state;
the profile JSON follows on subsequent lines
MISS\\n<write-path> git repo, no valid entry — caller derives the
profile and calls `put <write-path-or-any-file>`
NO-CACHE no git repo or no writable cache — caller derives
the profile fresh and skips `put`
`put <file>` reads the profile JSON from <file>, wraps it with a validity
stamp, and writes it atomically to the computed cache path. Prints the path
on success, `NO-CACHE` when the repo/cache is unavailable.
Cache path:
/tmp/compound-engineering/repo-profile/<root-sha>/<head-sha>.json
root-sha = lexicographically-first `git rev-list --max-parents=0 HEAD`
(deterministic even for multi-root histories) — the repo identity,
shared across worktrees and clones.
head-sha = `git rev-parse HEAD` — the working state.
Validity (HIT) requires ALL of:
- the cache file exists and parses as JSON,
- stored `head_sha` == current HEAD,
- stored `profile_schema_version` == PROFILE_SCHEMA_VERSION,
- no profile-input path is dirty or newly-added per `git status --porcelain`
(the schema-derived superset in `is_profile_input`, which also catches
untracked `??` files — a newly-added manifest or AGENTS.md must invalidate).
Cardinal rule: this cache is an optimization, never a correctness dependency.
Every failure mode (not a git repo, unreadable/malformed cache, no writable
/tmp, git errors) degrades to NO-CACHE/MISS and exits 0 — it never raises and
never serves a profile it cannot prove fresh.
Pure stdlib. No third-party dependencies.
"""
import json
import os
import subprocess
import sys
import tempfile
from datetime import datetime, timezone
# Bump when the profile schema changes so a newer reader never reuses an
# entry written under an older (narrower) schema.
PROFILE_SCHEMA_VERSION = "1"
CACHE_ROOT = "/tmp/compound-engineering/repo-profile"
# --- Profile-input set (the schema-derived superset, per the plan's R3) -------
# Any change to one of these — including a NEW untracked file — must invalidate
# the cached profile. Conservative by design: over-invalidating costs a
# re-derive; under-invalidating serves a stale profile (a cardinal-rule break).
# Dependency manifests + lockfiles. Matched by basename at ANY depth so a
# monorepo workspace's manifest also invalidates. The profiler derives
# stack/deps for ANY language, so this list must span ecosystems, not just JS —
# an omitted manifest means a dirty dep bump at unchanged HEAD serves a stale
# profile (a cardinal-rule break).
_MANIFEST_LOCKFILE = {
# JavaScript / TypeScript / Deno
"package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml",
"pnpm-workspace.yaml", "bun.lock", "bun.lockb", "npm-shrinkwrap.json",
"deno.json", "deno.jsonc", "deno.lock",
# Monorepo / workspace orchestrators
"nx.json", "lerna.json", "turbo.json", "rush.json",
# Go (incl. workspaces)
"go.mod", "go.sum", "go.work", "go.work.sum",
# Rust
"Cargo.toml", "Cargo.lock",
# Ruby
"Gemfile", "Gemfile.lock", "gems.rb", "gems.locked",
# Python
"pyproject.toml", "poetry.lock", "Pipfile", "Pipfile.lock",
"requirements.txt", "setup.py", "setup.cfg",
"uv.lock", "pdm.lock", "environment.yml", "environment.yaml",
# PHP
"composer.json", "composer.lock",
# JVM (Maven / Gradle incl. version catalogs)
"pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle",
"settings.gradle.kts", "libs.versions.toml", "build.sbt",
# Elixir / Dart
"mix.exs", "mix.lock", "pubspec.yaml", "pubspec.lock",
# Swift / iOS (a live target for this project)
"Package.swift", "Package.resolved", "Podfile", "Podfile.lock",
"Cartfile", "Cartfile.resolved",
# .NET
"packages.config", "Directory.Packages.props", "Directory.Build.props",
"paket.dependencies", "paket.lock",
# C / C++
"CMakeLists.txt", "conanfile.txt", "conanfile.py", "vcpkg.json",
# Haskell
"stack.yaml", "stack.yaml.lock", "cabal.project",
}
# Project-file extensions whose presence or version edit changes the stack
# profile. Suffix-matched at any depth (e.g. Foo.csproj, App.sln).
_PROJECT_FILE_SUFFIXES = (
".csproj", ".fsproj", ".vbproj", ".sln", ".cabal", ".tf", ".tfvars",
)
_LICENSE = {"LICENSE", "LICENSE.md", "LICENSE.txt", "LICENCE", "COPYING"}
# Topology / deployment sources. Basename match at any depth — these determine
# the derived deployment model (monolith / multi-service / serverless).
_TOPOLOGY = {
"Dockerfile", "Containerfile",
"docker-compose.yml", "docker-compose.yaml",
"vercel.json", "netlify.toml", "fly.toml", "render.yaml",
"serverless.yml", "serverless.yaml", "app.yaml", "Procfile",
# IaC descriptors that define the deployment topology.
"Pulumi.yaml", "Pulumi.yml", "Chart.yaml",
# CI descriptors outside .github/workflows/ (that prefix is handled below).
".gitlab-ci.yml", "Jenkinsfile", "azure-pipelines.yml",
}
# Path prefixes whose contents shape the profile (conventions / CI / deploy).
_INPUT_PREFIXES = (
".cursor/", ".github/workflows/", ".circleci/",
"terraform/", "k8s/", "kubernetes/",
)
# Root-level instruction/doc files cached in the profile. Matched ONLY at the
# repo root — subdirectory-scoped instruction files (e.g. nested CLAUDE.md /
# AGENTS.md) are NOT cached; consumers re-glob those fresh, so a subdir change
# must not invalidate the root profile.
_ROOT_DOCS = {
"AGENTS.md", "CLAUDE.md", "GEMINI.md",
"CONCEPTS.md", "STRATEGY.md",
"ARCHITECTURE.md", "README.md", "CONTRIBUTING.md",
".cursorrules", # legacy root-level Cursor rules (the profiler reads it)
}
# Runtime / tool version selectors that pin a language or tool version OUTSIDE
# the manifests (the profiler reads these for stack versions). Basename match.
_VERSION_SELECTORS = {
".nvmrc", ".node-version", ".python-version", ".ruby-version",
".java-version", ".go-version", ".terraform-version",
".tool-versions", "mise.toml", ".mise.toml", ".sdkmanrc",
}
def is_profile_input(path: str) -> bool:
"""True when a changed path is one the cached profile derives from.
Deliberately a conservative superset: anything plausibly feeding the
stack/deps/topology/conventions profile invalidates. Over-matching costs a
re-derive; under-matching serves a stale profile (a cardinal-rule break).
"""
base = os.path.basename(path)
if (
base in _MANIFEST_LOCKFILE
or base in _LICENSE
or base in _TOPOLOGY
or base in _VERSION_SELECTORS
):
return True
if base.endswith(_PROJECT_FILE_SUFFIXES):
return True
if "/" not in path and base in _ROOT_DOCS:
return True
if path.startswith(_INPUT_PREFIXES):
return True
return False
def git(*args: str) -> "str | None":
"""Run a git command; return stripped stdout, or None on any failure."""
try:
result = subprocess.run(
["git", *args], capture_output=True, text=True, check=False
)
except OSError:
return None
if result.returncode != 0:
return None
return result.stdout.strip()
def root_sha() -> "str | None":
out = git("rev-list", "--max-parents=0", "HEAD")
if not out:
return None
# Multi-root histories print several SHAs; pick a deterministic one.
return sorted(out.split("\n"))[0]
def changed_paths() -> "list[str] | None":
"""Paths from `git status --porcelain`, or None if it could not run.
Includes untracked (`??`) entries so a newly-added profile input is seen.
None signals "could not determine cleanliness" — the caller treats that
conservatively as a miss rather than serving an unverified profile.
"""
# --untracked-files=all lists individual untracked files; without it git
# collapses a fully-untracked new directory to a single `?? dir/` entry,
# which would hide a newly-added manifest inside it.
#
# Call subprocess directly rather than via git(): porcelain's status
# columns include a significant LEADING space (e.g. " M path"), and
# git()'s .strip() would eat it and shift the path slice.
try:
result = subprocess.run(
["git", "status", "--porcelain", "--untracked-files=all"],
capture_output=True,
text=True,
check=False,
)
except OSError:
return None
if result.returncode != 0:
return None
def clean(token: str) -> str:
token = token.strip()
# git quotes paths containing special characters.
if len(token) >= 2 and token[0] == '"' and token[-1] == '"':
token = token[1:-1]
return token
paths: list[str] = []
for line in result.stdout.split("\n"):
if not line.strip():
continue
rest = line[3:]
# Rename/copy entries are "old -> new"; BOTH endpoints changed. A
# profile input renamed *away* (e.g. `package.json -> pkg.json`) must
# still invalidate, so keep the source path, not just the destination.
if " -> " in rest:
for token in rest.split(" -> ", 1):
p = clean(token)
if p:
paths.append(p)
continue
p = clean(rest)
if p:
paths.append(p)
return paths
def cache_path(root: str, head: str) -> str:
return os.path.join(CACHE_ROOT, root, f"{head}.json")
def resolve_keys() -> "tuple[str, str] | None":
"""The (root-sha, head-sha) cache key, or None if not a usable git repo."""
root = root_sha()
head = git("rev-parse", "HEAD")
if not root or not head:
return None
return root, head
_PROFILE_KEYS = ("stack", "dependencies", "topology", "conventions", "vocabulary")
def is_valid_profile(profile: object) -> bool:
"""A profile must be an object carrying every expected top-level key. This
rejects a profiler failure that still returned JSON — a wrapper/error object
or a partial result — which would otherwise be cached and served as a HIT,
leaving consumers to skip fresh derivation and read missing fields from a
broken object."""
return isinstance(profile, dict) and all(k in profile for k in _PROFILE_KEYS)
def do_get() -> int:
keys = resolve_keys()
if keys is None:
print("NO-CACHE")
return 0
root, head = keys
path = cache_path(root, head)
def miss() -> int:
print("MISS")
print(path)
return 0
# A missing file raises FileNotFoundError (an OSError) and degrades to the
# same MISS, so no separate existence check is needed.
try:
with open(path) as f:
# /tmp is world-shared, so reject a cache file not owned by us: a
# co-tenant could plant an entry that passes the gates below and
# feed attacker-controlled text into the agent as the "profile"
# (indirect prompt injection). Skip where geteuid is unavailable
# (non-POSIX), where this shared-tmp threat does not apply.
geteuid = getattr(os, "geteuid", None)
if geteuid is not None and os.fstat(f.fileno()).st_uid != geteuid():
return miss()
doc = json.load(f)
except (OSError, ValueError):
return miss()
profile = doc.get("profile") if isinstance(doc, dict) else None
if (
not isinstance(doc, dict)
or doc.get("head_sha") != head
or doc.get("profile_schema_version") != PROFILE_SCHEMA_VERSION
or not is_valid_profile(profile)
):
return miss()
changed = changed_paths()
# Could not determine cleanliness, or a profile input changed/was added.
if changed is None or any(is_profile_input(p) for p in changed):
return miss()
print("HIT")
print(json.dumps(profile))
return 0
def do_put(profile_file: str) -> int:
keys = resolve_keys()
if keys is None:
print("NO-CACHE")
return 0
root, head = keys
try:
with open(profile_file) as f:
profile = json.load(f)
except (OSError, ValueError) as exc:
sys.stderr.write(f"repo-profile-cache: cannot read profile: {exc}\n")
print("NO-CACHE") # nothing persisted; keep the stdout contract
return 0 # degrade — never block the caller
# Shape guard: the profile must be an object carrying the expected top-level
# keys. A misbehaving profiler that returns garbage JSON (`{}`, `"oops"`,
# `[]`, `42`) or a partial/error object must not be cached and then served
# to every skill as the agnostic profile. Reject it (the caller already has
# its own derived profile for this run; the next run re-derives).
if not is_valid_profile(profile):
sys.stderr.write(
"repo-profile-cache: profile is not a valid profile object; not caching\n"
)
print("NO-CACHE")
return 0
# Do not cache a profile derived from a DIRTY tree: it reflects uncommitted
# edits to profile inputs, yet it would be stored under the clean HEAD key
# and served as a HIT after those edits are reverted (same HEAD, clean tree)
# — stale. Only persist a profile that matches the committed HEAD.
changed = changed_paths()
if changed is None or any(is_profile_input(p) for p in changed):
sys.stderr.write(
"repo-profile-cache: profile inputs are dirty; not caching\n"
)
print("NO-CACHE")
return 0
doc = {
"profile_schema_version": PROFILE_SCHEMA_VERSION,
"root_sha": root,
"head_sha": head,
"built_at": datetime.now(timezone.utc).isoformat(),
"profile": profile,
}
path = cache_path(root, head)
try:
os.makedirs(os.path.dirname(path), exist_ok=True)
# Atomic write: temp file in the same dir + os.replace (atomic on
# POSIX) so a concurrent reader never sees a torn JSON.
fd, tmp = tempfile.mkstemp(
dir=os.path.dirname(path), prefix=".tmp-", suffix=".json"
)
try:
with os.fdopen(fd, "w") as f:
json.dump(doc, f)
os.replace(tmp, path)
except BaseException:
try:
os.unlink(tmp)
except OSError:
pass
raise
except Exception as exc: # never block the caller, whatever the failure
sys.stderr.write(f"repo-profile-cache: cannot write cache: {exc}\n")
print("NO-CACHE")
return 0
print(path)
return 0
def usage() -> int:
sys.stderr.write(
"usage: repo-profile-cache.py get | put <profile-json-file>\n"
)
return 2
def main(argv: "list[str]") -> int:
if len(argv) < 2:
return usage()
cmd = argv[1]
if cmd == "get":
return do_get()
if cmd == "put":
if len(argv) != 3:
return usage()
return do_put(argv[2])
return usage()
if __name__ == "__main__":
sys.exit(main(sys.argv))