diff --git a/skills/plan/SKILL.md b/skills/plan/SKILL.md index 5f64dffb..cafaa63b 100644 --- a/skills/plan/SKILL.md +++ b/skills/plan/SKILL.md @@ -155,6 +155,10 @@ a previous permission. This is an agent instruction, not a runtime cache. branch. If an independent review slice needs a different branch, announce current branch fit, proposed base/name and permission before any Git action. + Before declaring an individual ticket ready, apply the ticket questions in + [the Plan readiness review](references/plan-readiness-review.md). Reuse + task-authoring mechanics; this review challenges usability, not field count. + For UI work describe objects and CRUD (or N/A), changed zones using +/~/-/=, an ASCII sketch and the real visual verification planned. A drawing is not a rendered proof. Resolve product/UX choices rather than invent approval. @@ -175,6 +179,11 @@ a previous permission. This is an agent instruction, not a runtime cache. parent hierarchy from prerequisites; report actual IDs and errors, or proposed tickets when writes are unavailable. Task authoring does not approve this plan. + Before approving the complete current plan for execution, apply the global + delegated challenge in the Plan readiness review already read above. Review + the whole graph, including implicit prerequisites; do not substitute ticket + readback or self-review for this gate. + Verify actual delegated planning completion, source identity and result locator before integration. Reconcile conflicts and retain unresolved decisions and missing proof. Failed or missing worker results remain provisional. Collect source-linked diff --git a/skills/plan/references/plan-readiness-review.md b/skills/plan/references/plan-readiness-review.md new file mode 100644 index 00000000..8ccbe197 --- /dev/null +++ b/skills/plan/references/plan-readiness-review.md @@ -0,0 +1,78 @@ +# Plan readiness review + +Use before declaring tickets ready and before approving the complete current plan +for execution. This is a decision review, not another phase or ticket-authoring +procedure. Reuse the caller's established scope, authoring guidance, research, +review policy and current permissions. Readiness is not acceptance. + +## 1. Challenge each individual ready ticket + +Read the actual ticket, canonical criteria, native fields and resolvable references. +Use the following open questions to explain the result; answer each paired closed +question **yes / no / unknown**, citing the relevant input. Yes passes that gate; +no fails it; unknown never passes. Mark an inactive question N/A with a scope-based +reason. Do not demand extra headings or copy this checklist into every ticket. + +| Open inquiry | Closed readiness question | +|---|---| +| Why is this result useful to the actual user, maintainer or system, and what changes afterward? | Is one usable, independently reviewable outcome tied to a verified requirement rather than a file/phase activity? | +| What must already exist or be decided, and what evidence shows those prerequisites hold? | Are actual prerequisites, native blocking edges and textual assumptions consistent, with no critical unresolved input? | +| Which supported research premise justifies this approach, and what would invalidate it? | Are decision-material hypotheses supported or explicitly unresolved rather than asserted as facts? | +| How will a reader distinguish success from a plausible but incorrect result? | Do stable source-derived criteria have independent observable checks, including relevant errors/boundaries and approved applicable NFRs? | +| Which inspected inputs are needed first, what is created or modified, and what must stay untouched? | Are Read/Create/Modify/Preserve/Exclude paths or assets verified/proposed honestly, ownership conflicts resolved and adjacent work excluded? | +| What can a fresh worker do first using only this ID and its references? | Does the first restart action name an accessible input, operation, prerequisite and expected result within current permissions? | + +Use exact documents/resources instead of invented code paths for non-code work. +Unknown critical input blocks the affected ticket only; retain the rest of the ready +frontier. Record each gap's decision owner (unknown if not established), impact and +smallest resolving action. Repair only authorized affected sections through the +caller's conditional task-authoring route; preserve criteria/history/native fields. + +## 2. Delegate the complete-plan logical challenge + +Before the plan is approved for execution, request a **separate read-only subagent** +review of the **complete current plan**, not a selected easy ticket. Supply objective, +Goals/Non-goals, requirement/criterion sources, research premises and unresolved gaps, +all current plan nodes (including coarse future work), native edges, relevant source +identity, permissions and an agreed bounded turn/time/output budget. Give reviewer +criteria, not expected answers or the builder's desired verdict/private reasoning. +The reviewer must not have participated in candidate construction; disclose inherited +context and exposure limits. No source, tracker, Git or candidate writes. + +Ask the reviewer: +- What is the least viable ordering that delivers the requested usable outcome? + Are outcomes covered without speculative capabilities or missing integration? +- Which implicit semantic dependencies are missing from native edges: a foundation + contract before a consumer, architecture selection before its implementation, + credential/bootstrap or security boundary before the operation relying on it? + Check only relevant boundaries; do not dump a generic security checklist. +- Do native edges and described prerequisites agree? Are there cycles (including + implicit cycles), reverse prerequisites, false parallelism or shared-file conflicts? +- Which research gaps can change ordering, scope or observable criteria? Which nodes + are actually ready now, which must wait, and what single resolving probe is needed? +- Is decomposition by independently usable result, not a microticket per file, + phase or SOURCE marker? Keep distant work coarse; task count is not quality. + +Return source-linked findings, blocking/nonblocking classification, affected node +IDs, missing or incorrect edges with rationale, a proposed least viable ordering, +ready frontier, research prerequisites and completion/provenance limits. The +coordinator verifies invocation, completion, source identity and result locator, +reconciles findings under existing permissions and retains unresolved contradictions. +A worker summary is not plan approval or independent acceptance of implementation. + +If delegation is unavailable, name the exact prerequisite: coordinator/user supplies +a separate non-builder read-only reviewer with access to this complete plan and +criteria. Keep the global approval gate pending; no silent direct fallback or +self-acceptance. This blocks approval of the affected plan, not unrelated authorized +work. Run this challenge once before approval; rerun only when changes affect the +graph, criteria or material inputs, not for every minor wording edit. + +## 3. Stop with an executable frontier + +Finish when every applicable readiness question is answered (or inactive with a +reason), critical gaps have named impacts/owners/probes, and the global review is +complete or has its precise prerequisite. Report ready/blocked nodes and the next +executable action, not a speculative backlog or bulk ticket rewrite. Neither this +review nor task authoring grants execution authority. Use proportional checks; +no automatic exhaustive web research, full test suite or evaluation campaign, and +no performance or agent-effectiveness claim from source readiness. diff --git a/skills/research/SKILL.md b/skills/research/SKILL.md index 7eb75e2f..33951f83 100644 --- a/skills/research/SKILL.md +++ b/skills/research/SKILL.md @@ -164,6 +164,10 @@ a previous permission. This is an agent instruction, not a runtime cache. [the communication contract](references/communication-contract.md). Preserve ownership, evidence and conditional UI safeguards in standalone use too. Respond in the user's language; unavailable capabilities are explicit, never simulated execution evidence. + - Before declaring research sufficient for the planning decision, apply + [the Research readiness review](references/research-readiness-review.md). + Resolve only decision-material gaps; retain supported premises, targeted + impact paths and the next discriminating probe, not a blanket catalog. - Collect research report facts for inline delivery or an explicitly authorized saved report: product context, outcome, evidence, affected seams, assumptions, options, unknowns and next decision/probe. This is not a competing tracker. diff --git a/skills/research/references/research-readiness-review.md b/skills/research/references/research-readiness-review.md new file mode 100644 index 00000000..a969de42 --- /dev/null +++ b/skills/research/references/research-readiness-review.md @@ -0,0 +1,60 @@ +# Research readiness review + +Use before declaring research sufficient for a particular planning decision. Reuse +current cited context and the caller's investigation/evidence contracts. The goal +is enough grounded understanding for an executable plan, not exhaustive research +or a new procedure/ticket for every source. + +## 1. Explain the decision chain + +Keep a compact, cited chain for each decision-material conclusion: +**hypothesis → evidence → supported premise → decision → tasks → observable criteria**. +A hypothesis is a question to test, not a premise until supported. Evidence includes +contrary findings and its scope/version/environment limits; a supported premise +states only what that evidence warrants. Label conclusions observed, inferred or +unverified; an inference cites its premises, not merely its confidence. Decisions +and tasks may be proposed planning inputs, not fabricated approved tickets. + +For a remaining assumption record its owner (or unknown), affected decision/tickets, +readiness impact and smallest deciding probe with an observable resolving result. +Absence of a search result is not evidence that a problem or capability is absent. +Do not promote library documentation, generic design patterns or a familiar request +into proof of actual local behavior. + +## 2. Ask mixed sufficiency questions + +For each open inquiry explain the answer using targeted citations. Answer its closed +question **yes / no / unknown**: yes passes, no fails, unknown never passes. Inactive +questions may be N/A with a scope-based reason. No mandatory matrix or blank catalog. + +| Open inquiry | Closed sufficiency question | +|---|---| +| What problem does the actual user, maintainer or system encounter, and which outcome matters now? | Are purpose, requested outcome, current behavior and boundaries grounded in accessible primary sources without invented users/capabilities? | +| Which hypothesis explains the issue or supports the option, and what would refute it? | Is each decision-critical premise supported by applicable evidence, with observed/inferred/unverified status and contradictions exposed? | +| Where will the decision have impact, and what sources must a worker read to act safely? | Are relevant actual files/symbols/tests, consumers and preserve/exclude boundaries identified with citations, rather than guessed paths or broad folders? | +| Which known functional, code, library or domain-principle problems could invalidate the approach here? | Have relevant local known issues, actual code/tests, dependency limitations and applicable principles been checked, or material unknowns assigned an owner and deciding probe? | +| What changes between the cited source/library version and this environment? | Are source identity, library version, environment and freshness recorded when material, with unavailable/stale inputs explicit? | +| Why choose this option over the smallest viable alternative, and what must happen before implementation? | Can supported premises justify the decision, task prerequisites and independent observable criteria without an unsupported leap? | +| What information would actually change the next plan, and what can proceed without it? | Are critical gaps scoped to affected decisions/tickets, nonblocking assumptions explicit and the next resolving action executable within authority? | + +Inspect only consumed, relevant sources; pass ordered file/section/symbol pointers +with reasons and identity to Plan under the existing pointer contract. This is not +a mandate to load all catalogs, patterns, tests or domain principles. A source check +is static evidence, not reproduced behavior; for a bug retain the caller's +symptom-specific reproduction gate or precise missing fixture/access. + +## 3. Resolve material gaps or stop + +Start with relevant local evidence. Search external known issues only when risk or +uncertainty could change the plan, the question is bounded and an authorized channel +is available. Record source/version/date/applicability and limits; do not infer a +negative fact from unavailable searches. No implicit paid web access, installation, +model calls, exhaustive web review, full tests or evaluation campaign. Stable, +sufficient local evidence needs no external refresh ritual. + +Stop when every applicable question is answered or inactive with justification and +the cited chain supports the next decision. Critical gaps block only affected +planning/implementation tickets, not the rest of the ready frontier. Return each +gap's owner, impact and deciding probe rather than claiming sufficiency or creating +a speculative backlog. Nonblocking assumptions travel with the planning inputs. +No performance/agent-effectiveness claim or acceptance follows from this review. diff --git a/tests/test_plan_research_readiness.py b/tests/test_plan_research_readiness.py new file mode 100644 index 00000000..ff077b6f --- /dev/null +++ b/tests/test_plan_research_readiness.py @@ -0,0 +1,104 @@ +"""User-request source contracts only; no workflow execution/effectiveness proof.""" +from pathlib import Path +import re +import unittest + +ROOT = Path(__file__).resolve().parents[1] + + +def source(phase, name): + return (ROOT / 'skills' / phase / 'references' / name).read_text() + + +class ReadinessSourceContracts(unittest.TestCase): + def setUp(self): + self.plan = source('plan', 'plan-readiness-review.md') + self.research = source('research', 'research-readiness-review.md') + + def test_step_local_contained_links_before_readiness_actions(self): + for phase, name in [('plan', 'plan-readiness-review.md'), + ('research', 'research-readiness-review.md')]: + text = (ROOT / 'skills' / phase / 'SKILL.md').read_text() + self.assertEqual(text.count('references/' + name), 1) + self.assertLess(text.index('references/' + name), + text.index('**Verify readiness and draft')) + for link in re.findall(r'\]\(([^)]+)\)', text): + if '://' not in link and not link.startswith('#'): + target = (ROOT / 'skills' / phase / link.split('#')[0]).resolve() + self.assertTrue(target.is_relative_to((ROOT / 'skills' / phase).resolve())) + self.assertTrue(target.is_file(), link) + self.assertLessEqual(len(text.splitlines()), 500) + + def test_individual_ticket_questions_cover_usable_execution(self): + for requirement in ('independently reviewable outcome', 'verified requirement', + 'actual prerequisites', 'native blocking edges', + 'independent observable checks', 'errors/boundaries', + 'Read/Create/Modify/Preserve/Exclude', 'ownership conflicts', + 'first restart action', 'accessible input', 'current permissions'): + self.assertIn(requirement, self.plan) + self.assertIn('actual user, maintainer or system', self.plan) + self.assertIn('conditional task-authoring route', self.plan) + self.assertIn('preserve criteria/history/native fields', self.plan) + + def test_open_questions_and_closed_three_state_gates_are_distinct(self): + for text in (self.plan, self.research): + rows = [line for line in text.splitlines() if line.startswith('|')][2:] + self.assertGreaterEqual(len(rows), 6) + for row in rows: + fields = row.strip('|').split('|') + self.assertEqual(len(fields), 2) + self.assertTrue(all('?' in field for field in fields)) + for gate in ('yes / no / unknown', 'yes passes', 'no fails', + 'unknown never passes', 'N/A', 'scope-based reason'): + self.assertIn(gate.lower(), ' '.join(text.lower().split())) + + def test_complete_plan_review_is_separate_readonly_not_self_approval(self): + for rule in ('separate read-only subagent', 'complete current plan', + 'not have participated in candidate construction', + 'criteria, not expected answers', 'No source, tracker, Git or candidate writes', + 'implicit semantic dependencies', 'native edges', 'implicit cycles', + 'foundation', 'consumer', 'architecture selection', 'security boundary', + 'least viable ordering', 'ready frontier', 'completion', 'source identity', + 'result locator', 'not plan approval'): + self.assertIn(rule, self.plan) + self.assertIn('Run this challenge once before approval', self.plan) + self.assertIn('graph, criteria or material inputs', self.plan) + self.assertIn('not for every minor wording edit', self.plan) + for rule in ('If delegation is unavailable', 'exact prerequisite', + 'global approval gate pending', 'no silent direct fallback', + 'not unrelated authorized'): + self.assertIn(rule, self.plan) + + def test_research_chain_keeps_hypotheses_and_evidence_distinct(self): + self.assertIn('hypothesis → evidence → supported premise → decision → tasks → observable criteria', self.research) + for rule in ('not a premise until supported', 'contrary findings', + 'observed, inferred or', 'unverified', 'its premises', + 'owner (or unknown)', 'deciding probe', 'observable resolving result', + 'not fabricated approved tickets', 'Absence of a search result is not evidence'): + self.assertIn(rule, self.research) + + def test_research_checks_actual_relevant_limits_and_impact_sources(self): + for rule in ('functional, code, library or domain-principle problems', + 'actual code/tests', 'dependency limitations', 'actual files/symbols/tests', + 'consumers', 'preserve/exclude boundaries', 'source identity', + 'library version', 'environment', 'freshness', 'unavailable/stale', + 'ordered file/section/symbol pointers', 'symptom-specific reproduction'): + self.assertIn(rule.lower(), self.research.lower()) + self.assertIn('not reproduced behavior', self.research) + + def test_proportional_scoped_stop_not_procedural_expansion(self): + for text in (self.plan, self.research): + for word in ('critical gaps', 'owner', 'probe', 'ready frontier', + 'Stop', 'full test', 'evaluation campaign', 'performance', + 'agent-effectiveness'): + self.assertIn(word.lower(), text.lower()) + for rule in ('microticket per file', 'task count is not quality', 'distant work coarse'): + self.assertIn(rule, self.plan) + for rule in ('only when risk or', 'authorized channel', 'No implicit paid web access', + 'not the rest of the ready frontier', 'No mandatory matrix', + 'sufficient local evidence needs no external refresh'): + self.assertIn(rule, self.research) + + +if __name__ == '__main__': + unittest.main()