Skip to content

Appendix 07

Addendum on Collaboration Tools: How to Verify the Scheduling Boundary ​

Xia A and Lao Z observe parallel workstations, coordinate results at a merge table, and inspect a stalled station.

Someone on the team proposed a plan: use Paseo to split coding tasks across several executors and run them in parallel. "That is definitely faster," they said. Xiao A was tempted, but he remembered Lao Z's standing rule — don't trust the conclusion yet. Which tasks? Who assigns them? What happens on a conflict? Whose failure is it? Until those details were pinned down, "faster" was only an optimistic guess. He took the plan to Lao Z.

Lao Z nodded. "That alertness is exactly right. Parallelism is an optimization, not a default state. A single executor has no scheduling problem. Once you hand work out, four problems appear at the same time: ownership, state, conflict, and failure. This appendix turns 'should we parallelize' into 'can we verify'. A plan is fit to ship only if each of those details can be walked, one by one, into evidence."

"There is one more thing to align first: the metric," Lao Z went on. "'Faster in parallel' usually means wall-clock time. What a team actually cares about is often total cost — scheduling, merging, debugging, and reruns added together. If the wall clock shrinks but the total cost grows, who is keeping that account? So what you quantify is not only 'how much faster' but also 'how much more expensive'. Only when both speak in numbers can you call it verification."

This appendix serves Part II · Pi in Source and Part III · Build It. Read it when your team is considering handing coding tasks to multiple executors. The snapshot of this repository contains no verifiable Paseo implementation, configuration, or first-hand documentation, so this appendix states no specific Paseo commands, model support, or automation behavior. What it teaches is the general method for verifying a collaboration tool, not a feature list for one product.

For the term "scheduling boundary", Lao Z drew a line. It runs between what the scheduler can handle and what it cannot. What it can handle: the task queue, executor assignment, state tracking. What it cannot: the correctness of a task's content, conflicts over shared state, the rollback of side effects. The verification method in this appendix revolves around that line — first know which parts belong to the scheduler, then know who owns the rest.

"Hand it out and it completes itself" is an illusion ​

Xiao A guessed: "It's called a scheduling tool — so once a task is handed out, it completes on its own, right?"

Lao Z shook his head. "You can't infer that. 'Scheduling' only answers 'who does it'. It does not answer 'was it done right'. Take the word apart and look: at bottom it is a task list plus a dispatcher — give task A to executor X, task B to executor Y, then wait for the results to come back. The list and the dispatcher carry no 'correctness' of their own. They only move work onto different executors. Whether the result is right, whether it merges, whether it rolls back — all of that must be answered by something outside the scheduler. So any collaborative scheduler must first answer four questions: who owns the task, how shared state is managed, how wide the permission scope is, and who sets the acceptance criteria."

"Then who sets ownership and the acceptance criteria?"

"Usually the host — your orchestration code," Lao Z said. "Say task A belongs to executor X. That word 'belongs' has two layers: X has permission to touch it, and X is accountable for it through acceptance. Many scheduling tools have the first layer and not the second. They can restrict who writes, but they cannot say what a good result looks like. Acceptance criteria are the easiest thing to leave floating. A scheduler can record 'task complete', but it will not define whether 'complete' means the command exited with code 0 or means the tests actually passed. Those two definitions are worlds apart, and the tool only reports the first."

Lao Z asked Xiao A to consider the simplest case: two executors editing the same file at the same time.

  • If both executors start from version v1 of the file and produce v2-a and v2-b, who resolves the conflict at merge time? The scheduler will not merge semantic conflicts automatically. Changes that stitch back together textually may fight each other semantically — one adds a parameter, the other deletes the function that calls it.
  • If executor A changes config.ts while executor B is reading the old config.ts to make a decision, B's decision rests on stale information. Whose error is that? A's, because A did not notify? B's, because B did not re-read? The scheduler will not answer for you.
  • If one executor fails, should its partial side effects — files already written, requests already sent — be rolled back? Does the scheduler even know what it did? If it was aborted halfway through a write, the half-written file it leaves behind is worse than no file at all.
  • If both executors send requests to the same external API, whose quota does the rate limit count against? Schedulers usually keep the books per executor; they do not convert them into one overall budget.

Xiao A copied the four questions down. "They're really four faces of one thing — asking about ownership, shared state, side effects, and quota. What they have in common is that none of them looks like something the scheduler will answer for me."

"Right. The four questions map to four agreements that live outside the scheduler: who has permission to touch this file, who is guaranteed to read the newest version, where the damage boundary is drawn, and whose account is charged when a resource runs out. The scheduler only reports four states (pending/running/done/failed), and the world outside those four states is invisible to it. If nobody writes those four agreements, parallelism just scales the problems out over more executors."

Lao Z pointed out another detail that is easy to miss. "The scheduler carries one more hidden assumption: that executors are homogeneous. In practice, executors may be connected to different models, different API keys, different context windows. The same task may run for executor A and not for executor B, and the scheduler will neither know it nor change the assignment because of it. 'Who does it' is equivalent in its eyes; in the results it is not. Think that difference through during task design too."

Xiao A stared at the third bullet. "On rollback after failure — I assumed the scheduler handled that automatically."

"It doesn't," Lao Z said. "Most schedulers only recognize those four states and track nothing about a task's side effects. Which files were written, which network requests were sent, which database rows were changed — all of that lives inside the executor, out of the scheduler's sight. Rollback has to come from compensation logic the executor writes itself, or from a workspace snapshot taken from outside. The scheduler only triggers it; it does not implement it."

"What does compensation logic look like?"

"Two levels. The first is the executor declaring for itself: 'here are the side effects I will produce, and here is how I undo them on failure'. For example, write a temp file first and rename it atomically only after everything succeeds; or treat a logging failure as a failure of the whole task, so it cannot succeed on its own. The second is the host providing workspace snapshots and replay, so a broken workspace can be restored to its state before the task started. Neither level exists by default: the first needs the executor to cooperate in writing it, the second needs the host to cooperate in building it. When you get a new scheduling tool, do not assume it supports rollback. Verify it under the rule 'no rollback by default, unless explicitly declared'."

"One more thing," Lao Z said. "Parallelism is faster only when the tasks are genuinely independent. Coding tasks usually carry implicit dependencies: shared files, shared configuration, shared tests. Force-parallelize dependent tasks and you get more than conflicts — you get intermittent bugs that are far harder to locate. An error that reproduces reliably at step 5 in a serial run may only surface in parallel when the timing of step 3 and step 4 collide, and the timing is different every run."

Xiao A jumped in: "I know this one — look at the shared resources."

"Yes, that's the cheapest method," Lao Z said. "Two tasks both reading and writing config.ts — that is a file intersection. Draw it and you can see it. The hard one is the timing intersection. One task runs lint while another edits code; there seems to be no intersection, but lint is reading a half-changed file, and that is far harder to spot than a file intersection. There is a crude way to check for timing intersections: swap the execution order of the two tasks, run each once, and if the results differ there is an implicit dependency. Order-sensitivity is itself the evidence."

Xiao A said: "So if we rule out both file intersections and timing intersections, we're safe?"

"One layer is still missing," Lao Z said. "A data race need not be at the file layer; it can be at the external-service layer. If both executors call the same external service, the server-side state is shared — two tasks pushing to the same Git remote, or both updating the same database row. You can draw the file-layer intersection. You cannot draw the external-service-layer one. But the consequences of the conflict are just as real. Verifying it takes runtime observation: run the two tasks in parallel, watch the service's state changes, and look for mutual overwrites."

How to read an unverified case ​

Lao Z said: "This isn't a ban on parallelism. It is parallelism after verification. When you treat Paseo, or any collaboration scheduler, as a case awaiting verification, gather first-hand material in five steps."

  1. Confirm the source: version number, release date, official documentation URL. Do not take a feature seriously if there is no first-hand documentation for it.
  2. Find the real interface: the concrete APIs or commands for task creation, dependency declaration, cancellation, retry, and result aggregation. A verbal "it can do that" does not count.
  3. Verify the permission boundary: each executor's workspace, file ownership, and credential scope. Can two executors write to the same path?
  4. Build a conflict test: deliberately create two tasks that modify the same file, and observe the conflict policy (error? silent overwrite? lock?). Do not assume automatic merging.
  5. Quantify the comparison: record call counts, elapsed time, failure rate, and how often a human had to step in, then compare against a single-agent baseline. "Faster" has to be proved with numbers, not felt.

"Keep the order too," Lao Z said. "The first two steps establish what it is. The middle two put it under pressure and show how it actually behaves. The last step returns to the business goal and asks whether it pays off. Skip the first two and go straight to the comparison, and the thing you are comparing may not be the version you intend to use."

Xiao A asked: "For step two, what counts as having found the interface?"

"The standard is that you can write out a complete call sequence, from task creation to result aggregation, with a specific API name or command at every hop. Anywhere you cannot write it down is a place you have not verified yet," Lao Z said. "A lot of product pages draw a beautiful architecture diagram. But when you replace the arrows in the picture with actual calling code, you find that some arrows have no counterpart at all. Those arrows are advertised features, not usable ones."

"For step five, how do you pick the baseline?"

"The baseline is the numbers for one agent running the same tasks serially. Do not use 'how the team works today' as the baseline — that may itself be very slow," Lao Z said. "Run one batch of tasks serially first, record elapsed time and failure rate, then run the same batch in parallel, and put the two numbers side by side. 'Faster' means exactly this: parallel elapsed time is less than serial elapsed time. If the time parallelism saves gets eaten by merging, debugging, and reruns, then 'faster' is on the books only, not in reality."

Xiao A pointed at step one. "What about a feature with beautiful documentation but no API to verify it against?"

"That is marketing copy," Lao Z said. "A feature needs at least three things to count: an entry point — how you call it; parameters — what you pass when you call it; an observable result — what you can see once it finishes. Miss any one of the three and you cannot verify it, and when something goes wrong you cannot tell 'I used it wrong' apart from 'it is broken'. Keeping separate accounts for 'what the documentation describes' and 'what you can produce evidence from' is the first step of verification."

"Which of these steps goes wrong most often?"

"Step four, building the conflict test. Tool documentation almost never volunteers 'my conflict policy is silent overwrite', yet for many tools that is exactly the default." Lao Z asked Xiao A to work through three possible defaults in his head:

  • Raise an error and keep both: the scheduler surfaces the conflict and lets a human, or an agent above it, decide. Safest, but it needs an extra merge process.
  • Queue / lock: only one executor may hold a given resource at a time, and the other waits. Safe, but it degrades into serial execution, and "parallel is faster" loses its value right there.
  • Last write wins: whoever writes last decides, and the intermediate version is lost. Fastest and most dangerous — it raises no error; changes simply vanish without a trace.

"Reading the docs you might think it is the first one. You run the conflict test and find out it is the third," Lao Z said. "So the conflict test is not there to find bugs. It is there to establish which of the three the tool actually is."

Xiao A thought of another layer: "Besides both writing, is there any case that reads as safe and runs as dangerous?"

"Yes. The most common one is the read-modify-write race," Lao Z said. "Both executors read the same file first, modify their own copy in memory, then write it back. Neither touches the same byte, yet the commit that lands later overwrites the earlier one in full. A concurrent-write test checks two writers on the same path. Read-modify-write needs its own test: let two tasks start from the same v1, each modify and each commit, then see whether only one of v2-a and v2-b survives. Checking each write in isolation and finding no conflict is useless. What the scheduling boundary has to verify is that nothing conflicts after the merge." Xiao A added this test to his list.

"What if the documentation is incomplete, or I can't run the conflict test?"

"Then leave it marked 'awaiting verification', and do not complete the product's story for it," Lao Z said. "The most dangerous move is to fill a documentation gap with imagination and then draw conclusions from that imagination. When first-hand material and evidence from actual runs are insufficient, 'I don't know' is more responsible than 'I assume it can'."

Lao Z added one practical tip: "If you can run tests, test three things first. Concurrent writes to the same path, to see the conflict policy. An executor cancelled midway, to see what residue it leaves. A retry after failure, to see whether side effects are replayed — duplicate emails, duplicate charges. Documentation is usually vague on these three, but one run shows the real behavior."

"After running the tests, how do you keep the results?"

"Keep a verification record," Lao Z said. "Write down which version you checked, the check date, which tests you ran, what behavior you observed, and what you concluded — for example, 'verified as of Paseo vX.Y / 2026-08: concurrent writes to the same path use a queuing policy'. That record is the reference baseline the next time you upgrade the tool. Upgrade the tool and every conclusion is void until it is re-verified. Without the record, you will not even remember how you verified it last time."

The real preconditions for multi-agent parallelism ​

This matches the conclusion of the "multi-agent" chapter in Part I · Concepts: parallelism is worth something only when three conditions hold at once.

  • Tasks are independent: one task's input does not depend on another's output, or the dependency can be modeled explicitly.
  • Deliverables merge cleanly: the executors' outputs combine without conflict, or there is a defined conflict-resolution mechanism.
  • Failures are attributable: when one executor fails, you can pin the responsibility on it instead of scrapping the whole batch.

Lao Z used the three as a test of which ones you may drop: fail any one of them, and parallelism degrades into serial execution or worse.

  • Tasks are not independent but you parallelize anyway: A's output is B's input, yet you run them at the same time, so B consumes an input that does not exist and produces a result that looks plausible and is incoherent. This is slower than serial, because you still have to go back and work out whether A was wrong or B was.
  • Deliverables do not merge: three executors each change one module, and afterwards the interfaces they call across each other do not line up. Nothing merges, so you hold three independently useless drafts.
  • Failures are not attributable: one executor errors, but you cannot tell whether it is its own problem or shared state that someone else corrupted, so you rerun the whole batch.

Lao Z had Xiao A map the three conditions onto three engineering actions: tasks independent means do dependency analysis; deliverables mergeable means define contracts and a merge process; failures attributable means build isolation and logging. The first two belong to the task-design phase; the third belongs to the runtime environment. Most people only do the first — chop the work into small pieces and parallelize — and then merging and debugging are the two hard chores waiting at the end.

"Is there one of these three we could let slide for now?"

"No. But one of them is easiest to satisfy in appearance and not in fact, and it is mergeable deliverables," Lao Z said. "Three executors each change one module, and the three Git branches merge without reporting a conflict. That only means the text stitches back together. After the merge, compiling and passing tests — that is genuinely mergeable. The first standard says the text does not fight; the second says the semantics do not. For parallel deliverables, set the acceptance criteria at the semantic level."

"There is another boundary people miss: merging is itself work that must be verified, and it is not free," Lao Z went on. "If the time parallelism saves gets eaten back by merging, the deal loses. So estimate the merge cost before the project starts: can the outputs be merged mechanically, or does a human have to review them one by one? Mechanical merging is cheap and parallelism pays. Manual merging is expensive and parallelism may not."

Xiao A asked: "Can 'tasks are independent' be turned into a table you can check?"

"Yes, and it costs almost nothing," Lao Z said. "Take a grid with the task names on both rows and columns, and mark each cell with one of three symbols. Two tasks share a file — mark 'shared'. One task's output is another's input — mark 'dependency'. No relation at all — leave it blank. If any non-blank 'shared' or 'dependency' cell exists, those two tasks must not be parallelized blindly. This grid is the raw evidence that parallelism is viable. Only after drawing it do you know how many boundaries the scheduler has to manage for you, and which boundaries you have to cover with your own agreements."

"The third condition, 'failures are attributable', sounds like the hardest."

"It is, because it requires observable execution boundaries," Lao Z said. "Every executor needs its own logs, its own workspace, and a replayable input. If executors share memory, share files, or share a connection pool, failure spreads along the shared state and every one of them says 'not me'. That is why isolation — separate workspaces, separate credentials — is not only a security requirement but a debugging requirement."

"Separate logs — how do you actually land that?"

"One directory, one log stream, and one replayable copy of the input per executor," Lao Z said. "You pay in disk and complexity, and what you buy is the ability to point at a single line and say 'here — it started going wrong here'. Many teams skip this step and mix all executors into one log. When a concurrency incident appears, nobody can establish the order of events, and order is precisely the most important information in a concurrency incident. The space a shared log saves gets paid back many times over during debugging."

"A scheduler manages the queue and the dispatch," Lao Z said. "It does not automatically remove correlated errors, privilege creep, or missing acceptance criteria. Those three risks are the intrinsic price of parallelism. A scheduling tool can reduce them; it cannot remove them. Treating the scheduler as 'I just throw tasks in and they become correct' is turning a queue manager into a correctness judge."

A collaboration tool is a runtime boundary, not a silver bullet ​

Xiao A asked at last: "So are collaboration tools useful at all?"

"They are useful, but they are a verifiable runtime boundary, not a silver bullet," Lao Z said. "They fit the cases where you have already proved that serial execution is too slow and the tasks really are independent — running lint once over 100 independent files, or handing 50 unrelated issues to one executor each for triage. They do not fit the cases that look parallelizable but carry hidden shared state — several people changing the same module, or several executors all updating one configuration."

Lao Z offered one more test: look at whether the tasks touch the write set or the run set. Each writing its own files means independent write sets, which suit parallelism. Sharing one runtime environment, one test suite, one dependency tree means a shared run set. Parallelize tasks like that and the conflict is not in the code you wrote — it is in the churn of the environment.

"So the coding tasks that suit parallelism usually have this shape: each writes its own new files, each runs its own verification, and everything merges in one place at the end?"

"Right. As for telling a genuinely independent task from one pretending to be independent, ask three questions: what does it read? What does it write? What state does its success depend on?" Lao Z said. "If two tasks' write sets or read sets intersect, they are not truly independent. If one task's success depends on the other's output, there is a dependency. This judgment needs no scheduler. Draw the intersection on a piece of paper and you can see it."

"Drawing the intersection on paper is still not enough," Lao Z said. "You also have to follow 'what state does success depend on' all the way down. A task reports that lint passed, but it ran against stale code — that is a fake success. The way to verify is to look at its acceptance artifact: what observable result did the task leave when it finished, and was that result computed from the newest input? A success with no observable artifact can neither be merged nor traced. It amounts to nothing being done."

"Is fake success especially common in parallel setups?"

"Common, and easier to hide than in serial work," Lao Z said. "Serially you watch every step, so a deviation shows up at once. In parallel you only see each executor's final report. Whether those reports step on each other only emerges at the merge stage. So the merge stage is not assembling results — it is a second pass of verification: every result must trace back to its input, its workspace, and its logs. A result that cannot be traced back ranks behind one that merely looks fine."

Xiao A asked again: "Then how should I cite a specific tool like Paseo?"

"An external tool's version, features, and scheduling behavior change with each release, and this appendix endorses no version of Paseo," Lao Z said. "Whenever you cite a specific scheduling behavior of it — 'it queues concurrent writes', say — mark the version and the date you checked: 'verified as of vX.Y / 2026-08, default conflict policy is raise-error'. Once you pass that check date, the conclusion must be re-verified. Treat a citation of an external tool as time-limited evidence, not as a constant."

"If every behavior is subject to verification, can you give a template for the verification record?"

"Yes. Five columns are enough: subject (tool name plus version), check date, what you tested, what you observed, and the conclusion with its validity window," Lao Z said. "In the 'what you observed' column, write facts, not conclusions — for example, 'two tasks wrote to config.ts at the same time, both succeeded, the first one's change was overwritten by the second, and no error was raised'. State the facts clearly and anyone can reproduce the conclusion, which here is last write wins. The point of a record is to keep others from repeating your experiment, so do not leave out the observation column."

"One more thing people most often miss," Lao Z added. "When you update a tool, your verification records matter more than the documentation. Plenty of people read only the release notes when upgrading, and release notes are exactly what will not tell you that a default changed — 'the conflict policy was switched', for instance."

The two non-negotiables for verifying a collaboration tool

  1. The conflict policy must be observable: when two tasks hit the same resource, the tool's behavior is defined (error / queue / lock), not undefined. "Undefined" is usually silent overwrite — another way of saying data loss.
  2. Failures must be attributable: after one executor fails, you can trace it from the logs instead of facing a pile of mixed results with nowhere to start. This requires each executor to have its own log stream and a replayable input.

"Those two non-negotiables," Lao Z said, "are 'no data loss' and 'incidents you can investigate'. Miss the first and your parallel run is losing things without noticing. Miss the second and every incident becomes a cold case. Everything else can wait; these two cannot. They are the ground you stand on when you hand tasks out."

"Now look back at that 'parallel is faster' plan from the beginning," Lao Z said. "You know how to take it. First make it answer the four questions: who owns, who shares, who rolls back, whose quota. Then run the conflict test and the failure-attribution test. Then compare against the serial baseline. A plan that cannot answer the four questions, and whose two non-negotiables cannot be measured, has a 'faster' that is floating in the air."

Back to the goal of the whole book: any collaboration tool should be treated as a verifiable runtime boundary. Parallelism is not a default option; it is an optimization you apply after independence has been proved. That proof has to come from a run of your own, not from the vendor's marketing page. When first-hand material falls short, mark the tool as awaiting verification rather than completing its story with imagination.

Xiao A taped the five-column verification-record template to the wall of his cubicle. The next time someone proposes "let's run that in parallel with Paseo", he knows the old question he will open with: "Have you tested the conflict policy?"