voyage/skills/annotating/SKILL.md
Kjell Tore Guttormsen 1be961aab1
fix(gate): part 2 verifier findings — non-object findings, carried backed copy, rule_keys, marks, approval prose, brief_path, engine put-back once
1. plan-review-dedup skips only a null or undefined finding; any other
   entry that is not an object (a string, a number, an array) goes on as
   before E, a finding with no valid severity that the plan gate counts as
   a blocker (it was dropped, so a string blocker recorded PASS).
2. plan-gate --record: a previous finding that rule A carried as counted and
   whose id is backed this round leaves the counted set, the counts, the
   verdict and the stop rule's summary (it was counted and listed in
   known_outside at once: BLOCK, not-fewer, and --accept-open refused).
3. A known_outside entry keeps rule_keys; extractVerdict accepts it (a list
   of strings), and the bare gate re-checks with the same keys the recorder
   used, so a merged finding whose security key is only in rule_keys is a
   WARN, not a STOP.
4. plan-critic, scope-guardian and /trekplan Phase 9's round-2 sentences say
   the Critic Verdict also lists known_outside and each is marked by id in
   previous. Code already accepts such a mark (dedup passes it; countRound
   ignores ids it does not carry; --record removes the entry on true);
   pinned in diff mode and full count.
5. The approval prose names ## Threat Model where it named only ## Intent +
   ## Goal: skills/annotating/SKILL.md (description, step 4, guard list),
   docs/command-modes.md, Handover 1's intent_approved_hash row, README
   (approval, Approve row, intent approval gate) and CLAUDE.md's /trekbrief
   row.
6. threat-model.mjs gains usableInRepo (a brief path relative to the repo
   top level) and repoRelativeBrief; --record stores brief_path relative to
   the top level, never absolute, and both gates read it from there, so the
   gate run from a subdirectory reads the same brief. reviewThreatModel
   uses usableInRepo. Handover 4 and the gate header say so.
7. review-finalize's outside pass puts an entry back only when no finding
   carries its row yet (engine-compare rowOf): an engine finding listed in
   known_outside while its payload is saved went back twice.
8. A test runs scripts/release-check.mjs on a review whose known_outside
   entry the brief does not back: exit 2 (control: backed passes the review
   check).

Existing tests changed: plan-gate-threat-model's entry-keys assertion now
expects rule_keys (item 3), and its planted non-security entry drops the
security key from rule_keys too, since the gate reads rule_keys (item 3).

Part 2 fix round of the E plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 10:50:20 +01:00

7.1 KiB

name description
annotating The operator's own annotation loop for a Voyage brief, plan or review (a research document too, when asked): build the page with scripts/annotate.mjs so the person writes every note, take the pasted Copy Prompt back, and revise note by note. Fix and Change edit only the text the note points at; a Question is answered and changes nothing unless the person says so; nothing the notes did not ask for is edited. A note on ## Intent or ## Goal (or ## Threat Model) of an approved brief makes the approval stale, and a changed plan needs a new approval, said before the edit. The page is built again on the revised file. Use when someone wants to comment on, annotate or mark up a brief, plan, review or research document in the browser, or pastes back the notes from that page (Fix, Change, Question).

Annotating

Overview

The annotation page lets the person read a Voyage artifact in the browser, click a line and write their own note, then copy every note back as one prompt. The loop is theirs: Claude builds the page, and later does what the notes say, no more. Two things go wrong without this skill. A Question gets answered and then written into the text as if it were a change. And the revision spreads: the same "fix" applied to parts no note named, a section rewritten to match, a new criterion added. Every such edit is a decision the person did not make, and on an approved brief or plan it also throws away an approval.

When to use

  • A /trek* command has just written a brief, plan or review and builds the page.
  • Someone says "let me comment on the plan", "I want to mark up the brief", "annotate this".
  • Someone asks to annotate a research document under research/. /trekresearch does not build the page itself; build it on request.
  • The person pastes a prompt that starts "Please revise the voyage artifact at" and lists notes as [Fix], [Change] or [Question].
  • Not for: someone who asks for Claude's own review of the artifact. That is a review (voyage:reviewing-adversarially, /trekreview), not annotation.

Process

  1. Build the page. Run the script on the file and keep its stdout, the absolute path of the page:

    ANNOT_HTML=$(node ${CLAUDE_PLUGIN_ROOT}/scripts/annotate.mjs "<path to the .md>" 2>&1)
    

    Print file://<path> and the line open file://<path>. If the script exits non-zero, say so in one line and go on: the page is a convenience, never a gate.

  2. The person writes the notes. Claude writes none. Next to the link, say only how the page works: click a line (or select a phrase first), pick Fix, Change or Question, write the note, and press "Copy Prompt" when done; the notes stay in the browser if the tab is closed. No list of "points I would look at", no summary of weaknesses, no suggested notes. Those would steer the notes the person writes. If they want Claude's view as well, they ask for a review.

  3. Read the pasted notes. Each note has a number, a type, the section, the quoted text it is anchored to, and the comment. Work from the quote and the comment, one note at a time.

  4. Before any edit, check what the edit costs.

    • A brief with intent_approved_hash in its frontmatter, and a Fix or Change in ## Intent or ## Goal (or ## Threat Model, when the brief has one): say in one line, before the edit, that the edit makes the approval stale, that /trekplan halts until the new text is approved, and how to approve it (/trekbrief --approve <project dir>). Then edit.
    • A plan the person approved: any edit gives it a new sha256, the approval log no longer covers it, and /trekexecute refuses it until the plan is approved again. Say that before the edit.
    • Never stamp, log or commit an approval in this loop. Only the person's explicit answer approves.
  5. Fix and Change: edit the text the note points at, and only that. The quote marks the place. If the comment cannot be done without touching other text (a summary that now disagrees, the same gap in another step, a new criterion the change seems to need), do not edit that text. List it in the reply as a question with a recommended answer. A validator error that was already there before the notes is reported, not fixed in passing.

  6. Question: answer it in the reply. The text stays as it was. No new row under alternatives, no new rule, no rewording to "make the answer visible". End the answer with one line offering to write it in if the person wants that. Only their yes turns a Question into a change.

  7. Build the page again on the revised file, and print the new link, so the person reads the revised text where they wrote the notes. The page keeps notes per file path, so last round's notes still show in its sidebar: tell the person that "Clear all" empties it before the next round.

  8. Reply note by note: for each number, "edited" (with the new wording) or "answered" (with the answer); then the approval consequence from step 4, the open questions from step 5, and the link.

Red flags

Thought What it means
"I'll add a few points of my own next to the link, to help" The notes are the person's. Your points steer theirs. Offer a review instead.
"The Question shows a gap, so I'll fix the gap too" Answer it. The gap is theirs to turn into a Change.
"I'll record the answer under alternatives, so it isn't lost" That is an edit nobody asked for, and on an approved plan it costs the approval.
"The same fix applies to steps 2 to 4 as well" Then ask. The note named one step.
"The summary no longer matches the new intent, I'll update it" Ask, with a recommended answer. The person approves the words.
"The brief fails the validator anyway, I'll fix the frontmatter while I'm here" Report it. A fix in passing is still a change the person did not ask for.
"I'll mention the approval is stale at the end" Say it before the edit. It is the price of the edit.
"I'll re-stamp it so planning can go on" Only the person's explicit approval stamps.

Enforced by

  • tests/lib/doc-consistency.test.mjs and tests/lib/skills-annotating-prose-pins.test.mjs (in npm test): the script scripts/annotate.mjs exists, the three producing commands call it, print the file:// link and the open line, and name this skill. Deterministic, but only at test time.
  • lib/validators/intent-approval.mjs (inside /trekplan): an edit to ## Intent or ## Goal (or ## Threat Model) after approval makes the approval stale, and planning halts until the new text is approved.
  • lib/util/approval-log.mjs (inside /trekexecute, K2): a plan whose current sha256 the approval log does not cover is refused. Both checks hold whether or not the edit's cost was said.
  • prose only: that the person writes every note and Claude none, that a Question changes nothing, that only what the notes ask for is edited, that the cost of the edit is said before it, and that the page is built again.

Credits

Voyage's own content: the page (scripts/annotate.mjs), the loop and its rules come from Voyage's pipeline and from the red baseline run for this skill. Nothing here is borrowed.