Skip to content

fix: detect hidden requirements in main specs#966

Merged
TabishB merged 2 commits intomainfrom
TabishB/validate-issue-954
Apr 12, 2026
Merged

fix: detect hidden requirements in main specs#966
TabishB merged 2 commits intomainfrom
TabishB/validate-issue-954

Conversation

@TabishB
Copy link
Copy Markdown
Contributor

@TabishB TabishB commented Apr 12, 2026

Closes #954

Summary

  • detect stray delta headers and hidden ### Requirement: blocks outside the parsed ## Requirements section in main specs
  • fail archive early with a structural error when a target main spec is malformed instead of surfacing a misleading missing-requirement error
  • ignore fenced-code headings during markdown parsing and normalize existing source-of-truth specs so parsed requirement counts match visible headers

Testing

  • npm test

Summary by CodeRabbit

  • Documentation

    • Simplified spec headings by removing redundant "Requirement:" prefixes for clearer reading.
  • Bug Fixes

    • Headings inside Markdown code blocks no longer affect document structure.
    • Spec updates now abort when structural issues (misplaced requirement headers or delta headers) are detected.
  • Tests

    • Added tests covering fenced-code handling and structural validation behavior.

@1code-async
Copy link
Copy Markdown
Contributor

1code-async bot commented Apr 12, 2026

Task completed.

I'll review this PR by examining the changes. Let me start by understanding the codebase and the diff.


View full conversation

Powered by 1Code

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

Adds code-fence-aware Markdown parsing and a new spec-structure checker that detects stray delta headers and requirement headers outside the main "## Requirements" region; integrates these structural checks into validation and spec-apply/archive paths and adds tests covering positive and fenced-code negative cases. (48 words)

Changes

Cohort / File(s) Summary
Docs: Heading normalization
openspec/specs/cli-update/spec.md, openspec/specs/openspec-conventions/spec.md
Removed leading Requirement: prefix from several subsection headings; content otherwise unchanged.
Markdown & Change parsers
src/core/parsers/markdown-parser.ts, src/core/parsers/change-parser.ts
Added code-fence detection (buildCodeFenceMask/getFenceMarker/isClosingFence) and skip logic so headings inside fenced code blocks do not split sections; ensured trailing newlines.
Spec structure checker
src/core/parsers/spec-structure.ts
New module: findMainSpecStructureIssues and stripFencedCodeBlocksPreservingLines to detect `## (ADDED
Validation & apply integration
src/core/validation/validator.ts, src/core/specs-apply.ts
Validator now calls spec-structure checker and surfaces each issue as an ERROR; buildUpdatedSpec aborts when structural issues are found (throws with per-issue details).
Tests
test/core/parsers/markdown-parser.test.ts, test/core/validation.test.ts, test/core/archive.test.ts, test/specs/source-specs-normalization.test.ts
Added tests verifying fenced-code fence handling, delta-header detection (including case-insensitive variants), requirement-outside-section detection, and archive abort behavior; normalized tests to use new utilities.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant CLI as CLI (archive/validate)
participant Loader as Spec Loader
participant Parser as MarkdownParser / ChangeParser
participant Structure as SpecStructureChecker
participant Validator as Validator / SpecsApply

CLI->>Loader: read spec.md
Loader->>Parser: parse content (parseSpec / extractRequirements)
Parser->>Structure: (optional) strip fenced blocks / provide lines
Loader->>Structure: findMainSpecStructureIssues(content)
alt issues found
    Structure-->>Validator: issues[] (with line numbers/messages)
    Validator-->>CLI: throw / return errors (abort archive or validation)
else no issues
    Loader-->>Validator: proceed with requirements extraction and apply/archive flows
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibble fences, count each header line,

No stray H2 will hide a requirement of mine.
Inside code-block burrows, examples stay sweet,
Validator hops in place — malformed specs, retreat! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes significant out-of-scope changes beyond issue #954: documentation file edits removing 'Requirement:' prefixes and major refactoring of markdown parsing logic across multiple parser files that exceed the proposed 25-line minimal change. Remove unscoped changes (documentation edits, parser refactoring) and focus on the core validator delta-header detection rule and tests specified in issue #954.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: detect hidden requirements in main specs' accurately describes the main change in the PR, which adds detection for stray delta headers and hidden requirements outside the Requirements section.
Linked Issues check ✅ Passed The PR successfully implements all primary objectives from issue #954: detects delta H2 headers and hidden requirements, fails early with structural errors, strips fenced code blocks, and includes comprehensive test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch TabishB/validate-issue-954

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/core/parsers/markdown-parser.ts`:
- Around line 26-60: buildCodeFenceMask currently treats any line that starts
with a fence marker as a closing fence; change the closing-fence detection so
that only a line consisting solely of the fence marker (optionally surrounded by
whitespace) closes the active fence. In practice, keep getFenceMarker for
opening detection but when activeFence is set, validate the candidate closing
line with a stricter check (e.g., test the full line against a regex that
matches only whitespace + matching marker repeated >= activeFence.length +
optional trailing whitespace) before clearing activeFence; update
buildCodeFenceMask to use this stricter test so fenced blocks with trailing
content don't prematurely re-enable header parsing.

In `@src/core/parsers/spec-structure.ts`:
- Around line 1-4: Update the four header regexes to allow 0–3 leading spaces
(Markdown-valid indentation) and avoid matching 4+ space indented code blocks by
prefixing each pattern with a non-capturing optional-space group like (?:
{0,3}), e.g. change REQUIREMENTS_SECTION_HEADER, TOP_LEVEL_SECTION_HEADER,
DELTA_HEADER and REQUIREMENT_HEADER to start with /^(?: {0,3}).../ while keeping
the rest of each pattern (the header text, case-insensitivity and end anchors)
unchanged so headings are recognized when indented up to 3 spaces but not
mistaken for 4-space code fences.

In `@test/specs/source-specs-normalization.test.ts`:
- Around line 42-56: The test counts requirement headers from the raw markdown
(rawRequirementCount) which incorrectly includes headers inside fenced code
examples; update the test to compute rawRequirementCount from content with
fenced code blocks removed or ignored before applying REQUIREMENT_HEADER_PATTERN
(e.g., strip triple-backtick/tilde fences or use a helper to remove fenced
blocks), so that rawRequirementCount matches the visible headers the
MarkdownParser (parser.parseSpec / MarkdownParser) actually parses and keeps the
existing assertion comparing spec.requirements.length to rawRequirementCount.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 60737c7a-f139-4a13-ab4e-928abe08cca0

📥 Commits

Reviewing files that changed from the base of the PR and between cd5e493 and 179f100.

📒 Files selected for processing (11)
  • openspec/specs/cli-update/spec.md
  • openspec/specs/openspec-conventions/spec.md
  • src/core/parsers/change-parser.ts
  • src/core/parsers/markdown-parser.ts
  • src/core/parsers/spec-structure.ts
  • src/core/specs-apply.ts
  • src/core/validation/validator.ts
  • test/core/archive.test.ts
  • test/core/parsers/markdown-parser.test.ts
  • test/core/validation.test.ts
  • test/specs/source-specs-normalization.test.ts

Comment on lines +1 to +4
const REQUIREMENTS_SECTION_HEADER = /^##\s+Requirements\s*$/i;
const TOP_LEVEL_SECTION_HEADER = /^##\s+/;
const DELTA_HEADER = /^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements\s*$/i;
const REQUIREMENT_HEADER = /^###\s+Requirement:\s*(.+)\s*$/;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Support Markdown-valid indentation bounds for headings/fences.

Current patterns can miss valid indented headings (1–3 spaces) and can over-match 4-space-indented fence-like lines.

💡 Proposed fix
-const REQUIREMENTS_SECTION_HEADER = /^##\s+Requirements\s*$/i;
-const TOP_LEVEL_SECTION_HEADER = /^##\s+/;
-const DELTA_HEADER = /^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements\s*$/i;
-const REQUIREMENT_HEADER = /^###\s+Requirement:\s*(.+)\s*$/;
+const REQUIREMENTS_SECTION_HEADER = /^\s{0,3}##\s+Requirements\s*$/i;
+const TOP_LEVEL_SECTION_HEADER = /^\s{0,3}##\s+/;
+const DELTA_HEADER = /^\s{0,3}##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements\s*$/i;
+const REQUIREMENT_HEADER = /^\s{0,3}###\s+Requirement:\s*(.+)\s*$/;

-    const fenceMatch = line.match(/^\s*(`{3,}|~{3,})(.*)$/);
+    const fenceMatch = line.match(/^\s{0,3}(`{3,}|~{3,})(.*)$/);

Also applies to: 82-82

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/parsers/spec-structure.ts` around lines 1 - 4, Update the four
header regexes to allow 0–3 leading spaces (Markdown-valid indentation) and
avoid matching 4+ space indented code blocks by prefixing each pattern with a
non-capturing optional-space group like (?: {0,3}), e.g. change
REQUIREMENTS_SECTION_HEADER, TOP_LEVEL_SECTION_HEADER, DELTA_HEADER and
REQUIREMENT_HEADER to start with /^(?: {0,3}).../ while keeping the rest of each
pattern (the header text, case-insensitivity and end anchors) unchanged so
headings are recognized when indented up to 3 spaces but not mistaken for
4-space code fences.

Copy link
Copy Markdown
Collaborator

@alfred-openspec alfred-openspec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. This closes the hidden-requirement parsing gap cleanly, adds the right guardrails in validation/archive, and covers the fenced-code edge case with solid tests.

Copy link
Copy Markdown
Collaborator

@alfred-openspec alfred-openspec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approving per request.

@TabishB TabishB added this pull request to the merge queue Apr 12, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/core/parsers/spec-structure.ts (1)

1-4: ⚠️ Potential issue | 🟡 Minor

Support Markdown-valid indentation bounds for headings and fences.

Current patterns still require column-0 headings and allow 4+ space-indented fence-like lines. This can miss valid indented headings and over-match indented code blocks.

💡 Proposed fix
-const REQUIREMENTS_SECTION_HEADER = /^##\s+Requirements\s*$/i;
-const TOP_LEVEL_SECTION_HEADER = /^##\s+/;
-const DELTA_HEADER = /^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements\s*$/i;
-const REQUIREMENT_HEADER = /^###\s+Requirement:\s*(.+)\s*$/;
+const REQUIREMENTS_SECTION_HEADER = /^\s{0,3}##\s+Requirements\s*$/i;
+const TOP_LEVEL_SECTION_HEADER = /^\s{0,3}##\s+/;
+const DELTA_HEADER = /^\s{0,3}##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements\s*$/i;
+const REQUIREMENT_HEADER = /^\s{0,3}###\s+Requirement:\s*(.+)\s*$/;

-    const fenceMatch = line.match(/^\s*(`{3,}|~{3,})(.*)$/);
+    const fenceMatch = line.match(/^\s{0,3}(`{3,}|~{3,})(.*)$/);

-  const fenceMatch = line.match(/^\s*(`{3,}|~{3,})\s*$/);
+  const fenceMatch = line.match(/^\s{0,3}(`{3,}|~{3,})\s*$/);

Also applies to: 82-82, 111-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/parsers/spec-structure.ts` around lines 1 - 4, The heading and fence
regexes (REQUIREMENTS_SECTION_HEADER, TOP_LEVEL_SECTION_HEADER, DELTA_HEADER,
REQUIREMENT_HEADER) currently require column-0 anchors and allow overly-indented
fence-like lines; update each to accept up to three leading spaces per Markdown
spec by changing the start anchor to allow 0–3 spaces (e.g., use a ^ {0,3}
prefix) and also adjust any fence-detection regexes elsewhere to limit allowed
indentation to at most three spaces (not four or more) so indented code blocks
aren't over-matched while still recognizing valid indented headings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/core/parsers/spec-structure.ts`:
- Around line 1-4: The heading and fence regexes (REQUIREMENTS_SECTION_HEADER,
TOP_LEVEL_SECTION_HEADER, DELTA_HEADER, REQUIREMENT_HEADER) currently require
column-0 anchors and allow overly-indented fence-like lines; update each to
accept up to three leading spaces per Markdown spec by changing the start anchor
to allow 0–3 spaces (e.g., use a ^ {0,3} prefix) and also adjust any
fence-detection regexes elsewhere to limit allowed indentation to at most three
spaces (not four or more) so indented code blocks aren't over-matched while
still recognizing valid indented headings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7019ab7b-5bc2-4e3b-9e33-4ca7be8cbbde

📥 Commits

Reviewing files that changed from the base of the PR and between 179f100 and fc7680b.

📒 Files selected for processing (4)
  • src/core/parsers/markdown-parser.ts
  • src/core/parsers/spec-structure.ts
  • test/core/parsers/markdown-parser.test.ts
  • test/specs/source-specs-normalization.test.ts
✅ Files skipped from review due to trivial changes (1)
  • test/specs/source-specs-normalization.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/core/parsers/markdown-parser.test.ts
  • src/core/parsers/markdown-parser.ts

Merged via the queue into main with commit c8e2072 Apr 12, 2026
9 checks passed
@TabishB TabishB deleted the TabishB/validate-issue-954 branch April 12, 2026 14:00
stanleykao72 added a commit to stanleykao72/OpenSpec that referenced this pull request Apr 13, 2026
- Update skill template parity hashes for rebased content
- Move requirements outside ## Requirements section to fix
  findMainSpecStructureIssues validation (upstream Fission-AI#966)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(validator): detect stray delta headers that silently truncate main specs

2 participants