Skip to content

feat(e3b): skill-test-scaffold-audit-coverage — test quality advisory suite#24

Merged
javiAI merged 5 commits into
mainfrom
feat/e3b-skill-test-scaffold-audit-coverage
Apr 25, 2026
Merged

feat(e3b): skill-test-scaffold-audit-coverage — test quality advisory suite#24
javiAI merged 5 commits into
mainfrom
feat/e3b-skill-test-scaffold-audit-coverage

Conversation

@javiAI

@javiAI javiAI commented Apr 25, 2026

Copy link
Copy Markdown
Owner

Summary

E3b delivers three skills (writer-scoped + read-only advisory) for test quality scaffolding and audit. Closes Phase E.

  • /pos:test-scaffold (writer-scoped strict, main-strict): Detects test pair convention (co-located vs tests/); generates skeleton if ≥80% clear; STOP+propose options if ambiguous. No source mods, no execution.
  • /pos:test-audit (read-only advisory, main-strict): Declares candidate signals via static analysis — flaky risk, orphan tests, trivial assertions. No pytest/vitest execution, no file mods, no exhaustivity claim.
  • /pos:coverage-explain (read-only advisory, main-strict): Reads existing coverage reports (lcov.json / pytest-cov / NYC / htmlcov), declares parsing strategy, explains gaps, proposes minimum targets. No execution, no threshold mods.

Scope (strict)

  • Three frontmatter-minimal SKILL.md files (name / description / allowed-tools).
  • All declarative/advisory-only — no enforcement, no execution, no source code mutation.
  • skills_allowed: 10 → 13 (test-scaffold, test-audit, coverage-explain added).

Docs-sync (complete)

  • ROADMAP.md ✅ — E3 phase row marked done (E3a + E3b).
  • HANDOFF.md ✅ — §1 snapshot updated (current=E3b, next=F1 audit-session); §9 rewritten for F1; new §17 Estado E3a + §18 Estado E3b appended.
  • MASTER_PLAN.md ✅ — § Rama E3b expanded from 2-line stub to full close (Decisiones A1..A6, Entregables, Contrato fijado, V1→V2 ajustes, Resultado, Criterio de salida).
  • .claude/rules/skills-map.md ✅ — Pattern + Test rows for the three E3b skills populated with finalized lifecycle / context / scope / allowed-tools text matching SKILL.md bodies.

Pre-pr-gate baseline (HANDOFF.md) and conditional skills/** rule (.claude/rules/skills-map.md) were missing from the original push and were closed in commit 9312a69.

Critical review (Opus pass over Haiku-authored diff)

  • SKILL.md content from prior Haiku-authored commits verified correct on re-read; no bugs introduced. Issues were docs-sync gap + era-bound naming drift in two test functions, both addressed in 9312a69.
  • Test renames to drop stale era prefix:
    • hooks/tests/test_lib_policy.py: test_real_skills_allowed_populated_by_e3a_by_e3b (docstring 10-tuple → 13-tuple).
    • hooks/tests/test_skills_log_contract.py: test_all_ten_e1_e3a_skills_end_to_endtest_all_thirteen_e1_e3b_skills_end_to_end (docstring + f-string updated to E1a+E1b+E2a+E2b+E3a+E3b).

Deliverables

  • 3 SKILL.md files with detailed steps 1–6.
  • 15 behavior contract tests in .claude/skills/tests/test_e3b_behavior.py (writer-scoped + main-strict + advisory framing locked).
  • ALLOWED_SKILLS extended to 13.
  • policy.yaml.skills_allowed updated.
  • Docs-sync: ROADMAP + HANDOFF + MASTER_PLAN + skills-map all inside this PR.

Tests

  • Full suite: 777 passed + 1 skipped on hooks/tests/ + .claude/skills/tests/.
  • No regression on D1..D6 + E1a..E3a.
  • Behavior tests lock framing + scope boundaries (no execution, no source mods, allowed-tools shape).

🤖 Generated with Claude Code

Javier and others added 3 commits April 25, 2026 21:15
Scope: three main-strict read-only + writer-scoped skills for test quality.

- /pos:test-scaffold: writer-scoped. Detects convention, generates skeleton test pair.
- /pos:test-audit: read-only advisory. Declares candidate signals (flaky/orphan/trivial).
- /pos:coverage-explain: read-only advisory. Explains coverage gaps + minimum targets.

RED phase deliverables:
- SKILL.md files with frontmatter minimal + step-by-step bodies (strict boundaries).
- test_e3b_behavior.py: 15 contract tests (all passing).
- ALLOWED_SKILLS extended 10→13.
- policy.yaml skills_allowed extended 10→13.

Docs-sync: ROADMAP, HANDOFF, MASTER_PLAN, skills-map.md, policy.yaml updated.

Next: Fase 0 (hook validation) → Fase 1+ (refinement).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 25, 2026 19:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds E3b “test quality advisory suite” skills to the meta-repo skill registry, along with contract-style tests and documentation updates so the Stop-hook allowlist recognizes the new skills.

Changes:

  • Extend policy.yaml.skills_allowed and the test allowlist to include test-scaffold, test-audit, and coverage-explain.
  • Add 3 new skill definitions (SKILL.md) for scaffolding tests, auditing tests, and explaining coverage gaps.
  • Add E3b behavior contract tests and mark E3b complete in ROADMAP.md.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
policy.yaml Adds E3b skills to skills_allowed allowlist for Stop-hook enforcement.
ROADMAP.md Marks E3b as complete and updates the branch tracking row.
.claude/skills/tests/test_e3b_behavior.py New behavior contract tests for E3b skill framing/scope boundaries.
.claude/skills/tests/_allowed_skills.py Extends canonical ALLOWED_SKILLS list with the 3 new skills.
.claude/skills/test-scaffold/SKILL.md Defines writer-scoped scaffolding skill for creating a test-pair skeleton.
.claude/skills/test-audit/SKILL.md Defines read-only advisory test audit skill (candidate-signal reporting).
.claude/skills/coverage-explain/SKILL.md Defines read-only advisory coverage report explanation skill.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +20
### You MAY:
- Read coverage report files (lcov.json, .coverage, coverage.json, htmlcov/*, etc.).
- Analyze what lines/branches are uncovered.

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skill suggests reading .coverage directly as a report input, but .coverage is typically a coverage.py SQLite database and isn’t human-readable without running tooling to export JSON/XML. Consider prioritizing coverage.json, coverage.xml, or htmlcov/index.html as the readable artifacts, and treating .coverage as a hint that a report can be generated rather than something to parse directly.

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +46
1. **Locate coverage report** (Glob + Bash + Read):
- Search canonical paths: `coverage/lcov.json`, `.coverage`, `coverage.json`, `coverage.html/`, `htmlcov/`, `.nyc_output/coverage.json`, `coverage/coverage.json`.
- If user provides a path arg, read that directly (Bash find to verify existence).
- If no report found, declare: "No coverage report detected in canonical locations. Run your test suite with coverage enabled first (e.g., `npm run test-coverage`, `pytest --cov`)."
- Declare which format was found (lcov.json, pytest-cov JSON, NYC, etc.).

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coverage/lcov.json is not a standard lcov artifact (lcov is usually lcov.info text, often under coverage/lcov.info or coverage/lcov-report/lcov.info). Updating the canonical path examples here will make the guidance work with common NYC/Vitest setups.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +49
- Search canonical paths: `coverage/lcov.json`, `.coverage`, `coverage.json`, `coverage.html/`, `htmlcov/`, `.nyc_output/coverage.json`, `coverage/coverage.json`.
- If user provides a path arg, read that directly (Bash find to verify existence).
- If no report found, declare: "No coverage report detected in canonical locations. Run your test suite with coverage enabled first (e.g., `npm run test-coverage`, `pytest --cov`)."
- Declare which format was found (lcov.json, pytest-cov JSON, NYC, etc.).

2. **Parse report** (Read + analyze):
- **lcov.json**: extract file entries (`SF:filename`, `LH:lines_hit`, `LF:lines_found`); compute per-file coverage %.

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc refers to "lcov.json" but then describes parsing SF:/LH:/LF: entries, which are part of the lcov text format (usually lcov.info), not JSON. Please align the format name and parsing description (either document lcov.info parsing, or document actual JSON coverage formats).

Suggested change
- Search canonical paths: `coverage/lcov.json`, `.coverage`, `coverage.json`, `coverage.html/`, `htmlcov/`, `.nyc_output/coverage.json`, `coverage/coverage.json`.
- If user provides a path arg, read that directly (Bash find to verify existence).
- If no report found, declare: "No coverage report detected in canonical locations. Run your test suite with coverage enabled first (e.g., `npm run test-coverage`, `pytest --cov`)."
- Declare which format was found (lcov.json, pytest-cov JSON, NYC, etc.).
2. **Parse report** (Read + analyze):
- **lcov.json**: extract file entries (`SF:filename`, `LH:lines_hit`, `LF:lines_found`); compute per-file coverage %.
- Search canonical paths: `coverage/lcov.info`, `.coverage`, `coverage.json`, `coverage.html/`, `htmlcov/`, `.nyc_output/coverage.json`, `coverage/coverage.json`.
- If user provides a path arg, read that directly (Bash find to verify existence).
- If no report found, declare: "No coverage report detected in canonical locations. Run your test suite with coverage enabled first (e.g., `npm run test-coverage`, `pytest --cov`)."
- Declare which format was found (lcov.info, pytest-cov JSON, NYC, etc.).
2. **Parse report** (Read + analyze):
- **lcov.info (lcov text format)**: extract file entries (`SF:filename`, `LH:lines_hit`, `LF:lines_found`); compute per-file coverage %.

Copilot uses AI. Check for mistakes.
Comment thread policy.yaml
Comment on lines +293 to +295
- test-scaffold
- test-audit
- coverage-explain

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The section header comment above still says the allowlist is "E3a 10 skills", but this PR extends it to 13 (E3b). Please update that comment so the documentation matches the actual list size/phase progression.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +24
import sys
from pathlib import Path

import pytest

REPO_ROOT = Path(__file__).resolve().parents[3]
SKILLS_DIR = REPO_ROOT / ".claude" / "skills"
SKILLS_TEST_DIR = REPO_ROOT / ".claude" / "skills" / "tests"

if str(SKILLS_TEST_DIR) not in sys.path:
sys.path.insert(0, str(SKILLS_TEST_DIR))
from _allowed_skills import ALLOWED_SKILLS # noqa: E402


Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALLOWED_SKILLS is imported but never used in this test module, and the preceding sys.path manipulation exists only to support that import. Either remove the unused import (and sys.path tweak) or actually use ALLOWED_SKILLS for parametrization.

Suggested change
import sys
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[3]
SKILLS_DIR = REPO_ROOT / ".claude" / "skills"
SKILLS_TEST_DIR = REPO_ROOT / ".claude" / "skills" / "tests"
if str(SKILLS_TEST_DIR) not in sys.path:
sys.path.insert(0, str(SKILLS_TEST_DIR))
from _allowed_skills import ALLOWED_SKILLS # noqa: E402
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[3]
SKILLS_DIR = REPO_ROOT / ".claude" / "skills"

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +30


def read_skill_body(slug: str) -> str:
"""Return only the body (post-frontmatter) of a SKILL.md."""
path = SKILLS_DIR / slug / "SKILL.md"
raw = path.read_text(encoding="utf-8")
parts = raw.split("---\n", 2)
return parts[2] if len(parts) >= 3 else ""

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read_skill_body() re-implements the frontmatter split/parsing logic instead of reusing the existing read_skill() helper from test_skill_frontmatter.py. Reusing the shared helper would avoid delimiter drift and gives clearer assertion failures when SKILL.md frontmatter is malformed.

Suggested change
def read_skill_body(slug: str) -> str:
"""Return only the body (post-frontmatter) of a SKILL.md."""
path = SKILLS_DIR / slug / "SKILL.md"
raw = path.read_text(encoding="utf-8")
parts = raw.split("---\n", 2)
return parts[2] if len(parts) >= 3 else ""
from test_skill_frontmatter import read_skill # noqa: E402
def read_skill_body(slug: str) -> str:
"""Return only the body (post-frontmatter) of a SKILL.md."""
_, body = read_skill(slug)
return body

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +80
def test_scaffold_allowed_tools_includes_write(self):
"""test-scaffold must include Write tool in allowed-tools."""
path = SKILLS_DIR / "test-scaffold" / "SKILL.md"
raw = path.read_text(encoding="utf-8")
# Check that Write appears in allowed-tools section.
assert "Write" in raw

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test claims to verify that Write is present in the allowed-tools frontmatter, but it only checks whether the string "Write" appears anywhere in the raw SKILL.md. That can pass even if Write is missing from frontmatter (e.g., if the body mentions "Write"). Parse the frontmatter and assert membership in the allowed-tools list instead.

Copilot uses AI. Check for mistakes.
- Verify framing and scope boundaries declared in each SKILL.md body.
- Test that behavior aligns with contrato E3b (no execution, read-only advisory where declared).
- Lock-down wording: "declares", "proposes", "advises" (not "detects" or "parses").
- Ensure STOP boundaries are explicit when convictions ambiguous or output uncertain.

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring wording: "when convictions ambiguous" reads like the wrong word choice in English. Consider replacing "convictions" with something like "conclusions"/"confidence" to match the intent.

Suggested change
- Ensure STOP boundaries are explicit when convictions ambiguous or output uncertain.
- Ensure STOP boundaries are explicit when confidence is ambiguous or output uncertain.

Copilot uses AI. Check for mistakes.
Javier and others added 2 commits April 25, 2026 21:55
Pre-pr-gate baseline (HANDOFF.md) and conditional skills/** rule
.claude/rules/skills-map.md were missing from PR #24; this commit
closes the docs-sync gap + reconciles two test-name drifts left over
from the era-bound naming convention that ALLOWED_SKILLS replaced in
E2a.

Changes:

- HANDOFF.md §1+§9 updated for E3b PR #24 + next branch F1
  audit-session. New §17 Estado E3a snapshot + §18 Estado E3b
  with deliverables, ratified decisions, V1→V2 scope-trim, YAML
  colon gotcha note.

- MASTER_PLAN.md § Rama E3b expanded from 2-line scope stub to full
  branch close: Decisiones A1..A6 ratified, Entregables, Contrato
  fijado, Ajustes vs plan, Resultado, Criterio de salida PR #24.

- .claude/rules/skills-map.md Pattern + Test section: three E3b
  rows populated with finalized lifecycle / context / scope /
  allowed-tools text matching the SKILL.md bodies.

- hooks/tests/test_lib_policy.py — test rename _by_e3a -> _by_e3b;
  docstring 10-tuple -> 13-tuple, adds E3b line referencing
  test-quality skills.

- hooks/tests/test_skills_log_contract.py — test rename
  test_all_ten_e1_e3a_skills_end_to_end -> test_all_thirteen_e1_e3b_skills_end_to_end;
  docstring + f-string updated to E1a+E1b+E2a+E2b+E3a+E3b.

Tests: 777 passed + 1 skipped on full hooks/tests + .claude/skills/tests
suite. No regression.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eight inline review comments triaged FIX (no DISCUSS, no SKIP).
All factually correct or a real test-correctness improvement.

Changes:

- coverage-explain/SKILL.md: drop wrong references to `lcov.json`
  + `.coverage` as readable artifacts. Canonical paths now list
  `lcov.info` (lcov text format with `SF:/LH:/LF:`), `coverage.json`,
  `coverage.xml`, `htmlcov/index.html`, NYC. `.coverage` (coverage.py
  SQLite DB) reframed as a hint that a JSON/XML report can be
  generated, not parsed directly. Step 2 lcov branch renamed to
  match the format.

- policy.yaml: header above `skills_allowed:` extended from
  "...→ E3a 10 skills" to "...→ E3a 10 skills → E3b 13 skills".
  Era-bound naming drift carried over from E3a; same class of fix
  as the test renames in commit 9312a69.

- test_e3b_behavior.py:
  - replace unused `ALLOWED_SKILLS` import with `read_skill` from
    `test_skill_frontmatter` (DRY: kills the duplicated frontmatter
    splitter in `read_skill_body`).
  - `test_scaffold_allowed_tools_includes_write` now parses the
    frontmatter and asserts membership in the `allowed-tools` list
    (mirrors `simplify`'s pattern). Previous "Write" in raw text
    was a false-positive risk.
  - docstring typo fixed: "convictions ambiguous" → "confidence
    ambiguous".

Tests: 777 passed + 1 skipped on full hooks/tests + .claude/skills/tests
suite. No regression.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@javiAI

javiAI commented Apr 25, 2026

Copy link
Copy Markdown
Owner Author

Review applied — 8/8 fixed (commit 0113551)

All 8 inline comments from @copilot triaged FIX (no DISCUSS, no SKIP):

Coverage-explain doc corrections (factually correct)

  • Comment 1.coverage is coverage.py's SQLite DB; reframed as a hint that JSON/XML can be generated (not parsed directly).
  • Comment 2 — canonical lcov path corrected: coverage/lcov.jsoncoverage/lcov.info.
  • Comment 3 — Step 2 format/parse mismatch resolved: documented as "lcov.info (lcov text format)" with SF:/LH:/LF: parsing (was claiming JSON parsing on text-format markers).

Era-bound drift carryover

  • Comment 4policy.yaml header above skills_allowed: extended from "→ E3a 10 skills" to "→ E3a 10 skills → E3b 13 skills". Same class of drift as the test renames in 9312a69.

Test correctness

  • Comment 5 — unused ALLOWED_SKILLS import replaced with read_skill from test_skill_frontmatter (the sys.path.insert now has a real consumer).
  • Comment 6read_skill_body collapsed to one-line reuse of the shared read_skill (no more duplicated frontmatter splitter).
  • Comment 7test_scaffold_allowed_tools_includes_write now parses frontmatter and asserts list membership (mirrors simplify's pattern). Previous raw-text check had a false-positive risk.
  • Comment 8 — docstring typo fixed: "convictions ambiguous" → "confidence ambiguous".

Tests: 777 passed + 1 skipped on full hooks/tests/ + .claude/skills/tests/ suite. No regression.

Copilot AI commented Apr 25, 2026

Copy link
Copy Markdown

Review applied — 8/8 fixed (commit 0113551)

All 8 inline comments from @copilot triaged FIX (no DISCUSS, no SKIP):...

Confirmed — all 8 fixes from commit 0113551 verified locally: 777 passed + 1 skipped. The three correctness fixes (coverage/lcov.jsonlcov.info, .coverage reframed as SQLite hint, Step 2 format alignment), the policy.yaml comment drift fix, and the three test improvements (unused import replaced with read_skill reuse, frontmatter-parsed allowed-tools assertion, docstring typo) all look correct. No regressions.

@javiAI javiAI merged commit 5d6091d into main Apr 25, 2026
7 checks passed
@javiAI javiAI deleted the feat/e3b-skill-test-scaffold-audit-coverage branch April 25, 2026 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants