vercel-labs / vercel-labs/konsistent

Negation entry with a constrained placeholder silently disables the entire convention

Open
#80 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
176
Forks
6
Avg merge
20h 26m
Merged PRs (30d)
11

Description

Bug: a paths negation with a constrained placeholder silently disables the entire convention

Versions: reproduced on konsistent@1.0.0-beta.3 and 1.0.0-beta.4 (Node 26, macOS).

Repro

repro/
  package.json                 # {"name":"repro","private":true}
  konsistent.json
  packages/good/{package.json,tsconfig.json,src/index.ts,test/}
  packages/temporal/{package.json,tsconfig.json,src/activities.ts,test/}   # NOTE: no src/index.ts
  packages/excluded-pkg/package.json
{
  "version": "v1",
  "conventions": [
    {
      "name": "shell",
      "paths": ["packages/{packageName}", "!packages/{packageName:matches(^(excluded-pkg)$)}"],
      "must": { "haveType": "directory", "haveFiles": ["src/index.ts"] }
    }
  ]
}

konsistent check --format=json[], exit 0. The convention matches nothing: packages/temporal (missing src/index.ts) is not flagged, and neither is packages/excluded-pkg.

Change only the negation to a literal:

"paths": ["packages/{packageName}", "!packages/excluded-pkg"]

→ correctly reports packages/temporal Missing required file: src/index.ts (and skips excluded-pkg), exit 1.

Control without any negation: both temporal and excluded-pkg are flagged, as expected.

Expected

A negation entry with an inline constraint (!packages/{p:matches(^(a|b)$)}) should exclude only the paths whose captured value satisfies the constraint — per docs/reference/path-patterns.md#negation + constraints.md, constraints "skip" non-matching captures, so the negation should apply to a/b only.

Actual

The presence of a constrained-placeholder negation entry appears to negate every path the positive pattern matched (or fails to match and swallows the set) — the convention becomes a silent no-op. konsistent validate reports the config as valid, so nothing surfaces the dead rule.

Why this is nasty

Our production config used exactly this shape ("!packages/{packageName:matches(^(blob|extract|monitor)$)}") and our main structural convention was a no-op for weeks while check printed green. Suggest: either fix constraint evaluation in negation entries, or reject constrained placeholders in negations at validate time.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with the provided konsistent.json and run konsistent check --format=json, then compare constrained and literal negation behavior. Read docs/reference/path-patterns.md#negation and constraints.md for the intended semantics. Done means constrained negations exclude only matching captures, nonmatching paths are still checked, and validation does not accept a silently dead convention.

Written by the indexing model from the issue text.

Assessment

Tech stack
nodejs, typescript
Domain
cli, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.