v0.8: Scaffolding That Works Out of the Box
When we ran the five-task experiment last week, we proved that structural constraint enforcement beats prompt-based communication. But the experiment also revealed a gap: the generated scaffold code still needed manual work to handle real-world patterns like status transitions, associations, and pagination.
SysMARA v0.8 closes that gap. Projects generated from a single prompt now produce code that handles real HTTP semantics correctly — no manual fixups needed.
What changed
The v0.8 release is the result of running AI agents against real SysMARA projects and cataloging every place where the generated code broke, returned wrong status codes, or silently ignored inputs. Here is what we fixed.
Status transition scaffolding
Before v0.8, a capability named publish_post or approve_comment would generate
a generic handler that called repo.findById() — a read-only operation. The "publish" or
"approve" part was left as a TODO.
Now, SysMARA recognizes 20+ verb prefixes for status transitions: publish_,
archive_, approve_, reject_, submit_,
moderate_, flag_, and more. The scaffolded handler generates proper
find-and-update-status logic:
// Generated for capability: publish_post
export async function handlePublishPost(ctx) {
const repo = orm.repository('post', 'publish_post');
const entity = await repo.findById(ctx.params.id);
if (!entity) throw new NotFoundError('post', ctx.params.id);
const updated = await repo.update(ctx.params.id, { status: 'published' });
return updated;
}
The generated server also maps these capabilities to the correct HTTP method and path:
publish_post becomes PATCH /posts/:id/publish.
Association scaffolding
Similarly, tag_post and untag_post no longer generate incorrect
findById calls. They now produce create/delete logic on the association entity.
Route inference maps them to POST /posts/:id/tag and POST /posts/:id/untag.
List handlers read query parameters, not body
This was a subtle but critical bug. Scaffolded list_* handlers were reading filters from
ctx.body — but GET requests have no body. Every list endpoint silently ignored filters.
Now list handlers read from ctx.query and support pagination out of the box:
GET /posts?limit=10&offset=20&order_by=created_at&order_dir=DESC
// Response:
{
"items": [...],
"count": 10,
"limit": 10,
"offset": 20
} Policy violations return 403, not 500
Before this fix, scaffolded policy enforcement threw a generic Error, which the server
caught as a 500 INTERNAL_ERROR. Clients had no way to distinguish "you don't have permission"
from "something crashed."
Now policy violations throw ForbiddenError and return HTTP 403 with a structured
error response including the policy name.
Header-based actor extraction
Both sysmara start and the generated app/server.ts now extract actor
identity from HTTP headers on every request:
| Header | Description | Default |
|---|---|---|
X-Actor-Id | User identifier | anonymous |
X-Actor-Roles | Comma-separated roles | (empty) |
Previously, all requests were treated as anonymous — making every policy check meaningless. Now policy enforcers receive real actor context and can make real access control decisions.
Generated server parses real specs
The generated app/server.ts entry point was calling the ORM constructor with empty
arrays instead of actual parsed specs. This meant entity tables were never created and repositories
operated on phantom schemas.
Now the generated server calls parseSpecDirectory() and passes real specs to the ORM
constructor. Tables are created from actual entity definitions.
ORM fails fast when not connected
Before v0.8, calling orm.repository() without await orm.connect() would
silently return empty results — findById returned null,
findMany returned [], create echoed the input back. It looked
like the system was working, but no data was actually persisted.
Now the ORM throws immediately: "ORM not connected. Call await orm.connect() before creating repositories." The error is obvious, not silent.
tsconfig.json generation
sysmara init now generates a tsconfig.json configured for ES2022/NodeNext.
Previously, AI agents had to create this file manually — and often got the module resolution wrong.
Also in this release: file nodes in the system graph (v0.7.2)
The system graph now includes file nodes for every generated artifact. Each capability
produces three file nodes: routes/*.ts, tests/*.test.ts, and
metadata/*.json. Module → file owns edges connect each module to
its generated files.
This means impact analysis now shows exactly which files are affected by a change —
not just which abstract concepts. The ImpactSurface object includes an
affectedFiles array, and the terminal formatter displays an "Affected Files" section.
$ sysmara impact entity post
Affected Files (6):
- app/generated/routes/create_post.ts
- app/generated/tests/create_post.test.ts
- app/generated/metadata/create_post.json
- app/generated/routes/publish_post.ts
- app/generated/tests/publish_post.test.ts
- app/generated/metadata/publish_post.json This matches the formal graph definition G = (V, E, τv, τe) from the SysMARA design paper — generated files are first-class citizens of the architecture, not ephemeral build artifacts.
The net effect
Before v0.8, an AI agent generating a SysMARA project from a single prompt would produce code that compiled but needed 5-10 manual fixes to actually work: wrong HTTP methods, missing query parameter parsing, silent auth failures, phantom ORM connections. These were not bugs in the agent's reasoning — they were gaps in what the scaffold generator produced.
After v0.8, the same single-prompt workflow produces code that works correctly out of the box. Status transitions use PATCH. List endpoints parse query strings. Policies return 403. The ORM connects to a real database with real specs.
The philosophy has not changed: humans define boundaries, AI agents implement within them. But now the boundaries include correct HTTP semantics, not just business rules.
Upgrading
npm install @sysmara/core@latest
Existing scaffold files are not overwritten — only new capabilities will get the improved generators.
To regenerate all scaffold files, delete the app/ directory and run
sysmara build.
Full changelog: CHANGELOG.md