An agent can receive instructions from almost anywhere. A chat message describes the task. A scheduler supplies parameters. A repository contains conventions. A wrapper adds defaults. A saved prompt contributes another set of rules. Each source is convenient in isolation.
Together they can create a job nobody can reproduce.
The disagreement isn’t always dramatic. One instruction says to write output into a staging directory while another names the final directory. One names the allowed file set and another uses a broader pattern. A chat says “use the latest version,” while the scheduled job still points at a fixed model or script. The agent runs, produces something plausible, and leaves me to guess which instruction won.
For recurring or consequential agent work, I prefer one canonical job specification stored with the work it governs. Other surfaces may launch it, display it, or supply explicitly permitted inputs. They don’t get to maintain private versions of its operating rules.
Decision: the repository owns the job
My choice is a versioned job file in the repository. It defines the allowed inputs, output location, tool boundaries, verification command, and completion condition. If the job uses a prompt, the prompt is referenced from that same definition or lives beside it. The schedule points to a job identity and passes narrow runtime values such as the selected item or execution window.
This makes the repository authoritative for behavior, not for every piece of runtime state. Credentials still belong in a secret store. Large inputs may live elsewhere. Run results need their own records. “One source of truth” shouldn’t become an excuse to put everything in one file.
The source owns the rules that answer a specific question: what is this job supposed to do? If I need to change that answer, I change the job definition and its version. The next run can report which version it loaded.
That identity matters more than a copied paragraph of instructions. When an output surprises me, I can inspect the exact definition rather than compare several interfaces from memory. A scheduler history that says job revision X is more useful than one that preserves a long prompt whose relationship to the repository is unknown.
Launchers may add values, not policy
A launcher still needs flexibility. A manual run may target one file. A schedule may supply a date range. A test harness may substitute a temporary output directory. Those are values inside a contract the job already declares.
I separate parameters from policy by listing which parameters are accepted and what they may affect. A launcher can choose an input from the allowed scope; it can’t widen the scope. It can select dry-run mode if the job exposes one; it can’t disable verification by adding an enthusiastic sentence. Unknown parameters fail instead of becoming improvised instructions.
Chat works the same way. I may use conversation to explore the task or prepare a proposed change to the job. Once the work is recurring, the conversational wording is not the maintained specification. If the chat uncovered a missing rule, that rule has to move into the canonical file before I trust another run to remember it.
This boundary is less magical than telling an agent to combine all available context. That’s a feature. Precedence rules are easy to describe when there are only two classes: canonical policy and allowed runtime values. They become fragile when every source can override every other source according to prose interpretation.
The tradeoff is deliberate friction
The repository path is not the fastest place to make a one-off adjustment. An interface toggle would be quicker. A sentence added to the scheduled prompt would avoid an edit and review. There are times when that speed is useful, especially during exploration.
I accept slower changes once the job matters because the friction leaves a diff. It forces me to decide whether I’m changing an input, changing the policy, or creating a different job. It also makes rollback ordinary: select the previous definition and run it against appropriate inputs. I’m not trying to remember which hidden prompt field I edited two weeks ago.
The cost includes maintenance. Paths change. Verification commands evolve. A job file can become ceremonial if nobody checks that launchers actually load it. I guard against that by having the run record the specification identity and reject a missing or invalid definition. A canonical file that execution ignores is just documentation wearing a badge.
I also resist turning the job definition into a universal agent constitution. Broad rules shared across unrelated work grow vague and contentious. The useful source of truth is scoped. A writing job, a release check, and a local file classifier can share a small set of repository conventions while keeping their own inputs, effects, and acceptance rules.
That does mean some duplication between jobs. I’d rather repeat a short explicit boundary than inherit a clever rule through three layers and wonder why a harmless edit changed everything. Shared behavior earns extraction when it’s genuinely the same policy and has an owner.
The operating choice is simple: launchers launch, parameters select within bounds, and the versioned job specification defines behavior. If a run can’t name the specification it followed, I don’t let its fluent output settle the argument. I fix the source path first, because a system with five convenient truths has no reliable way to explain the sixth result.