Skip to content
Merged
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
6 changes: 4 additions & 2 deletions docs/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,10 @@ cycle and parses one explicit criteria section. Accepted headings are exactly
Markdown heading (`#` through `######`) or bold label. A trailing colon or
period is allowed inside a bold label or after it. The section is a flat list
at column zero using `-`, `+`, `*`, `1.` or `1)` markers and optional Markdown
checkboxes; another heading or bold label ends it. Empty, missing, duplicate,
placeholder or unparseable sections produce deterministic `needs_refinement`
checkboxes; another heading or a bold label ends it. Punctuated labels may
include prose on the same line, such as `**Label.** text` or `**Label:** text`.
Bold markup inside a list item does not end the section. Empty, missing,
duplicate, placeholder or unparseable sections produce deterministic `needs_refinement`
before any implementation, even with issue review off or difficulty 1. They
are insufficient specification, never a failed test. Existing issues are not
migrated. `.github/ISSUE_TEMPLATE/work-item.md` supplies the grammar for new work.
Expand Down
2 changes: 1 addition & 1 deletion scripts/verification_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def from_dict(cls, value: Mapping[str, Any]) -> Criteria:
_LABEL = r"(?:Acceptance criteria|Criterios de aceptación)"
_HEADING = re.compile(rf"^\s*(?:#{{1,6}}\s+({_LABEL})\s*[:.]?\s*#*|\*\*({_LABEL})[:.]?\*\*[:.]?)\s*$", re.I)
_ITEM = re.compile(r"^(?:[-+*]|\d+[.)])\s+(?:\[[ xX]\]\s+)?(\S.*)$")
_SECTION = re.compile(r"^\s*(?:#{1,6}\s+|\*\*[^*]+\*\*[:.]?\s*$)")
_SECTION = re.compile(r"^\s*(?:#{1,6}\s+|\*\*[^*]+\*\*[:.]?\s*$|\*\*[^*]+[.:]\*\*[:.]?\s+\S.*$)")


def parse_criteria(provider: str, body: str | None) -> Criteria:
Expand Down
38 changes: 38 additions & 0 deletions tests/fixtures/issue-330.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
**Prioridad:** P0. **Esfuerzo:** M.

**Problema y justificación.** Si Orca interpreta perfiles y condiciones por separado, aparecerían dos políticas de ciclo con el mismo nombre.

**Objetivo y alcance.** Consumir el mismo plan, resolver y validadores en la orquestación supervisada de Orca.

**Componentes.** `cc-orca-orchestrator`, `OrcaDispatchContext`, `OrcaAdapter`, helpers de planificación y tests de instalación.

**Orientación técnica.** Conservar Run, Task, worker y receipts de Orca. El adapter consume esas identidades; no las inventa. Las decisiones de política vienen del componente común.

**Dependencia estricta.** #329.

**Criterios de aceptación:**

- Entradas equivalentes producen obligaciones equivalentes en Orca y nativos.
- El perfil efectivo y la revisión del plan viajan con la delegación.
- Un receipt de lanzamiento nunca se acepta como resultado funcional.
- Se mantienen workspaces de review y permisos de publicación.
- Cambios de HEAD y resultados parciales siguen las mismas reglas de invalidez.
- Los cinco modos tienen ejemplos y resultados terminales documentados.
- Versiones de Orca y limitaciones verificadas se registran en la evidencia.
- Rereview permanece condicional en todos los modos: sin modificaciones posteriores relevantes ni evidencia invalidada no se repite, tampoco en Full. Hallazgos abiertos sin correcciones mantienen sus bloqueos; Orca no lanza otra revisión idéntica para intentar otro resultado. Los cambios posteriores reactivan solo los controles cuya evidencia deba reevaluarse.

**Tests.** Fixtures equivalentes entre transportes; errores de contexto, lanzamiento sin finalización, resume, cancelación y al menos una campaña controlada con Orca real. Incluir paridad de selección/omisión de rereview en los cinco modos.

**Riesgos.** Suposiciones sobre capacidades no disponibles en la versión instalada.

**Fuera de alcance.** Sustituir Orca, crear un coordinador nuevo, renderers o protocolo remoto universal.

## Trazabilidad del roadmap aprobado

**Referencia estratégica:** [docs/ROADMAP_V2.md](https://github.com/datacas/code-cycle-toolkit/blob/35b9f9cdced8d5da549d9a84290a4b260243a0df/docs/ROADMAP_V2.md) (versión aprobada; la ruta canónica se conserva en el índice del repositorio).

**Bloqueos estrictos:** #329

Esta issue materializa la planificación aprobada el 26 de octubre de 2026; su existencia no acredita implementación. Las recomendaciones y condiciones no son bloqueos generales. No autoriza iniciar funcionalidades durante la materialización del roadmap.

<!-- cct-v2-approved-plan:2026-10-26 -->
34 changes: 34 additions & 0 deletions tests/test_verification_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,40 @@ def test_closed_grammar_english_spanish_heading_bold_lists(self):
self.assertEqual(parsed.status, "evaluable")
self.assertEqual(parsed.items, ("Works",))

def test_inline_bold_section_label_ends_criteria(self):
for label in ("**Tests.** Fixtures cover the parser.",
"**Tests:** Fixtures cover the parser."):
with self.subTest(label=label):
parsed = criteria(f"## Acceptance criteria\n- A\n- B\n\n{label}\nOther prose")
self.assertEqual(parsed.status, "evaluable")
self.assertEqual(parsed.items, ("A", "B"))

def test_malformed_criteria_remain_needs_refinement(self):
for body, reason in (
("## Acceptance criteria\nprose", "criteria must be a flat Markdown list"),
("## Acceptance criteria\n- Works\n**Prose** is still prose.",
"criteria must be a flat Markdown list"),
("## Acceptance criteria\n- Works\n - nested", "criteria must be a flat Markdown list"),
("## Acceptance criteria\n- TODO", "placeholder criterion"),
("## Acceptance criteria\n", "acceptance-criteria list is empty"),
):
with self.subTest(body=body):
parsed = criteria(body)
self.assertEqual(parsed.status, "needs_refinement")
self.assertEqual(parsed.reason, reason)

def test_bold_markup_inside_criterion_does_not_end_section(self):
parsed = criteria("## Acceptance criteria\n- Keeps **bold markup** inside the criterion\n"
"**Tests.** Regression coverage follows.")
self.assertEqual(parsed.status, "evaluable")
self.assertEqual(parsed.items, ("Keeps **bold markup** inside the criterion",))

def test_issue_330_body_is_evaluable_without_remote_edit(self):
body = (ROOT / "tests" / "fixtures" / "issue-330.md").read_text(encoding="utf-8")
parsed = criteria(body)
self.assertEqual(parsed.status, "evaluable")
self.assertEqual(len(parsed.items), 8)

def test_missing_empty_duplicate_unparseable_and_placeholder_stay_open(self):
for body in ("## Problem\nWorks", "## Acceptance criteria\n", "## Acceptance criteria\nprose",
"## Acceptance criteria\n- Works\n - nested",
Expand Down
Loading