ctxr-cli 0.19.0 → 0.20.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/README.md +9 -14
- package/dist/commands/adapters-generate.d.ts +2 -14
- package/dist/commands/adapters-generate.d.ts.map +1 -1
- package/dist/commands/adapters-generate.js +2 -50
- package/dist/commands/adapters-generate.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +0 -3
- package/dist/commands/update.js.map +1 -1
- package/dist/config/defaults.d.ts +10 -7
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +10 -7
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/schema.js +3 -3
- package/dist/core/adapter-outputs.d.ts +19 -0
- package/dist/core/adapter-outputs.d.ts.map +1 -0
- package/dist/core/adapter-outputs.js +54 -0
- package/dist/core/adapter-outputs.js.map +1 -0
- package/dist/core/agents-doc.d.ts.map +1 -1
- package/dist/core/agents-doc.js +3 -1
- package/dist/core/agents-doc.js.map +1 -1
- package/dist/core/checks/write-lifecycle-checks.d.ts +8 -0
- package/dist/core/checks/write-lifecycle-checks.d.ts.map +1 -1
- package/dist/core/checks/write-lifecycle-checks.js +22 -3
- package/dist/core/checks/write-lifecycle-checks.js.map +1 -1
- package/dist/core/conventions.d.ts +3 -4
- package/dist/core/conventions.d.ts.map +1 -1
- package/dist/core/conventions.js +3 -4
- package/dist/core/conventions.js.map +1 -1
- package/dist/core/reconcile.d.ts +2 -1
- package/dist/core/reconcile.d.ts.map +1 -1
- package/dist/core/reconcile.js +11 -4
- package/dist/core/reconcile.js.map +1 -1
- package/dist/core/skills.d.ts +15 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +53 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/templates/agents/canonical.md +4 -2
- package/templates/agents/retrieval-leg-routing.md +6 -0
- package/templates/notes/Concept.md +2 -1
- package/templates/skills/ctxr-capture.md +3 -1
- package/templates/skills/ctxr-publish.md +16 -3
- package/templates/skills/ctxr-second-opinion/lenses.md +3 -1
- package/templates/skills/ctxr-second-opinion/run.mjs +47 -13
- package/templates/skills/ctxr-second-opinion.md +182 -0
- package/templates/skills/ctxr-session-capture.md +3 -2
|
@@ -99,9 +99,17 @@ before reporting the page as ready. It answers only what's derivable from the fi
|
|
|
99
99
|
in step 1, the form and reader choices in step 5, and factual accuracy stay judgment calls, not checker output.
|
|
100
100
|
|
|
101
101
|
`ctxr publish new` and `ctxr publish check` each end by naming the address `ctxr serve` answers for
|
|
102
|
-
this page.
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
this page. Look at the page before you hand it on. Whether the form works, whether the prose reads,
|
|
103
|
+
whether it renders at all is exactly what the checks above cannot answer, and it is cheapest to fix
|
|
104
|
+
while the page is still yours.
|
|
105
|
+
|
|
106
|
+
That address may be served from another host, or behind an access proxy, where you cannot reach it.
|
|
107
|
+
You do not need it: from this machine, start `ctxr serve` in the background, open the address it
|
|
108
|
+
prints followed by the page's route in your browser tool, and stop the server when you are done. A
|
|
109
|
+
page still in a session worktree is served there under its previewable route, the same as anywhere
|
|
110
|
+
else. If you have no way to see a rendered page, say in your hand-off that it has not been checked
|
|
111
|
+
visually, so whoever reviews it knows they are the first to look. Never report a page as ready on the
|
|
112
|
+
strength of `ctxr publish check` alone.
|
|
105
113
|
|
|
106
114
|
A page written in a session worktree is previewable rather than published, because the
|
|
107
115
|
published-pages address reads the store's own checkout and the page has not reached it yet. That
|
|
@@ -110,6 +118,11 @@ previewable address stops answering the moment the worktree is reclaimed — the
|
|
|
110
118
|
Report the address you actually opened, and say which of the two it is, so nobody is handed a link
|
|
111
119
|
that is about to change.
|
|
112
120
|
|
|
121
|
+
Both addresses are full URLs when the store declares `serve.base_url` in `contexture.yaml`, and bare
|
|
122
|
+
routes such as `/preview/…` when it does not. A bare route is not a link anyone else can open. Do not
|
|
123
|
+
guess a host to put in front of it: hand on the route as it is, and tell the operator that declaring
|
|
124
|
+
`serve.base_url` would make every address a command reports a link.
|
|
125
|
+
|
|
113
126
|
## 7. It's a page, not a note
|
|
114
127
|
|
|
115
128
|
The published-pages location is excluded from retrieval by default — a page never becomes a source for
|
|
@@ -12,4 +12,6 @@ drop one: a collapsed pair adds cost and no breadth.
|
|
|
12
12
|
- **brand-strategist** — long-term positioning, category ownership, how it ages.
|
|
13
13
|
|
|
14
14
|
A store may add its own lens inline as `Name:description`, for example
|
|
15
|
-
`"Plainspoken:blunt, substance first, distrusts slogans"`.
|
|
15
|
+
`"Plainspoken:blunt, substance first, distrusts slogans"`. The description may contain commas. A comma starts
|
|
16
|
+
the next lens only when the text after it is exactly a lens name above or begins `Name:`, so keep colons out
|
|
17
|
+
of a description.
|
|
@@ -33,25 +33,43 @@ const MAX_ARGV_BYTES = 120_000;
|
|
|
33
33
|
|
|
34
34
|
const env = (name, fallback) => process.env[`CTXR_SECOND_OPINION_${name}`] || fallback;
|
|
35
35
|
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Model choice lives here, in one table. Override by environment, never by editing a store.
|
|
38
|
+
*
|
|
39
|
+
* Effort is each provider's own recommended default, not one this runner picked:
|
|
40
|
+
* - claude: no --effort at all, so Claude Code applies its own default for the model.
|
|
41
|
+
* - codex: `medium`, the default its model catalog declares (`default_reasoning_level`). It is passed
|
|
42
|
+
* explicitly because the flag cannot simply be left off: with no pin in config.toml, codex resolves to
|
|
43
|
+
* `reasoning effort: none` on a ChatGPT login (seen 2026-10-05), which is no review at all. `low` at the
|
|
44
|
+
* `fast` tier. A machine that pins its own effort in config.toml is overridden by this; set
|
|
45
|
+
* CTXR_SECOND_OPINION_CODEX_EFFORT to use another.
|
|
46
|
+
* - agy: the effort is part of the model name; `(High)` is Gemini's default thinking level.
|
|
47
|
+
* For scale: on one short plan Claude took 35 s at `high` and 514 s at `max`, so a deeper pass is a choice
|
|
48
|
+
* (CTXR_SECOND_OPINION_CLAUDE_EFFORT=max), not something to leave on by default.
|
|
49
|
+
*/
|
|
37
50
|
function tiers() {
|
|
38
51
|
return {
|
|
39
52
|
strong: {
|
|
40
|
-
claude: { model: env('CLAUDE_MODEL', 'opus'), effort: env('CLAUDE_EFFORT',
|
|
41
|
-
codex: { effort: env('CODEX_EFFORT', '
|
|
53
|
+
claude: { model: env('CLAUDE_MODEL', 'opus'), effort: env('CLAUDE_EFFORT', null) },
|
|
54
|
+
codex: { effort: env('CODEX_EFFORT', 'medium') },
|
|
42
55
|
agy: { model: env('AGY_MODEL', 'Gemini 3.1 Pro (High)') },
|
|
43
56
|
},
|
|
44
57
|
fast: {
|
|
45
|
-
claude: { model: env('CLAUDE_MODEL', 'haiku'), effort: null },
|
|
46
|
-
codex: { effort: env('CODEX_EFFORT', '
|
|
58
|
+
claude: { model: env('CLAUDE_MODEL', 'haiku'), effort: env('CLAUDE_EFFORT', null) },
|
|
59
|
+
codex: { effort: env('CODEX_EFFORT', 'low') },
|
|
47
60
|
agy: { model: env('AGY_MODEL', 'Gemini 3.8 Flash (High)') },
|
|
48
61
|
},
|
|
49
62
|
};
|
|
50
63
|
}
|
|
51
64
|
|
|
52
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* The flags this runner passes, so preflight can confirm each CLI's --help still lists them. Only flags
|
|
67
|
+
* the help text documents belong here: `claude` still accepts a hidden --max-turns, but with `--tools ""`
|
|
68
|
+
* there is no tool turn to bound, so it is not passed at all. --effort is not listed because it is only
|
|
69
|
+
* passed when the environment asks for one.
|
|
70
|
+
*/
|
|
53
71
|
const FLAGS_USED = {
|
|
54
|
-
claude: { helpArgs: ['--help'], flags: ['--
|
|
72
|
+
claude: { helpArgs: ['--help'], flags: ['--tools', '--model', '--no-session-persistence', '--disable-slash-commands', '--output-format'] },
|
|
55
73
|
codex: { helpArgs: ['exec', '--help'], flags: ['--skip-git-repo-check', '--sandbox', '--ephemeral', '--color', '--config'] },
|
|
56
74
|
agy: { helpArgs: ['--help'], flags: ['--print', '--sandbox', '--disable-slash-commands', '--model'] },
|
|
57
75
|
};
|
|
@@ -59,7 +77,7 @@ const FLAGS_USED = {
|
|
|
59
77
|
class UsageError extends Error {}
|
|
60
78
|
|
|
61
79
|
function parseArgs(argv) {
|
|
62
|
-
const opts = { mode: 'critique', tier: 'strong', timeout:
|
|
80
|
+
const opts = { mode: 'critique', tier: 'strong', timeout: 600, only: null, lens: null, out: null, planFile: null, promptFile: null, preflight: false, noWrap: false, seed: null };
|
|
63
81
|
const takesValue = new Set(['--mode', '--tier', '--timeout', '--only', '--lens', '--out', '--plan-file', '--prompt-file', '--seed']);
|
|
64
82
|
for (let i = 0; i < argv.length; i++) {
|
|
65
83
|
const a = argv[i];
|
|
@@ -146,7 +164,7 @@ const firstLines = (text, n) => text.trim().split('\n').slice(0, n).join(' | ').
|
|
|
146
164
|
function buildInvocation(cli, prompt, tier) {
|
|
147
165
|
const t = tiers()[tier][cli];
|
|
148
166
|
if (cli === 'claude') {
|
|
149
|
-
const args = ['-p', '--
|
|
167
|
+
const args = ['-p', '--tools', '', '--model', t.model, '--no-session-persistence', '--disable-slash-commands', '--output-format', 'json'];
|
|
150
168
|
if (t.effort) args.push('--effort', t.effort);
|
|
151
169
|
return { cmd: 'claude', args, stdin: prompt };
|
|
152
170
|
}
|
|
@@ -209,7 +227,8 @@ export function parseCritique(text) {
|
|
|
209
227
|
const findings = [];
|
|
210
228
|
const blocks = text.split(/^### F\d+\b/m).slice(1);
|
|
211
229
|
for (const block of blocks) {
|
|
212
|
-
|
|
230
|
+
// Brackets around the severity are what the contract asks for, and models drop them: accept both.
|
|
231
|
+
const head = /^\s*\[?(critical|major|minor)\]?\s*(?:confidence\s*[=:]\s*([01](?:\.\d+)?))?/i.exec(block);
|
|
213
232
|
const evidence = /^Evidence:\s*(.+)$/im.exec(block)?.[1] ?? '';
|
|
214
233
|
findings.push({
|
|
215
234
|
severity: head?.[1]?.toLowerCase() ?? null,
|
|
@@ -260,13 +279,28 @@ function loadLensRoster() {
|
|
|
260
279
|
return roster;
|
|
261
280
|
}
|
|
262
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Splits `--lens` into one string per lens. Lenses are comma-separated, but an inline description may itself
|
|
284
|
+
* contain commas: a fragment starts a NEW lens only when it is exactly a roster name or begins `Name:`;
|
|
285
|
+
* any other fragment continues the lens before it. So `minimalist,House taste:blunt, anti-slogan` is two
|
|
286
|
+
* lenses, and a description must not contain a colon (that would read as the start of another lens).
|
|
287
|
+
*/
|
|
288
|
+
export function splitLenses(spec, roster) {
|
|
289
|
+
const lenses = [];
|
|
290
|
+
for (const fragment of spec.split(',').map((f) => f.trim()).filter(Boolean)) {
|
|
291
|
+
const startsLens = roster.has(fragment.toLowerCase()) || /^[^:,]{1,40}:/.test(fragment);
|
|
292
|
+
if (startsLens || lenses.length === 0) lenses.push(fragment);
|
|
293
|
+
else lenses[lenses.length - 1] += `, ${fragment}`;
|
|
294
|
+
}
|
|
295
|
+
return lenses;
|
|
296
|
+
}
|
|
297
|
+
|
|
263
298
|
function lensBlock(spec) {
|
|
264
299
|
const roster = loadLensRoster();
|
|
265
|
-
const specs = spec.split(',').map((s) => s.trim()).filter(Boolean);
|
|
266
300
|
const described = [];
|
|
267
|
-
for (const s of
|
|
301
|
+
for (const s of splitLenses(spec, roster)) {
|
|
268
302
|
if (roster.has(s.toLowerCase())) described.push(`- ${s.toLowerCase()} — ${roster.get(s.toLowerCase())}`);
|
|
269
|
-
else if (s
|
|
303
|
+
else if (/^[^:,]{1,40}:/.test(s)) {
|
|
270
304
|
const [name, ...rest] = s.split(':');
|
|
271
305
|
described.push(`- ${name.trim()} — ${rest.join(':').trim()}`);
|
|
272
306
|
} else throw new UsageError(`unknown lens "${s}" (known: ${[...roster.keys()].join(', ')}; or pass "Name:description")`);
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Get an independent read from models built by different labs before you commit to something you cannot
|
|
2
|
+
cheaply take back, or when your own taste is one vote among several. A second opinion from the same model
|
|
3
|
+
family you are running as mostly repeats you, so this skill sends the work to three command-line tools backed
|
|
4
|
+
by three different families and hands you their answers. It does not decide anything and it never carries out
|
|
5
|
+
what it reviews. Weighing the answers is your job, and steps 4 and 5 are how.
|
|
6
|
+
|
|
7
|
+
## 1. Critique, poll, or just answer?
|
|
8
|
+
|
|
9
|
+
- **Critique** when the question is *will this work?* A plan with several steps, an architectural or
|
|
10
|
+
data-model choice, anything that deletes, force-pushes, sends, spends money, or touches secrets. Also a
|
|
11
|
+
recovery sequence run against a broken system: those plans look fine right up to the step that makes it
|
|
12
|
+
worse.
|
|
13
|
+
- **Poll** when the question is *which of these is better?* and being wrong costs mild regret: a name, a
|
|
14
|
+
framing, a piece of copy, which of several tools fits. If all three agreeing would settle it, it is a poll.
|
|
15
|
+
If you would still want each reviewer's top objections and a suggested edit, it is a critique.
|
|
16
|
+
- **Answer directly** when there is one right answer, when the operator has already decided and wants
|
|
17
|
+
confirmation, or when the work is read-only investigation.
|
|
18
|
+
|
|
19
|
+
Push back when asked for a critique of a taste question. "Which domain suffix should I pick?" is a poll. "Should
|
|
20
|
+
I move my mail to a custom domain?" is a critique: it has steps, an order, and parts that cannot be undone. Say
|
|
21
|
+
which you are running and why in one line before you start.
|
|
22
|
+
|
|
23
|
+
A poll that asks a lens to judge whether something will *work* has become a critique. Stop and use that mode.
|
|
24
|
+
|
|
25
|
+
## 2. Check the tools before spending anything
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
node __SKILLS_PATH__/ctxr-second-opinion/run.mjs --preflight
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Each of the three should report `OK` within about twenty seconds. The preflight also confirms that each
|
|
32
|
+
tool still accepts the options the runner passes, because a renamed option makes a reviewer silently run at
|
|
33
|
+
default settings and nothing else notices.
|
|
34
|
+
|
|
35
|
+
If one is down, stop and give the operator the two real choices: sign that tool in and re-run, or continue
|
|
36
|
+
with the others by adding `--only` and naming them. Do not quietly run with one model. A single model is not a
|
|
37
|
+
second opinion, and the runner refuses to start with fewer than two.
|
|
38
|
+
|
|
39
|
+
Failure signatures: `Not logged in` means Claude needs `/login`; a `401` or an invalid refresh token from
|
|
40
|
+
Codex means `codex login`; an agy call that hangs usually means its sign-in expired and it needs one
|
|
41
|
+
interactive run.
|
|
42
|
+
|
|
43
|
+
If the plan came from someone other than the operator, leave agy out with `--only claude,codex`. Claude
|
|
44
|
+
runs with no tools at all and Codex in a read-only sandbox. Agy has no mode that removes its ability to act:
|
|
45
|
+
it runs in an empty scratch directory with terminal restrictions, which contains an accident and not an attack.
|
|
46
|
+
|
|
47
|
+
## 3. Run a critique
|
|
48
|
+
|
|
49
|
+
Write the plan to a file. The reviewers can only cite what is written down, and they cite by step number:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
# Plan: <one-line goal>
|
|
53
|
+
|
|
54
|
+
## Context
|
|
55
|
+
<two to five lines: what is being attempted and what constrains it>
|
|
56
|
+
|
|
57
|
+
## Steps
|
|
58
|
+
S1. <step>
|
|
59
|
+
S2. <step>
|
|
60
|
+
|
|
61
|
+
## Assumptions
|
|
62
|
+
- <what the plan takes for granted>
|
|
63
|
+
|
|
64
|
+
## Reversibility
|
|
65
|
+
- Reversible: <steps>
|
|
66
|
+
- Irreversible: <steps>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Then run it:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
node __SKILLS_PATH__/ctxr-second-opinion/run.mjs --mode critique --plan-file <plan> --out <dir>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Three reviewers each read the plan alone: structure and ordering, failure modes and trust boundaries,
|
|
76
|
+
simplicity and cost. None sees another's answer. That is deliberate: reviewers who read each other converge
|
|
77
|
+
on whoever spoke first, and a correct one gets talked out of it. Do not add a second round.
|
|
78
|
+
|
|
79
|
+
The runner prints the letters it wrote, whether each is valid or blind, and any reviewer that failed. It
|
|
80
|
+
never prints which model wrote which letter. A **blind** critique returned a well-formed verdict but cited
|
|
81
|
+
nothing in the plan, so it was written without reading it; it does not count. **Partial** means fewer than
|
|
82
|
+
two valid critiques: say so at the top of your answer and do not call anything a consensus.
|
|
83
|
+
|
|
84
|
+
Add `--tier fast` to drop each family to a cheaper model, for a plan that is small and easy to redo. The
|
|
85
|
+
default tier is right for anything the operator will act on.
|
|
86
|
+
|
|
87
|
+
Each reviewer runs at its provider's own recommended default effort, not one this skill chose. Claude gets no
|
|
88
|
+
effort setting, so Claude Code applies its default for the model. Codex gets `medium`, the default its model
|
|
89
|
+
catalog declares; the runner passes it explicitly because Codex left to itself, with no setting pinned, can
|
|
90
|
+
resolve to no reasoning at all. Gemini's effort is part of its model name, and `High` is its default. Measured on a
|
|
91
|
+
short plan, a default critique took about 40 seconds in all, 30 to 37 seconds per reviewer, and costs a few tens
|
|
92
|
+
of cents. A deeper pass is a choice: set `CTXR_SECOND_OPINION_CLAUDE_EFFORT=max` and Claude alone took over
|
|
93
|
+
eight minutes on the same plan, close enough to the runner's ten-minute limit that you should start such a run
|
|
94
|
+
in the background and tell the operator it is under way. A machine that pins its own Codex effort in its
|
|
95
|
+
configuration is overridden by `medium`; set `CTXR_SECOND_OPINION_CODEX_EFFORT` to use another.
|
|
96
|
+
|
|
97
|
+
## 4. Synthesize on the letters first
|
|
98
|
+
|
|
99
|
+
Read the `critique-A`, `critique-B` and `critique-C` files and write your synthesis before you open
|
|
100
|
+
`manifest.json`. You share a model family with one of the reviewers, and models rate their own family's
|
|
101
|
+
writing higher. Reading letters first is what keeps that from steering you. Then open the manifest and
|
|
102
|
+
attach roles and models to the letters in the final report.
|
|
103
|
+
|
|
104
|
+
Write it in this order:
|
|
105
|
+
|
|
106
|
+
- **Verdicts.** One line per letter, and any blind or failed reviewer stated plainly, not buried.
|
|
107
|
+
- **Consensus (all agree).** Highest signal. When all three independently flag the same simplification,
|
|
108
|
+
take it whole; do not keep a reduced version of complexity all three called wrong.
|
|
109
|
+
- **Majority (two of three).** Say whether the two agree on the fix or only on the diagnosis. Two
|
|
110
|
+
reviewers who name the same problem often propose different repairs: choose on merit, meaning which
|
|
111
|
+
repair keeps more of what you want (granular rollback, fewer side effects, smaller blast radius), and do
|
|
112
|
+
not default to the majority's.
|
|
113
|
+
- **Evidence-backed dissent.** A lone finding that cites the plan gets its own section and stays there. The
|
|
114
|
+
minority is right often enough that overruling it on your own authority is a net loss. Overrule only by
|
|
115
|
+
showing what the dissent missed.
|
|
116
|
+
- **Vetoes.** A REJECT that carries a critical finding is stated as a veto, first, with its evidence.
|
|
117
|
+
- **Revised plan**, and **what changed and why**: each change tied to the letter and finding that drove it.
|
|
118
|
+
- **Recommendation**: proceed with the revision, revise further, or stop.
|
|
119
|
+
|
|
120
|
+
Where the reviewers pull apart on ceremony, one wanting more safeguards and another fewer, decide by blast
|
|
121
|
+
radius. If being wrong is expensive to undo, side with more safeguards. If it is cheap to undo, side with
|
|
122
|
+
fewer. Where a safeguard needs machinery an earlier round already turned down (a background job, a state file
|
|
123
|
+
kept across sessions, a hook), side with fewer: adding it now defeats the earlier decision.
|
|
124
|
+
|
|
125
|
+
When you have critiqued this plan before, reread that synthesis first. The reviewers only see the current text
|
|
126
|
+
and drift toward their own local optimum. Name any proposal that reverses an earlier decision, and hold the
|
|
127
|
+
earlier line unless the new critique brings evidence the old one lacked.
|
|
128
|
+
|
|
129
|
+
## 5. Say what you are biased about, then stop
|
|
130
|
+
|
|
131
|
+
State in the report that one reviewer shares your model family, and say when that reviewer's finding is what
|
|
132
|
+
drove the recommendation. Name any reviewer that ran a different model from the one you expected: the
|
|
133
|
+
manifest records the model each tool reported.
|
|
134
|
+
|
|
135
|
+
Then hand the synthesis to the operator and stop. This skill never executes a step of the plan it critiqued.
|
|
136
|
+
If the operator says to go, proceed with the revised plan. If they push back, treat that as a fourth critique
|
|
137
|
+
and run the skill again on the changed plan. It is a new run and not a continuation.
|
|
138
|
+
|
|
139
|
+
To record the outcome in the plan itself, add a section near the top headed `## Revisions after critique`
|
|
140
|
+
that says it overrides anything inconsistent below, one numbered item per change, each naming the finding
|
|
141
|
+
behind it. Leave the original text in place as the record of what was reviewed. Run one more critique after a
|
|
142
|
+
plan has been substantially rewritten and before you carry it out: new wording creates new gaps. Do not rerun
|
|
143
|
+
for a typo.
|
|
144
|
+
|
|
145
|
+
## 6. Run a poll
|
|
146
|
+
|
|
147
|
+
Write the question and the options to a file, then:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
node __SKILLS_PATH__/ctxr-second-opinion/run.mjs --mode poll --prompt-file <question>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The same question goes to all three models, each answering with a pick, reasons, and anything surprising.
|
|
154
|
+
This mode names its files by model, because what you are reading is whether the models agree.
|
|
155
|
+
|
|
156
|
+
To get breadth as well as model diversity, add `--lens` with two or three taste lenses. Each model then
|
|
157
|
+
answers as every lens in one response, giving a lens-by-model grid. The built-in lenses are minimalist,
|
|
158
|
+
marketer, contrarian, end-user and brand-strategist. A store's own lens goes in as `Name:description`, for
|
|
159
|
+
example `--lens "minimalist,Plainspoken:blunt, substance first, distrusts slogans"`. A description may contain
|
|
160
|
+
commas: a comma starts the next lens only when what follows is a built-in lens name or begins `Name:`. So a
|
|
161
|
+
description must not contain a colon. Choose lenses that pull in different
|
|
162
|
+
directions; if two would obviously agree, drop one.
|
|
163
|
+
|
|
164
|
+
Read a lens grid on both axes. Down a column (one lens across the three models) agreement means that
|
|
165
|
+
taste value is robust to the model. Across a row (one model across the lenses) disagreement is where the
|
|
166
|
+
real decision is. Give your own recommendation and say which axis it rests on.
|
|
167
|
+
|
|
168
|
+
Report the answers, where they agree, where they split, and your read. The answers are evidence and not a
|
|
169
|
+
vote: one dissent with a substantive reason can outweigh two agreements. If two of the three agreeing models
|
|
170
|
+
include your own family, say so.
|
|
171
|
+
|
|
172
|
+
## 7. When something goes wrong
|
|
173
|
+
|
|
174
|
+
- A reviewer that timed out, exited non-zero, or returned no verdict is `failed` and carries its cause in
|
|
175
|
+
the manifest. Report it. Do not rerun that one reviewer on a different model to fill the gap.
|
|
176
|
+
- Do not poll from inside a critique to settle a quick doubt. If you want a gut check mid-critique, answer it
|
|
177
|
+
yourself.
|
|
178
|
+
- Model names change faster than releases. When a default is stale, set
|
|
179
|
+
`CTXR_SECOND_OPINION_CLAUDE_MODEL`, `CTXR_SECOND_OPINION_CLAUDE_EFFORT`, `CTXR_SECOND_OPINION_CODEX_EFFORT`
|
|
180
|
+
or `CTXR_SECOND_OPINION_AGY_MODEL` in the environment for the run. Nothing about the lineup lives in the store's configuration.
|
|
181
|
+
- The scratch files under the output directory hold each reviewer's raw output and are safe to delete when the
|
|
182
|
+
operator has the synthesis. They can contain the plan, so do not commit them.
|
|
@@ -42,8 +42,9 @@ self-contradicting document nobody reads. Propose one only when all four hold:
|
|
|
42
42
|
- It is a rule for future work, not a fact about past work.
|
|
43
43
|
- It applied more than once, or the user stated it as a correction.
|
|
44
44
|
- It is store-wide. A rule about one folder belongs in that folder's `README.md`, not here.
|
|
45
|
-
- It is not already in the shipped baseline
|
|
46
|
-
behavior as a house rule is the most likely way
|
|
45
|
+
- It is not already in the shipped baseline, the first block of `AGENTS.md`'s "Store conventions"
|
|
46
|
+
section — read it first. Restating contexture's own behavior as a house rule is the most likely way
|
|
47
|
+
to get this wrong.
|
|
47
48
|
|
|
48
49
|
Propose REMOVALS on the same bar: a convention the session contradicted, or that no longer matches how
|
|
49
50
|
work actually goes, is worse than none — it teaches the next agent something false. The inlined section
|