From 3f962b1ebf80f36de16d611419aa0fa2606782de Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 4 May 2026 11:46:02 -0400 Subject: [PATCH 1/3] docs: add AGENTS.md, symlink CLAUDE.md, correct v0.1 language matrix Python and Go structural support landed in v0.1.5 via plain `feat:` PRs, but README and CLAUDE.md still listed Go as planned for v0.2 and Python's status was inconsistent. Make AGENTS.md the canonical agent-instructions file (so non-Claude agents pick it up) and point CLAUDE.md at it via symlink. Also document the Cargo 0.x SemVer carve-out: while < 1.0.0, plain `feat:` is a patch bump; `feat!:` is required to cut 0.2.0. The next language batch (C#, Kotlin, Ruby, PHP) is shifted to v0.2 and will land with `feat!:` so release-plz actually bumps the minor. --- AGENTS.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 57 +-------------------------------------------- README.md | 14 +++++------ 3 files changed, 77 insertions(+), 63 deletions(-) create mode 100644 AGENTS.md mode change 100644 => 120000 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..35a6209 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,69 @@ +# Zift + +Static analysis tool that scans codebases for embedded authorization logic and generates Policy as Code (PaC). Rego/OPA today; architecture is designed to grow into other policy languages (e.g. Cedar) over time. + +> This file is the canonical instructions document for AI coding agents working on Zift. `CLAUDE.md` is a symlink to this file so Claude Code picks it up automatically; other agents (Codex, Aider, Cursor, etc.) should read `AGENTS.md` directly. + +## Setup + +After cloning, configure the pre-commit hook: + +```bash +git config core.hooksPath .githooks +``` + +## Build & Development + +```bash +cargo build +cargo build --release +cargo test +cargo fmt # required before committing +cargo clippy -- -D warnings +``` + +## Architecture + +- **CLI** (`src/cli.rs`): Subcommands — `scan`, `extract`, `report`, `rules`, `init` +- **Scanner** (`src/scanner/`): Tree-sitter AST parsing and pattern matching across languages +- **Rules** (`rules/`): TOML-based pattern definitions with tree-sitter queries and policy templates (Rego today) +- **Rego** (`src/rego/`): Policy-as-Code generation from scan findings (Rego/OPA today; additional engines like Cedar planned) +- **Output** (`src/output/`): Formatters (JSON, text; SARIF planned) + +### Design principles + +- Two-pass architecture: structural scan (tree-sitter, fast) then optional semantic scan (LLM-assisted) +- Rules are data (TOML), not code — easy to add new patterns without touching Rust +- Same finding schema for both passes + +### Language support + +- v0.1: TypeScript, JavaScript, Java, Python, Go +- v0.2 (planned): C#, Kotlin, Ruby, PHP + +## Conventional Commits & Versioning + +Uses release-plz for automated version bumping and changelog generation. + +Trigger prefixes (cause version bump): +- `feat:` — new feature +- `fix:` — bug fix +- `refactor:` — code refactoring +- `perf:` — performance improvement + +Skipped prefixes (no version bump): +- `docs:`, `test:`, `ci:`, `chore:`, `style:`, `build:` + +PR titles must use a conventional commit prefix. + +### Bump size (Cargo 0.x SemVer) + +While the crate is below `1.0.0`, release-plz follows Cargo's 0.x convention: the **minor** position acts as the major. That changes how prefixes map to bumps: + +| Commit | Pre-1.0 bump | Post-1.0 bump | +| --------------------------------------- | ----------------------- | ------------------------ | +| `fix:` / `refactor:` / `perf:` | patch (`0.1.x → 0.1.y`) | patch | +| `feat:` | patch (`0.1.x → 0.1.y`) | minor (`1.x → 1.(x+1).0`) | +| `feat!:` or `BREAKING CHANGE:` footer | minor (`0.1.x → 0.2.0`) | major (`1.x → 2.0.0`) | + +Practical consequence: a plain `feat:` on `0.1.x` will **not** produce `0.2.0`. To cut `0.2.0` deliberately, land the headline change with `feat!:` (or include a `BREAKING CHANGE:` footer). For example, Python and Go structural support landed as plain `feat:` PRs and rolled into `v0.1.5`; the next language batch will use `feat!:` so it cuts `v0.2.0`. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 73740e2..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,56 +0,0 @@ -# Zift - -Static analysis tool that scans codebases for embedded authorization logic and generates Policy as Code (PaC). Rego/OPA today; architecture is designed to grow into other policy languages (e.g. Cedar) over time. - -## Setup - -After cloning, configure the pre-commit hook: - -```bash -git config core.hooksPath .githooks -``` - -## Build & Development - -```bash -cargo build -cargo build --release -cargo test -cargo fmt # required before committing -cargo clippy -- -D warnings -``` - -## Architecture - -- **CLI** (`src/cli.rs`): Subcommands — `scan`, `extract`, `report`, `rules`, `init` -- **Scanner** (`src/scanner/`): Tree-sitter AST parsing and pattern matching across languages -- **Rules** (`rules/`): TOML-based pattern definitions with tree-sitter queries and policy templates (Rego today) -- **Rego** (`src/rego/`): Policy-as-Code generation from scan findings (Rego/OPA today; additional engines like Cedar planned) -- **Output** (`src/output/`): Formatters (JSON, text; SARIF planned) - -### Design principles - -- Two-pass architecture: structural scan (tree-sitter, fast) then optional semantic scan (LLM-assisted) -- Rules are data (TOML), not code — easy to add new patterns without touching Rust -- Same finding schema for both passes - -### Language support - -- v0.1: TypeScript, JavaScript (Java in progress) -- v0.2: Python, Go -- v0.3: C#, Kotlin, Ruby, PHP - -## Conventional Commits & Versioning - -Uses release-plz for automated version bumping and changelog generation. - -Trigger prefixes (cause version bump): -- `feat:` — new feature (minor) -- `fix:` — bug fix (patch) -- `refactor:` — code refactoring (patch) -- `perf:` — performance improvement (patch) - -Skipped prefixes (no version bump): -- `docs:`, `test:`, `ci:`, `chore:`, `style:`, `build:` - -PR titles must use a conventional commit prefix. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index 25ea320..bb1486e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Sift through your codebase for embedded authorization logic. Extract it into Policy as Code (PaC) — [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) for [OPA](https://www.openpolicyagent.org/) today, with other engines (e.g. Cedar) on the roadmap. -> **Status:** v0.1 — structural scanning ready for TypeScript, JavaScript, Java, and Python. `--deep` (LLM-assisted) mode functional via any OpenAI-compatible endpoint or MCP-capable agent host. +> **Status:** v0.1 — structural scanning ready for TypeScript, JavaScript, Java, Python, and Go. `--deep` (LLM-assisted) mode functional via any OpenAI-compatible endpoint or MCP-capable agent host. ## What is zift? @@ -27,7 +27,7 @@ zift report . # detailed findings report 1. **Structural scan** (tree-sitter) — fast, deterministic, zero-cost. Finds known authorization patterns: role checks, permission guards, auth middleware, security annotations. -2. **Semantic scan** (`--deep`, opt-in) — sends candidate code regions to an LLM that classifies authorization logic the structural pass missed or misjudged. Useful for business rules that implicitly encode access control, and for languages where structural support hasn't shipped yet (Go, etc.). +2. **Semantic scan** (`--deep`, opt-in) — sends candidate code regions to an LLM that classifies authorization logic the structural pass missed or misjudged. Useful for business rules that implicitly encode access control, and for languages where structural support hasn't shipped yet (C#, Kotlin, etc.). ## Supported languages @@ -36,11 +36,11 @@ zift report . # detailed findings report | TypeScript / JavaScript | yes (v0.1) | yes (v0.1) | Express, NestJS, Next.js | | Java | yes (v0.1) | yes (v0.1) | Spring Security, Jakarta Security | | Python | yes (v0.1) | yes (v0.1) | Django, Flask, FastAPI | -| Go | planned (v0.2) | yes (v0.1) | Gin, Echo | -| C# | planned (v0.3) | yes (v0.1) | ASP.NET Core | -| Kotlin | planned (v0.3) | yes (v0.1) | Spring (Kotlin) | -| Ruby | planned (v0.3) | yes (v0.1) | Rails | -| PHP | planned (v0.3) | yes (v0.1) | Laravel | +| Go | yes (v0.1) | yes (v0.1) | Gin, Echo | +| C# | planned (v0.2) | yes (v0.1) | ASP.NET Core | +| Kotlin | planned (v0.2) | yes (v0.1) | Spring (Kotlin) | +| Ruby | planned (v0.2) | yes (v0.1) | Rails | +| PHP | planned (v0.2) | yes (v0.1) | Laravel | Deep mode walks the full source tree by extension and detects auth-y function names with regex — so it produces useful results in any language well before structural support lands. From ead27a5d5290e24a6a1aaffa369b498994481898 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 4 May 2026 11:47:06 -0400 Subject: [PATCH 2/3] docs: clarify Amazon Q /q review trigger in address-pr-feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous wording told users to "wait for AQ's review" if none was present, but AQ doesn't auto-trigger on push (or at all, absent the org integration on initial open). Waiting alone never produces a review. Spell out the actual lever: post `/q review` as a PR comment. Apply the same clarification to the commit-lag section — if a user wants AQ to re-review the latest commit, they have to ask explicitly; AQ will not catch up on its own. --- .claude/commands/address-pr-feedback.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.claude/commands/address-pr-feedback.md b/.claude/commands/address-pr-feedback.md index eb4f008..cb9251a 100644 --- a/.claude/commands/address-pr-feedback.md +++ b/.claude/commands/address-pr-feedback.md @@ -37,8 +37,10 @@ gh api "repos/{owner}/{repo}/pulls/{pr}/comments" --paginate \ - review/inline comments (pulls comments endpoint), or - PR-level comments (issues comments endpoint — this is where AQ posts its "Critical Issue" summary). -If neither is present, inform the user: -> "Amazon Q hasn't reviewed this PR yet. Wait for its review, then re-run this command." +**Important — Amazon Q does NOT auto-trigger.** AQ only reviews on initial PR open (and only if the org has the integration installed) or when explicitly asked via `/q review` as a PR comment. Waiting alone will never produce a review if it hasn't already started. + +If no AQ activity is present, inform the user: +> "Amazon Q hasn't reviewed this PR yet. Post `/q review` as a PR comment to request a review (AQ does not auto-trigger on push), then re-run this command once it lands." ```bash # Check both channels for AQ activity @@ -54,7 +56,9 @@ gh api "repos/{owner}/{repo}/issues/{pr}/comments" --paginate \ CodeRabbit re-reviews on every push. If you ran a previous round of `/address-pr-feedback`, pushed a fix commit, and CodeRabbit's response to that push hasn't landed yet, the next round will miss the new findings and cause exactly the bug this section exists to prevent. -**Amazon Q does NOT re-review automatically on push** — it only reviews on initial PR open (or when explicitly triggered). After any fix push, AQ's `commit_id` will lag HEAD and that is *expected*. Don't block on it. +**Amazon Q does NOT re-review automatically on push** — it only reviews when explicitly triggered (initial PR open if the integration is wired up, or by posting `/q review` as a PR comment). After any fix push, AQ's `commit_id` will lag HEAD and that is *expected*. Don't block on it. + +If you (or the user) want AQ to re-review the latest commit before processing feedback, post `/q review` as a PR comment and wait for the new review to land. Otherwise, proceed with the existing AQ findings and note that they reflect an earlier commit. ```bash # Compare the head SHA of the PR to the most recent CodeRabbit review's commit_id @@ -68,7 +72,7 @@ echo "Last CR review commit: $LATEST_CR_COMMIT" If `$LATEST_CR_COMMIT` does not match `$HEAD_SHA`, CodeRabbit hasn't reviewed the latest commit yet. Tell the user: > "CodeRabbit's latest review is on commit `` but PR head is ``. Wait a few minutes for the new review to land, then re-run." -For Amazon Q, optionally surface its review `commit_id` for context but **do not block** on a mismatch — note to the user that AQ's findings (if any) will be from its initial review pass and proceed. +For Amazon Q, optionally surface its review `commit_id` for context but **do not block** on a mismatch — note to the user that AQ's findings (if any) will be from its initial review pass and proceed. If the user wants a fresh AQ review on the latest commit, they (or you) must post `/q review` as a PR comment; AQ will not catch up on its own. ### 3. Fetch review comments (token-efficient two-pass approach) From e91a6055dbad008eadeda6ff5678b76e61fb5be7 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 4 May 2026 11:54:18 -0400 Subject: [PATCH 3/3] docs: clarify bump-table placeholders in AGENTS.md Pre-1.0 and post-1.0 columns now use distinct placeholders (`0.M.p` vs `m.n.p`) so the same letter never refers to two different version positions. Also add a "see bump table" pointer to the trigger-prefix bullet list, since on Cargo 0.x `fix:` and `feat:` both land as patch bumps and the bullet list alone is misleading. --- AGENTS.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 35a6209..bf5940a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,7 +45,7 @@ cargo clippy -- -D warnings Uses release-plz for automated version bumping and changelog generation. -Trigger prefixes (cause version bump): +Trigger prefixes (cause version bump — see bump table below; exact bump depends on pre/post-1.0): - `feat:` — new feature - `fix:` — bug fix - `refactor:` — code refactoring @@ -60,10 +60,12 @@ PR titles must use a conventional commit prefix. While the crate is below `1.0.0`, release-plz follows Cargo's 0.x convention: the **minor** position acts as the major. That changes how prefixes map to bumps: -| Commit | Pre-1.0 bump | Post-1.0 bump | -| --------------------------------------- | ----------------------- | ------------------------ | -| `fix:` / `refactor:` / `perf:` | patch (`0.1.x → 0.1.y`) | patch | -| `feat:` | patch (`0.1.x → 0.1.y`) | minor (`1.x → 1.(x+1).0`) | -| `feat!:` or `BREAKING CHANGE:` footer | minor (`0.1.x → 0.2.0`) | major (`1.x → 2.0.0`) | +Placeholders below: pre-1.0 uses `0.M.p` (minor `M`, patch `p`); post-1.0 uses `m.n.p` (major `m`, minor `n`, patch `p`). + +| Commit | Pre-1.0 bump | Post-1.0 bump | +| --------------------------------------- | --------------------------- | --------------------------- | +| `fix:` / `refactor:` / `perf:` | patch (`0.M.p → 0.M.(p+1)`) | patch (`m.n.p → m.n.(p+1)`) | +| `feat:` | patch (`0.M.p → 0.M.(p+1)`) | minor (`m.n.p → m.(n+1).0`) | +| `feat!:` or `BREAKING CHANGE:` footer | minor (`0.M.p → 0.(M+1).0`) | major (`m.n.p → (m+1).0.0`) | Practical consequence: a plain `feat:` on `0.1.x` will **not** produce `0.2.0`. To cut `0.2.0` deliberately, land the headline change with `feat!:` (or include a `BREAKING CHANGE:` footer). For example, Python and Go structural support landed as plain `feat:` PRs and rolled into `v0.1.5`; the next language batch will use `feat!:` so it cuts `v0.2.0`.