<!-- FOR AI AGENTS - Human readability is a side effect, not a goal -->
<!-- Managed by agent: keep sections and order; edit content, not structure -->
<!-- Last updated: 2026-04-28 | Last verified: 2026-04-28 -->

# AGENTS.md

**Precedence:** the closest `AGENTS.md` to the files being changed wins. Explicit user instructions override this file.

## Scope

This file applies to the whole `nis2-compliance` WordPress plugin.

## Project Facts

| Item | Value |
| --- | --- |
| Platform | WordPress plugin |
| Entry point | `nis2.php` |
| Main orchestrator | `includes/class-nis2.php` |
| Plugin version | `1.6.3` |
| Text domain | `nis2-compliance` |
| Minimum PHP | `7.4` |
| WordPress tested up to | `6.8.3` |
| Declared package manager | None detected |
| CI config | None detected |

Do not invent Composer, npm, build, lint, or test commands unless their config files are added to the repository.

## Index of scoped AGENTS.md

<!-- AGENTS-GENERATED:START scope-index -->
No scoped `AGENTS.md` files are currently present.
<!-- AGENTS-GENERATED:END scope-index -->

## Useful Commands

| Purpose | Command |
| --- | --- |
| Release process | See `RELEASE.md` for full process |
| PHP syntax check | `find . -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l` |
| Bump version (e.g. 1.6.2 → 1.6.3) | `find . -name '*.php' -o -name '*.txt' -o -name '*.md' | xargs sed -i '' 's/1.6.2/1.6.3/g'` |

## Version Bumping

When bumping version (e.g. `1.6.2` → `1.6.3`), update all occurrences in these files:
- `nis2.php` - `Version:` and `NIS2_VERSION`
- `readme.txt` - `Stable tag:` and changelog section
- `changelog.txt` - version header
- `README.md` - `**Version:**`
- `bin/svn-publish.sh` - example tag command
- Update `AGENTS.md` - `Plugin version` and tag command
| List files | `rg --files` |
| Search code | `rg "pattern"` |

Run the PHP syntax check after editing PHP. There is no formal automated test suite in this repository at the time this file was created.

## Architecture Map

| Path | Responsibility |
| --- | --- |
| `nis2.php` | Plugin header, constants, bootstrap, activation/deactivation hooks |
| `includes/class-nis2.php` | Singleton plugin orchestrator, dependency loading, module creation, admin menus, assets, database tables, cron registration |
| `includes/class-nis2-cron.php` | Cron scheduling coordination |
| `includes/class-nis2-logger.php` | Security/activity logging, export, cleanup, notifications |
| `includes/class-nis2-monitor.php` | File integrity monitoring and baseline management |
| `includes/class-nis2-access-protection.php` | Login protection, IP blocking/whitelisting, CAPTCHA, REST rate limiting |
| `includes/class-nis2-vulnerability-scanner.php` | Core/plugin/theme vulnerability checks and exports |
| `includes/class-nis2-compliance-checker.php` | NIS2 compliance scoring and reports |
| `admin/class-nis2-admin.php` | Settings registration, sanitization, admin notices, admin AJAX |
| `admin/class-nis2-dashboard.php` | Dashboard data and display helpers |
| `admin/views/` | Admin page templates |
| `admin/js/` | Admin page JavaScript for AJAX and UI behavior |
| `admin/css/` | Admin styles; `nis2-force.scss` is present with generated CSS/map |
| `public/` | Public-facing hooks, shortcodes, and CSS |
| `assets/` and `static/` | WordPress.org assets and static images |

## WordPress Conventions

- Keep direct-access guards in PHP files: `if (! defined('ABSPATH')) { exit; }` or the existing file-local style.
- Use existing `NIS2_*` class naming and `nis2_` option/action/AJAX prefixes.
- Keep admin-only behavior behind `is_admin()` or WordPress capability checks as appropriate.
- Use the existing `wpNis2Ajax` localized object and `nis2_nonce` nonce for admin AJAX unless a narrower nonce is introduced consistently.
- Use `manage_options` for privileged admin actions unless the product requirements explicitly define a different capability.
- Escape output with WordPress helpers such as `esc_html()`, `esc_attr()`, `esc_url()`, `wp_kses_post()`, and translation-aware variants.
- Sanitize input using WordPress helpers such as `sanitize_text_field()`, `sanitize_email()`, `esc_url_raw()`, `wp_unslash()`, and the existing `NIS2_Admin::sanitize()` pattern.
- Use `$wpdb->prepare()` for SQL with dynamic values. Keep schema changes in the existing `dbDelta()` flow.
- Register and enqueue assets through WordPress APIs; do not hardcode plugin URLs when `NIS2_PLUGIN_URL` is available.

## Security-Sensitive Areas

Treat these paths as high risk and verify nonce, capability, sanitization, escaping, and SQL safety carefully:

| Area | Paths |
| --- | --- |
| AJAX handlers | `includes/*.php`, `admin/class-nis2-admin.php`, `admin/js/*.js` |
| Login and IP controls | `includes/class-nis2-access-protection.php` |
| Vulnerability scanning | `includes/class-nis2-vulnerability-scanner.php` |
| File integrity scanning | `includes/class-nis2-monitor.php` |
| Logging and exports | `includes/class-nis2-logger.php` |
| Settings storage | `admin/class-nis2-admin.php`, `admin/views/settings/*.php` |
| Database schema | `includes/class-nis2.php` |

Do not commit secrets, webhook URLs, API keys, tokens, real IP allow/block lists, or site-specific scan output.

## Editing Guidance

- Preserve the existing procedural WordPress style and class-per-file structure.
- Keep changes small and module-local when possible.
- Update this `AGENTS.md` in the same change when important project facts change, such as version metadata, architecture/module boundaries, security conventions, commands, CI/test tooling, dependencies, release process, or scoped agent rules.
- If adding a module, add the `require_once` in `includes/class-nis2.php` and initialize it in `NIS2::init_modules()`.
- If adding an admin page, wire menu registration, view rendering, assets, settings, AJAX, nonce checks, and capability checks together.
- If adding an AJAX action, update both PHP and the relevant `admin/js/` file, and return with `wp_send_json_success()` or `wp_send_json_error()`.
- If changing stored options, update defaults, sanitization, settings views, and any dependent modules together.
- If changing plugin metadata, keep `nis2.php`, `README.md`, `readme.txt`, and `changelog.txt` aligned.
- Avoid unrelated formatting churn; this repo mixes tabs and spaces in existing files.

## Manual Verification

After relevant changes:

1. Run `find . -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l`.
2. In WordPress admin, load the NIS2 dashboard, settings, activity log, file integrity, and vulnerabilities pages touched by the change.
3. Exercise any changed AJAX action from the UI and confirm failed nonce/capability paths are rejected.
4. For cron or scanner changes, trigger the affected hook manually in a local WordPress environment before release.
5. For public shortcode changes, test `[nis2_status]`, `[nis2_security_badge]`, or `[nis2_last_update]` as applicable.

## Repository Hygiene

- `.DS_Store` is ignored; do not add or rely on local OS files.
- Do not edit generated CSS maps unless regenerating the matching CSS source.
- Keep WordPress.org assets in `assets/` consistent with `readme.txt` when release-facing copy or screenshots change.
