create-claude-rails 0.3.4 → 0.4.1
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/lib/cli.js +14 -5
- package/lib/copy.js +18 -6
- package/lib/settings-merge.js +9 -0
- package/package.json +1 -1
- package/templates/hooks/cor-upstream-guard.sh +79 -0
- package/templates/scripts/cor-drift-check.cjs +84 -0
- package/templates/skills/debrief/SKILL.md +26 -4
- package/templates/skills/debrief/phases/upstream-feedback.md +106 -0
package/lib/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ const MODULES = {
|
|
|
15
15
|
name: 'Session Loop (orient + debrief)',
|
|
16
16
|
description: 'Context continuity between sessions. Claude starts informed, ends by recording what happened.',
|
|
17
17
|
mandatory: true,
|
|
18
|
-
templates: ['skills/orient', 'skills/debrief', 'skills/menu', 'hooks/stop-hook.md'],
|
|
18
|
+
templates: ['skills/orient', 'skills/debrief', 'skills/debrief/phases/upstream-feedback.md', 'skills/menu', 'hooks/stop-hook.md'],
|
|
19
19
|
},
|
|
20
20
|
'hooks': {
|
|
21
21
|
name: 'Git Guardrails + Telemetry',
|
|
@@ -23,7 +23,7 @@ const MODULES = {
|
|
|
23
23
|
mandatory: false,
|
|
24
24
|
default: true,
|
|
25
25
|
lean: true,
|
|
26
|
-
templates: ['hooks/git-guardrails.sh', 'hooks/skill-telemetry.sh', 'hooks/skill-tool-telemetry.sh'],
|
|
26
|
+
templates: ['hooks/git-guardrails.sh', 'hooks/cor-upstream-guard.sh', 'hooks/skill-telemetry.sh', 'hooks/skill-tool-telemetry.sh', 'scripts/cor-drift-check.cjs'],
|
|
27
27
|
},
|
|
28
28
|
'work-tracking': {
|
|
29
29
|
name: 'Work Tracking (pib-db or markdown)',
|
|
@@ -183,10 +183,12 @@ async function run() {
|
|
|
183
183
|
// --- Directory detection ---
|
|
184
184
|
const dirState = detectProjectState(projectDir);
|
|
185
185
|
|
|
186
|
+
let existingManifest = {};
|
|
186
187
|
if (dirState === 'existing-install') {
|
|
187
188
|
const existing = readMetadata(projectDir);
|
|
189
|
+
existingManifest = existing.manifest || {};
|
|
188
190
|
console.log(` Found existing installation (v${existing.version}, installed ${existing.installedAt.split('T')[0]})`);
|
|
189
|
-
console.log('
|
|
191
|
+
console.log(' Updating upstream-managed files and adding new files.');
|
|
190
192
|
if (!flags.yes && !flags.lean) {
|
|
191
193
|
const { proceed } = await prompts({
|
|
192
194
|
type: 'confirm',
|
|
@@ -401,6 +403,7 @@ async function run() {
|
|
|
401
403
|
skipConflicts: flags.yes || dirState === 'existing-install',
|
|
402
404
|
skipPhases: isSkill,
|
|
403
405
|
projectRoot: projectDir,
|
|
406
|
+
existingManifest,
|
|
404
407
|
});
|
|
405
408
|
totalCopied += results.copied.length;
|
|
406
409
|
totalSkipped += results.skipped.length;
|
|
@@ -429,8 +432,14 @@ async function run() {
|
|
|
429
432
|
}
|
|
430
433
|
|
|
431
434
|
if (flags.yes || dirState === 'existing-install') {
|
|
432
|
-
//
|
|
433
|
-
|
|
435
|
+
// If file is in the old manifest, it's upstream-managed — overwrite.
|
|
436
|
+
// If not, it's project-created — skip.
|
|
437
|
+
if (existingManifest[mPath]) {
|
|
438
|
+
if (!flags.dryRun) fs.copyFileSync(srcPath, destPath);
|
|
439
|
+
totalOverwritten++;
|
|
440
|
+
} else {
|
|
441
|
+
totalSkipped++;
|
|
442
|
+
}
|
|
434
443
|
allManifest[mPath] = incomingHash;
|
|
435
444
|
} else {
|
|
436
445
|
const response = await prompts({
|
package/lib/copy.js
CHANGED
|
@@ -11,13 +11,13 @@ function hashContent(content) {
|
|
|
11
11
|
* Recursively copy files from src to dest, surfacing conflicts.
|
|
12
12
|
* Returns { copied: string[], skipped: string[], overwritten: string[] }
|
|
13
13
|
*/
|
|
14
|
-
async function copyTemplates(src, dest, { dryRun = false, skipConflicts = false, skipPhases = false, projectRoot = null } = {}) {
|
|
14
|
+
async function copyTemplates(src, dest, { dryRun = false, skipConflicts = false, skipPhases = false, projectRoot = null, existingManifest = {} } = {}) {
|
|
15
15
|
const results = { copied: [], skipped: [], overwritten: [], manifest: {} };
|
|
16
|
-
await walkAndCopy(src, dest, src, results, dryRun, skipConflicts, skipPhases, projectRoot);
|
|
16
|
+
await walkAndCopy(src, dest, src, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest);
|
|
17
17
|
return results;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipConflicts, skipPhases, projectRoot) {
|
|
20
|
+
async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest) {
|
|
21
21
|
const entries = fs.readdirSync(currentSrc, { withFileTypes: true });
|
|
22
22
|
|
|
23
23
|
for (const entry of entries) {
|
|
@@ -37,7 +37,7 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
|
|
|
37
37
|
if (!dryRun && !fs.existsSync(destPath)) {
|
|
38
38
|
fs.mkdirSync(destPath, { recursive: true });
|
|
39
39
|
}
|
|
40
|
-
await walkAndCopy(srcRoot, destRoot, srcPath, results, dryRun, skipConflicts, skipPhases, projectRoot);
|
|
40
|
+
await walkAndCopy(srcRoot, destRoot, srcPath, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest);
|
|
41
41
|
} else {
|
|
42
42
|
const incoming = fs.readFileSync(srcPath, 'utf8');
|
|
43
43
|
const incomingHash = hashContent(incoming);
|
|
@@ -52,8 +52,20 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
if (skipConflicts) {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// Check if file is upstream-managed (in the old manifest).
|
|
56
|
+
// If so, overwrite — it can't be customized (hook enforces this).
|
|
57
|
+
// If not, skip — it's project-created content.
|
|
58
|
+
const manifestKey = projectRoot
|
|
59
|
+
? path.relative(projectRoot, destPath)
|
|
60
|
+
: relPath;
|
|
61
|
+
if (existingManifest[manifestKey]) {
|
|
62
|
+
if (!dryRun) fs.copyFileSync(srcPath, destPath);
|
|
63
|
+
results.overwritten.push(relPath);
|
|
64
|
+
results.manifest[relPath] = incomingHash;
|
|
65
|
+
} else {
|
|
66
|
+
results.skipped.push(relPath);
|
|
67
|
+
results.manifest[relPath] = incomingHash;
|
|
68
|
+
}
|
|
57
69
|
continue;
|
|
58
70
|
}
|
|
59
71
|
|
package/lib/settings-merge.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CoR Upstream Guard — PreToolUse hook for Edit and Write tool calls
|
|
3
|
+
#
|
|
4
|
+
# Blocks modifications to files managed by Claude on Rails. These files
|
|
5
|
+
# are upstream-owned: updates come through /cor-upgrade, not direct edits.
|
|
6
|
+
# Project-specific customization goes in _context.md and phase files.
|
|
7
|
+
#
|
|
8
|
+
# How it works:
|
|
9
|
+
# Reads .corrc.json manifest (list of CoR-installed files with hashes).
|
|
10
|
+
# If the target file_path is in the manifest, block the write.
|
|
11
|
+
#
|
|
12
|
+
# ROLLBACK: Comment out the PreToolUse entry for this hook in
|
|
13
|
+
# .claude/settings.json to disable it immediately.
|
|
14
|
+
#
|
|
15
|
+
# Hook contract:
|
|
16
|
+
# Input: $CLAUDE_TOOL_INPUT has the tool use JSON with "file_path" field
|
|
17
|
+
# Output: JSON on stdout with { "decision": "block"|"allow", "reason": "..." }
|
|
18
|
+
|
|
19
|
+
# Extract file_path from tool input
|
|
20
|
+
FILE_PATH=$(echo "$CLAUDE_TOOL_INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('file_path',''))" 2>/dev/null)
|
|
21
|
+
|
|
22
|
+
if [ -z "$FILE_PATH" ]; then
|
|
23
|
+
echo '{"decision":"allow"}'
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Find the project root (where .corrc.json lives)
|
|
28
|
+
# Walk up from current directory
|
|
29
|
+
find_project_root() {
|
|
30
|
+
local dir="$PWD"
|
|
31
|
+
while [ "$dir" != "/" ]; do
|
|
32
|
+
if [ -f "$dir/.corrc.json" ]; then
|
|
33
|
+
echo "$dir"
|
|
34
|
+
return 0
|
|
35
|
+
fi
|
|
36
|
+
dir=$(dirname "$dir")
|
|
37
|
+
done
|
|
38
|
+
return 1
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
PROJECT_ROOT=$(find_project_root)
|
|
42
|
+
|
|
43
|
+
if [ -z "$PROJECT_ROOT" ]; then
|
|
44
|
+
# No .corrc.json found — not a CoR project, allow everything
|
|
45
|
+
echo '{"decision":"allow"}'
|
|
46
|
+
exit 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Resolve file_path to a relative path from project root
|
|
50
|
+
# Handle both absolute and relative paths
|
|
51
|
+
if [[ "$FILE_PATH" = /* ]]; then
|
|
52
|
+
# Absolute path — make relative to project root
|
|
53
|
+
REL_PATH="${FILE_PATH#$PROJECT_ROOT/}"
|
|
54
|
+
# If the path didn't change, the file is outside the project
|
|
55
|
+
if [ "$REL_PATH" = "$FILE_PATH" ]; then
|
|
56
|
+
echo '{"decision":"allow"}'
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
else
|
|
60
|
+
REL_PATH="$FILE_PATH"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Check if this relative path is in the manifest
|
|
64
|
+
IN_MANIFEST=$(python3 -c "
|
|
65
|
+
import json, sys
|
|
66
|
+
try:
|
|
67
|
+
with open('$PROJECT_ROOT/.corrc.json') as f:
|
|
68
|
+
data = json.load(f)
|
|
69
|
+
manifest = data.get('manifest', {})
|
|
70
|
+
print('yes' if '$REL_PATH' in manifest else 'no')
|
|
71
|
+
except:
|
|
72
|
+
print('no')
|
|
73
|
+
" 2>/dev/null)
|
|
74
|
+
|
|
75
|
+
if [ "$IN_MANIFEST" = "yes" ]; then
|
|
76
|
+
echo "{\"decision\":\"block\",\"reason\":\"Blocked: $REL_PATH is managed by Claude on Rails. CoR-managed files are upstream-owned — edits come through /cor-upgrade, not direct modification. Put project-specific content in _context.md or phase files instead.\"}"
|
|
77
|
+
else
|
|
78
|
+
echo '{"decision":"allow"}'
|
|
79
|
+
fi
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CoR Drift Check — detect modified upstream-managed files
|
|
3
|
+
//
|
|
4
|
+
// Compares current file hashes against .corrc.json manifest hashes.
|
|
5
|
+
// Reports files that have been modified since install (drift).
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// node scripts/cor-drift-check.js # exit 0 if clean, 1 if drift
|
|
9
|
+
// node scripts/cor-drift-check.js --json # output JSON for programmatic use
|
|
10
|
+
// node scripts/cor-drift-check.js --fix # show what /cor-upgrade would fix
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const crypto = require('crypto');
|
|
15
|
+
|
|
16
|
+
const projectRoot = findProjectRoot();
|
|
17
|
+
if (!projectRoot) {
|
|
18
|
+
console.error('No .corrc.json found — not a CoR project.');
|
|
19
|
+
process.exit(2);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const metadataPath = path.join(projectRoot, '.corrc.json');
|
|
23
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
24
|
+
const manifest = metadata.manifest || {};
|
|
25
|
+
|
|
26
|
+
function findProjectRoot() {
|
|
27
|
+
let dir = process.cwd();
|
|
28
|
+
while (dir !== path.dirname(dir)) {
|
|
29
|
+
if (fs.existsSync(path.join(dir, '.corrc.json'))) return dir;
|
|
30
|
+
dir = path.dirname(dir);
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function hashContent(content) {
|
|
36
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const drifted = [];
|
|
40
|
+
const missing = [];
|
|
41
|
+
const clean = [];
|
|
42
|
+
|
|
43
|
+
for (const [relPath, expectedHash] of Object.entries(manifest)) {
|
|
44
|
+
const fullPath = path.join(projectRoot, relPath);
|
|
45
|
+
|
|
46
|
+
if (!fs.existsSync(fullPath)) {
|
|
47
|
+
missing.push(relPath);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
52
|
+
const currentHash = hashContent(content);
|
|
53
|
+
|
|
54
|
+
if (currentHash !== expectedHash) {
|
|
55
|
+
drifted.push(relPath);
|
|
56
|
+
} else {
|
|
57
|
+
clean.push(relPath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const args = process.argv.slice(2);
|
|
62
|
+
|
|
63
|
+
if (args.includes('--json')) {
|
|
64
|
+
console.log(JSON.stringify({ drifted, missing, clean: clean.length }, null, 2));
|
|
65
|
+
} else {
|
|
66
|
+
if (drifted.length === 0 && missing.length === 0) {
|
|
67
|
+
console.log(`All ${clean.length} CoR-managed files match upstream hashes.`);
|
|
68
|
+
} else {
|
|
69
|
+
if (drifted.length > 0) {
|
|
70
|
+
console.log(`Drifted (${drifted.length} files modified from upstream):`);
|
|
71
|
+
for (const f of drifted) console.log(` ${f}`);
|
|
72
|
+
}
|
|
73
|
+
if (missing.length > 0) {
|
|
74
|
+
console.log(`Missing (${missing.length} files deleted):`);
|
|
75
|
+
for (const f of missing) console.log(` ${f}`);
|
|
76
|
+
}
|
|
77
|
+
console.log(`\nClean: ${clean.length} files match.`);
|
|
78
|
+
if (args.includes('--fix')) {
|
|
79
|
+
console.log('\nRun /cor-upgrade to restore drifted files to upstream versions.');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
process.exit(drifted.length > 0 || missing.length > 0 ? 1 : 0);
|
|
@@ -28,6 +28,9 @@ related:
|
|
|
28
28
|
- type: file
|
|
29
29
|
path: .claude/skills/debrief/phases/loose-ends.md
|
|
30
30
|
role: "Project-specific: non-project items to capture"
|
|
31
|
+
- type: file
|
|
32
|
+
path: .claude/skills/debrief/phases/upstream-feedback.md
|
|
33
|
+
role: "Instruction: surface CoR friction back to source repo"
|
|
31
34
|
- type: file
|
|
32
35
|
path: .claude/skills/debrief/phases/report.md
|
|
33
36
|
role: "Project-specific: how to present the summary"
|
|
@@ -182,7 +185,24 @@ them now while context is fresh.
|
|
|
182
185
|
> different destinations (memory/feedback vs finding database). Both
|
|
183
186
|
> feed the enforcement pipeline, but through different channels.
|
|
184
187
|
|
|
185
|
-
### 8.
|
|
188
|
+
### 8. Upstream Feedback (core)
|
|
189
|
+
|
|
190
|
+
Read `phases/upstream-feedback.md`. This is an **instruction phase**
|
|
191
|
+
shipped with CoR — it tells Claude to reflect on whether the session
|
|
192
|
+
revealed friction with any CoR-provided skill, phase, or convention.
|
|
193
|
+
|
|
194
|
+
If friction is found, Claude drafts a short feedback item and surfaces
|
|
195
|
+
it in the report for the user to confirm, edit, or dismiss. If
|
|
196
|
+
confirmed, the feedback is delivered to the CoR repo (via local link
|
|
197
|
+
or GitHub issue). If nothing — the phase is silent.
|
|
198
|
+
|
|
199
|
+
This is different from `/extract` (which proposes generalizable
|
|
200
|
+
artifacts for upstreaming). This captures field friction: what hurt,
|
|
201
|
+
what was confusing, what needed a workaround.
|
|
202
|
+
|
|
203
|
+
**This phase should not be skipped.** It's how CoR learns from use.
|
|
204
|
+
|
|
205
|
+
### 9. Capture Loose Ends (core)
|
|
186
206
|
|
|
187
207
|
Read `phases/loose-ends.md` for non-project items and environmental
|
|
188
208
|
concerns to capture before closing. Sessions generate non-project
|
|
@@ -191,14 +211,14 @@ these aren't captured somewhere, they rely on human memory.
|
|
|
191
211
|
|
|
192
212
|
**Skip (absent/empty).**
|
|
193
213
|
|
|
194
|
-
###
|
|
214
|
+
### 10. Discover Custom Phases
|
|
195
215
|
|
|
196
216
|
After running the core phases above, check for any additional phase
|
|
197
217
|
files in `phases/` that the skeleton doesn't define. These are project-
|
|
198
218
|
specific extensions. Each custom phase file declares its position in
|
|
199
219
|
the workflow. Execute them at their declared position.
|
|
200
220
|
|
|
201
|
-
###
|
|
221
|
+
### 11. Present Report (presentation)
|
|
202
222
|
|
|
203
223
|
Read `phases/report.md` for how to present the debrief summary.
|
|
204
224
|
|
|
@@ -218,6 +238,7 @@ Read `phases/report.md` for how to present the debrief summary.
|
|
|
218
238
|
| `update-state.md` | Default: check system-status.md | What state files to update |
|
|
219
239
|
| `health-checks.md` | Skip | Session-end health checks |
|
|
220
240
|
| `record-lessons.md` | Default: ask what was learned | How to capture learnings |
|
|
241
|
+
| `upstream-feedback.md` | **Instruction: always runs** | Surface CoR friction to source repo |
|
|
221
242
|
| `loose-ends.md` | Skip | Non-project items to capture |
|
|
222
243
|
| `report.md` | Default: brief summary | How to present the report |
|
|
223
244
|
|
|
@@ -228,7 +249,8 @@ Phases are either **core** (maintain system state) or **presentation**
|
|
|
228
249
|
skip presentation phases. Core phases always run.
|
|
229
250
|
|
|
230
251
|
- **Core phases** (always run): inventory, close-work, auto-maintenance,
|
|
231
|
-
update-state, health-checks, record-lessons,
|
|
252
|
+
update-state, health-checks, record-lessons, upstream-feedback,
|
|
253
|
+
loose-ends, persist work
|
|
232
254
|
- **Presentation phases** (skippable): report
|
|
233
255
|
|
|
234
256
|
A project that wants a quick debrief variant skips the report and
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Upstream Feedback — Surface CoR Friction to the Source
|
|
2
|
+
|
|
3
|
+
**Position:** Runs after record-lessons (step 7), before capture loose
|
|
4
|
+
ends (step 8). Lessons are fresh; friction is top of mind.
|
|
5
|
+
|
|
6
|
+
**This is an instruction phase** — it tells Claude what to do, not a
|
|
7
|
+
customization point for the project. It ships with CoR and should not
|
|
8
|
+
be deleted or replaced with `skip: true`.
|
|
9
|
+
|
|
10
|
+
## What This Phase Does
|
|
11
|
+
|
|
12
|
+
During debrief, Claude already has full session context: what was built,
|
|
13
|
+
what went wrong, what was learned. This phase asks Claude to reflect on
|
|
14
|
+
one narrow question: **was there friction with anything CoR provided?**
|
|
15
|
+
|
|
16
|
+
- A skill whose flow didn't match how the project actually works
|
|
17
|
+
- A phase file whose default behavior was wrong or confusing
|
|
18
|
+
- A convention that fought the project's grain
|
|
19
|
+
- A missing capability that required a workaround
|
|
20
|
+
- An unclear SKILL.md that led to wasted time
|
|
21
|
+
|
|
22
|
+
This is NOT the same as `/extract` (which looks for generalizable
|
|
23
|
+
artifacts to upstream). This is field feedback — "this thing you shipped
|
|
24
|
+
hurt when I used it."
|
|
25
|
+
|
|
26
|
+
## Workflow
|
|
27
|
+
|
|
28
|
+
### 1. Claude Reflects (silent)
|
|
29
|
+
|
|
30
|
+
Review the session for CoR-specific friction. Consider:
|
|
31
|
+
|
|
32
|
+
- Did any CoR skill need to be worked around or used in an unintended way?
|
|
33
|
+
- Did a phase file's default behavior cause confusion or extra work?
|
|
34
|
+
- Was a SKILL.md unclear, leading to misinterpretation?
|
|
35
|
+
- Did the skeleton/phase separation feel wrong for something?
|
|
36
|
+
- Was something missing that would have helped?
|
|
37
|
+
- Did orient or debrief surface irrelevant information or miss something important?
|
|
38
|
+
|
|
39
|
+
If nothing comes to mind — **stop here silently**. Most sessions have
|
|
40
|
+
no CoR friction. Do not prompt the user with "any CoR feedback?" every
|
|
41
|
+
time. The phase produces nothing and costs nothing unless there's
|
|
42
|
+
something real.
|
|
43
|
+
|
|
44
|
+
### 2. Draft Feedback (if friction found)
|
|
45
|
+
|
|
46
|
+
For each friction point, draft a short feedback item:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
## [Short title]
|
|
50
|
+
|
|
51
|
+
**Skill/phase:** [which CoR component]
|
|
52
|
+
**Friction:** [what happened — 2-3 sentences max]
|
|
53
|
+
**Suggestion:** [what might be better — optional, can be "not sure"]
|
|
54
|
+
**Session context:** [one line about what the project was doing when this came up]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Keep it concrete. "The plan skill was confusing" is not useful.
|
|
58
|
+
"The plan skill's critique phase activated 4 perspectives when only 1
|
|
59
|
+
was relevant, adding 3 minutes of noise to every plan" is useful.
|
|
60
|
+
|
|
61
|
+
### 3. Surface for Confirmation
|
|
62
|
+
|
|
63
|
+
Include the draft in the debrief report under a distinct heading:
|
|
64
|
+
|
|
65
|
+
> **Upstream feedback for CoR:**
|
|
66
|
+
> I noticed friction with [component]. Here's what I'd send:
|
|
67
|
+
> [draft]
|
|
68
|
+
>
|
|
69
|
+
> Send this upstream? (yes / edit / skip)
|
|
70
|
+
|
|
71
|
+
The user confirms, edits, or dismisses. One quick decision per item.
|
|
72
|
+
Do not ask open-ended questions. Do not batch — if there are multiple
|
|
73
|
+
friction points (rare), present each separately.
|
|
74
|
+
|
|
75
|
+
### 4. Deliver
|
|
76
|
+
|
|
77
|
+
If the user confirms, deliver the feedback. Detection and delivery
|
|
78
|
+
follow the same pattern as `/extract`:
|
|
79
|
+
|
|
80
|
+
**If linked** (the CoR package resolves to a local directory — check
|
|
81
|
+
if `node -e "console.log(require.resolve('create-claude-rails'))"`
|
|
82
|
+
points to a local path rather than a `node_modules` path):
|
|
83
|
+
|
|
84
|
+
- Write the feedback as a markdown file in the CoR repo's `feedback/`
|
|
85
|
+
directory (create it if needed)
|
|
86
|
+
- Filename: `[source-project]-[date]-[short-title].md`
|
|
87
|
+
(e.g., `flow-2026-04-04-plan-critique-noise.md`)
|
|
88
|
+
- Add frontmatter: `type: field-feedback`, `source: [project]`,
|
|
89
|
+
`date: [ISO date]`, `component: [skill/phase name]`
|
|
90
|
+
|
|
91
|
+
**If not linked** (CoR is installed from npm):
|
|
92
|
+
|
|
93
|
+
- Open a GitHub issue on the CoR repo
|
|
94
|
+
- Title: `Field feedback: [short title]`
|
|
95
|
+
- Label: `field-feedback` (create if needed)
|
|
96
|
+
- Body: the feedback markdown
|
|
97
|
+
|
|
98
|
+
**If neither works** (no link, no gh access):
|
|
99
|
+
|
|
100
|
+
- Output the feedback to the terminal and tell the user to file
|
|
101
|
+
it manually or copy it to the CoR repo
|
|
102
|
+
|
|
103
|
+
### 5. Done
|
|
104
|
+
|
|
105
|
+
Note in the debrief report what was sent and where. Move on to the
|
|
106
|
+
next phase.
|