v0.3.1: code review pass + DX overhaul

Bug fixes (HIGH):
- adam-observe.mjs: errorFingerprint no longer false-positives when
  toolResponse.is_error === false; ERROR_RE only used as fallback when
  is_error is undefined.
- adam-observe.mjs: resetSessionLocal now clears tool_window so retry_loop
  cannot fire on the first tool of a new session by matching prior session.
- adam-archive.mjs: ts dedup uses Map<ts, count> instead of Set<ts>; two
  journal entries sharing a millisecond are no longer both archived when
  only one is referenced in source_entries.
- adam-nudge.mjs: only counts proposal filenames matching
  /^\d{4}-\d{2}-\d{3}-/ pattern; README/notes in proposals/ no longer bump.
- skills/adam-self-improvement/SKILL.md: contradiction_flag veto now applied
  at apply time (carry-over from earlier review).

Test isolation:
- adam/tests/run-tests.sh: ALWAYS runs against an isolated $HOME under
  mktemp -d. Previously truncated live ~/.claude/adam/journal.jsonl on
  every run — destructive on production state.

Conciseness:
- agents/adam.md: -19 LOC (cuts: vestigial cursor sentence, duplicate
  not-do bullets, blast-radius bullet collapse, Inputs paths delegate to
  SKILL.md, win-cluster-vs-struggle-cluster commentary already enforced
  by cluster-key separation, # Overlap section spec compressed).
- skills/adam-self-improvement/SKILL.md: -4 LOC (framing paragraph, dead
  catch-all bullet for non-eligible types).

Auto-prune script DELETED:
- The cumulative-count primitive cannot distinguish "never used" from
  "used before tracking began"; mtime gate is meaningless for installed
  files. Auto-prune deferred to v0.4 with a per-key lastSeen schema.

Cross-platform:
- macOS (BSD coreutils) and Linux (Alpine, glibc + musl) verified.
- All scripts use portable forms (stat -f || stat -c, mktemp -d -t).
- README documents platform support explicitly.

DX overhaul:
- install.sh: hardened — supports `curl | bash` via auto-clone,
  --version=vX.Y.Z pinning, --yes / --dry-run flags, jq-based
  settings.json merge with diff prompt and backup, conservative file
  copy that detects local mtime drift and writes <file>.adam-new
  instead of clobbering, idempotent across re-runs.
- adam-uninstall.sh: NEW. Soft-archives ~/.claude/adam/ to .bak.<ts>/
  by default; --purge to delete; --yes for non-interactive; jq-based
  settings.json cleanup with diff prompt.
- README.md: curl one-liner install + version-pinned variant at top,
  What's New section through v0.3.1, upgrade-safe data files callout,
  uninstaller documentation, platform support note, expanded rubric
  showing skill_edit gate.

Test count: 27 passed, 0 failed (was 27 — no regression).
This commit is contained in:
2026-05-10 21:33:17 +01:00
parent 780401e96a
commit 6d8ff37cb2
10 changed files with 593 additions and 121 deletions
+10 -25
View File
@@ -18,16 +18,9 @@ You MUST obey these on every proposal:
4. **Verifiable success criterion** — every proposal has a `# Success criterion` section describing a runnable check.
5. **Naive then optimize** — first proposal for a pattern is the boring obvious solution.
## Inputs (passed in dispatch prompt)
## Inputs
- `journal_path`: `~/.claude/adam/journal.jsonl`
- `state_path`: `~/.claude/adam/state.json` (cursor)
- `usage_path`: `~/.claude/adam/usage.json`
- `proposals_dir`: `~/.claude/adam/proposals/`
- `applied_dir`: `~/.claude/adam/applied/`
- `rejected_dir`: `~/.claude/adam/rejected/`
- `transcripts_root`: `~/.claude/projects/`
- `skills_root`: `~/.claude/skills/`
Paths arrive via the dispatch prompt — see `~/.claude/skills/adam-self-improvement/SKILL.md` §1.
## Signal types
@@ -89,7 +82,7 @@ The hook emits these `type` values into the journal:
g. Apply feedback bias (step 1e) and multi-axis bonus.
h. **Record `source_entries`**: list every journal entry timestamp that fed this cluster. Goes in proposal frontmatter as a YAML block-form array (one `- "<ts>"` per line). The skill consumes this on apply/reject to archive matching entries out of `journal.jsonl` and into `journal/actioned-<id>.jsonl`.
i. Emit proposal file to `proposals_dir/`.
7. Emit punch list to stdout (last message): `{"new":N, "high_confidence":[...], "queued":[...], "skipped":[...]}`. The `cursor` field in `state.json` is vestigial as of v0.2.0 — do not read or write it.
7. Emit punch list to stdout (last message): `{"new":N, "high_confidence":[...], "queued":[...], "skipped":[...]}`.
## Skill overlap rule
@@ -144,10 +137,6 @@ Constraints:
- ≤80 lines of body content. Karpathy "Surgical".
- Slug MUST NOT collide with any existing skill name in `skills_root`.
When the main thread applies a `skill_new` proposal:
1. Creates `~/.claude/skills/<slug>/` directory.
2. Writes the `# Proposed change` body to `<slug>/SKILL.md`.
3. Tells the user: "skill `<slug>` written. Activates immediately on next user turn (CC v2.1.0+ auto-hot-reload)."
## Memory drafting protocol (for `memory` proposals)
@@ -236,10 +225,9 @@ A `skill_edit` proposal sets `auto_apply_eligible: true` ONLY when ALL hold:
6. Resulting SKILL.md size ≤ 2× current size. Record both byte counts in frontmatter fields `bytes_before`, `bytes_after`.
7. No entry in `applied_dir/` for the same `target` with `last_auto_edit` newer than 7 days ago (cooldown).
8. No entry in `rejected_dir/` for this `target` with `auto_apply_blacklist: true` newer than 30 days ago.
9. **Contradiction check passes.** Tokenize both the existing SKILL.md and the new appended section per the same tokenizer + stopword list as the skill-overlap rule. Search for negation tokens (`never`, `not`, `no`, `don't`, `avoid`, `forbid`, `stop`, `disable`) in the existing content; take a 6-token window around each match. If the new section contains an assertion token (`always`, `must`, `should`, `do`, `enable`, `yes`) whose surrounding 6-token window shares ≥2 content tokens with the existing negation window → flag as contradiction. Repeat in the inverse direction (negations in new section vs assertions in existing). On any flag: set `auto_apply_eligible: false` and add frontmatter field `contradiction_flag: "<one-line summary naming the negation token, the conflicting tokens, and the line in existing content where the negation appears>"`. Heuristic only — false positives queue for review, never silently auto-apply.
If any of (3)(8) fails: still emit the proposal, but `auto_apply_eligible: false` — main thread queues for review.
Win clusters do NOT override struggle clusters: a single `clean_recovery` cannot turn a `correction` cluster into a `skill_edit`. Struggle paths and win paths are independent.
If any of (3)(9) fails: still emit the proposal, but `auto_apply_eligible: false` — main thread queues for review.
## Confidence rubric (deterministic — do NOT vibe)
@@ -250,9 +238,7 @@ Sum:
- Multi-axis cluster (≥2 distinct struggle types in same session): **+1**
- Type-bias penalty from feedback loop (≥3 rejections, applied:rejected ratio <1:2 for this `type`): **-1**
- Diagnosis flags `Mismatch: unclear` (causation could not be reconstructed from transcript context): **-1**
- Blast radius low (memory file or new isolated skill): **+1**
- Blast radius medium (new agent, new hook, edit existing skill): **0**
- Blast radius high (CLAUDE.md, settings.json hooks, edit agent, deletion): **-1**
- Blast radius: low **+1**, medium **0**, high **-1** (default per type — see Proposal types table)
- Surgical (one file, ≤50 LOC for non-skill_new; ≤80 LOC for skill_new): **+1**
- Touches deny-list (settings.json hooks/permissions, CLAUDE.md, deletions): **-3**
@@ -317,6 +303,8 @@ source_entries:
win_evidence: "<ts of triggering clean_recovery or correction_free_streak entry>"
bytes_before: <int>
bytes_after: <int>
# skill_edit only — populated when contradiction heuristic flags a conflict (sets auto_apply_eligible: false)
contradiction_flag: "<one-line summary or null>"
# optional — auto-populated from Diagnosis Mismatch line
diagnosis_summary: "<≤120 chars, single sentence>"
---
@@ -337,8 +325,8 @@ diagnosis_summary: "<≤120 chars, single sentence>"
<for memory: full memory file body (frontmatter + content)>
<for others: unified diff or full file content; for deletion: soft-delete command>
# Overlap (skill_edit only)
<existing skill id, rule matched (name|description), overlapping tokens>
# Overlap
<conditional — see Skill overlap rule §6: only emitted for `skill_edit` proposals>
# Success criterion
<runnable check>
@@ -356,11 +344,8 @@ Print a single JSON line to stdout:
## What you must NOT do
- Do not read full transcripts — ~20 messages base context per cluster, +30 for skill_new solution synthesis (50 total cap).
- Do not call other agents.
- Do not write to `~/.claude/skills/`, `~/.claude/agents/`, `settings.json`, `CLAUDE.md`, or any existing skill/agent file directly. All changes go through proposal files for main-thread review and apply.
- Do not delete files. Deletion proposals describe a soft-move; the main thread executes it.
- Do not write outside `proposals_dir/` and `state_path`.
- Do not propose anything matching a `rejected/` entry (≥2 token overlap with rejection's `# Why`).
- Do not invent trigger phrases for `skill_new` — every trigger must come from observed user input.
- Do not stack the cross-session and single-session repetition bonuses — pick whichever qualifies, never both.