sparrowhawk-labs / sparrowhawk
Deterministic TALL-stack app generation (Wright) and dataflow contract checking (Ward) from a single thin spec — declare what each page reads and writes, and catch violations before they bite.
Requires
- php: ^8.2
- illuminate/console: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- livewire/livewire: ^3.0|^4.0
Requires (Dev)
- larastan/larastan: ^3.10
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
This package is auto-updated.
Last update: 2026-07-18 01:25:51 UTC
README
Deterministic app generation and dataflow contract checking for the TALL stack.
"From apprentice to mage of the web."
Status
Alpha — a working vertical slice. The generator (Wright) and the checker (Ward) run end-to-end; APIs and spec vocabulary are not yet stable.
The idea: App = f(spec)
You write a small, finite spec — the Armature: your data schema plus, per page, what it reads and writes. From that:
- Wright (generator) derives a plain Laravel/Livewire app — migrations, models, Livewire components, Blade views, tests, routes — as a pure function. Same spec in, byte-identical app out. No LLM, no randomness, no "regenerate and hope".
- Ward (checker) verifies, statically, that the code — including everything you or your AI assistant edited afterwards — still honors the declared dataflow contract:
code traffic ⊆ declared reads/writes ⊆ entity columns.
The generated code is plain Livewire, written the way a beginner would read it. You own it, edit it, and grow it; Sparrowhawk adds no runtime, no base classes to inherit, no new DSL beyond the spec itself (which is plain PHP).
return App::define('contacts') ->entity('Contact', fn (Entity $e) => $e ->string('name') ->string('email') ->string('phone')) ->page('contacts/index', fn (Page $p) => $p ->kind('index') ->reads('Contact', ['name', 'email', 'phone'])) ->page('contacts/create', fn (Page $p) => $p ->kind('create') ->writes('Contact', ['name', 'email', 'phone']));
Quick start (the gen → edit → check loop)
composer install bin/ward gen playground/spec.php playground/app # generate the baseline app bin/ward check playground/spec.php playground/app # → all green # ... hand-edit a component or Blade view ... bin/ward check playground/spec.php playground/app # → ✗ violations / ⚠ flags
playground/SCENARIOS.md is a hands-on tour: each scenario is edit this → expect that → here's what it proves.
Ward is fail-closed
Ward does not infer intent. It resolves literal references (wire:model, {{ $contact->email }}, ->where('col'), ->col =, fillable arrays, …) against the closed vocabulary of your schema, and classifies each as a read or a write:
✓— all code traffic is inside the declared contract✗ violation— code exceeds the declaration (an undeclared read/write of a real column)⚠ flag— a construct Ward cannot resolve from the source text (dynamic column names, untyped receivers). It flags instead of silently passing. A missed defect that stays quiet is the one failure mode Ward is designed to never have.
Sensitive columns (->string('password', sensitive: true)) cannot be declared readable at all — exposure of a sensitive field is always a violation, on every page.
Does it work on apps Wright didn't generate?
Yes — that's the point of the contract being declared, not derived. verification/realworld/ contains a fully reproducible study against a third-party open-source app (the RealWorld/Conduit TALL implementation, Livewire 2.x, 9 pages):
- After a curation pass (extract → human-approve declarations), the untouched app checks 0 violations.
- 6 out of 6 seeded dataflow defects (undeclared writes, sensitive-field exposure, a renamed action, an over-reading view, a dynamic-property write) are caught — none silently pass.
To rerun it, clone the target app into verification/realworld/realworld-app (or set REALWORLD_APP) and run verification/realworld/realworld-harness.php.
What Sparrowhawk is not
- Not LLM scaffolding — generation and checking are deterministic; results are reproducible and free to rerun
- Not a lock-in platform — the output is ordinary Laravel/Livewire code; delete the spec and the app still stands
- Not a new DSL to learn — the spec is plain PHP; the generated code uses only mainstream Livewire idioms
- Not an admin-panel auto-generator (that's Filament's space) and not a UI library (that's
pinion-ui) - Not a whole-app oracle — Ward guarantees the structural seams (screen ↔ component ↔ database). Method bodies, business logic, and visual design are deliberately yours.
Ecosystem
sparrowhawk-labs/sparrowhawk— Wright + Ward (this repository)sparrowhawk-labs/pinion-ui— Blade UI components (Tailwind v4 + daisyUI v5 + Alpine.js, Tune token system)sparrowhawk-labs/pinion-icons— unified icon system (<x-i>Blade tag, Solar / Fluent Emoji / Pixelarticons)sparrowhawk-labs/pinion-ui-playground— showcase app for the UI catalog
pinion (= primary feather, 風切り羽) is the wing-feather a sparrowhawk steers with — the plugin family that ships alongside the core.
Inspiration
The project draws its name from Ursula K. Le Guin's Earthsea — a story about a young wizard (Sparrowhawk) whose journey from apprentice to mage mirrors the path of a developer learning to wield powerful tools with care. The true name motif fits: Sparrowhawk asks you to name your app's structure precisely — and in return, the structure holds.
License
MIT. See LICENSE.
Sponsor
Crafted by Yakaze Tech Studio.