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:
parent
50f0629baf
commit
84eae1fad7
2 changed files with 92 additions and 0 deletions
|
|
@ -554,6 +554,69 @@ Do NOT write this synthesis to disk. It is internal working context only.
|
||||||
|
|
||||||
## Phase 8 — Deep planning
|
## 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 brief file (from `--brief` or `--project`).
|
||||||
Read the plan template: @${CLAUDE_PLUGIN_ROOT}/templates/plan-template.md
|
Read the plan template: @${CLAUDE_PLUGIN_ROOT}/templates/plan-template.md
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)',
|
'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',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue