Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "personal",
"interface": {
"displayName": "Personal"
},
"plugins": [
{
"name": "react-doctor-loop",
"source": {
"source": "local",
"path": "./plugins/react-doctor-loop"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
]
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ review-*.md

# Track repository-owned agent skills, but keep other local agent state out.
/.agents/*
!/.agents/plugins/
!/.agents/plugins/marketplace.json
!/.agents/skills/
/.agents/skills/*
!/.agents/skills/react-doctor/
Expand Down Expand Up @@ -51,3 +53,4 @@ review-*.md
/scripts/print-batch-input.mjs
/scripts/rule-prompts/
/rules.json
/.react-doctor-loop/
18 changes: 18 additions & 0 deletions plugins/react-doctor-loop/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "react-doctor-loop",
"version": "0.1.0+codex.20260809025047",
"description": "Continuously turn confirmed React Doctor benchmark false positives into validated draft pull requests.",
"author": {
"name": "Million"
},
"skills": "./skills/",
"interface": {
"displayName": "React Doctor Loop",
"shortDescription": "Fix audited false positives with Codex",
"longDescription": "A single Codex-native loop for auditing one root-cause cohort, implementing the narrow fix, proving it against the benchmark and open-source corpus, and opening a review-only draft pull request.",
"developerName": "Million",
"category": "Developer Tools",
"capabilities": ["skills", "hooks"],
"defaultPrompt": "Use $react-doctor-loop to fix the next confirmed false-positive cohort."
}
}
51 changes: 51 additions & 0 deletions plugins/react-doctor-loop/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"description": "Dynamically activates the React Doctor audit loop and enforces its safety boundary.",
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|clear|compact",
"hooks": [
{
"type": "command",
"command": "node \"$PLUGIN_ROOT/scripts/hook.mjs\"",
"additionalContextLimit": 1200
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "node \"$PLUGIN_ROOT/scripts/hook.mjs\"",
"additionalContextLimit": 1200
}
]
}
],
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "node \"$PLUGIN_ROOT/scripts/hook.mjs\"",
"timeout": 5
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "node \"$PLUGIN_ROOT/scripts/hook.mjs\"",
"timeout": 15
}
]
}
]
}
}
122 changes: 122 additions & 0 deletions plugins/react-doctor-loop/scripts/complete.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { execFileSync } from "node:child_process";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";

const ALLOWED_PATH_PREFIXES = [
".changeset/",
"packages/core/",
"packages/fuzz/",
"packages/oxlint-plugin-react-doctor/",
];
const REQUIRED_EVIDENCE_KEYS = [
"focusedTests",
"auditReplay",
"coverageLedger",
"strictFuzz",
"fullFuzz",
"repositoryChecks",
"daytonaParity",
];

const fail = (message) => {
process.stderr.write(`${message}\n`);
process.exit(1);
};

const run = (command, argumentsList) =>
execFileSync(command, argumentsList, {
cwd: process.cwd(),
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
}).trim();

const [rule, cohort, evidencePath, parityPath] = process.argv.slice(2);
if (![rule, cohort, evidencePath, parityPath].every((value) => value?.length > 0)) {
fail("Usage: complete.mjs <rule> <cohort> <evidence.json> <parity.json>");
}

const branch = run("git", ["branch", "--show-current"]);
const head = run("git", ["rev-parse", "HEAD"]);
const status = run("git", ["status", "--porcelain=v1"]);
if (!branch.startsWith("loop/")) fail("The current branch must start with loop/.");
if (status.length > 0) fail("Commit the validated cohort before marking it complete.");

const changedFiles = run("git", ["diff", "--name-only", "origin/main...HEAD"])
.split("\n")
.filter(Boolean);
const disallowedFile = changedFiles.find(
(filePath) => !ALLOWED_PATH_PREFIXES.some((prefix) => filePath.startsWith(prefix)),
);
if (disallowedFile !== undefined) fail(`Disallowed changed file: ${disallowedFile}`);
if (!changedFiles.some((filePath) => /(?:\.test|\.regressions\.test)\.tsx?$/.test(filePath))) {
fail("The cohort needs a regression test.");
}
if (!changedFiles.some((filePath) => filePath.startsWith("packages/fuzz/corpus/"))) {
fail("The cohort needs a deduplicated fuzz fixture.");
}
if (
!changedFiles.some((filePath) => filePath.startsWith(".changeset/") && filePath.endsWith(".md"))
) {
fail("The cohort needs a patch changeset.");
}

const evidence = JSON.parse(await readFile(path.resolve(evidencePath), "utf8"));
if (evidence.rule !== rule || evidence.cohort !== cohort) fail("Evidence identity does not match.");
for (const key of REQUIRED_EVIDENCE_KEYS) {
const expectedExitCode = key === "daytonaParity" ? 1 : 0;
if (evidence[key]?.exitCode !== expectedExitCode || typeof evidence[key]?.command !== "string") {
fail(`Evidence step ${key} is missing or failed.`);
}
}
if (
!Array.isArray(evidence.coverageLedger.memberships) ||
evidence.coverageLedger.memberships.length === 0
) {
fail("The coverage ledger must list every cohort membership.");
}
if (
(evidence.coverageLedger.missing?.length ?? 0) > 0 ||
(evidence.coverageLedger.extra?.length ?? 0) > 0
) {
fail("The coverage ledger has missing or extra memberships.");
}

const parity = JSON.parse(await readFile(path.resolve(parityPath), "utf8"));
if (
!Array.isArray(parity.added) ||
!Array.isArray(parity.removed) ||
!Array.isArray(parity.skippedProjects)
) {
fail("Invalid Daytona parity report.");
}
if (parity.skippedProjects.length > 0) fail("Daytona parity excluded failed projects.");
const { added, removed } = parity;
if (added.length > 0) fail("RDE parity introduced diagnostics.");
if (removed.length === 0) fail("RDE parity did not reproduce the intended removal.");
const unrelatedRemoval = removed.find(
(entry) => entry.diagnostic?.rule !== rule && !entry.diagnostic?.rule?.endsWith(`/${rule}`),
);
if (unrelatedRemoval !== undefined)
fail(`Daytona parity changed unrelated rule ${unrelatedRemoval.diagnostic?.rule}.`);

const pullRequest = JSON.parse(
run("gh", ["pr", "view", "--json", "headRefOid,isDraft,state,title,url"]),
);
if (pullRequest.state !== "OPEN" || pullRequest.isDraft !== true)
fail("The pull request must be an open draft.");
if (!pullRequest.title.startsWith("[loop]")) fail("The pull request title must start with [loop].");
if (pullRequest.headRefOid !== head) fail("The draft pull request is not at the validated commit.");

const completion = {
branch,
cohort,
completedAt: new Date().toISOString(),
evidence,
head,
pullRequestUrl: pullRequest.url,
rule,
};
const completionPath = path.join(process.cwd(), ".react-doctor-loop", "completed.json");
await mkdir(path.dirname(completionPath), { recursive: true });
await writeFile(completionPath, `${JSON.stringify(completion, null, 2)}\n`, { mode: 0o600 });
process.stdout.write(`${pullRequest.url}\n`);
Loading
Loading