{"id":5842,"date":"2026-11-01T03:48:30","date_gmt":"2026-11-01T03:48:30","guid":{"rendered":"https:\/\/aiopsschool.com\/blog\/?p=5842"},"modified":"2026-11-01T03:48:32","modified_gmt":"2026-11-01T03:48:32","slug":"codex-skills-production-grade-engineering-guide-2026","status":"publish","type":"post","link":"https:\/\/aiopsschool.com\/blog\/codex-skills-production-grade-engineering-guide-2026\/","title":{"rendered":"Codex Skills: Production-Grade Engineering Guide (2026)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Version:<\/strong>&nbsp;2026-11-01<br><strong>Target:<\/strong>&nbsp;Codex CLI and IDE extension; local repository skills; optional custom prompts; lifecycle hooks; GitHub Actions<br><strong>Operating examples:<\/strong>&nbsp;macOS\/Linux\/WSL with Python 3 and Git<br><strong>Scope:<\/strong>&nbsp;creating, updating, deploying, discovering, invoking, testing, and enforcing two reproducible skills<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Read this correction first.<\/strong>&nbsp;Codex repository skills belong in&nbsp;<code>.agents\/skills\/&lt;name&gt;\/SKILL.md<\/code>&nbsp;in the current documented layout,&nbsp;<strong>not<\/strong>&nbsp;<code>.codex\/skills\/<\/code>. Native explicit invocation uses&nbsp;<code>$refactor<\/code>, not&nbsp;<code>\/refactor<\/code>. Legacy prompt files can expose&nbsp;<code>\/prompts:refactor<\/code>, but custom prompts are deprecated. Intent-based skill selection is probabilistic; reliable event-driven checks require lifecycle hooks and\/or CI. Do not invent a&nbsp;<code>hooks:<\/code>&nbsp;or&nbsp;<code>on_file_change:<\/code>&nbsp;key in&nbsp;<code>SKILL.md<\/code>&nbsp;frontmatter.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">Contents<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Architecture and execution sequence<\/li>\n\n\n\n<li>File and directory blueprint<\/li>\n\n\n\n<li>Exact skill schema and metadata rules<\/li>\n\n\n\n<li>Explicit\u00a0<code>$refactor<\/code>\u00a0implementation<\/li>\n\n\n\n<li>Implicit\u00a0<code>db-performance-review<\/code>\u00a0implementation<\/li>\n\n\n\n<li>Lifecycle event automation (guaranteed\u00a0<em>supported-tool<\/em>\u00a0check)<\/li>\n\n\n\n<li>CI enforcement and tests<\/li>\n\n\n\n<li>AGENTS.md orchestration<\/li>\n\n\n\n<li>Installation and deployment<\/li>\n\n\n\n<li>Optional legacy\u00a0<code>\/prompts:refactor<\/code>\u00a0command<\/li>\n\n\n\n<li>Validation matrix and live smoke tests<\/li>\n\n\n\n<li>Updating, rollout, rollback, and security<\/li>\n\n\n\n<li>Troubleshooting and operational boundaries<\/li>\n\n\n\n<li>Official references<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">1. Architecture and execution sequence<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Codex has&nbsp;<strong>four different extension surfaces<\/strong>. Do not confuse them.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Mechanism<\/th><th class=\"has-text-align-left\" data-align=\"left\">File(s)<\/th><th class=\"has-text-align-left\" data-align=\"left\">How it activates<\/th><th class=\"has-text-align-left\" data-align=\"left\">Guarantee<\/th><\/tr><\/thead><tbody><tr><td>Skill discovery and instructions<\/td><td><code>.agents\/skills\/&lt;name&gt;\/SKILL.md<\/code><\/td><td>Explicit&nbsp;<code>$name<\/code>&nbsp;or model selection from&nbsp;<code>description<\/code><\/td><td>Skill content is provided when selected; implicit selection is not mandatory<\/td><\/tr><tr><td>Repository guidance<\/td><td><code>AGENTS.md<\/code><\/td><td>Codex builds instruction chain for current directory at session start<\/td><td>Guidance is provided to model; it is not a shell-event handler<\/td><\/tr><tr><td>Tool lifecycle hook<\/td><td><code>.codex\/hooks.json<\/code>&nbsp;+ script<\/td><td><code>SessionStart<\/code>,&nbsp;<code>PostToolUse<\/code>, etc., for supported events<\/td><td>Hook runs when enabled, trusted, and matched; some tool paths opt out<\/td><\/tr><tr><td>CI policy<\/td><td><code>.github\/workflows\/*.yml<\/code><\/td><td>Pull request, push, manual dispatch<\/td><td>CI runs independently of an agent session, subject to pipeline availability<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">1.1 Precise high-level execution model<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n  A&#91;Codex starts in a Git repository] --&gt; B&#91;Load global and applicable AGENTS.md]\n  A --&gt; C&#91;Discover skill name, description, path]\n  D&#91;User task arrives] --&gt; E{Explicit $skill?}\n  E -- Yes --&gt; F&#91;Resolve named SKILL.md]\n  E -- No --&gt; G&#91;Model considers intent vs descriptions]\n  G -- Selected --&gt; F\n  G -- Not selected --&gt; I&#91;Normal Codex task handling]\n  F --&gt; H&#91;Load skill instructions and optional resources]\n  H --&gt; I\n  I --&gt; J&#91;Propose local tool call]\n  J --&gt; K&#91;Matching PreToolUse hooks if configured]\n  K --&gt; L&#91;Approved tool runs]\n  L --&gt; M&#91;Matching PostToolUse hooks]\n  M --&gt; N&#91;DB file hash comparison + static analysis]\n  N --&gt; O&#91;Additional review context to Codex]\n  O --&gt; P&#91;Verification and final response]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Codex starts with skill&nbsp;<strong>metadata<\/strong>&nbsp;(name, description and path). It does not need to read every full&nbsp;<code>SKILL.md<\/code>&nbsp;upfront. If a skill is explicitly named or selected by the agent, Codex loads its body and any needed references\/scripts. A skill is&nbsp;<strong>instructions + resources<\/strong>, not an independently executing file watcher, tool permission system, or automatically registered&nbsp;<code>\/name<\/code>&nbsp;route.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.2 Trigger semantics<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Input\/action<\/th><th class=\"has-text-align-left\" data-align=\"left\">Expected behavior<\/th><\/tr><\/thead><tbody><tr><td><code>$refactor src\/lib\/user.ts<\/code><\/td><td>Explicitly select the&nbsp;<code>refactor<\/code>&nbsp;skill, if installed and discoverable<\/td><\/tr><tr><td><code>\/skills<\/code><\/td><td>Open Codex&#8217;s skill browsing\/selection interface, not execute&nbsp;<code>\/refactor<\/code><\/td><\/tr><tr><td><code>Refactor this module<\/code>&nbsp;(without&nbsp;<code>$refactor<\/code>)<\/td><td>May select a relevant implicit skill; depends on metadata and runtime policy<\/td><\/tr><tr><td><code>Optimize this SQL query<\/code><\/td><td>Model may select&nbsp;<code>db-performance-review<\/code>&nbsp;from its description<\/td><\/tr><tr><td>Codex edits&nbsp;<code>db\/queries\/list.sql<\/code>&nbsp;with a supported tool<\/td><td>Matching&nbsp;<code>PostToolUse<\/code>&nbsp;hook can detect the changed file and run static analysis<\/td><\/tr><tr><td>Developer edits a file externally while Codex is idle<\/td><td>A Codex tool hook does not fire immediately; use editor file-watch tools or CI for that case<\/td><\/tr><tr><td>Pull request modifies&nbsp;<code>prisma\/schema.prisma<\/code><\/td><td>Independent GitHub Actions workflow can run static tests<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>False promise to avoid:<\/strong>&nbsp;YAML keys such as&nbsp;<code>triggers:<\/code>,&nbsp;<code>filePatterns:<\/code>,&nbsp;<code>onSave:<\/code>,&nbsp;<code>hooks:<\/code>,&nbsp;<code>events:<\/code>, or&nbsp;<code>watch:<\/code>&nbsp;in skill frontmatter do&nbsp;<strong>not<\/strong>&nbsp;define Codex lifecycle events. They will not provide guaranteed automatic invocation. Put hook configuration in&nbsp;<code>.codex\/hooks.json<\/code>&nbsp;or a supported&nbsp;<code>config.toml<\/code>&nbsp;hooks table.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.3 Scope and priority of AGENTS instructions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Global guidance:&nbsp;<code>$CODEX_HOME\/AGENTS.md<\/code>&nbsp;(typically&nbsp;<code>~\/.codex\/AGENTS.md<\/code>), unless&nbsp;<code>AGENTS.override.md<\/code>&nbsp;is present. Project instructions are discovered from repository root down to the current working directory; per-directory&nbsp;<code>AGENTS.override.md<\/code>&nbsp;takes precedence over&nbsp;<code>AGENTS.md<\/code>. Nested instructions apply to relevant directories.&nbsp;<code>AGENTS.md<\/code>&nbsp;should provide concise engineering policy, not try to simulate a file watcher.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Directory blueprint<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>my-project\/\n\u251c\u2500\u2500 AGENTS.md                        # repository-wide model guidance\n\u251c\u2500\u2500 .agents\/\n\u2502   \u2514\u2500\u2500 skills\/                       # actual Codex skill discovery root\n\u2502       \u251c\u2500\u2500 refactor\/\n\u2502       \u2502   \u251c\u2500\u2500 SKILL.md              # required\n\u2502       \u2502   \u2514\u2500\u2500 agents\/openai.yaml    # optional UI + explicit-only policy\n\u2502       \u2514\u2500\u2500 db-performance-review\/\n\u2502           \u251c\u2500\u2500 SKILL.md              # required\n\u2502           \u2514\u2500\u2500 agents\/openai.yaml    # optional UI + implicit policy\n\u251c\u2500\u2500 .codex\/\n\u2502   \u251c\u2500\u2500 hooks.json                    # tool\/session lifecycle handlers\n\u2502   \u2514\u2500\u2500 hooks\/db_performance.py       # deterministic hook implementation\n\u251c\u2500\u2500 .github\/workflows\/\n\u2502   \u2514\u2500\u2500 db-performance.yml           # independent PR guardrail\n\u251c\u2500\u2500 tests\/test_db_performance.py     # standard-library hook tests\n\u2514\u2500\u2500 src\/                              # your application, not included here\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Supported skill scopes:<\/strong>&nbsp;<code>$CWD\/.agents\/skills<\/code>, ancestor&nbsp;<code>.agents\/skills<\/code>&nbsp;paths up to Git root,&nbsp;<code>$HOME\/.agents\/skills<\/code>,&nbsp;<code>\/etc\/codex\/skills<\/code>, plus bundled system skills. A skill can include optional&nbsp;<code>scripts\/<\/code>,&nbsp;<code>references\/<\/code>, and&nbsp;<code>assets\/<\/code>&nbsp;if the workflow needs them. The examples deliberately omit empty placeholders.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What&nbsp;<code>.codex\/<\/code>&nbsp;is for:<\/strong>&nbsp;repository config and hooks,&nbsp;<strong>not<\/strong>&nbsp;the current documented repository skill root.&nbsp;<code>~\/.codex\/<\/code>&nbsp;is also the default Codex home for global&nbsp;<code>AGENTS.md<\/code>,&nbsp;<code>config.toml<\/code>, and the deprecated custom prompt folder.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Exact SKILL.md schema<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A minimal valid skill:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>---\nname: example-skill\ndescription: Analyze source files for a specific, well-defined problem. Use when the user asks for this analysis or when directly editing matching modules.\n---\n\n# Example Skill\n\n1. Locate the scoped files and project instructions.\n2. Carry out the workflow.\n3. Run verification.\n4. Report actions, results, and limitations.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Key rules:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>SKILL.md<\/code>\u00a0is required;\u00a0<code>name<\/code>\u00a0and\u00a0<code>description<\/code>\u00a0are required frontmatter fields.<\/li>\n\n\n\n<li>Keep names short, lowercase, action-oriented, with hyphens as needed. Match the folder name.<\/li>\n\n\n\n<li>Write the\u00a0<code>description<\/code>\u00a0as a\u00a0<strong>routing contract<\/strong>: the task, exact positive trigger contexts, and reasonable exclusions.<\/li>\n\n\n\n<li>Keep the body imperative and execution-focused; put large domain references under\u00a0<code>references\/<\/code>\u00a0if warranted.<\/li>\n\n\n\n<li><code>agents\/openai.yaml<\/code>\u00a0can configure display information, a default prompt and\u00a0<code>policy.allow_implicit_invocation<\/code>.<\/li>\n\n\n\n<li><code>policy.allow_implicit_invocation: false<\/code>\u00a0is documented for explicit-only skills; test it in your installed CLI because previous releases had discovery regressions.<\/li>\n\n\n\n<li>Avoid fabricating\u00a0<code>argument-hint<\/code>,\u00a0<code>allowed-tools<\/code>,\u00a0<code>model<\/code>,\u00a0<code>hooks<\/code>, or\u00a0<code>paths<\/code>\u00a0as a Codex skill event API. These may belong to other environments or deprecated custom prompts and are not a guaranteed Codex skill contract.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.1 Optional metadata: explicit only<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>interface:\n  display_name: \"Refactor\"\n  short_description: \"Safely refactor existing code without behavior changes\"\n  default_prompt: \"Use $refactor to refactor a specified scope.\"\npolicy:\n  allow_implicit_invocation: false\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.2 Optional metadata: implicit allowed<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>interface:\n  display_name: \"DB Performance Review\"\n  short_description: \"Check database changes for query and schema risks\"\n  default_prompt: \"Use $db-performance-review for the database changes.\"\npolicy:\n  allow_implicit_invocation: true\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4. Explicit skill:&nbsp;<code>$refactor<\/code>&nbsp;(complete source)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use this skill for explicitly requested&nbsp;<strong>behavior-preserving<\/strong>&nbsp;changes. It protects interfaces, tests, and unrelated work. It is&nbsp;<strong>not<\/strong>&nbsp;an alias for arbitrary feature rewrites.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>.agents\/skills\/refactor\/SKILL.md<\/code>&nbsp;\u2014 required instructions<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>---\nname: refactor\ndescription: Safely refactor existing application code while preserving externally observable behavior, public APIs, and data contracts. Use only when the user explicitly requests the $refactor workflow to improve readability, modularity, maintainability, or complexity without changing features.\n---\n\n# Behavior-Preserving Refactor\n\n## Input contract\n\nAccept a file, module, function, folder, or explicitly identified code region and optional goals. If scope is ambiguous, inspect the current changes and ask for a bounded target before touching unrelated code. Treat the user's requirements as the source of truth.\n\n## Procedure\n\n1. Inspect `AGENTS.md`, nested instructions, the relevant files, nearby tests, and `git status --short`. Identify unrelated work and leave it untouched.\n2. State a concise refactor plan: target, invariants, proposed changes, and verification commands.\n3. Identify behavior contracts: return values, errors, side effects, API shape, serialization, persistence, concurrency, performance-critical paths, and security checks. Preserve them.\n4. Establish a baseline by running the narrowest available tests. If tests do not exist, add focused characterization tests before making consequential edits, where feasible.\n5. Apply the smallest coherent change. Prefer named helpers, clearer control flow, reduced duplication, and bounded functions over broad rewrites. Do not silently add dependencies, change database schema, alter public APIs, or alter runtime behavior.\n6. Re-run targeted tests, type checks, and linters. When feasible, compare results with the baseline; run the broader relevant suite only when justified.\n7. Review the resulting diff (`git diff --check`, `git diff`) for regressions, extraneous edits, generated output, secrets, and accidental formatting churn.\n\n## Safety rules\n\n- Do not delete, reset, or overwrite uncommitted user work.\n- Do not run destructive commands or modify production infrastructure without explicit authorization.\n- Do not imply successful verification if a command was skipped, unavailable, or failed.\n- Keep behavior-changing opportunities in a separate recommendation rather than smuggling them into the refactor.\n- Never commit or push unless the user requests it.\n\n## Output contract\n\nReturn four concise sections:\n\n- <strong>**Scope:**<\/strong> files\/functions changed and why.\n- <strong>**Behavior preserved:**<\/strong> key invariants and any limitations.\n- <strong>**Verification:**<\/strong> exact commands and actual outcomes; explicitly note tests not run.\n- <strong>**Risks \/ follow-ups:**<\/strong> any remaining issues or suggested separate improvements.\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>.agents\/skills\/refactor\/agents\/openai.yaml<\/code>&nbsp;\u2014 explicit-only invocation<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>interface:\n  display_name: \"Refactor\"\n  short_description: \"Safely refactor existing code without behavior changes\"\n  default_prompt: \"Use $refactor to refactor the specified files while preserving behavior and verifying tests.\"\npolicy:\n  allow_implicit_invocation: false\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Invocation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Launch Codex from repository root:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>codex\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then type in the Codex composer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$refactor Refactor src\/services\/userService.ts to reduce complexity.\nPreserve public APIs, return values, error types, and database behavior.\nRun focused tests, type checks, and show the changed files.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The skill doesn&#8217;t require an&nbsp;<code>AGENTS.md<\/code>&nbsp;reference to load when invoked explicitly, but the repository&#8217;s&nbsp;<code>AGENTS.md<\/code>&nbsp;provides independent operating rules.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. Dynamic recognition skill:&nbsp;<code>db-performance-review<\/code>&nbsp;(complete source)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Automatic intent matching works through the frontmatter&nbsp;<code>description<\/code>. The name and description tell Codex the circumstances in which this review is relevant, and the Markdown body defines&nbsp;<strong>what to do after selection<\/strong>. Set&nbsp;<code>allow_implicit_invocation: true<\/code>&nbsp;or omit the&nbsp;<code>policy<\/code>&nbsp;entry (true by default). There is no file-change event handler in the skill frontmatter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>.agents\/skills\/db-performance-review\/SKILL.md<\/code>&nbsp;\u2014 implicit review workflow<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>---\nname: db-performance-review\ndescription: Review database query, SQL migration, ORM query, or schema changes for performance regressions. Use when tasks involve editing or reviewing .sql or .prisma files, database schemas, indexes, query builders, ORM repositories, or data access paths; assess query plans, indexes, cardinality, locks, and migration safety.\n---\n\n# Database Performance Review\n\n## Input contract\n\nAccept a planned or completed change to SQL queries, migration files, Prisma schemas, ORM query builders, or data access modules. Do not treat every code edit as database work.\n\n## Procedure\n\n1. Read repository instructions (`AGENTS.md`) and relevant schema and query files. Determine the database engine and deployment context; do not assume PostgreSQL merely because the example checks PostgreSQL-style SQL.\n2. Find changed database files using `git diff --name-only`, `git status --short`, and the user-specified target. Do not overwrite unrelated changes.\n3. Run the repository's deterministic check, if present:\n   `python3 .codex\/hooks\/db_performance.py --check-all`\n   Treat this as an anti-pattern scan, <strong>**not**<\/strong> as proof of a fast query. Report if the checker or runtime is unavailable.\n4. For each material query change, inspect filters, joins, sorting, pagination, returned column count, N+1 risks, parameterization, cardinality, and index coverage.\n5. For schema and migration changes, inspect lock duration, backfill strategy, index creation, uniqueness checks, transactional behavior, deployment compatibility, and rollback or forward-fix options. Do not execute migrations as part of a review.\n6. When a suitable non-production database is available <strong>**and the user authorizes database access**<\/strong>, obtain a query plan with `EXPLAIN (FORMAT JSON)` using representative parameters; verify statistics and consider row-estimate errors. Obtain actual timings with `EXPLAIN ANALYZE` only when safe and separately authorized. Never run explain-analyze on a write statement against production.\n7. Compare plans or benchmark distributions when a real baseline exists; otherwise describe risk hypotheses and the exact evidence still needed. Never invent speedups or execution times.\n8. If performance risks are material, propose a minimal query\/index\/schema fix, test it, and report whether the data supports the change.\n\n## Risk and confidentiality rules\n\n- Never print connection strings, passwords, literal customer records, or full production query parameters.\n- Do not bypass sandbox approvals or silently access an external database.\n- Never introduce a production migration solely to satisfy a heuristic warning.\n- Do not claim that a static regex scanner proves index usage, actual latency, or overall query efficiency.\n- Distinguish advice from mandatory project policy and label unverified hypotheses clearly.\n\n## Output contract\n\nReturn a compact review with: <strong>**Changed paths**<\/strong>, <strong>**Findings and severity**<\/strong>, <strong>**Indexes\/plans needed**<\/strong>, <strong>**Commands run and outcomes**<\/strong>, and <strong>**Recommended follow-ups**<\/strong>. If the review was automatically requested by a lifecycle hook, focus on the file(s) flagged by that event.\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>.agents\/skills\/db-performance-review\/agents\/openai.yaml<\/code>&nbsp;\u2014 automatic intent consideration<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>interface:\n  display_name: \"DB Performance Review\"\n  short_description: \"Check database changes for query and schema risks\"\n  default_prompt: \"Use $db-performance-review to inspect the changed SQL, ORM, and schema files for performance risks.\"\npolicy:\n  allow_implicit_invocation: true\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Prompts to test dynamic selection<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>We changed our Prisma schema and added an index.\nReview the migration for locking and query-plan regressions.\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Please optimize the SQL query in db\/queries\/list-active-users.sql.\nCheck filters, indexes, sorting, and pagination.\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Change the button color in src\/components\/Header.tsx.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In the first two prompts, Codex&nbsp;<strong>should be encouraged<\/strong>&nbsp;by the description to choose&nbsp;<code>db-performance-review<\/code>; the third should not match. This remains a model selection,&nbsp;<strong>not a hard enforcement guarantee<\/strong>. If you need guaranteed static checks for certain file writes, use the hook in the next section.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6. Deterministic database change check with Codex hooks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The starter includes a real&nbsp;<code>SessionStart<\/code>&nbsp;+&nbsp;<code>PostToolUse<\/code>&nbsp;implementation rather than pretending the skill itself can subscribe to filesystem events.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How the hook works<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>A trusted\u00a0<code>SessionStart<\/code>\u00a0handler inventories tracked and untracked DB-related files and stores SHA-256 hashes\u00a0<strong>inside Git metadata<\/strong>\u00a0(no repository pollution).<\/li>\n\n\n\n<li>Codex calls a supported tool. The matching\u00a0<code>PostToolUse<\/code>\u00a0handler runs synchronously after the tool finishes.<\/li>\n\n\n\n<li>The Python script inventories relevant files again and compares hashes with the previous snapshot.<\/li>\n\n\n\n<li>If no tracked DB-related file changed, it exits silently.<\/li>\n\n\n\n<li>For a detected change, it runs static SQL anti-pattern detection (e.g.,\u00a0<code>SELECT *<\/code>, leading-wildcard\u00a0<code>LIKE<\/code>,\u00a0<code>ORDER BY RANDOM()<\/code>) and sends concise additional context to Codex. The script is\u00a0<strong>advisory by default<\/strong>.<\/li>\n\n\n\n<li>The optional CI job runs the checker with\u00a0<code>--strict<\/code>, failing the PR for flagged SQL anti-patterns.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">This design tracks&nbsp;<code>*.sql<\/code>,&nbsp;<code>*.prisma<\/code>, and selected database\/queries\/repository source modules. Update&nbsp;<code>is_db_path()<\/code>&nbsp;to reflect your actual organization. The static checker&nbsp;<strong>cannot determine real query latency or index usage<\/strong>; that needs representative query plans and benchmarks from an approved database.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Event boundaries:<\/strong>&nbsp;Hooks work only for supported local tool events while a trusted, enabled Codex session is running. They do not observe arbitrary external editor saves in real time, cannot undo a completed tool call, and are not a substitute for CI or database-specific benchmarking. If all writes must be monitored regardless of source, use a filesystem watcher or an editor-native watcher plus CI.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6.1 Hook configuration<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\"><code>.codex\/hooks.json<\/code>&nbsp;\u2014 actual hook event registration<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"description\": \"Database change detection and static performance review for Codex local sessions.\",\n  \"hooks\": {\n    \"SessionStart\": &#91;\n      {\n        \"matcher\": \"startup|resume|clear|compact|fork\",\n        \"hooks\": &#91;\n          {\n            \"type\": \"command\",\n            \"command\": \"python3 \\\"$(git rev-parse --show-toplevel)\/.codex\/hooks\/db_performance.py\\\" --seed\",\n            \"timeout\": 30,\n            \"statusMessage\": \"Initializing database change detection\"\n          }\n        ]\n      }\n    ],\n    \"PostToolUse\": &#91;\n      {\n        \"matcher\": \"Bash|apply_patch|Edit|Write\",\n        \"hooks\": &#91;\n          {\n            \"type\": \"command\",\n            \"command\": \"python3 \\\"$(git rev-parse --show-toplevel)\/.codex\/hooks\/db_performance.py\\\" --post\",\n            \"timeout\": 45,\n            \"statusMessage\": \"Checking database file changes\"\n          }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why the matcher is&nbsp;<code>Bash|apply_patch|Edit|Write<\/code>:<\/strong>&nbsp;Codex maps shell\/unified-exec commands to&nbsp;<code>Bash<\/code>. Its&nbsp;<code>apply_patch<\/code>&nbsp;path may be matched by the canonical name or&nbsp;<code>Edit<\/code>\/<code>Write<\/code>&nbsp;aliases. The matcher filters tool names, not filenames. File selection happens in our Python script. For MCP-backed edits, add the actual MCP tool name to the matcher after checking the installed hook input schema. Not all tool paths support hooks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Trust:<\/strong>&nbsp;Codex reviews new or changed non-managed hooks before running them. From an interactive Codex session, use&nbsp;<code>\/hooks<\/code>&nbsp;to inspect and trust\/reject handlers. Never bypass hook trust casually; a repository hook can execute arbitrary local code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6.2 Full hook implementation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The following is working Python 3 standard-library code. The source is included in the accompanying starter archive so you can copy it as a file without extracting it from this guide.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>.codex\/hooks\/db_performance.py<\/code>&nbsp;\u2014 hash-based change tracking and static checks<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code><em>#!\/usr\/bin\/env python3<\/em>\n\"\"\"Codex database-change guardrail, Python standard library only.\n\n--seed: Snapshot database-related files on SessionStart.\n--post: Compare the snapshot on PostToolUse; inspect only changed files.\n--check-all: Scan all database-related files (manual\/advisory or --strict).\n--ci --base SHA: Scan only DB-related files changed in a pull request.\n\nThis is a *static anti-pattern check*. It is not an EXPLAIN or runtime benchmark.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nfrom contextlib import contextmanager\nimport hashlib\nimport json\nimport os\nfrom pathlib import Path, PurePosixPath\nimport re\nimport subprocess\nimport sys\nimport tempfile\nimport time\n\ntry:\n    import fcntl  <em># POSIX\/macOS; when unavailable, atomic lockfile is used instead.<\/em>\nexcept ImportError:\n    fcntl = None\n\n\nRULES = (\n    (\n        \"SELECT_STAR\",\n        re.compile(r\"\\bSELECT\\s+\\*(?=\\s|,)\", re.IGNORECASE),\n        \"Avoid SELECT * in frequently called queries; select needed columns.\",\n    ),\n    (\n        \"LEADING_WILDCARD\",\n        re.compile(r\"\\b(?:LIKE|ILIKE)\\s+&#91;'\\\"]%\", re.IGNORECASE),\n        \"A leading-wildcard LIKE often cannot use a normal B-tree index.\",\n    ),\n    (\n        \"ORDER_BY_RANDOM\",\n        re.compile(r\"\\bORDER\\s+BY\\s+(?:RANDOM|RAND)\\s*\\(\", re.IGNORECASE),\n        \"ORDER BY RANDOM()\/RAND() is often expensive for large results.\",\n    ),\n)\n\nCODE_EXTENSIONS = {\".ts\", \".tsx\", \".js\", \".jsx\", \".py\", \".go\", \".rs\", \".java\", \".kt\"}\nDB_DIRS = {\"db\", \"database\", \"queries\", \"query\", \"prisma\", \"migrations\", \"migration\", \"repositories\"}\n\n\ndef is_db_path(path: str) -&gt; bool:\n    \"\"\"Conservative file-path classifier; adapt for each repository.\"\"\"\n    parts = PurePosixPath(path.replace(\"\\\\\", \"\/\")).parts\n    if not parts or any(part.startswith(\".\") for part in parts):\n        return False\n    name = parts&#91;-1].lower()\n    suffix = PurePosixPath(name).suffix.lower()\n    if suffix in {\".sql\", \".prisma\"}:\n        return True\n    if suffix not in CODE_EXTENSIONS:\n        return False\n    if any(p.lower() in DB_DIRS for p in parts&#91;:-1]):\n        return True\n    return any(token in name for token in (\".query.\", \".queries.\", \".repository.\", \".schema.\"))\n\n\ndef sql_issues(text: str) -&gt; list&#91;dict&#91;str, object]]:\n    \"\"\"Return advisory issues with line numbers and inline suppression support.\n\n    To suppress a known false positive, put the following on the same or\n    immediately preceding line, with review justification in the PR:\n    -- codex-perf-ignore: SELECT_STAR\n    \"\"\"\n    lines = text.splitlines()\n    results: list&#91;dict&#91;str, object]] = &#91;]\n    for rule_name, pattern, explanation in RULES:\n        for match in pattern.finditer(text):\n            line_number = text.count(\"\\n\", 0, match.start()) + 1\n            current = lines&#91;line_number - 1] if line_number &lt;= len(lines) else \"\"\n            previous = lines&#91;line_number - 2] if line_number &gt;= 2 else \"\"\n            marker = f\"codex-perf-ignore: {rule_name}\"\n            if marker in current or marker in previous:\n                continue\n            results.append({\"rule\": rule_name, \"line\": line_number, \"advice\": explanation})\n    return sorted(results, key=lambda issue: (int(issue&#91;\"line\"]), str(issue&#91;\"rule\"])))\n\n\ndef git_run(cwd: Path, *args: str) -&gt; bytes:\n    return subprocess.check_output(\n        &#91;\"git\", \"-C\", str(cwd), *args], stderr=subprocess.DEVNULL\n    )\n\n\ndef git_root(cwd: Path) -&gt; Path:\n    return Path(os.fsdecode(git_run(cwd, \"rev-parse\", \"--show-toplevel\")).strip()).resolve()\n\n\ndef git_dir(root: Path) -&gt; Path:\n    path = Path(os.fsdecode(git_run(root, \"rev-parse\", \"--git-dir\")).strip())\n    return (root \/ path).resolve() if not path.is_absolute() else path.resolve()\n\n\ndef tracked_and_untracked(root: Path) -&gt; list&#91;str]:\n    out = git_run(root, \"ls-files\", \"--cached\", \"--others\", \"--exclude-standard\", \"-z\")\n    paths = {os.fsdecode(p) for p in out.split(b\"\\0\") if p}\n    return sorted(path for path in paths if is_db_path(path))\n\n\ndef digest(path: Path) -&gt; str:\n    hash_obj = hashlib.sha256()\n    with path.open(\"rb\") as handle:\n        while True:\n            part = handle.read(1024 * 1024)\n            if not part:\n                break\n            hash_obj.update(part)\n    return hash_obj.hexdigest()\n\n\ndef snapshot(root: Path) -&gt; dict&#91;str, str]:\n    found: dict&#91;str, str] = {}\n    for relative in tracked_and_untracked(root):\n        path = root \/ relative\n        if path.is_file() and not path.is_symlink():\n            found&#91;relative] = digest(path)\n    return found\n\n\ndef state_path(root: Path, session: str) -&gt; Path:\n    <em># Hash session IDs so unexpected characters can't escape the git metadata directory.<\/em>\n    token = hashlib.sha256(session.encode(\"utf-8\")).hexdigest()&#91;:24]\n    return git_dir(root) \/ \"codex-db-hooks\" \/ f\"{token}.json\"\n\n\n@contextmanager\ndef locked(path: Path):\n    \"\"\"Synchronize parallel tool-completion callbacks for one session.\"\"\"\n    path.parent.mkdir(parents=True, exist_ok=True)\n    if fcntl is not None:\n        with (path.parent \/ (path.name + \".lock\")).open(\"a+b\") as handle:\n            fcntl.flock(handle.fileno(), fcntl.LOCK_EX)\n            try:\n                yield\n            finally:\n                fcntl.flock(handle.fileno(), fcntl.LOCK_UN)\n        return\n    lockfile = path.parent \/ (path.name + \".lock\")\n    fd = None\n    for _ in range(100):\n        try:\n            fd = os.open(lockfile, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)\n            break\n        except FileExistsError:\n            <em># Recover abandoned lockfiles on non-POSIX hosts.<\/em>\n            if lockfile.exists() and time.time() - lockfile.stat().st_mtime &gt; 120:\n                lockfile.unlink(missing_ok=True)\n            time.sleep(0.05)\n    if fd is None:\n        raise TimeoutError(f\"Could not lock {path}\")\n    try:\n        yield\n    finally:\n        os.close(fd)\n        lockfile.unlink(missing_ok=True)\n\n\ndef write_state(path: Path, content: dict&#91;str, str]) -&gt; None:\n    path.parent.mkdir(parents=True, exist_ok=True)\n    tmp_name = \"\"\n    try:\n        with tempfile.NamedTemporaryFile(\n            mode=\"w\", encoding=\"utf-8\", dir=path.parent, prefix=\"state-\", delete=False\n        ) as handle:\n            json.dump(content, handle, sort_keys=True)\n            tmp_name = handle.name\n        os.replace(tmp_name, path)\n    finally:\n        if tmp_name and os.path.exists(tmp_name):\n            os.unlink(tmp_name)\n\n\ndef read_event() -&gt; dict&#91;str, object]:\n    raw = sys.stdin.read()\n    if not raw.strip():\n        return {}\n    event = json.loads(raw)\n    if not isinstance(event, dict):\n        raise ValueError(\"Hook event must be a JSON object\")\n    return event\n\n\ndef read_issue_list(root: Path, paths: list&#91;str]) -&gt; list&#91;tuple&#91;str, dict&#91;str, object]]]:\n    all_issues: list&#91;tuple&#91;str, dict&#91;str, object]]] = &#91;]\n    for relative in paths:\n        path = root \/ relative\n        if path.suffix.lower() != \".sql\" or not path.is_file():\n            continue\n        text = path.read_text(encoding=\"utf-8\", errors=\"replace\")\n        for issue in sql_issues(text):\n            all_issues.append((relative, issue))\n    return all_issues\n\n\ndef format_issues(issues: list&#91;tuple&#91;str, dict&#91;str, object]]]) -&gt; list&#91;str]:\n    return &#91;\n        f\"{path}:{issue&#91;'line']} &#91;{issue&#91;'rule']}] {issue&#91;'advice']}\"\n        for path, issue in issues\n    ]\n\n\ndef seed(root: Path, event: dict&#91;str, object]) -&gt; int:\n    session = str(event.get(\"session_id\") or \"manual\")\n    path = state_path(root, session)\n    with locked(path):\n        write_state(path, snapshot(root))\n    return 0\n\n\ndef post(root: Path, event: dict&#91;str, object]) -&gt; int:\n    session = str(event.get(\"session_id\") or \"manual\")\n    path = state_path(root, session)\n    with locked(path):\n        current = snapshot(root)\n        if not path.exists():\n            write_state(path, current)\n            print(json.dumps({\n                \"hookSpecificOutput\": {\n                    \"hookEventName\": \"PostToolUse\",\n                    \"additionalContext\": \"DB file tracking initialized without SessionStart baseline; the first earlier edit cannot be determined.\"\n                }\n            }))\n            return 0\n        previous = json.loads(path.read_text(encoding=\"utf-8\"))\n        write_state(path, current)\n    changed = sorted(\n        path for path in (set(previous) | set(current))\n        if previous.get(path) != current.get(path)\n    )\n    if not changed:\n        return 0  <em># Silent after unrelated tool calls.<\/em>\n    issues = read_issue_list(root, &#91;path for path in changed if path in current])\n    summary = &#91;\n        f\"DB change guardrail checked {len(changed)} modified DB-related file(s):\",\n        *&#91;f\"- {path}\" for path in changed&#91;:20]],\n    ]\n    if len(changed) &gt; 20:\n        summary.append(f\"- ... plus {len(changed) - 20} more\")\n    if issues:\n        summary.append(\"Static SQL performance smells (verify against actual query plans):\")\n        summary.extend(f\"- {line}\" for line in format_issues(issues)&#91;:20])\n    else:\n        summary.append(\"Static heuristics found no flagged SQL patterns; this is NOT a runtime performance pass.\")\n    summary.append(\"Consider $db-performance-review for index, migration and plan-level review.\")\n    payload: dict&#91;str, object] = {\n        \"hookSpecificOutput\": {\n            \"hookEventName\": \"PostToolUse\",\n            \"additionalContext\": \"\\n\".join(summary),\n        }\n    }\n    if issues:\n        payload&#91;\"systemMessage\"] = f\"Database performance guardrail found {len(issues)} advisory issue(s).\"\n    print(json.dumps(payload))\n    return 0\n\n\ndef check_all(root: Path, strict: bool) -&gt; int:\n    files = tracked_and_untracked(root)\n    issues = read_issue_list(root, files)\n    print(f\"DB performance static scan: {len(files)} tracked\/untracked DB-related files.\")\n    if not issues:\n        print(\"No SQL anti-patterns flagged. This is not an execution benchmark.\")\n    for line in format_issues(issues):\n        print(line)\n    return 1 if strict and issues else 0\n\n\ndef check_ci(root: Path, base: str, strict: bool) -&gt; int:\n    changed_raw = git_run(root, \"diff\", \"--name-only\", \"--diff-filter=ACMRT\", \"-z\", f\"{base}...HEAD\")\n    files = sorted({\n        os.fsdecode(p) for p in changed_raw.split(b\"\\0\")\n        if p and is_db_path(os.fsdecode(p))\n    })\n    issues = read_issue_list(root, files)\n    print(f\"DB performance pull-request scan: {len(files)} changed DB-related files.\")\n    if not issues:\n        print(\"No SQL anti-patterns flagged. Plan inspection remains manual.\")\n    for line in format_issues(issues):\n        print(line)\n    return 1 if strict and issues else 0\n\n\ndef main() -&gt; int:\n    parser = argparse.ArgumentParser(description=__doc__)\n    mode = parser.add_mutually_exclusive_group(required=True)\n    mode.add_argument(\"--seed\", action=\"store_true\", help=\"Record initial DB file hashes\")\n    mode.add_argument(\"--post\", action=\"store_true\", help=\"Check post-tool DB changes\")\n    mode.add_argument(\"--check-all\", action=\"store_true\", help=\"Scan all monitored DB files\")\n    mode.add_argument(\"--ci\", action=\"store_true\", help=\"Check changes relative to --base\")\n    parser.add_argument(\"--base\", help=\"Merge base commit for --ci\")\n    parser.add_argument(\"--strict\", action=\"store_true\", help=\"Fail manual\/CI scan on advisories\")\n    args = parser.parse_args()\n    if args.ci and not args.base:\n        parser.error(\"--ci requires --base\")\n    try:\n        event = read_event() if (args.seed or args.post) else {}\n        cwd = Path(str(event.get(\"cwd\") or os.getcwd()))\n        root = git_root(cwd)\n        if args.seed:\n            return seed(root, event)\n        if args.post:\n            return post(root, event)\n        if args.ci:\n            return check_ci(root, args.base, args.strict)\n        return check_all(root, args.strict)\n    except (subprocess.CalledProcessError, OSError, ValueError, TimeoutError) as exc:\n        print(f\"DB guardrail could not complete: {exc}\", file=sys.stderr)\n        return 1\n\n\nif __name__ == \"__main__\":\n    raise SystemExit(main())\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">6.3 Advisory versus enforceable validation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The hook returns a&nbsp;<code>PostToolUse<\/code>&nbsp;<code>additionalContext<\/code>&nbsp;message to tell Codex what changed. When it finds a heuristic issue, it also adds a warning via&nbsp;<code>systemMessage<\/code>. This&nbsp;<strong>does not rollback<\/strong>&nbsp;a completed edit. To prevent merges, use the CI job below.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For actual database plan validation, extend this starter with an application-specific,&nbsp;<strong>non-production<\/strong>&nbsp;database harness. Example of an approved PostgreSQL read-only plan inspection:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BEGIN READ ONLY;\nSET LOCAL statement_timeout = '5s';\nEXPLAIN (FORMAT JSON)\nSELECT id, email\nFROM users\nWHERE status = 'active'\nORDER BY created_at DESC\nLIMIT 20;\nROLLBACK;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Execute only after approval against a suitable non-production database with representative data and parameter values.&nbsp;<code>EXPLAIN<\/code>&nbsp;without&nbsp;<code>ANALYZE<\/code>&nbsp;estimates a plan, it does not measure actual latency.&nbsp;<code>EXPLAIN ANALYZE<\/code>&nbsp;executes statements and should not be used casually, especially on writes or production.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7. Independent CI enforcement (complete source)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The workflow is intentionally independent of Codex invocation, hooks, session state, or agent choice. It scans only DB files changed against a PR base, uses the standard-library test suite, and exits nonzero for the three configured SQL anti-patterns. Your team can replace the heuristics with dialect-specific linting, schema validators, migration checks, or an approved plan-regression suite as your database requirements mature.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>.github\/workflows\/db-performance.yml<\/code>&nbsp;\u2014 GitHub Actions pull-request guardrail<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Database performance guardrails\non:\n  pull_request:\n    paths:\n      - '**\/*.sql'\n      - '**\/*.prisma'\n      - '**\/db\/**'\n      - '**\/database\/**'\n      - '**\/queries\/**'\n      - '**\/migrations\/**'\n      - '.codex\/hooks\/db_performance.py'\n      - '.github\/workflows\/db-performance.yml'\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  db-static-check:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout with base history\n        uses: actions\/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Set up Python\n        uses: actions\/setup-python@v5\n        with:\n          python-version: '3.12'\n      - name: Unit tests\n        run: python3 -m unittest discover -s tests -v\n      - name: Check modified DB files\n        if: github.event_name == 'pull_request'\n        env:\n          BASE_SHA: ${{ github.event.pull_request.base.sha }}\n        run: python3 .codex\/hooks\/db_performance.py --ci --base \"$BASE_SHA\" --strict\n      - name: Check all DB files on manual run\n        if: github.event_name == 'workflow_dispatch'\n        run: python3 .codex\/hooks\/db_performance.py --check-all --strict\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>tests\/test_db_performance.py<\/code>&nbsp;\u2014 repeatable local\/CI tests<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\"\"\"Fast tests for deterministic DB-change detection, no external packages.\"\"\"\n\nfrom __future__ import annotations\n\nimport importlib.util\nimport json\nfrom pathlib import Path\nimport subprocess\nimport sys\nimport tempfile\nimport unittest\n\nSCRIPT = Path(__file__).resolve().parents&#91;1] \/ \".codex\" \/ \"hooks\" \/ \"db_performance.py\"\nspec = importlib.util.spec_from_file_location(\"db_performance\", SCRIPT)\nassert spec and spec.loader\nmodule = importlib.util.module_from_spec(spec)\nspec.loader.exec_module(module)\n\n\nclass ClassifierTests(unittest.TestCase):\n    def test_database_path_detection(self):\n        positives = &#91;\n            \"db\/queries\/list-users.sql\", \"prisma\/schema.prisma\", \"src\/db\/users.ts\",\n            \"src\/repositories\/user_repository.py\", \"backend\/migrations\/001_add.sql\",\n            \"src\/user.query.ts\", \"queries\/get-users.sql\",\n        ]\n        negatives = &#91;\"src\/components\/card.tsx\", \"README.md\", \".git\/index\", \"node_modules\/x.js\"]\n        for path in positives:\n            with self.subTest(path=path):\n                self.assertTrue(module.is_db_path(path))\n        for path in negatives:\n            with self.subTest(path=path):\n                self.assertFalse(module.is_db_path(path))\n\n    def test_static_sql_rule_and_suppression(self):\n        bad = \"SELECT * FROM users;\\nSELECT id FROM users ORDER BY RANDOM();\\n\"\n        self.assertEqual(&#91;v&#91;\"rule\"] for v in module.sql_issues(bad)],\n                         &#91;\"SELECT_STAR\", \"ORDER_BY_RANDOM\"])\n        allowed = \"-- codex-perf-ignore: SELECT_STAR\\nSELECT * FROM lookup;\\n\"\n        self.assertEqual(module.sql_issues(allowed), &#91;])\n\n\nclass EventTests(unittest.TestCase):\n    def setUp(self):\n        self.temp = tempfile.TemporaryDirectory()\n        self.addCleanup(self.temp.cleanup)\n        self.root = Path(self.temp.name)\n        subprocess.run(&#91;\"git\", \"init\", \"-q\", str(self.root)], check=True)\n        (self.root \/ \"db\").mkdir()\n        (self.root \/ \"db\" \/ \"get.sql\").write_text(\"SELECT id FROM users;\\n\")\n        (self.root \/ \"README.md\").write_text(\"Hello\\n\")\n        self.session = \"unit-session-ABC\"\n\n    def run_script(self, *args: str, event: dict | None = None):\n        return subprocess.run(\n            &#91;sys.executable, str(SCRIPT), *args],\n            cwd=self.root, input=json.dumps(event or {\"session_id\": self.session, \"cwd\": str(self.root)}),\n            capture_output=True, text=True, check=False,\n        )\n\n    def test_ignores_unrelated_edits_and_detects_db_changes(self):\n        seeded = self.run_script(\"--seed\")\n        self.assertEqual(seeded.returncode, 0, seeded.stderr)\n\n        (self.root \/ \"README.md\").write_text(\"Changed not DB\\n\")\n        unrelated = self.run_script(\"--post\")\n        self.assertEqual(unrelated.returncode, 0, unrelated.stderr)\n        self.assertEqual(unrelated.stdout.strip(), \"\")\n\n        (self.root \/ \"db\" \/ \"get.sql\").write_text(\"SELECT * FROM users;\\n\")\n        changed = self.run_script(\"--post\")\n        self.assertEqual(changed.returncode, 0, changed.stderr)\n        payload = json.loads(changed.stdout)\n        context = payload&#91;\"hookSpecificOutput\"]&#91;\"additionalContext\"]\n        self.assertIn(\"db\/get.sql\", context)\n        self.assertIn(\"SELECT_STAR\", context)\n        self.assertIn(\"systemMessage\", payload)\n\n        repeat = self.run_script(\"--post\")\n        self.assertEqual(repeat.stdout.strip(), \"\")\n\n    def test_ci_and_check_all(self):\n        (self.root \/ \"db\" \/ \"get.sql\").write_text(\"SELECT * FROM users;\\n\")\n        advisory = self.run_script(\"--check-all\")\n        self.assertEqual(advisory.returncode, 0, advisory.stderr)\n        strict = self.run_script(\"--check-all\", \"--strict\")\n        self.assertEqual(strict.returncode, 1, strict.stderr)\n        self.assertIn(\"SELECT_STAR\", strict.stdout)\n\n        subprocess.run(&#91;\"git\", \"-C\", str(self.root), \"config\", \"user.email\", \"test@example.com\"], check=True)\n        subprocess.run(&#91;\"git\", \"-C\", str(self.root), \"config\", \"user.name\", \"Test\"], check=True)\n        (self.root \/ \"db\" \/ \"get.sql\").write_text(\"SELECT id FROM users;\\n\")\n        subprocess.run(&#91;\"git\", \"-C\", str(self.root), \"add\", \"db\/get.sql\"], check=True)\n        subprocess.run(&#91;\"git\", \"-C\", str(self.root), \"commit\", \"-qm\", \"baseline\"], check=True)\n        baseline = subprocess.check_output(&#91;\"git\", \"-C\", str(self.root), \"rev-parse\", \"HEAD\"], text=True).strip()\n        (self.root \/ \"db\" \/ \"get.sql\").write_text(\"SELECT * FROM users;\\n\")\n        subprocess.run(&#91;\"git\", \"-C\", str(self.root), \"add\", \"db\/get.sql\"], check=True)\n        subprocess.run(&#91;\"git\", \"-C\", str(self.root), \"commit\", \"-qm\", \"regression\"], check=True)\n        ci = self.run_script(\"--ci\", \"--base\", baseline, \"--strict\")\n        self.assertEqual(ci.returncode, 1, ci.stderr)\n        self.assertIn(\"SELECT_STAR\", ci.stdout)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">8. AGENTS.md: global + repository instructions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>AGENTS.md<\/code>&nbsp;is the model-level orchestration layer. It should guide how Codex works and when it should consider appropriate skills. It does&nbsp;<strong>not<\/strong>&nbsp;register new slash commands, listen to files, or override hook trust\/permissions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">8.1 Repository root&nbsp;<code>AGENTS.md<\/code><\/h3>\n\n\n\n<h3 class=\"wp-block-heading\"><code>AGENTS.md<\/code>&nbsp;\u2014 project guidance<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Repository guidance for Codex\n\n## Working agreements\n- Inspect `git status --short` before edits. Preserve unrelated work.\n- Follow existing project conventions and tests. Prefer minimal diffs.\n- Run the narrowest relevant tests and `git diff --check` after code changes.\n- Never access production databases, run destructive migrations, or add dependencies without authorization.\n\n## Skills and database changes\n- Use the `refactor` skill when I explicitly invoke `$refactor`.\n- For SQL, Prisma, migration, ORM query, or schema edits, consider the `db-performance-review` skill and inspect index \/ query-plan implications.\n- Run `python3 .codex\/hooks\/db_performance.py --check-all` when working on database changes. This is a heuristic scan, not a real execution benchmark.\n- Explain performance claims using verified `EXPLAIN`, representative measurements, and before\/after evidence. Do not invent timings.\n- The `.codex\/hooks.json` lifecycle hook adds an automatic static check on supported tool operations; GitHub Actions is the independent enforcement point.\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">8.2 Optional user-global&nbsp;<code>~\/.codex\/AGENTS.md<\/code><\/h3>\n\n\n\n<h3 class=\"wp-block-heading\"><code>docs\/optional-global-AGENTS.md<\/code>&nbsp;\u2014 copy to your Codex home if desired<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Global Codex guidance\n\n- Make minimal, reviewable changes.\n- Preserve uncommitted work.\n- Read repository AGENTS.md and nested overrides before edits.\n- Avoid production database access and destructive commands without permission.\n- Report commands executed, verification results, and tests not run.\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">8.3 Optional user configuration&nbsp;<code>~\/.codex\/config.toml<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use this only as a&nbsp;<strong>merge snippet<\/strong>: don&#8217;t overwrite an existing configuration file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>docs\/optional-config.toml<\/code>&nbsp;\u2014 hook feature and optional skill disablement<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code><em># Optional user-level ~\/.codex\/config.toml additions.<\/em>\n<em># Merge with existing config rather than overwriting it.<\/em>\n\n&#91;features]\nhooks = true\n\n<em># A different entry can disable a skill without removing its files.<\/em>\n<em># &#91;&#91;skills.config]]<\/em>\n<em># path = \"\/absolute\/path\/to\/repo\/.agents\/skills\/refactor\/SKILL.md\"<\/em>\n<em># enabled = false<\/em>\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">9. Installation and deployment: reproducible steps<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">9.1 Prerequisites<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Git and Python 3 available on\u00a0<code>PATH<\/code>.<\/li>\n\n\n\n<li>A supported Codex CLI or IDE extension that recognizes repository\u00a0<code>.agents\/skills<\/code>\u00a0and lifecycle hooks. Verify version with\u00a0<code>codex --version<\/code>.<\/li>\n\n\n\n<li>Codex installed and authenticated. One installation path is\u00a0<code>npm install -g @openai\/codex<\/code>; check the current installation docs for alternatives and enterprise restrictions.<\/li>\n\n\n\n<li>A repository in which the provided skill and hook files can be trusted and reviewed.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">9.2 Install the starter into a repository<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The accompanying&nbsp;<code>codex-skill-starter.zip<\/code>&nbsp;contains the complete tree. Always&nbsp;<strong>inspect the archive before merging<\/strong>&nbsp;with any existing&nbsp;<code>.codex\/<\/code>,&nbsp;<code>.agents\/<\/code>,&nbsp;<code>.github\/<\/code>, or&nbsp;<code>AGENTS.md<\/code>&nbsp;content.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em># Work from your existing Git repository root.<\/em>\ngit rev-parse --show-toplevel\npwd\n\n<em># Unzip into a temporary directory first, inspect, then copy or merge<\/em>\n<em># the selected files into your repository. Do not blindly overwrite AGENTS.md.<\/em>\nunzip -l \/path\/to\/codex-skill-starter.zip\nmkdir -p \/tmp\/codex-skill-install\nunzip \/path\/to\/codex-skill-starter.zip -d \/tmp\/codex-skill-install\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For manual creation without the zip, these are the top-level folders you need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir -p .agents\/skills\/refactor\/agents\nmkdir -p .agents\/skills\/db-performance-review\/agents\nmkdir -p .codex\/hooks .github\/workflows tests\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Copy each complete source code block from Sections 4\u20138 into its corresponding path. The starter ZIP already contains the same files; you need not recreate them manually when using the archive.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">9.2.1 Safe, conflict-aware installer (recommended)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The starter archive also includes&nbsp;<code>scripts\/install.py<\/code>. It checks that the destination is a Git repository root, aborts&nbsp;<strong>before writing<\/strong>&nbsp;when any existing destination differs, and copies only necessary runtime and test files. It never overwrites project configuration.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em># Run from the folder where you unpacked the starter archive.<\/em>\npython3 scripts\/install.py --repo \/absolute\/path\/to\/my-project\n\n<em># When the repo already has its own AGENTS.md, do not overwrite it.<\/em>\n<em># Merge the provided guidance manually after installation.<\/em>\npython3 scripts\/install.py --repo \/absolute\/path\/to\/my-project --skip-agents\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Complete implementation (Python 3 standard library):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>#!\/usr\/bin\/env python3<\/em>\n\"\"\"Install starter files into a Git repo without overwriting existing content.\n\nUsage: python3 scripts\/install.py --repo \/absolute\/repo\/path &#91;--skip-agents]\n\"\"\"\n\nimport argparse\nimport filecmp\nfrom pathlib import Path\nimport shutil\nimport subprocess\nimport sys\n\nKIT = Path(__file__).resolve().parents&#91;1]\nFILES = (\n    \".agents\/skills\/refactor\/SKILL.md\",\n    \".agents\/skills\/refactor\/agents\/openai.yaml\",\n    \".agents\/skills\/db-performance-review\/SKILL.md\",\n    \".agents\/skills\/db-performance-review\/agents\/openai.yaml\",\n    \".codex\/hooks.json\",\n    \".codex\/hooks\/db_performance.py\",\n    \".github\/workflows\/db-performance.yml\",\n    \"tests\/test_db_performance.py\",\n    \"AGENTS.md\",\n)\n\n\ndef main() -&gt; int:\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\"--repo\", required=True, help=\"Existing destination Git repository root\")\n    parser.add_argument(\n        \"--skip-agents\", action=\"store_true\",\n        help=\"Do not install AGENTS.md (manually merge guidance into an existing file)\",\n    )\n    args = parser.parse_args()\n    target = Path(args.repo).expanduser().resolve()\n    if not target.is_dir():\n        parser.error(f\"Repository does not exist: {target}\")\n    try:\n        actual_root = Path(subprocess.check_output(\n            &#91;\"git\", \"-C\", str(target), \"rev-parse\", \"--show-toplevel\"],\n            stderr=subprocess.DEVNULL, text=True,\n        ).strip()).resolve()\n    except subprocess.CalledProcessError:\n        parser.error(\"Destination is not inside a Git repository\")\n    if actual_root != target:\n        parser.error(f\"Use Git repository root: {actual_root}\")\n\n    chosen = &#91;name for name in FILES if not (args.skip_agents and name == \"AGENTS.md\")]\n    conflicts = &#91;\n        name for name in chosen\n        if (target \/ name).exists() and not filecmp.cmp(KIT \/ name, target \/ name, shallow=False)\n    ]\n    if conflicts:\n        print(\"ABORTED: existing destination files differ; no changes made.\", file=sys.stderr)\n        for name in conflicts:\n            print(f\"  {name}\", file=sys.stderr)\n        print(\"Review and merge these files manually. Use --skip-agents only for AGENTS.md.\", file=sys.stderr)\n        return 2\n\n    added = 0\n    for name in chosen:\n        destination = target \/ name\n        if destination.exists():\n            print(f\"UNCHANGED {name}\")\n            continue\n        destination.parent.mkdir(parents=True, exist_ok=True)\n        shutil.copy2(KIT \/ name, destination)\n        print(f\"ADDED {name}\")\n        added += 1\n    if args.skip_agents:\n        print(\"Remember to merge docs\/optional-global-AGENTS.md or starter AGENTS.md policy where appropriate.\")\n    print(f\"Installed {added} new file(s). Review with git status --short before committing.\")\n    return 0\n\n\nif __name__ == \"__main__\":\n    raise SystemExit(main())\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">9.3 Validate the local files<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>python3 -m json.tool .codex\/hooks.json &gt; \/dev\/null\npython3 -m py_compile .codex\/hooks\/db_performance.py\npython3 -m unittest discover -s tests -v\npython3 .codex\/hooks\/db_performance.py --check-all\n\ngit diff --check\ngit status --short\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Optional structural validation if you have OpenAI&#8217;s skill-creator scripts installed locally:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python3 \/path\/to\/skill-creator\/scripts\/quick_validate.py .agents\/skills\/refactor\npython3 \/path\/to\/skill-creator\/scripts\/quick_validate.py .agents\/skills\/db-performance-review\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The local test suite verifies the hash-state change detector, path classifier, rule suppression, manual scan, and CI check. No database connection is required.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">9.4 Load skills and hooks in Codex<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Start a\u00a0<strong>new Codex session from the correct Git repository directory<\/strong>\u00a0using\u00a0<code>codex<\/code>\u00a0(or open that repository in the Codex IDE extension).<\/li>\n\n\n\n<li>Inspect\u00a0<code>\/skills<\/code>\u00a0or type\u00a0<code>$<\/code>\u00a0to find\u00a0<code>refactor<\/code>\u00a0and\u00a0<code>db-performance-review<\/code>.<\/li>\n\n\n\n<li>Inspect\u00a0<code>\/hooks<\/code>. Review the repository&#8217;s\u00a0<code>SessionStart<\/code>\u00a0and\u00a0<code>PostToolUse<\/code>\u00a0hook commands, then explicitly trust them if appropriate. Codex must permit them to execute.<\/li>\n\n\n\n<li>Use the explicit\u00a0<code>$refactor ...<\/code>\u00a0prompt in Section 4.<\/li>\n\n\n\n<li>Use a database-related natural-language request without\u00a0<code>$<\/code>\u00a0and observe whether the agent selects\u00a0<code>db-performance-review<\/code>.<\/li>\n\n\n\n<li>Make a monitored SQL edit via a supported Codex tool. Watch for the database guardrail&#8217;s context or warning.<\/li>\n\n\n\n<li>If newly installed or updated skills do not appear, restart the Codex session and confirm\u00a0<code>CODEX_HOME<\/code>\u00a0and Git root. Codex documents automatic skill change detection with restart as a fallback.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">9.5 GitHub deployment<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code><em># Review first; add only the intended files.<\/em>\ngit status --short\ngit diff --check\n\n<em># Example: stage the skill and validation directories.<\/em>\ngit add AGENTS.md .agents\/skills .codex\/hooks.json .codex\/hooks\/db_performance.py \\\n  .github\/workflows\/db-performance.yml tests\/test_db_performance.py\n\ngit diff --cached --check\ngit diff --cached --stat\n\n<em># Commit\/push only when you have reviewed the changes and intend to deploy.<\/em>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On pull requests, the workflow runs tests and scans the changed DB-related files. The local hook remains a&nbsp;<strong>developer feedback mechanism<\/strong>, while CI provides independent gatekeeping.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">10. Optional legacy slash-style command:&nbsp;<code>\/prompts:refactor<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Codex&nbsp;<strong>skills are not automatically&nbsp;<code>\/refactor<\/code>&nbsp;slash commands<\/strong>. For installations that still support the deprecated custom prompt feature, the correct user-defined command syntax is&nbsp;<code>\/prompts:refactor<\/code>. These prompt files live under&nbsp;<code>$CODEX_HOME\/prompts<\/code>&nbsp;(typically&nbsp;<code>~\/.codex\/prompts<\/code>), not in repository&nbsp;<code>.agents\/skills<\/code>. This legacy adapter is local to the user.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>docs\/legacy-refactor-prompt.md<\/code>&nbsp;\u2014 optional deprecated custom prompt source<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>---\ndescription: Refactor a bounded code scope while preserving behavior\nargument-hint: FILES=&lt;paths&gt; GOAL=\"&lt;description&gt;\"\n---\n\nRefactor the following existing files: $FILES\n\nGoal: $GOAL\n\nBefore editing, inspect repository AGENTS.md and, if present, follow\n`.agents\/skills\/refactor\/SKILL.md` as the implementation contract.\nPreserve public APIs, behavior, error semantics, and unrelated changes.\nRun available targeted tests and report exactly what was verified.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Copy and invoke:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir -p ~\/.codex\/prompts\ncp docs\/legacy-refactor-prompt.md ~\/.codex\/prompts\/refactor.md\n<em># Restart Codex or start a new chat after changing custom prompts.<\/em>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Inside the Codex composer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/prompts:refactor FILES=\"src\/services\/userService.ts\" GOAL=\"extract duplicated validation logic\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This command expands a prompt. It is separate from the native&nbsp;<code>$refactor<\/code>&nbsp;skill mechanism and&nbsp;<strong>is not a supported guarantee of a literal&nbsp;<code>\/refactor<\/code>&nbsp;alias<\/strong>. Prefer&nbsp;<code>$refactor<\/code>&nbsp;for new workflows.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">11. Deployment and testing matrix<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Test<\/th><th class=\"has-text-align-left\" data-align=\"left\">Action<\/th><th class=\"has-text-align-left\" data-align=\"left\">Expected outcome<\/th><th class=\"has-text-align-left\" data-align=\"left\">Interpretation<\/th><\/tr><\/thead><tbody><tr><td>Skill frontmatter<\/td><td>Validate both&nbsp;<code>SKILL.md<\/code>&nbsp;files<\/td><td><code>name<\/code>&nbsp;and&nbsp;<code>description<\/code>&nbsp;accepted<\/td><td>Static schema pass<\/td><\/tr><tr><td>Hook JSON<\/td><td><code>python3 -m json.tool .codex\/hooks.json<\/code><\/td><td>Exit 0<\/td><td>Syntax pass, not runtime trust<\/td><\/tr><tr><td>Hook code<\/td><td><code>python3 -m py_compile ...<\/code><\/td><td>Exit 0<\/td><td>Python syntax pass<\/td><\/tr><tr><td>Unit tests<\/td><td><code>python3 -m unittest discover -s tests -v<\/code><\/td><td>All tests pass<\/td><td>Local deterministic behavior pass<\/td><\/tr><tr><td>Explicit skill<\/td><td><code>$refactor ...<\/code>&nbsp;in a new Codex session<\/td><td>Refactor workflow selected<\/td><td>Requires installed Codex runtime<\/td><\/tr><tr><td>Explicit-only policy<\/td><td>Prompt&nbsp;<code>refactor this<\/code>&nbsp;without&nbsp;<code>$<\/code><\/td><td>Should not implicitly select&nbsp;<code>refactor<\/code><\/td><td>Verify on your CLI version<\/td><\/tr><tr><td>Positive implicit intent<\/td><td>Ask to optimize query and index<\/td><td><code>db-performance-review<\/code>&nbsp;considered\/selected<\/td><td>Model-dependent, not guaranteed<\/td><\/tr><tr><td>Negative implicit intent<\/td><td>Ask to change a UI button color<\/td><td>DB skill should not be selected<\/td><td>Model-dependent selectivity<\/td><\/tr><tr><td>Hook trust<\/td><td>Open&nbsp;<code>\/hooks<\/code><\/td><td>Hook visible; review\/trust needed<\/td><td>Runtime hook source discovery<\/td><\/tr><tr><td>Clean tool event<\/td><td>Codex reads\/edits unrelated file<\/td><td>DB hook produces no change report<\/td><td>Prevents irrelevant feedback<\/td><\/tr><tr><td>Monitored SQL edit<\/td><td>Codex modifies an&nbsp;<code>.sql<\/code>&nbsp;file<\/td><td>PostToolUse emits DB change context<\/td><td>Supported tool\/event check<\/td><\/tr><tr><td>Prisma edit<\/td><td>Codex changes&nbsp;<code>schema.prisma<\/code><\/td><td>Changed file reported; plan review suggested<\/td><td>Static detection, no actual DB benchmark<\/td><\/tr><tr><td>Static anti-pattern<\/td><td>Introduce&nbsp;<code>SELECT *<\/code>&nbsp;in&nbsp;<code>.sql<\/code><\/td><td>Advisory in local session<\/td><td>Heuristic detection<\/td><\/tr><tr><td>CI static policy<\/td><td>PR introduces flagged SQL<\/td><td>CI strict job exits nonzero<\/td><td>Independent gate<\/td><\/tr><tr><td>Restart<\/td><td>Restart Codex after changed config<\/td><td>New instructions\/hooks considered<\/td><td>Test loading lifecycle<\/td><\/tr><tr><td>External IDE save<\/td><td>Edit&nbsp;<code>.sql<\/code>&nbsp;outside Codex<\/td><td>No immediate Codex hook guarantee<\/td><td>Use CI\/editor watcher as needed<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">11.1 Test hook behavior without Codex (isolated Git repository)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Run these commands from your target repository root; they create a disposable&nbsp;<strong>temporary<\/strong>&nbsp;repo and do not alter your application:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CHECKER=\"$PWD\/.codex\/hooks\/db_performance.py\"\nTMP=\"$(mktemp -d)\"\ngit -C \"$TMP\" init -q\nmkdir -p \"$TMP\/db\/queries\"\nprintf 'SELECT id FROM users;\\n' &gt; \"$TMP\/db\/queries\/list.sql\"\n\nprintf '{\"session_id\":\"smoke\",\"cwd\":\"%s\",\"hook_event_name\":\"SessionStart\",\"source\":\"startup\"}\\n' \"$TMP\" \\\n  | python3 \"$CHECKER\" --seed\n\nprintf 'SELECT * FROM users;\\n' &gt; \"$TMP\/db\/queries\/list.sql\"\nprintf '{\"session_id\":\"smoke\",\"cwd\":\"%s\",\"hook_event_name\":\"PostToolUse\",\"tool_name\":\"apply_patch\"}\\n' \"$TMP\" \\\n  | python3 \"$CHECKER\" --post\n\n<em># Expected JSON contains db\/queries\/list.sql and SELECT_STAR.<\/em>\n( cd \"$TMP\" &amp;&amp; python3 \"$CHECKER\" --check-all --strict ) || true\n\nrm -rf \"$TMP\"  <em># Only the disposable directory created above.<\/em>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For a real Codex hook integration test, use a&nbsp;<em>disposable repository<\/em>, run&nbsp;<code>codex<\/code>&nbsp;from its root, review&nbsp;<code>\/hooks<\/code>, then ask Codex to change&nbsp;<code>db\/queries\/list.sql<\/code>&nbsp;through a supported local tool. The Python unit test does not prove the hook is trusted, enabled, or delivered by a real Codex runtime.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">11.2 Explicit versus implicit routing acceptance tests<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Positive explicit:<\/strong>&nbsp;<code>$refactor simplify src\/helpers\/strings.ts without changing behavior<\/code>&nbsp;should use&nbsp;<code>refactor<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Negative explicit-only:<\/strong>&nbsp;<code>simplify src\/helpers\/strings.ts<\/code>&nbsp;should not implicitly load an explicit-only skill on a compliant runtime. Check actual behavior; legacy versions have had bugs with explicit-only visibility.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Positive implicit:<\/strong>&nbsp;<code>Review this SQL migration for index and locking risks<\/code>&nbsp;should encourage&nbsp;<code>db-performance-review<\/code>&nbsp;activation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Negative implicit:<\/strong>&nbsp;<code>Change the text of the login button<\/code>&nbsp;should not trigger database work.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because implicit selection is model-driven, run multiple prompts and record which skill Codex actually used before considering the routing description production-ready.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">12. Updating, rollout, rollback, and security<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Updating safely<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a branch; inspect clean\/dirty worktree status and preserve unrelated changes.<\/li>\n\n\n\n<li>Change\u00a0<code>SKILL.md<\/code>\u00a0and\u00a0<code>agents\/openai.yaml<\/code>\u00a0together when refining routing conditions.<\/li>\n\n\n\n<li>Change\u00a0<code>.codex\/hooks.json<\/code>\u00a0and\u00a0<code>.codex\/hooks\/db_performance.py<\/code>\u00a0together when changing event semantics. Require fresh hook trust approval when Codex asks.<\/li>\n\n\n\n<li>Add unit tests for every new path pattern, SQL heuristic, or suppression behavior.<\/li>\n\n\n\n<li>Run local validation, then test in a fresh Codex session with positive and negative prompts.<\/li>\n\n\n\n<li>Review and merge through normal PR controls; use CI and code owner approval as appropriate.<\/li>\n\n\n\n<li>Roll back by reverting the Git change, or temporarily set\u00a0<code>[[skills.config]] enabled = false<\/code>\u00a0for a specific local skill. Disable hooks with\u00a0<code>[features] hooks = false<\/code>\u00a0only as an emergency local option; this disables\u00a0<strong>all<\/strong>\u00a0hooks, not just DB checks.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Security boundaries<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Treat any skills and hook scripts from external repositories as executable\/untrusted content. Read them before granting access.<\/li>\n\n\n\n<li>Skill descriptions\/instructions do\u00a0<strong>not<\/strong>\u00a0elevate permission or silently authorize secrets and production access.<\/li>\n\n\n\n<li>Hook trust is separate from regular task approval. Non-managed hooks may be skipped until explicitly trusted.<\/li>\n\n\n\n<li>Keep local hook scripts out of secret material and don&#8217;t print database credentials or user data in hook outputs.<\/li>\n\n\n\n<li>Use read-only, scoped staging database credentials for plans and tests; never assume\u00a0<code>EXPLAIN ANALYZE<\/code>\u00a0is side-effect-free.<\/li>\n\n\n\n<li>A\u00a0<code>PostToolUse<\/code>\u00a0hook only reacts\u00a0<strong>after<\/strong>\u00a0a tool has run. For strict prevention, combine\u00a0<code>PreToolUse<\/code>\u00a0checks, code review, CI, and database permissions; not every tool path is interceptable.<\/li>\n\n\n\n<li>In very large monorepos, full-content hashing after every supported tool call has performance overhead. Narrow\u00a0<code>is_db_path<\/code>\u00a0rules or switch to a targeted event\/index strategy if needed.<\/li>\n\n\n\n<li>The\u00a0<code>--strict<\/code>\u00a0checker intentionally enforces only the three included heuristics; compliance with those rules is\u00a0<strong>not<\/strong>\u00a0proof of performant SQL.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">13. Troubleshooting<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Symptom<\/th><th class=\"has-text-align-left\" data-align=\"left\">Diagnosis<\/th><th class=\"has-text-align-left\" data-align=\"left\">Fix<\/th><\/tr><\/thead><tbody><tr><td><code>$refactor<\/code>&nbsp;unavailable<\/td><td>Wrong directory, missing file, stale discovery, explicit-only regression<\/td><td>Check&nbsp;<code>pwd<\/code>, Git root,&nbsp;<code>SKILL.md<\/code>,&nbsp;<code>\/skills<\/code>, then restart Codex<\/td><\/tr><tr><td><code>\/refactor<\/code>&nbsp;not recognized<\/td><td>Native skill isn&#8217;t a literal slash command<\/td><td>Use&nbsp;<code>$refactor<\/code>; legacy prompt is&nbsp;<code>\/prompts:refactor<\/code><\/td><\/tr><tr><td>DB skill doesn&#8217;t auto-select<\/td><td>Description too vague or task does not clearly match<\/td><td>Front-load trigger terms; test many positive\/negative prompts<\/td><\/tr><tr><td>Hook never runs<\/td><td>Hook untrusted, disabled, wrong config path, unsupported tool, or not a local session<\/td><td>Inspect&nbsp;<code>\/hooks<\/code>,&nbsp;<code>.codex\/hooks.json<\/code>,&nbsp;<code>[features]<\/code>, and tool event coverage<\/td><\/tr><tr><td>No DB warning for first change<\/td><td>Missing&nbsp;<code>SessionStart<\/code>&nbsp;baseline or hook loaded late<\/td><td>Restart and trust hook before editing; verify in fresh session<\/td><\/tr><tr><td>Hook fires too often<\/td><td>Database matcher is too broad<\/td><td>Narrow&nbsp;<code>is_db_path()<\/code>; tune matcher if relevant<\/td><\/tr><tr><td><code>git<\/code>&nbsp;not found<\/td><td>Missing Git executable or outside Git repo<\/td><td>Install Git and run from a Git worktree<\/td><\/tr><tr><td>Query check passes but query is slow<\/td><td>Static checks don&#8217;t measure database execution<\/td><td>Use approved staging&nbsp;<code>EXPLAIN<\/code>, statistics, and benchmarks<\/td><\/tr><tr><td>CI fails on intentional&nbsp;<code>SELECT *<\/code><\/td><td>Deliberate false positive<\/td><td>Add an adjacent documented&nbsp;<code>-- codex-perf-ignore: SELECT_STAR<\/code>&nbsp;and justify in review<\/td><\/tr><tr><td>Windows native hook command fails<\/td><td>Example uses POSIX shell command substitution<\/td><td>Use WSL or adapt&nbsp;<code>commandWindows<\/code>&nbsp;to your installed shell and absolute repo path<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">14. Official sources and further reading<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"https:\/\/learn.chatgpt.com\/docs\/build-skills\">Build skills \u2014 current Codex\/ChatGPT documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/learn.chatgpt.com\/docs\/custom-prompts\">Custom prompts \u2014 deprecated slash-command mechanism<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/learn.chatgpt.com\/docs\/agent-configuration\/agents-md\">AGENTS.md \u2014 instruction discovery and precedence<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/learn.chatgpt.com\/docs\/hooks\">Hooks \u2014 events, configuration, matchers, trust and output schemas<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/learn.chatgpt.com\/docs\/cli\">Codex CLI \u2014 installation and execution<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/openai\/codex\/blob\/main\/codex-rs\/skills\/src\/assets\/samples\/skill-creator\/SKILL.md\">OpenAI Codex skill-creator reference<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/agentskills.io\/specification\">Agent Skills open specification<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/openai\/skills\">OpenAI Codex skill repository<\/a><\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Validation note (2026-11-01):<\/strong>&nbsp;Both skills were checked with the available local skill validator;&nbsp;<code>db_performance.py<\/code>&nbsp;was Python-compiled and passed four local unit tests, including a fresh temporary-Git-repository hash\/CI scenario. Codex itself was not installed in the generation environment, so explicit invocation, implicit selection, and hook trust\/execution need the live smoke tests above in your Codex installation. No database benchmarking was performed because no approved database or query workloads were supplied.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Version:&nbsp;2026-11-01Target:&nbsp;Codex CLI and IDE extension; local repository skills; optional custom prompts; lifecycle hooks; GitHub ActionsOperating examples:&nbsp;macOS\/Linux\/WSL with Python 3 and [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-5842","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/5842","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=5842"}],"version-history":[{"count":1,"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/5842\/revisions"}],"predecessor-version":[{"id":5843,"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/5842\/revisions\/5843"}],"wp:attachment":[{"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=5842"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=5842"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/aiopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=5842"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}