diff --git a/.claude/harness/CHANGELOG.md b/.claude/harness/CHANGELOG.md index c2bc440..0f12bcf 100644 --- a/.claude/harness/CHANGELOG.md +++ b/.claude/harness/CHANGELOG.md @@ -58,3 +58,12 @@ or old harness during a heterogeneous rollout. See machine; budget WARN trips correctly on a synthetic over-budget value. - Also reconciled the remaining "GrepAI first" docs (standard + CODING_GUIDELINES) with the wiki-first recall hierarchy (started in CLAUDE_EXTENDED). + +## 1.4.2 — 2026-06-08 (Task 3 leftover: command-restates-standard lint) +- /self-check gained a `consistency` category — the command-restates-standard lint. Deterministic + half: for each manifest.command_standard_links pair, the standard must still carry its + defer-to-SSOT pointer to the owning command; a lost pointer WARNs (the standard likely drifted + back into restating the command — the Syncro-timers failure mode). Seeded with the syncro-billing + link (time-entry-protocol.md -> /syncro). Semantic contradiction pass (read both, judge actual + conflict) delegated to the model in SKILL.md, mirroring the memory pass. Verified PASS; negative- + tested (WARN fires when the pointer is removed). New pairs: add to manifest.command_standard_links. diff --git a/.claude/harness/VERSION b/.claude/harness/VERSION index 347f583..9df886c 100644 --- a/.claude/harness/VERSION +++ b/.claude/harness/VERSION @@ -1 +1 @@ -1.4.1 +1.4.2 diff --git a/.claude/skills/self-check/SKILL.md b/.claude/skills/self-check/SKILL.md index 26455ba..479c590 100644 --- a/.claude/skills/self-check/SKILL.md +++ b/.claude/skills/self-check/SKILL.md @@ -82,6 +82,7 @@ SELFCHECK_TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ | **duplicates** | command/skill names present in BOTH the repo and `~/.claude`. Divergent content = WARN (the "same `/cmd`, different behaviour on the Mac" bug); identical = INFO (redundant, will drift). CRLF-only differences are ignored. | | **memory** | `MEMORY.md` index exists; no orphaned memory files; manifest-declared contradiction patterns (see semantic pass below). Never FAILs the grade. | | **harness** | the 1.4.0 invariants (read-only): VERSION marker present + not older than `manifest.harness.min_version`; **skill-registry description budget** (sum of all SKILL.md `description:` fields under `registry_desc_budget_chars` — WARN on regrowth); global deploy targets `~/.claude/skills` + `~/.claude/commands` populated (the "Mac wiped global skills" failure); `harness-guard.sh` present + wired into `sync.sh`; core scripts parse (`bash -n` on sync/guard/now-phoenix); `now-phoenix.sh --date` emits a valid date. Budget/min-version/script-list are tunable in `manifest.harness`. | +| **consistency** | the **command-restates-standard** lint (deterministic half): for each `manifest.command_standard_links` pair, the standard must still contain its defer-to-SSOT pointer to the owning command. A lost pointer = WARN (the standard likely drifted back into restating the command — the Syncro-timers failure mode). The semantic contradiction judgement is delegated to the model (see below). | | **vault** | vault repo exists; sops+age present; `vault.sh list` succeeds (decrypt wired). | | **connectivity** | coord API (required), main API + internal Gitea (advisory; off-network is OK). | @@ -110,6 +111,26 @@ Ollama Tier-0 per the house rules; Claude reviews the result): Genuinely machine-specific guidance in a *shared* memory is the usual culprit — the fix is to scope it ("on Windows…") or split it, not to globally flip it. +### Semantic pass 2 — command vs standard contradiction + +The `consistency` category only checks that the defer-to-SSOT *pointer* is present. +Whether a command and its standard actually **say contradictory things** is a +judgement task — do it the same way (Ollama Tier-0 for the read/classify, Claude +reviews): + +1. For each `manifest.command_standard_links` pair, read BOTH the standard and the + owning command it points to. +2. Flag any rule the standard states that **conflicts** with the command (e.g. the + standard mandates a timer for routine billing while `/syncro` says line-item is + normal and timers are outlier-only — the original drift this lint exists to catch). +3. Report: the topic, the conflicting claims (quote both sides), and which one is the + SSOT. **Do not edit** — surface for the operator; the SSOT (the command) wins, so + the fix is almost always to correct the standard, not the command. + +New links are cheap to add — drop another `{topic, standard, must_reference, why}` +into `manifest.command_standard_links` whenever a command and a standard speak to the +same rule. + ## Fleet self-remediation loop (machines fix themselves) We never fix a remote machine. The flow is: diff --git a/.claude/skills/self-check/baseline/manifest.json b/.claude/skills/self-check/baseline/manifest.json index 419565b..b30cf22 100644 --- a/.claude/skills/self-check/baseline/manifest.json +++ b/.claude/skills/self-check/baseline/manifest.json @@ -18,6 +18,16 @@ "guard_wired_in": ".claude/scripts/sync.sh" }, + "command_standard_links": [ + { + "topic": "syncro-billing", + "standard": ".claude/standards/syncro/time-entry-protocol.md", + "must_reference": "syncro\\.md|single source of truth", + "why": "the time-entry standard must DEFER to the /syncro command (one SSOT), not restate billing mechanics. A past drift had the standard say 'always timer' while the command said 'outlier only' — losing the pointer is the early warning of that re-drift." + } + ], + "command_standard_links_note": "Deterministic half of the command-restates-standard lint: each linked standard must contain a defer-to-SSOT pointer (must_reference, a grep -iE regex). A WARN means the standard may have drifted back into restating/contradicting the command. The SEMANTIC contradiction judgement (read both files, decide if they actually conflict) is delegated to the model in SKILL.md, mirroring the memory contradiction pass.", + "required_tools": [ { "name": "bash", "why": "hooks, scripts, sync, vault wrapper" }, { "name": "git", "why": "repo + submodules + Gitea sync" }, diff --git a/.claude/skills/self-check/scripts/self-check.sh b/.claude/skills/self-check/scripts/self-check.sh index 0a01c00..34c2130 100644 --- a/.claude/skills/self-check/scripts/self-check.sh +++ b/.claude/skills/self-check/scripts/self-check.sh @@ -661,6 +661,36 @@ check_harness_smoke() { fi } +# --------------------------------------------------------------------------- +# CHECK: command <-> standard consistency (the "command-restates-standard" lint). +# Deterministic core only: for each manifest-declared (command, standard) link, +# verify the standard still contains its defer-to-SSOT pointer (must_reference). +# A standard that loses the pointer has likely drifted back into RESTATING the +# command's rules -- the exact failure mode behind the Syncro timers contradiction +# (standard said 'always timer' while /syncro said 'outlier only'). The SEMANTIC +# pass (read both, judge actual contradiction) is delegated to the model in +# SKILL.md, mirroring check_memory. +# --------------------------------------------------------------------------- +check_command_standard() { + local has; has="$(jq -r '(.command_standard_links // []) | length' "$MANIFEST" 2>/dev/null)" + [ "${has:-0}" -gt 0 ] 2>/dev/null || return + local topic stdf ref why p + while IFS=$'\t' read -r topic stdf ref why; do + [ -n "$topic" ] || continue + p="$REPO_ROOT/$stdf" + if [ ! -f "$p" ]; then + emit "consistency.$topic" consistency WARN "standard missing for '$topic': $stdf" \ + "Restore via /sync, or remove the link from manifest.command_standard_links" + elif grep -qiE "$ref" "$p" 2>/dev/null; then + emit "consistency.$topic" consistency PASS "'$topic' standard defers to the owning command (SSOT pointer present)" + else + emit "consistency.$topic" consistency WARN \ + "'$topic' standard ($stdf) lost its defer-to-SSOT pointer ($why)" \ + "Re-add the pointer to the owning command, and confirm the standard does NOT restate or contradict it" + fi + done < <(jq -r '(.command_standard_links // [])[] | [.topic, .standard, .must_reference, .why] | @tsv' "$MANIFEST") +} + # --------------------------------------------------------------------------- # Build the census JSON from accumulated results # --------------------------------------------------------------------------- @@ -846,6 +876,7 @@ check_skills_commands check_duplicates check_memory check_harness_smoke +check_command_standard check_vault check_connectivity