Facets
A facet is a Markdown-defined persona that controls prompt framing, tool access, skill access, model pinning, and session color. The frontmatter declares the facet settings, and the body supplies the system-prompt framing the model works under. For day-to-day facet switching, see Prompting and Input.
Switching facets changes the system prompt and the available tools while keeping
the same conversation. That is how plan and execute work together. The model
researches and writes a plan in plan, then handoff_plan moves the work into
execute, which gives the model its edit and shell tools. The conversation built
up while planning carries over.
Where facets live
Section titled “Where facets live”A facet file goes in .polytoken/facets/ in your project, or in
$XDG_CONFIG_HOME/polytoken/facets/ (default: ~/.config/polytoken/facets/)
for facets you want everywhere. The filename is the facet’s name: scribe.md
defines a facet named scribe, and the name key in the frontmatter must
match the filename, or Polytoken rejects the file.
Facets override by replacement, not by merge. When two facets share a name, the
more specific one wins: a project facet overrides a global one, and either
overrides a shipped facet such as plan or execute. A custom plan is your
plan, not an edited copy of the shipped one.
The body is a template
Section titled “The body is a template”The body below the frontmatter is a template, rendered as described in Templating. Polytoken renders the facet body into the facet’s complete system-prompt framing. Polytoken does not add a base prompt of its own.
In most facets you will want Polytoken’s shipped base prompt under your own
framing. Include it with transclude at the top of the body:
{{ transclude("polytoken://system_prompts/facet.md") }}
Your job is to help the user with their documents.The shipped plan and execute facets both start this way, then add their own
lines. The shipped base prompt carries tool-use guidance and other behavior the
model depends on. Leaving the transclude out means writing that guidance
yourself.
Customizing the shipped plan facet
Section titled “Customizing the shipped plan facet”The shipped plan facet investigates with read-only shell access for repository
inspection but with project editing tools withheld, writes a handoff plan, runs
the built-in plan-reviewer, and asks you before moving work into execute.
The plan facet exposes shell_exec so the model can run repository-state
commands that built-in tools cannot cover, such as git status, git log, and
gh. The facet prompt steers the model toward grep, glob, and file_read
for file inspection, and both the prompt and autonomous classifier prohibit any
shell command that modifies files, the working tree, or system state. The
reviewer checks the plan against your project override when
project_vars.plan_facet.plan_spec_override is set, or against Polytoken’s
default plan shape otherwise. Replace the whole facet with
.polytoken/facets/plan.md when you want to change that behavior, the tools the
facet exposes, or the prompt outside the plan format.
To keep the shipped behavior and change only the handoff-plan format, set a
project variable for the shipped plan facet. The project variable replaces the
shipped plan format rather than adding to it, and plan-reviewer uses the same
override as the shape contract, so write the complete plan-format instructions
you want the model and reviewer to follow. See
Template Reference
for the exact key and YAML shape.
A small facet
Section titled “A small facet”This example defines a scribe facet in .polytoken/facets/scribe.md, giving
the model a documents-assistant framing and Notion tools:
---name: scribepolytoken: tools: [mcp__notion] color: "#7c3aed"---{{ transclude("polytoken://system_prompts/facet.md") }}
Your job is to help the user with their documents. You have full access to theirNotion workspace through its tools. Work from what the user asks for.mcp__notion grants every tool from the Notion MCP server. To grant the server
but exclude one tool, add a tools_deny:
tools: [mcp__notion] tools_deny: [mcp__notion__delete_page]Frontmatter keys
Section titled “Frontmatter keys”Conventional keys like name sit at the top level. Every Polytoken-specific key
is polytoken.<key>. The table below lists the keys a facet can use.
| Key | Type | Default | Meaning |
|---|---|---|---|
name | string | required | The facet’s name. Must match the filename. |
polytoken.model | string | active model | Pins the facet to a model. Without it, the facet uses the active model. |
polytoken.fallback_models | list | none | Models to fall back to when the primary is unavailable. |
polytoken.tools | list | none | The tools the facet exposes. Accepts literal names, mcp__<server> for all of a server’s tools, tag!ALL for every tool, and tag!ALL_MCP for every MCP tool. |
polytoken.tools_deny | list | none | Tools to remove from the granted set. Literal names only, using the full mcp__<server>__<tool> name for an MCP tool. |
polytoken.undeferred_tools | list | none | The granted tools whose full definitions appear up front rather than through tool search. Literal names only. |
polytoken.skills_allow | list | allow all | The skills the facet permits, as names or tag!<name> groups. Leaving it out means no restriction, so every skill is allowed. |
polytoken.skills_deny | list | none | The skills the facet blocks. Deny wins over allow. |
polytoken.color | string | none | The facet’s accent color, as #RRGGBB. Sets both light and dark. |
polytoken.color_light | string | none | The accent color for light themes. Overrides color. |
polytoken.color_dark | string | none | The accent color for dark themes. Overrides color. |
When a facet omits all three color fields, Polytoken generates a deterministic
accent from the facet name for both light and dark themes. The two generated
colors share the same hue but differ in lightness so each reads against its
background.
| polytoken.autonomous_hint | string | none | Guidance for the autonomous permissions classifier. When Autonomous mode evaluates a tool call in this facet, Polytoken adds this text to the classifier’s context to steer its allow, ask, and deny decisions. |
| polytoken.compaction_hint | string | none | Guidance Polytoken uses when summarizing the conversation during compaction. When set, this text helps the summarization preserve the context most relevant to this facet’s purpose, so the compacted summary retains what matters for the facet’s work. |
| polytoken.facet_transitions.<target>.allowed | boolean | open | Whether this facet may switch to <target>. With no transitions declared, every switch is allowed. |
| polytoken.facet_transitions.<target>.condition | string | none | When set, switching to <target> first asks you to confirm, showing this text. |
Tool and skill access notes
Section titled “Tool and skill access notes”switch_facet is a regular tool. List it in polytoken.tools to let a facet
switch to another facet. The shipped execute facet includes it; plan leaves
it out, so the model cannot leave planning on its own and uses handoff_plan to
transition the work to you. handoff_plan submits the active plan for your
approval; you can implement it in a fresh context or in the current one, or
cancel. The shipped plan facet runs the built-in plan-reviewer before
handing off as a recommendation, not a requirement. You decide at the approval
step whether to proceed, and you can skip review entirely. The reviewer
evaluates the current saved plan, so its findings correspond to the version
you are approving.
Leaving polytoken.skills_allow out means no restriction, so the facet gets every
skill. Once you write a non-empty list, though, it is a commitment: if your
patterns match no skills, through a misspelled name or an empty tag! group,
Polytoken fails the facet’s skill access closed rather than reverting to allow-all.
A non-empty polytoken.skills_allow that matches nothing leaves the facet with no
skills.
The tag!ALL and tag!ALL_MCP shorthands belong in polytoken.tools. The
lowercase tag!<name> form is for skills only, in polytoken.skills_allow and
polytoken.skills_deny, and Polytoken rejects it in a tool list.
Loading changes
Section titled “Loading changes”Polytoken loads facets when it starts and when you reload its configuration. Editing a facet file does not take effect until then.