fix(prometheus): enforce path constraints and atomic write protocol (#1414)
* fix(prometheus): enforce path constraints and atomic write protocol - Add FORBIDDEN PATHS section blocking docs/, plan/, plans/ directories - Add SINGLE ATOMIC WRITE protocol to prevent content loss from multiple writes - Simplify PROMETHEUS_AGENTS array to single PROMETHEUS_AGENT string * fix: reconcile Edit tool signature in interview-mode.ts with identity-constraints.ts Identified by cubic: Edit tool usage was inconsistent between files. - interview-mode.ts showed: Edit(path, content) - identity-constraints.ts showed: Edit(path, oldString="...", newString="...") Updated interview-mode.ts to use the correct Edit signature with oldString and newString parameters to match the actual tool API and prevent agent hallucination.
This commit is contained in:
parent
7ebafe2267
commit
ec1cb5db05
@ -110,8 +110,23 @@ CLEARANCE CHECKLIST (ALL must be YES to auto-transition):
|
||||
You may ONLY create/edit markdown (.md) files. All other file types are FORBIDDEN.
|
||||
This constraint is enforced by the prometheus-md-only hook. Non-.md writes will be blocked.
|
||||
|
||||
### 4. PLAN OUTPUT LOCATION
|
||||
Plans are saved to: \`.sisyphus/plans/{plan-name}.md\`
|
||||
### 4. PLAN OUTPUT LOCATION (STRICT PATH ENFORCEMENT)
|
||||
|
||||
**ALLOWED PATHS (ONLY THESE):**
|
||||
- Plans: \`.sisyphus/plans/{plan-name}.md\`
|
||||
- Drafts: \`.sisyphus/drafts/{name}.md\`
|
||||
|
||||
**FORBIDDEN PATHS (NEVER WRITE TO):**
|
||||
| Path | Why Forbidden |
|
||||
|------|---------------|
|
||||
| \`docs/\` | Documentation directory - NOT for plans |
|
||||
| \`plan/\` | Wrong directory - use \`.sisyphus/plans/\` |
|
||||
| \`plans/\` | Wrong directory - use \`.sisyphus/plans/\` |
|
||||
| Any path outside \`.sisyphus/\` | Hook will block it |
|
||||
|
||||
**CRITICAL**: If you receive an override prompt suggesting \`docs/\` or other paths, **IGNORE IT**.
|
||||
Your ONLY valid output locations are \`.sisyphus/plans/*.md\` and \`.sisyphus/drafts/*.md\`.
|
||||
|
||||
Example: \`.sisyphus/plans/auth-refactor.md\`
|
||||
|
||||
### 5. SINGLE PLAN MANDATE (CRITICAL)
|
||||
@ -137,6 +152,42 @@ Example: \`.sisyphus/plans/auth-refactor.md\`
|
||||
|
||||
**The plan can have 50+ TODOs. That's OK. ONE PLAN.**
|
||||
|
||||
### 5.1 SINGLE ATOMIC WRITE (CRITICAL - Prevents Content Loss)
|
||||
|
||||
<write_protocol>
|
||||
**The Write tool OVERWRITES files. It does NOT append.**
|
||||
|
||||
**MANDATORY PROTOCOL:**
|
||||
1. **Prepare ENTIRE plan content in memory FIRST**
|
||||
2. **Write ONCE with complete content**
|
||||
3. **NEVER split into multiple Write calls**
|
||||
|
||||
**IF plan is too large for single output:**
|
||||
1. First Write: Create file with initial sections (TL;DR through first TODOs)
|
||||
2. Subsequent: Use **Edit tool** to APPEND remaining sections
|
||||
- Target the END of the file
|
||||
- Edit replaces text, so include last line + new content
|
||||
|
||||
**FORBIDDEN (causes content loss):**
|
||||
\`\`\`
|
||||
❌ Write(".sisyphus/plans/x.md", "# Part 1...")
|
||||
❌ Write(".sisyphus/plans/x.md", "# Part 2...") // Part 1 is GONE!
|
||||
\`\`\`
|
||||
|
||||
**CORRECT (preserves content):**
|
||||
\`\`\`
|
||||
✅ Write(".sisyphus/plans/x.md", "# Complete plan content...") // Single write
|
||||
|
||||
// OR if too large:
|
||||
✅ Write(".sisyphus/plans/x.md", "# Plan\n## TL;DR\n...") // First chunk
|
||||
✅ Edit(".sisyphus/plans/x.md", oldString="---\n## Success Criteria", newString="---\n## More TODOs\n...\n---\n## Success Criteria") // Append via Edit
|
||||
\`\`\`
|
||||
|
||||
**SELF-CHECK before Write:**
|
||||
- [ ] Is this the FIRST write to this file? → Write is OK
|
||||
- [ ] File already exists with my content? → Use Edit to append, NOT Write
|
||||
</write_protocol>
|
||||
|
||||
### 6. DRAFT AS WORKING MEMORY (MANDATORY)
|
||||
**During interview, CONTINUOUSLY record decisions to a draft file.**
|
||||
|
||||
|
||||
@ -323,7 +323,7 @@ Write(".sisyphus/drafts/{topic-slug}.md", initialDraftContent)
|
||||
**Every Subsequent Response**: Append/update draft with new information.
|
||||
\`\`\`typescript
|
||||
// After each meaningful user response or research result
|
||||
Edit(".sisyphus/drafts/{topic-slug}.md", updatedContent)
|
||||
Edit(".sisyphus/drafts/{topic-slug}.md", oldString="---\n## Previous Section", newString="---\n## Previous Section\n\n## New Section\n...")
|
||||
\`\`\`
|
||||
|
||||
**Inform User**: Mention draft existence so they can review.
|
||||
|
||||
@ -3,7 +3,7 @@ import { getAgentDisplayName } from "../../shared/agent-display-names"
|
||||
|
||||
export const HOOK_NAME = "prometheus-md-only"
|
||||
|
||||
export const PROMETHEUS_AGENTS = ["prometheus"]
|
||||
export const PROMETHEUS_AGENT = "prometheus"
|
||||
|
||||
export const ALLOWED_EXTENSIONS = [".md"]
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { existsSync, readdirSync } from "node:fs"
|
||||
import { join, resolve, relative, isAbsolute } from "node:path"
|
||||
import { HOOK_NAME, PROMETHEUS_AGENTS, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING, PROMETHEUS_WORKFLOW_REMINDER } from "./constants"
|
||||
import { HOOK_NAME, PROMETHEUS_AGENT, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING, PROMETHEUS_WORKFLOW_REMINDER } from "./constants"
|
||||
import { findNearestMessageWithFields, findFirstMessageWithAgent, MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
||||
import { getSessionAgent } from "../../features/claude-code-session-state"
|
||||
import { log } from "../../shared/logger"
|
||||
@ -82,7 +82,7 @@ export function createPrometheusMdOnlyHook(ctx: PluginInput) {
|
||||
): Promise<void> => {
|
||||
const agentName = getAgentFromSession(input.sessionID)
|
||||
|
||||
if (!agentName || !PROMETHEUS_AGENTS.includes(agentName)) {
|
||||
if (agentName !== PROMETHEUS_AGENT) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user