Let a person set standing instructions that every coworker follows - #335
Open
nicklaunches wants to merge 1 commit into
Open
Let a person set standing instructions that every coworker follows#335nicklaunches wants to merge 1 commit into
nicklaunches wants to merge 1 commit into
Conversation
A deployment had two carriers for a durable instruction and both are about the work rather than about the person. A coworker's role belongs to the coworker and reads the same to everybody who talks to it; a skill is pulled in for one task. Neither can hold "always write to me in British English" or "we are a two-person company, never call us a team", which is a fact about the person and true in every channel, so people retyped it into the composer or went without. Settings now carries one text per person. It is spliced into every built-in coworker's prompt straight after the role, on every run in every channel, and into the runs a routine starts on the owner's behalf while they are asleep, so overnight work reads the way their own turn would. The block carries its own precedence sentence: the role decides what a coworker does and these decide how it does it, because a person may say how they want things done and must not be able to say what a coworker is for. The text belongs to the person who wrote it. The routes take the actor from the session and never from the path or the body, there is no route for reading or writing somebody else's, an administrator included, and the row cascades with the account. Remote AG-UI Bots are not given it: that prompt is composed at somebody else's endpoint, so there is no knowing whether it is read or how it ranks against the role. Empty is absence rather than a stored empty string, so there is one representation of "has written none". Reading them fails silently at build time: a coworker that could not be told loses a paragraph, where one that refused to start would lose the conversation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WaHWJ1niprhBc5NzJ9pxme
nicklaunches
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso and
tylerslaton
as code owners
September 2, 2026 14:53
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related: #325, #280.
The problem
A deployment has two carriers for a durable instruction, and both are about the work rather than
about the person. A coworker's role belongs to the coworker and reads the same to everybody who
talks to it. A skill is pulled in for one task.
Neither can hold "always write to me in British English", or "we are a two-person company, never
call us a team", or "our product is spelled like this". Those are facts about the person asking,
true of every task they will ever ask for, in every channel, from every coworker. Today the only
places to put them are the composer, retyped every time, or somebody's role, where it becomes
everybody's.
What changed
A settings section. One text box per person on the Settings page, between the appearance switch
and the shortcut list, since it is the only thing on that screen that changes what a coworker says
rather than what the browser looks like. Empty is absence: an empty save deletes the row rather than
storing
"", so there is one representation of "has written none".A prompt seam.
standingInstructionsGuidancerenders the text into a block placed straightafter the coworker's role and before everything else, and the block carries its own precedence
sentence: the role decides what a coworker does, these decide how it does it. That sentence is load
bearing, not decoration. Two standing instructions in one prompt is a conflict otherwise resolved by
whichever the model read last, and the resolution is not symmetric: a person may say how they want
things done and must not be able to say what a coworker is for. Without it, "always answer in one
line" quietly overrides a role that exists to produce a filing with its sources in it.
Every path a run can take.
LoadInstructionsis a closure rather than a captured string, forthe same reason
LoadToolsForBotis: a person can change their instructions between two runs, and avalue read at boot would serve everybody the first person's preferences. It is wired into the
request path, into
agentForso a handoff delivered at three in the morning carries the same textthe Bot in front of the person does, and into the routine builder so overnight work reads the way
the owner's own turn would. It is read once per roster build and only when something built-in is
being built.
Ownership. The routes are
requireUser, scoped tocontext.var.actor.id, and there isdeliberately no route for reading or writing somebody else's, an administrator included: this text
goes into a prompt that then speaks as that person's coworker, so a way to set it for another
account would be a way to put words in their mouth in every channel they work in. The row cascades
with the account.
Remote Bots are not sent it. A remote AG-UI Bot composes its own prompt at somebody else's
endpoint. Whether it would honour the text and how it would rank against the role are both unknown,
and one deployment's person addressing another deployment's server with their own prose is a
separate question. Not done here, and the reasoning is written down where somebody will look for it.
Failure is silence. A read that fails costs a coworker a paragraph. One that refused to start
would cost the conversation, and a preferences row is not worth a run.
Schema. New
user_instructionstable, migration0026_user_instructions. The user id is theprimary key rather than a column beside a surrogate one, because "what has this person asked every
coworker to do" must have exactly one answer.
How it was verified
server/tests/schema.test.tscovers the table: the user id as primary key, the column set, andthe cascade to
users.server/tests/settings-instructions-routes.test.tscovers the routes: read, write, delete onempty, the length limit, the 503 when the store is absent, and that the actor comes from the
session rather than the request.
server/tests/copilot.test.tscovers the prompt seam: where the block sits relative to the role,that it is absent when nobody has written any, that it never reaches a remote Bot, and that a
failed read costs a paragraph rather than a run.
bun test server/tests/copilot.test.ts server/tests/schema.test.ts server/tests/settings-instructions-routes.test.tsgives 61 pass, 0 fail.server/tests/user-instructions.integration.test.tscovers the store against a real database(upsert, delete on empty, and the cascade). It needs a live Postgres and was not run in this
environment; it runs in CI with the rest of the integration suite.
bun run typecheckclean across app, server and worker;bunx biome checkclean on all changedfiles.
🤖 Generated with Claude Code