Commit 0aa91534837e

Vincent Demeester <vincent@sbr.pm>
2026-02-05 17:05:18
feat(path-validator): add filename format enforcement
Added requiredFilenameFormat, formatDescription, and formatExample fields to path policies. Policies can now block file writes that don't match expected naming conventions (e.g., YYYY-MM-DD-desc.md for sessions, kebab-case.md for plans). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e3f0e3b
Changed files (2)
dots
config
pi
agent
extensions
path-validator
dots/config/ai/path-policies.json
@@ -1,19 +1,29 @@
 {
   "policies": [
     {
-      "name": "ai-sessions",
+      "name": "ai-sessions-location",
       "description": "Session files should go to unified AI storage",
       "filenamePattern": ".*session.*\\.md$",
       "blockedPaths": [
         "~/.config/claude/history/sessions/",
         "~/.claude/history/sessions/"
       ],
-      "suggestedPath": "~/.local/share/ai/sessions/",
+      "suggestedPath": "~/.local/share/ai/sessions/YYYY-MM/",
       "action": "warn",
       "enabled": true
     },
     {
-      "name": "ai-plans",
+      "name": "ai-sessions-format",
+      "description": "Session files must follow YYYY-MM-DD-description.md format",
+      "pathPattern": ".*/ai/sessions/.*\\.md$",
+      "requiredFilenameFormat": "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$",
+      "formatDescription": "YYYY-MM-DD-description.md (lowercase, hyphens only)",
+      "formatExample": "2026-02-05-unified-ai-storage.md",
+      "action": "block",
+      "enabled": true
+    },
+    {
+      "name": "ai-plans-location",
       "description": "Plan files should go to unified AI storage",
       "filenamePattern": ".*plan.*\\.md$",
       "blockedPaths": [
@@ -25,27 +35,57 @@
       "enabled": true
     },
     {
-      "name": "ai-learnings",
+      "name": "ai-plans-format",
+      "description": "Plan files must follow kebab-case.md format",
+      "pathPattern": ".*/ai/plans/.*\\.md$",
+      "requiredFilenameFormat": "^[a-z0-9-]+\\.md$",
+      "formatDescription": "kebab-case.md (lowercase, hyphens, no dates)",
+      "formatExample": "unified-ai-storage-implementation.md",
+      "action": "block",
+      "enabled": true
+    },
+    {
+      "name": "ai-learnings-location",
       "description": "Learning files should go to unified AI storage",
       "filenamePattern": ".*learning.*\\.md$",
       "blockedPaths": [
         "~/.config/claude/history/learnings/"
       ],
-      "suggestedPath": "~/.local/share/ai/learnings/",
+      "suggestedPath": "~/.local/share/ai/learnings/YYYY-MM/",
       "action": "warn",
       "enabled": true
     },
     {
-      "name": "ai-research",
+      "name": "ai-learnings-format",
+      "description": "Learning files must follow YYYY-MM-DD-description.md format",
+      "pathPattern": ".*/ai/learnings/.*\\.md$",
+      "requiredFilenameFormat": "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$",
+      "formatDescription": "YYYY-MM-DD-description.md (lowercase, hyphens only)",
+      "formatExample": "2026-02-05-debugging-timezone-issues.md",
+      "action": "block",
+      "enabled": true
+    },
+    {
+      "name": "ai-research-location",
       "description": "Research files should go to unified AI storage",
       "filenamePattern": ".*research.*\\.md$",
       "blockedPaths": [
         "~/.config/claude/history/research/"
       ],
-      "suggestedPath": "~/.local/share/ai/research/",
+      "suggestedPath": "~/.local/share/ai/research/YYYY-MM/",
       "action": "warn",
       "enabled": true
     },
+    {
+      "name": "ai-research-format",
+      "description": "Research files must follow YYYY-MM-DD-description.md format",
+      "pathPattern": ".*/ai/research/.*\\.md$",
+      "requiredFilenameFormat": "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$",
+      "formatDescription": "YYYY-MM-DD-description.md (lowercase, hyphens only)",
+      "formatExample": "2026-02-05-chrono-node-evaluation.md",
+      "action": "block",
+      "enabled": true
+    },
     {
       "name": "no-secrets-in-repos",
       "description": "Prevent writing secrets to git repositories",
dots/pi/agent/extensions/path-validator/index.ts
@@ -32,6 +32,12 @@ interface PathPolicy {
 	allowedPaths?: string[];
 	// Paths where this file type is blocked
 	blockedPaths?: string[];
+	// Required filename format (regex) - blocks if doesn't match
+	requiredFilenameFormat?: string;
+	// Human-readable description of required format
+	formatDescription?: string;
+	// Example of valid filename
+	formatExample?: string;
 	// Suggested correct path (for redirects)
 	suggestedPath?: string;
 	// Action: "warn" (default), "block", or "suggest"
@@ -220,6 +226,19 @@ export default function (pi: ExtensionAPI) {
 					});
 				}
 			}
+
+			// Check required filename format
+			if (policy.requiredFilenameFormat) {
+				const formatRegex = new RegExp(policy.requiredFilenameFormat);
+				if (!formatRegex.test(filename)) {
+					const formatDesc = policy.formatDescription || policy.requiredFilenameFormat;
+					const example = policy.formatExample ? ` (e.g., ${policy.formatExample})` : "";
+					violations.push({
+						policy,
+						reason: `Filename doesn't match required format: ${formatDesc}${example}`,
+					});
+				}
+			}
 		}
 
 		return {