Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
13bfa12
feat(multigraph): projections + schema-aware loader + stable edge ide…
narcolepticsun-cyber May 22, 2026
b7e05f7
chore: no-waiver lint/type/security cleanup + upstream v8 rebase
narcolepticsun-cyber May 27, 2026
1d4c5d6
feat(multigraph): PR 4A dedup/remap contract for keyed parallel edges
narcolepticsun-cyber May 27, 2026
32b7eb4
feat(multigraph): PR 4B algorithm/report safety via explicit projections
narcolepticsun-cyber May 27, 2026
cab1964
style: format rebased multigraph stack
narcolepticsun-cyber May 29, 2026
0c5b402
feat(multigraph): PR 5 query/path/explain/MCP surfaces show bundled r…
narcolepticsun-cyber May 29, 2026
6f9bef2
test: isolate backend-detection tests from ambient API keys
narcolepticsun-cyber May 29, 2026
a1811e6
style: format PR 5 surfaces
narcolepticsun-cyber May 29, 2026
2e84cc3
fix: fall back when process pool is unavailable
narcolepticsun-cyber May 29, 2026
ae8b472
chore: keep private test tooling out of public metadata
narcolepticsun-cyber May 29, 2026
014b673
fix: restore lint-clean style in test_languages.py after upstream rebase
narcolepticsun-cyber May 30, 2026
52df584
feat(multigraph): PR 6 export/visualization surfaces preserve paralle…
narcolepticsun-cyber May 30, 2026
50c0cd3
fix(multigraph): keep canvas edge ids globally unique
narcolepticsun-cyber May 30, 2026
02e2a76
feat(multigraph): PR 7 watch/cache/incremental update preserve keyed …
narcolepticsun-cyber May 30, 2026
5c64c92
fix(multigraph): keep watch rebuild links idempotent
narcolepticsun-cyber May 30, 2026
5ff5f54
feat(multigraph): PR 8 global graph/merge/recovery preserve keyed par…
narcolepticsun-cyber May 30, 2026
96dc491
fix(multigraph): harden PR 8 recovery paths
narcolepticsun-cyber May 30, 2026
19b00e7
feat(multigraph): PR 9 public --multigraph CLI, sticky profile, docs
narcolepticsun-cyber May 30, 2026
814718f
fix(multigraph): preserve no-cluster sticky extracts
narcolepticsun-cyber May 30, 2026
526f848
fix(multigraph): preserve graph on empty rebuilds
narcolepticsun-cyber May 31, 2026
322c074
fix(multigraph): preserve saved graph during stateful dedup
narcolepticsun-cyber May 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 252 additions & 0 deletions .AUDIT/copilot-local-review.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
#!/usr/bin/env bash
set -uo pipefail
# Owner: Codex
#
# Private local gate: run GitHub Copilot CLI against the staged diff before a
# local commit. This is an early-warning review, not a replacement for the
# origin/upstream PR review gate.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$ROOT" || exit 2

if [[ -f "$ROOT/.venv/bin/activate" ]]; then
# shellcheck disable=SC1091
source "$ROOT/.venv/bin/activate"
fi

if [[ "${AUDIT_PRIVATE_GUARD:-1}" != "0" && -x "$SCRIPT_DIR/private-guard.sh" ]]; then
"$SCRIPT_DIR/private-guard.sh" --quiet || exit "$?"
fi

usage() {
cat <<'EOF'
Usage: .AUDIT/copilot-local-review.sh [--cached|--worktree|--base <ref>] [--advisory] [--max-diff-bytes <n>]

Runs GitHub Copilot CLI against a local diff and blocks when Copilot reports
actionable findings.

Modes:
--cached review staged changes; default and intended for pre-commit
--worktree review unstaged worktree changes
--base <ref> review changes from merge-base(<ref>, HEAD) to HEAD
--advisory always exit 0 after saving/reporting review output

Environment:
AUDIT_COPILOT_MAX_DIFF_BYTES default 120000; local review blocks above this
AUDIT_COPILOT_REPORT_DIR default .AUDIT/reports

Output contract:
Copilot must emit exactly one decision line:
LOCAL_COPILOT_REVIEW_DECISION: PASS
LOCAL_COPILOT_REVIEW_DECISION: BLOCK

PASS means no actionable correctness/security/regression/test issue was found.
BLOCK or ambiguous output stops the commit gate.
EOF
}

mode="cached"
base_ref=""
advisory=0
max_diff_bytes="${AUDIT_COPILOT_MAX_DIFF_BYTES:-2000000}"

while [[ $# -gt 0 ]]; do
case "$1" in
--cached)
mode="cached"
;;
--worktree)
mode="worktree"
;;
--base)
if [[ $# -lt 2 ]]; then
echo "[copilot-local-review] USAGE_ERROR: --base requires a ref" >&2
exit 2
fi
mode="base"
base_ref="$2"
shift
;;
--base=*)
mode="base"
base_ref="${1#--base=}"
;;
--advisory)
advisory=1
;;
--max-diff-bytes)
if [[ $# -lt 2 ]]; then
echo "[copilot-local-review] USAGE_ERROR: --max-diff-bytes requires a number" >&2
exit 2
fi
max_diff_bytes="$2"
shift
;;
--max-diff-bytes=*)
max_diff_bytes="${1#--max-diff-bytes=}"
;;
--help|-h)
usage
exit 0
;;
*)
echo "[copilot-local-review] USAGE_ERROR: unknown option '$1'" >&2
usage >&2
exit 2
;;
esac
shift
done

case "$max_diff_bytes" in
''|*[!0-9]*)
echo "[copilot-local-review] USAGE_ERROR: max diff bytes must be a non-negative integer" >&2
exit 2
;;
esac

if ! command -v copilot >/dev/null 2>&1; then
echo "[copilot-local-review] BLOCKED: GitHub Copilot CLI is not installed or not on PATH" >&2
echo "[copilot-local-review] Install/authenticate Copilot CLI or set AUDIT_SKIP_LOCAL_COPILOT=1 for an explicit bypass." >&2
exit 1
fi

diff_file="$(mktemp)"
stat_file="$(mktemp)"
trap 'rm -f "$diff_file" "$stat_file"' EXIT

if [[ "$mode" == "cached" ]]; then
git diff --cached --stat >"$stat_file"
git diff --cached --no-ext-diff --binary --unified=80 >"$diff_file"
elif [[ "$mode" == "worktree" ]]; then
git diff --stat >"$stat_file"
git diff --no-ext-diff --binary --unified=80 >"$diff_file"
else
merge_base="$(git merge-base HEAD "$base_ref")" || {
echo "[copilot-local-review] GIT_CONTEXT_ERROR: could not merge-base HEAD and $base_ref" >&2
exit 2
}
git diff --stat "$merge_base..HEAD" >"$stat_file"
git diff --no-ext-diff --binary --unified=80 "$merge_base..HEAD" >"$diff_file"
fi

if [[ ! -s "$diff_file" ]]; then
echo "[copilot-local-review] clean: no diff to review for mode=$mode"
exit 0
fi

if grep -Eq '^(Binary files |GIT binary patch)' "$diff_file"; then
echo "[copilot-local-review] BLOCKED: binary diff present; Copilot local text review cannot inspect it reliably" >&2
exit 1
fi

diff_bytes="$(wc -c <"$diff_file" | tr -d '[:space:]')"
if (( max_diff_bytes > 0 && diff_bytes > max_diff_bytes )); then
echo "[copilot-local-review] BLOCKED: diff is ${diff_bytes} bytes, above local review limit ${max_diff_bytes}" >&2
echo "[copilot-local-review] Split the commit or use origin PR review as the authoritative review surface." >&2
exit 1
fi

report_dir="${AUDIT_COPILOT_REPORT_DIR:-$SCRIPT_DIR/reports}"
mkdir -p "$report_dir"
report_file="$report_dir/$(date +%Y%m%d-%H%M%S)-copilot-local-review.md"

prompt_file="$(mktemp)"
trap 'rm -f "$diff_file" "$stat_file" "$prompt_file"' EXIT

{
cat <<'EOF'
You are GitHub Copilot reviewing a local staged diff before commit.

Review only the supplied diff. Do not edit files. Do not run tools. Do not ask
questions. Focus on correctness, security, data loss, regression risk, broken
tests, missing tests for changed behavior, and user-visible behavior. Ignore
pure style unless it can create functional risk.

Your response MUST include exactly one decision line:

LOCAL_COPILOT_REVIEW_DECISION: PASS

or:

LOCAL_COPILOT_REVIEW_DECISION: BLOCK

Use PASS only if you find no actionable issue. Use BLOCK if there is any
actionable issue or if the diff is too incomplete to review safely.

After the decision line, provide concise findings with file/path references
when blocking. If passing, provide a brief explanation of the risk areas you
checked.

Diff stat:
EOF
cat "$stat_file"
printf '\nDiff:\n```diff\n'
cat "$diff_file"
printf '\n```\n'
} >"$prompt_file"

echo "[copilot-local-review] invoking Copilot CLI mode=$mode diff_bytes=$diff_bytes"
set +e
copilot_output="$(
copilot \
-p "$(cat "$prompt_file")" \
--disable-builtin-mcps \
--disallow-temp-dir \
--no-color \
--output-format text 2>&1
)"
copilot_rc=$?
set -e

{
echo "# Local Copilot Review"
echo
echo "- Mode: \`$mode\`"
[[ -n "$base_ref" ]] && echo "- Base: \`$base_ref\`"
echo "- Diff bytes: \`$diff_bytes\`"
echo "- Copilot exit code: \`$copilot_rc\`"
echo "- Started: \`$(date -u +%Y-%m-%dT%H:%M:%SZ)\`"
echo
echo "## Diff Stat"
echo
echo '```text'
cat "$stat_file"
echo '```'
echo
echo "## Copilot Output"
echo
echo '```text'
printf '%s\n' "$copilot_output"
echo '```'
} >"$report_file"

printf '%s\n' "$copilot_output"
echo "[copilot-local-review] report=$report_file"

if (( advisory == 1 )); then
echo "[copilot-local-review] advisory mode: not blocking"
exit 0
fi

if (( copilot_rc != 0 )); then
echo "[copilot-local-review] BLOCKED: Copilot CLI exited $copilot_rc" >&2
exit 1
fi

pass_count="$(grep -c '^LOCAL_COPILOT_REVIEW_DECISION: PASS$' "$report_file" || true)"
block_count="$(grep -c '^LOCAL_COPILOT_REVIEW_DECISION: BLOCK$' "$report_file" || true)"

if [[ "$pass_count" == "1" && "$block_count" == "0" ]]; then
echo "[copilot-local-review] clean: Copilot returned PASS"
exit 0
fi

if [[ "$block_count" != "0" ]]; then
echo "[copilot-local-review] BLOCKED: Copilot returned BLOCK" >&2
exit 1
fi

echo "[copilot-local-review] BLOCKED: Copilot output did not contain the required PASS decision line" >&2
exit 1
32 changes: 32 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## graphify

This project has a graphify knowledge graph at graphify-out/.

Rules:
- Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
- After modifying code files in this session, run `graphify update .` to keep the graph current (AST-only, no API cost)

## Remote and Rebase Policy

Rules:
- `origin` is the active development target unless the user explicitly says otherwise.
- `upstream` is read-only/reference by default. Do not open, update, or describe work as ready for upstream contribution unless the user explicitly reopens that path.
- Still follow upstream Graphify closely: before planning implementation slices or rebases, fetch `origin` and `upstream`, verify current branch/HEAD/ahead-behind state, and compare live upstream changes that touch the same files.
- Prefer rebasing/pulling useful upstream changes into the local/origin development branch when doing so preserves the Graphify direction and reduces future drift.
- Upstream synchronization is preauthorized only for this Graphify/vampyre checkout (`/Users/jonathandirks/Development/vampyre`). Do not generalize this permission to any other repo or project. In this checkout, do not ask for human approval before stashing local work, fetching `origin`/`upstream`, comparing upstream changes, rebasing onto useful upstream updates, resolving conflicts by comparison, reapplying the stash, and rerunning verification.
- If upstream changes are harmful, irrelevant, or incompatible with the local Graphify direction, skip them only after comparing the change and recording the reason in the handoff.
- Helper scripts for stash/fetch/rebase workflows may be created only in local-only paths that are excluded from git, such as `.agent-local/`; do not add those helper scripts to GitHub-bound history.
- Absolute conflict rule: never resolve merge, rebase, cherry-pick, or generated-artifact conflicts by blindly choosing ours/theirs or by assuming the local branch is always correct. Always inspect and compare both sides, identify the intended behavior on each side, preserve useful upstream behavior unless it is deliberately incompatible with the local plan, and document the resolution in the handoff or commit notes.
- If conflict behavior is unclear after comparing both sides and the relevant tests/docs, stop and ask before resolving.
- For this checkout only, publishing a verified local/origin development branch is part of closing an upstream sync. Before pushing, run the full local verification stack, including local Copilot review, tests, lint/type/security/warning gates, pre-commit/pre-push gates, and graph refresh when applicable.
- After verification passes, fetch `origin` and compare. If local `HEAD` contains `origin` or deliberately supersedes it because origin-only commits are already integrated, duplicated by rebase, or intentionally rejected with the reason recorded, update `origin` with a normal push for fast-forwards or `--force-with-lease` for rewritten history.
- Do not leave `origin` behind a verified local stack merely because the local branch was rebased. If the lease fails, or if origin contains new valuable or unclear work that is not in local, stop, fetch, compare, and integrate or ask before publishing.
- This does not authorize publishing to `upstream`, changing remotes, or generalizing the Graphify/vampyre publication rule to any other repo.

## Verification and Failure Policy

Rules:
- Do not waive test failures, skips, warnings, linter findings, gate failures, graphify update failures, or audit findings as "pre-existing." If an issue is reproducible in the current workspace, it becomes the current agent's active task until resolved or until the user explicitly redirects the work.
- Do not mark a slice, PR, branch, or handoff as ready while any reproduced failure, skip, warning, or gate finding remains unresolved.
- If another agent reports an issue as pre-existing, independently reproduce it, root-cause it, fix it when it is in scope, and record the invalid waiver in the handoff or audit notes.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ graphify extract ./docs --mode deep # richer semantic extraction via
graphify extract ./docs --no-cluster # raw extraction only, skip clustering
graphify extract ./docs --force # overwrite graph.json even if new graph has fewer nodes (use after refactors or to clear ghost duplicates)
graphify extract ./docs --dedup-llm # LLM tiebreaker for ambiguous entity pairs (uses same API key)
graphify extract ./docs --multigraph # build a MultiDiGraph that preserves parallel edges (e.g. A calls B AND A imports B)
graphify extract ./docs --simple # force a simple graph even over an existing multigraph (lossy — warns on collapse)
graphify extract ./docs --global --as myrepo # extract and register into the cross-project global graph
GRAPHIFY_MAX_OUTPUT_TOKENS=32768 graphify extract ./docs --backend claude # raise output cap for dense corpora

Expand Down
1 change: 1 addition & 0 deletions graphify/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def __getattr__(name):
}
if name in _map:
import importlib

mod_name, attr = _map[name]
mod = importlib.import_module(mod_name)
return getattr(mod, attr)
Expand Down
Loading