@ijfw/memory-server 1.3.0 → 1.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.
Files changed (68) hide show
  1. package/README.md +67 -0
  2. package/fixtures/team/book.json +47 -0
  3. package/fixtures/team/business.json +47 -0
  4. package/fixtures/team/content.json +47 -0
  5. package/fixtures/team/design.json +47 -0
  6. package/fixtures/team/mixed.json +59 -0
  7. package/fixtures/team/research.json +47 -0
  8. package/fixtures/team/software.json +47 -0
  9. package/package.json +1 -9
  10. package/src/.registry-meta-key.pem +3 -0
  11. package/src/active-extension-writer.js +142 -0
  12. package/src/blackboard.js +360 -0
  13. package/src/cli-run.js +91 -0
  14. package/src/codex-agents.js +177 -0
  15. package/src/compute/extract.js +3 -0
  16. package/src/compute/fts5.js +4 -4
  17. package/src/compute/graph-lock.js +0 -2
  18. package/src/compute/migrations/003-tier-semantic.js +3 -3
  19. package/src/compute/runner.js +44 -15
  20. package/src/compute/schema.sql +1 -1
  21. package/src/cross-orchestrator-cli.js +974 -13
  22. package/src/cross-orchestrator.js +9 -1
  23. package/src/dashboard-client.html +353 -1
  24. package/src/dashboard-server.js +318 -2
  25. package/src/design-intelligence.js +721 -0
  26. package/src/dispatch/colon-syntax.js +31 -3
  27. package/src/dispatch/domain-manifest.js +251 -0
  28. package/src/dispatch/extension.js +637 -0
  29. package/src/dispatch/override.js +221 -0
  30. package/src/dispatch-planner.js +1 -0
  31. package/src/dream/runner.mjs +3 -3
  32. package/src/extension-installer.js +1269 -0
  33. package/src/extension-manifest-schema.js +301 -0
  34. package/src/extension-permission-check.mjs +79 -0
  35. package/src/extension-registry.js +619 -0
  36. package/src/extension-signer.js +905 -0
  37. package/src/gate-result-formatter.js +95 -0
  38. package/src/gate-result-schema.js +274 -0
  39. package/src/gate-result.js +195 -0
  40. package/src/intent-router.js +2 -0
  41. package/src/lib/npm-view.js +1 -0
  42. package/src/memory/fts5.js +3 -3
  43. package/src/memory/migrations/002-tier-semantic.js +2 -2
  44. package/src/memory/staleness.js +1 -1
  45. package/src/memory/tier-promotion.js +6 -6
  46. package/src/memory/tokenize.js +1 -1
  47. package/src/memory-feedback.js +372 -0
  48. package/src/override-manifest-schema.js +146 -0
  49. package/src/override-resolver.js +699 -0
  50. package/src/override-use-registry.js +307 -0
  51. package/src/overrides/presets/academic.md +101 -0
  52. package/src/overrides/presets/book.md +87 -0
  53. package/src/overrides/presets/campaign.md +95 -0
  54. package/src/overrides/presets/screenplay.md +99 -0
  55. package/src/recovery/checkpoint.js +191 -0
  56. package/src/redactor.js +2 -0
  57. package/src/runtime-mediator.js +207 -0
  58. package/src/sandbox.js +17 -3
  59. package/src/server.js +94 -2
  60. package/src/swarm/dispatch-prompt.js +154 -0
  61. package/src/swarm/planner.js +399 -0
  62. package/src/swarm/review.js +136 -0
  63. package/src/swarm/worktree.js +239 -0
  64. package/src/team/generator.js +119 -0
  65. package/src/team/schemas.js +341 -0
  66. package/src/trident/dispatch.js +47 -0
  67. package/src/update-check.js +1 -1
  68. package/src/vectors.js +7 -8
@@ -0,0 +1,307 @@
1
+ /**
2
+ * override-use-registry.js
3
+ *
4
+ * IJFW v1.4.0 Wave 3 / t14 — Cross-Project Override-Use Registry
5
+ *
6
+ * Per F8: replace SHA256 fingerprint + BM25 search with a STRUCTURED
7
+ * registry. When a user has the same overrides active in N+ projects, the
8
+ * prelude can suggest promoting them to user defaults.
9
+ *
10
+ * Storage: ~/.ijfw/state/override-use.json (kept SEPARATE from
11
+ * active-overrides.json per R12 — different concerns. active-overrides.json
12
+ * tracks per-project state for the resolver; this registry tracks
13
+ * cross-project signal for promotion intelligence and can include
14
+ * project_type and other analytic fields the resolver doesn't need).
15
+ *
16
+ * Shape:
17
+ * {
18
+ * "schema_version": "1.0",
19
+ * "projects": {
20
+ * "/abs/path/to/project": {
21
+ * "project_type": "book",
22
+ * "active_overrides": [
23
+ * { "preset": "book", "scope": "project",
24
+ * "applied_at": "ISO-8601" }
25
+ * ]
26
+ * }
27
+ * }
28
+ * }
29
+ *
30
+ * Zero new prod deps. Built-in Node only.
31
+ */
32
+
33
+ import fs from 'node:fs/promises';
34
+ import path from 'node:path';
35
+ import os from 'node:os';
36
+
37
+ const SCHEMA_VERSION = '1.0';
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Path helpers — read os.homedir()/HOME at call time so tests can swap HOME
41
+ // via process.env between calls.
42
+ // ---------------------------------------------------------------------------
43
+
44
+ function homeDir() {
45
+ // os.homedir() honours process.env.HOME on POSIX, which is what the smoke
46
+ // test in the t14 brief assumes. Use os.homedir() to keep cross-platform
47
+ // semantics aligned with the rest of the codebase.
48
+ return os.homedir();
49
+ }
50
+
51
+ function registryPath() {
52
+ return path.join(homeDir(), '.ijfw', 'state', 'override-use.json');
53
+ }
54
+
55
+ function settingsPath() {
56
+ return path.join(homeDir(), '.ijfw', 'state', 'settings.json');
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // IO
61
+ // ---------------------------------------------------------------------------
62
+
63
+ async function readRegistry() {
64
+ const p = registryPath();
65
+ try {
66
+ const raw = await fs.readFile(p, 'utf8');
67
+ const parsed = JSON.parse(raw);
68
+ if (!parsed || typeof parsed !== 'object') {
69
+ return { schema_version: SCHEMA_VERSION, projects: {} };
70
+ }
71
+ if (!parsed.projects || typeof parsed.projects !== 'object') {
72
+ parsed.projects = {};
73
+ }
74
+ if (!parsed.schema_version) parsed.schema_version = SCHEMA_VERSION;
75
+ return parsed;
76
+ } catch (err) {
77
+ if (err && err.code === 'ENOENT') {
78
+ return { schema_version: SCHEMA_VERSION, projects: {} };
79
+ }
80
+ if (err instanceof SyntaxError) {
81
+ return { schema_version: SCHEMA_VERSION, projects: {} };
82
+ }
83
+ throw err;
84
+ }
85
+ }
86
+
87
+ async function writeRegistry(state) {
88
+ const p = registryPath();
89
+ await fs.mkdir(path.dirname(p), { recursive: true });
90
+ const tmp = `${p}.tmp`;
91
+ await fs.writeFile(tmp, JSON.stringify(state, null, 2), 'utf8');
92
+ await fs.rename(tmp, p);
93
+ }
94
+
95
+ async function readSettings() {
96
+ try {
97
+ const raw = await fs.readFile(settingsPath(), 'utf8');
98
+ const parsed = JSON.parse(raw);
99
+ return parsed && typeof parsed === 'object' ? parsed : {};
100
+ } catch {
101
+ return {};
102
+ }
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Public API
107
+ // ---------------------------------------------------------------------------
108
+
109
+ /**
110
+ * Record (or update) an active override for a project in the registry.
111
+ * Idempotent — re-recording the same (project, preset) updates applied_at
112
+ * rather than duplicating the entry.
113
+ *
114
+ * @param {string} projectRoot
115
+ * @param {string} preset
116
+ * @param {string} scope 'base' | 'user' | 'org' | 'project'
117
+ * @param {string} [project_type] auto-detected project type, defaults to
118
+ * whatever is already on file or 'unknown'.
119
+ */
120
+ export async function recordOverrideUse(projectRoot, preset, scope, project_type) {
121
+ if (typeof projectRoot !== 'string' || !projectRoot) {
122
+ throw new Error('recordOverrideUse: projectRoot must be a non-empty string');
123
+ }
124
+ if (typeof preset !== 'string' || !preset) {
125
+ throw new Error('recordOverrideUse: preset must be a non-empty string');
126
+ }
127
+ if (typeof scope !== 'string' || !scope) {
128
+ throw new Error('recordOverrideUse: scope must be a non-empty string');
129
+ }
130
+
131
+ const state = await readRegistry();
132
+ const proj = state.projects[projectRoot] || { active_overrides: [] };
133
+ if (!Array.isArray(proj.active_overrides)) proj.active_overrides = [];
134
+ if (project_type && typeof project_type === 'string') {
135
+ proj.project_type = project_type;
136
+ } else if (!proj.project_type) {
137
+ proj.project_type = 'unknown';
138
+ }
139
+
140
+ const appliedAt = new Date().toISOString();
141
+ const existingIdx = proj.active_overrides.findIndex(
142
+ (o) => o && o.preset === preset
143
+ );
144
+ if (existingIdx >= 0) {
145
+ proj.active_overrides[existingIdx] = {
146
+ ...proj.active_overrides[existingIdx],
147
+ preset,
148
+ scope,
149
+ applied_at: appliedAt,
150
+ };
151
+ } else {
152
+ proj.active_overrides.push({ preset, scope, applied_at: appliedAt });
153
+ }
154
+ state.projects[projectRoot] = proj;
155
+ await writeRegistry(state);
156
+ }
157
+
158
+ /**
159
+ * Remove a project's record of having an override active. Leaves the project
160
+ * entry in place with an empty active_overrides array so project_type and
161
+ * any other analytic fields persist for future signal.
162
+ *
163
+ * @param {string} projectRoot
164
+ * @param {string} preset
165
+ */
166
+ export async function removeOverrideUse(projectRoot, preset) {
167
+ const state = await readRegistry();
168
+ const proj = state.projects[projectRoot];
169
+ if (!proj || !Array.isArray(proj.active_overrides)) return;
170
+ proj.active_overrides = proj.active_overrides.filter(
171
+ (o) => !(o && o.preset === preset)
172
+ );
173
+ // Intentionally do NOT delete state.projects[projectRoot] — see jsdoc.
174
+ state.projects[projectRoot] = proj;
175
+ await writeRegistry(state);
176
+ }
177
+
178
+ /**
179
+ * Find every project that has `preset` recorded as active.
180
+ *
181
+ * @param {string} preset
182
+ * @returns {Promise<Array<{project: string, project_type: string, applied_at: string}>>}
183
+ */
184
+ export async function findProjectsWithOverride(preset) {
185
+ const state = await readRegistry();
186
+ const out = [];
187
+ for (const [project, entry] of Object.entries(state.projects || {})) {
188
+ if (!entry || !Array.isArray(entry.active_overrides)) continue;
189
+ const hit = entry.active_overrides.find(
190
+ (o) => o && o.preset === preset
191
+ );
192
+ if (hit) {
193
+ out.push({
194
+ project,
195
+ project_type: entry.project_type || 'unknown',
196
+ applied_at: hit.applied_at,
197
+ });
198
+ }
199
+ }
200
+ return out;
201
+ }
202
+
203
+ /**
204
+ * Find projects whose recorded override SET is similar (Jaccard) to
205
+ * currentOverrides above threshold. Excludes opts.excludeProject if given.
206
+ *
207
+ * @param {string[]} currentOverrides preset names
208
+ * @param {number} [threshold=0.5]
209
+ * @param {{excludeProject?: string}} [opts]
210
+ * @returns {Promise<Array<{project: string, project_type: string, overrides: string[], similarity: number}>>}
211
+ */
212
+ export async function findProjectsWithSimilarOverrideSet(
213
+ currentOverrides,
214
+ threshold = 0.5,
215
+ opts = {}
216
+ ) {
217
+ if (!Array.isArray(currentOverrides) || currentOverrides.length === 0) {
218
+ return [];
219
+ }
220
+ const exclude = opts && typeof opts.excludeProject === 'string'
221
+ ? opts.excludeProject
222
+ : null;
223
+ const current = new Set(currentOverrides);
224
+ const state = await readRegistry();
225
+ const out = [];
226
+ for (const [project, entry] of Object.entries(state.projects || {})) {
227
+ if (exclude && project === exclude) continue;
228
+ if (!entry || !Array.isArray(entry.active_overrides)) continue;
229
+ const theirs = new Set(
230
+ entry.active_overrides
231
+ .filter((o) => o && typeof o.preset === 'string')
232
+ .map((o) => o.preset)
233
+ );
234
+ if (theirs.size === 0) continue;
235
+ let intersection = 0;
236
+ for (const p of current) if (theirs.has(p)) intersection += 1;
237
+ const union = new Set([...current, ...theirs]).size;
238
+ const similarity = union === 0 ? 0 : intersection / union;
239
+ if (similarity >= threshold) {
240
+ out.push({
241
+ project,
242
+ project_type: entry.project_type || 'unknown',
243
+ overrides: Array.from(theirs),
244
+ similarity,
245
+ });
246
+ }
247
+ }
248
+ // Sort highest similarity first for deterministic prelude output.
249
+ out.sort((a, b) => b.similarity - a.similarity || a.project.localeCompare(b.project));
250
+ return out;
251
+ }
252
+
253
+ /**
254
+ * Compose a promote-to-user-defaults suggestion when N+ projects share the
255
+ * current project's override set. Returns null if the threshold is not met.
256
+ *
257
+ * Threshold is configurable via ~/.ijfw/state/settings.json
258
+ * `override_promote_min_matches` (default 3) or opts.minMatches.
259
+ *
260
+ * @param {string} currentProjectRoot
261
+ * @param {string[]} currentOverrides
262
+ * @param {{minMatches?: number, threshold?: number}} [opts]
263
+ * @returns {Promise<null | {message: string, projects: string[], suggestion: string}>}
264
+ */
265
+ export async function getPromoteSuggestion(currentProjectRoot, currentOverrides, opts = {}) {
266
+ if (!Array.isArray(currentOverrides) || currentOverrides.length === 0) {
267
+ return null;
268
+ }
269
+ const state = await readRegistry();
270
+ const projectCount = Object.keys(state.projects || {}).length;
271
+ if (projectCount < 2) return null;
272
+
273
+ const settings = await readSettings();
274
+ const configuredMin = Number.isInteger(settings.override_promote_min_matches)
275
+ ? settings.override_promote_min_matches
276
+ : null;
277
+ const minMatches = Number.isInteger(opts.minMatches)
278
+ ? opts.minMatches
279
+ : (configuredMin ?? 3);
280
+ const threshold = typeof opts.threshold === 'number' ? opts.threshold : 0.5;
281
+
282
+ const matches = await findProjectsWithSimilarOverrideSet(
283
+ currentOverrides,
284
+ threshold,
285
+ { excludeProject: currentProjectRoot }
286
+ );
287
+ if (matches.length < minMatches) return null;
288
+
289
+ const presetList = currentOverrides.slice().sort().join(', ');
290
+ const presetArgs = currentOverrides.slice().sort().join(' ');
291
+ const message =
292
+ `You have [${presetList}] active in ${matches.length} other projects. ` +
293
+ `Consider promoting to user defaults so new projects pick them up automatically.`;
294
+ return {
295
+ message,
296
+ projects: matches.map((m) => m.project),
297
+ suggestion: `ijfw override promote --scope user ${presetArgs}`,
298
+ };
299
+ }
300
+
301
+ // Test seam — internal-only, do not import in production code.
302
+ export const __test = {
303
+ registryPath,
304
+ settingsPath,
305
+ readRegistry,
306
+ writeRegistry,
307
+ };
@@ -0,0 +1,101 @@
1
+ ---
2
+ extends: []
3
+ scope: base
4
+ skill: ijfw-critique
5
+ ---
6
+
7
+ # Academic / Research Writing Critique Preset
8
+
9
+ This preset adapts `ijfw-critique` for academic papers, dissertations,
10
+ literature reviews, conference submissions, and grant proposals. It
11
+ replaces the default rubric, evidence, and output-format sections so
12
+ the critique focuses on the standards of scholarly argument.
13
+
14
+ <!-- ijfw-override: rubric -->
15
+ Evaluate the manuscript against these criteria. Score each 1-5 and
16
+ write feedback at the level of detail a reviewer for a respectable
17
+ journal would expect.
18
+
19
+ 1. **Argument structure** -- Is there a thesis the rest of the paper
20
+ visibly supports? Can you reconstruct the argument as a numbered
21
+ chain of claims, or does the paper drift between adjacent topics
22
+ without a load-bearing spine?
23
+ 2. **Evidence sufficiency** -- For each major claim, is there enough
24
+ data, primary source, or prior result to carry the weight asked of
25
+ it? Distinguish claims that are demonstrated, claims that are
26
+ merely asserted, and claims that are smuggled past the reader.
27
+ 3. **Citation density and relevance** -- Are sources cited where the
28
+ reader needs them, not just sprinkled in the introduction? Are
29
+ the cited works the canonical anchors for their claims, or are
30
+ they convenience citations that look right but do not say what is
31
+ implied?
32
+ 4. **Definitional precision** -- Are key terms defined before they
33
+ are used, and used consistently after? Watch for slippage between
34
+ neighbouring concepts that the field treats as distinct.
35
+ 5. **Methodological transparency** -- Could a reader of the same
36
+ discipline reproduce the analysis from what is on the page?
37
+ Sample size, instrument, coding scheme, inclusion criteria, and
38
+ procedure must be legible.
39
+ 6. **Audience appropriate for field** -- Does the prose pitch itself
40
+ to readers of the named field at the named level, without either
41
+ over-explaining undergraduate fundamentals or omitting context a
42
+ sibling subfield would need?
43
+
44
+ The artifact is scholarship. Do not evaluate it as software or as
45
+ marketing; evaluate it as an argument that has to survive informed
46
+ peer scrutiny.
47
+ <!-- ijfw-override-end -->
48
+
49
+ <!-- ijfw-override: evidence -->
50
+ Quote the manuscript itself. For each observation, anchor with one
51
+ or more of these passage types:
52
+
53
+ - The **thesis statement** -- the sentence (often near the end of the
54
+ introduction) that names what the paper is arguing.
55
+ - A **citation pattern** -- two or three nearby in-text citations
56
+ that together reveal whether sourcing is load-bearing or decorative.
57
+ - A **gap-claim** -- a sentence asserting that the literature has
58
+ not yet addressed something. These deserve scrutiny: is the gap
59
+ real, or constructed by narrow framing?
60
+
61
+ Quote 1-2 sentences and give a locator the author can find: section
62
+ heading, paragraph position within the section, or page if the
63
+ manuscript is paginated. Never refer to source lines, files, or
64
+ other software locators; this is a paper.
65
+ <!-- ijfw-override-end -->
66
+
67
+ <!-- ijfw-override: output-format -->
68
+ Return the critique as markdown with these five sections, in this
69
+ order:
70
+
71
+ ## Thesis
72
+
73
+ Quote the thesis as written, then restate the argument in your own
74
+ words. If the thesis cannot be located or cannot be paraphrased
75
+ without addition, say so.
76
+
77
+ ## Evidence
78
+
79
+ For each major claim, name the evidence the paper offers and judge
80
+ whether it is sufficient. Flag claims that rest on a single source
81
+ where the field expects triangulation.
82
+
83
+ ## Method
84
+
85
+ Summarise the method as a reader of the discipline would reconstruct
86
+ it from the page. Note every place a competent reader would still
87
+ have to guess.
88
+
89
+ ## Limitations
90
+
91
+ What does the paper acknowledge, and what does it omit that a
92
+ reviewer would press on? Be specific about which population, scope,
93
+ or condition the conclusions cannot legitimately reach.
94
+
95
+ ## Missing citations
96
+
97
+ List the works (or kinds of works) the argument needs but does not
98
+ cite. Where possible, name the author or paradigm by surname rather
99
+ than a placeholder; if you cannot, describe the work specifically
100
+ enough that the author can search for it.
101
+ <!-- ijfw-override-end -->
@@ -0,0 +1,87 @@
1
+ ---
2
+ extends: []
3
+ scope: base
4
+ skill: ijfw-critique
5
+ ---
6
+
7
+ # Book / Long-form Prose Critique Preset
8
+
9
+ This preset adapts `ijfw-critique` for evaluating manuscripts, chapters,
10
+ short stories, and other long-form prose. It replaces the default
11
+ rubric, evidence, and output-format sections so the critique focuses
12
+ on craft of language rather than software artifacts.
13
+
14
+ <!-- ijfw-override: rubric -->
15
+ Evaluate the prose against these criteria. Score each 1-5 and explain
16
+ in plain language; the writer is not a programmer.
17
+
18
+ 1. **Clarity of voice** -- Is the narrator's perspective consistent
19
+ from paragraph to paragraph? Does the voice feel like one mind
20
+ speaking, or does it drift between registers without intent?
21
+ 2. **Pacing** -- Do scenes breathe where they should and compress
22
+ where they should? Flag pages where summary smothers scene, or
23
+ where scene drags past its emotional beat.
24
+ 3. **Scene specificity** -- Are settings rendered through sensory
25
+ detail that only this scene could have, or does the prose lean on
26
+ generic furniture (a room, a street, a window)?
27
+ 4. **Dialogue authenticity** -- Does each character sound distinct
28
+ when their attribution is removed? Are speech rhythms tied to
29
+ biography, mood, and stakes, or do they all sound like the author?
30
+ 5. **Character motivation** -- Can the reader name what each
31
+ point-of-view character wants in this passage, and what is in the
32
+ way? If motivation is absent or interchangeable, call it out.
33
+ 6. **Sentence variety** -- Are sentences shaped to the moment? Look
34
+ for monotony of length, repeated opening structures, and reliance
35
+ on the same conjunctions or verbs of being.
36
+
37
+ Do not evaluate code quality or technical correctness; the artifact
38
+ is prose. If the writer included code samples inside fiction (a
39
+ hacker character, a thriller subplot), treat them as narrative
40
+ texture, not as systems to audit.
41
+ <!-- ijfw-override-end -->
42
+
43
+ <!-- ijfw-override: evidence -->
44
+ Ground every observation in the manuscript itself. Quote a 1-2
45
+ sentence passage with a chapter and page reference (or section
46
+ heading where pages are absent). Example shape:
47
+
48
+ > Chapter 3, p. 47: "The kitchen smelled like onions and old rain."
49
+
50
+ Then say what the passage does well or fails to do. Never cite by
51
+ file path, function name, or line number; this is a manuscript, not a
52
+ codebase. Avoid bullet-only feedback that floats free of the page --
53
+ the writer needs to find the exact passage you mean.
54
+
55
+ When a pattern repeats across the manuscript, quote two or three
56
+ short examples from different chapters rather than one long block;
57
+ the spread itself is the evidence.
58
+ <!-- ijfw-override-end -->
59
+
60
+ <!-- ijfw-override: output-format -->
61
+ Return the critique as markdown with these four sections, in this
62
+ order:
63
+
64
+ ## Voice
65
+
66
+ What does the narrator sound like, and where does that voice waver?
67
+ Quote the clearest example of the voice working, and the clearest
68
+ example of it slipping.
69
+
70
+ ## Pacing
71
+
72
+ Map the energy of the passage. Where does it move, where does it
73
+ stall, and what should be cut or expanded? Reference chapters or
74
+ scenes by name.
75
+
76
+ ## Specificity
77
+
78
+ Where does the prose earn its world through sensory detail, and where
79
+ does it rely on placeholders? Quote a strong line and a generic line
80
+ side by side.
81
+
82
+ ## Suggested cuts
83
+
84
+ List 3-7 specific passages that could be deleted or compressed
85
+ without losing meaning. Quote the opening words of each so the writer
86
+ can find them quickly.
87
+ <!-- ijfw-override-end -->
@@ -0,0 +1,95 @@
1
+ ---
2
+ extends: []
3
+ scope: base
4
+ skill: ijfw-critique
5
+ ---
6
+
7
+ # Campaign / Marketing Copy Critique Preset
8
+
9
+ This preset adapts `ijfw-critique` for marketing campaigns, product
10
+ launches, email sequences, sales pages, ad copy, and other
11
+ persuasion-driven artifacts. It replaces the default rubric,
12
+ evidence, and output-format sections so the critique focuses on
13
+ whether the copy moves the reader to act.
14
+
15
+ <!-- ijfw-override: rubric -->
16
+ Evaluate the campaign against these criteria. Score each 1-5 and
17
+ write in language a copywriter or founder will recognise -- not the
18
+ language of software engineering.
19
+
20
+ 1. **Call-to-action clarity** -- After the reader finishes, do they
21
+ know exactly one next step? Is the CTA verb-led, specific, and
22
+ placed where momentum is highest, or is it buried, hedged, or
23
+ multi-headed?
24
+ 2. **Audience fit** -- Does the copy speak to the named segment in
25
+ their own vocabulary, status concerns, and objections? Generic
26
+ "you" with no anchor is a fail; over-narrow jargon that excludes
27
+ the segment is also a fail.
28
+ 3. **Hook strength** -- Does the opening earn the next sentence?
29
+ Test the first 1-2 lines: do they promise something specific
30
+ enough to make the reader stay, or do they warm up before saying
31
+ anything?
32
+ 4. **Persuasion arc** -- Is there a recognisable shape (problem,
33
+ stakes, promise, proof, offer, close), and does each beat carry
34
+ weight? Flag missing beats and beats that repeat without adding.
35
+ 5. **Channel-appropriate format** -- Email reads like email; a
36
+ landing page reads like a landing page; a paid ad reads like an
37
+ ad. Length, density, and rhythm should match the medium and
38
+ placement.
39
+ 6. **Claim defensibility** -- Every superlative ("best", "only",
40
+ "guaranteed") and every number needs a believable source or a
41
+ concrete demonstration nearby. Unsupported claims cost trust.
42
+
43
+ The artifact is copy, not software. Do not evaluate it as if it were
44
+ a product or codebase; evaluate how it sells.
45
+ <!-- ijfw-override-end -->
46
+
47
+ <!-- ijfw-override: evidence -->
48
+ Quote the actual words on the page. Pull out three anchor passages
49
+ for almost every campaign you review:
50
+
51
+ 1. The **headline** (or subject line, or ad hook).
52
+ 2. The **CTA** in its surrounding sentence.
53
+ 3. The **close** -- the final paragraph or sign-off before the CTA.
54
+
55
+ Quote them in full, then say what they do or fail to do. If the
56
+ campaign has multiple sections (hero, social proof, FAQ, footer), name
57
+ the section before the quote so the writer can navigate.
58
+
59
+ Never cite by line number, file path, or any technical locator. The
60
+ campaign is read top to bottom by a human; speak in those terms
61
+ ("the second paragraph", "the testimonial block", "the P.S. line").
62
+ <!-- ijfw-override-end -->
63
+
64
+ <!-- ijfw-override: output-format -->
65
+ Return the critique as markdown with these five sections, in this
66
+ order:
67
+
68
+ ## Hook
69
+
70
+ Quote the opening line(s). Does it earn the next sentence? What
71
+ would a sharper version look like?
72
+
73
+ ## Promise
74
+
75
+ What is the campaign actually offering the reader, in plain language?
76
+ Is the promise specific, believable, and worth their attention?
77
+
78
+ ## Proof
79
+
80
+ What proof does the copy bring -- testimonials, case studies,
81
+ numbers, demonstration, third-party validation? Where is proof
82
+ missing where it would land hardest?
83
+
84
+ ## CTA
85
+
86
+ Quote every call-to-action verbatim. Are they consistent in
87
+ language? Is the primary CTA unambiguous? Where should it appear
88
+ that it currently does not?
89
+
90
+ ## Friction points
91
+
92
+ List the moments the reader is most likely to stall, doubt, or close
93
+ the tab. Quote the exact phrase that creates the friction, and
94
+ suggest a replacement.
95
+ <!-- ijfw-override-end -->
@@ -0,0 +1,99 @@
1
+ ---
2
+ extends: []
3
+ scope: base
4
+ skill: ijfw-critique
5
+ ---
6
+
7
+ # Screenplay / Script Critique Preset
8
+
9
+ This preset adapts `ijfw-critique` for film, television, and short-form
10
+ scripts -- features, pilots, episodes, shorts, and stage plays where
11
+ the conventions hold. It replaces the default rubric, evidence, and
12
+ output-format sections so the critique reads like coverage from a
13
+ working development executive, not like a code review.
14
+
15
+ <!-- ijfw-override: rubric -->
16
+ Evaluate the script against these criteria. Score each 1-5 and write
17
+ in the language of a story editor or showrunner.
18
+
19
+ 1. **Scene-level conflict** -- Does every scene have a protagonist
20
+ pursuing a tangible want against a tangible obstacle, with the
21
+ scene ending in change (got it, denied, or got something worse)?
22
+ Flag scenes that exist only to deliver information.
23
+ 2. **Character agency** -- Do the leads drive events through choices,
24
+ or are they reactive vehicles for the plot? When a character
25
+ acts, can you trace the want and the cost? Passive protagonists
26
+ should be called out, especially in the first and third acts.
27
+ 3. **Pacing per act** -- Does each act do its structural work
28
+ (setup, escalation, crisis, climax) at roughly the page count the
29
+ format expects? Flag soggy middles, late inciting incidents, and
30
+ climaxes that arrive without setup paying off.
31
+ 4. **Dialogue distinctness per character** -- Cover the character
32
+ names and read three pages aloud: can you still tell who is
33
+ speaking? Flag characters who share vocabulary, rhythm, or
34
+ posture without the script earning the resemblance.
35
+ 5. **Visual storytelling** -- Are emotional beats rendered as images,
36
+ blocking, and behaviour, or do characters announce their feelings
37
+ in dialogue? Showing over telling is the rule; flag exposition
38
+ that should be on screen instead of in the mouth.
39
+ 6. **Genre conventions** -- Does the script honour or deliberately
40
+ subvert the audience contract of its genre (thriller pace, comedy
41
+ beats, horror dread, procedural structure)? Subversion is fine;
42
+ accidental drift out of genre is not.
43
+
44
+ The artifact is a script. Do not treat it as code or as a marketing
45
+ deck; treat it as something a room will shoot.
46
+ <!-- ijfw-override-end -->
47
+
48
+ <!-- ijfw-override: evidence -->
49
+ Quote the script as it is written, in screenplay shorthand. Three
50
+ anchor types do most of the work:
51
+
52
+ - **Slug lines** -- e.g. `INT. WAREHOUSE -- NIGHT`. Quote the slug
53
+ when calling out scene economy, location bloat, or geography that
54
+ is impossible to shoot.
55
+ - **Action lines** -- the prose between slug and dialogue. Quote
56
+ short passages (one to four lines) to evaluate visual specificity,
57
+ overwriting, or unfilmable description (an internal feeling with
58
+ no behaviour attached).
59
+ - **Dialogue exchanges** -- quote a 2-6 line back-and-forth, with
60
+ character names, to evaluate distinctness, pace, or on-the-nose
61
+ exposition.
62
+
63
+ Anchor each quote with the scene number or slug ("the diner scene on
64
+ p. 22"). Never use software locators; this is industry-standard
65
+ formatted material and should be referenced as such.
66
+ <!-- ijfw-override-end -->
67
+
68
+ <!-- ijfw-override: output-format -->
69
+ Return the critique as markdown with these four sections, in this
70
+ order:
71
+
72
+ ## Conflict per scene
73
+
74
+ Walk through the script scene by scene (or in clusters for longer
75
+ scripts) and name the want, obstacle, and change for each. Flag
76
+ every scene that fails this test and propose either a cut or a
77
+ reframe.
78
+
79
+ ## Character voices
80
+
81
+ For each principal, characterise their voice in two or three traits
82
+ (diction, rhythm, what they will and will not say). Quote a passage
83
+ where the voice is clearest, and a passage where the character bleeds
84
+ into another. Suggest a fix for the bleed.
85
+
86
+ ## Visual specificity
87
+
88
+ Pull the most cinematic moments the script already contains and
89
+ celebrate them. Then list the beats that are currently spoken or
90
+ narrated which should instead be shown. Quote the dialogue or action
91
+ line you would convert.
92
+
93
+ ## Pacing
94
+
95
+ Note the page where each act break lands, where the inciting incident
96
+ appears, and where the climax begins. Compare against format
97
+ expectations and call out the lag or rush. Recommend two or three
98
+ specific compressions or expansions.
99
+ <!-- ijfw-override-end -->