People need to save preferences they would otherwise repeat in every conversation.
Updated September 12, 2026
Problem
People repeat the same preferences in each conversation. When they forget, they may have to rewrite the answer.
Solution
Let people save instructions that apply to future conversations. Keep these separate from preferences the assistant infers through memory.
Show when saved instructions are active, with a direct way to read or edit them. Include when they were last updated so people can spot old guidance.
Support project-specific instructions when people do different kinds of work. Explain which instructions take priority when they conflict. Encourage short instructions and make clear that the AI may still fail to follow them.
Use when
People use the assistant regularly and have preferences that apply across requests.
Don’t use when
Use is occasional or the account is shared. Instructions written by one person on a shared login silently reshape everyone else’s answers, and the people affected have no idea the block exists.
Trade-offs
Saved instructions reduce repetition, but forgotten rules can make answers confusing. Project rules and memory can conflict with them. Longer instructions also use more of the space available for the conversation.
Checklist
Can the viewer tell from a conversation that instructions are active?
Is the text reachable in one click from where it takes effect?
Is the date each instruction was written visible?
Do global and project instructions layer in a stated order?
Is there a budget or a warning as the block grows?
Does the interface promise determinism it cannot deliver?
What happens when an instruction conflicts with a stored memory?
Can instructions be turned off for one conversation without deleting them?
On a shared account, is it clear whose instructions are in force?
Are the instructions included when a conversation is shared or exported?
Compare
ChatGPT keeps a global instruction block and adds per-project instructions above it, so the layering is explicit in the interface rather than implied. Claude attaches the equivalent to projects, so the natural unit is a body of work rather than the account. That fits anyone who switches between roles. Gemini leans toward inferring preferences from past chats instead of asking for them, trading the audit trail for less setup. Notion puts the equivalent in the workspace as written documentation the assistant reads, so the standing instructions are also a thing colleagues can see and edit.
Related
Memory chip shows preferences saved by the assistant. Scoped context determines which project instructions apply. Conversation history contains earlier answers shaped by those instructions.
First-run state can introduce the feature. Refusal may result when instructions rule out the requested answer.
Wireframe — the pattern's anatomy, not any one product's version of it
Implementation
Written once, in force forever, forgotten by March. Each rule carries a date, the project layer overrides the global one, and the block spends part of every turn whether or not it still applies.
100 of 400 characters, spent on every turn, forever
Standing guidance: advisory rather than binding, and a long block dilutes itself
CustomInstructions.tsxTwo scopes in a stated order, a date on every rule, a character budget computed from the text, and a chip that counts what is in force.
import { Button } from "@/components/ui/button";import { Progress } from "@/components/ui/progress";/** A rule the viewer wrote, and when. The date is what turns a standing order * into something a person can audit six months later. */export type Rule = { text: string; written: Date };/** Two scopes, no more. Project sits above global and wins on conflict; the * order is printed rather than left to be discovered. */export type Scope = "project" | "global";export type Layer = { scope: Scope; name: string; rules: Rule[] };const SCOPE_ORDER: Scope[] = ["project", "global"];const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];const day = (d: Date) => `${d.getUTCDate()} ${MONTHS[d.getUTCMonth()]}`;type Props = { layers: Layer[]; /** Characters the block may spend on each turn. Every rule is prepended to * every conversation, so the cost is per turn and it never stops. */ limit: number; /** The chip on the conversation opens the text. One click, no settings dive. */ onOpen: () => void;};export function CustomInstructions({ layers, limit, onOpen }: Props) { const ordered = [...layers].sort((a, b) => SCOPE_ORDER.indexOf(a.scope) - SCOPE_ORDER.indexOf(b.scope)); const rules = ordered.flatMap((l) => l.rules); const used = rules.reduce((n, r) => n + r.text.length, 0); const pct = Math.min(100, Math.round((used / limit) * 100)); return ( <div className="grid grid-cols-[1.1fr_0.9fr] gap-5 rounded-lg border bg-card p-4 text-[11px]"> <div className="rounded-lg border bg-muted"> {ordered.map((layer, i) => ( <div key={layer.scope} className={i > 0 ? "border-t" : undefined}> <p className="px-3 pt-2.5 uppercase tracking-wide text-muted-foreground">{layer.name}</p> <ul className="pb-2.5"> {layer.rules.map((r) => ( <li key={r.text} className="flex items-baseline gap-3 px-3 pt-1.5"> <span className="text-card-foreground">{r.text}</span> <time className="ml-auto tabular-nums text-muted-foreground" dateTime={r.written.toISOString()}> {day(r.written)} </time> </li> ))} </ul> </div> ))} </div> <div> <p className="text-muted-foreground">project overrides global</p> <Progress value={pct} className="mt-2 h-2.5 bg-muted [&>div]:bg-scale-seq-3" aria-label="context budget" /> <p className="mt-1.5 text-muted-foreground"> {used} of {limit} characters, spent on every turn, forever </p> {/* The box is not a settings panel and should not look like one. */} <p className="mt-4 leading-relaxed text-status-warn"> Standing guidance: advisory rather than binding, and a long block dilutes itself </p> </div> <div className="col-span-2 border-t pt-4"> <Button variant="ghost" size="sm" onClick={onOpen} className="h-7 rounded-md bg-chart-1/15 px-3 text-[11px] text-chart-1 hover:bg-chart-1/25 hover:text-chart-1"> {rules.length} instructions active </Button> </div> </div> );}
demo.tsxHow it is called: one project rule over two global ones, 99 characters of a 400 budget.
import { CustomInstructions } from "./CustomInstructions";/** * One project rule over two global ones, each dated. 99 characters against a * 400 budget puts the bar at a quarter. The chip is what a conversation shows; * clicking it would open this panel. */export default function Demo() { return ( <CustomInstructions layers={[ { scope: "project", name: "This project", rules: [{ text: "Cite the source file for every claim", written: new Date("2026-09-01") }], }, { scope: "global", name: "Everywhere", rules: [ { text: "British English, no bullet lists", written: new Date("2026-02-04") }, { text: "I am a designer, not an engineer", written: new Date("2026-02-04") }, ], }, ]} limit={400} onOpen={() => {}} /> );}
What it renders. Identical markup in both panes, with only the token values changing.