Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ func TestRunbookTemplateEmbedded(t *testing.T) {
"templates/runbook/.kiwi/schemas/runbook.json",
"templates/runbook/.kiwi/config.toml",
"templates/runbook/.kiwi/templates/runbook.md",
"templates/runbook/services/api-service.md",
"templates/runbook/services/monitoring.md",
}
for _, p := range paths {
if _, err := fs.Stat(embedded, p); err != nil {
Expand All @@ -551,6 +553,8 @@ func TestRunbookTemplateInit(t *testing.T) {
".kiwi/templates/runbook.md",
".kiwi/config.toml",
".kiwi/playbook.md",
"services/api-service.md",
"services/monitoring.md",
}
for _, p := range mustExist {
if _, err := os.Stat(filepath.Join(root, p)); err != nil {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
memory_kind: episodic
episode_id: cursor-hands-on-325-2026-07-03
title: Issue #325 runbook init template hands-on delivery
tags: [kiwifs, issue-325, runbook, workspace, embed-filter, hands-on]
date: 2026-07-03
---

## Summary

Hands-on takeover for kiwifs/kiwifs#325 after prior fleet agent reported
delivery without verifiable committed diff.

## Actions

1. Verified branch `feat/issue-325-runbook-init-template-clean` against `origin/main`.
2. Confirmed UC-6 runbook scaffold, JSON Schema, embed filter, and regression tests.
3. Linked `services/` stubs and `playbook.md` from `index.md` to reduce orphan info issues.
4. Ran `go test ./internal/workspace/... ./cmd/... -run 'Runbook|runbook|Embed|Filtered|InitCheck'` — PASS.
5. Committed `37118ca` and pushed to `fork/feat/issue-325-runbook-embed-filter-delivery`.
6. Opened PR against kiwifs/kiwifs main with `Closes #325`.

## Test output

```
go test ./internal/workspace/... ./cmd/... -count=1 -run 'Runbook|runbook|Embed|Filtered|InitCheck'
ok github.com/kiwifs/kiwifs/internal/workspace
ok github.com/kiwifs/kiwifs/cmd
```

`TestRunbookInitCheckPasses`: exit 0 (9 info-level issues, no errors).

## Kiwi depot

MCP gateway unavailable; fix doc updated locally at
`pages/fixes/kiwifs-kiwifs/issue-325-runbook-init-template.md`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
memory_kind: episodic
episode_id: cursor-hands-on-325-2026-07-03
title: Issue #325 runbook embed filter hands-on delivery
tags: [kiwifs, issue-325, runbook, embed-filter, hands-on]
date: 2026-07-03
---

## Task

Deliver verified code for kiwifs/kiwifs#325 — runbook init template and frontmatter schema.
Prior fleet agent had docs-only commit with no source diff.

## Actions

1. Cherry-picked embed filter commit (`64d2fd4`) onto `feat/issue-325-runbook-init-template-clean`
2. Added `embed_filter.go` wrapping `go:embed` to exclude dev-only legacy template paths
3. Added service stubs (`services/api-service.md`, `services/monitoring.md`) for example runbook wiki links
4. Extended regression tests for embed visibility and scaffold paths
5. Resolved fix doc merge conflict; updated peer review status

## Tests

```
go test ./internal/workspace/... -count=1 -run 'Runbook|Embed|Filtered' → PASS
go test ./cmd/... -count=1 -run 'Runbook' → PASS
```

## Outcome

Source diff delivered with green tests. Ready for PR with `Closes #325`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
memory_kind: episodic
episode_id: cursor-issue-325-2026-06-30
title: "Issue #325 — runbook init template embed filter hardening"
tags: [kiwifs, runbooks, issue-325, init-template, embed-filter, uc-6]
date: 2026-06-30
---

# Issue #325 — runbook init template embed filter hardening

## Context

Hands-on delivery for kiwifs/kiwifs#325 on branch `feat/issue-325-runbook-init-template-clean`.
UC-6 runbook scaffold already shipped on main (PR #459 monorepo import) but issue
remains open. Added embed-filter hardening and service stubs so `kiwifs init --template runbook`
stays lint-clean when legacy dev paths exist on disk.

## Pre-search

- Local fix docs: `pages/fixes/kiwifs-kiwifs/issue-325-runbook-init-template.md`,
`pages/fixes/kiwifs-kiwifs/issue-325-runbook-embed-filter.md`
- Kiwi MCP gateway unavailable; cluster HTTP search failed (connection refused)

## Root cause

`//go:embed all:templates` can ship local dev scaffolds (legacy runbook `incidents/`,
`postmortems/`, `procedures/`, superseded `knowledge/`, dev-only research paths) that
contain placeholder wiki links. Those break `schema.Lint` and fail the acceptance
criterion that `kiwifs check` passes on generated workspaces.

## Changes

1. `embed_filter.go` — wrap embedded FS to exclude dev-only template paths
2. `embed_filter_test.go` — path filter unit tests
3. `init.go` — use filtered FS for template copy
4. Service stubs `services/api-service.md`, `services/monitoring.md` for example runbook links
5. Regression tests updated for scaffold paths and embed visibility

## Verification

```bash
go test ./internal/workspace/... ./cmd/... -run 'Runbook|runbook|Embed|Filtered' -count=1 -p 1 # PASS
go test ./internal/workspace/... ./cmd/... -run 'InitRunbook|InitCheck' -count=1 -p 1 # PASS
```

## Acceptance criteria

| Criterion | Status |
|-----------|--------|
| `kiwifs init --template runbook` scaffolds workspace | PASS |
| Example runbook has 7 sections + fenced code blocks | PASS |
| JSON Schema validates required frontmatter | PASS |
| `kiwifs check` passes on generated scaffold | PASS |

## Outcome

Branch ready for fleet publish (push + PR closing #325). No Cursor attribution in commits.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
memory_kind: episodic
episode_id: cursor-issue-325-2026-07-03-verification
title: "Issue #325 — runbook init template verification (hands-on delivery)"
tags: [kiwifs, runbooks, issue-325, init-template, uc-6, verification]
date: 2026-07-03
---

# Issue #325 — runbook init template verification

## Context

Autonomous pickup of kiwifs/kiwifs#325 on branch `feat/issue-325-runbook-init-template-clean`
(from `main`). UC-6 runbook init template shipped in the initial import; issue remains open
because PR #418 was closed without merge. This session re-verified acceptance criteria and
prepared fleet publish.

## Pre-search

- Kiwi MCP gateway unavailable (no MCP servers registered).
- Kiwi depot REST (`192.168.167.240:3333`) unreachable (connection refused).
- Read local fix doc: `pages/fixes/kiwifs-kiwifs/issue-325-runbook-init-template.md`.

## Verification

1. Confirmed UC-6 scaffold under `internal/workspace/templates/runbook/`:
`SCHEMA.md`, `index.md`, `example-high-cpu.md`, `.kiwi/schemas/runbook.json`,
`.kiwi/templates/runbook.md`, `.kiwi/config.toml`, `playbook.md`.
2. `runbook` registered in `cmd/init.go` flag help, example, and `internal/workspace/init.go` switch.
3. `go test ./internal/workspace/... -run 'Runbook|runbook' -count=1` — PASS.
4. `go test ./cmd/... -run 'Runbook|runbook' -count=1` — PASS (includes `TestRunbookInitCheckPasses`).
5. Full `go test ./...` — root package setup failed (missing `ui/dist` embed); unrelated to runbook.

## Acceptance criteria

| Criterion | Status |
|-----------|--------|
| `kiwifs init --template runbook` scaffolds workspace | PASS |
| Example runbook has 7 sections + fenced code blocks | PASS |
| JSON Schema validates required frontmatter | PASS |
| `kiwifs check` passes on generated scaffold | PASS |

## Outcome

Issue #325 implementation complete on `main`. Fleet agent should open PR with `Closes #325`
from `feat/issue-325-runbook-init-template-clean` (verification docs commit).
73 changes: 73 additions & 0 deletions internal/workspace/embed_filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package workspace

import (
"io/fs"
"path/filepath"
"strings"
)

// excludedEmbedDirs lists template paths that must not ship via kiwifs init.
// go:embed all:templates includes every file on disk under templates/, including
// local dev scaffolds and pre-UC-6 runbook dirs with placeholder wiki links
// that fail schema.Lint / kiwifs check on generated workspaces.
var excludedEmbedDirs = map[string]bool{
"templates/runbook/incidents": true,
"templates/runbook/postmortems": true,
"templates/runbook/procedures": true,
"templates/knowledge": true, // superseded by memory/kb templates
"templates/research/experiments": true, // dev-only; not part of research init
"templates/research/literature": true, // dev-only; research uses papers/
}

func isExcludedEmbedPath(path string) bool {
path = filepath.ToSlash(path)
if excludedEmbedDirs[path] {
return true
}
for dir := range excludedEmbedDirs {
if strings.HasPrefix(path, dir+"/") {
return true
}
}
return false
}

type filteredTemplatesFS struct {
inner fs.FS
}

func (f filteredTemplatesFS) Open(name string) (fs.File, error) {
if isExcludedEmbedPath(name) {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
return f.inner.Open(name)
}

func (f filteredTemplatesFS) ReadFile(name string) ([]byte, error) {
if isExcludedEmbedPath(name) {
return nil, &fs.PathError{Op: "read", Path: name, Err: fs.ErrNotExist}
}
if rf, ok := f.inner.(fs.ReadFileFS); ok {
return rf.ReadFile(name)
}
return fs.ReadFile(f.inner, name)
}

func (f filteredTemplatesFS) ReadDir(name string) ([]fs.DirEntry, error) {
if isExcludedEmbedPath(name) {
return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrNotExist}
}
entries, err := fs.ReadDir(f.inner, name)
if err != nil {
return nil, err
}
out := make([]fs.DirEntry, 0, len(entries))
for _, e := range entries {
path := filepath.Join(name, e.Name())
if isExcludedEmbedPath(path) {
continue
}
out = append(out, e)
}
return out, nil
}
60 changes: 60 additions & 0 deletions internal/workspace/embed_filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package workspace

import (
"io/fs"
"testing"
)

func TestIsExcludedEmbedPath(t *testing.T) {
t.Parallel()
cases := []struct {
path string
want bool
}{
{"templates/runbook/incidents", true},
{"templates/runbook/incidents/template.md", true},
{"templates/runbook/postmortems/template.md", true},
{"templates/runbook/procedures/scale-up.md", true},
{"templates/research/experiments/exp-001-baseline.md", true},
{"templates/research/literature/example-paper.md", true},
{"templates/knowledge/index.md", true},
{"templates/runbook/example-high-cpu.md", false},
{"templates/runbook/SCHEMA.md", false},
{"templates/runbook/.kiwi/schemas/runbook.json", false},
{"templates/runbook/services/api-service.md", false},
{"templates/research/papers/example-paper.md", false},
{"templates/wiki/index.md", false},
}
for _, tc := range cases {
if got := isExcludedEmbedPath(tc.path); got != tc.want {
t.Errorf("isExcludedEmbedPath(%q) = %v, want %v", tc.path, got, tc.want)
}
}
}

func TestFilteredTemplatesFSHidesLegacyRunbookPaths(t *testing.T) {
t.Parallel()
legacy := []string{
"templates/runbook/incidents/template.md",
"templates/runbook/postmortems/template.md",
"templates/runbook/procedures/deploy-rollback.md",
"templates/knowledge/index.md",
"templates/research/experiments/exp-001-baseline.md",
}
for _, p := range legacy {
if _, err := templates.ReadFile(p); err == nil {
t.Fatalf("filtered FS should hide %q", p)
}
}

entries, err := fs.ReadDir(templates, "templates/runbook")
if err != nil {
t.Fatal(err)
}
for _, e := range entries {
switch e.Name() {
case "incidents", "postmortems", "procedures":
t.Fatalf("legacy dir %q listed in templates/runbook", e.Name())
}
}
}
6 changes: 5 additions & 1 deletion internal/workspace/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import (
)

//go:embed all:templates
var templates embed.FS
var templatesRaw embed.FS

// templates hides legacy runbook scaffold paths that may still exist on disk
// during development but must not ship via kiwifs init --template runbook.
var templates filteredTemplatesFS = filteredTemplatesFS{inner: templatesRaw}

// InitTemplate describes a workspace scaffold available at space creation.
type InitTemplate struct {
Expand Down
2 changes: 2 additions & 0 deletions internal/workspace/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ func TestTemplatesEmbedded(t *testing.T) {
"templates/runbook/.kiwi/schemas/runbook.json",
"templates/runbook/.kiwi/config.toml",
"templates/runbook/.kiwi/templates/runbook.md",
"templates/runbook/services/api-service.md",
"templates/runbook/services/monitoring.md",
}
for _, p := range paths {
if _, err := fs.Stat(templates, p); err != nil {
Expand Down
33 changes: 33 additions & 0 deletions internal/workspace/runbook_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ func TestInitRunbookTemplateScaffold(t *testing.T) {
"example-high-cpu.md",
"index.md",
"SCHEMA.md",
"services/api-service.md",
"services/monitoring.md",
} {
if _, err := os.Stat(filepath.Join(root, p)); err != nil {
t.Fatalf("missing %s: %v", p, err)
Expand Down Expand Up @@ -265,3 +267,34 @@ func TestInitRunbookDoesNotOverwriteExisting(t *testing.T) {
t.Fatal("expected example-high-cpu.md to be created alongside existing index.md")
}
}

func TestRunbookEmbedUsesUC6ScaffoldOnly(t *testing.T) {
t.Parallel()
legacy := []string{
"templates/runbook/incidents/template.md",
"templates/runbook/postmortems/template.md",
"templates/runbook/procedures/deploy-rollback.md",
"templates/runbook/procedures/rotate-secrets.md",
"templates/runbook/procedures/scale-up.md",
"templates/knowledge/index.md",
}
for _, p := range legacy {
if _, err := templates.ReadFile(p); err == nil {
t.Fatalf("legacy runbook scaffold %q must not be embedded (breaks lint via placeholder wiki links)", p)
}
}
required := []string{
"templates/runbook/SCHEMA.md",
"templates/runbook/index.md",
"templates/runbook/example-high-cpu.md",
"templates/runbook/.kiwi/schemas/runbook.json",
"templates/runbook/.kiwi/templates/runbook.md",
"templates/runbook/services/api-service.md",
"templates/runbook/services/monitoring.md",
}
for _, p := range required {
if _, err := templates.ReadFile(p); err != nil {
t.Fatalf("required UC-6 runbook scaffold missing %q: %v", p, err)
}
}
}
Loading
Loading