@jamie-tam/forge 6.1.0 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,47 @@
1
1
  #!/bin/bash
2
2
  # Pre-compact hook: Save critical state before context window compression.
3
- # This ensures the agent can recover its working context after compaction.
4
3
  #
5
- # Writes to .forge/state/notepad.md which survives context compression
6
- # because it's re-read by the agent when it detects context loss.
4
+ # Writes a "## Checkpoints" entry to aiwiki/sessions/{date}-{session_id_short}.md
5
+ # which is the canonical per-session handoff file. The session file is created
6
+ # lazily on the first event (this hook, /wrap, /dream, or harden's session write).
7
7
  #
8
- # Scans `.forge/work/*/` (typed subdirs) for the single in-progress work item.
9
- # Skips `escalated`/`completed` manifests (terminal states). Uses `{type}/{name}`
10
- # as the canonical work identifier because names can collide across types.
8
+ # Reads session_id from Claude Code's PreCompact hook payload (stdin JSON).
9
+ # Falls back to "0000000" short id if stdin payload is missing or malformed —
10
+ # the file is still created, just without traceable session linkage.
11
+ #
12
+ # Recovery path: post-compact agent (and the next SessionStart) reads the
13
+ # latest aiwiki/sessions/*.md, looks at ## Checkpoints, and acts on any
14
+ # `Status: unconsumed` directive.
15
+ #
16
+ # Notepad (.forge/state/notepad.md) — REMOVED in v6.2. The single source of
17
+ # truth for session recovery state is now aiwiki/sessions/{date}-{id}.md.
18
+ # See templates/aiwiki/schemas/session.md (schema v2) for the file shape.
19
+ #
20
+ # Also logs the compaction event to .forge/state/telemetry.jsonl (unchanged).
11
21
 
12
22
  set -euo pipefail
13
23
 
14
- # Find project root (look for .forge/ or .git/)
24
+ # Read stdin JSON payload (Claude Code's hook contract). The payload includes
25
+ # session_id, transcript_path, cwd, hook_event_name, trigger. We need session_id.
26
+ PAYLOAD="$(cat 2>/dev/null || true)"
27
+
28
+ # Extract session_id via sed (no jq dependency). UUID format expected.
29
+ SESSION_ID=""
30
+ if [ -n "$PAYLOAD" ]; then
31
+ SESSION_ID="$(echo "$PAYLOAD" | sed -n 's/.*"session_id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)"
32
+ fi
33
+ SESSION_ID_SHORT="${SESSION_ID:0:7}"
34
+ if [ -z "$SESSION_ID_SHORT" ]; then
35
+ SESSION_ID_SHORT="0000000"
36
+ fi
37
+
38
+ # Find project root
15
39
  PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
16
40
  STATE_DIR="$PROJECT_ROOT/.forge/state"
17
-
18
- # Create state directory if needed
19
41
  mkdir -p "$STATE_DIR"
20
42
 
21
- # Note: .forge/state/ gitignore management lives in `forge init` (src/init.ts
22
- # ensureGitignoreEntries). This hook used to append from here, but mutating a
23
- # tracked user file from a runtime hook is surprising — moved to install time.
24
-
25
- # Find active work item (status: in-progress), scanning typed subdirs
26
- NOTEPAD="$STATE_DIR/notepad.md"
27
- ACTIVE_WORK="" # Canonical identifier: {type}/{name}
43
+ # Find active work item (status: in-progress)
44
+ ACTIVE_WORK=""
28
45
  MANIFEST_PATH=""
29
46
 
30
47
  if [ -d "$PROJECT_ROOT/.forge/work" ]; then
@@ -39,49 +56,100 @@ if [ -d "$PROJECT_ROOT/.forge/work" ]; then
39
56
  done
40
57
  fi
41
58
 
42
- # Build notepad content
43
- {
44
- echo "# Forge Context Recovery"
45
- echo ""
46
- echo "**This file was auto-generated before context compaction.**"
47
- echo "**Re-read the manifest and continue from where you left off.**"
48
- echo ""
49
- echo "## Active Work Item"
50
-
51
- if [ -n "$ACTIVE_WORK" ]; then
52
- echo "- Work: $ACTIVE_WORK"
53
- echo "- Manifest: $MANIFEST_PATH"
59
+ # Detect recent aiwiki/ activity (24h window) for dream-directive decision
60
+ RECENT_AIWIKI_CHANGES=0
61
+ if [ -d "$PROJECT_ROOT/aiwiki" ]; then
62
+ if [ -d "$PROJECT_ROOT/aiwiki/raw" ] && [ -n "$(find "$PROJECT_ROOT/aiwiki/raw" -type f -name '*.md' -mtime -1 2>/dev/null | head -1)" ]; then
63
+ RECENT_AIWIKI_CHANGES=1
64
+ fi
65
+ if [ "$RECENT_AIWIKI_CHANGES" -eq 0 ]; then
66
+ for typed in gotchas decisions conventions architecture oracles; do
67
+ if [ -d "$PROJECT_ROOT/aiwiki/$typed" ] && [ -n "$(find "$PROJECT_ROOT/aiwiki/$typed" -type f -name '*.md' -mtime -1 2>/dev/null | head -1)" ]; then
68
+ RECENT_AIWIKI_CHANGES=1
69
+ break
70
+ fi
71
+ done
72
+ fi
73
+ fi
74
+
75
+ # Session file path
76
+ TODAY="$(date -u '+%Y-%m-%d')"
77
+ TIMESTAMP="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
78
+ SESSIONS_DIR="$PROJECT_ROOT/aiwiki/sessions"
79
+ SESSION_FILE="$SESSIONS_DIR/${TODAY}-${SESSION_ID_SHORT}.md"
80
+
81
+ # If aiwiki/ doesn't exist (forge installed but never bootstrapped), skip
82
+ # session-file write entirely — there's nowhere to put it. Telemetry still logs.
83
+ if [ -d "$PROJECT_ROOT/aiwiki" ]; then
84
+ mkdir -p "$SESSIONS_DIR"
85
+
86
+ # Lazy-create session file with bare frontmatter if missing
87
+ if [ ! -f "$SESSION_FILE" ]; then
88
+ FOCUS_INIT="${ACTIVE_WORK:-unset}"
89
+ {
90
+ echo "---"
91
+ echo "schema_id: session"
92
+ echo "schema_version: 2"
93
+ echo "status: active"
94
+ echo "date_start: $TODAY"
95
+ echo "session_id: ${SESSION_ID:-unknown}"
96
+ echo "focus: $FOCUS_INIT"
97
+ echo "last_commit: ~"
98
+ echo "---"
99
+ echo ""
100
+ echo "## Checkpoints"
101
+ echo ""
102
+ } > "$SESSION_FILE"
103
+ fi
104
+
105
+ # Append PreCompact checkpoint entry
106
+ {
107
+ echo "### PreCompact at $TIMESTAMP"
54
108
  echo ""
109
+ if [ -n "$ACTIVE_WORK" ]; then
110
+ echo "Active work: $ACTIVE_WORK"
111
+ echo "Manifest: $MANIFEST_PATH"
112
+ echo ""
113
+ if [ -f "$MANIFEST_PATH" ]; then
114
+ echo "Phase status (from manifest):"
115
+ echo '```yaml'
116
+ grep -A 1 'status:\|gate-passed:\|locked_at:' "$MANIFEST_PATH" 2>/dev/null | head -30
117
+ echo '```'
118
+ echo ""
119
+ fi
120
+ else
121
+ echo "Active work: none (no in-progress manifest found)"
122
+ echo ""
123
+ fi
55
124
 
56
- # Extract current phase info from manifest
57
- if command -v grep &>/dev/null && [ -f "$MANIFEST_PATH" ]; then
58
- echo "### Phase Status (from manifest)"
59
- echo '```yaml'
60
- grep -A 1 'status:\|gate-passed:' "$MANIFEST_PATH" 2>/dev/null | head -30
61
- echo '```'
125
+ if [ "$RECENT_AIWIKI_CHANGES" -eq 1 ]; then
126
+ echo "**Dream directive (unconsumed):**"
127
+ echo ""
128
+ echo "- Scope: \`aiwiki/raw/\` + typed pages touched in the last 24h (gotchas/, conventions/, etc.)"
129
+ echo "- Trigger: \`pre-compact\`, context ~85%"
130
+ echo "- Action: invoke \`support-dream\` skill before resuming other work"
131
+ echo "- Session-file refinement: if this file's other sections (Files touched, Decisions, etc.) exist, dream may also refine them"
132
+ echo ""
133
+ echo "Mark consumed: after invoking dream, change this entry's header from \`**Dream directive (unconsumed):**\` to \`**Dream directive (consumed at {ISO-timestamp}):**\`. Header-flip is what session-start.sh counts; appending a 'Status: consumed' line leaves the original header in place and the next session-start still surfaces it as unconsumed."
134
+ echo ""
62
135
  fi
63
- else
64
- echo "- No active work item found"
65
- fi
66
136
 
67
- echo ""
68
- echo "## Recovery Instructions"
69
- echo "1. Read the manifest file listed above"
70
- echo "2. Find the first phase with \`status: pending\` or \`gate-passed: false\`"
71
- echo "3. Continue from that phase"
72
- echo "4. If build phase: check which tasks are complete vs pending"
73
- echo ""
74
- echo "---"
75
- echo "*Generated at: $(date -u '+%Y-%m-%dT%H:%M:%SZ')*"
76
- } > "$NOTEPAD"
77
-
78
- # Log compaction event to telemetry
137
+ echo "---"
138
+ echo ""
139
+ } >> "$SESSION_FILE"
140
+
141
+ echo "Pre-compact: appended checkpoint to $SESSION_FILE" >&2
142
+ else
143
+ echo "Pre-compact: aiwiki/ not present — skipped session checkpoint. Telemetry logged." >&2
144
+ fi
145
+
146
+ # Log compaction event to telemetry (unchanged)
79
147
  TELEMETRY_FILE="$STATE_DIR/telemetry.jsonl"
80
- TIMESTAMP="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
81
148
  WORK_JSON="${ACTIVE_WORK:-null}"
82
149
  if [ -n "$ACTIVE_WORK" ]; then WORK_JSON="\"$ACTIVE_WORK\""; fi
150
+ SESSION_JSON="${SESSION_ID:-null}"
151
+ if [ -n "$SESSION_ID" ]; then SESSION_JSON="\"$SESSION_ID\""; fi
83
152
 
84
- echo "{\"event\":\"compact\",\"timestamp\":\"$TIMESTAMP\",\"work\":$WORK_JSON}" >> "$TELEMETRY_FILE"
153
+ echo "{\"event\":\"compact\",\"timestamp\":\"$TIMESTAMP\",\"work\":$WORK_JSON,\"session_id\":$SESSION_JSON,\"aiwiki_active\":$RECENT_AIWIKI_CHANGES}" >> "$TELEMETRY_FILE"
85
154
 
86
- echo "Pre-compact: Saved context recovery state to $NOTEPAD" >&2
87
155
  exit 0
@@ -1,12 +1,19 @@
1
1
  #!/usr/bin/env bash
2
2
  # Session start check: surface promotion-pending gotchas, unresolved hotfix
3
- # workarounds, and paused work items. Runs as a SessionStart hook — warns
4
- # loudly via stderr but does not block (Claude Code treats SessionStart exit
5
- # codes advisorily).
3
+ # workarounds, paused work items, and the latest session handoff. Runs as a
4
+ # SessionStart hook — warns loudly via stderr but does not block (Claude Code
5
+ # treats SessionStart exit codes advisorily).
6
6
  #
7
7
  # Resolves .forge at the repo root via `git rev-parse --show-toplevel` to match
8
8
  # pre-compact.sh and telemetry.sh. Scans `.forge/work/*/` (typed subdirs) and
9
9
  # skips `escalated`/`completed` manifests (terminal states).
10
+ #
11
+ # Session handoff: reads the LATEST aiwiki/sessions/*.md (by mtime, within
12
+ # 7 days) and surfaces:
13
+ # - one-line summary (focus + status)
14
+ # - any "Status: unconsumed" Checkpoints directives the next agent should act on
15
+ # The session file is created lazily by pre-compact.sh / /wrap / /dream / harden —
16
+ # NOT by this hook (avoids empty-file noise from trivial 30-second sessions).
10
17
 
11
18
  set -u
12
19
 
@@ -15,6 +22,38 @@ FORGE_DIR="$PROJECT_ROOT/.forge"
15
22
  GLOBAL_GOTCHAS="$HOME/.claude/gotchas"
16
23
  WARNINGS=""
17
24
 
25
+ # Latest session handoff: find the most recently modified session file within
26
+ # the last 7 days and surface its focus + any unconsumed checkpoints.
27
+ if [ -d "$PROJECT_ROOT/aiwiki/sessions" ]; then
28
+ LATEST_SESSION="$(find "$PROJECT_ROOT/aiwiki/sessions" -maxdepth 1 -type f -name '*.md' \
29
+ ! -name 'INDEX.md' ! -name 'CLAUDE.md' \
30
+ -mtime -7 -print0 2>/dev/null \
31
+ | xargs -0 stat -f '%m %N' 2>/dev/null \
32
+ | sort -rn | head -1 | cut -d' ' -f2-)"
33
+
34
+ if [ -n "$LATEST_SESSION" ] && [ -f "$LATEST_SESSION" ]; then
35
+ SESSION_NAME="$(basename "$LATEST_SESSION" .md)"
36
+ FOCUS="$(grep -m1 '^focus:' "$LATEST_SESSION" 2>/dev/null | sed 's/^focus:[[:space:]]*//')"
37
+ STATUS="$(grep -m1 '^status:' "$LATEST_SESSION" 2>/dev/null | sed 's/^status:[[:space:]]*//')"
38
+ # Count active directives by the exact bold-header pattern. Loose patterns
39
+ # like 'Status: unconsumed' false-positive on prose mentions of the
40
+ # mark-consumed protocol (surfaced during dogfood, 2026-05-18).
41
+ # `grep -c` already prints "0" when no matches; the `|| true` swallows the
42
+ # exit-1 without appending a second "0" (which would break the -gt check
43
+ # below — also dogfood-found).
44
+ UNCONSUMED_COUNT="$(grep -c '^\*\*Dream directive (unconsumed):\*\*$' "$LATEST_SESSION" 2>/dev/null || true)"
45
+ UNCONSUMED_COUNT="${UNCONSUMED_COUNT:-0}"
46
+
47
+ WARNINGS="${WARNINGS}SESSION HANDOFF: previous session ${SESSION_NAME} (status: ${STATUS:-unknown}, focus: ${FOCUS:-unset}).\n"
48
+ WARNINGS="${WARNINGS} File: ${LATEST_SESSION}\n"
49
+ if [ "$UNCONSUMED_COUNT" -gt 0 ]; then
50
+ WARNINGS="${WARNINGS} HARD-INTERRUPT: ${UNCONSUMED_COUNT} unconsumed checkpoint directive(s) — read ## Checkpoints in this file, act on the directive (typically: invoke support-dream), then mark the entry consumed before doing other work.\n"
51
+ elif [ "${STATUS:-}" = "active" ]; then
52
+ WARNINGS="${WARNINGS} Previous session left active (no /wrap fired). Read ## Files touched / ## Next steps for context, then continue or run /wrap to finalize.\n"
53
+ fi
54
+ fi
55
+ fi
56
+
18
57
  # Hard-interrupt: promotion-pending gotchas.
19
58
  # When `support-gotcha` auto-drafts a proposed rule at the 3rd occurrence,
20
59
  # the gotcha's INDEX status column flips to `promotion-pending` and a
@@ -5,6 +5,12 @@
5
5
  #
6
6
  # Output: .forge/state/telemetry.jsonl (one JSON object per line)
7
7
  #
8
+ # Each record now includes work_id when there's an in-progress manifest.
9
+ # gate-enforcer.sh filters by work_id so a stale invocation from another work
10
+ # item no longer satisfies a fresh gate — closes the cross-work-item bypass
11
+ # vector flagged in v6.1-beta audit P0-5. Records with no in-progress work
12
+ # omit the work_id field (null on the consuming side).
13
+ #
8
14
  # Note: the subagent-dispatch tool was renamed `Agent` → `Task` (canonical name
9
15
  # in current Claude Code releases). Both names are accepted here for backward
10
16
  # compatibility with existing telemetry written before the rename; `Task` is
@@ -24,17 +30,41 @@ INPUT=$(cat)
24
30
  TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"')
25
31
  TIMESTAMP=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
26
32
 
33
+ # Locate the in-progress work item (if any). Same scan pattern used by
34
+ # pre-compact.sh and session-start.sh. Cost is sub-millisecond — a small
35
+ # loop over a handful of manifest files — well within PostToolUse overhead.
36
+ ACTIVE_WORK=""
37
+ if [ -d "$PROJECT_ROOT/.forge/work" ]; then
38
+ for manifest in "$PROJECT_ROOT/.forge/work"/*/*/manifest.yaml; do
39
+ if [ -f "$manifest" ] && grep -q 'status: in-progress' "$manifest" 2>/dev/null; then
40
+ WORK_NAME="$(basename "$(dirname "$manifest")")"
41
+ WORK_TYPE="$(basename "$(dirname "$(dirname "$manifest")")")"
42
+ ACTIVE_WORK="$WORK_TYPE/$WORK_NAME"
43
+ break
44
+ fi
45
+ done
46
+ fi
47
+
48
+ # work_id field — emitted only when we resolved an in-progress manifest.
49
+ # Records without work_id naturally fail the enforcer's per-work-id filter,
50
+ # which is the desired behavior (an invocation that happened with no
51
+ # work-in-progress context cannot satisfy a gate on a specific work item).
52
+ WORK_FIELD=""
53
+ if [ -n "$ACTIVE_WORK" ]; then
54
+ WORK_FIELD=",\"work_id\":\"$ACTIVE_WORK\""
55
+ fi
56
+
27
57
  # Extract relevant fields based on tool type.
28
58
  # Task and Agent share input shape (subagent_type + description); accept either.
29
59
  case "$TOOL_NAME" in
30
60
  Skill)
31
61
  SKILL_NAME=$(echo "$INPUT" | jq -r '.tool_input.skill // "unknown"')
32
- echo "{\"timestamp\":\"$TIMESTAMP\",\"type\":\"skill\",\"name\":\"$SKILL_NAME\"}" >> "$TELEMETRY_FILE"
62
+ echo "{\"timestamp\":\"$TIMESTAMP\",\"type\":\"skill\",\"name\":\"$SKILL_NAME\"$WORK_FIELD}" >> "$TELEMETRY_FILE"
33
63
  ;;
34
64
  Task|Agent)
35
65
  AGENT_TYPE=$(echo "$INPUT" | jq -r '.tool_input.subagent_type // "unknown"')
36
66
  DESCRIPTION=$(echo "$INPUT" | jq -r '.tool_input.description // ""')
37
- echo "{\"timestamp\":\"$TIMESTAMP\",\"type\":\"agent\",\"name\":\"$AGENT_TYPE\",\"description\":\"$DESCRIPTION\"}" >> "$TELEMETRY_FILE"
67
+ echo "{\"timestamp\":\"$TIMESTAMP\",\"type\":\"agent\",\"name\":\"$AGENT_TYPE\",\"description\":\"$DESCRIPTION\"$WORK_FIELD}" >> "$TELEMETRY_FILE"
38
68
  ;;
39
69
  esac
40
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jamie-tam/forge",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "description": "AI Development Life Cycle — structured AI-assisted development with quality gates, feature manifests, and agent coordination",
5
5
  "type": "module",
6
6
  "bin": {
@@ -44,7 +44,7 @@ v6 also adds phase skills: concept-slides, build-wireframe, build-prototype, ite
44
44
  refactor/{name}/ # manifest, codebase analysis, tasks, test results
45
45
  hotfix/{name}/ # manifest, minimal debug, smoke tests
46
46
  greenfield/{name}/ # manifest + full project scaffold artifacts
47
- state/ # Runtime state (notepad.md, telemetry, dream history)
47
+ state/ # Runtime state (telemetry, dream history). Per-session handoff lives in aiwiki/sessions/ — see below.
48
48
  ```
49
49
 
50
50
  The `.forge/` directory retains operational state only (manifests, telemetry, dream history). Project knowledge lives in `aiwiki/` (next section).
@@ -61,8 +61,8 @@ Work items are identified by `{type}/{name}` — names may collide across types.
61
61
  | `gotchas/` | Recurring failure modes with reproducible fixes | `iterate-prototype`, `quality-code-review`, manual |
62
62
  | `conventions/` | Established patterns in this codebase | `iterate-prototype`, `harden`, manual |
63
63
  | `architecture/` | Component maps, sequence diagrams | `harden`, manual |
64
- | `sessions/` | Per-session work notes (index-of-links) | manual or `harden` at codify; session-checkpoint hooks deferred |
65
- | `oracle/` | Trusted external references (RFCs, vendor docs) | manual |
64
+ | `sessions/` | Per-session handoff: `## Checkpoints` event log (pre-compact, /dream) + index sections (Files touched, Decisions, Gotchas, Open questions, Next steps) filled by `/wrap`. Filename `{date}-{session_id_short}.md`. Lazy-created by first writer in the session. | `hooks/scripts/pre-compact.sh`, `/wrap`, `/dream`, `harden` |
65
+ | `oracles/` | Prototype behavior snapshots that Phase 6 production code must reproduce (setup trigger → assertions) | `harden` (Step 2.5) |
66
66
  | `raw/` | Append-only research/brainstorm capture (no schema) | `/note <text>`, manual |
67
67
 
68
68
  **Auto-capture during phases.** `iterate-prototype` (Phase 4) writes gotchas and conventions surfaced during prototype iteration. Phases 1 (concept) and 2 (wireframe) have **no auto-capture** — use `/note <text>` to land research/brainstorm in `aiwiki/raw/{date}.md`.
@@ -70,7 +70,7 @@ Work items are identified by `{type}/{name}` — names may collide across types.
70
70
  **Lint** (`wiki-lint` PostToolUse hook). Fires on every `aiwiki/**.md` write (Edit/Write/MultiEdit). Validates frontmatter, required H2 sections, line caps, citation hash drift, and possible-secret patterns in cited content against the page's schema. Skips `aiwiki/raw/` (no schema) and `aiwiki/proposed/` (lint runs there separately at dream-complete). Findings surface on stderr — non-blocking, but the next edit should address them.
71
71
 
72
72
  **Dream** (`support-dream` skill). Async consolidation:
73
- - **PreCompact** (~85% context): consolidates `aiwiki/raw/` + recently-touched typed pages before lossy compaction. (Session-file refinement deferred until session-checkpoint hooks ship see `aiwiki/sessions/` row above.)
73
+ - **PreCompact** (~85% context): consolidates `aiwiki/raw/` + recently-touched typed pages before lossy compaction. Also refines the active session file's `## Checkpoints` event log (if it exceeds ~10 entries) and pre-populates index sections from git diff. The pre-compact hook itself writes an unconsumed dream directive to the session file's checkpoints; the post-compact agent reads it and dispatches this skill.
74
74
  - **Phase-close** (Phase 4/5/6/7 lock): promotes phase outputs into curated wiki state. The next phase **blocks** until the user reviews the proposed dream.
75
75
  - **Manual `/dream`**: user-driven cleanup.
76
76
 
@@ -78,19 +78,39 @@ Dream outputs to `aiwiki/proposed/{dream_id}/` — input store is never modified
78
78
 
79
79
  ### When the AI should write to aiwiki
80
80
 
81
- The default is **don't**. The user-customizable `aiwiki/CLAUDE.md` (auto-loaded when `aiwiki/` is scaffolded by `/setup`) carries the per-page-type writing rules; respect them. Three reminders that override any temptation to over-capture:
81
+ The discipline is **selectivity, not abstention.** AI writes happen routinely — `support-gotcha`, `iterate-prototype` (via `prototype-builder`), and `harden` all write to typed pages directly during their normal operation, and the pre-compact hook + `/wrap` write to `aiwiki/sessions/`. Wiki-lint validates schema on every write; the user can edit or delete any page after the fact. The rule is what content qualifies — not whether AI can write.
82
82
 
83
- 1. **Never write speculation, conversation history, or "we might want to revisit this" notes.** This is the strongest rule in `aiwiki/CLAUDE.md`. Wiki pages answer recurring questions; conversational asides do not.
84
- 2. **Typed pages need durable, reusable knowledge.** Decisions go to `aiwiki/decisions/` only after the decision is finalized (architectural, public-surface, security, schema, or other hard-to-reverse choice). Gotchas to `aiwiki/gotchas/` only when the failure mode is reproducible with a concrete fix. Conventions to `aiwiki/conventions/` only when the pattern is established across multiple sites. If the knowledge isn't yet durable, it doesn't belong in a typed page.
85
- 3. **Ad-hoc research, half-formed thoughts, deferred decisions, comparison notes — these go to `aiwiki/raw/` ONLY when the user explicitly invokes `/note <text>`.** The AI does not autonomously decide to write conversational content to raw. The user is the gate.
83
+ Per-folder discipline:
86
84
 
87
- If a typed-page-worthy insight emerges mid-conversation (e.g., "this is a real gotcha"), you MAY write it to the correct typed page — but only if it meets that page's durability test. When in doubt, ask the user.
85
+ | Folder | Who writes | Gate |
86
+ |---|---|---|
87
+ | `raw/` | **User only**, via `/note <text>` | The user is the gate. AI does NOT decide to write conversational asides to raw on its own. |
88
+ | `decisions/` `gotchas/` `conventions/` `architecture/` `oracles/` | AI writes directly via `support-gotcha`, `iterate-prototype`, `harden`, code-review subagents | Schema (enforced by wiki-lint) + durability test (below). User can edit/delete after-the-fact. |
89
+ | `sessions/` | `pre-compact.sh` (Checkpoints) + `/wrap` (index sections) + `harden` (codify-time session entry) | Schema. `/wrap` confirms with user before setting `status: done`. |
90
+ | `proposed/` | `dreamer` subagent only (via `support-dream`) | User reviews each proposal via `forge wiki ui` / `forge wiki review` and atomically accepts or rejects. |
91
+
92
+ Three durability rules that override any temptation to over-capture:
93
+
94
+ 1. **Never write speculation, conversation history, or "we might want to revisit this" notes.** This is the strongest rule. Wiki pages answer recurring questions; conversational asides do not. Speculation goes to raw via `/note` (user-gated), not to typed pages.
95
+ 2. **Typed pages need durable, reusable knowledge.** ADRs go to `decisions/` only when the choice is finalized AND hard-to-reverse (architectural, public-surface, security, schema). Gotchas to `gotchas/` only when the failure mode is reproducible with a concrete fix. Conventions to `conventions/` only when the pattern is established across multiple sites. If the knowledge isn't yet durable, it doesn't belong in a typed page.
96
+ 3. **Raw is `/note`-only.** Ad-hoc research, half-formed thoughts, deferred decisions, comparison notes all go to `aiwiki/raw/` — and ONLY when the user explicitly invokes `/note <text>`. The AI does not autonomously decide what becomes raw content.
97
+
98
+ When a typed-page-worthy insight emerges mid-conversation (e.g., "this is a real gotcha"), write it to the correct typed page if it meets that page's durability test. When the durability is uncertain, ask the user instead of speculating.
88
99
 
89
- **Session-end handoff.** Forge has no automated session-end capture mechanism. If a session produced durable knowledge that warrants persisting, the AI should OFFER to write the appropriate typed pages before ending the session — never silently. The user accepts or declines.
100
+ **Session-end handoff.** Forge provides `/wrap` for explicit session-end capture. Run it at a natural session boundary: it fills the active session file's index sections (Files touched / Decisions made / Gotchas surfaced / Open questions / Next steps) and sets `status: done`. The next `SessionStart` hook surfaces the latest session's focus and any unconsumed `## Checkpoints` directives. If `/wrap` isn't invoked, the session file (if pre-compact created one) stays `status: active` `SessionStart` will surface it as unfinalized so the next session can pick up. The AI should OFFER to run `/wrap` before a long break or context shift; never silently fabricate a session summary.
90
101
 
91
102
  ## Context Recovery
92
103
 
93
- If `.forge/state/notepad.md` exists, read it FIRST before doing anything else.
104
+ On session start or after compaction, recovery state lives in **two places**, in priority order:
105
+
106
+ 1. **`aiwiki/sessions/{date}-{session_id_short}.md`** — the per-session handoff file. Read this FIRST.
107
+ - The `## Checkpoints` section is an append-only event log. Any entry with header `**Dream directive (unconsumed):**` is an action item for the current agent (typically: invoke `support-dream`). After acting, change the header from `(unconsumed)` to `(consumed at {ISO-timestamp})`. The `session-start.sh` hook counts active directives by matching the bold header literally — flipping the header is what makes the count accurate (dogfood-validated 2026-05-18).
108
+ - The index sections (`## Files touched` / `## Decisions made` / `## Gotchas surfaced` / `## Open questions` / `## Next steps`) describe the prior session's work. If `status: done`, the file was `/wrap`'d. If `status: active`, the prior session didn't run `/wrap` — surface the file as in-progress context.
109
+ 2. **The active manifest** (`.forge/work/*/*/manifest.yaml` with `status: in-progress`). Read after the session file to anchor on the work item's current phase and gate state.
110
+
111
+ `SessionStart` hook (`hooks/scripts/session-start.sh`) automatically surfaces the latest session file's focus + any unconsumed checkpoints on stderr. The agent should still re-read the file's full content when acting on a directive.
112
+
113
+ **Notepad deprecated.** Earlier versions wrote recovery state to `.forge/state/notepad.md`. v6.2 consolidated session recovery into `aiwiki/sessions/{date}-{session_id_short}.md`. If a legacy `.forge/state/notepad.md` exists from an older install, it's safe to delete — nothing reads or writes it anymore.
94
114
 
95
115
  ## Rules
96
116
 
@@ -6,6 +6,8 @@ description: Gate-state names and pass criteria summary — auto-loaded every se
6
6
 
7
7
  Gates block phase transitions until criteria are met. See [references/common/quality-gates.md](../../references/common/quality-gates.md) for the full gate-set tables, pass criteria, and fail actions.
8
8
 
9
+ **Enforcement.** `gate-enforcer.sh` (PreToolUse hook) blocks manifest writes that set `gate-passed: true` unless the gate's required skill/agent (per `hooks/config/gate-requirements.json`) was invoked **for this work item**. The check is `work_id`-scoped: the enforcer extracts `work_id` from the edited manifest's path (`.forge/work/{type}/{name}/manifest.yaml`) and filters `.forge/state/telemetry.jsonl` to records matching that work_id. Stale invocations from another work item — or from before the work_id field was added (legacy records) — do not satisfy fresh gates. After upgrade from a pre-work_id version, re-invoke any required skill once to write a tagged telemetry record.
10
+
9
11
  Gate names (per `hooks/config/gate-requirements.json`):
10
12
  - code-review-final
11
13
  - code-review (per-slice)
@@ -300,6 +300,15 @@ VERDICT: Ready to deploy
300
300
  - If any issues were encountered during deploy, invoke `support-gotcha`
301
301
  - If deployment revealed process improvements, note for `/forge-evolve`
302
302
 
303
+ 4. **Retrospective dream (Phase 7 auto-fire on lock).** After writing `artifacts.deliver.locked_at`, invoke the **support-dream** skill to consolidate the full feature's wiki accumulation. This is the last consolidation pass for the feature — gotchas, conventions, and any deploy-time learnings get merged into the durable wiki state before the manifest closes.
304
+ - **scope:** `aiwiki/raw/`, `aiwiki/gotchas/`, `aiwiki/conventions/`, `aiwiki/sessions/` (any subfolder touched during this feature's lifetime)
305
+ - **trigger:** `phase-close`
306
+ - **trigger_detail:** `"Phase 7 (deliver) retrospective — {feature/name}"`
307
+
308
+ Surface the dream id + review path to the user. The feature manifest's `status: completed` transition should wait until the retrospective dream is reviewed — this is the consolidation pass the next feature's `discover-codebase-analysis` will read from.
309
+
310
+ **Skip when:** no `aiwiki/` writes occurred during this feature (rare — most non-trivial features produce at least one gotcha or convention).
311
+
303
312
  ## Rollback Procedures
304
313
 
305
314
  ### When to Roll Back
@@ -135,9 +135,23 @@ Each write triggers wiki-lint synchronously; lint failures must be resolved befo
135
135
 
136
136
  ### Step 4: trigger a focused dream
137
137
 
138
- After the writes land, fire a phase-close dream over the touched subfolders. The dream is the consolidation pass that may merge new ADRs with existing ones, promote raw entries that became relevant during codification, and prune stale entries the new architecture supersedes.
138
+ After the Step 3 writes land, fire a phase-close dream. Invoke the `support-dream` skill with:
139
139
 
140
- Dream output goes to `aiwiki/proposed/{dream_id}/` for user review. The phase does NOT close until the user accepts or rejects the dream output.
140
+ - **scope:** the subfolders Step 3 touched typically `aiwiki/architecture/`, `aiwiki/decisions/`, `aiwiki/conventions/`, `aiwiki/gotchas/`, `aiwiki/oracles/`, `aiwiki/sessions/`
141
+ - **trigger:** `phase-close`
142
+ - **trigger_detail:** `"Phase 5 (codify) — {feature/name}"`
143
+
144
+ The dream is the consolidation pass: it may merge new ADRs with existing ones, promote raw entries that became relevant during codification, and prune stale entries the new architecture supersedes.
145
+
146
+ Dream output goes to `aiwiki/proposed/{dream_id}/` for user review. Surface the dream id and review path:
147
+
148
+ ```
149
+ Phase 5 writes complete. Dream queued: 2026-05-18-HHMM-codify-{name}
150
+ Review: `forge wiki review {dream_id}` or open `forge wiki ui`
151
+ The codify gate (Step 5) does not pass until this dream is reviewed.
152
+ ```
153
+
154
+ The phase does NOT close until the user accepts or rejects the dream output.
141
155
 
142
156
  ### Step 5: gate
143
157
 
@@ -181,6 +181,28 @@ Two convergence signals:
181
181
 
182
182
  In autopilot mode, the loop hook reads the LOCKED signal from the manifest and exits cleanly.
183
183
 
184
+ ### Step 9: phase-close dream (auto-fire on lock)
185
+
186
+ **Trigger:** Step 8 just wrote `artifacts.prototype.locked_at`. The prototype phase is closing, and Phase 4's accumulated `aiwiki/` writes (gotchas, conventions, raw notes from `/note`) should be consolidated before the next phase (codify) reads from it.
187
+
188
+ **Action:** invoke the `support-dream` skill with:
189
+
190
+ - **scope:** `aiwiki/raw/`, `aiwiki/gotchas/`, `aiwiki/conventions/` (the subfolders Phase 4 wrote to)
191
+ - **trigger:** `phase-close`
192
+ - **trigger_detail:** `"Phase 4 (iterate) lock — {feature/name}"`
193
+
194
+ `support-dream` dispatches the `dreamer` subagent and writes a consolidation proposal to `aiwiki/proposed/{dream_id}/`. Surface the dream id and review path to the user:
195
+
196
+ ```
197
+ Phase 4 closed. Dream queued: 2026-05-18-HHMM-iterate-{name}
198
+ Review: `forge wiki review {dream_id}` or open `forge wiki ui`
199
+ The codify (Phase 5) gate blocks until this dream is reviewed.
200
+ ```
201
+
202
+ **Gate coupling:** the codify gate (`harden` Step 0 readiness check) must verify any phase-4 dream is reviewed before proceeding. The next phase reads consolidated wiki state, so it cannot proceed against pending unreviewed proposals.
203
+
204
+ **Skip when:** no aiwiki writes occurred during Phase 4 (gotchas/conventions/raw all unchanged since prototype scaffold). No-op dreams add noise.
205
+
184
206
  ## Anti-fatigue
185
207
 
186
208
  If pending feedback grows past 10 items without any being resolved, the loop is not iterating — it's accumulating. Surface this to the user explicitly: "Pending list has grown to N items without resolution; consider whether the wireframe needs an update before continuing prototype iteration." This is the prototype-equivalent of the wiki accept-fatigue safeguard.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: support-dream
3
- description: "Use when the user asks to 'consolidate the wiki', 'clean up aiwiki', 'merge duplicate notes', 'prune stale gotchas', or when /forge-evolve runs maintenance. Also auto-fires on phase-close or when context approaches compaction (~85%). Merges aiwiki duplicates, resolves contradictions, refreshes the session index. Output goes to aiwiki/proposed/ for user review — input is never modified."
3
+ description: "Use when the user asks to 'consolidate the wiki', 'clean up aiwiki', 'merge duplicate notes', 'prune stale gotchas', or when /forge-evolve runs maintenance. Auto-fires via /dream slash command (manual), hooks/scripts/pre-compact.sh (PreCompact, ~85% context — writes a 'Status: unconsumed' Checkpoints entry to aiwiki/sessions/{date}-{session_id}.md that the post-compact agent acts on), and phase-close skills (iterate-prototype Step 9, harden Step 4, feature.md Step 8.5 per-slice, deliver-deploy Phase 5.4 retrospective) which dispatch this skill after writing their respective artifacts.{phase}.locked_at. Merges aiwiki duplicates, resolves contradictions, refines the active session file. Output goes to aiwiki/proposed/ for user review — input is never modified."
4
4
  ---
5
5
 
6
6
  # Support: Dream (Wiki Consolidation)
@@ -22,7 +22,7 @@ Three triggers, distinct intents:
22
22
  | Trigger | Intent | Scope |
23
23
  |---|---|---|
24
24
  | **Phase close** (Phase 4 (iterate) lock, Phase 5 (codify) lock, Phase 6 (production-build) per-slice close, Phase 7 (deliver) retrospective) | Promote phase outputs into curated wiki state | Subfolders touched by the phase |
25
- | **PreCompact hook fire** (~85% context utilization) | Consolidate signal into durable form before lossy compaction | `aiwiki/raw/` + recently-touched typed pages. If `aiwiki/sessions/{current}.md` exists, refine it too; if not, skip (session-checkpoint hooks deferred see `templates/aiwiki/schemas/session.md`). |
25
+ | **PreCompact hook fire** (~85% context utilization) | Consolidate signal into durable form before lossy compaction | `aiwiki/raw/` + recently-touched typed pages. Also refines the active session file (`aiwiki/sessions/{date}-{session_id_short}.md`) pre-compact creates it lazily on first fire; dream consolidates its `## Checkpoints` event log if it grows beyond ~10 entries. |
26
26
  | **Manual `/dream`** | User-driven cleanup (after major refactor, branch merge, accumulated noise) | User-specified |
27
27
 
28
28
  **Do NOT skip when:**
@@ -55,14 +55,15 @@ Fired by phase-close hooks (after user emits the phase-lock signal):
55
55
 
56
56
  ### PreCompact trigger
57
57
 
58
- Fired by forge's existing pre-compact hook (~85% context):
58
+ Fired indirectly via the pre-compact hook (`hooks/scripts/pre-compact.sh`). The hook itself cannot dispatch a subagent — instead it writes a `**Dream directive (unconsumed)**` block to the active session file's `## Checkpoints` section. The post-compact agent reads the session file, finds the unconsumed directive, and invokes this skill.
59
59
 
60
- 1. Hook calls skill with scope `[aiwiki/raw/, recently-touched-typed-pages]`. If `aiwiki/sessions/{current}.md` exists, ALSO include it in scope.
61
- 2. **Conditional session prerequisite** — if `aiwiki/sessions/{current}.md` exists (session-checkpoint hooks shipped and active), dream IMPROVES it; does not produce its existence. If it does NOT exist (current state session-checkpoint hooks deferred), dream SKIPS session-file refinement and consolidates raw/typed pages only. No `SESSION_FILE_MISSING` failure.
60
+ 1. Skill is invoked by the post-compact agent acting on the directive. Scope from directive: `aiwiki/raw/` + recently-touched typed pages, plus the active session file itself (`aiwiki/sessions/{date}-{session_id_short}.md`).
61
+ 2. **Session file is always present at PreCompact time** — pre-compact.sh creates it lazily on first fire and appends the checkpoint. Dream refines the session file's `## Checkpoints` section if it has grown unwieldy (>10 entries) and may pre-populate the index sections (`## Files touched`, etc.) from git diff and aiwiki writes, leaving `/wrap` to finalize.
62
62
  3. Dispatches `dreamer` subagent
63
63
  4. Output to `aiwiki/proposed/{dream_id}/`
64
64
  5. LINT runs on proposed output
65
- 6. Native compaction proceeds (does not wait for user review — the consolidated raw/typed pages are already on disk)
65
+ 6. Native compaction does not wait for user review — the consolidated raw/typed pages and session-file proposal are already on disk
66
+ 7. **Mark the directive consumed** — after dispatch returns, the agent changes the originating entry's header from `**Dream directive (unconsumed):**` to `**Dream directive (consumed at {ISO-timestamp}):**`. Header-flip is what `session-start.sh` counts. Appending a separate "Status: consumed" line leaves the original unconsumed-marked header in place and the next session-start will still surface it as a HARD-INTERRUPT (dogfood-validated 2026-05-18).
66
67
 
67
68
  ### Manual /dream trigger
68
69
 
@@ -176,7 +177,7 @@ This skill operates the dream side; the user-facing CLI is separate but document
176
177
  | Mistake | Fix |
177
178
  |---|---|
178
179
  | Modifying `aiwiki/` directly during consolidation | All writes go to `aiwiki/proposed/{dream_id}/`; the swap is the user's action |
179
- | Writing to `aiwiki/sessions/{current}.md` to "create" the file | Session checkpoint hook creates the file; dream only refines an existing file |
180
+ | Writing a new file in `aiwiki/sessions/` instead of refining the active session file | The active session file is `aiwiki/sessions/{date}-{session_id_short}.md` created by pre-compact, /wrap, /dream, or harden. Dream refines that file in place (via the proposed-output path); it does not produce a separate sessions file. |
180
181
  | Running dream when there's no signal to consolidate | Skip the dream entirely; record nothing |
181
182
  | Skipping LINT on proposed output | LINT MUST run; lint warnings go into the manifest, surface during review |
182
183
  | Promoting raw entries without reading the target schema | Read `aiwiki/schemas/{type}.md` first; the raw entry must satisfy required sections + frontmatter |