Skip to content

fix: anchor catalog type/version regex to export block#1121

Merged
stack72 merged 1 commit intomainfrom
fix/anchor-catalog-type-regex
Apr 6, 2026
Merged

fix: anchor catalog type/version regex to export block#1121
stack72 merged 1 commit intomainfrom
fix/anchor-catalog-type-regex

Conversation

@stack72
Copy link
Copy Markdown
Contributor

@stack72 stack72 commented Apr 6, 2026

Summary

  • Anchor the type and version extraction regexes in populateCatalogFromDir to the export const model/extension = { block, preventing them from matching unrelated type:/version: properties earlier in the file (e.g., inside helper function calls or schemas)
  • Add regression test with a decoy type: property before the export block

Fixes #1110 (code fix only — skill doc changes are separate)

Test Plan

  • New unit test: decoy type:/version: before export block → catalog stores the real type, not the decoy
  • All 4185 existing tests pass
  • deno check, deno lint, deno fmt clean

🤖 Generated with Claude Code

The type and version regexes in populateCatalogFromDir matched the first
`type:` / `version:` property anywhere in the source file. When a model
file had unrelated `type:` properties before the export block (e.g.,
inside helper function calls), the catalog was poisoned with the wrong
type. Anchor both regexes on `export const (model|extension) = {` so
they only extract from the actual model definition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review

Clean, well-scoped fix. The regex anchoring approach correctly solves the problem of decoy type:/version: properties matching before the actual export block.

Blocking Issues

None.

Suggestions

  1. Regex edge case with nested type: inside export block — The [\s\S]*? (non-greedy) will match the first type: after export const model = {. If a model ever has a nested object with a type: property before the top-level type: (e.g., export const model = { schema: { type: "object" }, type: "@test/real" }), it would extract the wrong value. This is unlikely in practice and already an improvement over the previous regex (which matched anywhere in the file), so not blocking — just worth noting.

  2. Large deno.lock diff — The lock file adds ~5k lines of new entries (promptfoo and transitive deps). This appears to be from the lock file catching up with the existing deno.json workspace declaration rather than a new dependency, so it's fine — just visually noisy.

Overall: solid fix with a good regression test. LGTM.

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Adversarial Review

Critical / High

None.

Medium

  1. src/domain/models/user_model_loader.ts:836,844[\s\S]*? can cross the export block boundary. The non-greedy [\s\S]*? between the export const anchor and type:/version: will match across the closing } of the export block if type: or version: is absent from the export but present later in the file. For type:, this is moot — if type: is missing from the export, you'd get a bogus type, but the continue on line 838 prevents that from persisting (the regex just wouldn't match the expected format). For version:, if the export block lacks a version: property but one exists elsewhere in the file (e.g., in a comment or unrelated object), it would be captured. However, the old regex had the exact same cross-boundary issue, and the comment in the old code acknowledged this is best-effort with correction on subsequent runs. No regression from the old behavior.

  2. src/domain/models/user_model_loader.ts:824-826 vs :836 — gate check matches model: (type annotation) but extraction regex does not. The gate on line 824 uses [=:] which matches export const model: SomeType = {, but the extraction regex on line 836 uses \s*=\s*\{ which would NOT match that pattern (it'd miss the : SomeType between the name and =). If anyone wrote export const model: ModelDef = { ... }, the gate would pass but extraction would fail, silently skipping the file. Verified that no files in the repo currently use this pattern, so this is theoretical only.

Low

  1. src/domain/models/user_model_loader.ts:836 — comment inside export block could be matched. If someone wrote export const model = { // type: "commented-out" \n type: "@real/type" }, the non-greedy match would capture "commented-out". Extremely unlikely in practice and identical behavior to the old regex.

  2. deno.lock — includes unrelated promptfoo dependency addition. The lock file diff is ~5700 lines for what appears to be a lock file refresh, not related to the regex fix. Harmless but noisy.

Verdict

PASS — The regex anchoring fix is correct and directly addresses the reported bug. The [\s\S]*? non-greedy scan between the anchor and type: will correctly skip decoy properties before the export block. The edge cases identified are either theoretical (no files use type-annotated exports) or identical to pre-existing behavior. Test coverage is appropriate.

@stack72 stack72 merged commit 356fab3 into main Apr 6, 2026
10 checks passed
@stack72 stack72 deleted the fix/anchor-catalog-type-regex branch April 6, 2026 18:19
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.

issue-lifecycle skill fails in worktrees: missing init and dev extension model resolution

1 participant