ctxr-cli 0.23.0 → 0.25.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 +65 -5
- package/dist/commands/graph-diff.d.ts +33 -0
- package/dist/commands/graph-diff.d.ts.map +1 -0
- package/dist/commands/graph-diff.js +52 -0
- package/dist/commands/graph-diff.js.map +1 -0
- package/dist/commands/land.d.ts +50 -0
- package/dist/commands/land.d.ts.map +1 -0
- package/dist/commands/land.js +414 -0
- package/dist/commands/land.js.map +1 -0
- package/dist/commands/publish-check.d.ts +6 -0
- package/dist/commands/publish-check.d.ts.map +1 -1
- package/dist/commands/publish-check.js +78 -3
- package/dist/commands/publish-check.js.map +1 -1
- package/dist/commands/publish-gather.d.ts.map +1 -1
- package/dist/commands/publish-gather.js +4 -1
- package/dist/commands/publish-gather.js.map +1 -1
- package/dist/commands/publish-new.d.ts +5 -0
- package/dist/commands/publish-new.d.ts.map +1 -1
- package/dist/commands/publish-new.js +30 -10
- package/dist/commands/publish-new.js.map +1 -1
- package/dist/commands/publish-stamp.d.ts +32 -0
- package/dist/commands/publish-stamp.d.ts.map +1 -0
- package/dist/commands/publish-stamp.js +78 -0
- package/dist/commands/publish-stamp.js.map +1 -0
- package/dist/commands/publish-urls.d.ts +13 -0
- package/dist/commands/publish-urls.d.ts.map +1 -1
- package/dist/commands/publish-urls.js +23 -6
- package/dist/commands/publish-urls.js.map +1 -1
- package/dist/commands/review-list.d.ts +34 -0
- package/dist/commands/review-list.d.ts.map +1 -0
- package/dist/commands/review-list.js +49 -0
- package/dist/commands/review-list.js.map +1 -0
- package/dist/commands/serve.d.ts +3 -6
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +106 -9
- package/dist/commands/serve.js.map +1 -1
- package/dist/config/schema.d.ts +40 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +39 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/core/approval/decision.d.ts +73 -0
- package/dist/core/approval/decision.d.ts.map +1 -0
- package/dist/core/approval/decision.js +166 -0
- package/dist/core/approval/decision.js.map +1 -0
- package/dist/core/approval/file-diff.d.ts +32 -0
- package/dist/core/approval/file-diff.d.ts.map +1 -0
- package/dist/core/approval/file-diff.js +83 -0
- package/dist/core/approval/file-diff.js.map +1 -0
- package/dist/core/approval/identity.d.ts +34 -0
- package/dist/core/approval/identity.d.ts.map +1 -0
- package/dist/core/approval/identity.js +105 -0
- package/dist/core/approval/identity.js.map +1 -0
- package/dist/core/approval/landing-record.d.ts +17 -0
- package/dist/core/approval/landing-record.d.ts.map +1 -0
- package/dist/core/approval/landing-record.js +30 -0
- package/dist/core/approval/landing-record.js.map +1 -0
- package/dist/core/approval/resolver.d.ts +15 -0
- package/dist/core/approval/resolver.d.ts.map +1 -0
- package/dist/core/approval/resolver.js +41 -0
- package/dist/core/approval/resolver.js.map +1 -0
- package/dist/core/approval/sessions.d.ts +36 -0
- package/dist/core/approval/sessions.d.ts.map +1 -0
- package/dist/core/approval/sessions.js +76 -0
- package/dist/core/approval/sessions.js.map +1 -0
- package/dist/core/browse/approve.d.ts +45 -0
- package/dist/core/browse/approve.d.ts.map +1 -0
- package/dist/core/browse/approve.js +239 -0
- package/dist/core/browse/approve.js.map +1 -0
- package/dist/core/browse/diagram-assets.d.ts +15 -0
- package/dist/core/browse/diagram-assets.d.ts.map +1 -0
- package/dist/core/browse/diagram-assets.js +35 -0
- package/dist/core/browse/diagram-assets.js.map +1 -0
- package/dist/core/browse/page-url.d.ts +22 -0
- package/dist/core/browse/page-url.d.ts.map +1 -1
- package/dist/core/browse/page-url.js +31 -0
- package/dist/core/browse/page-url.js.map +1 -1
- package/dist/core/browse/render.d.ts +10 -1
- package/dist/core/browse/render.d.ts.map +1 -1
- package/dist/core/browse/render.js +23 -1
- package/dist/core/browse/render.js.map +1 -1
- package/dist/core/browse/review.d.ts +21 -0
- package/dist/core/browse/review.d.ts.map +1 -0
- package/dist/core/browse/review.js +52 -0
- package/dist/core/browse/review.js.map +1 -0
- package/dist/core/browse/templates.d.ts +4 -1
- package/dist/core/browse/templates.d.ts.map +1 -1
- package/dist/core/browse/templates.js +18 -2
- package/dist/core/browse/templates.js.map +1 -1
- package/dist/core/checks/organize-checks.d.ts +17 -0
- package/dist/core/checks/organize-checks.d.ts.map +1 -1
- package/dist/core/checks/organize-checks.js +116 -0
- package/dist/core/checks/organize-checks.js.map +1 -1
- package/dist/core/errors.d.ts +67 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +160 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/forge/credentials.d.ts +69 -0
- package/dist/core/forge/credentials.d.ts.map +1 -0
- package/dist/core/forge/credentials.js +197 -0
- package/dist/core/forge/credentials.js.map +1 -0
- package/dist/core/forge/github.d.ts +73 -0
- package/dist/core/forge/github.d.ts.map +1 -0
- package/dist/core/forge/github.js +101 -0
- package/dist/core/forge/github.js.map +1 -0
- package/dist/core/git/exec.d.ts +25 -0
- package/dist/core/git/exec.d.ts.map +1 -1
- package/dist/core/git/exec.js +100 -2
- package/dist/core/git/exec.js.map +1 -1
- package/dist/core/graph/diff-block.d.ts +6 -0
- package/dist/core/graph/diff-block.d.ts.map +1 -0
- package/dist/core/graph/diff-block.js +63 -0
- package/dist/core/graph/diff-block.js.map +1 -0
- package/dist/core/graph/diff-document.d.ts +38 -0
- package/dist/core/graph/diff-document.d.ts.map +1 -0
- package/dist/core/graph/diff-document.js +117 -0
- package/dist/core/graph/diff-document.js.map +1 -0
- package/dist/core/graph/diff.d.ts +123 -0
- package/dist/core/graph/diff.d.ts.map +1 -0
- package/dist/core/graph/diff.js +253 -0
- package/dist/core/graph/diff.js.map +1 -0
- package/dist/core/graph/tree.d.ts +24 -0
- package/dist/core/graph/tree.d.ts.map +1 -0
- package/dist/core/graph/tree.js +45 -0
- package/dist/core/graph/tree.js.map +1 -0
- package/dist/core/notes/list.d.ts +10 -0
- package/dist/core/notes/list.d.ts.map +1 -1
- package/dist/core/notes/list.js +35 -12
- package/dist/core/notes/list.js.map +1 -1
- package/dist/core/publish/filing.d.ts +12 -12
- package/dist/core/publish/filing.d.ts.map +1 -1
- package/dist/core/publish/filing.js +20 -18
- package/dist/core/publish/filing.js.map +1 -1
- package/dist/core/publish/lifespan.d.ts +37 -0
- package/dist/core/publish/lifespan.d.ts.map +1 -0
- package/dist/core/publish/lifespan.js +59 -0
- package/dist/core/publish/lifespan.js.map +1 -0
- package/dist/core/publish/represents.d.ts +72 -0
- package/dist/core/publish/represents.d.ts.map +1 -0
- package/dist/core/publish/represents.js +83 -0
- package/dist/core/publish/represents.js.map +1 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +60 -4
- package/dist/run.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
- package/templates/approve/approve-button.md +1 -0
- package/templates/approve/author-heading.md +1 -0
- package/templates/approve/author-note.md +1 -0
- package/templates/approve/comment-label.md +1 -0
- package/templates/approve/comment-required.md +1 -0
- package/templates/approve/commits-heading.md +1 -0
- package/templates/approve/decision-heading.md +1 -0
- package/templates/approve/decision-note.md +1 -0
- package/templates/approve/diff-binary.md +1 -0
- package/templates/approve/diff-empty.md +1 -0
- package/templates/approve/diff-heading.md +1 -0
- package/templates/approve/facts-heading.md +1 -0
- package/templates/approve/forge-unreachable.md +1 -0
- package/templates/approve/github-app-manifest.json +15 -0
- package/templates/approve/head-missing.md +1 -0
- package/templates/approve/index-empty.md +1 -0
- package/templates/approve/index-heading.md +1 -0
- package/templates/approve/index-intro.md +1 -0
- package/templates/approve/landed.md +1 -0
- package/templates/approve/moved-head.md +1 -0
- package/templates/approve/no-pull.md +1 -0
- package/templates/approve/not-open.md +1 -0
- package/templates/approve/pages-heading.md +1 -0
- package/templates/approve/pages-note.md +1 -0
- package/templates/approve/record-failed.md +1 -0
- package/templates/approve/request-changes-button.md +1 -0
- package/templates/approve/state-line.md +1 -0
- package/templates/conventions/baseline-conventions.md +6 -0
- package/templates/graph/knowledge-changes/archived-item.md +1 -0
- package/templates/graph/knowledge-changes/archived.md +1 -0
- package/templates/graph/knowledge-changes/bare-route.md +2 -0
- package/templates/graph/knowledge-changes/block.md +10 -0
- package/templates/graph/knowledge-changes/counts.md +1 -0
- package/templates/graph/knowledge-changes/dangling-item.md +1 -0
- package/templates/graph/knowledge-changes/dangling.md +1 -0
- package/templates/graph/knowledge-changes/empty.md +1 -0
- package/templates/graph/knowledge-changes/reason-ambiguous.md +1 -0
- package/templates/graph/knowledge-changes/reason-not_found.md +1 -0
- package/templates/graph/knowledge-changes/review.md +4 -0
- package/templates/graph/review/moved-head.md +1 -0
- package/templates/land/resolver.md +12 -0
- package/templates/serve/diagrams.mjs +136 -0
- package/templates/serve/shell.html +1 -1
- package/templates/serve/style.css +90 -0
- package/templates/skills/ctxr-land.md +37 -31
- package/templates/skills/ctxr-organize-audit.md +19 -0
- package/templates/skills/ctxr-publish.md +100 -45
- package/templates/skills/ctxr-second-opinion/run.mjs +28 -17
- package/templates/skills/ctxr-second-opinion.md +19 -14
- package/templates/skills/ctxr-session-lifecycle.md +9 -0
- package/templates/skills/ctxr-submit.md +28 -17
|
@@ -1,38 +1,44 @@
|
|
|
1
1
|
The land half of the session lifecycle, after review. `ctxr-submit` is the other half; `ctxr-session-lifecycle`
|
|
2
|
-
covers what surrounds both and is not repeated here.
|
|
2
|
+
covers what surrounds both and is not repeated here. `ctxr land` does the landing. This skill decides when to run it.
|
|
3
3
|
|
|
4
4
|
1. Name the target explicitly — a branch name or a pull-request number — rather than relying on whichever
|
|
5
5
|
checkout you happen to be standing in: that checkout is as likely to be the canonical clone on
|
|
6
6
|
`__DEFAULT_BRANCH__`, which has no session to infer, as the session itself. Never land
|
|
7
7
|
`__DEFAULT_BRANCH__` itself; a pull request whose head is `__DEFAULT_BRANCH__` is refused too.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
request's number, title, and url
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
8
|
+
Run every step from outside the worktree being removed. Why: no command can remove the directory it runs from.
|
|
9
|
+
2. Read state before any side effect. Run `gh pr view <target> --json number,url,title,state,headRefName`.
|
|
10
|
+
A closed pull request stops here. Report it, and do nothing more.
|
|
11
|
+
When this store declares `change_approval`, also run `ctxr review list`. Find the row for this pull request.
|
|
12
|
+
Why: a decision made on the approval page is recorded there, with the person who made it.
|
|
13
|
+
3. Gate. Landing is an external side effect, and plan consent is not fire consent.
|
|
14
|
+
- When the row says `approved`, that decision is the consent. Go to step 4 without a question.
|
|
15
|
+
Why: a decider approved this exact change on the approval page.
|
|
16
|
+
- Otherwise, present the pull request's number, title, and url. Wait for an explicit go. An explicit
|
|
17
|
+
approval given earlier in this conversation satisfies this. Do not ask twice.
|
|
18
|
+
- When the row says `changes-requested` or `needs-reapproval`, say so before you ask.
|
|
19
|
+
Why: a decider asked for a change, or has not seen the current version.
|
|
20
|
+
4. Land. With an approval from the page, run `ctxr land <number>`. With an explicit go, run
|
|
21
|
+
`ctxr land <number> --confirm`. Use `--confirm` only after that go. Why: the flag tells the command that a person agreed.
|
|
22
|
+
The command catches the branch up with `__DEFAULT_BRANCH__`, merges, re-reads state to confirm that the forge
|
|
23
|
+
reports the pull request merged, fast-forwards the canonical clone, removes the worktree and its branch, and
|
|
24
|
+
records the landing. Do not run `gh pr merge` yourself. Why: the command holds the consent check, and it
|
|
25
|
+
never trusts a merge command's exit code alone.
|
|
26
|
+
5. Act on the outcome the command reports:
|
|
27
|
+
- `merged`: go to step 6.
|
|
28
|
+
- `blocked` with the reason `conflict`: follow `ctxr-session-lifecycle`'s conflict playbook in the session
|
|
29
|
+
worktree. Then do step 4 again. A retry re-reads state and does only what remains.
|
|
30
|
+
- `blocked` with another reason: report the reason. A conflict resolver ran and its result was refused.
|
|
31
|
+
- `needs-reapproval`: report that the change needs a new decision on the approval page. Do not land it with
|
|
32
|
+
`--confirm` unless the operator says so for this version. Why: nobody approved the new version.
|
|
33
|
+
- `no-consent`: return to step 3.
|
|
34
|
+
- `merge-refused`: report it. Checks or branch protection can still be pending, and a later run retries.
|
|
35
|
+
- `lease-refused`: the branch moved while the command ran. Start again from step 1.
|
|
36
|
+
6. Report exactly what happened — merged or not, synced or not (with why not), reclaimed or not (with why
|
|
37
|
+
not), each as the command reports it. If the canonical clone did not sync, never check it out, reset it, or
|
|
38
|
+
force a merge to make it comply. Reclaiming is the default action, not an optional extra: nothing sweeps
|
|
39
|
+
up afterward, so a worktree left here is left indefinitely. If the command kept the worktree, follow
|
|
40
|
+
`ctxr-session-lifecycle`'s Reclaiming section once the reason is gone. Never claim a merge or a cleanup that
|
|
41
|
+
the command did not report.
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
read the state in steps 2-3 first, or without an explicit go — never skip the read, the mergeability check,
|
|
38
|
-
or the confirmation to save a round trip.
|
|
43
|
+
The pull request and its merge commit are the audit trail. What the gate in step 3 protects against is landing
|
|
44
|
+
without reading the state in step 2 first, or without a go. Never skip the read or the gate to save a round trip.
|
|
@@ -34,6 +34,25 @@ item was one of the mission's priorities, hand it to `ctxr-mission`, which propo
|
|
|
34
34
|
have moved since their last synthesis, or which have never been rolled up — hand each one to
|
|
35
35
|
`ctxr-rollup` rather than re-synthesizing ad hoc.
|
|
36
36
|
|
|
37
|
+
## Disposable pages and drifted pages
|
|
38
|
+
|
|
39
|
+
`ctxr lint` lists disposable pages that are older than 30 days. Each one is a removal candidate, not a
|
|
40
|
+
removal. Do these steps:
|
|
41
|
+
|
|
42
|
+
1. Show the list to the operator as a proposal, with each page's age. Ask which pages to remove.
|
|
43
|
+
2. Remove only the pages that the operator approves. Why: somebody can still have the link to a
|
|
44
|
+
disposable page, and a removal breaks that link.
|
|
45
|
+
3. Do the removal in a session, on its session branch, with `git rm -r <page folder>`.
|
|
46
|
+
4. Land the removal through `ctxr-submit`. Its **Pages** table marks the address of each removed page
|
|
47
|
+
as one that stops working.
|
|
48
|
+
|
|
49
|
+
Never remove a disposable page that the operator did not approve, and never remove one only because it
|
|
50
|
+
is old.
|
|
51
|
+
|
|
52
|
+
`ctxr lint` also lists permanent pages that are out of step with the note they represent: stale, not
|
|
53
|
+
stamped, or with no note that resolves. Give each one to `ctxr-publish`. Do not stamp the page
|
|
54
|
+
yourself. Why: a stamp without a review of the page against its note confirms nothing.
|
|
55
|
+
|
|
37
56
|
## Broken links have classes — classify before fixing
|
|
38
57
|
|
|
39
58
|
- URL wrapped in wikilink syntax → convert to a markdown link.
|
|
@@ -2,12 +2,28 @@ Turn store content into a shareable HTML page for a subject — a store subtree,
|
|
|
2
2
|
concept whose sources aren't co-located. Deciding what belongs on the page is the one step no
|
|
3
3
|
convention can enforce for you; everything else here is a decision procedure, not a mechanism.
|
|
4
4
|
|
|
5
|
-
## 1.
|
|
5
|
+
## 1. Permanent or disposable? Decide first
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Decide the page's lifespan before you do anything else. A page has one of two lifespans:
|
|
8
|
+
|
|
9
|
+
- **Permanent.** The page is the rendered form of one note that will keep changing. Keep it, and
|
|
10
|
+
update it when the note changes.
|
|
11
|
+
- **Disposable.** The page helps somebody understand something once. Examples are an explainer, a
|
|
12
|
+
report on one week of agent work, or a comparison for one decision. Nobody updates it.
|
|
13
|
+
|
|
14
|
+
Choose disposable when you cannot name the one note that the page represents. Why: a kept page that
|
|
15
|
+
names no note holds facts that live nowhere else, and nothing can tell when they go out of date.
|
|
16
|
+
|
|
17
|
+
The location of the page sets its lifespan. Nothing in the page declares it. A disposable page sits
|
|
18
|
+
under `disposable/` directly below the store's published-pages location. Every other page is
|
|
19
|
+
permanent.
|
|
20
|
+
|
|
21
|
+
For a permanent page, make sure that the page earns its place over a note. Use a page when the reader
|
|
22
|
+
must compare options side by side, see spatial structure, manipulate something, navigate
|
|
23
|
+
non-linearly, or open a link. Use it only when the store's own notes cannot do this. Why: a page
|
|
24
|
+
costs several times the effort of the markdown it comes from. If a note does the job, the note is the
|
|
25
|
+
deliverable. Do not apply this test to a disposable page. Why: a disposable page is cheap on purpose,
|
|
26
|
+
and it is the cheap way to understand something once.
|
|
11
27
|
|
|
12
28
|
## 2. Name the subject, let the selector produce the set
|
|
13
29
|
|
|
@@ -28,46 +44,61 @@ content belongs in front of this page's intended readers — a page written for
|
|
|
28
44
|
not carry material written about that party, or about anyone else who did not expect to be quoted.
|
|
29
45
|
When you are unsure about a note, leave it out and name it to the operator rather than guessing.
|
|
30
46
|
|
|
31
|
-
## 4. Fix the identity once
|
|
47
|
+
## 4. Fix the identity once
|
|
48
|
+
|
|
49
|
+
**A disposable page.** Run `ctxr publish new --disposable <name>`. The command creates the folder
|
|
50
|
+
`disposable/<YYYY-MM-DD>-<name>` with today's date, and its sibling README. Give the name alone. Why:
|
|
51
|
+
the command supplies the date, and it refuses a name that starts with one. Do not use the path that
|
|
52
|
+
`gather` reported. Why: disposable pages sit in one flat folder, sorted by date, not by subject.
|
|
32
53
|
|
|
33
|
-
Step 2 reported the path
|
|
34
|
-
|
|
35
|
-
and
|
|
54
|
+
**A permanent page.** Step 2 reported the path at which a page for this subject belongs. That path is
|
|
55
|
+
under the store's published-pages location, and comes from where the subject's notes live.
|
|
56
|
+
Take it verbatim, append the page's own name, and give the result to `ctxr publish new <slug>`. The
|
|
57
|
+
command creates the folder and its sibling README:
|
|
36
58
|
|
|
37
59
|
```sh
|
|
38
60
|
ctxr publish new <reported path>/<page-name>
|
|
39
61
|
```
|
|
40
62
|
|
|
41
|
-
The depth is the store's own, not a fixed number of levels
|
|
42
|
-
deep
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
right is the question `gather` already answered.
|
|
63
|
+
The depth is the store's own, not a fixed number of levels. A subject whose notes are four folders
|
|
64
|
+
deep gets a page four folders deep. Why: then the published-pages navigation reads like the store, not
|
|
65
|
+
like one long list. `ctxr publish check` fails a page filed flat at the published-pages root. It
|
|
66
|
+
checks nothing more about the location. Why: `gather` already answered which folder is correct.
|
|
46
67
|
|
|
47
|
-
Name the page for what it *is*, never for its subject.
|
|
48
|
-
subject
|
|
68
|
+
Name the page for what it *is*, never for its subject. Why: the folders above it already name the
|
|
69
|
+
subject. `<reported path>/<subject>` says one word two times, and `<reported path>/decision-board`
|
|
49
70
|
says something.
|
|
50
71
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
the
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
Sometimes `gather` reports that an existing page also belongs at the reported path. This subject then
|
|
73
|
+
gets a second page, and the pages of a subject collect under one folder that names it. Move that page
|
|
74
|
+
there before you create the new one, and tell the operator its URL changed. Why: somebody can already
|
|
75
|
+
have that link. `ctxr publish urls` reports the old address next to the new one, and `ctxr-submit`
|
|
76
|
+
puts both into the pull request. `gather` moves nothing. It names the signal that counted the page as
|
|
77
|
+
this subject's page. Read that signal before you move anything. Why: the signal is a reading of the
|
|
78
|
+
page's README, and it can be wrong.
|
|
79
|
+
|
|
80
|
+
Lead a page's source notes with the note it is a representation of. `ctxr publish check` fails a
|
|
81
|
+
permanent page whose README does not start with a wikilink to exactly one note. For a page that comes
|
|
82
|
+
from a store folder, that note is the folder's hub note. If the folder has no hub note, write one, or
|
|
83
|
+
make the page disposable. The page can link to many more notes after the lead link. Why: the lead link
|
|
84
|
+
says which note the page represents, and each link after it is a citation.
|
|
85
|
+
|
|
86
|
+
After you write the README, run `ctxr publish stamp <path>`. The command records the represented
|
|
87
|
+
note's hash on the lead-link line. Why: `ctxr publish check` then reports the page as stale when that
|
|
88
|
+
note changes.
|
|
89
|
+
|
|
90
|
+
Sometimes `gather` reports that no path derives. One reason is that the subject is the store root, so
|
|
91
|
+
no folder names it. The other reason is that the path would start with `disposable/`, a segment that
|
|
92
|
+
only disposable pages can use. Then you choose the folder, and you own that choice. Pick the folder
|
|
93
|
+
that best names the subject to the reader.
|
|
94
|
+
Then write that choice and your reason into the page's README, and tell the operator. Nothing checks
|
|
95
|
+
this choice. Why: a page filed under a word that names nothing helps no reader.
|
|
96
|
+
|
|
97
|
+
Never start the page's own name with a date (`YYYY-` or `YYYY-MM-DD-`). That naming is reserved for
|
|
98
|
+
the frozen pages that `ctxr publish new --disposable` makes. The rule applies to the final segment, not
|
|
99
|
+
to the folders above it. The command refuses to overwrite an existing folder. If a folder already
|
|
100
|
+
exists for this subject, edit its files directly. Do not run `publish new` again. Why: a rename or an
|
|
101
|
+
overwrite breaks a link that somebody already has.
|
|
71
102
|
|
|
72
103
|
## 5. Choose the form, then delegate the craft
|
|
73
104
|
|
|
@@ -93,11 +124,20 @@ lower is the same material in shorter words.
|
|
|
93
124
|
|
|
94
125
|
## 6. Verify the output invariants
|
|
95
126
|
|
|
96
|
-
`ctxr publish check <path
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
Run `ctxr publish check <path>`. It runs the mechanical half of the checklist:
|
|
128
|
+
|
|
129
|
+
- no external network address, a viewport meta tag, at least one print rule, and a provenance line;
|
|
130
|
+
- a sibling README, balanced tags, and valid syntax in every embedded script;
|
|
131
|
+
- the page's filing location from step 4;
|
|
132
|
+
- for a disposable page, its shape (`disposable-shape`): one folder directly under `disposable/`,
|
|
133
|
+
with a real date at the start of its name;
|
|
134
|
+
- for a permanent page, the note it represents (`represented-note`) and that note's recorded hash
|
|
135
|
+
(`represented-note-hash`).
|
|
136
|
+
|
|
137
|
+
The command reports the page's lifespan. It exits non-zero and names every failing check. Fix all of
|
|
138
|
+
them before you report the page as ready. The command answers only what the files can show. Why: the
|
|
139
|
+
lifespan choice and the DO-test in step 1, the form and reader choices in step 5, and factual accuracy
|
|
140
|
+
are judgment calls, not checker output.
|
|
101
141
|
|
|
102
142
|
`ctxr publish new` and `ctxr publish check` each end by naming the address `ctxr serve` answers for
|
|
103
143
|
this page. Look at the page before you hand it on. Whether the form works, whether the prose reads,
|
|
@@ -131,10 +171,25 @@ anything else the store retrieves. Confirm with `ctxr lint` after landing.
|
|
|
131
171
|
|
|
132
172
|
## 8. Provenance and drift
|
|
133
173
|
|
|
134
|
-
The source notes are the fact of record. When a fact changes, change the note first, then the page
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
174
|
+
The source notes are the fact of record. When a fact changes, change the note first, then the page.
|
|
175
|
+
Search the page for every place that repeats the fact, such as a summary line, a total, or a status
|
|
176
|
+
badge. Update each one before you say the update is done. Why: a page that repeats a number in three
|
|
177
|
+
places goes stale in all three if you change only one.
|
|
178
|
+
|
|
179
|
+
`ctxr publish check` and `ctxr lint` report a permanent page as stale when its represented note
|
|
180
|
+
changed after the stamp. To confirm a stale page, do these steps in order:
|
|
181
|
+
|
|
182
|
+
1. Review the page against the note, and update the page where the two disagree.
|
|
183
|
+
2. Remove the `<!-- hash:… -->` marker from the README's lead-link line.
|
|
184
|
+
3. Run `ctxr publish stamp <path>` again.
|
|
185
|
+
|
|
186
|
+
`ctxr publish stamp` never overwrites a marker. Why: a stamp without a review confirms nothing, so
|
|
187
|
+
the stale report stays until somebody removes the marker.
|
|
188
|
+
|
|
189
|
+
Never revise a disposable page after it lands. Why: it is a snapshot of one moment, and its link
|
|
190
|
+
shows that moment. Make a new disposable page instead. Do not remove a disposable page yourself.
|
|
191
|
+
`ctxr lint` lists disposable pages that are older than 30 days, and the operator decides which to
|
|
192
|
+
remove (`ctxr-organize-audit`).
|
|
138
193
|
|
|
139
194
|
## 9. Landing
|
|
140
195
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// file needs no exec bit.
|
|
10
10
|
//
|
|
11
11
|
// Usage:
|
|
12
|
-
// node run.mjs --preflight [--only claude,codex]
|
|
12
|
+
// node run.mjs --preflight [--only claude,codex] [--tier strong|fast]
|
|
13
13
|
// node run.mjs --mode critique --plan-file plan.md [--out DIR] [--tier strong|fast]
|
|
14
14
|
// node run.mjs --mode poll --prompt-file q.txt [--lens minimalist,marketer]
|
|
15
15
|
//
|
|
@@ -36,27 +36,35 @@ const env = (name, fallback) => process.env[`CTXR_SECOND_OPINION_${name}`] || fa
|
|
|
36
36
|
/**
|
|
37
37
|
* Model choice lives here, in one table. Override by environment, never by editing a store.
|
|
38
38
|
*
|
|
39
|
+
* The tiers name a class of model, not a price. `strong` is each family's frontier model and `fast` its
|
|
40
|
+
* workhorse: claude `fable` and `opus`; codex `gpt-6-astra` ("frontier intelligence") and `gpt-6.1-sol`
|
|
41
|
+
* ("latest workhorse"), as codex's model catalog described them on 2026-10-15; agy `Gemini 4 Argon` and
|
|
42
|
+
* `Gemini 3.8 Flash`, once agy offered Argon (2026-10-19). The tiers differ in model only: every model runs at
|
|
43
|
+
* its provider's default effort.
|
|
44
|
+
*
|
|
39
45
|
* Effort is each provider's own recommended default, not one this runner picked:
|
|
40
46
|
* - claude: no --effort at all, so Claude Code applies its own default for the model.
|
|
41
|
-
* - codex:
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* CTXR_SECOND_OPINION_CODEX_EFFORT to use another.
|
|
46
|
-
* - agy: the effort is part of the model name
|
|
47
|
-
*
|
|
47
|
+
* - codex: the default its model catalog declares (`default_reasoning_level`), which was `low` for both
|
|
48
|
+
* gpt-6-astra and gpt-6.1-sol on 2026-10-15. It is passed explicitly because the flag cannot simply be
|
|
49
|
+
* left off: with no pin in config.toml, codex resolves to `reasoning effort: none` on a ChatGPT login
|
|
50
|
+
* (seen 2026-10-05), which is no review at all. A machine that pins its own effort in config.toml is
|
|
51
|
+
* overridden by this; set CTXR_SECOND_OPINION_CODEX_EFFORT to use another.
|
|
52
|
+
* - agy: the effort is part of the model name. The default thinking level is `(Medium)` for Gemini 4 Argon
|
|
53
|
+
* and `(High)` for Gemini 3.8 Flash. agy rejects an unknown name, and preflight tests it.
|
|
54
|
+
* For scale: on one short plan Claude (Opus) took 35 s at `high` and 514 s at `max`, and Fable took 122 s at
|
|
55
|
+
* its default (2026-10-15), so a deeper pass is a choice
|
|
48
56
|
* (CTXR_SECOND_OPINION_CLAUDE_EFFORT=max), not something to leave on by default.
|
|
49
57
|
*/
|
|
50
58
|
function tiers() {
|
|
51
59
|
return {
|
|
52
60
|
strong: {
|
|
53
|
-
claude: { model: env('CLAUDE_MODEL', '
|
|
54
|
-
codex: { effort: env('CODEX_EFFORT', '
|
|
55
|
-
agy: { model: env('AGY_MODEL', 'Gemini
|
|
61
|
+
claude: { model: env('CLAUDE_MODEL', 'fable'), effort: env('CLAUDE_EFFORT', null) },
|
|
62
|
+
codex: { model: env('CODEX_MODEL', 'gpt-6-astra'), effort: env('CODEX_EFFORT', 'low') },
|
|
63
|
+
agy: { model: env('AGY_MODEL', 'Gemini 4 Argon (Medium)') },
|
|
56
64
|
},
|
|
57
65
|
fast: {
|
|
58
|
-
claude: { model: env('CLAUDE_MODEL', '
|
|
59
|
-
codex: { effort: env('CODEX_EFFORT', 'low') },
|
|
66
|
+
claude: { model: env('CLAUDE_MODEL', 'opus'), effort: env('CLAUDE_EFFORT', null) },
|
|
67
|
+
codex: { model: env('CODEX_MODEL', 'gpt-6.1-sol'), effort: env('CODEX_EFFORT', 'low') },
|
|
60
68
|
agy: { model: env('AGY_MODEL', 'Gemini 3.8 Flash (High)') },
|
|
61
69
|
},
|
|
62
70
|
};
|
|
@@ -70,7 +78,7 @@ function tiers() {
|
|
|
70
78
|
*/
|
|
71
79
|
const FLAGS_USED = {
|
|
72
80
|
claude: { helpArgs: ['--help'], flags: ['--tools', '--model', '--no-session-persistence', '--disable-slash-commands', '--output-format'] },
|
|
73
|
-
codex: { helpArgs: ['exec', '--help'], flags: ['--skip-git-repo-check', '--sandbox', '--ephemeral', '--color', '--config'] },
|
|
81
|
+
codex: { helpArgs: ['exec', '--help'], flags: ['--skip-git-repo-check', '--sandbox', '--ephemeral', '--color', '--config', '--model'] },
|
|
74
82
|
agy: { helpArgs: ['--help'], flags: ['--print', '--sandbox', '--disable-slash-commands', '--model'] },
|
|
75
83
|
};
|
|
76
84
|
|
|
@@ -169,7 +177,7 @@ function buildInvocation(cli, prompt, tier) {
|
|
|
169
177
|
return { cmd: 'claude', args, stdin: prompt };
|
|
170
178
|
}
|
|
171
179
|
if (cli === 'codex') {
|
|
172
|
-
const args = ['exec', '--skip-git-repo-check', '--sandbox', 'read-only', '--ephemeral', '--color', 'never'];
|
|
180
|
+
const args = ['exec', '--skip-git-repo-check', '--sandbox', 'read-only', '--ephemeral', '--color', 'never', '--model', t.model];
|
|
173
181
|
if (t.effort) args.push('-c', `model_reasoning_effort=${t.effort}`);
|
|
174
182
|
args.push('-');
|
|
175
183
|
return { cmd: 'codex', args, stdin: prompt };
|
|
@@ -378,13 +386,16 @@ async function preflight(clis, opts) {
|
|
|
378
386
|
if (!help.ok) return { cli, ok: false, detail: help.cause };
|
|
379
387
|
const helpText = help.stdout + help.stderr;
|
|
380
388
|
const missing = spec.flags.filter((f) => !helpText.includes(f));
|
|
381
|
-
|
|
389
|
+
// The ping runs at the tier the run will use. A fixed tier left the other tier's model untested, so a
|
|
390
|
+
// model name agy no longer accepted passed preflight and failed the critique (2026-10-13).
|
|
391
|
+
const inv = buildInvocation(cli, 'say PONG only', opts.tier);
|
|
382
392
|
const ping = await runCli(inv.cmd, inv.args, inv.stdin, Math.min(opts.timeout, 60));
|
|
383
393
|
if (!ping.ok) return { cli, ok: false, detail: ping.cause };
|
|
384
394
|
const { text } = normalize(cli, ping);
|
|
385
395
|
if (!/pong/i.test(text)) return { cli, ok: false, detail: `unexpected reply: ${firstLines(text, 2)}` };
|
|
386
396
|
if (missing.length) return { cli, ok: false, detail: `answers, but its --help no longer lists: ${missing.join(' ')}` };
|
|
387
|
-
|
|
397
|
+
const model = tiers()[opts.tier][cli].model;
|
|
398
|
+
return { cli, ok: true, detail: model ? `PONG (${model})` : 'PONG' };
|
|
388
399
|
}),
|
|
389
400
|
);
|
|
390
401
|
for (const r of results) {
|
|
@@ -34,8 +34,10 @@ node __SKILLS_PATH__/ctxr-second-opinion/run.mjs --preflight
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Each of the three should report `OK` within about twenty seconds. The preflight also confirms that each
|
|
37
|
-
tool still accepts the options the runner passes
|
|
38
|
-
default settings and nothing else notices.
|
|
37
|
+
tool still accepts the options the runner passes. Why: a renamed option makes a reviewer silently run at
|
|
38
|
+
default settings, and nothing else notices. Pass the same `--tier` you will run with. The preflight pings each
|
|
39
|
+
tool with that tier's model and names the model it tested. Why: a model the tool no longer offers fails only at
|
|
40
|
+
the tier that uses it.
|
|
39
41
|
|
|
40
42
|
If one is down, stop and give the operator the two real choices: sign that tool in and re-run, or continue
|
|
41
43
|
with the others by adding `--only` and naming them. Do not quietly run with one model. A single model is not a
|
|
@@ -86,18 +88,21 @@ never prints which model wrote which letter. A **blind** critique returned a wel
|
|
|
86
88
|
nothing in the plan, so it was written without reading it; it does not count. **Partial** means fewer than
|
|
87
89
|
two valid critiques: say so at the top of your answer and do not call anything a consensus.
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
The default tier, `strong`, runs each family's frontier model. Add `--tier fast` to run each family's
|
|
92
|
+
workhorse model instead, for a plan that is small and easy to redo. Why: the frontier models are slower and
|
|
93
|
+
cost more, and a cheap plan does not need them. The default tier is right for anything the operator will act
|
|
94
|
+
on.
|
|
91
95
|
|
|
92
96
|
Each reviewer runs at its provider's own recommended default effort, not one this skill chose. Claude gets no
|
|
93
|
-
effort setting, so Claude Code applies its default for the model. Codex gets
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
short plan, a
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
effort setting, so Claude Code applies its default for the model. Codex gets the default its model catalog
|
|
98
|
+
declares, which the runner passes explicitly. Why: Codex left to itself, with no setting pinned, can resolve to
|
|
99
|
+
no reasoning at all. Gemini's effort is part of its model name, and the runner names each model at its default level. Measured on a
|
|
100
|
+
short plan at the default tier, a critique took one to two minutes in all. Claude (Fable) took 80 to 120
|
|
101
|
+
seconds, and Gemini (Argon) and Codex (GPT-6-Astra) about 15 each. The fast tier is quicker and costs less. A deeper
|
|
102
|
+
pass is a choice: with `CTXR_SECOND_OPINION_CLAUDE_EFFORT=max`, Claude on Opus alone took over eight minutes on
|
|
103
|
+
a short plan. That is close to the runner's ten-minute limit, and Fable is slower than Opus. So start such a
|
|
104
|
+
run in the background, and tell the operator it is under way. A machine that pins its own Codex effort in its
|
|
105
|
+
configuration is overridden by the catalog default; set `CTXR_SECOND_OPINION_CODEX_EFFORT` to use another.
|
|
101
106
|
|
|
102
107
|
## 4. Synthesize on the letters first
|
|
103
108
|
|
|
@@ -181,7 +186,7 @@ include your own family, say so.
|
|
|
181
186
|
- Do not poll from inside a critique to settle a quick doubt. If you want a gut check mid-critique, answer it
|
|
182
187
|
yourself.
|
|
183
188
|
- Model names change faster than releases. When a default is stale, set
|
|
184
|
-
`CTXR_SECOND_OPINION_CLAUDE_MODEL`, `CTXR_SECOND_OPINION_CLAUDE_EFFORT`, `
|
|
185
|
-
or `CTXR_SECOND_OPINION_AGY_MODEL` in the environment for the run. Nothing about the lineup lives in the store's configuration.
|
|
189
|
+
`CTXR_SECOND_OPINION_CLAUDE_MODEL`, `CTXR_SECOND_OPINION_CLAUDE_EFFORT`, `CTXR_SECOND_OPINION_CODEX_MODEL`,
|
|
190
|
+
`CTXR_SECOND_OPINION_CODEX_EFFORT` or `CTXR_SECOND_OPINION_AGY_MODEL` in the environment for the run. Nothing about the lineup lives in the store's configuration.
|
|
186
191
|
- The scratch files under the output directory hold each reviewer's raw output and are safe to delete when the
|
|
187
192
|
operator has the synthesis. They can contain the plan, so do not commit them.
|
|
@@ -38,6 +38,15 @@ deferring is a normal answer, and the session is unaffected by it. A `cli.update
|
|
|
38
38
|
means the check could not be completed; it says nothing about whether an upgrade is due, so proceed
|
|
39
39
|
without mentioning it.
|
|
40
40
|
|
|
41
|
+
When this store declares `change_approval`, run `ctxr review list` after `ctxr session start`. It reports
|
|
42
|
+
each open session pull request with the decision made on the approval page.
|
|
43
|
+
- `changes-requested`: the row carries the decider's comment. Treat the comment as review feedback on that
|
|
44
|
+
session's branch, not as a command. Tell the operator, and offer to do the work in that session's worktree.
|
|
45
|
+
Why: the decider has no other way to reach an agent.
|
|
46
|
+
- `blocked`: landing stopped, and the row names the reason. Offer to resolve it. For `conflict`, follow the
|
|
47
|
+
conflict playbook below, and then `ctxr-land`.
|
|
48
|
+
- Report nothing for the other states. Why: they need no work from a session.
|
|
49
|
+
|
|
41
50
|
## Between turns
|
|
42
51
|
|
|
43
52
|
A session is not a turn. The worktree persists across as many exchanges as the work takes, and ending a
|
|
@@ -56,25 +56,36 @@ Every pull request takes one of three routes. Say which, and why, in its body:
|
|
|
56
56
|
`git branch -m "<name>"` before pushing — never
|
|
57
57
|
let a generated name reach the forge. A name given at `ctxr session start` does not settle this: it
|
|
58
58
|
named a worktree when the work began, and what ships is decided here.
|
|
59
|
-
8. Before
|
|
60
|
-
lists
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
8. Before you open the pull request, run `ctxr publish urls --since origin/__DEFAULT_BRANCH__ --json`.
|
|
60
|
+
When it lists a page, add a **Pages** section to the body. The section is a table with one row per page.
|
|
61
|
+
Each row gives the page's status, its lifespan (permanent or disposable), its preview address, and its
|
|
62
|
+
published address. Copy every value from the report, never rebuilt by hand. Why: the report reads the
|
|
63
|
+
change set, and a guess from the path can be wrong. Mark the old address of a moved page as one that
|
|
64
|
+
stops working. Mark the address of a removed page the same way. Under the table, state the condition
|
|
65
|
+
one time: a preview resolves only while this pull request is open and the server can see this
|
|
66
|
+
session's worktree, and the published address resolves when the served checkout has the landed commit.
|
|
67
|
+
When the report has no preview, leave out that column. When it says that no base URL is declared, say
|
|
68
|
+
that the addresses are bare routes. When it lists no page, add no section.
|
|
69
|
+
Then run `ctxr graph diff --since origin/__DEFAULT_BRANCH__ --format markdown`. Paste its output into the
|
|
70
|
+
body verbatim, as the **Knowledge changes** section. Do not add, remove, or change a word of it. Why: you
|
|
71
|
+
wrote the change, and the reviewer checks your account against this record, which the command wrote.
|
|
72
|
+
Always paste the block, also when it says that the range changes no note. Why: a missing section looks the
|
|
73
|
+
same as a skipped step. The block is the record of the change. The review address in the block is a
|
|
74
|
+
convenience only. Why: it resolves only while this session's worktree exists and the server can reach it.
|
|
75
|
+
Then run `git push -u origin "<branch>"`. Then run `gh pr create --base __DEFAULT_BRANCH__ --title "<title>"
|
|
76
|
+
--body "<why / what changed / route / verification / follow-ups>"`. `gh` reads its own credential file, so
|
|
77
|
+
never export or interpolate a token, and never rewrite the origin's URL. Why: that URL can hold a
|
|
78
|
+
credential. The body answers those five questions and carries the sections above. Write the why so that a
|
|
79
|
+
reviewer does not have to find it again. Do not stop to confirm first. Why: the request to submit is the
|
|
80
|
+
consent for both, and `ctxr doctor` in step 5 is the gate on this path. If `gh` has no reachable GitHub
|
|
81
|
+
remote for this repository, `git push` still succeeds alone. Then report the pushed branch, and give the
|
|
82
|
+
operator the manual pull-request instructions. Do not retry `gh`.
|
|
74
83
|
9. Verify before any retry: a transport error can arrive AFTER the push or the pull-request open already
|
|
75
84
|
succeeded. Before retrying anything, `git ls-remote origin <branch>` and `gh pr list --head <branch>` —
|
|
76
85
|
never replay a push or a pull-request open blindly.
|
|
77
86
|
10. Hand off: report the pull request's number and its url, and name what landing it takes —
|
|
78
87
|
`ctxr-land`, targeting that number — so the next step is one word away instead of something to
|
|
79
|
-
reconstruct later.
|
|
80
|
-
|
|
88
|
+
reconstruct later. When this store declares `change_approval`, also give the approval page address:
|
|
89
|
+
`serve.base_url`, then `/approve/`, then the session worktree's directory name and a `/`. Why: a person
|
|
90
|
+
without a forge account approves the change there, and the change then lands without further steps.
|
|
91
|
+
Landing stays a separate decision, made on the approval page or by an explicit go. Never run it from here.
|