From 83269b30865ac94051e5cf3615ffd44fe837dcd4 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Fri, 28 Aug 2026 10:15:15 -0700 Subject: [PATCH] feat(diagnosing-superpowers): structure test and verified harness session references Claude-Session: https://claude.ai/code/session_01DyaGKhTXvHNs2JgPhDktz7 --- .../references/claude-code-sessions.md | 100 ++++++++++++++ .../references/codex-sessions.md | 77 +++++++++++ .../references/other-harnesses.md | 30 ++++ .../test-skill-structure.sh | 128 ++++++++++++++++++ 4 files changed, 335 insertions(+) create mode 100644 skills/diagnosing-superpowers/references/claude-code-sessions.md create mode 100644 skills/diagnosing-superpowers/references/codex-sessions.md create mode 100644 skills/diagnosing-superpowers/references/other-harnesses.md create mode 100755 tests/diagnosing-superpowers/test-skill-structure.sh diff --git a/skills/diagnosing-superpowers/references/claude-code-sessions.md b/skills/diagnosing-superpowers/references/claude-code-sessions.md new file mode 100644 index 000000000..7293b910e --- /dev/null +++ b/skills/diagnosing-superpowers/references/claude-code-sessions.md @@ -0,0 +1,100 @@ +# Claude Code session store + +Verified against: Claude Code 2.1.247 (transcript `version` field), macOS. +When a field below is missing from the file in front of you, trust the file +and say so in coverage notes. + +## Where + +- Main transcript: `~/.claude/projects//.jsonl`, where + `` is the working directory with every `/` replaced by `-` + (e.g. `/tmp/work` → `-tmp-work`). +- Subagent transcripts: `~/.claude/projects///subagents/agent-.jsonl`, + each with a sibling `agent-.meta.json` + (`agentType`, `description`, `toolUseId`, `spawnDepth`, optional `model`). +- Plugin registry: `~/.claude/plugins/installed_plugins.json` — per plugin: + `installPath`, `version`, `installedAt`, `lastUpdated`, `gitCommitSha`. +- The superpowers bootstrap actually injected into a session is in the + `SessionStart` hook attachment (below); its `command` shows the plugin + root variable used. A dev checkout loaded with `--plugin-dir` will not be + in the registry, so report both the registry entry and the hook evidence. + +## Which file is the current session + +The most recently modified `.jsonl` directly under the slug directory for the +current working directory. Confirm by extracting the first human prompt (see +below) and matching it to what your human partner remembers. If two files +are close in mtime, show both first prompts and ask. + +## Line types + +Every line is one JSON object. `type` values seen: `user`, `assistant`, +`attachment`, `system`, plus session-level records (`permission-mode`, +`mode`, `bridge-session`, `last-prompt`, `ai-title`, `atis-latch`, +`pr-link`, `queue-operation`, `relocated`, `worktree-state`). + +Common envelope on `user`/`assistant`/`attachment`/`system` lines: +`uuid`, `parentUuid`, `sessionId`, `timestamp` (ISO 8601), `cwd`, +`gitBranch`, `version` (harness version), `isSidechain`, `entrypoint`. + +| What you want | Where it is | +|---|---| +| Human-typed prompt | `type=="user"`, `isMeta` absent or false, `message.content` is a string or a list whose first block is `type:"text"`. Lines whose first block is `tool_result` are tool results, not prompts. `` text inside a prompt is injected, not typed. | +| Assistant text / tool calls | `type=="assistant"`, `message.content[]` blocks of `type:"text"` or `type:"tool_use"` (`id`, `name`, `input`). | +| Tool result | `type=="user"`, `message.content[0].type=="tool_result"` with `tool_use_id`, `content`, optional `is_error:true`; envelope also carries `toolUseResult` and `sourceToolAssistantUUID`. | +| Model | `message.model` on assistant lines. | +| Tokens | `message.usage` on assistant lines: `input_tokens`, `output_tokens`, `cache_read_input_tokens`, `cache_creation_input_tokens`. | +| Skill invocation | `tool_use` block with `name:"Skill"` and `input.skill` (e.g. `superpowers:brainstorming`); the tool result line has `toolUseResult.commandName`. | +| Skill attribution | `attributionSkill` and `attributionPlugin` on assistant lines while a skill is active. | +| Subagent dispatch | `tool_use` with `name:"Agent"` (`input.description`, `input.subagent_type`, `input.prompt`); the subagent's own file is matched by `toolUseId` in its `.meta.json`. Subagent lines have `isSidechain:true` and `agentId`. | +| Hook output | `type=="attachment"`, `attachment.type` `hook_success`/`hook_failure`, `attachment.hookName` (e.g. `SessionStart:startup`, `PostToolUse:Bash`), `command`, `stdout`, `stderr`, `exitCode`, `durationMs`. | +| Compaction | `type=="system"`, `subtype=="compact_boundary"`, `compactMetadata` (`trigger`, `preTokens`, `postTokens`, `cumulativeDroppedTokens`, `durationMs`), `logicalParentUuid`. | +| Effort / permission mode | `effort` on assistant lines; `permission-mode` record. | + +## Safe extraction + +Lines can exceed a megabyte. Never print a whole line. Check size first: + +```bash +F=~/.claude/projects//.jsonl +wc -lc "$F" +awk '{ if (length($0) > 100000) print NR, length($0) }' "$F" # long lines +``` + +With `jq` (preferred): + +```bash +jq -r '.type' "$F" | sort | uniq -c # line-type census +jq -r 'select(.type=="user" and .isMeta!=true and ((.message.content|type)=="string" or .message.content[0].type=="text")) + | "\(input_line_number)\t\(.timestamp)\t\((.message.content|if type=="string" then . else .[0].text end)[0:160])"' "$F" # human prompts +jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") + | {name, id, input: (.input|tostring|.[0:120])}' "$F" # tool calls +jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use" and .name=="Skill") | .input.skill' "$F" # skill invocations +jq -c 'select(.type=="assistant") | {ts:.timestamp, model:.message.model, skill:.attributionSkill, + u:(.message.usage|{input_tokens,output_tokens,cache_read_input_tokens,cache_creation_input_tokens})}' "$F" # per-message usage +jq -c 'select(.subtype=="compact_boundary") | {line:input_line_number, ts:.timestamp, + m:(.compactMetadata|{trigger,preTokens,postTokens,cumulativeDroppedTokens,durationMs})}' "$F" # compactions (full compactMetadata also has UUID lists; keep this trimmed) +jq -c 'select(.type=="attachment" and (.attachment.type|startswith("hook"))) | {line:input_line_number, hook:.attachment.hookName, exit:.attachment.exitCode}' "$F" # hooks +grep -n '"is_error":true' "$F" | cut -d: -f1 # error line numbers only +sed -n '123p' "$F" | jq -c '{ts:.timestamp, first:((.message.content // "") as $c + | ($c | if type=="array" then ($c[0] // "") else $c end) | tostring | .[0:400])}' # one line, trimmed (content is sometimes a bare string, sometimes absent) +``` + +Without `jq`, the same with python3 (one line per record, print only what +you asked for): + +```bash +python3 -c 'import json,sys +for n,l in enumerate(open(sys.argv[1]),1): + o=json.loads(l) + if o.get("type")=="assistant": + for b in o["message"].get("content",[]): + if b.get("type")=="tool_use": print(n, b["name"], str(b.get("input"))[:120])' "$F" +``` + +## Subagents + +List `~/.claude/projects///subagents/`. For each `agent-*.meta.json` +print `agentType`, `description`, `model`; the matching `.jsonl` is that +subagent's transcript and follows the same line format. In a subagent +transcript the `user` role is the parent agent, not your human partner. diff --git a/skills/diagnosing-superpowers/references/codex-sessions.md b/skills/diagnosing-superpowers/references/codex-sessions.md new file mode 100644 index 000000000..1406ea96d --- /dev/null +++ b/skills/diagnosing-superpowers/references/codex-sessions.md @@ -0,0 +1,77 @@ +# Codex session store + +Verified against: Codex CLI 0.146.0 and 0.147.0 rollouts (`cli_version` in +`session_meta`), macOS. When a field below is missing from the file in front +of you, trust the file and say so in coverage notes. + +## Where + +`~/.codex/sessions/YYYY/MM/DD/rollout--.jsonl`. +Subagent threads are separate rollout files whose `session_meta.payload` +has `thread_source: "subagent"` and `source.subagent.thread_spawn.parent_thread_id` +pointing at the parent thread id. Root sessions have `thread_source: "user"`. + +## Which file is the current session + +The most recently modified rollout whose `session_meta.payload.cwd` is the +current working directory and whose `thread_source` is `user`. Confirm by +matching the first `user_message` event to what your human partner +remembers. + +## Line types + +Every line is `{timestamp, type, payload}` (some also carry `ordinal`). +`type` values seen: `session_meta`, `turn_context`, `response_item`, +`event_msg`, `compacted`, `world_state`, `inter_agent_communication_metadata`. + +| What you want | Where it is | +|---|---| +| Session identity | `session_meta.payload`: `id`, `session_id`, `cwd`, `originator` (e.g. `Codex Desktop`), `cli_version`, `model_provider`, `thread_source`, `source`, `git` (`commit_hash`, `branch`, `repository_url`), `base_instructions.text`. | +| Model per turn | `turn_context.payload`: `turn_id`, `model`, `effort`, `cwd`, `approval_policy`, `sandbox_policy`, `multi_agent_version`. Also `event_msg` `thread_settings_applied`. | +| Human-typed prompt | `event_msg` with `payload.type=="user_message"`: `payload.message`. (`response_item` messages with `role:"developer"` are injected, not typed; see Subagents below for rollouts that carry no `user_message` event at all.) | +| Assistant text | `event_msg` `agent_message` (`payload.message`, `payload.phase`) or `response_item` `message` with `role:"assistant"`. | +| Tool calls | `response_item` with `payload.type` `function_call` (`name`, `arguments`, `call_id`) or `custom_tool_call` (`name`, `input`, `call_id`); outputs are `function_call_output` / `custom_tool_call_output` matched by `call_id`. Also `event_msg` `patch_apply_end` (`success`, `changes`), `web_search_end`, `mcp_tool_call_end` (`invocation.server`, `invocation.tool`). | +| Turn timing | `event_msg` `task_started` (`turn_id`, `started_at`, `model_context_window`) and `task_complete` (`duration_ms`, `time_to_first_token_ms`, `last_agent_message`); `turn_aborted` (`reason`, `duration_ms`). | +| Tokens | `event_msg` `token_count`: `payload.info.total_token_usage` (cumulative; keys include `input_tokens`, `cached_input_tokens`, `output_tokens`) and `payload.rate_limits`. | +| Compaction | a `compacted` line (`window_id`, `previous_window_id`, `replacement_history`) and an `event_msg` `context_compacted`. | +| Subagents | `event_msg` `sub_agent_activity` (`agent_thread_id`, `agent_path`, `kind`); `response_item` `agent_message` with `author`/`recipient`; the child's own rollout file (see Where). | +| Skill use | No attribution field. Look for `SKILL.md` in `function_call.arguments` / `custom_tool_call.input` and in `world_state`/`session_meta` instruction text. | +| Reasoning | `response_item` `reasoning` (`summary[].text`; `encrypted_content` is opaque). | + +## Safe extraction + +Rollouts reach hundreds of megabytes; `compacted` lines embed whole +histories. Never print a whole line. Check size first: + +```bash +F=~/.codex/sessions/YYYY/MM/DD/rollout-....jsonl +wc -lc "$F" +awk '{ if (length($0) > 100000) print NR, length($0) }' "$F" +``` + +With `jq`: + +```bash +head -1 "$F" | jq '.payload | {id, cwd, originator, cli_version, model_provider, thread_source, git}' # identity +jq -r '.type + "/" + (.payload.type // "")' "$F" | sort | uniq -c # census +jq -r 'select(.type=="event_msg" and .payload.type=="user_message") | "\(input_line_number)\t\(.timestamp)\t\(.payload.message[0:160])"' "$F" # human prompts +jq -r 'select(.type=="turn_context") | "\(.timestamp)\t\(.payload.model)\t\(.payload.effort)"' "$F" # model per turn +jq -c 'select(.type=="response_item" and (.payload.type=="function_call" or .payload.type=="custom_tool_call")) + | {line:input_line_number, name:.payload.name, args:((.payload.arguments // .payload.input)|tostring|.[0:120])}' "$F" # tool calls +jq -c 'select(.payload.type=="task_complete" or .payload.type=="turn_aborted") | {ts:.timestamp, type:.payload.type, ms:.payload.duration_ms}' "$F" # turn timing +jq -c 'select(.payload.type=="token_count") | {ts:.timestamp, t:.payload.info.total_token_usage}' "$F" # tokens (cumulative) +grep -n '"type":"compacted"\|"context_compacted"' "$F" | cut -d: -f1 # compaction line numbers +grep -n 'SKILL\.md' "$F" | cut -d: -f1 # skill-read line numbers +sed -n '123p' "$F" | jq -c '{ts:.timestamp, type, p:(.payload|tostring|.[0:400])}' # one line, trimmed +``` + +Find a thread's subagent rollouts (filenames only, never content): + +```bash +grep -l '"parent_thread_id":""' ~/.codex/sessions/*/*/*/rollout-*.jsonl +``` + +A subagent rollout can carry no `event_msg` `user_message` at all — the +parent agent's dispatch prompt instead shows up as a `response_item` +`message` with `role:"user"`. If a `user_message` event is present, it is +from the parent agent, not your human partner. diff --git a/skills/diagnosing-superpowers/references/other-harnesses.md b/skills/diagnosing-superpowers/references/other-harnesses.md new file mode 100644 index 000000000..c10a931b6 --- /dev/null +++ b/skills/diagnosing-superpowers/references/other-harnesses.md @@ -0,0 +1,30 @@ +# Other harnesses: discover, then report what you found + +This file is for any harness without a verified reference in this +directory. You know your own harness better than this file does. Use that +knowledge, and write down exactly what you found so the report reader can +judge it. + +## Procedure + +1. **Ask the harness.** Many harnesses expose a session or history command + (` session list`, `/sessions`, a "resume" picker). Use it to get + the session id and, if shown, the file path. +2. **Look under the harness's config directory** (`~/./`, + `~/.config//`, `~/.local/share//`) for `sessions`, + `history`, `chats`, `threads`, or `projects` directories holding `.jsonl` + or `.json` files. +3. **Confirm a candidate** by extracting its first human message with a + size-safe command (`head -c 2000`, or `jq` on the first record) and + matching it to what your human partner remembers. Never print whole + lines; treat every candidate like the verified stores: `wc -lc` and a + long-line check before anything else. +4. **Map the fields you need** by reading a handful of records with `jq -c + 'keys'` or `head -c`: human prompt, assistant text, tool call and result, + model, harness version, timestamps, subagent linkage, compaction. +5. **Record in the case file and the report's coverage notes**: the store + path, the layout you inferred, which of the fields above you could and + could not find, and your confidence. Field-level claims in the report + are marked "inferred from the file, not a documented format". +6. **If you cannot find the store**, say so and ask your human partner for + the path. Do not guess a layout from another harness. diff --git a/tests/diagnosing-superpowers/test-skill-structure.sh b/tests/diagnosing-superpowers/test-skill-structure.sh new file mode 100755 index 000000000..af13f2268 --- /dev/null +++ b/tests/diagnosing-superpowers/test-skill-structure.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# Structural checks for skills/diagnosing-superpowers. Behavior is tested by +# the scenarios in CREATION-LOG.md; this script only checks the things a +# shell can check: frontmatter, referenced files exist, no local paths or +# names leaked into shipped files, SKILL.md word budget. +set -u + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +SKILL_DIR="$REPO_ROOT/skills/diagnosing-superpowers" +SKILL_MD="$SKILL_DIR/SKILL.md" +WORD_BUDGET=900 + +PASSES=0 +FAILURES=0 + +pass() { echo " [PASS] $1"; PASSES=$((PASSES + 1)); } +fail() { echo " [FAIL] $1"; FAILURES=$((FAILURES + 1)); } + +echo "diagnosing-superpowers structure" + +# --- SKILL.md frontmatter ------------------------------------------------- +if [ -f "$SKILL_MD" ]; then + pass "SKILL.md exists" + frontmatter="$(awk 'NR==1 && $0!="---"{exit} NR>1 && $0=="---"{exit} NR>1{print}' "$SKILL_MD")" + if printf '%s\n' "$frontmatter" | grep -q '^name: diagnosing-superpowers$'; then + pass "frontmatter name is diagnosing-superpowers" + else + fail "frontmatter name is diagnosing-superpowers" + fi + description="$(printf '%s\n' "$frontmatter" | awk '/^description:/{sub(/^description:[ ]*/,""); print; found=1; next} found && /^[ ]/{print} found && !/^[ ]/{exit}' | tr '\n' ' ')" + if printf '%s' "$description" | grep -q '^Use when'; then + pass "description starts with 'Use when'" + else + fail "description starts with 'Use when' (got: ${description:0:60})" + fi + if [ "${#description}" -le 1024 ]; then + pass "description under 1024 characters" + else + fail "description under 1024 characters (${#description})" + fi + for banned in "dispatch" "then" "step"; do + if printf '%s' "$description" | grep -qiw "$banned"; then + fail "description contains workflow word '$banned'" + else + pass "description avoids workflow word '$banned'" + fi + done + + # --- word budget -------------------------------------------------------- + body_words="$(awk 'BEGIN{fm=0} NR==1 && $0=="---"{fm=1; next} fm==1 && $0=="---"{fm=2; next} fm==2{print}' "$SKILL_MD" | wc -w | tr -d ' ')" + if [ "$body_words" -le "$WORD_BUDGET" ]; then + pass "SKILL.md body within $WORD_BUDGET words ($body_words)" + else + fail "SKILL.md body within $WORD_BUDGET words ($body_words)" + fi + + # --- required sections -------------------------------------------------- + for heading in "## Hard rules" "## Red Flags"; do + if grep -q "^$heading" "$SKILL_MD"; then + pass "SKILL.md has section '$heading'" + else + fail "SKILL.md has section '$heading'" + fi + done + + # --- every referenced skill file exists -------------------------------- + while IFS= read -r ref; do + if [ -f "$SKILL_DIR/$ref" ]; then + pass "referenced file exists: $ref" + else + fail "referenced file exists: $ref" + fi + done < <(grep -o '\(references\|prompts\|templates\)/[A-Za-z0-9._-]*\.md' "$SKILL_MD" | sort -u) +else + fail "SKILL.md exists" +fi + +# --- expected files ------------------------------------------------------- +expected_files=( + references/claude-code-sessions.md + references/codex-sessions.md + references/other-harnesses.md + prompts/skill-timeline.md + prompts/plan-adherence.md + prompts/repeated-work.md + prompts/stumbles.md + prompts/quality-evidence.md + prompts/request-conflicts.md + prompts/cost-and-time.md + prompts/scrub.md + prompts/scrub-audit.md + prompts/similar-session.md + templates/case.md + templates/report.md + templates/bundle-README.md + templates/issue.md + CREATION-LOG.md +) +for rel in "${expected_files[@]}"; do + if [ -f "$SKILL_DIR/$rel" ]; then + pass "expected file present: $rel" + else + fail "expected file present: $rel" + fi +done + +# --- no local paths or names in shipped files ---------------------------- +leaks="$(grep -rn -E '/Users/|/home/|jesse' "$SKILL_DIR" 2>/dev/null || true)" +if [ -z "$leaks" ]; then + pass "no machine-specific paths or names in shipped files" +else + fail "no machine-specific paths or names in shipped files" + printf '%s\n' "$leaks" | head -10 | sed 's/^/ /' +fi + +# --- "the user" never appears in skill prose ----------------------------- +user_hits="$(grep -rn -i 'the user' "$SKILL_DIR" --include='*.md' 2>/dev/null | grep -v CREATION-LOG.md || true)" +if [ -z "$user_hits" ]; then + pass "skill files say 'your human partner', not 'the user'" +else + fail "skill files say 'your human partner', not 'the user'" + printf '%s\n' "$user_hits" | head -10 | sed 's/^/ /' +fi + +echo +echo "Passed: $PASSES Failed: $FAILURES" +[ "$FAILURES" -eq 0 ]