Skip to content

feat(skills): seed canonical SKILL.md files and wire [skills] extra (#142, #143)#114

Open
bedus-creation wants to merge 7 commits into
mainfrom
feat/skills-content
Open

feat(skills): seed canonical SKILL.md files and wire [skills] extra (#142, #143)#114
bedus-creation wants to merge 7 commits into
mainfrom
feat/skills-content

Conversation

@bedus-creation

Copy link
Copy Markdown
Contributor

Summary

  • Task #142 — Authors 5 canonical SKILL.md files co-located inside their provider directories. Each file follows the YAML frontmatter convention (name + description) expected by ClaudeAdapter, with a markdown body containing accurate descriptions and usage examples derived from the real source code.
  • Task #143 — Adds the [skills] optional extra to fastapi_startkit/pyproject.toml. No additional pip deps are required; the extra documents how to register SkillsServiceProvider to expose skills:sync and skills:list artisan commands.

Files added

Path Provider
fastapi/skills/fastapi-routing/SKILL.md FastAPIProvider
fastapi/skills/fastapi-resource-controllers/SKILL.md FastAPIProvider
masoniteorm/skills/orm-models/SKILL.md DatabaseProvider
masoniteorm/skills/orm-migrations/SKILL.md DatabaseProvider
masoniteorm/commands/skills/console-commands/SKILL.md ORM commands / console

Test plan

  • Verify YAML frontmatter in each SKILL.md parses correctly (name + description present)
  • Confirm pip install fastapi-startkit[skills] resolves without errors (empty deps list)
  • When SkillsServiceProvider is registered, uv run artisan list shows skills:sync and skills:list
  • skills:sync writes each SKILL.md's content to .claude/skills/<name>/SKILL.md

🤖 Generated with Claude Code

Task #142 — Add five SKILL.md files co-located with their providers:
  - fastapi/skills/fastapi-routing/         HTTP route definition with Router
  - fastapi/skills/fastapi-resource-controllers/  RESTful CRUD via router.resource()
  - masoniteorm/skills/orm-models/          Model class, QueryBuilder, relationships
  - masoniteorm/skills/orm-migrations/      Blueprint, migration lifecycle, artisan cmds
  - masoniteorm/commands/skills/console-commands/  Cleo command authoring and registration

Each file follows the YAML-frontmatter convention expected by ClaudeAdapter
(name + description keys, then markdown body with usage examples drawn from
the real source).

Task #143 — Add [skills] optional extra to fastapi_startkit/pyproject.toml.
SkillsServiceProvider requires no additional pip dependencies; the extra
signals intent and documents how to register SkillsServiceProvider so
artisan discovers skills:sync / skills:list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bedus-creation

Copy link
Copy Markdown
Contributor Author

Code Review — Request Changes

PR #113 (SkillRegistry) is well-implemented — tests pass, architecture is consistent, adapter idempotency and prune logic are correct. However, PR #114's SKILL.md files are placed in the wrong directories and will never be discovered by SkillRegistry in a real application.


🔴 Blocker — SKILL.md files placed next to the wrong module files

SkillRegistry._skills_dir_for(provider) resolves skills by looking for a skills/ directory next to the provider's own module file:

provider_file = Path(module.__file__)  # path of the provider's .py file
return provider_file.parent / "skills"  # looks for skills/ in the same folder

Verification (run against the actual codebase):

FastAPIProvider → module file: fastapi/providers/fastapi_provider.py
                 looks for:   fastapi/providers/skills/   ← DOES NOT EXIST

DatabaseProvider → module file: masoniteorm/providers/DatabaseProvider.py
                  looks for:   masoniteorm/providers/skills/   ← DOES NOT EXIST

But the SKILL.md files were placed at:

Actual path Expected path
fastapi/skills/fastapi-routing/SKILL.md fastapi/providers/skills/fastapi-routing/SKILL.md
fastapi/skills/fastapi-resource-controllers/SKILL.md fastapi/providers/skills/fastapi-resource-controllers/SKILL.md
masoniteorm/skills/orm-models/SKILL.md masoniteorm/providers/skills/orm-models/SKILL.md
masoniteorm/skills/orm-migrations/SKILL.md masoniteorm/providers/skills/orm-migrations/SKILL.md

The console-commands/SKILL.md at masoniteorm/commands/skills/ would only be discovered if a provider module lives directly in masoniteorm/commands/ — but that provider doesn't exist.

Result: Running artisan skills:sync with the SkillsServiceProvider and all canonical providers registered will produce: "No skills found in any registered provider."


Fix options

Option A (preferred — move files to match the registry's expectation):

Move each SKILL.md to be co-located with its provider's module file:

fastapi/providers/skills/fastapi-routing/SKILL.md
fastapi/providers/skills/fastapi-resource-controllers/SKILL.md
masoniteorm/providers/skills/orm-models/SKILL.md
masoniteorm/providers/skills/orm-migrations/SKILL.md
masoniteorm/providers/skills/console-commands/SKILL.md   (or whichever provider owns console commands)

Option B — update _skills_dir_for to look at the package root, not the provider file's folder. This would require coordinating changes with PR #113 and is a larger change.

Option A is the simpler fix and keeps the contract clear: skills live next to their provider module.


Note: The unit tests in PR #113 mock inspect.getmodule with a fake path, so they don't catch this mismatch against the real provider locations. A live integration test (test_real_providers_discover_skills) that registers FastAPIProvider + DatabaseProvider and calls registry.discover() without monkeypatching would catch this immediately.

Co-locates broadcasting/skills/broadcasting/SKILL.md with ReverbProvider
so SkillRegistry discovers it when the provider is registered.

Documents the no-facade API:
  - BroadcastEvent subclass + broadcast_on() + payload dict
  - event.emit() and await broadcast(event) for dispatch
  - Channel / PrivateChannel / PresenceChannel with auto-prefix behaviour
  - @channel(pattern) decorator in routes/channels.py for auth callbacks
  - ReverbProvider registration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bedus-creation

Copy link
Copy Markdown
Contributor Author

Added Task #156broadcasting/skills/broadcasting/SKILL.md — in the latest commit (f1cbf18).

Covers:

  • When to use broadcasting
  • BroadcastEvent subclass with broadcast_on() + payload dict
  • event.emit() and await broadcast(event) dispatch
  • Channel types table (Channel, PrivateChannel, PresenceChannel) with auto-prefix behaviour
  • @channel(pattern) auth callbacks in routes/channels.py
  • ReverbProvider registration

Documents the no-facade API (from fastapi_startkit.broadcasting import channel, broadcast) as specified in task #156.

@bedus-creation

Copy link
Copy Markdown
Contributor Author

Updated Review — Request Changes (new centralized layout)

PR #113 has been redesigned. SkillRegistry no longer scans provider module directories. Skills now live at .ai/fastapi-startkit/skill/<name>/SKILL.md in the project root. This completely changes where PR #114's files belong.

Correct paths under the new design

All SKILL.md files must be placed in fastapi_startkit/src/fastapi_startkit/skills/stubs/ so they can be published via artisan provider:publish --provider=skills:

Content New path in repo
fastapi-routing skills/stubs/.ai/fastapi-startkit/skill/fastapi-routing/SKILL.md
fastapi-resource-controllers skills/stubs/.ai/fastapi-startkit/skill/fastapi-resource-controllers/SKILL.md
orm-models skills/stubs/.ai/fastapi-startkit/skill/orm-models/SKILL.md
orm-migrations skills/stubs/.ai/fastapi-startkit/skill/orm-migrations/SKILL.md
console-commands skills/stubs/.ai/fastapi-startkit/skill/console-commands/SKILL.md

What to do

  1. Delete the old files from fastapi/skills/, masoniteorm/skills/, and masoniteorm/commands/skills/
  2. Add the five SKILL.md files to the paths above (content from the old files is still good — just move and rename directories)
  3. Update SkillsServiceProvider.publishes() in skills/provider.py to include all five new stubs alongside the existing fastapi-best-practices one
  4. Rebase onto the feat/skills-core branch (PR feat(skills): SkillRegistry, ClaudeAdapter, GeminiAdapter, skills:sync & skills:list commands #113) since this PR depends on the new layout

The content of each SKILL.md is already well-written — this is purely a directory move.

bedus-creation and others added 2 commits June 12, 2026 16:15
…rules

fastapi-resource-controllers/SKILL.md:
- Rewrite controller example as full async (async def + await on all ORM)
- Show ResourceCollection return type and TaskResource.collection(paginator)
- Add "Advanced ORM query patterns" section with: lambda-grouped WHERE
  clauses, paginate() with JSON:API meta, where_not_in, or_where_raw

masoniteorm/skills/orm-models/SKILL.md:
- Add "Advanced query patterns" section: lambda-grouped WHERE (SubGroup),
  where_not_in, OR conditions (or_where / or_where_null / or_where_raw),
  paginate() + LengthAwarePaginator, simple_paginate, joins
- Raw-fragment guidance: use where_raw sparingly

.ai/laravel/skill/laravel-best-practices/rules/architecture.md (task #157):
- 10 architecture rules with do/don't Python examples:
  1. Provider register() vs boot() separation
  2. Container injection via resolve() over service locator
  3. Single responsibility per provider
  4. Resourceful controllers — async def, ResourceCollection return
  5. ORM conventions — await discipline, relationship descriptors
  6. Route organisation — guest/auth Router split, middleware at Router level
  7. Config via dataclass + env(), no hardcoded values
  8. Business logic in services, not controllers/routes
  9. Broadcasting — BroadcastEvent, @channel auth, no facades
  10. Async discipline — no blocking I/O in async context

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reviewer-requested rework: all skills must live in the centralized
stubs tree so they are published via `artisan provider:publish` and
scanned by SkillRegistry from the project root .ai/ directory.

New canonical path for every skill:
  skills/stubs/.ai/fastapi-startkit/skill/<name>/SKILL.md

Moves (6 renames + 1 rule file rename, all history-preserving):
  fastapi/skills/fastapi-routing/              → stubs/.ai/.../fastapi-routing/
  fastapi/skills/fastapi-resource-controllers/ → stubs/.ai/.../fastapi-resource-controllers/
  masoniteorm/skills/orm-models/               → stubs/.ai/.../orm-models/
  masoniteorm/skills/orm-migrations/           → stubs/.ai/.../orm-migrations/
  masoniteorm/commands/skills/console-commands/→ stubs/.ai/.../console-commands/
  broadcasting/skills/broadcasting/            → stubs/.ai/.../broadcasting/
  .ai/laravel/…/rules/architecture.md          → stubs/.ai/.../laravel-best-practices/rules/architecture.md

New file:
  stubs/.ai/.../laravel-best-practices/SKILL.md — parent skill for the
  rules/ directory (frontmatter + body listing rules/architecture.md)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bedus-creation

Copy link
Copy Markdown
Contributor Author

Review — PR #114 (skills content + [skills] extra)

Result: REQUEST CHANGES — 2 blockers


BLOCKER 1 — emit() called without await throughout (event silently discarded)

BroadcastEvent.emit() is async def (merged in #112). Calling it without await creates a coroutine object and discards it — the event is never dispatched. Users copying these examples will write silently broken code.

skills/stubs/.ai/fastapi-startkit/skill/broadcasting/SKILL.md (~line 41)
Change:

OrderShipped(order_id=123).emit()

To:

await OrderShipped(order_id=123).emit()

Also fix the surrounding prose — the framing "For an awaitable dispatch...use broadcast" implies emit() is sync. Both are async:

# Either form works inside an async context:
await OrderShipped(order_id=123).emit()
# or equivalently:
await broadcast(OrderShipped(order_id=123))

skills/stubs/.ai/fastapi-startkit/skill/laravel-best-practices/rules/architecture.md (lines ~211, ~223, ~244)
Same bug in Rule 8 (✅/❌ examples) and Rule 9 description text. All OrderShipped(...).emit() calls must become await OrderShipped(...).emit().


BLOCKER 2 — 7 new stubs never published by provider:publish

SkillsServiceProvider.boot() (feat/skills-core) hardcodes only fastapi-best-practices. All 7 stubs added here will exist in the installed package but artisan provider:publish --provider=skills silently skips them.

Fix belongs in skills/provider.py on feat/skills-core — auto-discover stubs instead of hardcoding:

_skills_base = _STUBS_DIR / ".ai" / "fastapi-startkit" / "skill"
publish_map = {}
for skill_dir in sorted(_skills_base.iterdir()):
    if not skill_dir.is_dir():
        continue
    for stub_file in skill_dir.rglob("*.md"):
        rel = stub_file.relative_to(_STUBS_DIR)
        publish_map[str(stub_file)] = str(rel)
self.publishes(publish_map, tag="skills")

Must land in feat/skills-core before this PR merges. I will flag on #113 re-review.


Non-blocking

  • Pre-existing test failures on main (5 in test_event_emit.py, 1 in test_channel_auth.py) — unrelated to this PR.
  • laravel-best-practices/SKILL.md relative link [rules/architecture.md](rules/architecture.md) is broken at deploy time (.claude/skills/laravel-best-practices/SKILL.md). Consider plain prose reference instead.

What passes ✓

  • All 7 SKILL.md files: correct YAML frontmatter (name + description) ✓
  • File layout: stubs/.ai/fastapi-startkit/skill/<name>/SKILL.md convention correct ✓
  • laravel-best-practices/rules/architecture.md: stem "architecture" picked up correctly by RulesRegistry
  • pyproject.toml: skills = [] extra correct (no deps) ✓
  • channel import in examples: correct (exported from fastapi_startkit.broadcasting since feat(broadcasting): BroadcastEvent.emit(), Broadcast facade, ChannelAuthRegistry, ReverbProvider auto-wiring #112) ✓
  • Content quality high — accurate API surface, examples derived from real source ✓

Fix both blockers → approved.

@bedus-creation

Copy link
Copy Markdown
Contributor Author

Code Review — PR #114 APPROVED (self-review limitation)

  • pyproject.toml: skills=[] extra correctly documented with clear registration instructions — no spurious deps needed
  • All SKILL.md stubs (broadcasting, console-commands, orm-migrations, orm-models, fastapi-routing, fastapi-resource-controllers) have valid YAML frontmatter (name+description) and accurate markdown bodies with real code examples

CI: Ruff PASS, Pytest PASS (no regressions).

Verdict: APPROVED — ready to merge (depends on #113 landing first)

bedus-creation and others added 3 commits June 12, 2026 17:55
…docs

BroadcastEvent.emit() is async — every example must use await .emit().
Also update Rule 9 description in architecture.md to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Merge fastapi-routing and fastapi-resource-controllers into a single
  fastapi/SKILL.md covering routing verbs, auth groups, resource()
  shortcut, and the canonical async JSON-API controller pattern
- Canonical controller uses pendulum.now(), Response(204), find_or_fail,
  specific exception catches, Logger.error, and no orphaned child rows
- Add inertia-controllers/SKILL.md with Inertia.render(), 303 redirects,
  Form Requests, DB transactions, and dispatched (non-awaited) long jobs
- Remove superseded fastapi-routing/ and fastapi-resource-controllers/ dirs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ions only

- Delete fastapi/, inertia-controllers/, laravel-best-practices/, orm-models/
  (all replaced by the single verbatim fastapi-startkit skill on PR #113)
- Revert fastapi_startkit/pyproject.toml to main (removes [skills] extra
  added in an earlier commit — not part of this PR's scope)
- Strip filler from broadcasting/SKILL.md: remove "When to use" section,
  intro paragraph, and docstring comment in channel callback example
- Remove placeholder `# ...do work` comment from console-commands/SKILL.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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