[object Object]

← back to Cli Printing Press

Annotate PM workflows as read-only (#674)

a880a7f763167f98cd3de2301a2ce9b5e3b6191b · 2026-05-07 02:48:40 -0700 · Trevin Chow

Files touched

Diff

commit a880a7f763167f98cd3de2301a2ce9b5e3b6191b
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Thu May 7 02:48:40 2026 -0700

    Annotate PM workflows as read-only (#674)
---
 internal/generator/generator_test.go               | 29 ++++++++++++++++++++++
 .../generator/templates/workflows/pm_load.go.tmpl  |  1 +
 .../templates/workflows/pm_orphans.go.tmpl         |  1 +
 .../generator/templates/workflows/pm_stale.go.tmpl |  1 +
 4 files changed, 32 insertions(+)

diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index e4ce9254..bca6fce8 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -7159,6 +7159,9 @@ func TestTemplatesEmitReadOnlyAnnotation(t *testing.T) {
 		{"tail.go.tmpl", 1, "polls API GETs, NDJSON to stdout"},
 		{"jobs.go.tmpl", 2, "list and get; prune is omitted (mutates ledger)"},
 		{"channel_workflow.go.tmpl", 2, "status and generated payment-plan are read-only; workflow parent and archive omitted"},
+		{"workflows/pm_stale.go.tmpl", 1, "queries the local store for stale items"},
+		{"workflows/pm_orphans.go.tmpl", 1, "queries the local store for missing fields"},
+		{"workflows/pm_load.go.tmpl", 1, "queries the local store for workload distribution"},
 	}
 
 	for _, tc := range cases {
@@ -7174,6 +7177,32 @@ func TestTemplatesEmitReadOnlyAnnotation(t *testing.T) {
 	}
 }
 
+func TestProjectManagementWorkflowsEmitReadOnlyAnnotations(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("pmworkflows")
+	outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+	gen := New(apiSpec, outputDir)
+	gen.VisionSet = VisionTemplateSet{
+		Store: true,
+		Sync:  true,
+		Workflows: []string{
+			"workflows/pm_stale.go.tmpl",
+			"workflows/pm_orphans.go.tmpl",
+			"workflows/pm_load.go.tmpl",
+		},
+	}
+	require.NoError(t, gen.Generate())
+
+	annotationRE := regexp.MustCompile(`Annotations:\s+map\[string\]string\{"mcp:read-only":\s*"true"\}`)
+	for _, file := range []string{"pm_stale.go", "pm_orphans.go", "pm_load.go"} {
+		data, err := os.ReadFile(filepath.Join(outputDir, "internal", "cli", file))
+		require.NoError(t, err)
+		assert.Len(t, annotationRE.FindAllString(string(data), -1), 1,
+			"%s should emit exactly one mcp:read-only annotation", file)
+	}
+}
+
 // TestSearchTemplateEmitsEmptyJSONEnvelope pins the contract: the
 // generated `search` command in --json (or piped) mode must always emit
 // a valid JSON envelope, including on no matches. Agents pipe stdout
diff --git a/internal/generator/templates/workflows/pm_load.go.tmpl b/internal/generator/templates/workflows/pm_load.go.tmpl
index 68befc5a..cee189a5 100644
--- a/internal/generator/templates/workflows/pm_load.go.tmpl
+++ b/internal/generator/templates/workflows/pm_load.go.tmpl
@@ -29,6 +29,7 @@ person. Helps identify overloaded team members and unbalanced workload.`,
 
   # Output as JSON
   {{.Name}}-pp-cli load --json`,
+		Annotations: map[string]string{"mcp:read-only": "true"},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if dbPath == "" {
 				dbPath = defaultDBPath("{{.Name}}-pp-cli")
diff --git a/internal/generator/templates/workflows/pm_orphans.go.tmpl b/internal/generator/templates/workflows/pm_orphans.go.tmpl
index 6d453fe7..fa1ae764 100644
--- a/internal/generator/templates/workflows/pm_orphans.go.tmpl
+++ b/internal/generator/templates/workflows/pm_orphans.go.tmpl
@@ -26,6 +26,7 @@ such as assignee, project, priority, or labels. Useful for triaging unowned work
 
   # Output as JSON
   {{.Name}}-pp-cli orphans --json`,
+		Annotations: map[string]string{"mcp:read-only": "true"},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if dbPath == "" {
 				dbPath = defaultDBPath("{{.Name}}-pp-cli")
diff --git a/internal/generator/templates/workflows/pm_stale.go.tmpl b/internal/generator/templates/workflows/pm_stale.go.tmpl
index 35157264..acf4e689 100644
--- a/internal/generator/templates/workflows/pm_stale.go.tmpl
+++ b/internal/generator/templates/workflows/pm_stale.go.tmpl
@@ -34,6 +34,7 @@ the specified number of days. Useful for identifying forgotten or blocked work.`
 
   # Output as JSON
   {{.Name}}-pp-cli stale --days 30 --json`,
+		Annotations: map[string]string{"mcp:read-only": "true"},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if dbPath == "" {
 				dbPath = defaultDBPath("{{.Name}}-pp-cli")

← a590b6b9 Document write-time PII redaction in dogfood reports (#673)  ·  back to Cli Printing Press  ·  Fix catalog display_name precedence (#675) 3da34f16 →