A coding agent will edit a Dockerfile the way a helpful intern edits a wiki: confidently, locally, and with no memory of the last time someone did the same thing three services over.
That is fine in a three-service repo. It is a slow accident in a landscape. Flisk makes the accident specific, because it already has two Dockerfiles, and flisk up builds only one of them.
If your agent cannot tell those two files apart, it should not be allowed to touch either.
The file that runs is not the file that looks like source
Read this from the project's own scope note, not from a launch tweet. For a generated service, flisk up builds services/<stack>/<name>/.autogenerated/Dockerfile.app.autogenerated. The build context is the project root. The stages start from local golden images, named like <project>-l1:latest, -l2, -l3-backend, -l4-backend. Those images are built on your machine. They are not published.
So that Dockerfile cannot be built on a laptop, or in a CI job, that has not built the golden layers first. The final stage runs as user bun. The local tag looks like shop_orders-api:dev. The production and development targets in that file are the same stage. It is a local image.
flisk resource also writes a plain Dockerfile next to service.json. It even has a comment saying so. flisk up does not build it. Its PORT, EXPOSE, and health check follow the port in service.json. It used to say 3000 while a backend ran on 4000, which is exactly the class of lie an agent will reintroduce if you let it "clean up" the file. The plain Dockerfile is a starting point for building outside Flisk. Whether it is fit to ship was not checked. Do not promote it to a production image because it sits in the nice part of the tree.
| File | Who may edit it | What actually runs it |
|---|---|---|
service.json |
You, or the agent, if the task is a topology change | Nothing. It is the input. |
.autogenerated/Dockerfile.app.autogenerated |
Nobody by hand |
flisk up, after local golden layers exist |
Plain Dockerfile beside service.json
|
Nobody, unless you are leaving Flisk's local loop | Not flisk up. A later, separate build, if you choose one |
| Your own Dockerfile on a bring-your-own service | You | Your image, if you pointed the resource at it |
Bring-your-own is the escape hatch, not a magic sync. If you want the image you ship to be the image that runs locally, use a bring-your-own resource with your own Dockerfile or --image. That shrinks the gap. It does not make local and production the same computer.
What the agent is good at, and what it will counterfeit
The agent is good at the thing inside the container: a handler, a query, a failing test, a prompt, a tool call.
It is bad at the thing around the container, because that thing has to be the same on every service:
- the port in the manifest, the route, and the health check
- the host npmrc versus the container npmrc
- the Vite config the dev server uses versus the one nginx serves
- the golden layer the local Dockerfile expects
- the Helm value that production actually reads
Flisk does not generate a chart. .tdk/.tdk-out/ and .autogenerated/ are local output. Copying them into Helm is how "works on my laptop" becomes a deploy. The port, the image, and the health path will exist twice, once in service.json and once in the chart. Nothing keeps them in sync. An agent that "helps" by updating only the file it can see will desync them in one commit.
A generated frontend makes the surface obvious. Under .autogenerated/ you get the local Dockerfile, an nginx config, host and container TypeScript configs, host and container package configs, Vite configs, an API client, and a short AGENTS.md. A Bun backend swaps in backend env, Vite, and Prisma files. Python, Go, and Rust still get a local Dockerfile, and skip the Bun-only pile. The exact set depends on appType, language, and features. The rule does not.
Edit service.json. Let the generator write the rest.
The warning file is a sign, not a policy
Each service's .autogenerated/AGENTS.md tells a coding agent that the directory is generated. On the free in-repo generator, that file is a short notice. It is not a substitute for a root policy. The downloaded agents-md implementation is the one their feature table marks as Premium. Do not assume a two-paragraph warning will stop a determined agent that was told to "make the container build."
Put the real rule where every task starts:
# AGENTS.md
## Source of truth
- Topology changes go in `service.json` or `.tdk/project.json`.
- Application code stays in the service, outside `.autogenerated/`.
- Production deploy config is Helm or the pipeline. Flisk does not generate it.
## Do not edit
- `.autogenerated/**`
- `.tdk/.tdk-out/**`
- `.autogenerated/.tdk-contract/**`
- The plain `Dockerfile` next to `service.json`, unless the task is explicitly "prepare a non-Flisk build" and a human asked for that file.
## Commands
- Inspect: `flisk doctor`
- Preview: `flisk up <stack> --dry-run`
- Run: `flisk up <stack>`
- Check the generated contract: `flisk config verify`
- After a manifest change, run `flisk up` again. Do not patch generated output to match the manifest.
## Stop and ask
- The task needs a production image, a cluster, or a secret from `.env`.
- A generated file looks wrong. Change the manifest or the generator, then regenerate.
- Local auth, `*.localhost`, or the shared Postgres database name would have to be hardcoded. Read those from the environment.
Use tdk in that file if that is the binary your install prints. The package and the docs still disagree on the name. The boundary does not.
This file is guidance, not a sandbox. An agent can ignore it. The drift check is the part that does not care about the agent's manners.
The gate is more useful than the lecture
flisk up compares generated service files with the snapshot in .autogenerated/.tdk-contract/ before Tilt starts. A hand edit blocks startup and names the file. --dry-run runs the same check and starts nothing. service.yaml is exempt from that comparison. It is a projection of service.json for Tilt. Edit the JSON.
flisk config verify checks the wider contract under .tdk/.tdk-out/, validates discovered manifests, and compares service files with their snapshots. flisk doctor is for machine readiness, not for "did an agent rewrite nginx."
The repair is boring, which is the point. Move the intended change into service.json, then run flisk up so the outputs are regenerated. flisk config regenerate replaces generated project files. A hand-edited service file keeps blocking until the edit is gone or the check is explicitly bypassed. Bypass is a human decision. It is not a step in an agent prompt.
The earlier post on derived artifacts covers that gate. This post is about the file the agent can see and still misunderstand.
When the agent should edit a Dockerfile
Sometimes the Dockerfile is the source. Say so, or the rule becomes superstition.
- Two or three services and a Compose file that already fits on one screen. Flisk is extra machinery. Let the agent edit Compose and the Dockerfile, then read the diff yourself.
- A bring-your-own service whose image is built by your CI. The Dockerfile next to that service is product infrastructure. Review it like code. Do not also let the agent "fix" the generated local Dockerfile so they match. They are not supposed to match line for line.
- A one-off debug of the golden-layer build, done by a human who is changing the generator. That edit belongs in the generator, then every service regenerates. It does not belong in one service's
.autogenerated/folder as a snowflake.
The test is ownership. If deleting the file and running flisk up would recreate it, the agent does not own it. If deleting it would break the image your pipeline ships, the agent may edit it, and a human reviews the diff before merge.
One loop that keeps the agent in its lane
flisk doctor
flisk up shop --dry-run
# agent edits application code, or service.json if the task is topology
flisk config verify
flisk up shop
If verify fails because the agent touched .autogenerated/, revert that path. Do not ask the agent to reconcile the generated Dockerfile with the plain Dockerfile. That reconciliation is the bug.
Local Flisk also leaks fake production if you let application code depend on it. A scaffolded backend runs with NODE_ENV=development, local JWT auth, one shared Postgres 16, and *.localhost routes. Those are environment facts. The moment they are string literals in source, the agent has coupled the product to a dev harness. The scope note's rule is the right one: run the real production image through a bring-your-own resource at least once, and keep Flisk-only hosts and database names out of the code.
The size of this rule
Flisk is still a small CLI. This rule is the large part. A coding agent can write the service. It cannot be the system of record for how that service is built, routed, and health-checked on a laptop, and it definitely cannot be the system of record for the image you ship.
Give it service.json, the application tree, and a root AGENTS.md that names the forbidden directories. Take the Dockerfiles away.
Top comments (1)
yes!