← back to Cli Printing Press
Annotate PM workflows as read-only (#674)
a880a7f763167f98cd3de2301a2ce9b5e3b6191b · 2026-05-07 02:48:40 -0700 · Trevin Chow
Files touched
M internal/generator/generator_test.goM internal/generator/templates/workflows/pm_load.go.tmplM internal/generator/templates/workflows/pm_orphans.go.tmplM internal/generator/templates/workflows/pm_stale.go.tmpl
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 →