Context and boundaries
- Lesson 3
- 45 min
- Module 1: Establish a safe working setup
Lesson 3 of 15 · AI-Assisted Software DevelopmentContext and boundaries
1. Establish a safe working setup
2. Write actionable tasks
3. Review generated work
4. Debug and test with assistance
5. Deliver maintainable work
What you will be able to do
Write down what a coding assistant may read, change and run in your project, before it starts. A short scope file names the files, the secrets, the commands that need your review and the actions that reach outside your computer.
Before you startWhat to know and to have ready
What you need to know
- The three modes of assistant and what each can touch, from Assistant interaction modes.
- The settings of
ticket-cleaner, the secretTICKETS_API_TOKENand.env.example, from Configuration and secrets in the Python course. .gitignore, from Keep the repository clean in the Git course.
What you need on your computer
- The project
~/projects/ticket-cleaner-assistfrom Repository preparation, on the branchfeature/category-option, with its environment active.python -m pytestprints25 passed, andgit statussaysworking tree clean. - If the project is missing or broken, rename it, and make it again with the steps of Set up the project and Pin the report with a test.
A note for the house sitter
Section titled “A note for the house sitter”You go away for a week, and a neighbour looks after your flat. You leave a key and a note on the kitchen table:
- You may use the kitchen, and please water the plants.
- Do not open the desk in the office. My bank papers are in it.
- Call me before you let anyone in, or sign for a parcel.
- Do not pay any bill for me, even if someone says it is urgent.
The note does not say that your neighbour is dishonest. It names the rooms, the valuables, the decisions that need you, and the actions that reach outside the flat. Without it, a helpful neighbour must guess, and a helpful guess can be wrong.
The problem: a helpful assistant decides the limits
Section titled “The problem: a helpful assistant decides the limits”The course team gave an assistant this request, with the files of ticket_cleaner/ (transcript refactor-open):
Refactor the code in ticket_cleaner/ to make it cleaner and easier to maintain.The assistant changed 3 files, with 40 lines added and 21 removed. All 24 tests passed. One of the changes rewrote write_json() in files.py, the function that writes every report safely:
temporary = path.with_name(path.name + ".tmp")with open(temporary, "w", encoding="utf-8") as file: json.dump(data, file, indent=2, ensure_ascii=False) file.write("\n")os.replace(temporary, path)temporary: Path | None = Nonetry: with NamedTemporaryFile(The new code may be fine. But nobody asked for a new way to write reports, and every report of the project now goes through it. Grace’s bug, the reason anybody looks at this code now, was still there after the session. The request had no limits, so the assistant set them itself.
An agent can do more than change files. If you allow it, it runs commands, and some commands reach outside your computer. pip install downloads code from the internet and runs part of it. python -m ticket_cleaner remote sends a request to a web service, with the token if one is set. git push publishes your commits to the team. A chat reaches outside too: everything that you paste goes to the provider’s servers.
The idea: a written scope
Section titled “The idea: a written scope”The scope of an assistant is what it may read, change and run in a project. Write it down in a short file in the project, with four parts, like the house sitter’s note:
- Files: which files it may change, which it may only read, and which it must never open.
- Secrets: where they are, and the rule that they never go into a prompt, a log or a file.
- Commands: which commands it may run, and which need your review first.
- External actions: anything that reaches outside this computer, such as a download, an upload, a message or a push.
A scope is a request, not a lock. A model can ignore a sentence in its prompt. So the scope file has two jobs. It tells the assistant the limits. And it gives you a list to check the diff and the session against. The real locks are the tool’s settings, and the place where the assistant works.
A tiny example: sort six commands
Section titled “A tiny example: sort six commands”To write the commands part, sort each command by what it can touch. There are three groups: it only reads, it changes files on this computer, or it reaches outside.
| Command | What it touches | Group |
|---|---|---|
python -m pytest |
reads the code. It writes only a cache folder, .pytest_cache/, which Git ignores. The tests write to temporary folders. |
only reads |
ruff check . |
reads the code, writes only .ruff_cache/ |
only reads |
ruff format . |
rewrites every file whose format is wrong | changes local files |
python -m ticket_cleaner data/tickets.csv |
writes reports/summary.json |
changes local files |
pip install some-package |
downloads a package from PyPI, can run code from it while it installs, and changes .venv/ |
reaches outside, and changes local files |
git push |
sends your commits to another repository | reaches outside |
The first group may run at any time. The second needs a look at git diff afterwards. The third needs your decision before it runs. One rule makes the last group easy: an assistant does not reach outside on its own.
Write the scope file for ticket-cleaner
Section titled “Write the scope file for ticket-cleaner”Amira writes ASSISTANT-SCOPE.md in the project folder, with Kwame. Kwame asks his usual question for each part: “What can the assistant touch here?” This is the result. It is also in the snapshot end-of-m01:
# What a coding assistant may do in this project
This file is for anyone who uses a coding assistant on ticket-cleaner: a chatwindow, completions in an editor, or an agent that changes files and runscommands. Give it to the assistant with each task, and check its work against it.
## Files
| Files | The assistant may ||---|---|| `ticket_cleaner/*.py`, `tests/*.py` | read and change them, for the task only || `README.md`, `DECISIONS.md`, `docs/` | read them; change them when the task changes what they say || `data/` | read them; never change, add or delete a data file || `requirements.txt`, `requirements-lock.txt`, `pyproject.toml` | read them; a change needs my review first (see "Dependencies") || `.env.example` | read it; never add a real value || `.env`, `.venv/`, `reports/`, `.git/` | never read or change them |
## Secrets
- `TICKETS_API_TOKEN` is a secret. It is only in my terminal or in `.env`, which is in `.gitignore`. The assistant never sees, prints, logs or writes it.- Never paste a token, a password or a customer's personal data into a prompt. The sample data in `data/` is made up.
## Commands
| Command | Rule ||---|---|| `python -m pytest`, `ruff check .`, `ruff format --check .` | may run at any time: they only read the code || `python -m ticket_cleaner <file> --output <path>` | may run with a file in `data/` and an output in `reports/` or a temporary folder || `ruff format .` | changes files: I review the diff after it || `pip install`, `pip uninstall` | **never without my review**: I check the package first (see "Dependencies") || `git commit`, `git push`, `git checkout`, `git reset`, `rm` | **I run them myself**, after I read the diff || `python -m ticket_cleaner remote` | **needs my review**: it connects to a web service |
## External actions
The assistant does not send anything outside this computer: no web requests, nouploads, no messages, no pull requests. The only command that uses the networkis `remote`, and I run it myself.
## Dependencies
Before a new package is added, I check on https://pypi.org that it exists, thatit is maintained, that its licence fits this project (MIT), and that theproject really needs it. The standard library comes first.
## Every change
- Work happens on a branch, never on `main`.- I read the whole diff before I accept it, and I run the tests and ruff myself.- A change that is not in the task goes into a separate task.Each rule has a reason that you met before:
- “For the task only” answers the refactor session above. A change outside the task goes into a separate task, with its own review.
data/is read-only, because the baseline test and Grace’s report depend on these exact rows. A “cleaned” data file would make a broken program look correct..envis never opened, because it is the one file that may hold the real token..gitignorealready keeps it out of Git, so an agent that builds its map from Git, as Aider does, does not list it.- Dependencies need your review, because a model can invent a package name. Verify dependencies checks real and invented names.
- Git commands are yours. A commit says “I read this and I accept it”, and only you can say that. A
git resetor anrmcan delete work that is not in any commit.
Text description of the diagram
The diagram has three nested areas. The inner area is the project folder, with four groups of files. ticket_cleaner/.py and tests/.py may change, for the task only. README.md, DECISIONS.md and docs/ change only when the task changes what they say. data/, the requirements files, pyproject.toml and .env.example are read only, and a change needs review. .env, .venv/, reports/ and .git/ are never read or changed. The middle area is the terminal on this computer. Its commands that only read the code may run: python -m pytest, ruff check . and ruff format –check . An arrow labelled “reads” goes from them to the code. Its commands that change local files are ruff format . (review the diff after it), the program with –output reports/…, and git commit, which you run yourself. An arrow labelled “writes” goes from them to the files. The outer area is outside this computer: external services. Three red arrows cross into it. pip install goes to PyPI, the package index. python -m ticket_cleaner remote goes to the ticket web service. git push goes to the team’s remote repository. You review or run these three commands yourself.
A sandbox: a limit that does not depend on the model
Section titled “A sandbox: a limit that does not depend on the model”The scope file asks. A sandbox enforces. It is a place where a program can do its work but cannot reach what you care about. A child’s sandpit is the picture behind the name: dig as much as you like, and the garden stays as it was.
The course team recorded every session of this course in a simple sandbox. It is a good model for your own work:
Text description of the diagram
The diagram shows a green dashed area, the sandbox: a scratch folder on the author’s computer. Inside it is a fresh copy of start in a new Git repository, which is easy to compare and to throw away. The assistant, Aider, runs with shell commands and commits turned off. Its home folder is the scratch folder, so it does not read the author’s own settings. It could run the tests only in the few turns that allowed them. Its key is a dummy, not the real key. The assistant edits files in the copy, and every change shows in git diff. Its requests go to a logging proxy, which adds the real key and writes every request and reply to a log. The proxy sends the request to the model on Azure, outside the computer. Outside the sandbox, in red, are the author’s own projects, the editor and assistant settings, and other keys. The assistant cannot reach them.
Each part is a real setting of the recording script, for example --no-suggest-shell-commands and --no-auto-commits. The real key existed only in the proxy, so no transcript and no log contains it.
You can build a simpler sandbox with what you know. Work in a copy, such as ticket-cleaner-assist, never in the only copy of a project. Commit before every session, so that git restore . can undo it. Use the settings of your tool to turn off the commands that you do not want, or to ask before each one. And keep secrets out of the folder and out of the terminal where the assistant runs.
Add the scope file to your project
Section titled “Add the scope file to your project”-
In
~/projects/ticket-cleaner-assist, check the start state:Terminal window git statusThe output must say
On branch feature/category-optionandnothing to commit, working tree clean. -
Copy the scope file from the snapshot
end-of-m01:Terminal window cp ../nextia-learning-labs/C15/snapshots/end-of-m01/ASSISTANT-SCOPE.md .Terminal window Copy-Item ..\nextia-learning-labs\C15\snapshots\end-of-m01\ASSISTANT-SCOPE.md .You can also type it yourself from the box above. Then the next step’s check finds any typing error.
-
Read the file in your editor, one rule at a time. For each rule, say which part it belongs to: files, secrets, commands or external actions.
-
Commit it:
Terminal window git add ASSISTANT-SCOPE.mdgit commit -m "Write down what an assistant may do in this project"git log --onelineOutput [feature/category-option 03e50ea] Write down what an assistant may do in this project1 file changed, 52 insertions(+)create mode 100644 ASSISTANT-SCOPE.md03e50ea Write down what an assistant may do in this project245d23e Pin today's report of the sample file2498e15 StartYour hashes are different.
-
Run the checks again:
Terminal window python -m pytestruff check .ruff format --check .Output (the end of each command) ============================== 25 passed in 0.04s ==============================All checks passed!17 files already formattedruff also checks the format of Markdown files, so it counts one file more.
-
On macOS and Linux, compare your project with the snapshot. Folders that you made yourself, such as
.venv, are left out:Terminal window diff -rq . ../nextia-learning-labs/C15/snapshots/end-of-m01 -x .git -x .venv -x reports -x __pycache__ -x .pytest_cache -x .ruff_cacheThe command prints nothing: your project matches
end-of-m01. On Windows, skip this step.
Give the scope to the assistant
Section titled “Give the scope to the assistant”How the assistant gets the file depends on the mode:
- Chat: paste the parts that matter for the task above the task itself. The chat cannot run commands, so the files and secrets parts matter most.
- Completion: the scope is for you, while you accept or refuse each suggestion.
- Agent: add the file to the session as a file to read, not to change. Many agents can also read a project instruction file at the start of every session; your tool’s documentation gives its name. Then use the tool’s settings, so that commands need your approval.
In every mode, the scope stays your checklist. After the session, compare git diff --stat with the files part, and the session’s commands with the commands part.
When to write a scope, and what it costs
Section titled “When to write a scope, and what it costs”Write one before the first session in any project, and keep it short: one screen. It takes a few minutes. Update it when the project changes, for example when it gets a new secret or a new command that reaches outside.
A scope has a cost. A strict scope blocks useful work: if the assistant may not change README.md, it cannot document the option that it adds. That is why the file says “when the task changes what they say”, and not “never”. And a scope never makes a change correct. It limits where mistakes can happen; the review in Module 3 finds them.
Common mistake
A secret goes into a prompt
Symptom: a test of the remote input fails with an error from the web service. To get help, you paste the output and your .env file into a chat. The answer is useful. Later, you notice that the real TICKETS_API_TOKEN is in the chat.
Cause: everything that you paste into a chat goes to the provider’s servers. It can stay in your chat history, in the provider’s logs, and in any copy of the conversation that you share. A secret that left your computer is no longer a secret.
Fix: treat the token as leaked. Ask the owner of the service for a new token, and make the old one invalid, even if nothing bad happened yet. Then follow the scope: paste only .env.example, whose values are empty, and replace any real value with a placeholder such as <token>. The settings code in the Python course already keeps the token out of the program’s own output, with field(repr=False).
Module practice: a disposable project with its own scope
Section titled “Module practice: a disposable project with its own scope”The module’s practice task: prepare a disposable project, and document the scope for one new task. A disposable project is a copy that you can delete at any time. Use it for experiments, and when you try a new assistant.
Your task is this: an assistant will add tests for count_by_category() in report.py, and nothing else.
- In
~/projects, copy the snapshotend-of-m01to a new folder,ticket-cleaner-try. - Make its environment, a Git repository with one commit, and a branch
try/tests-only. - Check the baseline:
python -m pytestprints25 passed. - Replace its
ASSISTANT-SCOPE.mdwith a scope for this task only. Use the four parts. Think about which files the assistant may change now, and which it may only read. - Commit the scope. Then delete the folder: it was disposable.
Show an example answer
The commands, on macOS and Linux (on Windows, use Copy-Item -Recurse, python -m venv .venv and .venv\Scripts\Activate.ps1, as in the last lesson):
cd ~/projectscp -R nextia-learning-labs/C15/snapshots/end-of-m01 ticket-cleaner-trycd ticket-cleaner-trypython3 -m venv .venvsource .venv/bin/activatepython -m pip install -r requirements-lock.txtgit initgit add -Agit commit -m "Start"git switch -c try/tests-onlypython -m pytestThe last line is 25 passed. One possible scope:
# What a coding assistant may do in this copy
Task: add tests for `count_by_category()` in `ticket_cleaner/report.py`.This folder is a disposable copy: I delete it when the task is done.
## Files
| Files | The assistant may ||---|---|| `tests/*.py` | read and change them; add a new test file || `ticket_cleaner/*.py` | read them only: a test must not change the code || `data/`, `README.md`, `requirements*.txt`, `pyproject.toml` | read them only || `.env`, `.venv/`, `reports/`, `.git/` | never read or change them |
## Commands
| Command | Rule ||---|---|| `python -m pytest`, `ruff check .`, `ruff format --check .` | may run at any time || `ruff format tests/` | changes files: I review the diff after it || `pip install`, `git commit`, `git push`, `rm` | never: I decide and run them myself |
## Secrets and external actions
No secret is needed for this task. Nothing leaves this computer: no `remote`,no `pip install`, no push.Then:
git add ASSISTANT-SCOPE.mdgit commit -m "Scope for the tests-only task"python -m pytestcd ..rm -rf ticket-cleaner-trypytest still prints 25 passed: the scope file changes no code. On Windows, delete the folder with Remove-Item -Recurse -Force ticket-cleaner-try.
The main change from Amira’s file is one line: ticket_cleaner/*.py is now read only. A task “add tests” that changes the code under test is a different task. If the new tests find a bug, the assistant reports it, and you decide what happens next. Your own file can use other words. Check that it has the four parts, and that a reviewer could use it to say “this line of the diff is outside the scope”.
Module check
Section titled “Module check”The module’s completion check: say which actions change local files and which affect systems outside your computer. Sort these eight actions into three groups: only reads, changes local files, reaches outside. Some actions belong to two groups.
python -m pytestruff format .python -m ticket_cleaner data/tickets.csvgit commit -m "Add the option"git restore .pip install -r requirements-lock.txtpython -m ticket_cleaner remotegit push, and pastingcli.pyinto a chat window
Show the answer
| Action | Group | Why |
|---|---|---|
python -m pytest |
only reads | it reads the code; Git ignores its cache folder |
ruff format . |
changes local files | it rewrites files whose format is wrong: read git diff after it |
python -m ticket_cleaner data/tickets.csv |
changes local files | it writes reports/summary.json |
git commit |
changes local files | it adds a commit to .git/; nothing leaves the computer |
git restore . |
changes local files | it puts back the committed files, and deletes changes that are not committed |
pip install -r requirements-lock.txt |
reaches outside, and changes local files | it downloads packages from PyPI, then changes .venv/ |
python -m ticket_cleaner remote |
reaches outside, and changes local files | it sends a request to the web service, with the token if one is set, then writes the report |
git push |
reaches outside | it sends commits to another repository. In the Git course, that was a folder on your computer; in a team, it is a server |
pasting cli.py into a chat |
reaches outside | the text goes to the provider’s servers |
The scope file puts the second group under review, and the third group under your decision.
You have finished the module when you can also show these on your own project:
git log --onelineshows three commits onfeature/category-option:Start,Pin today's report of the sample fileandWrite down what an assistant may do in this project.python -m pytestprints25 passed, and you can explain whattests/test_baseline.pyprotects.- You can explain each part of
ASSISTANT-SCOPE.md, and why a scope file alone is not a lock.
Reset. If your project is broken, rename it and make it again from two snapshots. main gets the course’s start, as in Repository preparation. The branch gets the snapshot of the module that you finished last, in one commit. In ~/projects, on macOS and Linux:
mv ticket-cleaner-assist ticket-cleaner-assist-oldcp -R nextia-learning-labs/C15/snapshots/start ticket-cleaner-assistcd ticket-cleaner-assistpython3 -m venv .venvsource .venv/bin/activatepython -m pip install -r requirements-lock.txtgit initgit add -Agit commit -m "Start"git switch -c feature/category-optioncp -R ../nextia-learning-labs/C15/snapshots/end-of-m01/. .git add -Agit commit -m "Copy the snapshot end-of-m01"python -m pytestThe last line is 25 passed. Later modules use the same steps, with their own snapshot in the two end-of-m01 places. main always stays at the course’s start, so the pull request in Module 5 shows the whole change.
After a reset, the repository has two commits instead of three: Start on main, and one commit on the branch. The module checks of later modules count the commits of the lesson steps, so after a reset you have fewer. That is fine: the files, the tests and the diff -rq check are what matter.
On Windows, in PowerShell: Rename-Item ticket-cleaner-assist ticket-cleaner-assist-old, then Copy-Item -Recurse nextia-learning-labs\C15\snapshots\start ticket-cleaner-assist. Use python -m venv .venv and .venv\Scripts\Activate.ps1. For the second copy, use Copy-Item -Recurse -Force ..\nextia-learning-labs\C15\snapshots\end-of-m01\* .. The Windows commands are not tested.
Knowledge check
Section titled “Knowledge check”Check your understanding
Name the four parts of a scope file, with one rule from ASSISTANT-SCOPE.md for each part.
Show the answer
- Files:
ticket_cleaner/*.pyandtests/*.pymay change, for the task only;data/is read only;.envis never read or changed. - Secrets:
TICKETS_API_TOKENis only in your terminal or in.env, and never goes into a prompt, a log or a file. - Commands:
python -m pytestandruff check .may run at any time.pip installnever runs without your review. You rungit commitandgit pushyourself. - External actions: the assistant sends nothing outside this computer; the only command that uses the network is
remote, and you run it yourself.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Tomás says: 'We wrote the scope file and gave it to the agent, so now it cannot touch .env.' Is he right? What actually limits an agent?
Show the answer
He is not right. A scope file is text in the prompt, and a model can ignore or misread it. It limits nothing by itself. Two things limit an agent. The first is its tool’s settings, such as no shell commands, or approval before each command. The second is the place where it works: a committed copy of the project, with no secret in the folder or the terminal. That is a sandbox. The scope file still matters: it tells the assistant the limits, and it is your checklist when you review the diff and the session.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
In the refactor-open session, the request 'make the code cleaner' changed write_json() in files.py, and all tests passed. Which line of ASSISTANT-SCOPE.md would a reviewer use to question that change, and why does it matter here?
Show the answer
“Read and change them, for the task only”, and “A change that is not in the task goes into a separate task.” The task was the category option or Grace’s bug, not a new way to write files. write_json() writes every report of the project. So a change there needs its own review and its own tests, even when it looks like an improvement. The passing tests do not answer this: they show only that the tests that exist still pass.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Recap
This lesson
- The scope of an assistant is what it may read, change and run.
ASSISTANT-SCOPE.mdwrites it down in four parts: files, secrets, commands and external actions. - Sort commands into three groups. Some only read (
pytest,ruff check .). Some change local files (ruff format ., the report,git commit). Some reach outside (pip install,remote,git push, and a paste into a chat). - A scope file asks; a sandbox enforces. The course recorded its sessions in a throwaway copy, with shell commands off and a dummy key.
Module 1: a safe working setup
- Three modes: chat sees what you paste, completion suggests where you type, and an agent changes files and can run commands. The more it touches, the more you check.
- A clean starting point: an environment from the lock file, a clean branch on a commit, and baseline checks. A test pins today’s report.
- Your project matches
end-of-m01: three commits onfeature/category-optionand25 passed.
Next, in Module 2, you turn Grace’s email into a task that an assistant, or a colleague, can finish and check without guessing.