Prompt-to-App Agent
Give OpenFactory a one-sentence brief and a template repo, and you get back a
deployed app at https://<slug>.apps.openfactory.tech. The brief is saved on
the App record so later iteration instructions can be applied against the
original ask without losing context.
Already have a Git repo to deploy? Skip the prompt path and use
deploy_appdirectly. The prompt path is for “give me a draft from a brief” flows, not “deploy this exact repo”.
When to use this
- You want a scaffolded app stood up from a short description, not a full repo.
- You want to record iteration instructions (“make the header green”, “add CSV export”) against an app and have them tracked alongside the original brief.
- You want a stable App record that an agent can take over later and keep refining.
Shipped today (M1: synchronous template fallback)
The shipped path is a synchronous template deploy. You supply both the
brief and a template git URL; OpenFactory creates the App with the brief saved
to App.origin, deploys the template via the existing
deploy pipeline, and returns the URL.
What this means in practice:
- Brief is preserved, not yet executed by an LLM. M1 does not generate source code from the brief. The template you pass is what gets deployed. Pick a scaffold close to what the brief asks for.
iterate_apprecords, it does not apply. Iteration instructions are appended toApp.origin.iterationsso the agent loop can read them later. To actually redeploy in M1, calldeploy_app.- The data model and API surface are locked. When the agent loop lands,
the swap is a localized change inside
AppPromptService; the MCP tools, REST endpoints, andApp.originshape do not move.
LLM code generation, agent self-test loops (via
walk_app), and automatic application of iteration
instructions land in a follow-up milestone.
How it works
- Submit a brief with
create_app_from_prompt(brief, template_git_url, vm_name, ...). OpenFactory creates an App, setsorigin = {type: "prompt", brief, template_git_url, iterations: [], ...}, and deploys the template into your tester VM. - Inspect with
get_app_build_status(app_id)to read the brief, deploy history, current URL, env-version drift, and recorded iterations. - Record iterations with
iterate_app(app_id, instruction)as you refine the ask. Each instruction is timestamped onorigin.iterations.
MCP tools
| Tool | Use |
|---|---|
create_app_from_prompt | Create an App from a brief, deploy a template, preserve the brief on App.origin |
iterate_app | Record an iteration instruction against a prompt-built app |
get_app_build_status | Read brief, deploy history, current URL, and iterations for a prompt-built app |
create_app_from_prompt
Synchronous M1 path. Creates the App with origin.brief populated, runs the
template through deploy_into_vm, and returns the deployed URL.
Example call:
create_app_from_prompt(
brief="Invoice tracker for small shops with CSV export",
template_git_url="https://github.com/example/nextjs-starter.git",
vm_name="of-tester-001",
name="Invoice Tracker",
visibility="public"
)Example response:
{
"app_id": "app-abc123",
"slug": "invoice-tracker",
"status": "live",
"url": "http://localhost:3000",
"public_url": "https://invoice-tracker.apps.openfactory.tech",
"origin": {
"type": "prompt",
"brief": "Invoice tracker for small shops with CSV export",
"template_git_url": "https://github.com/example/nextjs-starter.git",
"structure_id": null,
"task_id": null,
"iterations": [],
"created_at": "2026-06-26T14:32:10.123456+00:00"
},
"deploy_log": ["..."],
"error": null,
"next": "App deployed. iterate_app(app_id='app-abc123', instruction=…) to refine, walk_app(app_url='http://localhost:3000', mode='hybrid') to self-test."
}Validation errors come back as {"error": "..."} rather than throwing. The
brief is required, template_git_url must be an http(s) or git@ URL, and
vm_name is required.
iterate_app
Appends a timestamped instruction to App.origin.iterations. Only works on
apps created via create_app_from_prompt (origin type must be prompt).
Example call:
iterate_app(
app_id="app-abc123",
instruction="Make the header green and add a CSV export button"
)Example response:
{
"app_id": "app-abc123",
"iteration_count": 1,
"iteration": {
"ts": "2026-06-26T14:35:22.456789+00:00",
"instruction": "Make the header green and add a CSV export button"
},
"message": "Instruction recorded. M1 ships the iteration data model only; the agent loop that actually applies the instruction lands in M2."
}Common errors: "instruction is required", "App not found: <app_id>", and
"App <app_id> was not created from a prompt …" when called against an App
that was not created via the prompt path.
get_app_build_status
Cheap read of the App’s current state. No agent round-trip in M1.
Example call:
get_app_build_status(app_id="app-abc123")Example response:
{
"app_id": "app-abc123",
"slug": "invoice-tracker",
"status": "live",
"url": "http://localhost:3000",
"public_url": "https://invoice-tracker.apps.openfactory.tech",
"origin": {
"type": "prompt",
"brief": "Invoice tracker for small shops with CSV export",
"template_git_url": "https://github.com/example/nextjs-starter.git",
"iterations": [
{
"ts": "2026-06-26T14:35:22.456789+00:00",
"instruction": "Make the header green and add a CSV export button"
}
],
"created_at": "2026-06-26T14:32:10.123456+00:00"
},
"deploy_count": 2,
"last_deploy": {
"deploy_id": "d-abc123de",
"ref": "main",
"mode": "inplace",
"status": "live",
"started_at": "2026-06-26T14:35:50.000000+00:00",
"completed_at": "2026-06-26T14:35:55.000000+00:00",
"duration_s": 5,
"error": null
},
"env_version": 0,
"applied_env_version": 0
}env_version vs applied_env_version is the drift signal from
App Secrets & Environment: if they differ, the App has
pending env changes that have not been redeployed.
REST endpoints
All three are owner-scoped via the standard Authorization: Bearer <jwt> or
X-Guest-Id header. Wrong-owner and unknown app_id both return 404.
Validation errors return 422.
| Method | Path | Body / use |
|---|---|---|
POST | /api/apps/from-prompt | {brief, template_git_url, vm_name, name?, visibility?, branch?} |
POST | /api/apps/{app_id}/iterate | {instruction} |
GET | /api/apps/{app_id}/agent-status | Read brief, deploy history, iterations |
curl -X POST http://localhost:8000/api/apps/from-prompt \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"brief": "Guestbook app with entries and comments",
"template_git_url": "https://github.com/example/nextjs-starter.git",
"vm_name": "of-tester-001",
"visibility": "public"
}'curl -X POST http://localhost:8000/api/apps/app-abc123/iterate \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"instruction": "Add a delete button for guestbook entries"}'curl http://localhost:8000/api/apps/app-abc123/agent-status \
-H "Authorization: Bearer $JWT"End-to-end (M1)
1. create_app_from_prompt(brief="Guestbook with entries and comments",
template_git_url="https://github.com/example/nextjs-starter.git",
vm_name="of-tester-001")
→ { app_id: "app-xyz", url: "http://localhost:3000",
public_url: "https://guestbook.apps.openfactory.tech", origin: {...} }
2. get_app_build_status(app_id="app-xyz")
→ full origin (brief + iterations), deploy_count, last_deploy, env_version
3. iterate_app(app_id="app-xyz",
instruction="Add a delete button for guestbook entries")
→ iteration recorded on origin.iterations
4. deploy_app(app_id="app-xyz") # M1: manual redeploy after editing the template
→ new entry in deploy_history; same public_url
5. walk_app(app_url="https://guestbook.apps.openfactory.tech", mode="hybrid")
→ findings (console_error, dead_control, ...) and auto-emitted scenariosIn the next milestone, step 4 is the agent’s job: the iteration is applied via
a Direktor task-tune, the app is redeployed, and walk_app is invoked
automatically as the agent’s self-test step.
Notes
- The brief is capped at 2000 characters, the instruction at 1000. Keep
iterations focused; long history lives in the
iterationsarray, not in any single instruction. origin.typeis the discriminator. Apps created viacreate_app(no brief) haveorigin = Noneand cannot be iterated through this path; usedeploy_appon those instead.- Public URL only appears when
status == "live"andvisibility == "public". Private apps still get aurl, but it requires an OpenFactory login to reach. - VM must stay running. The deployed app serves from its tester VM. If the VM is stopped, the public URL stops resolving.
Related
- App Deployment — the
deploy_apppipeline the prompt path delegates to, and the home ofcreate_appfor non-prompt apps. - App UI Testing — drive the deployed URL with
reusable, self-hardening GUI scenarios. Also the surface the agent’s
eventual self-test loop will call into via
walk_app. - MCP Integration — set up the OpenFactory MCP server so these tools are reachable from your agent.