You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Draft planning artifact generated via spec-kit — see specs/006-nats/ (spec, plan, research, data-model, contracts, quickstart, tasks).
⚠️ Unverified draft: authored without a build environment (no Gradle/JDK 17/network/TIBCO/DuckDB). On branch t/yolo-d76fe7 (not yet pushed).
Tasks (mirrors specs/006-nats/tasks.md)
Phase 1: Setup
T001 (build-env) Add io.nats:jnats (Apache-2.0, pure-Java, single pinned version — research R6, verify latest) to gradle/libs.versions.toml and wire it into build.gradle.kts (feature 001 files); confirm it resolves and loads on the Java 17 toolchain (FR-018; CONTRACT-NATS-DEP-1/2; SC-006)
T002 [P] Create the package src/main/org/rvsnoop/transport/nats/ (Apache-2.0 file headers) for the adapter, config, converter, subjects, factory, and Guice module classes
T003 [P] Create the test package src/test/org/rvsnoop/transport/nats/ (Apache-2.0 file headers) for the offline unit tests and the opt-in integration test
T004 (build-env, gate) Evaluate an Apache-2.0 + Java-17 in-process/embeddable NATS server harness (candidate io.nats:jnats-server-runner — research R5) and, if it qualifies, add it as a pinned dev/test-scoped dependency in gradle/libs.versions.toml + build.gradle.kts; if none qualifies, record the fallback to mock-only tests in research.md (FR-018/FR-019; CONTRACT-NATS-DEP-1, CONTRACT-NATS-TEST-2; SC-005/SC-006)
Phase 2: Foundational (Blocking Prerequisites)
T005 (gate; depends on feature 004) Reconcile against the feature-004 surface: confirm the exact Transport port signature (connect/subscribe/publish/close), the registry registration mechanism, the neutral connection model, and the common connection-state model; record any deltas in research.md (R3, R7). If the 004 port differs from the planned surface, only the thin adapter classes change (plan Complexity Tracking; FR-001, FR-011)
T006 (gate; depends on features 004/002) Confirm the shared NeutralMessage permits a single raw-bytes leaf and string / string-list fields, and that the record detail view renders bytes (hex/text) without mangling; if it lacks a raw-bytes leaf, coordinate a small additive extension once with 004/002 (do NOT duplicate the neutral model) — research R1 (FR-009/FR-010; CONTRACT-NATS-MSG-2; SC-002)
T007 Define NatsAuthMode enum (ANONYMOUS | USER_PASSWORD | TOKEN | CREDS_FILE) in src/main/org/rvsnoop/transport/nats/NatsAuthMode.java per data-model.md (FR-003)
T008 [P] Implement NatsSubjects (subject validation + local NATS wildcard matcher: * = exactly one token, > = one-or-more trailing tokens only as final token, reject empty tokens / non-tail >) in src/main/org/rvsnoop/transport/nats/NatsSubjects.java — research R2 (FR-006, FR-014; CONTRACT-NATS-SUBJ-1/2/3/4/6; SC-003)
T009 [P] Implement NatsClientFactory seam over the jnats Connection/dispatcher (mockable for offline tests; production impl wraps jnats) in src/main/org/rvsnoop/transport/nats/NatsClientFactory.java — research R5 (FR-019; CONTRACT-NATS-TEST-1)
T010 Implement NatsConnectionConfig (transportKind="nats", name, serverUrls ≥1, authMode + credential fields, tlsEnabled, reconnectionPolicy, subscriptions) and NatsSubscriptionConfig (subject, optional queueGroup, runtime active) with validation rules, in src/main/org/rvsnoop/transport/nats/NatsConnectionConfig.java and src/main/org/rvsnoop/transport/nats/NatsSubscription.java, specializing the feature-004 neutral connection model — data-model.md (FR-002/003/004/005/007/014)
T011 Implement NatsMessageConverter: NATS msg → shared NeutralMessage — concrete delivery subject, single raw-bytes payload leaf, headers as named string/string-list fields, reply-to when present, and envelope (timestamp, originating connection, sequence); no structural body parsing, in src/main/org/rvsnoop/transport/nats/NatsMessageConverter.java — research R1, data-model.md (FR-008/009/010; CONTRACT-NATS-MSG-1/2; SC-002)
Phase 3: User Story 1 - Capture messages from a NATS subject (Priority: P1) 🎯 MVP
T012 [P] [US1] NatsSubjectsTest — wildcard match correctness across the tested subject set (foo.* matches foo.a/foo.b not foo.a.b; foo.> matches foo.a.b/foo.a; reject > not in tail position and empty tokens) with zero false matches/misses, in src/test/org/rvsnoop/transport/nats/NatsSubjectsTest.java (CONTRACT-NATS-SUBJ-2/3/4/6; FR-006; SC-003)
T013 [P] [US1] NatsMessageConverterTest — conversion preserves subject + raw payload bytes; binary payload byte-for-byte (no truncation/mangling); empty payload not dropped; no-headers and multi-valued headers; reply-to recorded, in src/test/org/rvsnoop/transport/nats/NatsMessageConverterTest.java (CONTRACT-NATS-MSG-1/2/3/4/5/6; FR-009/010; SC-002)
T014 [P] [US1] NatsTransportLifecycleTest (subscribe/deliver slice) — via the mock seam, a published message on a subscribed subject delivers a NeutralMessage record; stopping/removing a subscription produces no further records; overlapping subscriptions yield one record per delivery received and do not corrupt the trace, in src/test/org/rvsnoop/transport/nats/NatsTransportLifecycleTest.java (CONTRACT-NATS-LIFE-5/6; FR-005/013)
T015 [US1] Implement NatsTransport core — connect(NatsConnectionConfig) opening a jnats connection via the seam, and close() draining/unsubscribing and releasing the connection, in src/main/org/rvsnoop/transport/nats/NatsTransport.java (implements the 004 Transport port) (FR-001; CONTRACT-NATS-PORT-1)
T016 [US1] Implement subscribe(subject, queueGroup?) in NatsTransport: subject passed to NATS verbatim, optional queue-group, message delivery on jnats dispatcher threads handing NeutralMessage records to the existing async ledger path off the UI thread; publish(...) is a no-op (passive tracer), in src/main/org/rvsnoop/transport/nats/NatsTransport.java (FR-005/006/007/008/013/020; CONTRACT-NATS-SUBJ-1/5, CONTRACT-NATS-LIFE-6/7, CONTRACT-NATS-MSG-1)
T017 [US1] Implement NatsTransportModule (Guice) registering the adapter under transport kind "nats" in the feature-004 registry — additive, no core/UI/other-adapter edits, in src/main/org/rvsnoop/transport/nats/NatsTransportModule.java (FR-001/021; CONTRACT-NATS-PORT-1/2)
T018 [US1] (depends on feature 004) Install NatsTransportModule in the app's Guice bootstrap (one binding line) in src/main/org/rvsnoop/RvSnoopModule.java (or the app's module) — additive only (FR-001/021; CONTRACT-NATS-PORT-1)
T019 [US1] Verify quickstart Scenario 1 (capture from foo.bar), Scenario 2 (wildcards foo.*/foo.>), Scenario 3 (stop subscription), Scenario 4 (filter/search over NATS records), and Scenario 5 (payload-fidelity edge cases) via the mock seam (SC-001/002/003)
Phase 4: User Story 2 - Configure and persist a NATS connection (Priority: P2)
T020 [US2] Map the four auth modes onto jnats Options/AuthHandler (anonymous; userInfo; token; credentials authenticator for .creds) and the TLS toggle (secure/sslContext) in NatsTransport.connect, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R4, data-model.md (FR-003/004; CONTRACT-NATS-CFG-2/3)
T021 [US2] Implement config validation surfacing clear errors with no zombie connection: ≥1 well-formed server URL, the credential required by the selected auth mode present, .creds file readable/parseable; reject before any connect attempt, in src/main/org/rvsnoop/transport/nats/NatsConnectionConfig.java (FR-014; CONTRACT-NATS-CFG-1/4)
T022 [US2] Distinguish authentication failures from network/connectivity failures by inspecting the jnats auth-violation callback/exception type, and ensure credentials (password, token, .creds path/contents) are never written to logs in plaintext, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R4 (FR-015/017; CONTRACT-NATS-CFG-5/6)
T023 [US2] (depends on feature 002) Persist/restore NatsConnectionConfig + its NatsSubscriptionConfig list through the existing project-store path (the 004 neutral connection model, kind "nats"); secrets follow the project credential policy, in the store-integration glue (per feature-002 store path) (FR-016/017; CONTRACT-NATS-CFG-7, CONTRACT-NATS-MSG-7)
T024 [P] [US2] Implement NatsConnectionPanel plugging into the shared new-connection dialog (server URLs, auth-mode selector + credential fields, TLS toggle, subscriptions) — no NATS-specific top-level flow, in src/main/org/rvsnoop/ui/NatsConnectionPanel.java (FR-002/003/004/005/007; SC-001; CONTRACT-NATS-PORT-1)
T025 [P] [US2] Add i18n labels for the panel in src/main/org/rvsnoop/ui/NatsConnectionPanel.properties (mirrors the existing *.properties convention)
T026 [P] [US2] NatsConnectionConfigTest — config validation (malformed URL, missing credential, unreadable .creds), all four auth modes configure/persist/restore, TLS toggle, auth-vs-network error distinction, no-plaintext-secret in logs, in src/test/org/rvsnoop/transport/nats/NatsConnectionConfigTest.java (CONTRACT-NATS-CFG-1/2/3/4/5/6; FR-003/004/014/015/017; SC-004)
T027 [US2] Verify quickstart Scenario 6 (persist all auth modes), Scenario 7 (auth + TLS at start), and Scenario 8 (invalid config + auth-vs-network errors) (SC-004)
Phase 5: User Story 3 - Resilient connection lifecycle (Priority: P3)
T028 [US3] Map jnats ConnectionListener events (CONNECTED/DISCONNECTED/RECONNECTED/CLOSED/LAME_DUCK — verify exact enum names) onto the feature-004 common connection-state model (started, paused, resumed, disconnected, reconnected, failed) and surface them through the same UI path as other transports, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R3, data-model.md (FR-011; CONTRACT-NATS-LIFE-1/2)
T029 [US3] Drive jnats reconnection options (maxReconnects, reconnectWait) from the per-connection reconnectionPolicy; on exhausted reconnect surface a failed state with a clear reason while the app stays usable, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R3 (FR-012; CONTRACT-NATS-LIFE-3; SC-008)
T030 [US3] Implement user pause/resume: pause suspends record dispatch (drain/unsubscribe or suspend the dispatcher, keeping TCP where practical) and resume re-enables delivery, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R3 (FR-011; CONTRACT-NATS-LIFE-4)
T031 [P] [US3] Extend NatsTransportLifecycleTest — via the mock seam: simulated transient disconnect resumes capture and transitions disconnected→reconnected; pause stops records until resume; exhausted reconnect → failed with a clear reason; capture/connect operations do not block the UI thread, in src/test/org/rvsnoop/transport/nats/NatsTransportLifecycleTest.java (CONTRACT-NATS-LIFE-1/2/3/4/7; FR-011/012/020; SC-008)
T032 [US3] Verify quickstart Scenario 9 (disconnect/reconnect resume; pause/resume; exhausted reconnect → failed) via the mock seam (SC-008)
Phase 6: Polish & Cross-Cutting Concerns
T033 [P] (broker / opt-in) Implement NatsTransportIntegrationTest against an in-process embeddable NATS server (real subscribe/wildcard delivery + reconnection), gated by an environment/profile flag and skipped by default; if no Apache-2.0 server qualified (T004), document the mock-only fallback, in src/test/org/rvsnoop/transport/nats/NatsTransportIntegrationTest.java (CONTRACT-NATS-TEST-1/2; FR-019; SC-005)
T034 [US?]-none (build-env) Run the full NATS test suite with no external server and confirm it passes on the mock seam (offline/CI), per quickstart Scenario 12 (FR-019; SC-005; CONTRACT-NATS-TEST-1)
T035 [P] Verify additivity/coexistence (quickstart Scenario 10): a NATS connection and another transport's connection coexist, both feed the same trace with per-connection attribution, and the existing transports' tests pass unchanged — confirm no NATS/jnats type leaks outside org.rvsnoop.transport.nats (+ its UI panel) (FR-021; CONTRACT-NATS-PORT-2/3; SC-007)
T036 [P] (build-env) Inspect the dependency graph (quickstart Scenario 12): jnats Apache-2.0 and pinned to one version, no new copyleft, no proprietary jar; reaffirm Dependabot (feature 001) tracks jnats and any test-only server (FR-018; CONTRACT-NATS-DEP-1/2; SC-006; Principles I & V)
T037 [P] Update developer docs/README: NATS as a new transport, supported auth modes, JetStream/publish/content-decoding out of scope, offline-test approach
T038 Run the full quickstart.md validation (Scenarios 1–12), validating earliest the high-risk Scenarios 5 (payload fidelity), 9 (lifecycle/reconnect), and 12 (offline-only + Apache-2.0 gate)
Draft planning artifact generated via spec-kit — see
specs/006-nats/(spec, plan, research, data-model, contracts, quickstart, tasks).t/yolo-d76fe7(not yet pushed).Tasks (mirrors
specs/006-nats/tasks.md)Phase 1: Setup
io.nats:jnats(Apache-2.0, pure-Java, single pinned version — research R6, verify latest) to gradle/libs.versions.toml and wire it into build.gradle.kts (feature 001 files); confirm it resolves and loads on the Java 17 toolchain (FR-018; CONTRACT-NATS-DEP-1/2; SC-006)io.nats:jnats-server-runner— research R5) and, if it qualifies, add it as a pinned dev/test-scoped dependency in gradle/libs.versions.toml + build.gradle.kts; if none qualifies, record the fallback to mock-only tests in research.md (FR-018/FR-019; CONTRACT-NATS-DEP-1, CONTRACT-NATS-TEST-2; SC-005/SC-006)Phase 2: Foundational (Blocking Prerequisites)
Transportport signature (connect/subscribe/publish/close), the registry registration mechanism, the neutral connection model, and the common connection-state model; record any deltas in research.md (R3, R7). If the 004 port differs from the planned surface, only the thin adapter classes change (plan Complexity Tracking; FR-001, FR-011)NeutralMessagepermits a single raw-bytes leaf and string / string-list fields, and that the record detail view renders bytes (hex/text) without mangling; if it lacks a raw-bytes leaf, coordinate a small additive extension once with 004/002 (do NOT duplicate the neutral model) — research R1 (FR-009/FR-010; CONTRACT-NATS-MSG-2; SC-002)NatsAuthModeenum (ANONYMOUS|USER_PASSWORD|TOKEN|CREDS_FILE) in src/main/org/rvsnoop/transport/nats/NatsAuthMode.java per data-model.md (FR-003)NatsSubjects(subject validation + local NATS wildcard matcher:*= exactly one token,>= one-or-more trailing tokens only as final token, reject empty tokens / non-tail>) in src/main/org/rvsnoop/transport/nats/NatsSubjects.java — research R2 (FR-006, FR-014; CONTRACT-NATS-SUBJ-1/2/3/4/6; SC-003)NatsClientFactoryseam over the jnatsConnection/dispatcher (mockable for offline tests; production impl wraps jnats) in src/main/org/rvsnoop/transport/nats/NatsClientFactory.java — research R5 (FR-019; CONTRACT-NATS-TEST-1)NatsConnectionConfig(transportKind="nats", name, serverUrls ≥1, authMode + credential fields, tlsEnabled, reconnectionPolicy, subscriptions) andNatsSubscriptionConfig(subject, optional queueGroup, runtime active) with validation rules, in src/main/org/rvsnoop/transport/nats/NatsConnectionConfig.java and src/main/org/rvsnoop/transport/nats/NatsSubscription.java, specializing the feature-004 neutral connection model — data-model.md (FR-002/003/004/005/007/014)NatsMessageConverter: NATS msg → sharedNeutralMessage— concrete delivery subject, single raw-bytes payload leaf, headers as named string/string-list fields, reply-to when present, and envelope (timestamp, originating connection, sequence); no structural body parsing, in src/main/org/rvsnoop/transport/nats/NatsMessageConverter.java — research R1, data-model.md (FR-008/009/010; CONTRACT-NATS-MSG-1/2; SC-002)Phase 3: User Story 1 - Capture messages from a NATS subject (Priority: P1) 🎯 MVP
NatsSubjectsTest— wildcard match correctness across the tested subject set (foo.*matchesfoo.a/foo.bnotfoo.a.b;foo.>matchesfoo.a.b/foo.a; reject>not in tail position and empty tokens) with zero false matches/misses, in src/test/org/rvsnoop/transport/nats/NatsSubjectsTest.java (CONTRACT-NATS-SUBJ-2/3/4/6; FR-006; SC-003)NatsMessageConverterTest— conversion preserves subject + raw payload bytes; binary payload byte-for-byte (no truncation/mangling); empty payload not dropped; no-headers and multi-valued headers; reply-to recorded, in src/test/org/rvsnoop/transport/nats/NatsMessageConverterTest.java (CONTRACT-NATS-MSG-1/2/3/4/5/6; FR-009/010; SC-002)NatsTransportLifecycleTest(subscribe/deliver slice) — via the mock seam, a published message on a subscribed subject delivers aNeutralMessagerecord; stopping/removing a subscription produces no further records; overlapping subscriptions yield one record per delivery received and do not corrupt the trace, in src/test/org/rvsnoop/transport/nats/NatsTransportLifecycleTest.java (CONTRACT-NATS-LIFE-5/6; FR-005/013)NatsTransportcore —connect(NatsConnectionConfig)opening a jnats connection via the seam, andclose()draining/unsubscribing and releasing the connection, in src/main/org/rvsnoop/transport/nats/NatsTransport.java (implements the 004Transportport) (FR-001; CONTRACT-NATS-PORT-1)subscribe(subject, queueGroup?)inNatsTransport: subject passed to NATS verbatim, optional queue-group, message delivery on jnats dispatcher threads handingNeutralMessagerecords to the existing async ledger path off the UI thread;publish(...)is a no-op (passive tracer), in src/main/org/rvsnoop/transport/nats/NatsTransport.java (FR-005/006/007/008/013/020; CONTRACT-NATS-SUBJ-1/5, CONTRACT-NATS-LIFE-6/7, CONTRACT-NATS-MSG-1)NatsTransportModule(Guice) registering the adapter under transport kind"nats"in the feature-004 registry — additive, no core/UI/other-adapter edits, in src/main/org/rvsnoop/transport/nats/NatsTransportModule.java (FR-001/021; CONTRACT-NATS-PORT-1/2)NatsTransportModulein the app's Guice bootstrap (one binding line) in src/main/org/rvsnoop/RvSnoopModule.java (or the app's module) — additive only (FR-001/021; CONTRACT-NATS-PORT-1)foo.bar), Scenario 2 (wildcardsfoo.*/foo.>), Scenario 3 (stop subscription), Scenario 4 (filter/search over NATS records), and Scenario 5 (payload-fidelity edge cases) via the mock seam (SC-001/002/003)Phase 4: User Story 2 - Configure and persist a NATS connection (Priority: P2)
Options/AuthHandler(anonymous;userInfo;token; credentials authenticator for.creds) and the TLS toggle (secure/sslContext) inNatsTransport.connect, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R4, data-model.md (FR-003/004; CONTRACT-NATS-CFG-2/3).credsfile readable/parseable; reject before any connect attempt, in src/main/org/rvsnoop/transport/nats/NatsConnectionConfig.java (FR-014; CONTRACT-NATS-CFG-1/4).credspath/contents) are never written to logs in plaintext, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R4 (FR-015/017; CONTRACT-NATS-CFG-5/6)NatsConnectionConfig+ itsNatsSubscriptionConfiglist through the existing project-store path (the 004 neutral connection model, kind"nats"); secrets follow the project credential policy, in the store-integration glue (per feature-002 store path) (FR-016/017; CONTRACT-NATS-CFG-7, CONTRACT-NATS-MSG-7)NatsConnectionPanelplugging into the shared new-connection dialog (server URLs, auth-mode selector + credential fields, TLS toggle, subscriptions) — no NATS-specific top-level flow, in src/main/org/rvsnoop/ui/NatsConnectionPanel.java (FR-002/003/004/005/007; SC-001; CONTRACT-NATS-PORT-1)*.propertiesconvention)NatsConnectionConfigTest— config validation (malformed URL, missing credential, unreadable.creds), all four auth modes configure/persist/restore, TLS toggle, auth-vs-network error distinction, no-plaintext-secret in logs, in src/test/org/rvsnoop/transport/nats/NatsConnectionConfigTest.java (CONTRACT-NATS-CFG-1/2/3/4/5/6; FR-003/004/014/015/017; SC-004)Phase 5: User Story 3 - Resilient connection lifecycle (Priority: P3)
ConnectionListenerevents (CONNECTED/DISCONNECTED/RECONNECTED/CLOSED/LAME_DUCK— verify exact enum names) onto the feature-004 common connection-state model (started, paused, resumed, disconnected, reconnected, failed) and surface them through the same UI path as other transports, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R3, data-model.md (FR-011; CONTRACT-NATS-LIFE-1/2)maxReconnects,reconnectWait) from the per-connectionreconnectionPolicy; on exhausted reconnect surface a failed state with a clear reason while the app stays usable, in src/main/org/rvsnoop/transport/nats/NatsTransport.java — research R3 (FR-012; CONTRACT-NATS-LIFE-3; SC-008)NatsTransportLifecycleTest— via the mock seam: simulated transient disconnect resumes capture and transitions disconnected→reconnected; pause stops records until resume; exhausted reconnect → failed with a clear reason; capture/connect operations do not block the UI thread, in src/test/org/rvsnoop/transport/nats/NatsTransportLifecycleTest.java (CONTRACT-NATS-LIFE-1/2/3/4/7; FR-011/012/020; SC-008)Phase 6: Polish & Cross-Cutting Concerns
NatsTransportIntegrationTestagainst an in-process embeddable NATS server (real subscribe/wildcard delivery + reconnection), gated by an environment/profile flag and skipped by default; if no Apache-2.0 server qualified (T004), document the mock-only fallback, in src/test/org/rvsnoop/transport/nats/NatsTransportIntegrationTest.java (CONTRACT-NATS-TEST-1/2; FR-019; SC-005)org.rvsnoop.transport.nats(+ its UI panel) (FR-021; CONTRACT-NATS-PORT-2/3; SC-007)