fix: replace all non-ASCII characters with safe ASCII equivalents
Replace box-drawing characters (U+2500) in session log with ASCII dashes. Replace em dashes (U+2014) in CLAUDE.md template with double hyphens. Remove em dash from comment in run.go. Add ASCII-guard tests for session log output and seed templates. Prevents mojibake on Windows terminals that misinterpret UTF-8 as CP1252. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,11 +17,11 @@ This workspace is scoped to a single task. Keep work focused on the task describ
|
||||
|
||||
## Files
|
||||
|
||||
- task.yaml — Task metadata (machine-managed, do not edit)
|
||||
- notes.md — Task log (human/agent-managed)
|
||||
- context/ — Reference documents (user-managed)
|
||||
- output/ — Task deliverables and artifacts
|
||||
- logs/ — Session logs (automatic session snapshots)
|
||||
- task.yaml -- Task metadata (machine-managed, do not edit)
|
||||
- notes.md -- Task log (human/agent-managed)
|
||||
- context/ -- Reference documents (user-managed)
|
||||
- output/ -- Task deliverables and artifacts
|
||||
- logs/ -- Session logs (automatic session snapshots)
|
||||
|
||||
## Session Handoff
|
||||
|
||||
@@ -32,7 +32,7 @@ Before ending a session, append a brief summary to notes.md with:
|
||||
- Open follow-ups or unfinished work
|
||||
- How to continue from here
|
||||
|
||||
Keep it concise — a few bullet points is enough. This helps the developer (or a future session) resume without losing context.
|
||||
Keep it concise -- a few bullet points is enough. This helps the developer (or a future session) resume without losing context.
|
||||
`, slug, category, workspacePath)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package seed
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClaudeMDIsASCII(t *testing.T) {
|
||||
content := ClaudeMD("test-slug", "general", "/tmp/test")
|
||||
for i, b := range []byte(content) {
|
||||
if b > 127 {
|
||||
t.Errorf("non-ASCII byte 0x%02x at position %d in CLAUDE.md template", b, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotesMDIsASCII(t *testing.T) {
|
||||
content := NotesMD("test title")
|
||||
for i, b := range []byte(content) {
|
||||
if b > 127 {
|
||||
t.Errorf("non-ASCII byte 0x%02x at position %d in notes.md template", b, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func FormatSessionEntry(info *SessionInfo) string {
|
||||
duration := info.EndTime.Sub(info.StartTime)
|
||||
timeFmt := "2006-01-02 15:04:05"
|
||||
|
||||
fmt.Fprintf(&b, "── Session %s ──\n", info.StartTime.Format(timeFmt))
|
||||
fmt.Fprintf(&b, "-- Session %s --\n", info.StartTime.Format(timeFmt))
|
||||
fmt.Fprintf(&b, "Agent: %s\n", info.Agent)
|
||||
fmt.Fprintf(&b, "Mode: %s\n", info.Mode)
|
||||
fmt.Fprintf(&b, "Start: %s\n", info.StartTime.Format(timeFmt))
|
||||
@@ -96,7 +96,7 @@ func FormatSessionEntry(info *SessionInfo) string {
|
||||
b.WriteString("(No changes detected)\n")
|
||||
}
|
||||
|
||||
b.WriteString("────────────────────────────────\n")
|
||||
b.WriteString("--------------------------------\n")
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
@@ -127,8 +127,8 @@ func TestAppendSessionLog(t *testing.T) {
|
||||
content := string(data)
|
||||
|
||||
// Should contain both sessions
|
||||
if strings.Count(content, "── Session") != 2 {
|
||||
t.Errorf("expected 2 session entries, got %d", strings.Count(content, "── Session"))
|
||||
if strings.Count(content, "-- Session") != 2 {
|
||||
t.Errorf("expected 2 session entries, got %d", strings.Count(content, "-- Session"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,3 +167,27 @@ func TestManifestCleanupOnSuccess(t *testing.T) {
|
||||
t.Error("manifest should be cleaned up after successful logging")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionLogIsASCII(t *testing.T) {
|
||||
start := time.Date(2026, 4, 5, 14, 30, 22, 0, time.UTC)
|
||||
end := time.Date(2026, 4, 5, 15, 45, 10, 0, time.UTC)
|
||||
|
||||
info := &SessionInfo{
|
||||
Agent: "claude",
|
||||
Mode: "local",
|
||||
StartTime: start,
|
||||
EndTime: end,
|
||||
Diff: &ManifestDiff{
|
||||
Added: []string{"output/result.md"},
|
||||
Modified: []string{"notes.md"},
|
||||
},
|
||||
}
|
||||
|
||||
entry := FormatSessionEntry(info)
|
||||
for i, b := range []byte(entry) {
|
||||
if b > 127 {
|
||||
t.Errorf("non-ASCII byte 0x%02x at position %d in session log output", b, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func Run(opts LaunchOpts) error {
|
||||
startManifest, err := CaptureManifest(opts.WsDir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[ctask] warning: failed to capture start manifest: %v\n", err)
|
||||
// Continue anyway — never block the user
|
||||
// Continue anyway -- never block the user
|
||||
}
|
||||
|
||||
if startManifest != nil {
|
||||
|
||||
Reference in New Issue
Block a user