diff --git a/skills/rudder/SKILL.md b/skills/rudder/SKILL.md index d667eeb..ef0e5d4 100644 --- a/skills/rudder/SKILL.md +++ b/skills/rudder/SKILL.md @@ -65,13 +65,40 @@ Coverage is loop control, never a source of test intent. - Do not write the next test until the user answers. Repository code may help frame the question, but it cannot supply the answer. - After each answer, rerun `scripts/context.mjs`, require a captured prompt - record for the answer, and add only the expectation that answer authorizes. -- Complete the red-green cycle before measuring coverage or asking another - question. + record for the answer, and queue only the expectation that answer authorizes. +- Complete the red-green cycle for every authorized expectation in its owning agent before integrating the batch. + Use the queue answered rewrites guidance to set up owning agents. + Do not measure coverage while a rewrite is pending. - If the answer is missing, declined, or not captured, stop below the target. Report the uncovered behavior. Never fill the gap by inference. +## Queue answered rewrites + +Only the main agent may ask the user questions. +The main agent owns intent interpretation, the rewrite queue, integration, and coverage. + +- After capturing an answer, prepare one bounded rewrite task with the authorized + expectation, exact source-intent tag, allowed test and production paths, + relevant repository instructions, and narrow test command. +- Use at most three rewrite subagents at once and dispatch no more than three tasks between coverage runs. + Dispatch each safe task without waiting so the main agent can continue the question flow. + If the host cannot launch subagents, execute the same tasks serially. +- Assign disjoint behavior and file ownership to every subagent. + Include the current worktree state in the task and tell the subagent not to undo user or other-agent changes. + If ownership cannot be made disjoint, run that rewrite serially in the main agent. +- Limit each subagent to its assigned rewrite. + Require it to write and tag the test, observe red, make the smallest production change, reach narrow green, and report files and commands. + Forbid it from asking questions, spawning agents, running coverage, committing, or editing outside its assigned paths. +- While fewer than three tasks have been dispatched since the last coverage run, continue from that coverage snapshot only when another unassigned, independent uncovered behavior is available and its question does not depend on a pending rewrite. + Ask exactly one question at a time. +- Do not ask another question after dispatching the third task. + Also stop dispatching when no safe independent task remains, the user stops, or an answer is missing. +- Wait for every subagent in the batch to finish before integration. + Inspect each result and the combined diff, verify its tag and path ownership, and complete a failed or invalid rewrite serially. + Run the related and full suites on the combined worktree. + Run coverage only after the joined suite is green. + ## Enforce red-green TDD For every new or changed expectation: @@ -86,7 +113,8 @@ For every new or changed expectation: Do not change unrelated behavior or weaken the test. Do not alter coverage configuration or thresholds. 4. Rerun the narrow test until it passes. - Then run the applicable related and full test commands. + For a queued rewrite, return the result to the main agent for batch integration. + Otherwise, run the applicable related and full test commands. 5. Measure coverage only after the suite is green. ## Run the workflow @@ -159,11 +187,11 @@ For every new or changed expectation: 10. When the suite is green, run the applicable coverage command. Measure changed production code when the tooling supports it. If coverage is below the target, follow the question-driven coverage loop. - Ask exactly one question to the user. -11. After the answer is captured, add only its authorized expectation. - Run the red-green cycle, measure coverage, then ask the next question. +11. Ask exactly one question at a time to the user. + After the answer is captured, queue only its authorized expectation using the bounded rewrite workflow. + Continue asking independent questions until the batch must join. + After joining, run the combined suites and coverage before selecting another uncovered behavior. Continue until the target passes or the user tells you to stop the flow. - Also stop if the user leaves a question unanswered. Report the requirements derived from intent and all files changed. Report commands run, coverage, unanswered ambiguities, and the backup location. diff --git a/test/plugin-package.test.ts b/test/plugin-package.test.ts index 39cc5c3..b84e475 100644 --- a/test/plugin-package.test.ts +++ b/test/plugin-package.test.ts @@ -110,7 +110,7 @@ test('keeps the Rudder package version synchronized across the codebase', () => }); -test('ships a public marketplace catalog and complete Rudder skill', () => { +test('ships a public marketplace catalog and its package resources', () => { const marketplace = JSON.parse( readFileSync( join(pluginRoot, '.claude-plugin', 'marketplace.json'), @@ -131,9 +131,6 @@ test('ships a public marketplace catalog and complete Rudder skill', () => { '@ruddercode/rudder-plugin' ); assert.match(skill, /^---\nname: rudder\n/); - assert.match(skill, /scripts\/context\.mjs/); - assert.match(skill, /scripts\/manage-data\.mjs/); - assert.doesNotMatch(skill, /\[TODO:/); for (const path of [ ['skills', 'rudder', 'scripts', 'backup-tests.mjs'], @@ -149,83 +146,6 @@ test('ships a public marketplace catalog and complete Rudder skill', () => { } }); -// codex/019faf66-7413-7a31-a0ea-b5fe1c9b66d6/019faf6c-61f1-7512-808a-f870371b1baa -test('runs the update helper before Rudder generates tests', () => { - const skill = readFileSync( - join(pluginRoot, 'skills', 'rudder', 'SKILL.md'), - 'utf8' - ); - const updateScript = readFileSync( - join(pluginRoot, 'skills', 'rudder', 'scripts', 'update.mjs'), - 'utf8' - ); - - assert.ok(updateScript.length > 0); - assert.equal(skill.match(/scripts\/update\.mjs check/gu)?.length, 1); - assert.ok( - skill.indexOf('scripts/update.mjs check') < - skill.indexOf('## Run the workflow') - ); -}); - -// codex/019faf66-7413-7a31-a0ea-b5fe1c9b66d6/019faf7c-40c9-7153-b2b9-f1a62201d44e -test('keeps coverage generation inside the user question flow', () => { - const skill = readFileSync( - join(pluginRoot, 'skills', 'rudder', 'SKILL.md'), - 'utf8' - ); - - assert.match(skill, /Ask exactly one question to the user\./); - assert.match(skill, /or the user tells you to stop the flow\./); - assert.match(skill, /Do not write the next test until the user answers\./); -}); - -// codex/019faf66-7413-7a31-a0ea-b5fe1c9b66d6/019faf93-22e4-7093-92da-391b03125caa -test('requires a prompt-backed red-green production cycle', () => { - const skill = readFileSync( - join(pluginRoot, 'skills', 'rudder', 'SKILL.md'), - 'utf8' - ); - - assert.match( - skill, - /Write and tag the focused test before changing production code\./ - ); - assert.match(skill, /observe the expected failure\./); - assert.match(skill, /smallest production change required/); - assert.match(skill, /Rerun the narrow test until it passes\./); - assert.match(skill, /Measure coverage only after the suite is green\./); - assert.doesNotMatch( - skill, - /Do not change production code, coverage configuration/ - ); -}); - -// codex/019faf9a-f8c0-7d33-8f77-a17b27aa7a14/019fafa2-116b-7440-8f47-3c40777c5b55 -test('restores only Rudder-tagged generated tests after the merge-base reset', () => { - const skill = readFileSync( - join(pluginRoot, 'skills', 'rudder', 'SKILL.md'), - 'utf8' - ); - - assert.match(skill, /immediately preceding Rudder source-intent tag/); - assert.match(skill, /Record the exact tagged test cases and their tags\./); - assert.match(skill, /attempt to restore each recorded, tagged test case/); - assert.match(skill, /do not restore untagged tests or the entire test file/); - assert.match(skill, /report the unsuccessful restoration/); -}); - -// codex/019faf66-7413-7a31-a0ea-b5fe1c9b66d6/019faf90-8df6-7223-aa9f-33012b8f26cb -test('keeps an unsuccessful plugin update non-blocking', () => { - const skill = readFileSync( - join(pluginRoot, 'skills', 'rudder', 'SKILL.md'), - 'utf8' - ); - - assert.match(skill, /report the failure and continue the active Rudder flow/); - assert.doesNotMatch(skill, /handle the response and stop/); -}); - test('releases the root plugin package with plugin-specific artifacts', () => { const publishWorkflow = readFileSync( join(pluginRoot, '.github', 'workflows', 'publish.yml'),