Skip to content
Merged
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
51 changes: 51 additions & 0 deletions cli/internal/mem/skill_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package mem

import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)

// quoted matches one single-quoted shell argument.
var quoted = regexp.MustCompile(`'([^']*)'`)

// exampleBody returns the handoff body a skill pipes into `dotf mem
// handoff-write`: the single-quoted printf arguments, one per line, ahead of the
// pipe into handoff-write. printf rather than a heredoc, because a heredoc's
// terminator breaks when the example sits indented inside a list item.
func exampleBody(t *testing.T, skill string) string {
t.Helper()
raw, err := os.ReadFile(filepath.Join("../../..", "harness", "skills", skill, "SKILL.md"))
if err != nil {
t.Fatal(err)
}
lines := strings.Split(string(raw), "\n")
for i, l := range lines {
if !strings.Contains(l, "printf '%s\\n'") || !strings.HasSuffix(strings.TrimSpace(l), "\\") {
continue
}
var body []string
for _, next := range lines[i+1:] {
for _, m := range quoted.FindAllStringSubmatch(next, -1) {
body = append(body, m[1])
}
if strings.Contains(next, "dotf mem handoff-write") {
return strings.Join(body, "\n") + "\n"
}
}
}
t.Fatalf("%s SKILL.md has no `printf '%%s\\n' ... | dotf mem handoff-write` example", skill)
return ""
}

// A skill that tells an agent to pipe a body into handoff-write must show a body
// the writer accepts without warning: an example without `**Next action:**` is
// copied literally and writes a thread with no first step (#3110).
func TestAutopilotHandoffExampleCarriesNextAction(t *testing.T) {
body := exampleBody(t, "autopilot")
if w := ThreadWarnings(body); len(w) != 0 {
t.Errorf("the autopilot example body draws handoff-write warnings: %q", w)
}
}
2 changes: 1 addition & 1 deletion cli/internal/mem/thread.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func ThreadWarnings(body string) []string {
th := ParseThread(body)
var out []string
if th.All("Next action") == "" {
out = append(out, "no Next action, so the next session has no first step")
out = append(out, "no `**Next action:**` line, so the next session has no first step")
}
for _, f := range th.Unknown() {
out = append(out, fmt.Sprintf("label %q is not one of: %s", f.Label, strings.Join(ThreadLabels, ", ")))
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/mem/thread_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func TestThreadWarningsNameOnlyWhatIsMissingOrUnknown(t *testing.T) {
t.Errorf("a canonical thread drew warnings: %q", w)
}
w := ThreadWarnings("**Last task:** x.\n**Next action:**\n**Trap:** y.\n")
if len(w) != 2 || !strings.Contains(w[0], "Next action") || !strings.Contains(w[1], `"Trap"`) {
if len(w) != 2 || !strings.Contains(w[0], "**Next action:**") || !strings.Contains(w[1], `"Trap"`) {
t.Errorf("warnings = %q, want the empty Next action and the unknown Trap", w)
}
}
Expand Down
11 changes: 9 additions & 2 deletions harness/skills/autopilot/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
generated: true
generated_from: 00_meta/skills/autopilot/SKILL.md
generated_sha: da95f19a047826e5
generated_sha: 9c64789731c9bbb6
id: autopilot-skill
type: skill
status: active
Expand Down Expand Up @@ -326,8 +326,15 @@ When the operator returns, the time window expires, the backlog empties, or a st
```bash
MEMORY_MD="$(dotf env path VAULT_PATH)/10_projects/<repo>/memory/MEMORY.md"
dotf mem thread # which thread am I: wt-<slug>, or main@<host>
printf '%s' "$BODY" | dotf mem handoff-write --memory "$MEMORY_MD" --agent <agent>
printf '%s\n' \
'> Updated: <YYYY-MM-DD><br>' \
'**Last task:** <what this run did, with PR numbers><br>' \
'**Next action:** <the one concrete first step for the next session><br>' \
'**Awaiting Manu:** <decisions only the operator can take; drop the line if none>' |
dotf mem handoff-write --memory "$MEMORY_MD" --agent <agent>
```
The `**Next action:**` line is not optional: without it `handoff-write` warns
"no `**Next action:**` line" and still writes the thread, so the next session starts with no first step.
Comment on lines +329 to +337

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Harness compliance

Class: REAL
SSOT (Standing Order #2, knowledge placement): the edited file is generated: true from 00_meta/skills/autopilot/SKILL.md, but only the render is changed here while the generator source stays stale. Concrete trace: any session that runs dotf harness refresh before the vault edit lands regenerates this file from the vault, restoring printf '%s' "$BODY" and dropping the **Next action:** line — agents again learn the broken example, and TestAutopilotHandoffExampleCarriesNextAction goes red in CI until someone re-applies the vault edit out of band. The PR discloses the pending vault edit, but as submitted the fix is one routine refresh away from being undone, and dotf harness check cannot see the divergence.

The full checklist (journal, board reconciliation, artifact summary, one next action) is
`00_meta/skills/handoff/SKILL.md`. `--memory "$REPO_ROOT/MEMORY.md"` — which an earlier draft used —
would write the handoff nowhere near where the next session reads it.
Expand Down
Loading