Skip to content

feat: add ##!=@ retrieve marker, disallow identifier on ##!=> - #323

Open
fzipi wants to merge 1 commit into
mainfrom
feat/disambiguate-assemble-output-marker
Open

feat: add ##!=@ retrieve marker, disallow identifier on ##!=>#323
fzipi wants to merge 1 commit into
mainfrom
feat/disambiguate-assemble-output-marker

Conversation

@fzipi

@fzipi fzipi commented Aug 6, 2026

Copy link
Copy Markdown
Member

what

  • Added a new ##!=@ <name> marker to the assemble processor that splices in a block previously stored with ##!=< <name>.
  • ##!=> (bare) keeps its existing meaning — a concatenation boundary — but now rejects an identifier instead of silently treating it as a splice. Passing one now fails with a clear error pointing at the replacement ('##!=>' no longer accepts an identifier; use '##!=@ <name>' to insert a stored block).
  • Removed AssembleInput/AssembleOutput string constants in regex/processors/assemble.go — dead code duplicating the patterns already in regex/definitions.go, with no callers.
  • Migrated the existing named-output test fixtures (regex/operators/assembler_test.go, cmd/regex/format/format_test.go) from ##!=> <name> to ##!=@ <name>, and added tests for the new marker and the new rejection error.

why

##!=> currently does two different jobs depending on whether it has an identifier: bare, it's a concatenation boundary; with a name, it splices in a stashed block. Same token, two different reader-intents — you have to scan for a trailing identifier to know which one you're looking at.

Real usage in coreruleset/coreruleset's regex-assembly/ (checked via a shallow clone): 348 total ##!=> occurrences, 265 (76%) bare, 83 (24%) named across 25 files. The bare/boundary form dominates, so it keeps the existing token; the splice case gets its own unambiguous one.

breaking change / migration needed

This is a breaking DSL change. coreruleset/coreruleset currently has 83 lines across 25 regex-assembly/*.ra files using the old ##!=> <name> form (e.g. 930100.ra, 941160.ra, 942420.ra, a few include/*.ra). Those need a mechanical migration (^(\s*)##!=>\s+(\S+)$$1##!=@ $2) before or alongside picking up this toolchain version — otherwise regex generate/compare/update will fail on those files with the new rejection error. Happy to open that migration PR once this lands.

refs

ai disclosure

  • tools used: Claude Code
  • assisted with: implementing the parser/processor change, migrating existing test fixtures to the new syntax, writing the new tests
  • review performed: ran the full test suite (go test ./..., excluding the pre-existing unrelated local GPG-signing failures in chore/release), go vet, and manually exercised both the new ##!=@ retrieval and the new rejection error against scratch .ra fixtures with the built binary before and after the change

Summary by CodeRabbit

  • New Features

    • Added ##!=@ <name> syntax for inserting previously stored assemble blocks.
    • Added clearer handling and reporting for invalid block references.
  • Bug Fixes

    • Prevented output markers from being used with identifiers.
    • Updated formatting behavior so ##!=> correctly represents an output boundary, while named block insertion uses the new retrieval syntax.

##!=> currently does two different jobs depending on whether it has
an identifier: bare, it's a concatenation boundary; with a name, it
splices in a block previously stashed with ##!=<. Same token, two
different reader-intents.

Give the splice case its own marker, ##!=@ <name>, and make ##!=>
reject an identifier going forward, since a boundary marker taking
one was the ambiguous case. Also drop the AssembleInput/AssembleOutput
string constants in assemble.go, which duplicated regex/definitions.go's
patterns and had no callers.

Resolves: #20
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change separates stored-block retrieval from assemble output boundaries. It adds retrieval matching, updates ProcessLine, rejects identifiers on output markers, and updates formatter and assembler tests to use ##!=@.

Changes

Assemble marker syntax

Layer / File(s) Summary
Marker contracts
regex/definitions.go
Documents output-marker boundary behavior and adds AssembleRetrieveRegex for named block retrieval.
Processor routing
regex/processors/assemble.go
Routes ##!=@ <name> to stored-block retrieval. Rejects identifiers on ##!=> and reports the replacement syntax.
Syntax migration and validation
regex/operators/assembler_test.go, cmd/regex/format/format_test.go
Updates stored-block references and formatter fixtures to ##!=@. Adds coverage for invalid output-marker identifiers.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the new retrieval marker and the rejection of identifiers on the existing output marker.
Description check ✅ Passed The description covers what changed, why it changed, the breaking migration, references, and test validation.
Linked Issues check ✅ Passed The PR addresses issue #20 by separating retrieval from concatenation-boundary behavior with distinct markers.
Out of Scope Changes check ✅ Passed The code, tests, fixture updates, and removal of unused constants are related to the marker disambiguation objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@regex/definitions.go`:
- Line 64: Align AssembleRetrieveRegex with AssembleInputRegex so identifiers
containing trailing text, such as spaces, remain retrievable after store
preserves them; either restrict input identifiers to a single non-whitespace
token or make retrieval capture and normalize the same grammar, and ensure the
migration error in assemble processing does not recommend an unusable form.

In `@regex/processors/assemble.go`:
- Around line 39-47: Update the retrieve-marker handling before the assembler’s
later content branches, near AssembleRetrieveRegex processing, to explicitly
detect lines beginning with the retrieve directive but not matching the complete
expected retrieve syntax. Reject malformed forms such as ##!=@ and ##!=@ name
extra with the existing validation/error path, while preserving successful
identifier extraction and append behavior for valid markers.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 91e17d4a-24b9-4246-8f00-2aad16c81632

📥 Commits

Reviewing files that changed from the base of the PR and between 3a2cea1 and e3e58a5.

📒 Files selected for processing (4)
  • cmd/regex/format/format_test.go
  • regex/definitions.go
  • regex/operators/assembler_test.go
  • regex/processors/assemble.go

Comment thread regex/definitions.go
// AssembleRetrieveRegex matches a retrieve line of the assemble processor
// (##!=@ <name>), splicing in a block previously stored with ##!=< <name>.
// The name is captured in group 1.
var AssembleRetrieveRegex = regexp.MustCompile(`^\s*##!=@\s*(\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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep store and retrieve identifiers on the same grammar.

AssembleInputRegex accepts all trailing text, and store saves it without trimming. AssembleRetrieveRegex accepts only one non-whitespace token. Therefore, ##!=< my block stores my block, but ##!=@ my block cannot retrieve it. The migration error in regex/processors/assemble.go can also direct users to this unusable form. Either reject whitespace in input identifiers or capture and normalize the same grammar for retrieval.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@regex/definitions.go` at line 64, Align AssembleRetrieveRegex with
AssembleInputRegex so identifiers containing trailing text, such as spaces,
remain retrievable after store preserves them; either restrict input identifiers
to a single non-whitespace token or make retrieval capture and normalize the
same grammar, and ensure the migration error in assemble processing does not
recommend an unusable form.

Comment on lines +39 to +47
match = regex.AssembleRetrieveRegex.FindStringSubmatch(line)
if len(match) > 0 {
identifier := match[1]
if err := a.append(identifier); err != nil {
var message string
if identifier != "" {
message = fmt.Sprintf("Failed to append output with name %s", identifier)
} else {
message = "Failed to append output of previous block"
}
logger.Error().Err(err).Msg(message)
logger.Error().Err(err).Msgf("Failed to retrieve stored output with name %s", identifier)
return err
}
return nil
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 5 'AssembleRetrieveRegex|ValidateAll|ProcessLine|##!=@' --glob '*.go' . || true

Repository: coreruleset/crs-toolchain

Length of output: 32906


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,120p' regex/validation/regex_validation.go
printf '\n--- assemble.go lines 160-210 ---\n'
sed -n '160,210p' regex/processors/assemble.go
printf '\n--- assemble_test.go relevant invalid marker tests ---\n'
python3 - <<'PY'
from pathlib import Path
p = Path('regex/processors/assemble_test.go')
text = p.read_text()
for needle in ['##!=@ unknown', '##!=@\n', '##!=@ name extra', '##!=@', 'Invalid assemble output', 'Invalid assemble retrieve']:
    print(f'{needle!r}: {text.find(needle)}')
PY
printf '\n--- assembler parser files ---\n'
fd -a '.go$' regex/parser | sed 's#^\./##'
rg -n 'NewParser|Parse\(|type .*Parser|Start|Line' regex/parser --glob '*.go'

Repository: coreruleset/crs-toolchain

Length of output: 13195


Reject malformed retrieve markers before assembler content.

validation.ValidateAll() only checks character classes and Unicode code points and does not see malformed ##!=@ directives here. Since AssembleRetrieveRegex requires an identifier, lines such as ##!=@ or ##!=@ name extra currently bypass retrieval and are handled by later assemble branches. Add an explicit validation/guard for malformed retrieve directives.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@regex/processors/assemble.go` around lines 39 - 47, Update the
retrieve-marker handling before the assembler’s later content branches, near
AssembleRetrieveRegex processing, to explicitly detect lines beginning with the
retrieve directive but not matching the complete expected retrieve syntax.
Reject malformed forms such as ##!=@ and ##!=@ name extra with the existing
validation/error path, while preserving successful identifier extraction and
append behavior for valid markers.

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.

Think about adding more operators to make meaning of operations more specific

1 participant