skills/growing/SKILL.md
Golden path /grow — confirm proposed matches, classify evidence, update confidence, run decay, surface growth changes. Full L4 backpropagation orchestrator.
npx skillsauth add 0xhoneyjar/construct-observer growingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Operator-confirmed response attribution loop. Reviews proposed matches from /listen, classifies signal quality, updates hypothesis confidence, runs decay, and surfaces growth changes.
All matches require operator confirmation. Deterministic matches (token/thread) are pre-selected for batch confirm but the operator must explicitly approve. No --auto flag.
/grow # Full growth loop for all users
/grow --user <user> # Single user only
gp_check_growth_dir/grow processes users one at a time with a fresh model context per user. Classification prompts (Step 2) MUST only include:
Cross-user context is NEVER included in classification prompts. Reset context between users.
source scripts/observer/golden-path-lib.sh
source scripts/observer/growth-state.sh
gp_status_header "grow"
# Clean up orphaned .tmp files from crashed runs (see scripts/staleness.md)
find grimoires/keeper/growth/ -name "*.tmp" -mmin +5 -delete 2>/dev/null
if ! gp_check_growth_dir; then
gp_status_fail "growth" "no growth files — run /listen first"
gp_status_blocked "run /listen to create growth state from follow-up responses"
gp_status_footer
return
fi
cycle_started_at = now_iso8601()
total_confirmed = 0
total_rejected = 0
total_decayed = 0
total_confidence_updated = 0
# Determine user list
IF --user flag provided:
users = [specified_user]
ELSE:
users = list_growth_users() # all *.yaml in growth dir
FOR each user in users (sequential, fresh context per user):
growth_path = "grimoires/keeper/growth/${user}.yaml"
proposed_path = "grimoires/keeper/growth/${user}.proposed_matches.yaml"
lock_path = "grimoires/keeper/growth/${user}.yaml.lock"
IF NOT exists(growth_path): CONTINUE
# ==== STEP 1: REVIEW PROPOSED MATCHES ====
# Check for pending proposed matches
has_pending = false
IF exists(proposed_path):
WITH flock(lock_path):
proposed_content = read_yaml(proposed_path)
pending_matches = [m for m in proposed_content.matches if m.status == "pending"]
IF len(pending_matches) > 0:
has_pending = true
IF NOT has_pending:
# No proposed matches — skip to Step 4 (decay)
GOTO STEP_4
# Display ALL pending matches for operator review
# Pre-select deterministic matches (token/thread) for batch confirm
# Show non-deterministic matches (keyword/temporal/ambiguous) with evidence for manual review
FOR each match in pending_matches:
Display:
- Match type: {match.match_type} ({match.match_confidence} confidence)
- Signal snippet: {match.evidence_snippet}
- Follow-up ID: {match.follow_up_id}
- Follow-up hypothesis: (look up from growth state)
- Token: {match.follow_up_token}
- Pre-selected: YES if match_type in [token, thread], NO otherwise
# Ask operator to batch-confirm
# "Which matches do you want to confirm? (token/thread matches are pre-selected)"
# Operator can: confirm all pre-selected, add/remove individual matches, reject remainder
confirmed_matches = [operator-confirmed matches]
rejected_matches = [operator-rejected matches]
# ==== STEP 2: CLASSIFY SIGNAL QUALITY + APPLY ====
# RLM ISOLATION: fresh model context with ONLY this user's data
WITH flock(lock_path):
growth_content = read_yaml(growth_path)
proposed_content = read_yaml(proposed_path)
evidence_list = []
FOR each match in confirmed_matches:
follow_up = find_follow_up_by_id(growth_content, match.follow_up_id)
IF follow_up is null:
Log: "Follow-up {match.follow_up_id} not found, skipping"
CONTINUE
# Idempotency: skip if already applied with same signal
IF follow_up.outcome == "responded" AND follow_up.match_evidence is not null:
IF follow_up.match_evidence.signal_id == match.signal_id:
Log: "Already applied signal {match.signal_id} to {match.follow_up_id}, skipping"
CONTINUE
# Retrieve full signal text from provenance
signal_record = Run: scripts/provenance/query.sh --hash {match.signal_id}
IF signal_record is null:
Log: "Provenance record not found for {match.signal_id}, skipping"
CONTINUE
# Agent classifies with ONLY this user's hypothesis + this signal
# Classification prompt includes:
# - The hypothesis text from follow_up.hypothesis
# - The signal text from provenance
# - Ask: Does this signal support, refute, or say nothing about the hypothesis?
# - Ask: How strong is the evidence? (high / medium / low)
# - Ask: Brief rationale (max 300 chars)
evidence = {
signal_id: match.signal_id,
hypothesis_id: follow_up.hypothesis_ids[0] if follow_up.hypothesis_ids else null,
direction: <agent_classified>, # supports | refutes | neutral
strength: <agent_classified>, # high | medium | low
quote_span: match.evidence_snippet, # display-only, max 200 chars
quote_hash: sha256(NFC_normalize(match.evidence_snippet)), # immutable evidence anchor
rationale: <agent_classified>, # max 300 chars
classified_by: "agent",
classified_at: now_iso8601()
}
# Apply to canonical growth state (in-memory, write once at end)
# Check outcome was pending/unknown before mutating (idempotency)
IF follow_up.outcome in ["pending", "unknown"]:
follow_up.outcome = "responded"
follow_up.response_date = match.signal_timestamp
follow_up.signal_quality = evidence.strength # high|medium|low maps to quality
follow_up.match_evidence = evidence
follow_up.outcome_at = now_iso8601()
# Update pattern counters (only on FIRST transition to responded)
pattern = follow_up.question_pattern
IF pattern in growth_content.question_patterns:
growth_content.question_patterns[pattern].responded_count += 1
quality_numeric = signal_quality_to_numeric(evidence.strength)
IF quality_numeric is not null:
growth_content.question_patterns[pattern].signal_quality_sum += quality_numeric
growth_content.question_patterns[pattern].signal_quality_count += 1
_recompute_pattern_metrics(growth_content, pattern)
evidence_list.append(evidence)
total_confirmed += 1
# Update proposed match statuses (in-memory)
FOR m in confirmed_matches: m.status = "confirmed"; m.confirmed_at = now_iso8601()
FOR m in rejected_matches: m.status = "rejected"
total_rejected += len(rejected_matches)
# ==== STEP 3: HYPOTHESIS CONFIDENCE UPDATE ====
FOR each evidence in evidence_list:
IF evidence.hypothesis_id is not null:
growth_content = echo "$growth_content" | growth_update_confidence \
evidence.hypothesis_id evidence.direction evidence.strength cycle_started_at
# growth_update_confidence is a pure transformer (stdin→stdout)
# Gates: stale skip, high-strength-only, neutral skip, per-cycle cap
total_confidence_updated += 1 # (approximate, function logs actual changes)
# SINGLE ATOMIC WRITE per user (hold lock once for all mutations)
WITH flock(lock_path):
growth_content.last_updated = now_iso8601()
write_yaml(growth_path + ".tmp", growth_content)
mv(growth_path + ".tmp", growth_path)
write_yaml(proposed_path + ".tmp", proposed_content)
mv(proposed_path + ".tmp", proposed_path)
STEP_4:
# ==== STEP 4: DECAY ENGINE ====
# Read config for decay thresholds
cycles_to_decaying = config.observer.growth.decay.cycles_to_decaying # default 3
cycles_to_stale = config.observer.growth.decay.cycles_to_stale # default 6
WITH flock(lock_path):
content = read_yaml(growth_path)
modified = growth_run_decay(user, cycles_to_decaying, cycles_to_stale, now_iso8601())
# growth_run_decay reads from file, returns modified content
# Count transitions
FOR each hypothesis:
IF old_state != new_state:
total_decayed += 1
write_yaml(growth_path + ".tmp", modified)
mv(growth_path + ".tmp", growth_path)
# ==== STEP 5: PER-USER REPORT ====
Display for user:
- Proposed matches: {confirmed} confirmed, {rejected} rejected, {still_pending} still pending
- Hypotheses: {active} active, {decaying} decaying, {stale} stale
- Effective patterns: list patterns with effectiveness_score >= 60
- Ineffective patterns: list patterns with effectiveness_score < 20 and times_used > 0
- Pending follow-ups: {count} still awaiting response
- Score deltas: latest if behavioral_signal is true
# END per-user loop
# ==== STEP 6: CROSS-USER PATTERNS + STATUS ====
# Surface cross-user patterns if 3+ users share the same pattern
IF len(users) >= 3:
# Check if 3+ users share the same effective/ineffective pattern
# Check if 3+ users have same hypothesis decay trend
Display any cross-user patterns found
gp_status_ok "matches" "${total_confirmed} confirmed, ${total_rejected} rejected"
gp_status_ok "decay" "${total_decayed} hypotheses transitioned"
gp_status_ok "confidence" "${total_confidence_updated} hypotheses evaluated"
gp_progression_summary
gp_suggest_next "grow"
gp_status_footer
Evidence is stored at follow_ups[batch].message_ids[msg].match_evidence in canonical growth state:
match_evidence:
signal_id: "abc123..." # Provenance content_hash
hypothesis_id: "H4" # First from hypothesis_ids
direction: supports # supports | refutes | neutral
strength: high # high | medium | low
quote_span: "first 200 chars" # Display-only
quote_hash: "sha256..." # sha256(NFC_normalize(quote_span))
rationale: "User confirms..." # Max 300 chars
classified_by: agent
classified_at: "2026-02-14T..."
| strength | quality_numeric | Description | |----------|----------------|-------------| | high | 1.0 | Strong, clear signal | | medium | 0.5 | Moderate signal | | low | 0.0 | Weak signal |
| Truename | Purpose |
|----------|---------|
| growth-state.sh | Growth state CRUD + growth_update_confidence + growth_run_decay |
| growth-match.sh | Response attribution (called by /listen, results consumed here) |
| scripts/provenance/query.sh | Retrieve provenance records for signal verification |
data-ai
Cognition orchestrator — analyze canvases, distill fears via /distill subagent, run gap analysis, optional cross-user synthesis.
development
Golden path /speak — generate RLM-isolated follow-ups with chronicle temporal context injection.
testing
# /snapshot — MiDi Experience Record (MER) Capture Capture a point-in-time MER for a wallet. Produces a 4-layer snapshot: data state, visual screenshot, user perception, and decision context. ## Usage ``` /snapshot <wallet-or-alias> /snapshot xabbu --trigger feedback /snapshot xabbu --data-only /snapshot --cohort /snapshot --cohort --diff MER-2026-001 ``` ## Arguments | Argument | Description | Required | |----------|-------------|----------| | `wallet-or-alias` | Wallet address or alias fr
development
Golden path /shape — consolidate journey patterns across canvases and file gap issues.