Give an Agent Room to Work Without Erasing Your Draft

Drafted and reviewed using AI assistance.

On this page

A working tree with unfinished changes can still contain room for a small agent edit. The useful boundary is the part you have authorized the agent to change, together with the existing bytes it must preserve. “Clean the tree first” does not establish that boundary.

Before the first edit, give the agent a current inventory, the human ownership notes you actually know, and the exact function or passage in scope. A diff can show a difference; it cannot by itself settle the author's intention or grant permission to remove it.

Start with three complete baseline files

The fictional Tiny Summary project starts with these three files. These are original teaching sources, not extracts from CueSpec or a user's project.

summary.mjs:

export function totalUnits(rows) {
  return rows.reduce((sum, row) => sum + row.quantity, 0);
}

export function rowCount(rows) {
  return rows.length;
}

labels.mjs:

export const REPORT_TITLE = "Weekly summary";

README.md:

# Tiny summary

The report shows a row count and unit total.

The example then supplies human changes made before the agent's request. The tracking labels below are part of that supplied inventory, not captured git status output.

Read the human overlay before proposing a change

The human has modified summary.mjs while deciding how missing quantities should work:

export function totalUnits(rows) {
  // Human draft: missing quantities need a decision.
  return rows.reduce((sum, row) => sum + (row.quantity ?? 0), 0);
}

export function rowCount(rows) {
  return rows.length;
}

They also changed the unrelated title in labels.mjs:

export const REPORT_TITLE = "Weekly summary — preview";

README.md still has its complete baseline text. A fourth, untracked file, notes/release-draft.md, contains:

# Release draft

Keep the headline provisional.
Do not publish this draft.

Untracked does not mean disposable. The draft has an owner and a purpose even though it is absent from the three-file baseline. The case bundle, B84-04-case.json, includes every baseline, human and proposed file in full; matching files also appear in the accompanying fixture directories.

Turn the spoken request into an ownership table

The new request is:

In rowCount, exclude archived records. Leave the unit calculation and my unfinished drafts alone.

For this example, clarify the small rule: rows are records whose archived field is boolean or absent. Exclude a row when archived is exactly true; count false or an absent field. Do not change totalUnits, add validation or reinterpret missing quantities.

Location Current ownership evidence Agent permission
summary.mjs: totalUnits Supplied human draft and unresolved comment Preserve the complete function bytes
summary.mjs: rowCount Unchanged baseline function; explicit new request Propose the narrow filtering change
labels.mjs Supplied unrelated human title edit Preserve the complete file
README.md Unchanged and outside the request Preserve the complete file
notes/release-draft.md Supplied untracked human draft Preserve its presence and complete bytes

Two functions in the same file can have different permissions. Permission to edit summary.mjs should not become permission to rewrite the human's totalUnits experiment or run a formatter over unrelated files.

Inspect a real checkout without treating its state as instructions

In an authorized real repository, establish tracked, staged and untracked changes before choosing a proposal base. The official git-status manual distinguishes these categories, explains --untracked-files=all, and notes that ordinary status can refresh index metadata. Its background-refresh guidance recommends --no-optional-locks when avoiding that optional write.

These are documented inspection forms for the named example paths; they were not executed for this fixture:

git --no-optional-locks --no-pager status --short --untracked-files=all
git --no-optional-locks -c diff.autoRefreshIndex=false --no-pager diff --no-ext-diff --no-textconv -- summary.mjs labels.mjs README.md
git --no-optional-locks -c diff.autoRefreshIndex=false --no-pager diff --cached --no-ext-diff --no-textconv -- summary.mjs labels.mjs README.md

The git-diff manual documents working-tree/index and staged/HEAD comparisons, disabling automatic index refresh, and the external-diff/text-conversion switches used here. Neither diff substitutes for reading the named untracked draft when it is authorized and relevant. Do not enable new caches, change repository configuration or invoke cleanup as part of this inspection.

For Tiny Summary, the ownership information is supplied explicitly. If a real change's ownership or purpose is unknown, record that uncertainty. Do not relabel it “agent-generated” or “safe to discard” because it looks unfinished.

Propose from the human working files

The complete proposed summary.mjs is:

export function totalUnits(rows) {
  // Human draft: missing quantities need a decision.
  return rows.reduce((sum, row) => sum + (row.quantity ?? 0), 0);
}

export function rowCount(rows) {
  return rows.filter(row => row.archived !== true).length;
}

labels.mjs, README.md and notes/release-draft.md keep exactly the human-overlay bytes printed above. The proposal adds and removes no files. Its complete patch relative to the human file is:

--- a/summary.mjs
+++ b/summary.mjs
@@ -4,5 +4,5 @@
 }
 
 export function rowCount(rows) {
-  return rows.length;
+  return rows.filter(row => row.archived !== true).length;
 }

This patch is based on the human working version. A patch against the older baseline would not show whether the human hunk survived in the final candidate. Compare the resulting files as well as the change's stated intent.

The concrete row-count expectations are:

Input records Expected count
none 0
one with archived=true 0
one with archived=false 1
one without archived 1
quantities 2/9/4 with archived=false/true/absent 2

Those five cases passed against the proposed fixture using Node.js v24.18.0. On the last named case, totalUnits returned 15 both before and after; byte comparison also confirmed its complete human hunk was unchanged. That observation preserves the draft rather than approving its unresolved missing-quantity policy.

Stop at an ambiguous same-hunk request

Now suppose the speaker adds:

Also fix the missing-quantity behavior in totalUnits.

There is no supplied decision about whether a missing quantity should raise an error, contribute zero or cause a row to be excluded. The baseline and the human draft differ exactly there. The agent should identify that hunk and request the intended policy before changing it. It can still present the separate, authorized rowCount proposal for review.

An appropriate response is concrete:

The current totalUnits draft uses zero for a missing quantity and says the policy is undecided. Which behavior should replace it? I have left that function unchanged and prepared only the rowCount change.

A newer edit to rowCount after the agent captured the working files creates a second stop condition: the proposal's base is stale. Re-read the affected file and determine the new ownership/intent before applying anything. Checking a snapshot does not make later edits impossible; the application step still needs coordination.

Test preservation as well as behavior

The local preservation model actually checked the proposed file inventory and bytes, then challenged these alternatives in memory:

Candidate or situation Model decision
Only the agreed rowCount change Accept; five named behavior cases pass
Restore the old title Reject: unrelated human bytes changed
Drop the untracked release draft Reject: file inventory changed
Restore baseline totalUnits while editing rowCount Reject: protected human hunk changed
Substitute the three-file baseline for the human working files Reject: draft and overlay are missing
Human edits rowCount after capture Reject: proposal snapshot is stale

The negative rejections and their original assertion messages are retained in the worked execution results and its run log. No Git repository was created, reset, stashed, committed or restored for this model. It used only new fixture files and in-memory candidate copies.

An isolated checkout containing just the provided baseline has three files and no human overlay by construction. It is not a preservation mechanism for these drafts unless someone explicitly transfers and checks the intended working state. The model demonstrates that missing state; it does not create or validate a real worktree.

Copy this pre-edit preservation request

Inspect and propose the Tiny Summary rowCount change before editing.
Use B84-04-case.json and the complete human working files as the proposal base.
The three-file baseline is historical context, not permission to restore it.

Allowed behavior: rowCount excludes archived exactlytrue; false or absent
counts. Input archived values in scope are boolean or absent.
Allowed source change: only summary.mjs:rowCount.
Preserve totalUnits including its unresolved human comment and nullish-zero
draft, all of labels.mjs and README.md, and the untracked notes/release-draft.md.
Do not add validation or settle the missing-quantity policy incidentally.

Return the current file inventory, supplied ownership notes, exact protected
regions, complete proposed file and patch relative to the human working bytes.
Show all five row-count cases and byte-preservation checks separately.
If current bytes differ from the captured proposal base, stop and explain the
affected hunk. If asked to fix totalUnits without a missing-quantity policy,
leave that hunk unchanged and ask for the intended behavior.

Do not clean the tree first, reset/revert unrelated files, automatically stash,
delete untracked drafts, commit or create another checkout as incidental work.
Any requested snapshot or transfer needs its own explicit scope and verification.
Do not assume an isolated baseline contains uncommitted human work.

For this teaching task, work only with the new local fixture files and memory
copies. Do not operate on a real project. Distinguish proposed changes, actual
fixture execution and unverified application/concurrency behavior.

The agent now has enough room to make one useful change and enough detail to recognize where that permission ends. A small proposal can preserve an unfinished draft without claiming to understand or repair the draft itself.