Skip to content

Add living specifications system#835

Open
edwh wants to merge 7 commits into
developfrom
feature/living-specs
Open

Add living specifications system#835
edwh wants to merge 7 commits into
developfrom
feature/living-specs

Conversation

@edwh

@edwh edwh commented Apr 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Introduces a living documentation system that embeds user stories directly in PHP controller code via PHP 8 attributes (#[Feature], #[UserStory], #[NoStory])
  • Adds specs:extract artisan command that uses nikic/php-parser AST analysis to extract 168 user stories across 8 feature areas and 6 personas from 29 controllers into a JSON manifest
  • Stories are further grouped thematically within each feature (e.g. Groups → Invitations, Manage volunteers; Events → Attendance & volunteers, Stats & data) so the spec pages mirror the "What X can do" structure
  • Adds @story:ControllerClass::method docblock annotations to test files, linking 165 of 168 stories (98%) to their tests
  • Generates a browsable VitePress static site with dual navigation (by feature and by persona), platform overview on the landing page, AI-written narrative overviews, test coverage indicators, and GitHub source links
  • Includes GitHub Actions workflow for automatic deployment to GitHub Pages on push to develop
  • Removes the historical features/ directory (100+ Gherkin files) — all relevant content has been folded into the new code-level annotations

Live preview: https://therestartproject.github.io/restarters.net/

What this enables

  • Claude maintenance: when modifying controller methods, the #[UserStory] attributes are visible inline, prompting updates when behaviour changes
  • CI validation: php artisan specs:extract --check detects manifest drift, unannotated public methods, and stale narratives
  • Browsable spec site: stakeholders can browse what the platform does without reading code, organised by feature area or user persona, grouped by theme within each feature
  • Test coverage tracking: each story shows whether it has test coverage via @story: references in test files

Feature areas

Events (53 stories), Groups (36), Users (25), Devices (14), Platform (20), Networks (15), Administration (3), Dashboard (2)

Personas

Admin, Host, Restarter, NetworkCoordinator, Guest, ThirdParty

Test plan

  • php artisan specs:extract generates valid manifest.json with 168 stories
  • 165/168 stories (98%) linked to test methods via @story: annotations
  • npm run build in specs-site/ produces working static site
  • All 8 feature pages and 6 persona pages render correctly
  • Stories grouped by theme within each feature on both feature and persona pages
  • Live preview deployed to GitHub Pages
  • Verify existing PHPUnit tests still pass (attributes are inert metadata)
  • Review a sample of controller annotations for accuracy

Comment thread .github/workflows/specs-site.yml Fixed
Comment thread .github/workflows/specs-site.yml Fixed
Comment thread .github/workflows/specs-site.yml Fixed
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
C Security Rating on New Code (required ≥ A)
D Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

edwh and others added 4 commits July 15, 2026 09:57
Introduces a code-first living documentation system that embeds user
stories directly in controller code via PHP 8 attributes (#[Feature],
#[UserStory], #[NoStory]), extracts them into a JSON manifest, and
builds a browsable VitePress static site with dual navigation by
feature and by persona.

- 3 PHP attribute classes in app/Attributes/
- specs:extract artisan command using nikic/php-parser AST analysis
  (already a transitive composer dependency, no new requirement)
- JSON manifest at docs/specs/manifest.json, 8 narrative markdown
  files in docs/specs/narratives/
- Standalone VitePress site in specs-site/ with prebuild page
  generation (independent package.json, does not touch the app's
  Vite/npm setup)
- GitHub Actions workflow for GitHub Pages deployment
- Removes the historical features/ directory (Gherkin-style specs),
  superseded by the annotation system; untouched since this branch
  first diverged from develop so nothing current is lost
- Adds a Living Specifications section to CLAUDE.md describing the
  workflow for keeping annotations current

Rebuilt from the original feature/living-specs branch (~600 commits
behind develop) on top of current develop. The @story annotations on
controllers and tests are ported in follow-up commits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The previous commit's message described this addition but the section
was left out of what got staged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports the user story attributes from the original feature/living-specs
branch onto the current controller code. Annotated via #[Feature] on
each controller class and #[UserStory]/#[NoStory] on public action
methods, matched by method name against the branch's final annotated
state and merged with a 3-way text merge (base = branch's original
divergence point, mine = current develop, theirs = branch tip) so
that unrelated evolution of these files (return-type hints added since)
is preserved alongside the new attributes.

168 user stories across 8 features and 6 personas, grouped into
themes within each feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports @story:ClassName::method docblock references from the original
feature/living-specs branch onto the current test suite, linking test
methods to the user stories added in the previous commit. specs:extract
picks these up via regex over docblocks so the VitePress site can show
per-story test coverage.

Merged the same way as the controller annotations: a 3-way text merge
per file (branch divergence point / current develop / branch tip),
which correctly threads new docblock lines through method signatures
that have since gained return-type hints, without disturbing them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@edwh
edwh force-pushed the feature/living-specs branch from fc617f1 to a0299fd Compare July 15, 2026 09:09
@edwh

edwh commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Rebuilt this branch on current develop rather than rebasing — the original was ~600 commits behind and the @story docblock diffs no longer applied after the Vite migration and general drift.

What changed:

  • Recreated the branch from current develop as 4 commits (system + docs, controller attributes, test annotations, one follow-up fixing a CLAUDE.md addition that got left out of the first pass).
  • Carried over the standalone deliverables as-is: app/Attributes/{Feature,NoStory,UserStory}.php, app/Console/Commands/SpecsExtract.php, docs/specs/manifest.json + narratives, the standalone VitePress site in specs-site/ (own package.json, doesn't touch the app's Vite/npm setup), the GitHub Pages workflow, and the design doc. Also re-applied the deletion of the legacy features/ Gherkin directory — it hadn't been touched since this branch first diverged, so nothing since has been lost by removing it.
  • Ported every #[Feature]/#[UserStory]/#[NoStory] attribute and every @story: test reference from the original branch's final state onto the current code, using a per-file 3-way text merge (merge-base version / current develop / branch tip). Where current develop had independently added PHP return-type hints on the same method signatures the branch was annotating, the merge preserves both.
  • Verified nothing was dropped: counted attributes/references before and after — 168/168 #[UserStory], 31/31 #[Feature], 15/15 #[NoStory], and 326/326 @story: test references all made it across untouched files or files that still exist with the same method names. No controller or test file referenced by the original branch had been deleted or renamed, so there was nothing to drop or note as stale.
  • Validated with php -l on all 94 touched PHP files (via the running restarters container) and ran the VitePress prebuild script locally against the carried-over manifest — both succeed.

Needs human follow-up:

  • docs/specs/manifest.json is carried over verbatim from the original branch's last generation and wasn't regenerated, since php artisan specs:extract needs a full app container this worktree doesn't have. Given the annotation counts match exactly, it should already be accurate, but it's worth running php artisan specs:extract once after merge (or as part of a follow-up) to confirm and refresh the generatedAt timestamp.
  • Did not run the PHPUnit suite (shared test DB, not safe to run from a second checkout) or the specs:extract command itself — only static validation (lint + counts + the JS prebuild step) was possible here.

Comment thread .github/workflows/specs-site.yml Fixed
Comment thread .github/workflows/specs-site.yml Fixed
Comment thread .github/workflows/specs-site.yml Fixed
edwh and others added 3 commits July 15, 2026 10:16
SonarCloud (rightly) wants write permissions scoped to the deploy job
rather than granted workflow-wide.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TzY1phBjyT3HogpeS53zon
- generate-pages.mjs: give every string sort an explicit localeCompare
  comparator (default sort is lexicographic-by-code-unit)
- SpecsExtract: coalesce php-parser dynamic-property iterables to [] -
  the analyzer cannot see them initialized, and a malformed node would
  genuinely make them so

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TzY1phBjyT3HogpeS53zon
Sonar's PHP analyzer cannot see php-parser's vendor property
declarations, so it treats every AST property read as uninitialized
even behind a null-coalesce. Assign to explicit locals and mark the
three reads NOSONAR with justification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TzY1phBjyT3HogpeS53zon
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

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.

2 participants