diff --git a/.claude/hooks/bash-guard.py b/.claude/hooks/bash-guard.py index ad11c8283e..03df247a7c 100755 --- a/.claude/hooks/bash-guard.py +++ b/.claude/hooks/bash-guard.py @@ -2,7 +2,9 @@ """ Claude Code PreToolUse Bash guard for Widthdom/CodeIndex. -Thin adapter around the shared CodeIndex guard core. +Tool-specific adapter around the shared guard core in +.agent_harness/command_guard_core.py. Codex uses the sibling adapter at +.codex/hooks/bash_guard.py. """ from __future__ import annotations diff --git a/.codex/hooks/bash_guard.py b/.codex/hooks/bash_guard.py index 4c317ddc98..d7ffbb224f 100644 --- a/.codex/hooks/bash_guard.py +++ b/.codex/hooks/bash_guard.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 -"""Codex PreToolUse Bash guard for Widthdom/CodeIndex.""" +""" +Codex PreToolUse Bash guard for Widthdom/CodeIndex. + +Tool-specific adapter around the shared guard core in +.agent_harness/command_guard_core.py. Claude Code uses the sibling adapter at +.claude/hooks/bash-guard.py. +""" from __future__ import annotations diff --git a/.codex/scripts/check-workflow-links.sh b/.codex/scripts/check-workflow-links.sh new file mode 100644 index 0000000000..a62cadeb67 --- /dev/null +++ b/.codex/scripts/check-workflow-links.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +cd "$repo_root" + +status=0 + +while IFS= read -r doc; do + while IFS= read -r target; do + [[ -z "$target" ]] && continue + if [[ ! -s "$target" ]]; then + printf '%s references missing or empty workflow file: %s\n' "$doc" "$target" >&2 + status=1 + fi + done < <( + awk ' + { + while (match($0, /`\.codex\/workflows\/[^`[:space:])]*\.md`/)) { + target = substr($0, RSTART + 1, RLENGTH - 2) + if (target !~ /[*?]/) { + print target + } + $0 = substr($0, RSTART + RLENGTH) + } + } + ' "$doc" | sort -u + ) +done < <( + { + git ls-files '*.md' + git ls-files '.codex/workflows/*.md' + } | sort -u +) + +exit "$status" diff --git a/.codex/workflows/README.md b/.codex/workflows/README.md index b417ec2478..a16c75c956 100644 --- a/.codex/workflows/README.md +++ b/.codex/workflows/README.md @@ -4,6 +4,7 @@ This directory contains shared task workflows for CodeIndex coding agents. Despite the `.codex` directory name, these workflows are used by both Codex and Claude Code. Use these workflows instead of pasting long repeated instructions into every prompt. +Start from the thin entry point for your tool (`AGENTS.md` for Codex or `CLAUDE.md` for Claude Code), then follow `AGENT_GUIDE.md` to the relevant workflow here. ## Workflows @@ -22,3 +23,7 @@ Use these workflows instead of pasting long repeated instructions into every pro - Codex-specific entry instructions go in `AGENTS.md`. - Claude-specific entry instructions go in `CLAUDE.md`. - Do not duplicate full workflows across entry-point files. + +## Link Checks + +Run `bash .codex/scripts/check-workflow-links.sh` after renaming, adding, or removing workflow files. The check scans tracked Markdown entry points and workflow docs for `.codex/workflows/*.md` references and fails when a referenced workflow file is missing or empty. diff --git a/.codex/workflows/precommit.md b/.codex/workflows/precommit.md index ab78fa89b7..abf860e895 100644 --- a/.codex/workflows/precommit.md +++ b/.codex/workflows/precommit.md @@ -16,6 +16,7 @@ Run this before each commit. 10. Confirm issue auto-close references will be placed in the PR body as `Fixes #...`. 11. Confirm the commit message is English and includes relevant issue numbers. 12. Check `git diff --stat` and `git diff` for accidental changes. +13. If workflow file names or `.codex/workflows/*.md` references changed, run `bash .codex/scripts/check-workflow-links.sh`. ## Validation Commands diff --git a/AGENT_GUIDE.md b/AGENT_GUIDE.md index 445fffb318..f03a774563 100644 --- a/AGENT_GUIDE.md +++ b/AGENT_GUIDE.md @@ -18,6 +18,7 @@ For implementation tasks: ## Workflow Index Task-specific procedures live in `.codex/workflows/`. The directory is a shared workflow library for all coding agents, not only Codex. +See `.codex/workflows/README.md` for the workflow directory map and rule-placement guidance. - issue fixing: `.codex/workflows/issue-fix.md` - changelog fragments: `.codex/workflows/changelog-fragment.md` @@ -55,6 +56,12 @@ This rule applies to code search and repository understanding. It does not forbi ## Tool-Specific Notes +Command-search enforcement is tool-specific and adapter-driven: + +- Codex uses `.codex/hooks.json`, which invokes `.codex/hooks/bash_guard.py` and `.codex/hooks/permission_request_guard.py`. +- Claude Code uses `.claude/settings.json`, which invokes `.claude/hooks/bash-guard.py`. +- Both Bash guard adapters delegate shared command policy to `.agent_harness/command_guard_core.py`; update the shared core for common command policy and review both adapters only when tool-specific behavior changes. + ### Claude Code - Follow the repository-tracked `.claude/settings.json` and `.claude/hooks/bash-guard.py` policy files when running in Claude Code. diff --git a/README.md b/README.md index 87df9e57bc..643531e9e7 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ version 2. | [Platform Support](docs/platform-support.md) | Official release asset RIDs, unsupported platforms, and source-build alternatives. | | [Developer Guide](DEVELOPER_GUIDE.md) | Architecture, implementation notes, release workflow, and the [`reference_kind` filtering matrix](DEVELOPER_GUIDE.md#reference-kind-filtering-matrix) for `callers` / `impact` / `deps` count reconciliation. | | [Testing Guide](TESTING_GUIDE.md) | Test conventions and validation commands. | +| [Agent Guide](AGENT_GUIDE.md) | Shared agent entry point, workflow index, search policy, and status contract maintenance rules. | +| [Claude Code Entry Point](CLAUDE.md) | Thin Claude Code entry point that redirects to the shared agent guide. | | [Self-Improvement Contract](SELF_IMPROVEMENT.md) | Rules for agents improving CodeIndex itself. | | [Integration Policy](INTEGRATION_POLICY.md) | Permitted CLI, JSON, MCP, and integration use. | | [Security Policy](SECURITY.md) | Private vulnerability reporting and coordinated disclosure policy. | @@ -323,6 +325,8 @@ hotspot-family readiness は、hotspot-family contract version 2 で導入され | [プラットフォームサポート](docs/platform-support.md#プラットフォームサポート) | 公式リリースアセットの RID、未対応 platform、source build の代替手段。 | | [開発者ガイド](DEVELOPER_GUIDE.md#開発者ガイド) | アーキテクチャ、実装メモ、リリース手順、`callers` / `impact` / `deps` の件数差を照合する [`reference_kind` フィルタの対応表](DEVELOPER_GUIDE.md#reference-kind-filtering-matrix)。 | | [テストガイド](TESTING_GUIDE.md#テストガイド) | テスト規約と検証コマンド。 | +| [エージェントガイド](AGENT_GUIDE.md) | 共有エージェント入口、workflow index、検索ポリシー、status contract の保守ルール。 | +| [Claude Code 入口](CLAUDE.md) | 共有エージェントガイドへリダイレクトする薄い Claude Code entry point。 | | [自己改善コントラクト](SELF_IMPROVEMENT.md#自己改善ループ) | CodeIndex 自身を改善するエージェント向けルール。 | | [統合ポリシー](INTEGRATION_POLICY.md) | CLI、JSON、MCP、各種統合で許可される利用。 | | [セキュリティポリシー](SECURITY.md) | 非公開の脆弱性報告と協調的開示の方針。 | diff --git a/SELF_IMPROVEMENT.md b/SELF_IMPROVEMENT.md index a7d75b792c..1d06c20d6a 100644 --- a/SELF_IMPROVEMENT.md +++ b/SELF_IMPROVEMENT.md @@ -41,7 +41,7 @@ The loop is not just "suggest ideas". It is: - After every commit, rebuild `cdidx` from the latest local source, run `dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll status --check --json`, and refresh `.cdidx/codeindex.db` with that freshly built binary only when the check does not report `index_matches_workspace: true`. - Prefer the lightest truthful refresh mode: use `--files` only for known in-place edits or new files, use `--commits HEAD` after a normal commit because it tracks renames/deletes from git history, use `--changed-between ` after branch switches when both refs are known, and reserve a full `cdidx . --json` scan for history-moving operations or cases where repo-wide stale files must be purged. - Prefer the **locally built latest binary** (`dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll`) over an older globally installed `cdidx` whenever the repository code has changed. **Never fall back to a global `cdidx`** — the global version may have an older DB schema, missing query features, or stale extraction logic that silently produces wrong results. This is enforced at the Claude Code harness level via the repo-tracked `.claude/settings.json`, which denies the full set of shell code-search and file-discovery commands: `rg`, `grep`, `egrep`, `fgrep`, `zgrep`, `rgrep`, `ripgrep`, `ag`, `ack`, `ack-grep`, `git grep`, `find`, `locate`, `mlocate`, `mdfind`, and `cdidx`. Use the built-in Grep / Glob tools or the locally built binary instead. -- Treat repo-tracked `.claude/settings.json` and `.claude/hooks/bash-guard.py` as policy files. Do not edit them during ordinary self-improvement work unless the task is explicitly about Claude Code guard behavior. The current guard is deny-oriented: it is intentionally permissive for routine `dotnet`, `git`, `gh`, `codex exec`, `/tmp` work, and read-only shell inspection, while still blocking dangerous shell patterns and sensitive local read paths. +- Treat repo-tracked `.claude/settings.json`, `.claude/hooks/bash-guard.py`, `.codex/hooks.json`, and `.codex/hooks/bash_guard.py` as tool-specific guard adapters. Do not edit them during ordinary self-improvement work unless the task is explicitly about agent guard behavior. Shared Bash command policy lives in `.agent_harness/command_guard_core.py`; update that shared core for common policy and review both adapters only when tool-specific behavior changes. The current guard is deny-oriented: it is intentionally permissive for routine `dotnet`, `git`, `gh`, `codex exec`, `/tmp` work, and read-only shell inspection, while still blocking dangerous shell patterns and sensitive local read paths. - Respect the current local-privacy boundary in Claude Code: home-directory personal areas and credential-like files are intentionally read-denied by policy. Do not weaken those restrictions as a convenience workaround for self-improvement tasks; if the task genuinely requires changing the guard, treat that as the task itself and update docs accordingly. - After `git switch`, re-index with the **locally built binary** using `dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll index . --changed-between --json` when the workflow has both the previous and current refs; this updates only the files changed between branches while still purging rename/delete old paths included by git. If the refs are not available, or after `git reset`, `git rebase`, `git commit --amend`, or `git merge`, use `dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll . --json` so stale files are purged against the current checkout. - When searching and navigating code to investigate bugs, plan fixes, or verify changes, always use the **locally built binary** — not the globally installed version. This ensures query results reflect the latest extraction rules and DB schema from this branch. diff --git a/changelog.d/unreleased/1906.fixed.md b/changelog.d/unreleased/1906.fixed.md new file mode 100644 index 0000000000..9fdc71af31 --- /dev/null +++ b/changelog.d/unreleased/1906.fixed.md @@ -0,0 +1,13 @@ +--- +category: fixed +issues: + - 1906 +--- + +## English + +- Added the shared agent guide and Claude Code entry point to the README documentation table. + +## 日本語 + +- README のドキュメント表に共有エージェントガイドと Claude Code 入口を追加しました。 diff --git a/changelog.d/unreleased/1979.fixed.md b/changelog.d/unreleased/1979.fixed.md new file mode 100644 index 0000000000..d612def8aa --- /dev/null +++ b/changelog.d/unreleased/1979.fixed.md @@ -0,0 +1,13 @@ +--- +category: fixed +issues: + - 1979 +--- + +## English + +- Added a workflow link check script and precommit reminder so `.codex/workflows/*.md` references fail fast when targets are missing or empty. + +## 日本語 + +- `.codex/workflows/*.md` 参照先の欠落や空ファイルを早期に検出できるよう、workflow link check script と precommit の確認項目を追加しました。 diff --git a/changelog.d/unreleased/1995.fixed.md b/changelog.d/unreleased/1995.fixed.md new file mode 100644 index 0000000000..81ede1d9fd --- /dev/null +++ b/changelog.d/unreleased/1995.fixed.md @@ -0,0 +1,13 @@ +--- +category: fixed +issues: + - 1995 +--- + +## English + +- Documented the Codex and Claude Code Bash guard adapters and their shared guard core to reduce policy drift. + +## 日本語 + +- Codex と Claude Code の Bash guard adapter と共有 guard core を明記し、ポリシーの drift を抑えました。 diff --git a/changelog.d/unreleased/1997.fixed.md b/changelog.d/unreleased/1997.fixed.md new file mode 100644 index 0000000000..de6db547c1 --- /dev/null +++ b/changelog.d/unreleased/1997.fixed.md @@ -0,0 +1,13 @@ +--- +category: fixed +issues: + - 1997 +--- + +## English + +- Clarified the command-guard enforcement boundary between Codex hooks and Claude Code settings. + +## 日本語 + +- Codex hooks と Claude Code settings の command guard enforcement boundary を明確にしました。 diff --git a/changelog.d/unreleased/1998.fixed.md b/changelog.d/unreleased/1998.fixed.md new file mode 100644 index 0000000000..dfc2dfca05 --- /dev/null +++ b/changelog.d/unreleased/1998.fixed.md @@ -0,0 +1,13 @@ +--- +category: fixed +issues: + - 1998 +--- + +## English + +- Cross-linked the shared agent guide and workflow directory README for easier workflow discovery. + +## 日本語 + +- workflow を見つけやすくするため、共有エージェントガイドと workflow directory README を相互にリンクしました。 diff --git a/changelog.d/unreleased/2677.fixed.md b/changelog.d/unreleased/2677.fixed.md new file mode 100644 index 0000000000..fa3c0c0062 --- /dev/null +++ b/changelog.d/unreleased/2677.fixed.md @@ -0,0 +1,13 @@ +--- +category: fixed +issues: + - 2677 +--- + +## English + +- Made fuzzy suggestion duplicate diagnostics tolerate unavailable stderr so deduplication does not fail during parallel test output capture. + +## 日本語 + +- fuzzy suggestion duplicate の診断出力で stderr が利用できない場合を許容し、並列テストの出力 capture 中に重複排除が失敗しないようにしました。 diff --git a/src/CodeIndex/Cli/SuggestionStore.cs b/src/CodeIndex/Cli/SuggestionStore.cs index d99b461c0f..904b8aecc0 100644 --- a/src/CodeIndex/Cli/SuggestionStore.cs +++ b/src/CodeIndex/Cli/SuggestionStore.cs @@ -460,14 +460,31 @@ private static (SuggestionRecord? Record, double? Score) FindDuplicate( if (best != null && bestScore >= threshold) { - Console.Error.WriteLine( - $"cdidx: fuzzy suggestion duplicate matched hash {best.Hash} with score {bestScore:0.###} (threshold {threshold:0.###})"); + WriteFuzzyDuplicateWarning(best.Hash, bestScore, threshold); return (best, bestScore); } return (null, null); } + private static void WriteFuzzyDuplicateWarning(string hash, double score, double threshold) + { + try + { + Console.Error.WriteLine( + $"cdidx: fuzzy suggestion duplicate matched hash {hash} with score {score:0.###} (threshold {threshold:0.###})"); + } + catch (ObjectDisposedException) + { + // Best-effort diagnostic only; suggestion deduplication must not fail + // because another thread or test fixture temporarily replaced stderr. + } + catch (IOException) + { + // Same rationale as ObjectDisposedException. + } + } + private static bool SameDedupScope(SuggestionRecord left, SuggestionRecord right) => string.Equals(left.Category?.Trim(), right.Category?.Trim(), StringComparison.OrdinalIgnoreCase) && string.Equals(left.Language?.Trim() ?? string.Empty, right.Language?.Trim() ?? string.Empty, StringComparison.OrdinalIgnoreCase); diff --git a/tests/CodeIndex.Tests/SuggestionStoreTests.cs b/tests/CodeIndex.Tests/SuggestionStoreTests.cs index f76be33596..584f0dd5a6 100644 --- a/tests/CodeIndex.Tests/SuggestionStoreTests.cs +++ b/tests/CodeIndex.Tests/SuggestionStoreTests.cs @@ -140,6 +140,35 @@ public void TryAddAndSubmit_FuzzyDuplicate_ReturnsMatchedHashAndScore() Assert.True(second.DuplicateScore >= SuggestionStore.DefaultDedupThreshold); } + [Fact] + public void TryAddAndSubmit_FuzzyDuplicate_IgnoresClosedDiagnosticStderr() + { + var originalError = Console.Error; + var closedError = new StringWriter(); + closedError.Dispose(); + + lock (TestConsoleLock.Gate) + { + Console.SetError(closedError); + try + { + var record1 = MakeRecord("language_support", "javascript", "missing arrow function support"); + var record2 = MakeRecord("language_support", "javascript", "arrow functions not supported"); + + var first = _store.TryAddAndSubmit(record1, null); + var second = _store.TryAddAndSubmit(record2, null); + + Assert.True(first.IsNew); + Assert.False(second.IsNew); + Assert.Equal(record1.Hash, second.DuplicateOfHash); + } + finally + { + Console.SetError(originalError); + } + } + } + [Fact] public void TryAdd_DifferentSuggestions_BothSucceed() {