Skip to content

docs(imap/commands): revert incorrect SEARCH criteria claim#84

Merged
ssilvius merged 1 commit intomainfrom
fix/revert-search-criteria-doc
Apr 11, 2026
Merged

docs(imap/commands): revert incorrect SEARCH criteria claim#84
ssilvius merged 1 commit intomainfrom
fix/revert-search-criteria-doc

Conversation

@ssilvius
Copy link
Copy Markdown
Contributor

Summary

Self-correction to a mistake in PR #81 (already merged). The SEARCH command section now falsely claims that flag-based criteria (`ANSWERED`, `DELETED`, `DRAFT`, `FLAGGED`, `SEEN`, `UNSEEN`) are unsupported. They are fully supported by the parser via a `FLAG_CRITERIA` lookup table I missed on my first audit.

How the mistake happened

I grepped for `case "` in `packages/imap/src/protocol/parser.ts` and counted 17 switch statement entries inside `parseCriterion`. I concluded the flag-based criteria were unsupported because they were not in the switch. I missed this at the top of the same function:

```typescript
const FLAG_CRITERIA: Record<string, { flag: string; negated: boolean }> = {
ANSWERED: { flag: "\\Answered", negated: false },
DELETED: { flag: "\\Deleted", negated: false },
DRAFT: { flag: "\\Draft", negated: false },
FLAGGED: { flag: "\\Flagged", negated: false },
OLD: { flag: "\\Recent", negated: true },
RECENT: { flag: "\\Recent", negated: false },
SEEN: { flag: "\\Seen", negated: false },
UNANSWERED: { flag: "\\Answered", negated: true },
UNDELETED: { flag: "\\Deleted", negated: true },
UNDRAFT: { flag: "\\Draft", negated: true },
UNFLAGGED: { flag: "\\Flagged", negated: true },
UNSEEN: { flag: "\\Seen", negated: true },
};

function parseCriterion(): SearchCriterion {
const token = next().toUpperCase();

const flagCriterion = FLAG_CRITERIA[token];
if (flagCriterion) {
return { type: "flag", ...flagCriterion }; // dispatches BEFORE the switch
}

switch (token) { ... }
}
```

The lookup table short-circuits flag tokens before the switch statement runs. All 13 flag criteria (12 flags + RECENT/OLD) are handled. Parser tests at `packages/imap/tests/protocol/parser.test.ts:326-339` explicitly cover `ANSWERED` and `DELETED` / `UNDELETED`.

The fix

  • Restored the correct "Supported criteria" list with all 13 flag variants
  • Added explicit reference to `FLAG_CRITERIA` + the parser tests so a future reader can audit without repeating my mistake
  • Narrowed the "Not yet supported" section to the actual remaining gaps: `KEYWORD`, `UNKEYWORD`, `HEADER`, `SENTBEFORE` / `SENTON` / `SENTSINCE`

Related

Lesson

For the memory: when auditing a parser, read the entire function, not just the switch statement. Lookup tables can dispatch tokens before any `case` is evaluated. My existing memory rule "Before recommending from memory, verify the file still exists" needs a corollary: "and read the whole function, not just the part that matches your grep."

Verification

  • Verified `FLAG_CRITERIA` contents directly in `packages/imap/src/protocol/parser.ts`
  • Verified test coverage at `packages/imap/tests/protocol/parser.test.ts:326` and line 332
  • `pnpm test`: 614 passing (unchanged)
  • `pnpm lint`: 0 warnings, 0 errors
  • `pnpm format:check`: clean

… has full flag support

Self-correction. On PR #81 I added a "Not yet supported" section
listing ANSWERED, DELETED, DRAFT, FLAGGED, SEEN, UNSEEN as missing,
based on an incomplete read of parser.ts. I grepped for `case "`
and saw 17 switch statement entries, concluding the flag criteria
were unsupported. I missed the FLAG_CRITERIA lookup table earlier
in the same function that dispatches flag tokens BEFORE the switch:

  const FLAG_CRITERIA: Record<string, { flag: string; negated: boolean }> = {
    ANSWERED, DELETED, DRAFT, FLAGGED, SEEN, RECENT,
    UNANSWERED, UNDELETED, UNDRAFT, UNFLAGGED, UNSEEN, OLD
  };

All 12 flag criteria + `NEW` + `ALL` + the header / date / size /
text / UID / NOT / OR criteria ARE supported. The parser tests at
packages/imap/tests/protocol/parser.test.ts cover ANSWERED and
DELETED / UNDELETED explicitly.

The original doc in vault had the full list correct. PR #81's
"fix" removed correct information and replaced it with an
incorrect gap claim. This commit restores the correct list and
adds explicit reference to the FLAG_CRITERIA table + the parser
test coverage so a future reader can audit the claim against
source without making the same mistake.

Also closing issue #82 (the "missing SEARCH criteria" gap I
filed based on the same mistake) as invalid.

The actual remaining gaps are narrower: KEYWORD, UNKEYWORD,
HEADER, SENTBEFORE / SENTON / SENTSINCE. Those are accurately
flagged as "Not yet supported" in this revision.

## How this slipped through

Per my own memory rule "Before recommending from memory, verify
the file still exists" -- I verified against source but only
partially. Next time: read the entire parser function, not just
the switch statement. Lookup tables can live above the switch and
short-circuit before any case hits.

## Verification

- Verified FLAG_CRITERIA contents against packages/imap/src/protocol/parser.ts
- Verified parser tests at packages/imap/tests/protocol/parser.test.ts:326-339
- pnpm test: 614 passing (unchanged)
- pnpm lint: 0 warnings, 0 errors
- pnpm format:check: clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ssilvius ssilvius merged commit 4362b38 into main Apr 11, 2026
1 check passed
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.

1 participant