From 49bdd37b3a4685c42ed4a1fec6c01cf2203182ad Mon Sep 17 00:00:00 2001 From: Enderaoe Date: Sun, 5 Jul 2026 18:14:41 +0800 Subject: [PATCH] feat(rules): scope 04-cybersecurity off the always-on set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The offensive-security doctrine was alwaysApply:true, loading into every session's context (~7.8 KB, ~29% of the always-on rule budget) even for non-security work. Flip it to alwaysApply:false with globs matching offensive-security file contexts (exploits, CTF/pwn, pentest findings, AI red-team configs). It now renders like the language rules: on-demand references/rules/04-cybersecurity.md for every target (preserved, not dropped — instruction-only hosts get the reference doc too) plus a native glob rule for cursor. Nothing is lost; it just loads when relevant. Effect: codex AGENTS.md 27727 -> 19874 bytes (-28.3%). check rules confirms 04 attaches only to the security-exploit scenario, not the 8 generic ones. Tests: updated the assertions that pinned 04's always-on placement to its real new placement (codex/kilo references vs rules dir; kilo instruction lists; cline file count 73->74 as 04 becomes a separate reference file). Verified against actual build /install output, not flipped to pass. check rules/check/check generated/build/test green. by mass-energy equivalence --- rules/04-cybersecurity.mdc | 16 ++++++++++++++-- tests/agent-surface.test.mjs | 25 +++++++++++++++---------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/rules/04-cybersecurity.mdc b/rules/04-cybersecurity.mdc index 5eb6bd7..d41575e 100644 --- a/rules/04-cybersecurity.mdc +++ b/rules/04-cybersecurity.mdc @@ -1,6 +1,18 @@ --- -description: "Authorized offensive security: red-team, CTF/exploit-dev, AI/ML red-teaming, kernel security, bug bounty, and coordinated disclosure" -alwaysApply: true +description: "Authorized offensive security: red-team, CTF/exploit-dev, AI/ML red-teaming, kernel security, bug bounty, and coordinated disclosure. Attaches on offensive-security file contexts (exploits, CTF/pwn, pentest findings, AI red-team configs); pull it in explicitly for scope/ROE-authorized work with no matching file yet." +globs: + - "**/exploit*" + - "**/poc*" + - "**/pwn*" + - "**/shellcode*" + - "ctf/**" + - "**/ctf/**" + - "pentest/**" + - "**/pentest*" + - "findings/**" + - "**/*.nmap" + - "**/.garak.*" +alwaysApply: false --- # Cybersecurity diff --git a/tests/agent-surface.test.mjs b/tests/agent-surface.test.mjs index 8a07fff..29f3417 100644 --- a/tests/agent-surface.test.mjs +++ b/tests/agent-surface.test.mjs @@ -342,12 +342,19 @@ const genericRules = run(["check", "rules", "--scenario", "generic-chat"]); assert.match(genericRules, /^generic-chat:$/m); assert.match(genericRules, /rules\/00-precedence-and-safety\.mdc/); assert.doesNotMatch(genericRules, /^errors:$/m); +// 04-cybersecurity is scoped (alwaysApply:false): it must NOT attach to a generic session. +assert.doesNotMatch(genericRules, /rules\/04-cybersecurity\.mdc/); for (const scenario of ["python-source", "python-tooling", "rust-source", "go-ci", "typescript-eslint", "shell-script", "security-exploit", "ordinary-patch"]) { const output = run(["check", "rules", "--scenario", scenario]); assert.match(output, new RegExp(`^${scenario}:$`, "m")); assert.doesNotMatch(output, /^errors:$/m); - assert.match(output, /rules\/04-cybersecurity\.mdc/); + // 04-cybersecurity attaches only where a security-related path matches its globs. + if (scenario === "security-exploit") { + assert.match(output, /rules\/04-cybersecurity\.mdc/); + } else { + assert.doesNotMatch(output, /rules\/04-cybersecurity\.mdc/); + } } run(["build", "--target", "all"]); @@ -381,7 +388,7 @@ assert.equal(generated.some((file) => file.endsWith(path.join("dist", "codex", " assert.equal(generated.some((file) => file.endsWith(path.join("dist", "codex", ".agents", "skills", "ops-flow", "agents", "openai.yaml"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "codex", ".codex", "AGENTS.md"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "codex", ".codex", "references", "rules", "10-python.md"))), true); -assert.equal(generated.some((file) => file.endsWith(path.join("dist", "codex", ".codex", "references", "rules", "04-cybersecurity.md"))), false); +assert.equal(generated.some((file) => file.endsWith(path.join("dist", "codex", ".codex", "references", "rules", "04-cybersecurity.md"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "codex", ".codex", "agents", "boss.toml"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "deepagents", ".deepagents", "agent", "skills", "ops-flow", "SKILL.md"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "deepagents", ".deepagents", "agent", "AGENTS.md"))), true); @@ -408,9 +415,9 @@ assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ". assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ".config", "kilo", "AGENTS.md"))), false); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ".config", "kilo", "kilo.jsonc"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ".config", "kilo", "rules", "00-precedence-and-safety.md"))), true); -assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ".config", "kilo", "rules", "04-cybersecurity.md"))), true); +assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ".config", "kilo", "rules", "04-cybersecurity.md"))), false); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ".config", "kilo", "references", "rules", "14-shell.md"))), true); -assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ".config", "kilo", "references", "rules", "04-cybersecurity.md"))), false); +assert.equal(generated.some((file) => file.endsWith(path.join("dist", "kilo", ".config", "kilo", "references", "rules", "04-cybersecurity.md"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "antigravity", "global_workflows", "ops-flow.md"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "antigravity-cli", "config", "plugins", "agent-surface", "plugin.json"))), true); assert.equal(generated.some((file) => file.endsWith(path.join("dist", "antigravity-cli", "config", "plugins", "agent-surface", "skills", "ops-flow.md"))), true); @@ -449,7 +456,8 @@ const cursorIgnore = readFileSync(path.join(root, "dist", "cursor", ".cursorigno assert.match(cursorIgnore, /agent-surface canonical AI-tool ignore baseline/); const codexInstructions = readFileSync(path.join(root, "dist", "codex", ".codex", "AGENTS.md"), "utf8"); assert.match(codexInstructions, /## 00-precedence-and-safety\.mdc/); -assert.match(codexInstructions, /## 04-cybersecurity\.mdc/); +// 04-cybersecurity is scoped now: it lives in references/rules/, not the always-on AGENTS.md (like 10-python). +assert.doesNotMatch(codexInstructions, /## 04-cybersecurity\.mdc/); assert.doesNotMatch(codexInstructions, /## 10-python\.mdc/); const codexPythonReference = readFileSync(path.join(root, "dist", "codex", ".codex", "references", "rules", "10-python.md"), "utf8"); assert.match(codexPythonReference, /Scoped agent-surface reference/); @@ -460,7 +468,6 @@ assert.deepEqual(kiloPreviewConfig.instructions, [ "./rules/01-response-style.md", "./rules/02-agent-workflow.md", "./rules/03-project-defaults.md", - "./rules/04-cybersecurity.md", "./rules/05-tooling.md", "./rules/06-test-policy.md", ]); @@ -877,13 +884,13 @@ const liveDest = "/tmp/agent-surface-live"; rmSync(liveDest, { recursive: true, force: true }); const liveInstall = run(["install", "--target", "cline", "--dest", liveDest]); assert.match(liveInstall, /^installed:$/m); -assert.match(liveInstall, /wrote: 73/); +assert.match(liveInstall, /wrote: 74/); assert.match(readFileSync(path.join(liveDest, ".clinerules", "workflows", "workflow-boss.md"), "utf8"), /^## OBJECTIVE/); assert.match(readFileSync(path.join(liveDest, ".clinerules", "workflows", "verify-readiness.md"), "utf8"), /^## OBJECTIVE/); assert.match(readFileSync(path.join(liveDest, ".clineignore"), "utf8"), /agent-surface canonical AI-tool ignore baseline/); const liveManifest = JSON.parse(readFileSync(path.join(liveDest, ".agent-surface", "cline-manifest.json"), "utf8")); assert.equal(liveManifest.target, "cline"); -assert.equal(liveManifest.managed.length, 73); +assert.equal(liveManifest.managed.length, 74); assert.equal(liveManifest.managed[0].managed_by, "agent-surface"); rmSync(liveDest, { recursive: true, force: true }); @@ -1255,7 +1262,6 @@ assert.deepEqual(inlineKiloConfig.instructions, [ ".kilo/rules/01-response-style.md", ".kilo/rules/02-agent-workflow.md", ".kilo/rules/03-project-defaults.md", - ".kilo/rules/04-cybersecurity.md", ".kilo/rules/05-tooling.md", ".kilo/rules/06-test-policy.md", ]); @@ -1403,7 +1409,6 @@ for (const target of [ ".kilo/rules/01-response-style.md", ".kilo/rules/02-agent-workflow.md", ".kilo/rules/03-project-defaults.md", - ".kilo/rules/04-cybersecurity.md", ".kilo/rules/05-tooling.md", ".kilo/rules/06-test-policy.md", ]);