feat(continuity): wire §6.3 shared-docs staleness notice

MarkdownChanges was deleted as dead code, but the underlying spec
requirement wasn't abandoned — rebuilt it independently. Adds
continuity.ConventionsHash for AGENTS.md/CLAUDE.md/VOCAB.md, tracks a
per-session snapshot on herdr.Session, and adds
Coordinator.checkConventions (run every Monitor tick) which compares
each active session's snapshot against its project's base repo and
pushes an in-pane notice via a new herdr.ConventionsNotifier
capability when they drift.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1rkJ2hBMybnJctPbcy4tT
This commit is contained in:
kami
2026-07-27 22:25:53 +04:00
parent 3fe3aee5b7
commit 0ca78243b9
7 changed files with 225 additions and 8 deletions
+25
View File
@@ -50,6 +50,31 @@ func TaskFileHash(root string) (string, error) {
return hex.EncodeToString(sum[:]), nil
}
// ConventionsFiles are the §6.3 shared docs ("Shared *.md ... all agents
// contribute") whose staleness the orchestra is responsible for tracking —
// never the agent, which only sees its own cached copy.
var ConventionsFiles = []string{"AGENTS.md", "CLAUDE.md", "VOCAB.md"}
// ConventionsHash hashes the concatenation of whichever ConventionsFiles
// exist at root, in that fixed order, so the result changes iff any of their
// contents change (a file appearing/disappearing also changes it, since a
// length-prefixed marker precedes each file's bytes). A repo with none of
// these files hashes to a stable, comparable empty-set value rather than
// erroring — §6.3 is optional infrastructure, not every project uses it.
func ConventionsHash(root string) (string, error) {
h := sha256.New()
for _, name := range ConventionsFiles {
b, err := os.ReadFile(filepath.Join(root, name))
if err != nil {
fmt.Fprintf(h, "%s:0\n", name)
continue
}
fmt.Fprintf(h, "%s:%d\n", name, len(b))
h.Write(b)
}
return hex.EncodeToString(h.Sum(nil)), nil
}
type Dirty struct {
Path string `json:"path"`
SHA256 string `json:"sha256"`