Good to see the control plane in place. In the previous lesson, you configured a dedicated edit worktree, persistent agent rules, a fail-closed preflight check, and a stage map. Now you will run the complete bounded loop: start from the untouched V1 series and upstream feedback, select one evidence-ready repair task, make a narrow change, run static gates, use Sashiko, optionally test hardware, and emit a reviewable patch output.
This is not a “tell the agent to fix the driver” loop. It is a controlled state machine. The agent may advance only when the current stage has produced its required evidence. A failed check, unclear manual statement, conflicting reviewer request, or unapproved lab action is a valid outcome: it means stop with evidence, not “try harder until green.”
1. The loop has one safe direction, with explicit exits
Use one run identifier for every command, log, artifact, and report generated during an attempt:
RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)-v2"
export RUN_ID
mkdir -p "logs/$RUN_ID" "output/$RUN_ID"
A complete run follows this structure:
Three rules make this more reliable than an unconstrained agent session:
-
One task at a time. A task may address one review comment, or a tightly linked set of comments with the same cause. Do not combine unrelated cleanup, binding changes, and DSA behavior repairs because they happen to be nearby.
-
Every code change returns to the gates. A runtime-driven fix is still a code change. It must return through builds, kernel and Device Tree checks, and Sashiko before it can enter the patch output.
-
A stop is not a failure of the workflow. It is the workflow protecting you from unsupported inference or accidental repository and lab damage.
The standard AI-development sequence is useful here, but narrowed for kernel work: explore, plan, make a small change, then review it. The loop adds the things a generic coding workflow lacks: preserved V1 and review provenance, hardware-evidence rules, kernel-specific checks, and human gates.
The Explore → Plan → Code → Commit workflow in Claude Code
Watch “The Explore → Plan → Code → Commit workflow in Claude Code” from the Claude channel. Although the interface shown is Claude Code, the explore-plan-code-review discipline applies equally when using Cursor.
Watch the core method for the reason planning comes before edits. Then watch read only planning, treating this as the equivalent of a Cursor read-only investigation or a Claude Code plan-mode pass. Finish with the review step; in this course, Sashiko and the required kernel checks make that step concrete.
For this driver effort, “commit” does not mean the agent independently creates a signed-off patch and submits it. The agent can prepare a draft commit message and patch output. A human remains responsible for review, authorship, sign-off, and submission.
2. Create durable state before asking the agent to repair anything
Add the following local values to the package configuration. These are paths and commands approved by you, not values the agent may invent or modify.
# Additional full-loop settings in .loop/local.env
# Prepared by a human: V1 applied on the exact base used for that series.
# The agent may inspect and build it, but may not reset, rebase, or modify it.
V1_BASELINE_TREE="$HOME/src/linux-dsa-v1-baseline"
# Approved executables maintained by the human/project, not by the agent.
CHECK_RUNNER="$LOOP_ROOT/scripts/run-required-checks.sh"
SASHIKO_REVIEW_SCRIPT="$LOOP_ROOT/scripts/review-patch-range.sh"
# Bounded repair behavior.
MAX_EDIT_PASSES_PER_TASK="2"
MAX_SASHIKO_PASSES_PER_TASK="3"
# Hardware runner is consulted only when HARDWARE_TESTS_ENABLED is 1.
HARDWARE_RUNNER="$LOOP_ROOT/scripts/run-hardware-tests.sh"
The baseline tree is separate from AGENT_EDIT_TREE for an important reason:
V1_BASELINE_TREEanswers: What did the original submission actually contain, and how did it behave or fail on its original base?AGENT_EDIT_TREEanswers: What should the next revision contain on the target submission base?
Never “restore V1” by applying old patches over the agent’s partially edited tree. That destroys the distinction between baseline evidence and new work.
Create a compact run-state file at the start of every run:
# logs/<RUN_ID>/state.yaml
run_id: "20250308T120000Z-v2"
stage: "preflight"
target_tree: "/absolute/path/to/linux-dsa-v2"
baseline_tree: "/absolute/path/to/linux-dsa-v1-baseline"
target_base_ref: "v6.12"
target_base_commit: ""
selected_task: ""
edit_passes: 0
static_checks: "not-run"
sashiko: "not-run"
hardware: "not-requested"
final_decision: "not-made"
Also keep four durable working records. They may begin small; their value is that they prevent the agent from blending evidence, guesses, and conclusions.
| Record | Purpose | Minimum contents |
|---|---|---|
work-list.yaml | Tracks every reviewer request | Original wording, thread URL or saved file, patch version, task ID, status, required proof |
evidence.md | Separates facts from inference | Manual location, target-kernel source location, V1 behavior, analogous-driver pattern, open questions |
decision-log.md | Explains why a task moved forward or stopped | Task ID, decision, evidence used, human approval where required |
findings.md | Resolves Sashiko and check findings | Finding text, classification, resolution, rerun log paths |
A task is ready only when all of the following are true:
- The original reviewer text and context are preserved.
- The review applies to the V1 patch or current target series; it is not clearly obsolete.
- The task has a bounded expected change.
- Required hardware facts are clear, or the task does not depend on hardware behavior.
- Any needed DSA or Device Tree rule can be checked against the target kernel source.
- The checks needed to validate the result are known.
- It has no unresolved dependency on another task.
If none of the comments are ready, the correct result is:
LOOP STATUS: STOPPED
STAGE: task selection
REASON: no evidence-ready review task
NEXT: use the stage map and prepare the missing evidence
Do not let the agent choose the “easiest-looking” comment merely to make progress.
3. Intake first: establish V1, feedback, and the single selected task
Run preflight again, then inspect rather than edit:
set -o pipefail
scripts/preflight.sh .loop/local.env 2>&1 \
| tee "logs/$RUN_ID/preflight.log"
test "${PIPESTATUS[0]}" -eq 0
git -C "$V1_BASELINE_TREE" status --short \
| tee "logs/$RUN_ID/v1-baseline-status.log"
git -C "$V1_BASELINE_TREE" log --oneline --decorate -n 20 \
| tee "logs/$RUN_ID/v1-baseline-history.log"
git -C "$AGENT_EDIT_TREE" rev-parse HEAD \
| tee "logs/$RUN_ID/target-start-commit.log"
At intake, the agent has five jobs:
- Inventory V1: patch count, commit subjects, changed files, declared base, and any missing material.
- Capture the baseline: build and check V1 in its prepared baseline tree; record warnings and failures exactly as observed.
- Inventory review material: reviewer names, thread sources, patch versions, and comments without rewriting their wording.
- Create tasks: turn each comment into a checkable item, but retain the original comment beside it.
- Select one task: choose the first evidence-ready task using the criteria above.
The V1 baseline is evidence, not a quality gate. It is normal for it to fail a current kernel build if the original patch was written against another version. What matters is that you know exactly which base, command, configuration, and output produced that result.
A task format that agents can safely operate on
- id: REV-014
source:
reviewer: "Reviewer Name"
patch_version: "v1"
thread_file: "input/review-threads/thread-03.mbox"
original_comment: >
Please explain why this callback returns success when the register
access fails.
scope:
likely_files:
- "drivers/net/dsa/vendor-switch.c"
change_goal: "Propagate the register-access failure when the DSA API permits it."
evidence_required:
- "Target-kernel callback return-value contract"
- "Manual behavior only if error handling changes hardware state"
validation_required:
- "Configured kernel build"
- "Configured static checks"
- "Sashiko review of final range"
status: "ready"
resolution: ""
This wording deliberately says “when the DSA API permits it.” The agent must not assume that every callback can return an error just because the underlying register access can fail. The target kernel’s callback signature and caller behavior are the rule.
Before editing a DSA driver, orient yourself around the framework’s registration and lifecycle requirements. The online documentation is a useful map, but the target kernel source tree remains authoritative when APIs differ.
Read the Linux kernel DSA architecture documentation to establish what the driver must provide during registration and setup. Use it to frame evidence questions, not as permission to copy behavior from an unrelated driver.
In the “Driver development” section, read the “Probing, registration and device lifetime” subsection. Begin at the registration lifecycle. Focus on the required dsa_switch members, the delayed setup of a multi-switch tree, and the mutual exclusion required between remove and shutdown. Then read the opening part of “Switch configuration,” beginning in the change_tag_protocol bullet at configuration callbacks. Note that setup must leave ports in a safe, isolated state and that per-port setup can be followed immediately by teardown.
For a V1 DSA driver, this yields a useful intake question set:
- Does V1 initialize
ds->dev,ds->num_ports,ds->ops, andds->privcorrectly before registration? - Does its
setup()behavior isolate ports and disable unavailable ones, if the hardware supports the required mechanism? - Is shutdown implemented at the bus-driver level, and is remove versus shutdown safe?
- Are callbacks implemented because the target DSA core needs them, or merely because a related driver has them?
- Do binding properties identify the correct CPU, user, and DSA ports under the standardized DSA binding?
If any answer needs an unclear register description or undocumented hardware state transition, stop at Module 3, Lesson 1. Current driver behavior and a similar switch driver are not proof.
4. Evidence, planning, and edits: keep the agent inside a narrow box
Once a task is selected, the agent performs a read-only evidence pass. Its plan must distinguish these categories:
| Category | Acceptable support | What it cannot prove |
|---|---|---|
| Hardware fact | Exact manual page, chapter, register description, or vendor clarification | DSA API behavior |
| Target-kernel rule | Target tree source, comments, documentation, or relevant kernel history | Hardware register meaning |
| V1 behavior | V1 code, baseline build, logs, or test observation | Intended hardware behavior |
| Analogy | Similar upstream driver or mailing-list discussion | That your device behaves identically |
| Open question | An explicit statement of what is missing | A basis for editing hardware-dependent code |
The agent’s implementation plan for a ready task should fit in a short record:
## Plan for REV-014
**Claim:** The callback currently discards a failed register access.
**Evidence**
- Target callback contract: <target-tree file and line range>
- V1 behavior: <file and line range>
- Hardware impact: none; no register programming sequence changes
**Planned files**
- drivers/net/dsa/vendor-switch.c
**Proposed change**
- Preserve the callback’s required behavior while reporting the register-access error
through the permitted return path.
**Validation**
- Required kernel build
- Required warning/static checks
- Sashiko on the patch range
**Risks and stop conditions**
- Stop if the target callback cannot report errors.
- Stop if the failure path requires hardware reset or state restoration not documented
by the manual.
Only after this plan exists may the agent edit. The edit should be small enough that you can explain its purpose in one commit subject line.
Before every edit pass, save a checkpoint:
git -C "$AGENT_EDIT_TREE" rev-parse HEAD \
> "logs/$RUN_ID/REV-014-before-head.txt"
git -C "$AGENT_EDIT_TREE" status --porcelain=v1 \
> "logs/$RUN_ID/REV-014-before-status.txt"
git -C "$AGENT_EDIT_TREE" diff --binary \
> "logs/$RUN_ID/REV-014-before.diff"
After editing, save the corresponding after files plus the complete diff. These are forensic records and recovery tools.
Rollback is a human decision
If the change is wrong, the agent must not run git reset --hard, discard untracked files, rebase, or delete a worktree. It must instead report:
- the before and after commit IDs;
- the complete current diff;
- the failed command and exit code;
- whether the change can be narrowed, needs more evidence, or should be abandoned.
A human then chooses one of three paths:
- Authorize another bounded repair pass.
- Manually restore the affected files after inspecting the saved checkpoint.
- Abandon the disposable edit worktree and recreate it from the known base.
This may feel slower than automatic cleanup, but it avoids losing a useful partial fix or silently erasing unrelated local work.
5. Static checks are a gate, not a suggestion
Your CHECK_RUNNER must be a human-maintained executable that runs the project’s chosen target-kernel commands and writes their commands, output, and exit status. The agent may invoke it but may not weaken it to make a patch pass.
A normal DSA-driver check set usually includes:
- the configured target architecture and kernel configuration;
- a build that compiles the driver and relevant networking code;
- warning-sensitive compilation, if your target supports it;
- kernel code checks selected for the project, such as
checkpatch.pl, sparse, or Coccinelle; - Device Tree binding schema validation for changed YAML;
- DTB and schema checks for the relevant board or example DTS.
The exact configuration is board- and kernel-version-specific. Do not substitute an arbitrary defconfig simply because it completes quickly. The configured checks must actually build the driver and validate the binding being changed.
A simple logged wrapper should record the exact tool invocation and result:
#!/usr/bin/env bash
# scripts/run-stage.sh
set -u -o pipefail
stage="${1:?stage name required}"
run_id="${2:?run ID required}"
shift 2
log_dir="logs/$run_id"
mkdir -p "$log_dir"
log_file="$log_dir/${stage}.log"
printf 'stage=%s\n' "$stage" | tee "$log_file"
printf 'command=' | tee -a "$log_file"
printf '%q ' "$@" | tee -a "$log_file"
printf '\n' | tee -a "$log_file"
"$@" 2>&1 | tee -a "$log_file"
rc="${PIPESTATUS[0]}"
printf 'exit_code=%s\n' "$rc" | tee -a "$log_file"
exit "$rc"
Use it for the required checks:
scripts/run-stage.sh static-checks "$RUN_ID" \
"$CHECK_RUNNER" "$RUN_ID"
Interpret results strictly:
- Pass: preserve the log and move to Sashiko.
- Known code failure with clear evidence: make one bounded repair pass, then rerun the entire required check set.
- Configuration or baseline problem: stop and use the stage map. Do not edit driver code merely to compensate for a wrong build configuration.
- Repeated failure beyond the configured limit: stop for human review with logs and diffs.
A Device Tree binding failure belongs to the binding stage even if the agent thinks it can “fix it quickly” by changing driver property parsing. The YAML schema, example DTS, driver behavior, and compatibility story must agree.
6. Treat Sashiko as an independent review gate
Sashiko is not a replacement for compiler checks or human judgment. It is a separate, probabilistic reviewer with useful coverage of architecture, execution flow, resources, locking, security, and hardware-facing code.
GitHub - sashiko-dev/sashiko: Agentic review of Linux Kernel code changes · GitHub
Read the Sashiko project documentation to understand why it belongs after static checks and why its findings must be classified rather than blindly accepted.
In the “Review Stages” section, read the staged review model. Pay particular attention to execution flow, resource management, locking, and the hardware engineer review; these are common fault lines in a DSA driver. Then, in “Usage,” read the “Local Review” material from local review behavior. The key operational point is that local review is intended for iterative development and uses a scratch clone rather than modifying your checkout.
Use the approved project wrapper rather than guessing Sashiko CLI options from memory:
scripts/run-stage.sh sashiko "$RUN_ID" \
"$SASHIKO_REVIEW_SCRIPT" \
"$TARGET_BASE_REF..HEAD" \
"logs/$RUN_ID"
Your wrapper should pin or record:
- the Sashiko version;
- the settings file used;
- the LLM provider and model configuration identifier, excluding secrets;
- the exact reviewed Git range;
- complete raw output;
- the run exit code.
Every finding gets one classification:
| Classification | Required response |
|---|---|
| Code fix | Add or update the task, make a bounded repair, rerun all required checks and Sashiko |
| Rejected concern | Record concrete evidence: target-tree code, documented API contract, manual citation, or demonstrable execution path |
| Hardware question | Stop; write a focused question with the exact manual ambiguity and affected code path |
| Human decision | Stop; explain the trade-off, affected users, compatibility impact, and options |
| Duplicate | Link it to the finding or review task that resolves the same cause |
“False positive” is not a sufficient resolution. The record must explain why it is not applicable to this patch.
The required terminal state is not “Sashiko found nothing.” It is:
Every Sashiko finding is resolved as a code fix, evidence-backed rejection,
hardware question, or explicit human decision.
If a Sashiko-driven edit occurs, return to the static checks before running Sashiko again. Never review an unbuilt change.
7. Hardware testing is opt-in and loops back to evidence
When HARDWARE_TESTS_ENABLED="0", record this explicitly:
hardware: skipped
reason: not enabled by a human
That is a valid path to patch output. Static review can proceed without lab access.
When a human sets HARDWARE_TESTS_ENABLED="1", the agent must still stop before touching the lab. Hardware testing requires a separate explicit approval that identifies:
- the DUT and serial-console endpoint;
- each traffic-generator host;
- the expected test topology;
- allowed reset and power actions;
- the tmux session naming and log directory;
- a timeout and recovery procedure;
- the person responsible if the DUT becomes unavailable.
At this point, use Module 7, Lesson 1 before running the hardware wrapper.
A hardware observation is not automatically an explanation. For example:
Observation: Port 3 fails to pass traffic after bridge join.
Not yet proven: The bridge-join callback programs the wrong forwarding mask.
The runtime result creates or updates an evidence task. If it leads to a code change, the loop returns through:
- evidence and a narrow plan;
- edit checkpoint;
- static checks;
- Sashiko;
- hardware retest only if the human keeps hardware testing enabled.
8. Run one bounded cycle with a tool-neutral agent prompt
Use this as your first full-loop request in Cursor or Claude Code. It is intentionally operational rather than conversational.
Operate one bounded DSA-driver repair cycle using the project contract.
Read:
- docs/agent-contract.md
- .loop/local.env
- docs/stage-map.md
- logs/<RUN_ID>/state.yaml
Rules:
- Work only in AGENT_EDIT_TREE.
- Treat V1_BASELINE_TREE, V1 patches, review archives, and manual files as
read-only evidence.
- Do not create or modify approval files, local configuration, check runners,
Sashiko settings, Git worktrees, branches, or remotes.
- Do not use destructive Git commands, network submission, privilege
escalation, or lab commands.
- Stop immediately on an evidence gap, manual ambiguity, reviewer conflict,
unapproved hardware action, or required human decision.
Procedure:
1. Run preflight and save the result.
2. Inventory V1 baseline and review material without editing code.
3. Update the work list while retaining exact original reviewer wording.
4. Select exactly one evidence-ready task. If none is ready, stop and report why.
5. Perform a read-only evidence pass and write a narrow implementation plan.
6. If evidence is sufficient, checkpoint the edit tree and make only the planned
change.
7. Run the configured static check runner and save all logs and exit codes.
8. If checks pass, run the configured Sashiko wrapper on the exact patch range.
9. Classify every finding. Make at most the configured number of repair passes.
10. Skip hardware unless it is enabled and separately approved.
11. Report current stage, selected task, changed files, evidence, commands,
log paths, unresolved issues, and the next required human decision.
Do not create commits or submit patches. Do not claim completion while any
reviewer task or Sashiko finding remains unresolved.
After the agent responds, inspect four things before allowing another cycle:
- Is the selected task genuinely ready, or did the agent silently assume hardware behavior?
- Does the diff match the stated plan and task scope?
- Did it run the approved checks rather than substituted commands?
- Are failures and unknowns reported as stops, rather than hidden behind a speculative fix?
9. Produce patch output only after the gates are clean
Once all selected tasks are resolved, required checks pass, Sashiko findings are closed, and optional hardware status is recorded, a human reviews the final diff and decides whether to create the commits.
After human-approved commits exist, produce patches from an explicit base, not from an ambiguous count of recent commits:
SERIES_BASE="$(git -C "$AGENT_EDIT_TREE" merge-base "$TARGET_BASE_REF" HEAD)"
printf '%s\n' "$SERIES_BASE" > "logs/$RUN_ID/series-base.txt"
mkdir -p "output/$RUN_ID/patches"
git -C "$AGENT_EDIT_TREE" format-patch \
--cover-letter \
--base="$SERIES_BASE" \
--output-directory "$LOOP_ROOT/output/$RUN_ID/patches" \
"$SERIES_BASE..HEAD"
Save alongside the patches:
output/<RUN_ID>/
├── patches/
├── work-list-final.yaml
├── evidence-final.md
├── sashiko-final.log
├── static-checks-final.log
├── hardware-result.md
├── reviewer-replies-draft.md
├── cover-letter-draft.md
└── final-decision.md
The final decision remains human-owned:
SUBMISSION DECISION: submit / do not submit / needs more evidence
DECIDED BY:
DATE:
KNOWN RISKS:
OPEN HARDWARE QUESTIONS:
Do not send mail, push a branch, or mark a comment resolved on someone else’s system as part of this loop.
You now have a runnable end-to-end loop with controlled progress: V1 and reviews become evidence-backed tasks; each edit is narrow and checkpointed; builds, kernel checks, Device Tree validation, and Sashiko are mandatory gates; hardware is optional and protected; and final patches are an output for human review, not an automatic submission.
The next lesson goes deeper into the place this loop most often stops first: restoring the V1 series on its exact original kernel version and recording a trustworthy baseline of builds, warnings, and validation results.
Can't find a good explanation? Sign up and we'll make it for you
Sign up