feat(ultraplan-local): seal Opus-4.7 schema-drift defense in Phase 8

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.
This commit is contained in:
Kjell Tore Guttormsen 2026-05-04 07:41:48 +02:00
commit 84eae1fad7
2 changed files with 92 additions and 0 deletions

View file

@ -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 <token>` 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

View file

@ -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',
);
});