Skip to content

Commit fe3a98b

Browse files
committed
fix(placeholders): extract apmInputPrefix constant, consistent double-guard, and complete docs
- Extract apmInputPrefix = "${input:" constant to eliminate literal triplication at lines 84/112/149 and avoid silent skew if the prefix ever changes - Apply consistent double-guard (Contains + MatchString) in MaskBodyTokens and stripBodyTokens to match ContainsBodyToken, avoiding wasted regex work on malformed prefixes like "${input:}" and closing the maintenance trap - Remove WHAT comments on apmInputRe (CLAUDE.md: comment only when WHY is non-obvious) - Add "malformed prefix unchanged" test case to drive the Contains guard red/green - Add apm-input-token to MDS003 and MDS027 rows in opt-in rules table (both rules call ContainsBodyToken which handles APMInputToken, so the table omission was misleading) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Cua8NLiPy6fhj58LvUWf1
1 parent b3647e8 commit fe3a98b

3 files changed

Lines changed: 9 additions & 7 deletions

File tree

docs/background/concepts/placeholder-grammar.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,10 @@ from `internal/placeholders`:
7474

7575
| Rule ID | Rule name | Useful tokens |
7676
| ------- | -------------------------------- | ------------------------------------------------------------------------- |
77-
| MDS003 | `heading-increment` | `heading-question`, `placeholder-section`, `var-token` |
77+
| MDS003 | `heading-increment` | `heading-question`, `placeholder-section`, `var-token`, `apm-input-token` |
7878
| MDS004 | `first-line-heading` | `heading-question`, `var-token`, `placeholder-section`, `apm-input-token` |
7979
| MDS018 | `no-emphasis-as-heading` | `var-token`, `heading-question`, `placeholder-section`, `apm-input-token` |
8080
| MDS020 | `required-structure` | `cue-frontmatter` |
8181
| MDS023 | `paragraph-readability` | `var-token`, `heading-question`, `placeholder-section`, `apm-input-token` |
8282
| MDS024 | `paragraph-structure` | `var-token`, `heading-question`, `placeholder-section`, `apm-input-token` |
83-
| MDS027 | `cross-file-reference-integrity` | `var-token`, `heading-question`, `placeholder-section` |
83+
| MDS027 | `cross-file-reference-integrity` | `var-token`, `heading-question`, `placeholder-section`, `apm-input-token` |

internal/placeholders/placeholders.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import (
2323
"github.com/jeduden/mdsmith/internal/fieldinterp"
2424
)
2525

26-
// apmInputRe matches APM ${input:NAME} prompt parameters.
27-
// NAME must start with a letter and contain only letters, digits, underscores, and hyphens.
26+
// apmInputPrefix is the byte sequence that uniquely opens an APM input parameter.
27+
const apmInputPrefix = "${input:"
28+
2829
var apmInputRe = regexp.MustCompile(`\$\{input:[A-Za-z][\w-]{0,63}\}`)
2930

3031
// Named placeholder tokens.
@@ -81,7 +82,7 @@ func ContainsBodyToken(text string, tokens []string) bool {
8182
return true
8283
}
8384
case APMInputToken:
84-
if strings.Contains(text, "${input:") && apmInputRe.MatchString(text) {
85+
if strings.Contains(text, apmInputPrefix) && apmInputRe.MatchString(text) {
8586
return true
8687
}
8788
}
@@ -109,7 +110,7 @@ func MaskBodyTokens(text string, tokens []string) string {
109110
return neutralText[PlaceholderSection]
110111
}
111112
case APMInputToken:
112-
if strings.Contains(text, "${input:") {
113+
if strings.Contains(text, apmInputPrefix) && apmInputRe.MatchString(text) {
113114
text = apmInputRe.ReplaceAllLiteralString(text, neutralText[APMInputToken])
114115
}
115116
}
@@ -146,7 +147,7 @@ func stripBodyTokens(text string, tokens []string) string {
146147
return ""
147148
}
148149
case APMInputToken:
149-
if strings.Contains(text, "${input:") {
150+
if strings.Contains(text, apmInputPrefix) && apmInputRe.MatchString(text) {
150151
text = apmInputRe.ReplaceAllLiteralString(text, "")
151152
}
152153
}

internal/placeholders/placeholders_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ func TestAPMInputToken_MaskBodyTokens(t *testing.T) {
335335
{"single token replaced", "${input:pr_url}", "word"},
336336
{"token in sentence", "review ${input:pr_url} now", "review word now"},
337337
{"two tokens replaced", "${input:a} and ${input:b}", "word and word"},
338+
{"malformed prefix unchanged", "${input:}", "${input:}"},
338339
{"non-matching unchanged", "${output:x}", "${output:x}"},
339340
{"plain text unchanged", "some prose", "some prose"},
340341
}

0 commit comments

Comments
 (0)