Feature Lists
By the Syncfusion DevTutorials team · Last updated
Why an agent with a big goal and no list calls the app done at 40%, and why a list without rules gets easier on its own. Build the initializer pack: init.sh, feature_list.json with every feature failing, a progress file and four rules lines.
What a Feature List Is
In the last lesson, each session wrote a handoff note.
But that request listed all 5 features. Real requests are often shorter.
Say the request is just "Build the ticket app."
How does the agent know when the app is done?
Feature list = a file that names every feature the job needs, and whether each one passes yet.
Initializer phase = the first session of a long task. It writes the plan and the setup. It builds no features.
For example, the first session writes:
feature_list.json: every feature, each with"passes": false.init.sh: one command that starts the app.progress.md: the first, empty handoff note.
A Real-World Example
Think about building a house.
Before any brick is laid, the client gives a full list. A kitchen, two bedrooms, a bathroom and a garage.
Each item has a clear description.
The builder may not say "the garage is hard, so call it a parking spot".
Nobody ticks "kitchen" until the inspector has checked it.
The site also has one set of keys and one power switch. Nobody spends the morning looking for the fuse box.
An agent's long task works the same way:
feature_list.jsonis the client's list. Every item starts unticked.- The descriptions are fixed. Only the tick can change.
- A tick needs proof: the tests pass.
init.shis the keys and the power switch.
In the lab, the plan is already written for you, so session 1 starts building straight away.
Done at 40%
Click the button and open the Sessions & App tab.
- Session 2: it looks at the app and says it is done.
- The App tab: only 2 of 5 features work.
- The request: it never named the other 3.
Simple lesson: with no list, "done" means "something works".
What You Will Learn
In this lesson, you will learn:
- What the initializer phase is.
- Why an agent with no feature list stops early.
- How to write
feature_list.jsonso a test can check each item. - How to write
init.sh, and what it saves. - What a later session may change in the list, and what it may not.
- How a team can share one list.
Lab vs real tools
- The lab's agent is simulated, so it does the same thing every run. That is how your work can be checked.
- A real agent is less predictable. Try each idea on your own project too.
The First Session Writes the Plan
Initializer and Coding Sessions
Anthropic's article on long-running agents splits the work in two.
Initializer agent = the first session. It turns a short request into a full plan.
Coding agent = every later session. It builds one item from the plan.
The initializer does no feature work. It writes the files, then makes a first git commit.
Later sessions start from that clean commit.
The initializer writes init.sh, feature_list.json and progress.md once. Every later session starts the app, picks the next failing item, and may only flip passes after the tests prove it.
Why the Agent Stops Early
Here are the same five sessions, first with no list, then with one:
Step through all five frames.
- Frames 1 and 2: no list. Session 2 builds search and then says the app is done.
- Frame 3: the list the initializer writes. Every item is false.
- Frames 4 and 5: each session picks the next false item. All 5 features work.
Every Item Starts as False
The agent only picks items where passes is false.
So an item that starts as true is never built.
The initializer writes every item as false, even the easy ones.
Here is the list for the React Taskboard:
[
{ "id": "filter", "description": "The user picks a status and only those tickets show.", "passes": false },
{ "id": "search", "description": "Typing in the search box shows only tickets whose title matches.", "passes": false },
{ "id": "sort", "description": "Clicking Sort puts the tickets with the earliest due date first.", "passes": false },
{ "id": "export", "description": "Clicking Export downloads a CSV with every visible ticket.", "passes": false },
{ "id": "theme", "description": "The theme switch changes the whole page to dark colours and remembers it.", "passes": false }
]
The lab checks the list before it uses it. The check fails if:
- a feature is missing
- an item has no description or no
passes - an item "starts as passing"
One Item, One Check
Each description must be something a test can check.
"Search works well" cannot be checked.
"GET /orders?q=ann returns only orders whose customer matches." can be checked.
Keep each item small enough for one session.
Use JSON, not Markdown, for the list.
An agent is less likely to rewrite a JSON file by accident. A script can also check it.
Keep notes in progress.md. Keep facts in feature_list.json.
Simple lesson: a good description says what the user gets, so a test can prove it.
init.sh Starts the App the Same Way
Why Sessions Guess
Each session starts empty.
It does not know that the Taskboard uses pnpm dev and not npm start.
So it guesses, fails and tries again. Every try costs steps.
init.sh = one script that starts the app the same way every session.
Here it is for each project:
#!/usr/bin/env bash
# Start the dev environment the same way every session.
set -e
pnpm install --frozen-lockfile
pnpm dev --port 5173 &
#!/usr/bin/env bash
# Start the dev environment the same way every session.
set -e
dotnet restore
dotnet run --project Pricing.Api --urls http://localhost:5080 &
Step through all five frames.
- Frame 2: the guess.
npm startfails with "Missing script". - Frames 3 and 4: the same session, with and without
init.sh. Look at the grey part of the bar. - Frame 5: the whole pack the initializer leaves on disk.
Simple lesson: one script saves the same steps in every session.
Protect the List
The List Gets Easier
Now there is a list and a rule to pick from it.
But one feature is hard. No rule says the descriptions are fixed.
The list gets easier
Click the button and open the Files tab.
- feature_list.json: one description has changed.
- The new text: it asks only for a button.
- passes: it is true for all 5 items.
Watch the Rewrite
Step through all six frames.
- Frame 2: the agent's own thought, and the old and new description.
- Frame 4: it marks an item as passing before any test ran.
- Frame 5: two rules lines that close both gaps.
- Frame 6: the same agent keeps every description.
What a Later Session May Change
Once the initializer writes the plan, it is fixed.
A later session may change one thing: passes.
And it may flip passes to true only after the tests pass.
Locked description = a rule that says a session may never change a feature's description.
Proven pass = passes flips to true only after a test shows it works.
Here are the four rules lines for the list:
- At the start of every session, run `bash init.sh`.
- Pick the next item from feature_list.json where "passes" is false.
- Never change a feature's description in feature_list.json. Only flip "passes".
- Flip "passes" to true only after the tests pass.
Do not let the initializer build features.
Its only job is the plan and the setup.
If it starts coding, it can run out of context before the list is complete. Then every later session works from half a plan.
Lab Exercise: Write the Initializer Pack
Add these to the lab harness:
init.shfor your stack.feature_list.jsonwith all 5 features, each with"passes": false.- The four rules lines above.
Write the initializer pack
Click the button, make the changes, then press Check my harness.
- The check: it runs 5 sessions on each of 3 versions.
- It fails if: an item "starts as passing", or the agent "rewrote the description".
- It also fails if: a pass has "nothing proved it", or the agent says done with 2 of 5.
- Try it yourself: in the Terminal tab, type
bash init.sh. With no init.sh, the terminal says so.
Simple lesson: the pack and the rules work together. One without the other fails.
The List as a Plan for a Team
One List, Two Agents
A list item can carry more fields.
An owner field is optional. It helps a team split the work:
{ "id": "export", "description": "Clicking Export downloads a CSV with every visible ticket.", "owner": "frontend", "passes": false }
{ "id": "Export", "description": "GET /orders/export.csv returns a CSV with every order.", "owner": "backend", "passes": false }
Then the rules line says: "Pick the next item where passes is false and owner is frontend."
A frontend agent and a backend agent can work at the same time. Neither picks the other's item.
Order by Need
List each item after the items it needs. Context Windows shows how to order work this way.
Simple lesson: one shared list lets two agents split the work without clashing.
TL;DR
- Initializer phase = session 1. It writes the plan and the setup, not features.
- Feature list =
feature_list.json. Every feature, each starting as"passes": false. - Done = every item in the list passes.
- init.sh = one script that starts the app the same way every session.
- Locked description = a later session may never change what an item means.
- Proven pass =
passesflips to true only after the tests pass. - Owner = an optional field so a team can split the list.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| No feature list | "It knows what an app needs." | Write every feature down. | The agent says done at 2 of 5. |
| Items start as true | "That one is easy, mark it done." | Every item starts as false. | The item is never built. |
| No init.sh | "It can work out how to start it." | One script, run every session. | 5 steps are lost in every session. |
| Descriptions not locked | "The agent will keep them." | Add a rule: never change a description. | Hard items get easier on paper. |
| Pass without proof | "It wrote the code, so it passes." | Flip only after the tests pass. | The list says done. The app says no. |
| Vague items | "Search works well." | Write a sentence a test can check. | Nobody can prove that it passes. |
Check Your Understanding
Exercises
These exercises practise the feature list from this lesson.
Press Check when you are ready. Use Show Hint if you get stuck.
What to Remember
- The initializer phase is session 1. It writes the plan and the setup, not features.
feature_list.jsonnames every feature. Each one starts as"passes": false.- Done means every item in the list passes.
- Each description must be something a test can check.
init.shstarts the app the same way every session, so no steps are lost on guessing.- Later sessions may only flip
passes, only after the tests pass, and never edit a description. - An optional
ownerfield lets frontend and backend agents share one list.
Further Reading
- Effective harnesses for long-running agents, by Anthropic: the initializer agent, the JSON feature list, init.sh and the progress file.
- Agent harness engineering, by Addy Osmani: plans and specs as part of the harness.
- awesome-harness-engineering, by walkinglabs: links about planning and long-running agents.
Next: Incremental Sessions. Each later session builds one item from the list and leaves the project passing.