From 84eae1fad752f5c68c66cb3dc3d65ef1832e68dd Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Mon, 4 May 2026 07:41:48 +0200 Subject: [PATCH] feat(ultraplan-local): seal Opus-4.7 schema-drift defense in Phase 8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline STEP_HEADING_REGEX, FORBIDDEN_HEADING_REGEX, the canonical step+manifest example, and the post-write plan-validator self-check directly into Phase 8 of commands/ultraplan-local.md. This eliminates the dependency on Opus 4.7 implicitly loading agents/planning-orchestrator.md — the format contract now travels with the command file itself. Source: research/04 D5 + plan-v2 Step 7. Pin in tests/lib/doc-consistency.test.mjs locks the substrings so future edits cannot silently regress the seal. --- .../commands/ultraplan-local.md | 63 +++++++++++++++++++ .../tests/lib/doc-consistency.test.mjs | 29 +++++++++ 2 files changed, 92 insertions(+) diff --git a/plugins/ultraplan-local/commands/ultraplan-local.md b/plugins/ultraplan-local/commands/ultraplan-local.md index a4b473c..8cc0f56 100644 --- a/plugins/ultraplan-local/commands/ultraplan-local.md +++ b/plugins/ultraplan-local/commands/ultraplan-local.md @@ -554,6 +554,69 @@ Do NOT write this synthesis to disk. It is internal working context only. ## Phase 8 — Deep planning +> **Schema-drift defense (sealed inline so this contract survives even when +> `agents/planning-orchestrator.md` is not implicitly loaded by Opus 4.7).** +> +> The plan you write MUST satisfy these regexes. The executor parses with +> strict regex matching; any deviation breaks parsing and forces a re-plan. +> +> ``` +> STEP_HEADING_REGEX = /^### Step (\d+):\s+(.+?)\s*$/m +> FORBIDDEN_HEADING_REGEX = /^(?:##|###) (?:Fase|Phase|Stage|Steg) \d+/m +> ``` +> +> **FORBIDDEN headings** (parser rejects these — do not emit them under +> Implementation Plan): +> - `## Fase 1`, `### Fase 1` — Norwegian narrative format +> - `## Phase 1`, `### Phase 1` — narrative phase format +> - `## Stage 1`, `### Stage 1` — narrative stage format +> - `## Steg 1`, `### Steg 1` — Norwegian step word +> - `### 1.` or `### 1)` — numbered without "Step" +> - `### Step 1 —` (em-dash instead of colon) +> - Any heading that doesn't match `STEP_HEADING_REGEX` +> +> **REQUIRED step shape** — copy this canonical example verbatim, substituting +> file paths, descriptions, and patterns. Preserve the exact heading format, +> bullet field names, and Manifest YAML structure. Do not invent new field +> names. Do not skip fields. Do not nest steps under sub-headings. +> +> ````markdown +> ### Step 1: Add JWT verification middleware +> +> - **Files:** `src/middleware/jwt.ts` +> - **Changes:** Create new middleware function `verifyJWT(req, res, next)` that reads `Authorization: Bearer ` header, verifies signature with `process.env.JWT_SECRET`, attaches decoded payload to `req.user`, and returns 401 on invalid/missing token. (new file) +> - **Reuses:** `jsonwebtoken.verify()` (already in package.json), pattern from `src/middleware/cors.ts` +> - **Test first:** +> - File: `src/middleware/jwt.test.ts` (new) +> - Verifies: valid token attaches user; invalid token returns 401; missing header returns 401 +> - Pattern: `src/middleware/cors.test.ts` (follow this style) +> - **Verify:** `npm test -- jwt.test.ts` → expected: `3 passing` +> - **On failure:** revert — `git checkout -- src/middleware/jwt.ts src/middleware/jwt.test.ts` +> - **Checkpoint:** `git commit -m "feat(auth): add JWT verification middleware"` +> - **Manifest:** +> ```yaml +> manifest: +> expected_paths: +> - src/middleware/jwt.ts +> - src/middleware/jwt.test.ts +> min_file_count: 2 +> commit_message_pattern: "^feat\\(auth\\): add JWT verification middleware$" +> bash_syntax_check: [] +> forbidden_paths: +> - src/middleware/cors.ts +> must_contain: +> - path: src/middleware/jwt.ts +> pattern: "verifyJWT" +> ``` +> ```` +> +> **Validator self-check (mandatory after writing `plan.md`):** run +> `node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-validator.mjs --strict --json {plan_path}` +> and re-revise the plan if it fails. The validator is the source of truth for +> heading shape, manifest presence, and required-field coverage. If +> `${CLAUDE_PLUGIN_ROOT}` is unset (rare in practice), fall back to the +> equivalent path under your validators cache or the repo's `lib/validators/`. + Read the brief file (from `--brief` or `--project`). Read the plan template: @${CLAUDE_PLUGIN_ROOT}/templates/plan-template.md diff --git a/plugins/ultraplan-local/tests/lib/doc-consistency.test.mjs b/plugins/ultraplan-local/tests/lib/doc-consistency.test.mjs index 5890765..c976b7b 100644 --- a/plugins/ultraplan-local/tests/lib/doc-consistency.test.mjs +++ b/plugins/ultraplan-local/tests/lib/doc-consistency.test.mjs @@ -157,3 +157,32 @@ test('rule-catalogue has exactly 12 entries', async () => { 'lib/review/rule-catalogue.mjs RULE_CATALOGUE size invariant: must be 12 (v1.0 baseline)', ); }); + +test('commands/ultraplan-local.md Phase 8 seals Opus-4.7 schema-drift defense', () => { + const cmd = read('commands/ultraplan-local.md'); + // Locate Phase 8 section + const phase8Start = cmd.indexOf('## Phase 8'); + assert.ok(phase8Start >= 0, 'Phase 8 heading missing'); + const phase8End = cmd.indexOf('## Phase 9', phase8Start); + assert.ok(phase8End > phase8Start, 'Phase 9 heading missing — could not bound Phase 8'); + const phase8 = cmd.slice(phase8Start, phase8End); + // Required regex source-of-truth references + assert.ok( + phase8.includes('STEP_HEADING_REGEX'), + 'Phase 8 should inline STEP_HEADING_REGEX so format contract survives without orchestrator-doc loading', + ); + assert.ok( + phase8.includes('FORBIDDEN_HEADING_REGEX'), + 'Phase 8 should inline FORBIDDEN_HEADING_REGEX (Step 7 — schema-drift seal)', + ); + // Required validator self-check + assert.ok( + phase8.includes('plan-validator.mjs --strict'), + 'Phase 8 should mandate post-write `plan-validator.mjs --strict` self-check', + ); + // Forbidden-headings list (literal "FORBIDDEN" appears more than once: in regex const + in human-readable list) + assert.ok( + /FORBIDDEN/.test(phase8), + 'Phase 8 should explicitly enumerate FORBIDDEN headings', + ); +});