2026-09-18-real-tcp-wire-codec.md 6.8 KB

Real TCP Wire Codec Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace the speculative 24-byte/XXTEA TCP transport with the verified 36-byte header encoded as a 48-byte wire header.

Architecture: A pure encode6 module embeds the verified 256-byte client substitution table. framing packs nine signed little-endian words, encodes only that 36-byte header, then appends the plaintext body. TcpClient reads the 48-byte wire boundary and owns a monotonically increasing diagnostic sequence.

Tech Stack: Python 3.12+, asyncio, struct, Pydantic, pytest, pytest-asyncio.

Spec: docs/superpowers/specs/2026-09-18-cq2-real-tcp-wire-codec-design.md

Global Constraints

  • The wire shape is encode6(header36) + body; do not apply XXTEA to TCP frames.
  • The real header is <9i: pre, msg_id, recog, param1, param2, param3, codec, body_len, sequence.
  • Export PLAIN_HEADER_SIZE = 36, WIRE_HEADER_SIZE = 48, and compatibility HEADER_SIZE = WIRE_HEADER_SIZE.
  • codec != 0 raises ValueError('unsupported body codec: N'); it must never return compressed bytes as a body.
  • initial_sequence defaults to 0; that value is not evidence of a production login initial sequence.
  • Do not add live game login, credentials, captures, inferred CS2001 payloads, or body decompression.

Task 1: Establish a runnable baseline

Files: no repository source changes.

  • Install the local test runner:
../../.venv/bin/python -m pip install 'pytest>=9.1.1' 'pytest-asyncio>=1.4.0' 'pydantic>=2.13.5'
  • Run and record the pre-change baseline:
PYTHONPATH=cq2_sdk/src ../../.venv/bin/python -m pytest cq2_sdk/tests/unit/test_framing.py cq2_sdk/tests/unit/test_tcp_client.py -q
  • Confirm .venv and cache files are still ignored with git status --short --ignored.

Task 2: Implement the verified table-driven 6-bit codec

Files:

  • Create: cq2_sdk/src/cq2_sdk/transport/encode6.py
  • Modify: cq2_sdk/tests/unit/test_framing.py

Interfaces: encode6(src: bytes) -> bytes; decode6(data: bytes, *, expected_size: int | None = None) -> bytes.

  • Write tests first using literal, hand-verified heartbeat values:
plain = bytes.fromhex(
    '0000000060ea0000b02daf6a000000000000000095000000000000000000000070000000'
)
wire = bytes.fromhex(
    '5d5452415d4c67445d545078495e55525d5452415d545241'
    '5d54507a5d5452415d5452415d5452415d54527b5d545241'
)
assert encode6(plain) == wire
assert decode6(wire, expected_size=36) == plain
with pytest.raises(ValueError, match='outside 6-bit wire range'):
    decode6(b'<~')
  • Run pytest test_framing.py -k 'encode6 or decode6' and confirm collection fails because the module does not exist.
  • Add the pure codec. Embed the first 256 bytes of docs/逆向成果-0918/Encode6BitBuf_替换表.bin (verified SHA-256 bd8e0d6b48cff81a2e25e497a86e5e5ef33fedab7cb4af6a1e0db05ddd59fe1e) as ENC_TABLE; build the inverse table in memory. Reject bytes outside 0x3c..0x7b and an incorrect expected_size.
  • Re-run the selected tests until green, then commit:
git add cq2_sdk/src/cq2_sdk/transport/encode6.py cq2_sdk/tests/unit/test_framing.py
git commit -m 'feat: add verified 6-bit header codec'

Task 3: Convert framing to the nine-word wire contract

Files:

  • Modify: cq2_sdk/src/cq2_sdk/transport/framing.py
  • Modify: cq2_sdk/src/cq2_sdk/transport/__init__.py
  • Modify: cq2_sdk/tests/unit/test_framing.py
  • Modify: cq2_sdk/tests/unit/test_recorder.py

Interfaces: FrameHeader(pre, msg_id, recog, param1, param2, param3, codec, body_len, sequence), encode_frame, decode_header, decode_frame.

  • Replace the old 24-byte tests with failing behavior tests asserting: a body-bearing frame has WIRE_HEADER_SIZE + len(body) bytes; a round trip preserves pre, sequence, and body; a truncated body raises; and codec=1 raises unsupported body codec: 1.
  • Update the recorder server test to read 48 bytes, use decode_header, and send its response through encode_frame; do not hand-unpack <6i.
  • Run PYTHONPATH=cq2_sdk/src ../../.venv/bin/python -m pytest cq2_sdk/tests/unit/test_framing.py cq2_sdk/tests/unit/test_recorder.py -q and confirm RED because old framing lacks fields and retains a 24-byte header.
  • Implement PLAIN_HEADER_FORMAT = '<9i', PLAIN_HEADER_SIZE = 36, WIRE_HEADER_SIZE = 48; pack all nine words, encode exactly the plain header, append the body, and decode the 48-byte header with decode6(..., expected_size=36).
  • Re-export new constants, run the affected tests until green, then commit:
git add cq2_sdk/src/cq2_sdk/transport/framing.py cq2_sdk/src/cq2_sdk/transport/__init__.py cq2_sdk/tests/unit/test_framing.py cq2_sdk/tests/unit/test_recorder.py
git commit -m 'feat: encode verified TCP wire frames'

Task 4: Update TCP buffering and sequence allocation

Files:

  • Modify: cq2_sdk/src/cq2_sdk/transport/tcp.py
  • Modify: cq2_sdk/tests/unit/test_tcp_client.py
  • Modify: cq2_sdk/src/cq2_sdk/client.py

Interfaces: TcpClient(host, port, *, initial_sequence: int = 0); existing six-argument send remains unchanged.

  • Write failing loopback tests that: (1) send an encoded frame in slices [:11], [11:48], [48:] and assert the client receives the full header/body; (2) decode two outgoing 48-byte headers server-side and assert a client with initial_sequence=9 emits sequences 9 then 10.
  • Run PYTHONPATH=cq2_sdk/src ../../.venv/bin/python -m pytest cq2_sdk/tests/unit/test_tcp_client.py -q and confirm the new tests are RED because the client reads 24 bytes and has no sequence counter.
  • Remove the encryption_key argument and xxtea call. Buffer until WIRE_HEADER_SIZE + body_len; use _next_sequence in encode_frame, then increment only after frame construction. Update client.py to say frame layout is implemented while CS2001 body, true initial sequence, body codec, and state machine remain unverified.
  • Re-run TCP tests until green, then commit:
git add cq2_sdk/src/cq2_sdk/transport/tcp.py cq2_sdk/src/cq2_sdk/client.py cq2_sdk/tests/unit/test_tcp_client.py
git commit -m 'feat: read real TCP wire frames'

Task 5: Regression verification and review

Files: no new source files unless a test finds a defect.

  • Run:
PYTHONPATH=cq2_sdk/src ../../.venv/bin/python -m pytest cq2_sdk/tests/unit/test_framing.py cq2_sdk/tests/unit/test_tcp_client.py cq2_sdk/tests/unit/test_recorder.py cq2_sdk/tests/unit/test_game_login.py -q
git diff master...HEAD --check
git status --short
  • Confirm no token/capture/.env file is staged and request review with the test output and the explicit non-goals above.