Two Real Problems in AI Programming: Multi-Project Task Management and Multi-User Collaboration Isolation
In multi-project, multi-developer AI programming practice, the continuity of task status and the isolation of personal configurations are key pain points affecting efficiency. This article proposes an engineering solution based on “sub-project Source of Truth” and “local rule isolation,” aiming to address cross-project task breakpoint management and team configuration pollution, while providing a replicable directory structure, read/write boundaries, and backup strategy.
Once an engineer starts using AI agents to write code frequently, the problem they quickly encounter isn’t “Can AI write functions?” but a more practical set of issues.
They maintain multiple projects simultaneously: some are for feature development, some for configuration migration, and others are just for occasional bug fixes. Every day when they open the AI agent, they have to re-explain: where is this project at, which tasks are complete, which are in progress, and which are just planned. Over time, task status gets scattered across various conversations, projects, and scattered documents. The AI can easily re-assign a completed task or overlook one that’s in progress but not yet finished.
Then a second problem emerges: some of these projects aren’t personal projects; they are shared, collaborative projects. Everyone uses AI agents differently. Some people like to create temporary drafts, then generate formal documents after review; others dislike this approach and have the AI generate detailed task files in one go. But these personal preferences shouldn’t be written into the team’s shared AGENT.md, nor should they pollute .gitignore or the project source code.
These two problems can be summarized as:
- Managing multiple projects for a single user.
- Collaboration isolation when a single project is managed by multiple users.
This article doesn’t discuss the usage of a specific tool, but rather an engineering solution that gradually formed during a real AI programming practice.
First, Look at the Overall Structure
This solution has two layers: the root project handles aggregation, handover, and backup; sub-projects hold the real task status and local personal rules.
flowchart LR
subgraph ROOT["Root Project / Aggregation & Backup"]
RP["planned.md
doing.md
completed.md"]
DOC["Handover Doc
new-project-pass-info-to-AGENT-MD.md"]
BK["Backup Directory
local-user-config-backups/"]
end
subgraph CHILD["Sub-project / Source of Truth"]
TS["Task Status
tasks-status/"]
AG["Team Rules
AGENT.md"]
LP["Personal Rules
SomeUser-agent.local.md"]
TMP["Temp Drafts
SomeUser-tmp/"]
EX["Local Ignore
.git/info/exclude"]
end
TS --> RP
DOC -. "Copy content to
sub-project agent" .-> AG
LP --> BK
EX --> BK
TMP -. "Not backed up by default" .-> BK
RP -. "Read-only aggregation" .-> TS
AG -. "Minimal hook" .-> LP
EX -. "Local ignore" .-> LP
EX -. "Local ignore" .-> TMPflowchart LR
subgraph ROOT["Root Project / Aggregation & Backup"]
RP["planned.md
doing.md
completed.md"]
DOC["Handover Doc
new-project-pass-info-to-AGENT-MD.md"]
BK["Backup Directory
local-user-config-backups/"]
end
subgraph CHILD["Sub-project / Source of Truth"]
TS["Task Status
tasks-status/"]
AG["Team Rules
AGENT.md"]
LP["Personal Rules
SomeUser-agent.local.md"]
TMP["Temp Drafts
SomeUser-tmp/"]
EX["Local Ignore
.git/info/exclude"]
end
TS --> RP
DOC -. "Copy content to
sub-project agent" .-> AG
LP --> BK
EX --> BK
TMP -. "Not backed up by default" .-> BK
RP -. "Read-only aggregation" .-> TS
AG -. "Minimal hook" .-> LP
EX -. "Local ignore" .-> LP
EX -. "Local ignore" .-> TMPflowchart LR
subgraph ROOT["Root Project / Aggregation & Backup"]
RP["planned.md
doing.md
completed.md"]
DOC["Handover Doc
new-project-pass-info-to-AGENT-MD.md"]
BK["Backup Directory
local-user-config-backups/"]
end
subgraph CHILD["Sub-project / Source of Truth"]
TS["Task Status
tasks-status/"]
AG["Team Rules
AGENT.md"]
LP["Personal Rules
SomeUser-agent.local.md"]
TMP["Temp Drafts
SomeUser-tmp/"]
EX["Local Ignore
.git/info/exclude"]
end
TS --> RP
DOC -. "Copy content to
sub-project agent" .-> AG
LP --> BK
EX --> BK
TMP -. "Not backed up by default" .-> BK
RP -. "Read-only aggregation" .-> TS
AG -. "Minimal hook" .-> LP
EX -. "Local ignore" .-> LP
EX -. "Local ignore" .-> TMPflowchart LR
subgraph ROOT["Root Project / Aggregation & Backup"]
RP["planned.md
doing.md
completed.md"]
DOC["Handover Doc
new-project-pass-info-to-AGENT-MD.md"]
BK["Backup Directory
local-user-config-backups/"]
end
subgraph CHILD["Sub-project / Source of Truth"]
TS["Task Status
tasks-status/"]
AG["Team Rules
AGENT.md"]
LP["Personal Rules
SomeUser-agent.local.md"]
TMP["Temp Drafts
SomeUser-tmp/"]
EX["Local Ignore
.git/info/exclude"]
end
TS --> RP
DOC -. "Copy content to
sub-project agent" .-> AG
LP --> BK
EX --> BK
TMP -. "Not backed up by default" .-> BK
RP -. "Read-only aggregation" .-> TS
AG -. "Minimal hook" .-> LP
EX -. "Local ignore" .-> LP
EX -. "Local ignore" .-> TMPThe key here isn’t the file names themselves, but the responsibility boundaries:
- The sub-project’s
tasks-status/is the source of truth for task status. - The root project’s
planned.md,doing.md,completed.mdare just aggregated views. - The team-shared
AGENT.mdonly contains a minimal hook. - Personal rules, temporary drafts, and local ignore files stay local to the individual.
- The root project can back up local configurations from an allowlist, but does not back up temporary directories by default.
Why Go Through All This Trouble?
Let’s first look at some common but problematic practices.
| Wrong Practice | Direct Consequence | Improved Process |
|---|---|---|
| Task status only exists in chat history | Status is lost or outdated when switching sessions, projects, or agents | Each sub-project maintains tasks-status/; the agent scans status files upon entering the project |
| Root project directly modifies sub-project task files | Root project becomes a cross-project high-privilege agent, increasing the scope of accidental modifications | Root project only reads sub-project task status, only updates its own summary files |
Everyone modifies the team AGENT.md | Personal preferences pollute team rules; everyone’s agent reads them | AGENT.md only retains a minimal hook; personal rules go into SomeUser-agent.local.md |
Writing personal files into the shared .gitignore | Personal workflow becomes team standard; collaboration boundaries blur | Use each sub-project’s own .git/info/exclude to ignore personal files |
| Backing up all ignored files | May include caches, keys, temporary drafts | Only allowlist backup of personal rules and .git/info/exclude |
There’s also a fundamental reason: The LLM’s context window is both expensive and easily polluted. If task status relies solely on chat history, it becomes longer and more chaotic; if personal rules are mixed into shared configurations, every collaborator’s agent will carry the same person’s preferences. This article doesn’t delve into RAG, tool isolation, or runtime isolation, but focuses on how to implement this through file and directory conventions.
Problem 1: One Person Managing Multiple Projects – How to Manage All Task Status?
The initial intuition was: can there be a “master project” dedicated to managing tasks for all sub-projects?
But a boundary issue quickly arises: if the master project can freely modify sub-project files, it becomes another high-privilege agent. It might modify sub-project documentation, configurations, or even source code in an attempt to “organize tasks.” This expands the risk.
So the first key constraint is:
The master project only reads sub-project task status; it does not directly modify any sub-project files.
Each sub-project maintains its own task status, and the master project is only responsible for reading and aggregating. This way, the sub-project remains the source of truth, and the master project is just an aggregated view.
Sub-projects expose a unified structure:
| |
Each task is an independent Markdown file placed in the corresponding status directory. For example:
| |
The master project reads these statuses and generates its own summary files:
| |
The summary files are not new task sources, just current views. Each summary entry retains the Source path, allowing readers to trace back to the original sub-project task document.
flowchart TD
A["Child Project A"] --> AS["tasks-status/*.md"]
B["Child Project B"] --> BS["tasks-status/*.md"]
C["Child Project C"] --> CS["tasks-status/*.md"]
AS --> R["Root Task Manager"]
BS --> R
CS --> R
R --> P["planned.md"]
R --> D["doing.md"]
R --> E["completed.md"]
R -. "read-only" .-> A
R -. "read-only" .-> B
R -. "read-only" .-> Cflowchart TD
A["Child Project A"] --> AS["tasks-status/*.md"]
B["Child Project B"] --> BS["tasks-status/*.md"]
C["Child Project C"] --> CS["tasks-status/*.md"]
AS --> R["Root Task Manager"]
BS --> R
CS --> R
R --> P["planned.md"]
R --> D["doing.md"]
R --> E["completed.md"]
R -. "read-only" .-> A
R -. "read-only" .-> B
R -. "read-only" .-> Cflowchart TD
A["Child Project A"] --> AS["tasks-status/*.md"]
B["Child Project B"] --> BS["tasks-status/*.md"]
C["Child Project C"] --> CS["tasks-status/*.md"]
AS --> R["Root Task Manager"]
BS --> R
CS --> R
R --> P["planned.md"]
R --> D["doing.md"]
R --> E["completed.md"]
R -. "read-only" .-> A
R -. "read-only" .-> B
R -. "read-only" .-> Cflowchart TD
A["Child Project A"] --> AS["tasks-status/*.md"]
B["Child Project B"] --> BS["tasks-status/*.md"]
C["Child Project C"] --> CS["tasks-status/*.md"]
AS --> R["Root Task Manager"]
BS --> R
CS --> R
R --> P["planned.md"]
R --> D["doing.md"]
R --> E["completed.md"]
R -. "read-only" .-> A
R -. "read-only" .-> B
R -. "read-only" .-> CThe focus here isn’t directory naming, but responsibility division:
- Sub-projects are responsible for maintaining real task status.
- The master project is responsible for aggregation and display.
- The master project cannot fix, move, or rename task files for sub-projects.
- If a sub-project lacks
tasks-status/, the master project can only report “not configured,” not create it for them.
This boundary makes the AI agent’s behavior more predictable.
Problem 1 Continued: Task Status Relies on Manual Maintenance – How to Ensure Accuracy?
The task status structure solves the “where to read” problem, but not the “is the status fresh” problem.
If a task is completed but the sub-project hasn’t moved it from doing/ to completed/, the status the master project sees will still be outdated. This problem cannot be fully solved by the master project because it is not the source of truth.
Therefore, discipline for status maintenance needs to be added for sub-project agents:
- Before scheduling a new task, scan
planned/,doing/,completed/. - At least check the task filenames in the three directories.
- If a filename seems relevant, or it’s impossible to determine if it’s a duplicate, read the specific task document.
- When status changes, immediately move the task file to the corresponding directory.
- When moving a task, synchronously rename the status segment in the filename.
- When a
doingtask undergoes significant changes, update the task document’s time, summary, current status, and next steps. - Before marking a task as
completed, confirm the document includes completion notes, completion time, remaining risks, or blocking items.
Task filenames also need strong constraints:
| |
Where <status> must match the directory it’s in:
| |
This design might seem verbose, but it solves a real problem for AI agents: agents rely heavily on clear, repetitive, scannable text protocols. The more stable the naming, the less status judgment relies on guesswork.
Problem 2: In Shared Projects, Personal AI Rules Must Not Pollute Team Configuration
The second problem comes from collaborative projects.
Shared projects usually have an AGENT.md to tell the AI agent how to work in that project. But if everyone writes their own preferences into it, the file quickly becomes a mix:
- Some people want Chinese conversations.
- Some people want English documentation.
- Some people want to keep temporary drafts.
- Some people have their own task maintenance habits.
- Some people use different local automations.
These are all real needs, but not necessarily team standards.
So the shared AGENT.md should remain minimal, containing only a hook:
| |
The actual personal rules go into a local file:
| |
Temporary drafts go into:
| |
These personal files are ignored via .git/info/exclude:
| |
The deliberate choice here is to use .git/info/exclude instead of the shared .gitignore. The reason is that these files are part of a personal workflow and shouldn’t necessarily become a team repository standard.
A more complete sub-project directory convention can be written as:
| |
Where:
AGENT.md: Team-shared rules, only containing project-level constraints and the personal rules hook.SomeUser-agent.local.md: The current user’s own AI working preferences.SomeUser-tmp/: The current user’s own temporary drafts and intermediate materials..git/info/exclude: The current user’s local ignore rules for this sub-project.tasks-status/: The source of truth for this sub-project’s own task status.
If multiple collaborators are in the same project, each person should have an independent namespace:
| |
user-a does not reuse user-b’s local files, and user-b does not overwrite user-a’s local files. The team-shared AGENT.md only needs to know: “if a user’s local file exists, read it as supplementary preferences; if not, ignore it.”
flowchart TD
G["Shared Project Repository"] --> A["AGENT.md"]
A --> H["Minimal hook only"]
H --> U1["user-a-agent.local.md"]
H --> U2["user-b-agent.local.md"]
U1 --> P1["user-a preferences"]
U2 --> P2["user-b preferences"]
E[".git/info/exclude"] --> I1["ignore user-a local files"]
E --> I2["ignore user-b local files"]
T1["user-a-tmp/"] --> C1["user-a drafts"]
T2["user-b-tmp/"] --> C2["user-b drafts"]
U1 -. "local-only" .-> G
U2 -. "local-only" .-> G
T1 -. "local-only" .-> G
T2 -. "local-only" .-> Gflowchart TD
G["Shared Project Repository"] --> A["AGENT.md"]
A --> H["Minimal hook only"]
H --> U1["user-a-agent.local.md"]
H --> U2["user-b-agent.local.md"]
U1 --> P1["user-a preferences"]
U2 --> P2["user-b preferences"]
E[".git/info/exclude"] --> I1["ignore user-a local files"]
E --> I2["ignore user-b local files"]
T1["user-a-tmp/"] --> C1["user-a drafts"]
T2["user-b-tmp/"] --> C2["user-b drafts"]
U1 -. "local-only" .-> G
U2 -. "local-only" .-> G
T1 -. "local-only" .-> G
T2 -. "local-only" .-> Gflowchart TD
G["Shared Project Repository"] --> A["AGENT.md"]
A --> H["Minimal hook only"]
H --> U1["user-a-agent.local.md"]
H --> U2["user-b-agent.local.md"]
U1 --> P1["user-a preferences"]
U2 --> P2["user-b preferences"]
E[".git/info/exclude"] --> I1["ignore user-a local files"]
E --> I2["ignore user-b local files"]
T1["user-a-tmp/"] --> C1["user-a drafts"]
T2["user-b-tmp/"] --> C2["user-b drafts"]
U1 -. "local-only" .-> G
U2 -. "local-only" .-> G
T1 -. "local-only" .-> G
T2 -. "local-only" .-> Gflowchart TD
G["Shared Project Repository"] --> A["AGENT.md"]
A --> H["Minimal hook only"]
H --> U1["user-a-agent.local.md"]
H --> U2["user-b-agent.local.md"]
U1 --> P1["user-a preferences"]
U2 --> P2["user-b preferences"]
E[".git/info/exclude"] --> I1["ignore user-a local files"]
E --> I2["ignore user-b local files"]
T1["user-a-tmp/"] --> C1["user-a drafts"]
T2["user-b-tmp/"] --> C2["user-b drafts"]
U1 -. "local-only" .-> G
U2 -. "local-only" .-> G
T1 -. "local-only" .-> G
T2 -. "local-only" .-> GThe effect of this is:
- The team-shared file only adds one minimal hook.
- Everyone can have their own AI working habits.
- Personal rules are not included in shared commits.
- Personal temporary files do not pollute formal documents.
- When no personal rules file exists, the project still runs on the original rules.
Project Initialization & New User Onboarding: Using SomeUser as a Placeholder
This addresses not just a single “new project onboarding” issue, but the naming problem during template initialization. There are typically two scenarios:
- The same user starts managing a new project.
- A new collaborator joins an existing project and starts using their own AI rules.
If this solution is to be used long-term, it cannot be tailored to just one person. Otherwise, in either scenario, you’ll end up copying a bunch of rules with an old name.
Therefore, the handover template uniformly uses SomeUser as a placeholder. Whether it’s project initialization or a new user joining an existing project, the agent should first ask the current user:
| |
After the user confirms, perform a full replacement:
| |
For example, if the current user chooses user-a, generate:
| |
If later user-b joins the same project, generate a separate set of local files for user-b, rather than reusing or overwriting user-a’s set:
| |
This namespace should ideally be a short, stable string suitable for filenames, for example:
| |
It is not recommended to include spaces, slashes, or shell special characters, as these increase the risk of script and path processing errors.
Implementation Layer: The Root Project Also Needs Boundaries
The root project itself requires rules. Otherwise, it will gradually evolve from a “management task” into a “control panel capable of modifying all sub-projects.”
The root project should have a limited scope of what it can manage, for example:
| |
Additional Note: Although the root project is typically managed by a single individual and could theoretically use just one
AGENT.mdwith a temporary folder named simplytmp, we maintain consistency with the sub-project structure by usingAGENT.mdplusSomeUser-agent.local.mdandSomeUser-tmp/. This design achieves the same end result as using a singleAGENT.mdwhile keeping the entire project system’s conventions uniform.
However, it must not modify:
| |
If a sub-project needs to adopt this rule set, the root project doesn’t directly modify the sub-project’s files. Instead, it provides handoff documentation: copy the content from new-project-pass-info-to-AGENT-MD.md and paste it into the target sub-project’s Codex or Claude dialog, letting the agent within that sub-project execute the configuration itself according to these instructions.
This constraint is crucial. It makes the main project function like a dashboard and harness, rather than an agent with cross-project write permissions.
Periodic Tasks: Separate Reading Reports from Writing Summaries
In practice, it’s natural to think about periodic tasks: generating task reports daily or each workday.
Here too, we need to distinguish between two types of tasks:
Report-only task Only reads the task status of each project, outputs a report, and does not write to project files.
Aggregation update task Reads the task status of each project and updates the root project’s
planned.md,doing.md, andcompleted.md.
These two task types carry different risks. The former is low-risk; the latter writes to root project files.
Therefore, after an update-type task executes, it needs to write a log, for example:
| |
A report-type task can reference this timestamp:
| |
This way, readers know exactly what point in time the report’s status reflects.
Personal Files Ignored by Git in Sub-Projects Also Need Governance
Personal rule files within sub-projects are not committed to Git, which solves the shared pollution problem but introduces another issue: could these files be lost?
For example:
| |
These files are local configurations not submitted to the shared repository. They could be lost during machine migration or project reconstruction.
The solution is not to “back up all ignored files.” That’s too risky because ignored files might contain caches, keys, build artifacts, or temporary drafts.
A safer approach is an allowlist:
| |
Default no-backup:
| |
Because the temporary draft directory may contain unorganized content, Chinese review drafts, sensitive context, or expired intermediate artifacts. Unless explicitly enabled, it should not be included in backups.
The principles for the backup script are:
- Scan only direct sub-projects.
- Read-only access to sub-projects.
- Write only to the root project’s backup directory.
- Save files organized by sub-project directory.
- Generate a
manifest.mdfor each backup directory. - The manifest records namespace, source path, backed-up files, and missing items.
flowchart LR
subgraph SRC["Direct Sub-Projects"]
S["Sub-project Directory"]
R1["Personal Rule File
NAMESPACE-agent.local.md"]
R2["Local Ignore Rules
.git/info/exclude"]
T["Temp Directory
NAMESPACE-tmp/"]
end
B["Backup Script
backup-local-user-configs.sh"]
subgraph OUT["Root Project Backup Directory"]
O["local-user-config-backups/
CHILD_PROJECT/"]
F1["NAMESPACE-agent.local.md"]
F2["git-info-exclude"]
M["manifest.md"]
end
S -. "read-only" .-> B
R1 --> B
R2 --> B
T -. "default not read" .-> B
B --> O
O --> F1
O --> F2
O --> Mflowchart LR
subgraph SRC["Direct Sub-Projects"]
S["Sub-project Directory"]
R1["Personal Rule File
NAMESPACE-agent.local.md"]
R2["Local Ignore Rules
.git/info/exclude"]
T["Temp Directory
NAMESPACE-tmp/"]
end
B["Backup Script
backup-local-user-configs.sh"]
subgraph OUT["Root Project Backup Directory"]
O["local-user-config-backups/
CHILD_PROJECT/"]
F1["NAMESPACE-agent.local.md"]
F2["git-info-exclude"]
M["manifest.md"]
end
S -. "read-only" .-> B
R1 --> B
R2 --> B
T -. "default not read" .-> B
B --> O
O --> F1
O --> F2
O --> Mflowchart LR
subgraph SRC["Direct Sub-Projects"]
S["Sub-project Directory"]
R1["Personal Rule File
NAMESPACE-agent.local.md"]
R2["Local Ignore Rules
.git/info/exclude"]
T["Temp Directory
NAMESPACE-tmp/"]
end
B["Backup Script
backup-local-user-configs.sh"]
subgraph OUT["Root Project Backup Directory"]
O["local-user-config-backups/
CHILD_PROJECT/"]
F1["NAMESPACE-agent.local.md"]
F2["git-info-exclude"]
M["manifest.md"]
end
S -. "read-only" .-> B
R1 --> B
R2 --> B
T -. "default not read" .-> B
B --> O
O --> F1
O --> F2
O --> Mflowchart LR
subgraph SRC["Direct Sub-Projects"]
S["Sub-project Directory"]
R1["Personal Rule File
NAMESPACE-agent.local.md"]
R2["Local Ignore Rules
.git/info/exclude"]
T["Temp Directory
NAMESPACE-tmp/"]
end
B["Backup Script
backup-local-user-configs.sh"]
subgraph OUT["Root Project Backup Directory"]
O["local-user-config-backups/
CHILD_PROJECT/"]
F1["NAMESPACE-agent.local.md"]
F2["git-info-exclude"]
M["manifest.md"]
end
S -. "read-only" .-> B
R1 --> B
R2 --> B
T -. "default not read" .-> B
B --> O
O --> F1
O --> F2
O --> MThis step embodies a key insight: although local files don’t enter Git, they can’t be left ungoverned. Backups must be precise, not greedy. After this treatment, the root project can consider syncing to its own Git repository, allowing the backup directory within the root project to serve a recovery function.
Failure Scenarios and Handling
This approach is not zero-cost. Key risks need to be documented upfront.
First, sub-project task files are not updated for a long time.
If a sub-project fails to move tasks from doing/ to completed/ promptly, the root project’s aggregation becomes stale. The solution isn’t for the root project to overstep and modify the sub-project, but for the aggregation report to clearly indicate the data timestamp and use periodic aggregation logs to expose “when this report’s status was generated.”
Second, multiple people modify the same task in doing/ simultaneously.
If a task genuinely requires collaboration, it’s best to break it into multiple owned sub-tasks, or clearly specify the owner and current handler within a single task document. Don’t let multiple agents mix different people’s status into an unowned file. If a Git conflict occurs, handle it like a normal code conflict, rather than letting an agent automatically guess which part to keep.
Third, local configuration loss.
SomeUser-agent.local.md and .git/info/exclude not being in the shared repository is cleaner, but they can be lost during machine migration or project reconstruction. This risk is mitigated by the root project’s allowlist backup: only back up personal rules and local ignore files, not SomeUser-tmp/ by default.
Fourth, personal temporary directory leakage.
SomeUser-tmp/ may contain unorganized content, sensitive context, or expired intermediate artifacts. Therefore, it’s excluded from backups and Git by default. If backup is truly needed, it should be explicitly enabled, rather than having the backup script automatically recurse through the entire ignored directory.
Effectiveness Evaluation
The benefits of this approach are primarily fourfold.
First, AI agents can more easily obtain stable context.
Task status no longer exists only in conversation history but is grounded in each sub-project’s clear tasks-status/ structure.
Second, multi-project visibility is clearer. The root project can aggregate the planned, doing, and completed status of all sub-projects without reverse-modifying them.
Third, collaboration pollution is reduced.
The shared AGENT.md only retains a minimal hook. Personal rules, temporary drafts, and local ignores all stay local.
Fourth, risk boundaries are clearer. Which files can be written, which can only be read, and which directories should never be touched are all codified as rules, rather than relying on ad-hoc reminders in each conversation.
However, it is not a zero-cost solution.
The biggest risk remains that state maintenance depends on human and agent discipline. If sub-projects don’t move task files promptly, the root project’s aggregation becomes stale. The solution isn’t for the root project to forcefully fix things, but to strengthen sub-project state maintenance rules and expose state timeliness through periodic aggregation logs.
Another risk is local configuration backup. Personal files ignored by .git/info/exclude won’t pollute the team repository, but they also won’t naturally enter version control. Hence the need for an allowlist backup mechanism, with a clear default of not backing up temporary directories.
Neither of these risks is a bug; they are engineering trade-offs. The key is to make those trade-offs explicit.
Returning to the Harness Engineering Philosophy
This practice ultimately lands on the harness philosophy.
A harness is not just a script or a prompt template. It’s more like an engineering shell that places the AI agent within a clear set of constraints:
flowchart LR
I["Input contracts"] --> H["AI Working Harness"]
R["Read boundaries"] --> H
W["Allowed write scope"] --> H
S["Status documents"] --> H
L["Logs and manifests"] --> H
P["Periodic tasks"] --> H
C["Human review points"] --> H
H --> O["Predictable AI operations"]
H --> A["Auditable state"]
H --> B["Lower collaboration risk"]flowchart LR
I["Input contracts"] --> H["AI Working Harness"]
R["Read boundaries"] --> H
W["Allowed write scope"] --> H
S["Status documents"] --> H
L["Logs and manifests"] --> H
P["Periodic tasks"] --> H
C["Human review points"] --> H
H --> O["Predictable AI operations"]
H --> A["Auditable state"]
H --> B["Lower collaboration risk"]flowchart LR
I["Input contracts"] --> H["AI Working Harness"]
R["Read boundaries"] --> H
W["Allowed write scope"] --> H
S["Status documents"] --> H
L["Logs and manifests"] --> H
P["Periodic tasks"] --> H
C["Human review points"] --> H
H --> O["Predictable AI operations"]
H --> A["Auditable state"]
H --> B["Lower collaboration risk"]flowchart LR
I["Input contracts"] --> H["AI Working Harness"]
R["Read boundaries"] --> H
W["Allowed write scope"] --> H
S["Status documents"] --> H
L["Logs and manifests"] --> H
P["Periodic tasks"] --> H
C["Human review points"] --> H
H --> O["Predictable AI operations"]
H --> A["Auditable state"]
H --> B["Lower collaboration risk"]Within this harness:
- Input contracts are
tasks-status/{planned,doing,completed}/. - Read boundaries mean the main project cannot modify sub-projects.
- The writable scope is the root project’s own aggregation files and backup directory.
- Status logs give reports a temporal basis.
- Allowlist backups make local personal configurations recoverable.
- The
SomeUserplaceholder allows the scheme to be reused by different users.
If this approach is later extended to the retrieval or tool layer, the same isolation principles should continue to apply, but that is beyond the scope of this article.
The core problem in AI programming is often not whether AI can write a certain piece of code, but within what boundaries it writes, based on what state, and how the results are tracked and recovered afterward.
When a project has only one person, one repository, and one task, these issues are not apparent. But when AI agents begin participating in multiple projects and enter a multi-person shared collaboration environment, a harness becomes necessary.
It transforms “let AI do things for me” into “let AI collaborate stably within engineering boundaries.” This is the layer truly needed when AI programming moves from personal technique to practical engineering practice.
🤖 AI Related Posts by semantic similarity
Want updates? Subscribe via RSS
Related Content
- Practical Guide · Building a Memory-Enabled AI Writing Partner (Part 1): Multi-Agent Architecture Evolution
- Hands-On: Building an Automated AI Semantic Search With Cloudflare Vectorize and Gemini
- From Azure SRE Agent to HolmesGPT: AIOps Practices in Multi-Cloud Kubernetes Environments
- Cilium 2026 (Continued): How the Unified Data Plane Is Reshaping Kubernetes Platform Architecture
- Weekend Project: Building a Local Load Balancer for LLM API Keys