Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions .claude/skills/command-api-mapping/reference/client-quirks.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,20 @@ export default {
```

Translation rules:
- **Method name = camelCase of the filename**, not `parseCommand`. `ARGET.ts` → `arGet`,
`ARGETRANGE.ts` → `arGetRange`, `ARSET.ts` → `arSet`. (node-redis generates the public
method from the filename.)
- **Method name = the name exported in `packages/client/lib/commands/index.ts`**, not
`parseCommand`. It is usually the camelCase of the filename (`ARGET.ts` → `arGet`,
`ARGETRANGE.ts` → `arGetRange`), but check `index.ts` when the word split isn't obvious:
`HIMPORT_PREPARE.ts` is exported as `hImportPrepare`, not `himportPrepare` (DOC-7172).
- **Drop the first `parser: CommandParser` param** — it's internal.
- **Keep the remaining params** with their types (`key: RedisArgument`, `index: number | string`).
These RESP types (`RedisArgument`, `RedisVariadicArgument`) are what users effectively pass;
keep them as-is.
- **Return type comes from `transformReply`**, not a return annotation. The type after
`() =>` is the reply (`NumberReply`, `BlobStringReply | NullReply`, …). Render the signature
return as `Promise<...>` of that reply where it reads naturally.
- **No doc comments exist** — all `description` fields are `""`.
- **The command files have no doc comments.** Some newer commands carry JSDoc (`@param`) on
their export in `commands/index.ts` (HIMPORT does, as of `redis@6.2.0`); use it for param
descriptions when present, otherwise `""`.
- **House-style format (matches existing entries — important):** the `signature` string carries
**no return-type suffix** (write `arSet(key: RedisArgument, index: number | string, value: RedisVariadicArgument)`,
not `...): Promise<NumberReply>`), and `returns.type` is the **bare reply type without the
Expand Down Expand Up @@ -108,7 +111,8 @@ Translation rules:
`returns.type` differs (sync `V` / async `RedisFuture<V>` / reactive `Mono<V>`).

## nredisstack_sync / nredisstack_async (C#)
- PascalCase methods; async variants end in `Async` and return `Task<T>`.
- PascalCase methods; async variants end in `Async` and return `Task<T>`, or plain `Task`
when the sync method returns `void` (`HashImport` → `HashImportAsync`).
- **Core commands aren't in NRedisStack** — they're in StackExchange.Redis (`IDatabase.cs` /
`IDatabaseAsync.cs`). Module commands (JSON/Search/TimeSeries) are in NRedisStack.
- XML doc comments (`<summary>`, `<param>`, `<returns>`) → descriptions; strip `<see cref=…>`.
Expand All @@ -126,8 +130,21 @@ Translation rules:
return type (`int`, `array`, `string|null`, `mixed`).
- Predis folds option flags into params rather than separate variants (e.g. `argrep` has a
`bool $withValues` param — no separate WITHVALUES method).
- Container commands (XGROUP/XINFO) expose subcommands via `@method` entries in
`src/Command/Container/`. PHPDoc descriptions when present.
- Container commands (XGROUP/XINFO/HIMPORT) expose subcommands via `@method` entries in
`src/Command/Container/<CMD>.php`, not in `ClientInterface.php`, which lists them only as a
property (`@property HIMPORT $himport`). Map the bare subcommand method (`prepare(...)`,
`set(...)`), as `XGROUP CREATE.json` does, with the return type from the container's
`@method` docblock. PHPDoc descriptions when present.

## ioredis (TypeScript)
- Signatures come from the generated `lib/utils/RedisCommander.ts`.
- Container commands are one generic method with a literal-typed `subcommand` first param.
Follow `XGROUP CREATE.json`: `xgroup(subcommand: "CREATE", ..., callback?: ...)`, with
`subcommand` and `callback?` listed as params.
- For `...args: [subcommand: 'X', ...]` tuple overloads (HIMPORT PREPARE/SET), list each
tuple element as its own param. Existing variadic rows such as `HSET.json` have garbled
params like `"[key"`; don't copy that. Use the real `Result<...>` return type, as
`HEXPIRE.json` does, rather than `"Any"`.

## redis_rs_sync / redis_rs_async (Rust)
- snake_case methods inside the `implement_commands!` macro. Drop `&self`/`&mut self`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ here yet; use the "finding new command files" recipe below.
| `nredisstack_sync` / `nredisstack_async` | github.com/redis/NRedisStack | `master` | C# |
| `php` | github.com/predis/predis | `main` | PHP |
| `redis_rs_sync` / `redis_rs_async` | github.com/redis-rs/redis-rs | `main` | Rust |
| `redis_rb` | github.com/redis/redis-rb | `master` | Ruby |

(Branch names drift; if a fetch 404s, try `main`/`master`/`develop`.)

Expand Down Expand Up @@ -47,6 +48,9 @@ here yet; use the "finding new command files" recipe below.
- **php (predis)** — `src/ClientInterface.php` for most signatures; container commands
(XGROUP, XINFO) define subcommands in `@method` docblocks under `src/Command/Container/`.
- **redis-rs** — `redis/src/commands/mod.rs` (the `implement_commands!` macro block).
- **redis_rb** — per-type modules under `lib/redis/commands/` (`hashes.rb`, `strings.rb`, …).
Some methods are overridden in `lib/redis.rb` (for example the HIMPORT fieldset registry);
map the public method name from `commands/`.

## Finding new command files (the AR* lesson)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ redis_py go-redis jedis
node_redis ioredis lettuce_sync
lettuce_async lettuce_reactive nredisstack_sync
nredisstack_async php redis_rs_sync
redis_rs_async redisvl
redis_rs_async redisvl redis_rb
```

Note the inconsistency: **`go-redis` uses a hyphen**, everything else uses underscores.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ Mono<Void> chain = reactiveCommands.hincrby("stats", "count", 1)
chain.block();
```

**Null replies are empty Monos.** A command that replies with nil completes without
emitting anything, so every later `flatMap`/`doOnNext` in the chain silently never runs —
and the test still passes. This covers a conditional `SET` that doesn't apply (`NX`, `XX`,
`IFEQ`) and a `GET` of a missing key. Where the step needs to show the nil result, end that
command with `.hasElement()` (emits `false`) or `.defaultIfEmpty(...)`:

```java
.flatMap(v -> reactiveCommands.set("bike:1", "Zeus",
SetArgs.Builder.compareCondition(CompareCondition.valueEq("Deimos"))).hasElement())
.doOnNext(res -> System.out.println(res)) // >>> false
```

### 4. Parallel Execution
```java
Mono.when(mono1, mono2, mono3).block();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ echo json_encode($all) . PHP_EOL;
// >>> {"field1":"value1","field2":"value2"}
```

`echo` prints an empty line for a `null` reply, so a `// >>> (null)` comment after it is
wrong. Use `var_export`:

```php
echo var_export($res, true) . PHP_EOL; // >>> NULL
```

### 5. Hash Operations
```php
// Single field — returns the number of new fields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,17 @@ r = Redis.new
```ruby
res = r.hgetall('myhash')
puts res.inspect
# >>> {"field1"=>"value1", "field2"=>"value2"}
# >>> {"field1" => "value1", "field2" => "value2"}
```

Note the `=>` hash-rocket form — that is what `inspect` actually emits. Output comments are
published verbatim, so writing the modern `{field1: "value1"}` there would show readers
something Ruby never printed.

Ruby 3.4 and later put spaces around the `=>` (`{"field1" => "value1"}`); earlier versions
print `{"field1"=>"value1"}`. Older files in `local_examples/` use the unspaced form. Write
the form your run actually printed, and don't rewrite existing files to match.

### 4. Replies are strings, except counters

Values read back from Redis are strings even when written as integers. The reply of an
Expand Down
15 changes: 15 additions & 0 deletions .claude/skills/tce-examples/reference/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@ version per client requires running both Java toolchains against the search sets
Java-heavy command sets. Until that's done, **a Java example that passes in one mode may
fail in the other**, and that's information rather than a bug.

## Known gap: client pins older than new commands

The portable pins lag the newest commands. As of DOC-7172 (HIMPORT, Redis 8.10), these
portable pins predated the first release with the command: jedis 7.5.3 (needs 8.0.0),
lettuce-core 6.5.5 in `pom-lettuce-sync.xml` (needs 7.7.0; the async and reactive poms are
already on 7.7.0), StackExchange.Redis 3.0.0 in `dotnet/harness.csproj` (needs 3.0.25),
the cached `npm i redis` 6.1.0 (needs 6.2.0) and ioredis 5.11.1 (needs 6.0.0) in `work/`,
and go-redis v9.18.0 in `fidelity/go-redis.mod` (needs 9.22.0, which also needs Go 1.24).

A run against those pins fails with "method not found" or "unknown command", which looks
like an example bug but is a pin gap. Until the pins are raised (a separate change, because
raising them can break other sets), test such a set outside `run.sh`. Give each client a
scratch project pinned to its first supporting release, with its own throwaway
`redis:<version>` container on a spare port, and change the port only in the scratch copy.

## Codex evaluation (Phase 4)

An independent reviewer with fresh context. It catches what the harness structurally cannot:
Expand Down
Loading