fix: move completed signal-triggers plan to done/, add missing liveness-drift to index, add plan-consistency lint checks

This commit is contained in:
2026-07-09 10:47:39 +02:00
parent e92a6ff7a5
commit 0d29b1db81
4 changed files with 124 additions and 6 deletions

View File

@@ -1,16 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Lint committed docs against .agents/shared/writing-style.md. """Lint committed docs against .agents/shared/writing-style.md.
Checks two mechanical rules: Checks:
1. Banned vocabulary (significance puffers, analytical verbs, poetic nouns, 1. Banned vocabulary (significance puffers, analytical verbs, poetic nouns,
promotional adjectives, opening crutches). promotional adjectives, opening crutches).
2. Broken relative markdown links. 2. Broken relative markdown links.
3. Plan status consistency (status vs location vs index).
Prose-voice rules are not machine-checkable; this covers the parts that are. Prose-voice rules are not machine-checkable; this covers the parts that are.
Run from the repo root: python3 .agents/skills/docs-lint/lint.py [paths...] Run from the repo root: python3 .agents/skills/docs-lint/lint.py [paths...]
Exit 1 if any violation is found. Exit 1 if any violation is found.
""" """
import os, re, sys import os, re, sys, glob
BANNED = [ BANNED = [
"pivotal", "crucial", "vital", "groundbreaking", "transformative", "testament", "pivotal", "crucial", "vital", "groundbreaking", "transformative", "testament",
@@ -33,9 +34,125 @@ def iter_md(paths):
if f.endswith(".md"): if f.endswith(".md"):
yield os.path.join(root, f) yield os.path.join(root, f)
def check_plans():
"""Check plan status consistency: active plans with 'Done' status, files
missing from index, dangling index entries, done files with wrong status."""
REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
plans_dir = os.path.join(REPO, "plans")
done_dir = os.path.join(REPO, "plans", "done")
index_path = os.path.join(plans_dir, "index.md")
if not os.path.exists(index_path):
return 0
violations = 0
STATUS_RE = re.compile(r'^\*\*Status:\*\*\s*(.+)', re.I)
# Parse index.md for active and done entries
active_files = set()
done_files = set()
current_section = None
with open(index_path) as f:
for line in f:
if line.startswith("## Active"):
current_section = "active"
continue
if line.startswith("## Done"):
current_section = "done"
continue
if current_section == "active":
m = re.search(r'\]\(([^)]+)\)', line)
if m:
active_files.add(m.group(1))
elif current_section == "done":
m = re.search(r'\]\(([^)]+)\)', line)
if m:
done_files.add(m.group(1))
# Active plans on disk (not in done/, not index.md)
disk_active = set()
for f in glob.glob(os.path.join(plans_dir, "*.md")):
name = os.path.basename(f)
if name == "index.md":
continue
disk_active.add(name)
# Done plans on disk
disk_done = set()
if os.path.isdir(done_dir):
for f in glob.glob(os.path.join(done_dir, "*.md")):
disk_done.add("done/" + os.path.basename(f))
# Check 1: active plans on disk whose internal status is Done/Implemented/Complete
for name in disk_active:
fpath = os.path.join(plans_dir, name)
with open(fpath) as f:
for line_num, line in enumerate(f, 1):
if line_num > 5:
break
m = STATUS_RE.match(line)
if m:
status = m.group(1).strip().lower()
done_keywords = ["done", "implemented", "complete", "completed"]
if any(status.startswith(kw) for kw in done_keywords):
print(f"{fpath}:{line_num}: status '{m.group(1).strip()}' — file is in plans/ but appears done; move to done/")
violations += 1
break
# Check 2: active plans on disk not in index
for name in sorted(disk_active):
if name not in active_files:
fpath = os.path.join(plans_dir, name)
print(f"{fpath}:1: not listed in plans/index.md Active table")
violations += 1
# Check 3: done plans on disk not in index
for name in sorted(disk_done):
if name not in done_files:
fpath = os.path.join(REPO, "plans", name)
print(f"{fpath}:1: not listed in plans/index.md Done table")
violations += 1
# Check 4: index entries with no file on disk
for name in sorted(active_files):
if name not in disk_active:
print(f"plans/index.md: active entry '{name}' — file not found on disk")
violations += 1
for name in sorted(done_files):
if name not in disk_done:
print(f"plans/index.md: done entry '{name}' — file not found on disk")
violations += 1
# Check 5: files in done/ whose internal status doesn't say Done
for name in disk_done:
fpath = os.path.join(REPO, "plans", name)
with open(fpath) as f:
found_status = False
for line_num, line in enumerate(f, 1):
if line_num > 5:
break
m = STATUS_RE.match(line)
if m:
found_status = True
status = m.group(1).strip().lower()
if not status.startswith("done"):
print(f"{fpath}:{line_num}: status '{m.group(1).strip()}' — file is in done/ but status is not 'Done'")
violations += 1
break
if not found_status:
print(f"{fpath}:1: file is in done/ but has no Status header")
violations += 1
return violations
def main(argv): def main(argv):
paths = argv or ["knowledge", ".agents", "operations", "investigations", "plans"] paths = argv or ["knowledge", ".agents", "operations", "investigations", "plans"]
violations = 0 violations = 0
if "plans" in paths or any(p.startswith("plans") for p in paths):
violations += check_plans()
# The style guide and this skill enumerate the banned words by definition. # The style guide and this skill enumerate the banned words by definition.
ban_exempt = ("shared/writing-style.md", "skills/docs-lint/") ban_exempt = ("shared/writing-style.md", "skills/docs-lint/")
for f in sorted(set(iter_md(paths))): for f in sorted(set(iter_md(paths))):

View File

@@ -1,8 +1,6 @@
# 2026-07-08 — Liveness, drift, and UX cohesion # 2026-07-08 — Liveness, drift, and UX cohesion
**Status:** Code complete for Phases 14 core scope; not yet deployed to the **Status:** In Progress — Phases 14 code complete; not yet deployed. Phase 5 deferred.
live containers (pending explicit go-ahead — see below). Phase 5 partially
covered by pre-existing endpoints; full CRUD UI deferred.
- **Phase 1 (drift/staleness):** done. Health/metrics/events misattribution - **Phase 1 (drift/staleness):** done. Health/metrics/events misattribution
fix, staleness sweep, `/entities` health+freshness, dashboard/fleet-health fix, staleness sweep, `/entities` health+freshness, dashboard/fleet-health

View File

@@ -1,6 +1,6 @@
# 2026-07-08 — Signal triggers: host health checks # 2026-07-08 — Signal triggers: host health checks
**Status:** Implemented (Phases 1-5 complete) **Status:** Done — Phases 1-5 complete
## Goal ## Goal

View File

@@ -13,7 +13,9 @@ went sideways, open an investigation.
| 2026-07-08 | [Oikos gaps, broken things, and improvements](2026-07-08-oikos-gaps-and-improvements.md) | Planned | | 2026-07-08 | [Oikos gaps, broken things, and improvements](2026-07-08-oikos-gaps-and-improvements.md) | Planned |
| 2026-07-08 | [Control room web UI](2026-07-08-control-room-webui.md) | In Progress | | 2026-07-08 | [Control room web UI](2026-07-08-control-room-webui.md) | In Progress |
| 2026-07-08 | [Nomos resident agent (renames Hermes)](2026-07-08-nomos-resident-agent.md) | In Progress | | 2026-07-08 | [Nomos resident agent (renames Hermes)](2026-07-08-nomos-resident-agent.md) | In Progress |
| 2026-07-08 | [Liveness, drift, and UX cohesion](2026-07-08-liveness-drift-and-ux-cohesion.md) | In Progress |
| 2026-07-09 | [Chat sessions: reliability, cost, and session-management fixes](2026-07-09-chat-sessions-improvements.md) | Planned | | 2026-07-09 | [Chat sessions: reliability, cost, and session-management fixes](2026-07-09-chat-sessions-improvements.md) | Planned |
| 2026-07-09 | [Session execution, UX, and learning improvements](2026-07-09-session-execution-and-ux-fixes.md) | Planned |
## Done ## Done
@@ -33,6 +35,7 @@ See [`done/`](done/) for executed plans:
| 2026-07-07 | [Client lifecycle in Go — enrollment through deprecation](done/2026-07-07-client-lifecycle-in-go.md) | | 2026-07-07 | [Client lifecycle in Go — enrollment through deprecation](done/2026-07-07-client-lifecycle-in-go.md) |
| 2026-07-08 | [Fix MCP analysis tools](done/2026-07-08-fix-mcp-analysis-tools.md) | | 2026-07-08 | [Fix MCP analysis tools](done/2026-07-08-fix-mcp-analysis-tools.md) |
| 2026-07-06 | [Consolidate Oikos control plane onto mac-mini](done/2026-07-06-consolidate-oikos-control-plane-onto-mac-mini.md) | | 2026-07-06 | [Consolidate Oikos control plane onto mac-mini](done/2026-07-06-consolidate-oikos-control-plane-onto-mac-mini.md) |
| 2026-07-08 | [Signal triggers: host health checks](done/2026-07-08-signal-triggers.md) |
## Conventions ## Conventions