@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.
- package/README.md +33 -29
- package/agents/dreamer.md +10 -6
- package/commands/discover.md +2 -0
- package/commands/dream.md +71 -0
- package/commands/feature.md +7 -2
- package/commands/wrap.md +130 -0
- package/dist/init.js +1 -1
- package/hooks/config/gate-requirements.json +1 -1
- package/hooks/scripts/gate-enforcer.sh +51 -6
- package/hooks/scripts/pre-compact.sh +120 -52
- package/hooks/scripts/session-start.sh +42 -3
- package/hooks/scripts/telemetry.sh +32 -2
- package/package.json +1 -1
- package/rules/common/forge-system.md +31 -11
- package/rules/common/quality-gates.md +2 -0
- package/skills/deliver-deploy/SKILL.md +9 -0
- package/skills/harden/SKILL.md +16 -2
- package/skills/iterate-prototype/SKILL.md +22 -0
- package/skills/support-dream/SKILL.md +8 -7
- package/templates/aiwiki/CLAUDE.md.template +48 -22
- package/templates/aiwiki/schemas/session.md +133 -49
|
@@ -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
|
|
6
|
-
#
|
|
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
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
22
|
-
|
|
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
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
echo "
|
|
59
|
-
echo
|
|
60
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
echo "
|
|
72
|
-
|
|
73
|
-
echo ""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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,
|
|
4
|
-
# loudly via stderr but does not block (Claude Code
|
|
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
|
@@ -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 (
|
|
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
|
|
65
|
-
| `
|
|
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. (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
package/skills/harden/SKILL.md
CHANGED
|
@@ -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
|
|
138
|
+
After the Step 3 writes land, fire a phase-close dream. Invoke the `support-dream` skill with:
|
|
139
139
|
|
|
140
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
61
|
-
2. **
|
|
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
|
|
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
|
|
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 |
|