waynestate / sourdough
A starter that makes a Wayne State app with the stack already wired in.
Requires
- php: ^8.3
- justinrainbow/json-schema: ^6.0
- openai-php/client: ^0.21
- symfony/console: ^7.4
- symfony/http-client: ^7.4
Requires (Dev)
- laravel/pint: ^1.32
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
- waynestate/pinstripe: ^0.1
- waynestate/squire: ^0.4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Sourdough
Feed it and it grows.
A starter you feed: sourdough new makes a Wayne State app with the stack already wired in.
Checks: composer ci (the same command runs on GitLab and GitHub).
Every loaf gets proofed before it ships.
Documentation: the docs site is built from docs/ by Squire's docs builder; composer docs rebuilds it and composer docs:check fails when it is stale.
Front page: site/index.html, a static page you can open in a browser or serve from site/.
On Citadel it is served by the raw-PHP deploy layer in deploy/: a Squire kit whose front controller serves site/ behind HELMET's security headers. For local development, keep serving site/ directly.
Where the code lives
The canonical repository is on nougat: enterprise-apps/sourdough. git.wayne.edu and GitHub (waynestate/sourdough) are read-only mirrors of it. Open issues and merge requests on nougat, not on the mirrors. This moves to git.wayne.edu once it has CI runners. Nougat is the starter culture; the mirrors are just slices.
Sourdough is the app starter for the WSU stack. One command scaffolds a new app with SESAME (auth), HELMET (baseline runtime), Pinstripe (coding standard), PROCTOR (checks), and the Preflight CI components already in place. It is one PHP command, so it works on Windows, macOS, and Linux.
The proofing log from the front page: what goes into the tub when you run sourdough new, and the day-to-day check that keeps it alive.
Install
composer global require waynestate/sourdough
After composer global require, the sourdough command lives in composer global config bin-dir --absolute; put that directory on your PATH.
Or scaffold without installing, with composer create-project waynestate/sourdough.
Usage
sourdough new [dir] [options]
dir is the directory to create the app in. It is required unless you use --describe.
Options:
--rawuse the raw PHP base--laraveluse the Laravel base--vueadd a Vue front end--inertiause Inertia/Vue (implies--laravel)--sailuse Laravel Sail for the local runtime (not available in this release; planned)--dockeruse a Docker image for the local runtime (not available in this release; planned)--dry-runprint the plan and write nothing--nameapp name (default: the basename of<dir>)--no-inputnever prompt (Sourdough 0.1 has no prompts)--jsonwith--dry-run, print the plan as JSON--describe "<sentence>"describe what the app does; a model plans it (needs theSOURDOUGH_LLM_*env vars)--plan-onlywith--describe, print the plan as JSON and write nothing--plan <file>a ready-made plan file (JSON matchingresources/plan.schema.json)--playbookkept for compatibility: Playbook is now the default--no-playbookskip the Playbook wiring (for CI and automated tests only; WSU apps need Playbook)--no-advisorskip the ADVISOR wiring (norequire-deventry, no.mcp.jsongit-ignore, no README section)
Exit codes: 0 done, 1 refused (invalid flags, non-empty target, not available, model gave up), 2 tool error.
By default the generated app requires waynestate/playbook (WSU's private shared-values package on git.wayne.edu): the VCS repository, the package in require, config.gitlab-domains so Composer sends your token to git.wayne.edu, the seven setting names in .env.example, a composer playbook script that shows whether Playbook is installed, and a "WSU values (PLAYBOOK)" README section that explains how to get read access. Before writing anything, sourdough new checks that this machine can read the package (a Composer credential, then a direct git probe); without read access it refuses with exit 1 and prints the how-to, and if it cannot reach git.wayne.edu it still generates the app but warns that composer install will need the connection. Non-interactive runs and CI environments skip the check. --dry-run runs the check but never refuses, and its JSON plan carries a playbook key with the mode, the access status and the probe that answered. After generation the Next steps point at composer playbook. --playbook is kept for compatibility and changes nothing; --no-playbook skips all of it. The two flags together are refused with exit 1.
Generated apps ship with ADVISOR wired in by default: waynestate/advisor lands in require-dev, .mcp.json is git-ignored (a client config starts a program for whoever opens the project), and the README gains an "ADVISOR" section. The Next steps print the install line once dependencies are in:
php vendor/bin/advisor install --client=claude --write
--no-advisor skips all of it.
The app name (from --name, from the plan, or from the basename of <dir>) must start with a letter or number and use only letters, numbers, spaces, dots, dashes and underscores, at most 63 characters; anything else is refused before anything is written.
Permutations
| Flags | Permutation |
|---|---|
(none) or --raw |
Raw PHP with WayneUI's standalone path |
--raw --vue |
Raw PHP + Vue SPA over JSON |
--laravel |
Laravel + Blade |
--laravel --vue |
Laravel + Blade + Vue islands |
--laravel --inertia (or --inertia) |
Laravel + Inertia/Vue |
--raw, --laravel, --laravel --vue and --inertia generate in 0.1. The last permutation, --raw --vue, prints the plan and exits 1 with "not available in this release"; a later step adds it.
Oracle is wired but dormant in every generated app. The settings ORACLE_USERNAME, ORACLE_PASSWORD, and either ORACLE_TNS or ORACLE_HOST, ORACLE_PORT, ORACLE_SERVICE_NAME ship commented out in .env.example; while they are empty, nothing connects and nothing errors. Once you fill them in, Laravel apps check the wiring with php artisan oracle:check, and raw PHP apps run queries through the App\Oracle helper, which takes values only as binds and answers mistakes with plain sentences. The command finds the check file where it lives: oracle-check.php at the app root, where Squire's Laravel layout ships it, first, then deploy/oracle-check.php.
Describe a plan
Instead of choosing flags, describe what the app does and let a model plan it:
sourdough new --describe "A blog with posts and comments"
Sourdough asks an OpenAI-compatible model for a plan, validates it against resources/plan.schema.json, and prints it. The model kneads the dough, but you decide when the loaf is ready to bake: if the answer is not valid JSON, does not match the schema, or the model gives up after one retry, Sourdough refuses with a message pointing you to --plan=<file> (exit 1). You write the plan yourself and pass it in.
To see the plan as JSON without writing anything:
sourdough new --describe "A blog with posts and comments" --plan-only
Generation from a plan is not built yet in 0.1. Save the plan to a file and pass it with --plan:
sourdough new my-app --plan plan.json
--plan reads the file, validates it against the schema, and continues the normal generation flow using the plan's permutation and name.
Model configuration
--describe needs three environment variables (no defaults, no key in code):
SOURDOUGH_LLM_BASE_URLthe OpenAI-compatible endpoint, including the API path (usually/v1)SOURDOUGH_LLM_API_KEYthe API keySOURDOUGH_LLM_MODELthe model name
Any OpenAI-compatible endpoint works: a hosted API, a local server, or WarriorGPT. Sourdough does not care about the hostname; it only needs the base URL, key, and model.
A plan is a small JSON object:
{
"permutation": "raw",
"parts": ["sesame", "helmet"],
"name": "my-app",
"models": [
{
"name": "Post",
"fields": [
{ "name": "title", "type": "string" },
{ "name": "body", "type": "text" }
]
}
],
"pages": ["/", "/posts"]
}
permutation, parts, and name are required; models and pages are optional. parts is a subset of sesame, helmet, proctor, and preflight.
The manifest
Every app Sourdough generates carries a .sourdough/manifest.json recording what was generated: the Sourdough version, the template, the answers you chose, and a sha256 hash of every file, each marked managed (Sourdough owns it and will update it) or seeded (Sourdough wrote it once and you own it now: .env.example and README.md).
The manifest is the starter's memory: it remembers what went into the loaf, so feed knows what to refresh and what to leave to rise on its own. A later sourdough feed reads it to bring template updates into your app without clobbering what you've changed.
Until feed ships, sourdough manifest --check is the gate. Run it in your app to list the managed files that have drifted from the manifest:
sourdough manifest --check
Exit codes: 0 clean, 1 a managed file changed or is missing, 2 tool error (no manifest, or no --check). Seeded files are never reported. They're yours.
Commit the manifest. It's the record that keeps feed honest.
The deploy files in your app (build.sh, scripts/release.sh, the .squire/ config, and their friends) come from Squire's exported copy, checked into this repository at templates/squire/. Squire owns them; Sourdough installs them as-is and records them in the manifest, so you never edit them by hand. To refresh them, run composer squire-copy:sync, which rewrites the copy from the installed waynestate/squire package, then verify it with composer squire-copy:check (part of composer ci).
Testing
Maintainers: how to build, test and release this project, and where its data comes from, is in MAINTAINING.md.
From the repo root:
docker run --rm -v "${PWD}:/app" -w /app composer:2 composer install --no-interaction
docker run --rm -v "${PWD}:/app" -w /app php:8.3-cli vendor/bin/phpunit
docker run --rm -v "${PWD}:/app" -w /app php:8.4-cli vendor/bin/phpunit
docker run --rm -v "${PWD}:/app" -w /app composer:2 composer validate --strict
docker run --rm -v "${PWD}:/app" -w /app php:8.4-cli php bin/sourdough new /tmp/demo --dry-run
The smoke gate bakes a shape for real and runs the generated app's own checks; see the "Smoke gate" section of MAINTAINING.md for the full recipe. On Windows, .\scripts\smoke-shape.ps1 <shape> runs it in one line.
CI
Strongly recommended: a git.wayne.edu instance runner.
Same kitchen, many ovens
Sourdough is one of the parts. Feed it, and it grows; just do not leave it on the counter over a long weekend.


