Skip to content

Ship the example project skeleton with an initialized checklist (#648) - #697

Merged
PavloSEO merged 2 commits into
mainfrom
feat/648-skeleton-coverage
Sep 9, 2026
Merged

PavloSEO merged 2 commits into
mainfrom
feat/648-skeleton-coverage

Conversation

@PavloSEO

@PavloSEO PavloSEO commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Advances #648.

The gap

Epic #648's definition of done says project status on the examples/ skeleton prints a coverage figure matching a hand count. It did not:

$ seohead project-status --directory examples/project-skeleton
  "checklist": {
    "state": "not_initialized",
    "reason": "coverage checklist is initialized by project checklist setup, not project creation"
  },

The one project in this repository a reader can run the documented command against answered with a state instead of a number. The epic also asked that the skeleton be executable and walkable by the doc gates, and grep -rn "project-skeleton" tests/ docs/ scripts/ returned nothing — no test and no document named that directory.

Separate initialization is deliberate and documented (docs/PROJECTS.md), so project new is unchanged. The example is fixed the other way: it now ships already initialized.

What changed

  • examples/project-skeleton/coverage.json is committed: 250 definitions, one per catalogue item, every one not_run with an empty records list. Nothing has been run against the synthetic site, so nothing claims a result.
  • Initialization stamps each definition with the wall clock, which was the only machine-specific value the generated file carried. Those 250 observed_at values are normalized to the project's own created_at (2026-09-09T00:00:00Z) — the only observation time this example can honestly state — so the committed file is byte-stable and carries no generating machine's clock. No absolute paths, no fabricated results.
  • scripts/generate_project_skeleton_coverage.py regenerates that file in one command, following the repository's other scripts/generate_*.py artifacts. It initializes a throwaway copy, so --check never writes into the shipped project and a stale committed file is compared against a first initialization rather than a reconciliation of itself.
  • tests/test_project_skeleton_coverage.py is the gate: it fails if the file disappears, if item IDs drift from the live catalogue, if any shipped item claims a result, if a definition time is not the project's creation time, if the bytes differ from a fresh generation, or if project-status stops reporting counts. The drift and staleness messages name python scripts/generate_project_skeleton_coverage.py, matching the other generated-artifact gates.
  • docs/PROJECTS.md describes the skeleton as already initialized and names it by path, in a runnable fence — so tests/test_docs_commands_execute.py now executes project status against the shipped project. That gate no longer creates a synthetic project when the documented directory already exists under examples/.
  • examples/README.md gains a section for the skeleton; changelog.d/648.md records the change.

Validation

Hand count of the live catalogue:

checks (seohead.sf.core.registry.CHECKS): 161
skills (.claude/skills/*/SKILL.md + seohead/skills/*/SKILL.md): 23 + 7 = 30
scenarios (docs/scenarios/*.md, excluding README): 59
161 + 30 + 59 = 250

seohead project-status --directory examples/project-skeleton after the change:

"checklist": {
  "state": "initialized",
  "revision": 1,
  "counts": {"run": 0, "not_applicable": 0, "not_run": 250, "total": 250,
             "disabled": 0, "complete": 0, "stale": 0, "remaining": 250},
  "by_kind": {"check": {"total": 161, "complete": 0},
              "skill": {"total": 30, "complete": 0},
              "scenario": {"total": 59, "complete": 0},
              "custom": {"total": 0, "complete": 0}}
}

The printed figure matches the hand count.

The gate was proven to fail without the fix, three ways, each restored afterwards:

  • committed file removed: all 6 tests fail, including state == "initialized" asserting against not_initialized — the exact pre-change behaviour.
  • one item ID dropped: AssertionError: examples/project-skeleton/coverage.json has drifted from the packaged catalogue: regenerate it with 'python scripts/generate_project_skeleton_coverage.py'
  • a succeeded record injected into one item: test_no_shipped_item_claims_a_result fails with Left contains one more item: {'reason': 'fabricated', 'status': 'succeeded'}.

Restored, the file matches a fresh generation byte for byte and all 6 pass.

Gate output:

$ ruff check .
All checks passed!
$ ruff format --check .
696 files already formatted

$ pytest tests/test_project_skeleton_coverage.py -q
6 passed in 0.05s

$ pytest tests/test_docs_drift.py tests/test_doc_counts.py tests/test_project_catalogue.py tests/test_docs_scenario_claims.py -q
227 passed, 1 warning in 10.85s

$ pytest -q
4528 passed, 2 skipped, 1 warning in 410.85s (0:06:50)

Note on the full run: on the uncommitted working tree, ten documented crawl-site lines failed with native scan provenance requires a clean source checkout. That is the build-provenance gate reacting to a dirty tree, not to this change; the run above, after committing, is clean.

`seohead project status --directory examples/project-skeleton` answered
`not_initialized`: the one project in this repository a reader can run the
documented command against reported a state instead of a coverage figure, and
nothing under tests/ or docs/ named that directory at all.

Checklist initialization stays separate from project creation, as documented.
The example is fixed the other way: `project checklist-init` was run against the
skeleton and its `coverage.json` is now committed, with one definition per
catalogue item, every item `not_run` and no execution record, because nothing
has been run against its synthetic site.

Initialization stamps each definition with the wall clock, which is the only
machine-specific value the generated file carried. Those 250 `observed_at`
values are normalized to the project's own `created_at`
(`2026-09-09T00:00:00Z`), the only observation time this example can honestly
state, so the committed file is byte-stable across regenerations and carries no
generating machine's clock. There are no absolute paths and no fabricated
results.

`scripts/generate_project_skeleton_coverage.py` performs that regeneration in
one command, following the repository's other generated-artifact scripts: it
initializes a throwaway copy so a `--check` run never writes into the shipped
project, then normalizes and renders.

`tests/test_project_skeleton_coverage.py` holds the shipped file to the live
catalogue. It fails if the file disappears, if its item IDs drift from the
catalogue, if any shipped item claims a result, if a definition time is not the
project's creation time, if the bytes differ from a fresh generation, or if
`project-status` stops reporting counts for it. The ID-drift and staleness
messages name the regeneration command, matching the other generated-artifact
gates.

The documented status command now points at the skeleton by path, so
`tests/test_docs_commands_execute.py` executes it against the shipped project;
that gate no longer creates a synthetic project when the documented directory
already exists under `examples/`.
@PavloSEO
PavloSEO merged commit 522e371 into main Sep 9, 2026
9 checks passed
@PavloSEO
PavloSEO deleted the feat/648-skeleton-coverage branch September 9, 2026 20:19
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.

1 participant