mirror of
https://github.com/mvanhorn/cli-printing-press.git
synced 2026-09-14 15:38:08 +08:00
fix(cli): stop selecting missing comm_health workflow template (#4665)
The communication archetype still listed workflows/comm_health.go.tmpl after that file was never added to the embed, so every generate against a communication spec warned and skipped it. Drop the dead selector and keep selected workflow/insight templates locked to files that exist. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com>
This commit is contained in:
@@ -377,7 +377,7 @@ Table stakes features (from the absorb gate). Every feature the top competitor h
|
||||
|
||||
Data layer (high-gravity entities). Domain-specific SQLite tables with proper columns (not JSON blobs), FTS5 full-text search, incremental sync with cursor tracking, `sql` command for raw queries, domain-specific `UpsertX()` and `SearchX()` methods.
|
||||
|
||||
Workflow commands (from archetype): `stale`, `orphans`, `load`, `channel-health`, `reconcile`, etc.
|
||||
Workflow commands (from archetype): `stale`, `orphans`, `load`, etc.
|
||||
|
||||
Insight commands (Rung 5): `health` (composite score), `similar` (duplicate detection), `trends`, `bottleneck`, `forecast`, `patterns`.
|
||||
|
||||
@@ -390,7 +390,7 @@ The profiler classifies every API into a domain archetype and auto-generates the
|
||||
| Archetype | Detected by | Auto-generated commands |
|
||||
|-----------|------------|------------------------|
|
||||
| Project Management | issue/task/ticket resources, assignee fields, priority levels | `stale`, `orphans`, `load`, `health`, `similar` |
|
||||
| Communication | message/channel/thread resources, threading fields | `channel-health`, `message-stats`, `health`, `similar` |
|
||||
| Communication | message/channel/thread resources, threading fields | `health`, `similar` |
|
||||
| Payments | charge/payment/invoice resources, amount/currency fields | `reconcile`, `revenue`, `health`, `similar` |
|
||||
| Infrastructure | server/deploy/instance resources | `health`, `similar` |
|
||||
| Content | document/page/block resources | `health`, `similar` |
|
||||
|
||||
@@ -2655,7 +2655,7 @@ func archetypePlaybook(arch profiler.DomainArchetype) []PlaybookEntry {
|
||||
case profiler.ArchetypeCommunication:
|
||||
return []PlaybookEntry{
|
||||
{Topic: "Message search", Insight: "Use the search tool on synced data rather than paginating through message history. Message APIs often have aggressive rate limits."},
|
||||
{Topic: "Channel health", Insight: "When analyzing channel activity, use the channel-health command or sql aggregation on synced messages. Don't iterate individual messages via API."},
|
||||
{Topic: "Channel health", Insight: "When analyzing channel activity, use sql aggregation on synced messages. Don't iterate individual messages via API."},
|
||||
}
|
||||
case profiler.ArchetypePayments:
|
||||
return []PlaybookEntry{
|
||||
|
||||
@@ -140,10 +140,6 @@ func SelectVisionTemplates(plan *vision.VisionaryPlan) VisionTemplateSet {
|
||||
"workflows/pm_orphans.go.tmpl",
|
||||
"workflows/pm_load.go.tmpl",
|
||||
}
|
||||
case "communication":
|
||||
set.Workflows = []string{
|
||||
"workflows/comm_health.go.tmpl",
|
||||
}
|
||||
}
|
||||
|
||||
// Invariant: a store without sync is useless — sync populates the store.
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/mvanhorn/cli-printing-press/v4/internal/naming"
|
||||
"github.com/mvanhorn/cli-printing-press/v4/internal/profiler"
|
||||
"github.com/mvanhorn/cli-printing-press/v4/internal/spec"
|
||||
"github.com/mvanhorn/cli-printing-press/v4/internal/vision"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSelectVisionTemplatesSelectedFilesExistInEmbed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
archetypes := []string{
|
||||
string(profiler.ArchetypeProjectMgmt),
|
||||
string(profiler.ArchetypeCommunication),
|
||||
string(profiler.ArchetypePayments),
|
||||
string(profiler.ArchetypeInfrastructure),
|
||||
string(profiler.ArchetypeContent),
|
||||
string(profiler.ArchetypeCRM),
|
||||
string(profiler.ArchetypeDeveloperPlatform),
|
||||
"",
|
||||
}
|
||||
insight := vision.NonObviousInsight{
|
||||
InsightFrame: "a local query surface",
|
||||
Implications: []string{"sync then sql"},
|
||||
}
|
||||
|
||||
for _, arch := range archetypes {
|
||||
t.Run(arch, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
set := SelectVisionTemplates(&vision.VisionaryPlan{
|
||||
Domain: vision.DomainInfo{Archetype: arch},
|
||||
Insight: insight,
|
||||
})
|
||||
selected := append(append([]string{}, set.Workflows...), set.Insights...)
|
||||
for _, tmpl := range selected {
|
||||
_, err := templateFS.ReadFile(path.Join("templates", tmpl))
|
||||
require.NoError(t, err, "selected template %s for archetype %q must exist in the embed", tmpl, arch)
|
||||
require.NotEmpty(t, commandConstructorForTemplate(tmpl),
|
||||
"selected template %s for archetype %q must have a command constructor", tmpl, arch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateCommunicationArchetypeDoesNotWarnOnMissingCommHealth(t *testing.T) {
|
||||
apiSpec := communicationSpec("commhealthwarn")
|
||||
profile := profiler.Profile(apiSpec)
|
||||
require.Equal(t, profiler.ArchetypeCommunication, profile.Domain.Archetype)
|
||||
|
||||
outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
|
||||
stderr, err := captureNovelFeatureStderr(t, func() error {
|
||||
return New(apiSpec, outputDir).Generate()
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, stderr, "comm_health")
|
||||
require.NotContains(t, stderr, "skipping workflow template")
|
||||
|
||||
_, statErr := os.Stat(filepath.Join(outputDir, "internal", "cli", "comm_health.go"))
|
||||
require.ErrorIs(t, statErr, os.ErrNotExist)
|
||||
|
||||
skillSrc := readGeneratedFile(t, outputDir, "SKILL.md")
|
||||
require.NotContains(t, skillSrc, "channel-health")
|
||||
}
|
||||
|
||||
func communicationSpec(name string) *spec.APISpec {
|
||||
apiSpec := minimalSpec(name)
|
||||
apiSpec.Resources = map[string]spec.Resource{
|
||||
"messages": {
|
||||
Description: "Channel messages",
|
||||
Endpoints: map[string]spec.Endpoint{
|
||||
"list": {Method: "GET", Path: "/messages", Description: "List messages"},
|
||||
},
|
||||
},
|
||||
"channels": {
|
||||
Description: "Chat channels",
|
||||
Endpoints: map[string]spec.Endpoint{
|
||||
"list": {Method: "GET", Path: "/channels", Description: "List channels"},
|
||||
},
|
||||
},
|
||||
}
|
||||
return apiSpec
|
||||
}
|
||||
Reference in New Issue
Block a user