diff --git a/docs/cn/design/decisions/0004-default-readonly-persona.md b/docs/cn/design/decisions/0004-default-readonly-persona.md new file mode 100644 index 00000000..8cf0ca0a --- /dev/null +++ b/docs/cn/design/decisions/0004-default-readonly-persona.md @@ -0,0 +1,345 @@ +# ADR-0004: 默认只读 Persona + +## 状态 + +提案中 — 2026-06-17. + +## 背景 + +San 目前没有默认 Persona。当没有明确选择 Persona 时,系统提示词由内置默认部分拼装, +所有工具均可用 —— 包括文件编辑、Git 提交、Shell 命令等写操作。 + +这意味着一个全新的 San 会话没有任何只读保护。常见的初始工作流 —— +"解释这段代码"、"这个测试为什么失败"、"这个包是做什么的" —— 本不需要写权限, +但用户却得到了全部权限。 + +目标是提供一个只读 Persona,具有两个核心优势: + +1. **环境保护**:Persona 在物理上无法修改用户的工作环境 —— + 不能写文件、不能变更 Git 状态、不能安装包。这消除了意外或幻觉产生的破坏性操作 + 损坏项目状态的风险。 + +2. **节省 Token**:默认系统提示词携带了大量与只读工作无关的内容 —— + 工程方法论、提交规范、Git 协议、任务管理规则、针对破坏性操作的安全约束。 + 只读 Persona 用最简化的、针对性的 prompt 替换全部三个散文部分, + 删除所有不服务于阅读、分析和解释的文本。 + +## 核心模型 + +一个**"readonly" Persona**,以标准 Persona 目录形式分发。与早期草案中回退到 +San 默认 `behavior.md` 不同,此 Persona **覆盖全部三个散文部分**, +每个部分都采用最简化、针对只读场景的内容。San 默认的工程提示词中 +没有任何内容残留 —— 模型只看到只读助手所需的内容。 + +``` +.san/personas/readonly/ +├── system/ +│ ├── identity.md ← 最简:"你是一个只读助手" +│ ├── behavior.md ← 最简:如何分析、解释、调试 +│ └── rules.md ← 最简:只读约束 + 被要求写操作时的回应方式 +└── settings.json ← permissions.deny 阻止写工具(强制执行层) +``` + +该 Persona 是普通的 Persona 目录 —— 无需任何引擎改动。它发布在独立仓库 +[github.com/genai-io/readonly-persona](https://github.com/genai-io/readonly-persona), +用户通过 `git clone` 安装到 `~/.san/personas/readonly/`。主仓库可以包含 +轻量引用或示例来展示模式并链接到独立仓库,但不负责维护完整的 Persona。 + +每个部分都刻意精简。设计原则:**如果一句话无助于模型阅读、分析或解释,就删掉。** + +### Prompt 内容 + +`system/identity.md`: + +```markdown +你是一个只读助手。你回答问题、分析代码、调试环境。 +你绝不修改文件、代码、配置或系统状态。 +``` + +`system/behavior.md`: + +```markdown +## 分析 + +分析代码时:先通读相关文件,系统性地追踪逻辑,在给出修复建议前找准根因。 +解释时:清晰简洁。使用具体的引用(文件路径、行号、函数名)。 + +## 调试 + +调试时:检查日志、检查状态、追踪执行路径。 +运行只读诊断命令(ls、cat、grep、git log、git status、git diff)。 +先定位问题,再解释问题。 + +## 沟通 + +- 回答所问的问题 —— 不跑题。 +- 直接回答优于铺陈叙述。 +- 如需更多上下文,追问而非猜测。 +``` + +`system/rules.md`: + +```markdown +## 只读约束 + +你当前处于只读模式。以下操作被禁止: + +- 创建、修改或删除文件 +- 执行会写入文件系统的 Shell 命令 +- 改变仓库状态的 Git 操作(commit、push、merge、rebase、tag 等) +- 安装包、依赖或系统软件 +- 修改任何配置 + +## 你可以做什么 + +- 读取文件和搜索代码库 +- 回答关于代码、架构、设计和规范的问题 +- 分析 Bug、追踪执行路径、解释行为 +- 运行只读 Shell 命令:ls、cat、grep、find、git log、git status、 + git diff、git show、git blame 等 +- 调试环境和诊断问题 + +## 被要求写操作时 + +说明你处于只读模式,无法执行写操作。 +建议通过 `/persona ` 切换到有写权限的 Persona。 +``` + +### 为什么覆盖 behavior.md + +San 的默认 `behavior.md` 包含了约 30 行工程方法论:工作习惯(先读设计文档、 +遵循分层架构、编写单元测试、运行 lint)、沟通风格(报告改了哪些文件、关联 PR)、 +范围控制。这些对只读会话完全无用。用最简化的替代内容覆盖它可以回收这些 Token, +并给模型提供真正与阅读分析相关的指导。 + +### 为什么覆盖 rules.md + +San 的默认 `rules.md` 集成了安全策略、工具协议、任务/Git 规范、破坏性命令的安全规则、 +以及各 Provider 的特殊行为。对只读会话而言这些都是无效负载 —— +Persona 根本没有写能力,关于提交信息格式、PR 规范、`--no-verify` 的规则纯属噪音。 +最简化的替代内容仅保留只读约束。 + +### Token 节省估算 + +San 内置默认值与只读替代内容的行数对比: + +| 部分 | 默认(约行数) | 只读(约行数) | 节省 | +|---|---|---|---| +| `identity` | ~3 | ~2 | 较少 | +| `behavior` | ~30 | ~12 | ~60% | +| `rules` | ~120 | ~18 | ~85% | +| **合计** | **~153** | **~32** | **~80%** | + +这些节省对每轮包含系统提示词的推理都生效(包括缓存未命中时)。 +同时,因为 Persona 提示词属于 prompt-cache 前缀的一部分,更小的提示词也意味着更小的缓存条目。 + +### 允许的只读操作 + +| 类别 | 示例 | +|---|---| +| 读取文件 | `Read`、`cat`、`head`、`tail` | +| 搜索 | `grep`、`find`、`git grep` | +| Git 读取 | `git log`、`git status`、`git diff`、`git show`、`git blame` | +| 分析 | 代码审查、架构分析、Bug 追踪 | +| 问答 | 关于代码、设计、规范的问题 | +| 调试 | 追踪错误、检查日志、检查环境状态 | + +### 禁止的写操作 + +| 类别 | 示例 | +|---|---| +| 文件写入 | `Edit`、`Write`、Shell 重定向(`>`、`>>`)、`tee` | +| 文件删除 | `rm`、`rmdir`、`shred` | +| 文件移动/复制 | `mv`、`cp`、`dd` | +| 文件创建 | `touch`、`mkdir` | +| 权限修改 | `chmod`、`chown` | +| Git 写入 | `commit`、`push`、`merge`、`rebase`、`tag`、`am`、`cherry-pick`、`stash` | +| 包安装 | `go install`、`npm install`、`pip install`、`make install`、`brew install` | +| 破坏性操作 | `rm -rf`、force push、`git reset --hard` | + +## 决策 + +### D1:以 Persona 目录形式分发,而非内置于二进制文件 + +Readonly Persona 是标准的 Persona 目录 —— 与任何用户创建的 Persona 格式完全一致。 +无需引擎改动、无需 `embed.FS`、无需特殊加载路径。现有 Persona 系统已支持其全部需求: +`system/{identity,behavior,rules}.md` 加上一个包含 `permissions.deny` 的 `settings.json`。 + +它发布在 [github.com/genai-io/readonly-persona](https://github.com/genai-io/readonly-persona)。 +用户通过 `git clone` 安装到 `~/.san/personas/readonly/` 或 +`.san/personas/readonly/`。安装后 `/persona readonly` 即可正常切换, +并在 `/persona` 选择器中可见。 + +San 主仓库可以包含轻量引用(README 指针或最小示例)来展示模式及链接到独立仓库, +但不负责维护完整的 Persona。 + +### D2:暂不作为系统默认值 + +Readonly Persona **默认可用**,但未选择 Persona 时的系统默认行为暂时不变 —— +保持全权限访问。 + +用户通过 `/persona readonly` 或在设置中配置 `persona: readonly` 来主动选择。 +是否将其设为系统默认值,留待评估迁移影响后再决定。 + +**理由**:改变默认 Persona 会打破用户对 San 会话启动即有全工具访问的预期。 +这需要充分的社区沟通,而不仅仅是代码变更。先交付 Persona 本身,待社区达成共识后再调整默认值。 + +### D3:覆盖全部三个散文部分 —— 不回退到默认值 + +Persona 提供自己的 `identity.md`、`behavior.md` 和 `rules.md`。 +不使用任何 San 的默认散文部分。这是有意为之: + +- **Token 效率**:默认部分承载了大量工程工作流的内容。去掉它们可节省系统提示词中 + 约 80% 的散文(见上文估算)。 +- **信号清晰**:模型只收到与只读任务相关的指令。没有工程方法论、Git 协议或提交规范 + 稀释提示词。 +- **无死规则**:关于 `--no-verify`、提交信息格式、分支命名、PR 描述的规则 + 在 Persona 无法写入的情况下纯属噪音。 + +每个部分保持最简 —— 设计目标是能产生正确只读行为的最短提示词。 + +### D4:双层防御 —— permissions.deny(强制)+ rules.md(建议) + +与 Persona 模型的设计哲学一致,采用双层防御: + +1. **`settings.json` 的 `permissions.deny`** —— *强制执行*层。在权限引擎层 + 阻止写工具调用,模型根本收不到这些工具。根据 Persona 权限合并语义, + 该 deny 列表不能被更低层(用户/项目设置)放松。 + +2. **`system/rules.md`** —— *建议*层。模型每轮阅读的自然语言约束。 + 即使某个工具漏过了 deny 列表,模型也被引导远离写操作。 + +```json +{ + "description": "只读 Persona — 回答问题、分析代码、调试环境。不可写入。", + "skills": {}, + "agents": [], + "disabledTools": {}, + "permissions": { + "defaultMode": "default", + "deny": [ + "Edit", + "Write", + "Bash(rm:*)", + "Bash(rmdir:*)", + "Bash(mv:*)", + "Bash(cp:*)", + "Bash(touch:*)", + "Bash(mkdir:*)", + "Bash(dd:*)", + "Bash(shred:*)", + "Bash(chmod:*)", + "Bash(chown:*)", + "Bash(git commit:*)", + "Bash(git push:*)", + "Bash(git merge:*)", + "Bash(git tag:*)", + "Bash(git rebase:*)", + "Bash(git reset:*)", + "Bash(git am:*)", + "Bash(git cherry-pick:*)", + "Bash(git stash:*)", + "Bash(go install:*)", + "Bash(npm install:*)", + "Bash(yarn add:*)", + "Bash(pip install:*)", + "Bash(pip3 install:*)", + "Bash(brew install:*)", + "Bash(make install:*)", + "Bash(curl * | *)", + "Bash(wget -O:*)" + ] + } +} +``` + +### D5:Git hooks —— 已评估,暂缓实施 + +Git hooks 曾被考虑作为第三层防御,但决定暂缓: + +- **优点**:pre-commit hook 可以在 Git 层面阻止提交,独立于 San 的工具权限体系, + 实现纵深防御。 +- **缺点**:hooks 仅覆盖 Git 操作,不覆盖一般文件写入。权限引擎已经覆盖了全部工具面。 + hooks 需要按仓库单独安装。活跃 Persona 是 San 运行时概念 —— hooks 没有原生的查询方式。 + +**后续如需**:提供一个 `san persona current` CLI 命令来输出当前活跃 Persona。 +pre-commit hook 可以调用它: + +```bash +#!/bin/bash +if [ "$(san persona current 2>/dev/null)" = "readonly" ]; then + echo "只读 Persona 模式下禁止提交。" + exit 1 +fi +``` + +这属于未来扩展,不在本 ADR 实施范围内。 + +### D6:子 Agent 继承 + +当 Readonly Persona 通过 Agent 工具创建子 Agent 时,只读约束通过权限层传播 —— +子 Agent 使用相同的 effective settings overlay。无需特殊的子 Agent 处理逻辑。 + +## 实现计划 + +### 阶段一:Persona 目录 + +创建 Persona 目录: + +``` +.san/personas/readonly/ +├── system/ +│ ├── identity.md +│ ├── behavior.md +│ └── rules.md +└── settings.json +``` + +按上文 [Prompt 内容](#prompt-内容) 编写三个 prompt 文件(`identity.md`、`behavior.md`、 +`rules.md`),以及包含 [D4](#d4双层防御--permissionsdeny强制--rulesmd建议) 中 deny 列表的 `settings.json`。 + +### 阶段二:发布为独立仓库 + +已发布在 [github.com/genai-io/readonly-persona](https://github.com/genai-io/readonly-persona)。 +用户通过以下命令安装: + +```bash +git clone https://github.com/genai-io/readonly-persona.git ~/.san/personas/readonly +``` + +### 阶段三:主仓库引用(可选) + +可选择在 San 主仓库中添加指针或最小示例,以展示 Persona 模式并链接到独立仓库 —— +不将完整 Persona 的维护责任纳入核心。 + +## 影响 + +### 正面 + +- **环境保护**:工作目录免受意外修改。无文件损坏、无意外的 Git 变更、 + 无幻觉导致的破坏性命令。 +- **Token 节省(约 80%)**:Persona 将约 153 行默认系统提示词散文替换为约 32 行 + 只读专用内容。更小的提示词在每轮推理中节省 Token,同时减小 prompt-cache 体积。 +- **信号清晰**:模型只收到与阅读、分析和解释相关的指令 —— + 没有工程方法论或 Git 协议稀释提示词。 +- **安全的起点**:用户可以安全地探索、提问和分析代码,无需担心意外写入。 +- **显式提权**:写操作需要主动切换 Persona,使用户意图明确清晰。 +- **无需新增基础设施**:复用现有 Persona 系统、权限引擎和 settings overlay, + 零新增机制。 + +### 负面 / 代价 + +- **Deny 列表覆盖度**:deny 列表必须显式枚举所有写工具和命令模式。 + 未来添加的新的可写工具(或不符合 deny 模式的创造性 Shell 命令)可能漏过。 + 建议层的 `rules.md` 可以缓解但不能根除这个缺陷。 +- **非沙箱**:San Persona 运行在用户的信任级别上 —— 这是护栏,不是安全边界。 + 有决心的用户或插件可以绕过。 +- **用户困惑**:期望开箱就能编辑文件的用户切换到 readonly 模式后会被阻止。 + Persona 选择器必须清晰展示每个 Persona 的能力边界。 + +## 参考资料 + +- [`persona.md`](../../concepts/persona.md) — Persona 系统设计 +- [`permission-model.md`](../../concepts/permission-model.md) — 权限引擎 +- [`ADR-0001`](0001-layered-package-architecture.md) — 分层包架构 +- [`ADR-0002`](0002-autonomous-dev-management.md) — 自主开发团队(Persona 配置示例) diff --git a/docs/design/decisions/0004-default-readonly-persona.md b/docs/design/decisions/0004-default-readonly-persona.md new file mode 100644 index 00000000..6e7f8e94 --- /dev/null +++ b/docs/design/decisions/0004-default-readonly-persona.md @@ -0,0 +1,384 @@ +# ADR-0004: Default Read-Only Persona + +## Status + +Proposed — 2026-06-17. + +## Context + +San ships without a default persona. When no persona is selected, the system +prompt is assembled from built-in defaults and all tools are available — +including write operations like file editing, git commits, and shell commands. + +This means a fresh San session has no read-only guardrails. Common starting +workflows — "explain this code," "why is this test failing," "what does this +package do" — don't need write access, but the user gets it anyway. + +The goal is to ship a readonly persona with two core benefits: + +1. **Environment protection**: The persona physically cannot modify the user's + working environment — no file writes, no git mutations, no package installs. + This eliminates the risk of an accidental or hallucinated destructive + operation corrupting the project state. + +2. **Token savings**: The default system prompt carries substantial weight from + parts that are irrelevant to read-only work — engineering methodology, + commit conventions, git protocols, task management rules, and safety + constraints for destructive operations. A read-only persona replaces all + three prose parts with minimal, purpose-specific prompts, dropping every + word that does not serve reading, analyzing, or explaining. + +## Core Model + +A **"readonly" persona** distributed as a standard persona folder. Unlike the +earlier draft that fell back to San's default `behavior.md`, this persona +**overrides all three prose parts** with minimal, read-only-specific content. +Nothing from San's default engineering prompt survives — the model only sees +what a read-only assistant needs. + +``` +.san/personas/readonly/ +├── system/ +│ ├── identity.md ← minimal: "You are a read-only assistant" +│ ├── behavior.md ← minimal: how to analyze, explain, debug +│ └── rules.md ← minimal: read-only constraint + what to do when asked to write +└── settings.json ← permissions.deny for write tools (enforcement layer) +``` + +The persona is a regular persona directory — no engine changes required. It is +published at [github.com/genai-io/readonly-persona](https://github.com/genai-io/readonly-persona). +Users install it by cloning into `~/.san/personas/readonly/`. The main repo may +carry a lightweight reference to demonstrate the pattern and link to the +standalone repo, but does not own or maintain the full persona. + +Each part is intentionally short. The design principle is: **if a sentence +does not help the model read, analyze, or explain, cut it.** + +### Prompt content + +`system/identity.md`: + +```markdown +You are a read-only assistant. You answer questions, analyze code, and debug +environments. You never modify files, code, config, or system state. +``` + +`system/behavior.md`: + +```markdown +## Analysis + +When analyzing code: read the relevant files first, trace the logic +systematically, identify root causes before suggesting fixes. When +explaining: be clear and concise. Use concrete references (file paths, +line numbers, function names). + +## Debugging + +When debugging: check logs, inspect state, trace execution paths. Run +read-only diagnostic commands (ls, cat, grep, git log, git status, +git diff). Isolate the problem before explaining it. + +## Communication + +- Answer the question asked — no digressions. +- Prefer direct answers over exposition. +- If you need more context, ask rather than guess. +``` + +`system/rules.md`: + +```markdown +## Read-only constraint + +You are in read-only mode. The following are blocked: + +- Creating, modifying, or deleting files +- Shell commands that write to the filesystem +- Git operations that change repository state (commit, push, merge, rebase, + tag, etc.) +- Installing packages, dependencies, or system software +- Modifying any configuration + +## What you can do + +- Read files and search codebases +- Answer questions about code, architecture, design, and conventions +- Analyze bugs, trace execution paths, explain behavior +- Run read-only shell commands: ls, cat, grep, find, git log, git status, + git diff, git show, git blame, and similar +- Debug environments and diagnose issues + +## If asked to write + +Explain that you are in read-only mode and cannot perform write operations. +Suggest switching to a persona with write access via `/persona `. +``` + +### Why override behavior.md + +San's default `behavior.md` carries ~30 lines of engineering methodology: +work habits (read design docs, follow layered architecture, write unit tests, +run lint), communication style (report what you changed, link PRs), and scope +discipline. None of this applies to a read-only session. Overriding it with a +minimal alternative recovers those tokens and gives the model guidance +actually relevant to reading and analyzing. + +### Why override rules.md + +San's default `rules.md` bundles policy, tool protocols, task/git conventions, +safety rules for destructive commands, and provider-specific quirks. For a +read-only session these are dead weight — the persona cannot write anyway, so +rules about commit messages, PR conventions, and `--no-verify` are noise. +A minimal replacement keeps only the readonly constraint. + +### Token savings estimate + +Rough line counts for San's built-in defaults vs. the readonly replacements: + +| Part | Default (approx. lines) | Readonly (approx. lines) | Savings | +|---|---|---|---| +| `identity` | ~3 | ~2 | small | +| `behavior` | ~30 | ~12 | ~60% | +| `rules` | ~120 | ~18 | ~85% | +| **Total** | **~153** | **~32** | **~80%** | + +These savings apply to every turn where the system prompt is included +(including cache misses), and because the persona prompt is part of the +prompt-cache prefix, the smaller prompt also means a smaller cache entry. + +### Read-only operations (allowed) + +| Category | Examples | +|---|---| +| Read files | `Read`, `cat`, `head`, `tail` | +| Search | `grep`, `find`, `git grep` | +| Git read | `git log`, `git status`, `git diff`, `git show`, `git blame` | +| Analyze | code review, architecture analysis, bug tracing | +| Answer | questions about code, design, conventions | +| Debug | trace errors, inspect logs, check environment state | + +### Write operations (blocked) + +| Category | Examples | +|---|---| +| File write | `Edit`, `Write`, shell redirect (`>`, `>>`), `tee` | +| File delete | `rm`, `rmdir`, `shred` | +| File move/copy | `mv`, `cp`, `dd` | +| File create | `touch`, `mkdir` | +| Permissions | `chmod`, `chown` | +| Git write | `commit`, `push`, `merge`, `rebase`, `tag`, `am`, `cherry-pick`, `stash` | +| Package install | `go install`, `npm install`, `pip install`, `make install`, `brew install` | +| Destructive | `rm -rf`, force push, `git reset --hard` | + +## Decision + +### D1: Ship as a persona folder, not built into the binary + +The readonly persona is a standard persona directory — the same shape as any +user-created persona. No engine changes, no `embed.FS`, no special loading +path. The existing persona system already supports everything it needs: +`system/{identity,behavior,rules}.md` plus a `settings.json` with +`permissions.deny`. + +It is published at +[github.com/genai-io/readonly-persona](https://github.com/genai-io/readonly-persona). +Users install it by cloning into `~/.san/personas/readonly/` or +`.san/personas/readonly/`. Once installed, `/persona readonly` works and it +appears in the `/persona` selector. + +The main San repo may carry a lightweight reference (a README pointer or a +minimal example) to demonstrate the pattern and link to the standalone repo, +but does not own or maintain the full persona. + +### D2: Not the system default (initially) + +The readonly persona is **available by default**, but the system default +behavior (when no persona is selected) remains unchanged for now — full access. + +Users opt into readonly mode via `/persona readonly` or by configuring +`persona: readonly` in their settings. Making it the system default is +deferred to a future decision after migration impact is assessed. + +**Rationale**: Changing the default persona breaks the expectation that +a fresh San session has full tool access. This is a migration that requires +communication, not just code. Ship the persona first; flip the default +later if the community agrees. + +### D3: Override all three prose parts — no default fallback + +The persona provides its own `identity.md`, `behavior.md`, and `rules.md`. +None of San's default prose parts are used. This is deliberate: + +- **Token efficiency**: The default parts carry substantial content for + engineering workflows. Dropping them saves ~80% of the system prompt prose + (see estimate above). +- **Signal clarity**: The model receives only instructions relevant to its + read-only task. No engineering methodology, git protocols, or commit + conventions dilute the prompt. +- **No dead rules**: Rules about `--no-verify`, commit message format, branch + naming, and PR descriptions are noise when the persona cannot write. + +Each part is kept minimal — the design goal is the shortest prompt that +produces correct read-only behavior. + +### D4: Enforcement — permissions.deny (primary) + rules.md (advisory) + +Two-layer defense, consistent with the persona model's design philosophy: + +1. **`settings.json` permissions.deny** — the *enforcement* layer. Blocks + write tools at the permission engine before they reach the model. Cannot + be loosened by lower layers (user/project settings), per the persona + permission merge semantics. + +2. **`system/rules.md`** — the *advisory* layer. Natural-language constraints + the model reads each turn. Guides the model away from write operations + even if a tool slips through the deny list. + +```json +{ + "description": "Read-only persona — answers questions, analyzes code, debugs environments. Cannot write.", + "skills": {}, + "agents": [], + "disabledTools": {}, + "permissions": { + "defaultMode": "default", + "deny": [ + "Edit", + "Write", + "Bash(rm:*)", + "Bash(rmdir:*)", + "Bash(mv:*)", + "Bash(cp:*)", + "Bash(touch:*)", + "Bash(mkdir:*)", + "Bash(dd:*)", + "Bash(shred:*)", + "Bash(chmod:*)", + "Bash(chown:*)", + "Bash(git commit:*)", + "Bash(git push:*)", + "Bash(git merge:*)", + "Bash(git tag:*)", + "Bash(git rebase:*)", + "Bash(git reset:*)", + "Bash(git am:*)", + "Bash(git cherry-pick:*)", + "Bash(git stash:*)", + "Bash(go install:*)", + "Bash(npm install:*)", + "Bash(yarn add:*)", + "Bash(pip install:*)", + "Bash(pip3 install:*)", + "Bash(brew install:*)", + "Bash(make install:*)", + "Bash(curl * | *)", + "Bash(wget -O:*)" + ] + } +} +``` + +### D5: Git hooks — evaluated and deferred + +Git hooks were considered as a third enforcement layer but are deferred: + +- **Pro**: A pre-commit hook could block commits at the Git level, independent + of San's tool permissions. This is defense-in-depth. +- **Con**: Hooks only cover Git operations, not general file writes. The + permission engine already covers the full tool surface. Hooks require + explicit installation per repository. The active persona is a San runtime + concept — hooks have no native way to query it. + +**If needed later**: Ship a `san persona current` CLI command that prints the +active persona. A pre-commit hook can call it: + +```bash +#!/bin/bash +if [ "$(san persona current 2>/dev/null)" = "readonly" ]; then + echo "Commits are blocked in readonly persona mode." + exit 1 +fi +``` + +This is a future extension; not part of this ADR. + +### D6: Subagent inheritance + +When the readonly persona spawns subagents (via the Agent tool), the readonly +constraint propagates through the permission layer — subagents use the same +effective settings overlay. No special subagent handling is needed. + +## Implementation + +### Phase 1 — Persona folder + +Create the persona directory: + +``` +.san/personas/readonly/ +├── system/ +│ ├── identity.md +│ ├── behavior.md +│ └── rules.md +└── settings.json +``` + +Write the three prompt files (`identity.md`, `behavior.md`, `rules.md`) as +defined in [Prompt content](#prompt-content) above, and `settings.json` with +the deny list from [D4](#d4-enforcement--permissionsdeny-primary--rulesmd-advisory). + +### Phase 2 — Publish as standalone repo + +Published at [github.com/genai-io/readonly-persona](https://github.com/genai-io/readonly-persona). +Users install via: + +```bash +git clone https://github.com/genai-io/readonly-persona.git ~/.san/personas/readonly +``` + +### Phase 3 — In-repo reference (optional) + +Optionally add a pointer or minimal example in the main San repo to +demonstrate the persona pattern and link to the standalone repo — without +committing core to maintaining the full persona. + +## Consequences + +### Positive + +- **Environment protection**: The working directory is safe from accidental + modification. No file corruption, no unintended git mutations, no + hallucinated destructive commands. +- **Token savings (~80%)**: The persona replaces ~153 lines of default + system prompt prose with ~32 lines of read-only-specific content. The + smaller prompt saves tokens on every turn and reduces prompt-cache size. +- **Signal clarity**: The model only receives instructions relevant to + reading, analyzing, and explaining — no engineering methodology or git + protocols dilute the prompt. +- **Safe starting point**: Users can explore, ask questions, and analyze code + without risk of accidental writes. +- **Intentional escalation**: Write operations require a conscious persona + switch — making the user's intent explicit. +- **No new infrastructure**: Reuses the existing persona system, permission + engine, and settings overlay — no new enforcement mechanisms. + +### Negative / costs + +- **Deny list coverage**: The deny list must explicitly enumerate every write + tool and command pattern. A new write-capable tool added to San (or a + creative shell command not matching the deny patterns) could slip through. + The advisory `rules.md` mitigates but does not eliminate this gap. +- **Not a sandbox**: San personas operate at the user's trust level — this is + a guardrail, not a security boundary. A determined user or a plugin can + bypass it. +- **User confusion**: Users who expect to edit files out of the box will be + blocked when they switch to readonly mode. The persona selector must + clearly communicate what each persona allows. + +## References + +- [`persona.md`](../../concepts/persona.md) — persona system design +- [`permission-model.md`](../../concepts/permission-model.md) — permission engine +- [`ADR-0001`](0001-layered-package-architecture.md) — layered package architecture +- [`ADR-0002`](0002-autonomous-dev-management.md) — autonomous dev team (persona config examples)