feat(voyage): add commands/trekrevise.md — 7th pipeline command + settings.json scope — v4.2 Step 6 [skip-docs]
Implements Phase 1-8 of /trekrevise (Handover 8 producer):
- Phase 1: parse mode + reject MULTI_ARTIFACT_NOT_SUPPORTED
- Phase 2: read source + check stale .local.bak
- Phase 3: parseAnchors + validateAnchorPlacement (no partial revisions)
- Phase 4: computeAnnotationDigest + non-additive detection
- Phase 5: revisionGuard orchestration (backup -> mutate -> validate -> rollback-on-fail)
- Phase 6: branch on outcome (applied / rolled-back / mutator-failed)
- Phase 7: optional review-gate (advisory, no auto-rollback)
- Phase 8: trekrevise-stats.jsonl + report
Frontmatter: name=trekrevise, model=opus, allowed-tools includes Read/Write/Edit/Bash/Grep/Glob.
Reuses lib/parsers/anchor-parser, lib/parsers/annotation-digest,
lib/util/markdown-write, lib/util/revision-guard, lib/validators/{brief,plan,review}.
settings.json: register new top-level scope trekrevise with
trekrevise-stats.jsonl tracking (mirrors trekplan/trekresearch shape).
Forward-pinning to keep doc-consistency invariants green:
- tests/lib/doc-consistency.test.mjs: known-scopes allowlist += trekrevise
- CLAUDE.md commands table: add /trekrevise row
Plan Step 13 owns the full README/CLAUDE.md/CHANGELOG content sync;
this commit is the implementation milestone, not the doc milestone.
Refs plan.md Step 6 + plan-critic M3.
This commit is contained in:
parent
2f4330265c
commit
4fbc52bbb4
4 changed files with 517 additions and 1 deletions
|
|
@ -15,6 +15,7 @@ Voyage — a contract-driven Claude Code pipeline: brief, research, plan, execut
|
|||
| `/trekplan` | Plan — brief-reviewer, explore, plan, review. Requires `--brief` or `--project`. Auto-discovers `architecture/overview.md` if present | opus |
|
||||
| `/trekexecute` | Execute — disciplined plan/session-spec executor with failure recovery | opus |
|
||||
| `/trekreview` | Review — independent post-hoc review of delivered code against the brief. Produces `review.md` with severity-tagged findings (Handover 6) | opus |
|
||||
| `/trekrevise` | Revise — apply operator annotations from the playground back into brief/plan/review with audit trail (Handover 8). Requires `--project` | opus |
|
||||
| `/trekcontinue` | Continue — resumes the next session of a multi-session voyage project. Reads `.session-state.local.json` (Handover 7) and immediately begins executing | opus |
|
||||
| `/trekendsession` | End-session — mark the current session complete and write session-state pointing at the next session. Helper for informal multi-session flows | sonnet |
|
||||
|
||||
|
|
|
|||
508
plugins/voyage/commands/trekrevise.md
Normal file
508
plugins/voyage/commands/trekrevise.md
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
---
|
||||
name: trekrevise
|
||||
description: |
|
||||
Apply operator-annotated brief/plan/review back into the source artifact
|
||||
with audit trail. Reads anchored comments from pasted (or `--from-file`)
|
||||
content, validates anchor placement, computes a canonical
|
||||
annotation_digest, and writes the revision in-place with
|
||||
rollback-on-validator-fail. Implements Handover 8 (annotation → revision).
|
||||
argument-hint: "--project <dir> [--from-file <path>] [--target {brief|plan|review|auto}] [--reason <text>] [--profile <name>] [--gates <mode>]"
|
||||
model: opus
|
||||
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
||||
---
|
||||
|
||||
# Ultrarevise Local v1.0
|
||||
|
||||
Apply operator annotations from the voyage playground back into the source
|
||||
artifact (`brief.md`, `plan.md`, or `review.md`) with audit-trail in
|
||||
frontmatter. Implements **Handover 8 (annotation → revision)**.
|
||||
|
||||
Pipeline position:
|
||||
|
||||
```
|
||||
/trekbrief → brief.md
|
||||
/trekresearch → research/*.md
|
||||
/trekplan → plan.md
|
||||
/trekexecute → progress.json (+ commits)
|
||||
/trekreview → review.md
|
||||
/trekrevise → in-place revision of brief|plan|review (this command)
|
||||
```
|
||||
|
||||
The annotation source is the **operator-edited markdown** that the voyage
|
||||
playground exports. The playground reads `{target}.md`, lets the operator
|
||||
add anchored comments via three creation gestures, and exports a markdown
|
||||
blob carrying `<!-- voyage:anchor ... -->` block-level HTML comments
|
||||
inline plus the operator's revised body content. `/trekrevise` consumes
|
||||
that blob, validates the anchor placement, computes a canonical
|
||||
`annotation_digest`, and replaces the source artifact in-place. A
|
||||
pre-revision backup (`{target}.md.local.bak`) is created and used to roll
|
||||
back if the post-write validator rejects the new content.
|
||||
|
||||
The revision is **additive by default**: applying the same annotation set
|
||||
twice is idempotent (same digest, same body). Non-additive revisions
|
||||
(structural step reorder, section removal, frontmatter `*_version`
|
||||
changes) require an explicit `--reason "<text>"`.
|
||||
|
||||
See `docs/HANDOVER-CONTRACTS.md § Handover 8` for the schema contract.
|
||||
|
||||
## Phase 1 — Parse mode and validate input
|
||||
|
||||
Parse `$ARGUMENTS` via the shared arg-parser:
|
||||
|
||||
```bash
|
||||
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekrevise "$@"
|
||||
```
|
||||
|
||||
The parser recognizes these flags (see `lib/parsers/arg-parser.mjs`
|
||||
FLAG_SCHEMA `trekrevise` entry):
|
||||
|
||||
| Flag | Type | Purpose |
|
||||
|------|------|---------|
|
||||
| `--project <dir>` | valued | Required. Path to the trekplan project folder containing the target artifact. |
|
||||
| `--from-file <path>` | valued | Optional. Read annotated content from a file instead of stdin/pasted prompt. |
|
||||
| `--target {brief\|plan\|review\|auto}` | valued | Optional. Which artifact to revise. `auto` (default) infers from frontmatter `type:` field of the annotated input. |
|
||||
| `--reason <text>` | valued | Optional. Required when revision is non-additive (see Phase 4). |
|
||||
| `--profile <name>` | valued | Optional. Model profile (`economy`, `balanced`, `premium`, custom). Default: `balanced`. |
|
||||
| `--gates <mode>` | valued | Optional. Autonomy mode (`open`, `closed`, `adaptive`). Default: `adaptive`. |
|
||||
|
||||
Resolution:
|
||||
|
||||
1. If `--project` is missing, print usage and stop:
|
||||
```
|
||||
Error: --project <dir> is required.
|
||||
Usage: /trekrevise --project <dir> [--from-file <path>] [--target {brief|plan|review|auto}] [--reason <text>]
|
||||
```
|
||||
2. Trim trailing slash from `{dir}`. Set:
|
||||
- `project_dir = {dir}`
|
||||
- `brief_path = {dir}/brief.md`
|
||||
- `plan_path = {dir}/plan.md`
|
||||
- `review_path = {dir}/review.md`
|
||||
3. If `{dir}` does not exist:
|
||||
```
|
||||
Error: project directory missing: {dir}
|
||||
Run /trekbrief first.
|
||||
```
|
||||
4. Determine the **annotated input source**:
|
||||
- If `--from-file <path>` is set, read that file. If the file is missing
|
||||
or unreadable, stop with `Error: --from-file path not readable: {path}`.
|
||||
- Otherwise, the annotated content MUST appear in the operator's prompt
|
||||
(pasted directly into the chat). Capture it from the prompt text.
|
||||
- If neither source yields content, stop with:
|
||||
```
|
||||
Error: no annotated content provided. Pass --from-file <path> or paste the
|
||||
content directly into your prompt.
|
||||
```
|
||||
5. **Reject multi-artifact bundle.** Scan the annotated input for two or
|
||||
more lines matching `^---$` at line-start that introduce frontmatter
|
||||
blocks (i.e. more than one `---\n...\n---\n` pair). If detected:
|
||||
```
|
||||
Error: MULTI_ARTIFACT_NOT_SUPPORTED — annotated input contains more than
|
||||
one artifact (frontmatter blocks detected: {N}). /trekrevise revises one
|
||||
artifact per invocation. Split the bundle and re-run for each target.
|
||||
```
|
||||
Stop. No partial revisions.
|
||||
6. Resolve `target`:
|
||||
- If `--target` is `brief`, `plan`, or `review`: use it explicitly.
|
||||
- If `--target` is `auto` (or missing): parse the annotated input's
|
||||
frontmatter via `parseDocument`. Read the `type:` field — expect
|
||||
`brief`, `plan`, or `review`. If absent, fall back to the artifact
|
||||
whose schema validates against the input (try in order: brief, plan,
|
||||
review). If none match, stop with:
|
||||
```
|
||||
Error: cannot infer --target from annotated input frontmatter.
|
||||
Pass --target {brief|plan|review} explicitly.
|
||||
```
|
||||
7. Resolve the target file path: `target_path = {project_dir}/{target}.md`.
|
||||
If it does not exist, stop:
|
||||
```
|
||||
Error: target artifact missing: {target_path}
|
||||
Run /trek{brief|plan|review} first to produce it.
|
||||
```
|
||||
|
||||
Set:
|
||||
- `mode = revise` (the only mode currently)
|
||||
- `profile`, `gates`, `reason` per flags
|
||||
|
||||
Report:
|
||||
```
|
||||
Mode: revise
|
||||
Project: {project_dir}
|
||||
Target: {target} → {target_path}
|
||||
Source: {--from-file path | pasted}
|
||||
Profile: {profile}
|
||||
```
|
||||
|
||||
## Phase 2 — Read source artifact + check rollback hygiene
|
||||
|
||||
Stale-backup precheck:
|
||||
|
||||
```bash
|
||||
test -f "{target_path}.local.bak"
|
||||
```
|
||||
|
||||
If a `.local.bak` exists from a previously aborted run, abort:
|
||||
|
||||
```
|
||||
Error: stale rollback backup found at {target_path}.local.bak
|
||||
Inspect it (it represents the pre-revision state of a prior aborted run),
|
||||
then either restore it manually with:
|
||||
cp "{target_path}.local.bak" "{target_path}"
|
||||
rm "{target_path}.local.bak"
|
||||
or delete it if you want to keep the current target_path:
|
||||
rm "{target_path}.local.bak"
|
||||
After resolving, re-run /trekrevise.
|
||||
```
|
||||
|
||||
Stop. The revision is not applied.
|
||||
|
||||
Read the source artifact:
|
||||
|
||||
```js
|
||||
import { parseDocument } from '${CLAUDE_PLUGIN_ROOT}/lib/util/frontmatter.mjs';
|
||||
const source = parseDocument(readFileSync(target_path, 'utf-8'));
|
||||
```
|
||||
|
||||
If `source.valid === false`:
|
||||
|
||||
```
|
||||
Error: source artifact has malformed frontmatter: {target_path}
|
||||
Fix it manually before annotating.
|
||||
```
|
||||
|
||||
Capture:
|
||||
- `existing_frontmatter = source.parsed.frontmatter`
|
||||
- `existing_body = source.parsed.body`
|
||||
- `existing_revision = existing_frontmatter.revision || 0`
|
||||
|
||||
## Phase 3 — Parse anchors + validate placement
|
||||
|
||||
Parse anchors from the **annotated input** (not the source artifact):
|
||||
|
||||
```js
|
||||
import { parseAnchors, validateAnchorPlacement, stripAnchors } from '${CLAUDE_PLUGIN_ROOT}/lib/parsers/anchor-parser.mjs';
|
||||
const annotated = parseDocument(annotated_input_text);
|
||||
const annotated_body = annotated.parsed.body;
|
||||
const anchors_result = parseAnchors(annotated_body);
|
||||
```
|
||||
|
||||
If `anchors_result.valid === false`:
|
||||
|
||||
```
|
||||
Error: anchor parse failed in annotated input.
|
||||
{for each error: file:line:rule_key — message}
|
||||
```
|
||||
|
||||
Stop. **No partial revisions** — abort BEFORE any write.
|
||||
|
||||
Run placement validation:
|
||||
|
||||
```js
|
||||
const placement_result = validateAnchorPlacement(annotated_body, anchors_result.parsed);
|
||||
```
|
||||
|
||||
If `placement_result.valid === false`:
|
||||
|
||||
```
|
||||
Error: anchor placement violations detected.
|
||||
{for each error: line N: rule_key — message}
|
||||
```
|
||||
|
||||
Stop.
|
||||
|
||||
Capture `anchors = anchors_result.parsed` (an array of anchor objects with
|
||||
`{id, target, line, snippet?, intent?}`).
|
||||
|
||||
If `anchors.length === 0`, the operator submitted an annotation-free
|
||||
revision. Continue — empty-anchor round-trip is a valid case (used by
|
||||
SC2). The body diff will still be applied; the audit fields will record
|
||||
zero anchors plus an all-zeros digest baseline.
|
||||
|
||||
## Phase 4 — Compute revision diff + digest
|
||||
|
||||
Compute the canonical digest from the annotation set:
|
||||
|
||||
```js
|
||||
import { computeAnnotationDigest } from '${CLAUDE_PLUGIN_ROOT}/lib/parsers/annotation-digest.mjs';
|
||||
const annotation_digest = computeAnnotationDigest(anchors);
|
||||
```
|
||||
|
||||
Determine the new revision counter:
|
||||
|
||||
```js
|
||||
const new_revision = (existing_revision | 0) + 1;
|
||||
```
|
||||
|
||||
**Detect non-additive revision.** A revision is non-additive when ANY of:
|
||||
|
||||
- The body removes a `### Step N:` heading that previously existed.
|
||||
- The body removes any `## <required-section>` heading per the target's
|
||||
validator (e.g. plan: "Implementation Plan", "Verification"; brief:
|
||||
"Goal", "Success Criteria"; review: "Executive Summary", "Coverage").
|
||||
- The frontmatter changes any `*_version` field (e.g. `plan_version`,
|
||||
`brief_version`, `review_version`).
|
||||
- A `### Step N:` heading is reordered (different N sequence than before).
|
||||
|
||||
Compute these by comparing `existing_body` heading sequence with the
|
||||
`annotated_body` (with anchors stripped via `stripAnchors`). The
|
||||
heading-sequence comparison is sufficient — content edits inside an
|
||||
existing step are always additive.
|
||||
|
||||
If non-additive AND `--reason` was NOT supplied:
|
||||
|
||||
```
|
||||
Error: non-additive revision detected:
|
||||
- {bullet list of detected non-additive changes}
|
||||
|
||||
Re-run with --reason "<short explanation>" to acknowledge the structural
|
||||
change and record it in revision_reason for the audit trail.
|
||||
```
|
||||
|
||||
Stop. The revision is not applied.
|
||||
|
||||
If non-additive AND `--reason` is supplied: capture
|
||||
`revision_reason = <reason>`. If additive, leave `revision_reason`
|
||||
unset (omitted from frontmatter).
|
||||
|
||||
## Phase 5 — Apply revisions in-place
|
||||
|
||||
Strip anchors from the annotated body to produce the new artifact body:
|
||||
|
||||
```js
|
||||
const new_body = stripAnchors(annotated_body);
|
||||
```
|
||||
|
||||
Build the new frontmatter object by merging the existing fields with the
|
||||
revision audit fields:
|
||||
|
||||
```js
|
||||
const new_frontmatter = {
|
||||
...existing_frontmatter,
|
||||
revision: new_revision,
|
||||
source_annotations: anchors.map(a => ({
|
||||
id: a.id,
|
||||
target_artifact: target,
|
||||
target_anchor: a.target,
|
||||
line: a.line,
|
||||
intent: a.intent || 'change',
|
||||
snippet: a.snippet || '',
|
||||
timestamp: new Date().toISOString(),
|
||||
})),
|
||||
annotation_digest,
|
||||
...(revision_reason ? { revision_reason } : {}),
|
||||
};
|
||||
```
|
||||
|
||||
Apply the revision via `revisionGuard` from `lib/util/revision-guard.mjs`,
|
||||
which performs the full **backup → mutate → atomic write → validate →
|
||||
rollback-on-fail** orchestration:
|
||||
|
||||
```js
|
||||
import { revisionGuard } from '${CLAUDE_PLUGIN_ROOT}/lib/util/revision-guard.mjs';
|
||||
import { validateBrief, validatePlan, validateReview } from '${CLAUDE_PLUGIN_ROOT}/lib/validators/...';
|
||||
|
||||
const validator = ({
|
||||
brief: validateBrief,
|
||||
plan: validatePlan,
|
||||
review: validateReview,
|
||||
})[target];
|
||||
|
||||
const result = revisionGuard(
|
||||
target_path,
|
||||
({ frontmatter, body }) => ({
|
||||
frontmatter: new_frontmatter,
|
||||
body: new_body,
|
||||
}),
|
||||
validator,
|
||||
);
|
||||
```
|
||||
|
||||
`revisionGuard` returns `{outcome, validator_result, sha256_before,
|
||||
sha256_after, error?}`. Outcomes:
|
||||
|
||||
- `applied` — write succeeded, validator passed, `.local.bak` deleted.
|
||||
- `rolled-back` — write succeeded, validator FAILED, file restored from
|
||||
`.local.bak`, byte-identical to pre-revision state.
|
||||
- `mutator-failed` — pre-existing backup, mutator threw, or read failed.
|
||||
|
||||
## Phase 6 — Validate revision
|
||||
|
||||
Branch on the `revisionGuard` outcome:
|
||||
|
||||
### outcome === 'applied'
|
||||
|
||||
The revision is in place. Surface validator warnings (if any) to the
|
||||
operator, but do not roll back. Continue to Phase 7.
|
||||
|
||||
### outcome === 'rolled-back'
|
||||
|
||||
The post-write validator rejected the new content. The file is
|
||||
byte-identical to its pre-revision state.
|
||||
|
||||
```
|
||||
Validator REJECTED the revision. Rolled back to pre-revision state.
|
||||
sha256_before === sha256_after (byte-identical): {true|false}
|
||||
|
||||
Validator errors:
|
||||
- {code} {file}:{line} — {message}
|
||||
...
|
||||
|
||||
To inspect the proposed (rejected) revision, retry with --from-file pointing
|
||||
at a fixed annotated input, or fix the annotation set in the playground and
|
||||
re-export.
|
||||
```
|
||||
|
||||
Stop. The audit-trail fields are NOT recorded; revision counter is
|
||||
unchanged.
|
||||
|
||||
### outcome === 'mutator-failed'
|
||||
|
||||
```
|
||||
Error: revision could not be applied: {revisionGuard.error}
|
||||
|
||||
If the error references a pre-existing backup, follow Phase 2's
|
||||
remediation steps.
|
||||
```
|
||||
|
||||
Stop.
|
||||
|
||||
## Phase 7 — Optional review-gate (plan/review targets only)
|
||||
|
||||
Only runs when `target === 'plan'` AND `{review_path}` exists, OR
|
||||
`target === 'review'` AND `{plan_path}` exists. Otherwise skip to Phase 8.
|
||||
|
||||
Run the existing review (validate-only mode) against the revised plan, or
|
||||
re-validate the revised review:
|
||||
|
||||
```bash
|
||||
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/review-validator.mjs --json "{review_path}"
|
||||
```
|
||||
|
||||
If the validator returns BLOCK or FAIL:
|
||||
|
||||
```
|
||||
Review-gate WARN: revised {target}.md is in place, but the review at
|
||||
{review_path} contains BLOCKER findings against the now-revised content.
|
||||
Verdict: BLOCK
|
||||
Top findings:
|
||||
- {list}
|
||||
|
||||
The revised file is NOT auto-rolled-back. You may:
|
||||
- Re-annotate to address the BLOCKER findings, then /trekrevise again.
|
||||
- Accept the gap and run /trekreview to refresh review.md.
|
||||
- Manually restore via:
|
||||
cp "{target_path}.local.bak" "{target_path}"
|
||||
(Note: the .local.bak was deleted on Phase 6 success. To re-rollback
|
||||
after Phase 7, use git: `git checkout HEAD -- "{target_path}"` if you
|
||||
have not yet committed the revision.)
|
||||
```
|
||||
|
||||
The revised file remains as written. Continue to Phase 8.
|
||||
|
||||
If the gate passes (verdict ALLOW or WARN-only): continue silently.
|
||||
|
||||
## Phase 8 — Stats + report
|
||||
|
||||
Append a stats line to `${CLAUDE_PLUGIN_DATA}/trekrevise-stats.jsonl`
|
||||
(create the file if it does not exist):
|
||||
|
||||
```json
|
||||
{"ts":"{ISO-8601}","target":"{brief|plan|review}","project_dir":"{dir}","revision":{N},"anchor_count":{N},"digest":"{16-char-hex}","validator_verdict":"{pass|fail-rolled-back}","outcome":"{applied|rolled-back|mutator-failed}","profile_used":"{profile}"}
|
||||
```
|
||||
|
||||
Use inline `appendFileSync` (mirrors the jsonl-append pattern in
|
||||
`hooks/scripts/post-bash-stats.mjs:50`). If `${CLAUDE_PLUGIN_DATA}` is
|
||||
unset or not writable, skip stats silently. Never let stats failures block
|
||||
the main workflow.
|
||||
|
||||
Emit the human-readable summary:
|
||||
|
||||
```
|
||||
## Ultrarevise Complete
|
||||
|
||||
**Project:** {project_dir}
|
||||
**Target:** {target} ({target_path})
|
||||
**Revision:** {existing_revision} → {new_revision}
|
||||
**Anchors applied:** {N}
|
||||
**annotation_digest:** {16-char-hex}
|
||||
**Outcome:** {applied | rolled-back | mutator-failed}
|
||||
{if revision_reason}: **revision_reason:** {revision_reason}
|
||||
|
||||
### Audit fields written
|
||||
- revision: {N}
|
||||
- source_annotations: {N entries}
|
||||
- annotation_digest: {hex}
|
||||
{if revision_reason}: - revision_reason: "{reason}"
|
||||
|
||||
{if Phase 7 surfaced findings}:
|
||||
### Review-gate WARN
|
||||
- Verdict: {BLOCK|WARN}
|
||||
- Top findings:
|
||||
- {list}
|
||||
|
||||
You can:
|
||||
- Inspect the revised file at {target_path}
|
||||
- Run /trekreview --project {project_dir} to refresh review.md
|
||||
- Run /trekplan --project {project_dir} to extend the plan
|
||||
- Re-annotate via the playground and run /trekrevise again
|
||||
- Roll back manually via: git checkout HEAD -- "{target_path}"
|
||||
```
|
||||
|
||||
## Profile (v4.1)
|
||||
|
||||
Accepts `--profile <name>` where `<name>` is `economy`, `balanced`, `premium`,
|
||||
or a custom profile under `voyage-profiles/`. Default: `balanced`.
|
||||
|
||||
Resolution order (per `lib/profiles/resolver.mjs`):
|
||||
|
||||
1. `--profile` flag (source: `flag`)
|
||||
2. `VOYAGE_PROFILE` env-var (source: `env`)
|
||||
3. `balanced` default (source: `default`)
|
||||
|
||||
The selected profile drives `phase_models.revise`. `/trekrevise` is mostly
|
||||
deterministic (parsing + validating + atomic writes), so all built-in
|
||||
profiles use sonnet for any sub-agent invocation. The operator-facing
|
||||
synthesis in Phase 6/7 stays in the main thread regardless of profile.
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
/trekrevise --profile balanced --project .claude/projects/2026-05-09-foo
|
||||
VOYAGE_PROFILE=premium /trekrevise --project ... --target plan
|
||||
```
|
||||
|
||||
Stats records emit `profile_used` (and `profile_source` when available).
|
||||
|
||||
## Hard rules
|
||||
|
||||
- **No partial revisions.** If anchor parse or placement validation fails
|
||||
in Phase 3, abort BEFORE any write. The source artifact is never left
|
||||
in a half-revised state.
|
||||
- **One artifact per invocation.** Multi-artifact bundles return
|
||||
`MULTI_ARTIFACT_NOT_SUPPORTED`. Split into per-artifact runs.
|
||||
- **revision_reason is required for non-additive changes.** Step
|
||||
reorder, section removal, and `*_version` bumps require an explicit
|
||||
reason recorded in frontmatter for audit.
|
||||
- **Backup hygiene.** Pre-existing `.local.bak` blocks the run. Operator
|
||||
must inspect or delete it; the executor never auto-overwrites it.
|
||||
- **Validator is the gate.** A revision that writes successfully but
|
||||
fails post-write validation is rolled back to byte-identical
|
||||
pre-revision state. Audit fields are NOT recorded on rollback.
|
||||
- **Idempotent digest.** Re-applying the same annotation set yields the
|
||||
same `annotation_digest`. The digest is canonical SHA-256 over a
|
||||
field-sorted, id-sorted, pipe-separated, line-joined serialization
|
||||
(16-char hex prefix).
|
||||
- **Forward-compat fields.** `revision`, `source_annotations`,
|
||||
`annotation_digest`, and `revision_reason` are additive frontmatter
|
||||
fields. Validators that predate v4.2 ignore them. Artifacts without
|
||||
`revision:` are treated as `revision: 0`.
|
||||
- **Anchor format.** `<!-- voyage:anchor id="ANN-NNNN" target="..."
|
||||
line="N" [snippet="..."] [intent="fix|change|question|block"] -->`
|
||||
block-level only; placement disipline enforced (not in list-items, not
|
||||
inside fenced code blocks, not at line-start collisions with frontmatter
|
||||
delimiter, manifest:, plan_version:, ### Step N:, ## required sections,
|
||||
or 40-char hex finding-IDs).
|
||||
- **No production code.** This command never runs production code, never
|
||||
writes to anything outside `{project_dir}` and `${CLAUDE_PLUGIN_DATA}`.
|
||||
- **Operator has final say.** The review-gate in Phase 7 is advisory —
|
||||
it never auto-rolls-back a successful revision. The operator decides
|
||||
whether to re-annotate, refresh the review, or accept the gap.
|
||||
|
|
@ -27,5 +27,12 @@
|
|||
"enabled": true,
|
||||
"statsFile": "trekresearch-stats.jsonl"
|
||||
}
|
||||
},
|
||||
"trekrevise": {
|
||||
"defaultMode": "default",
|
||||
"tracking": {
|
||||
"enabled": true,
|
||||
"statsFile": "trekrevise-stats.jsonl"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -80,7 +80,7 @@ test('commands/trekexecute.md still parses v1.7 plan schema', () => {
|
|||
|
||||
test('settings.json has only known top-level scopes after Spor 0 cleanup', () => {
|
||||
const cfg = JSON.parse(read('settings.json'));
|
||||
const known = ['trekplan', 'trekresearch'];
|
||||
const known = ['trekplan', 'trekresearch', 'trekrevise'];
|
||||
for (const k of Object.keys(cfg)) {
|
||||
assert.ok(known.includes(k), `Unknown top-level scope in settings.json: ${k}`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue