informix-db/docs/DECISION_LOG.md
Ryan Malloy f202dbce0c Initialize Phase 0 spike scaffold
Project goal: pure-Python implementation of the Informix SQLI wire
protocol. No CSDK, no JVM, no native deps. Targets icr.io/informix
/informix-developer-database (port 9088) as the dev/test instance.

Phase 0 is a documentation-only spike that gates all implementation
work. The four scaffolds:

- README.md: project status and Phase 0 deliverable index
- docs/PROTOCOL_NOTES.md: byte-level wire-format reference (TBD)
- docs/JDBC_NOTES.md: reverse-lookup index into the decompiled IBM
  JDBC driver (4.50.4.1), populated from build/jdbc-src/ once the
  decompile lands
- docs/DECISION_LOG.md: running rationale, with the Phase-1 paramstyle
  /Python-floor/autocommit decisions pre-locked so they don't churn
  later

CLAUDE.md is gitignored — operator-private context, public-PyPI repo.
2026-05-02 13:22:28 -06:00

5.3 KiB

Decision Log

Running rationale for protocol, auth, type, and architecture decisions made during the project. New decisions append; old ones are amended (with date) rather than overwritten.

Format: every decision has a date, a status (active / superseded / revisited), the chosen path, the discarded alternatives, and the why.


2026-05-02 — Project goal & off-ramp

Status: active Decision: Build a pure-Python implementation of the SQLI wire protocol. No IBM Client SDK. No JVM. No native libraries. Off-ramp (chosen by user during planning): if Phase 0 reveals the protocol is intractable in pure Python — e.g., mandatory undocumented crypto in the handshake — narrow scope (lock to one server version, drop async, drop prepared statements if needed) and stay pure-Python. Do not fall back to JPype/JDBC; that defeats the project's purpose. Why: The "no SDK / no JVM" goal is what makes this driver valuable. A JPype fallback would ship something that works but solves nothing the existing JDBC-via-JPype solution doesn't already solve.


2026-05-02 — Package name

Status: active Decision: informix-db Discarded: informixdb-pure (longer), ifxsqli (less discoverable), pyifx (obscure) PyPI availability: confirmed available 2026-05-02 (HTTP 404 on /pypi/informix-db/json). The legacy informixdb is taken (HTTP 200), informix is also free (404) but too generic. Why: Discoverability balanced with brevity. Anyone searching PyPI for "informix" finds it; the hyphen distinguishes it from the legacy C-extension wrapper.


2026-05-02 — License

Status: active Decision: MIT Discarded: Apache-2.0 (more defensive but less common in Python ecosystem), BSD-3-Clause Why: Simplest, most permissive, ecosystem-standard for Python libraries.


2026-05-02 — Sync first; async deferred

Status: active Decision: Build a sync, blocking-socket implementation. Async lands in Phase 6+ as a separate informix_db.aio subpackage following asyncpg's I/O-agnostic-protocol pattern. Why: Wire protocols are hard enough; debugging protocol bugs through asyncio plumbing is two layers of indirection too many. Sync-first means we can test against blocking sockets, prove correctness, then mechanically swap the I/O layer.


2026-05-02 — Test target

Status: active Decision: icr.io/informix/informix-developer-database (the IBM Informix Developer Edition image), port 9088 (native SQLI). Why: Free, official, no license click-through, supports plain-password auth out of the box. Pinning the digest (not :latest) is a Phase 1 requirement.


2026-05-02 — Phase 0 is a gate, not a step

Status: active Decision: No library code is written until PROTOCOL_NOTES.md meets all four exit criteria:

  1. Login byte layout documented end-to-end
  2. Message-type tags identified for login/execute/row/end-of-result/error/disconnect
  3. SELECT 1 round-trip fully labeled
  4. JDBC source and packet capture corroborate on login + execute paths

If exit criteria can't be met within bounded effort, invoke the off-ramp.

Why: Most greenfield projects fail by writing code before they understand the problem. This project has an undocumented wire protocol as its central unknown. Gating on Phase 0 means a failed spike still produces a publicly valuable artifact (PROTOCOL_NOTES.md) instead of a half-built driver.


2026-05-02 — Phase 1 architecture decisions (locked at start of Phase 1)

These are pre-decided so paramstyle/Python-floor/autocommit don't churn later. Recorded here so Phase 1 doesn't relitigate them.

  • paramstyle = "numeric" (:1, :2, …). Matches Informix ESQL/C convention.
  • Python ≥ 3.10. Gives us match, modern type hints, tomllib.
  • autocommit defaults to off. PEP 249 implicit semantics; opt-in via connect(autocommit=True).
  • Author: Ryan Malloy <ryan@supported.systems> (per global pyproject.toml convention).
  • Versioning: CalVer YYYY.MM.DD (2026.05.02 initial); same-day fixes use PEP 440 post-release 2026.05.02.1, .2, etc.

2026-05-02 — DATE pulled forward to MVP

Status: active Decision: DATE is included in the Phase 2 MVP type set, alongside SMALLINT/INTEGER/BIGINT/FLOAT/CHAR/VARCHAR/BOOLEAN. Discarded: leaving DATE in the "medium" / Phase 6 bucket. Why: Almost no real Informix database is DATE-free. The encoding is trivial once the type code is known (4-byte day count from the Informix epoch 1899-12-31). Cheap to include; expensive to leave out.

DATETIME / INTERVAL / DECIMAL / NUMERIC / MONEY remain in Phase 6+ — their encodings (qualifier-byte precision, BCD-style packed decimal) are non-trivial.


2026-05-02 — CLAUDE.md excluded from git and sdist

Status: active Decision: .gitignore excludes CLAUDE.md. Once pyproject.toml exists, [tool.hatch.build.targets.sdist].exclude will also list CLAUDE.md. Why: CLAUDE.md contains the user's email and operator-private context. Per global convention, only commit CLAUDE.md to private repos. This project is destined for PyPI / public Git.


(template — copy below this line for new entries)

## YYYY-MM-DD — <one-line decision title>

**Status**: active | superseded | revisited
**Decision**: <chosen path>
**Discarded**: <alternatives, briefly>
**Why**: <rationale>