From 390797aae6b8e29a0d6f22f68afa027c569b1434 Mon Sep 17 00:00:00 2001 From: adiel-sammak <71227923+aelsammak@users.noreply.github.com> Date: Thu, 1 Oct 2026 15:30:07 -0400 Subject: [PATCH 1/2] EBP-3105: Add Python API routing and the guaranteed pub/sub Quickstart - Route Solace Python API requests (solace-pubsubplus, MessagingService, PersistentMessageReceiver) to a new references/python.md entry file; remove Python from the description's exclusions and keep JMS, Go, .NET, and JavaScript excluded - Ask "JCSMP (Java) or Python?" when a Solace build request names no language; workspace files are facts to report, not the answer - Generalize Invariants 3 and 5: the registry per API (Maven metadata for JCSMP, PyPI info.version for Python) and the API's own bundled verify script, with the compile-only fallback for an API that has none yet - Add python/prerequisites.md, design-mode.md (five leaves; no guaranteed request-reply in the Python API), implement-mode.md, and implement-guaranteed-pubsub.md, mirroring the JCSMP files - Quickstart generates a venv project with requirements.txt pinned to the version PyPI reports at generation time, a gitignored config.json, and the four JCSMP VERIFY: markers; it ends in py_compile plus an import smoke and handed-back run commands until the Python verify.sh lands - Document the concurrency rules: receive_async() is not asyncio and calls the handler on a new thread per message, and the API cannot be used with multiprocessing - The direct pub/sub and direct request-reply leaves, and the Solace Suggested and Custom doors, stop with "not yet available" for Python until EBP-3106 - Flip the two Python trigger cases to positives (the guaranteed pub/sub one must_pass) and add Go, JavaScript, asyncio, and Kafka Python near-miss negatives --- .../evals/trigger-evals.json | 8 +- .../solace-application-development/SKILL.md | 18 +- .../references/python.md | 69 +++++ .../references/python/design-mode.md | 108 ++++++++ .../python/implement-guaranteed-pubsub.md | 53 ++++ .../references/python/implement-mode.md | 250 ++++++++++++++++++ .../references/python/prerequisites.md | 29 ++ 7 files changed, 526 insertions(+), 9 deletions(-) create mode 100644 plugins/solace-messaging-skills/skills/solace-application-development/references/python.md create mode 100644 plugins/solace-messaging-skills/skills/solace-application-development/references/python/design-mode.md create mode 100644 plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-guaranteed-pubsub.md create mode 100644 plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-mode.md create mode 100644 plugins/solace-messaging-skills/skills/solace-application-development/references/python/prerequisites.md diff --git a/plugins/solace-messaging-skills/evals/trigger-evals.json b/plugins/solace-messaging-skills/evals/trigger-evals.json index 750d510..d6f61d9 100644 --- a/plugins/solace-messaging-skills/evals/trigger-evals.json +++ b/plugins/solace-messaging-skills/evals/trigger-evals.json @@ -12,9 +12,13 @@ {"skill":"solace-application-development","prompt":"For the logistics platform we run in Java on our Solace event broker, a ShipmentTracker service publishes a DeliveryException event whenever a package misses its checkpoint. Three separate services each need to act on it independently: a CustomerNotifier emails the customer, a ReroutePlanner recalculates the route, and an AnalyticsIngest service records it for reporting. Each is owned by a different team, each needs its own full copy of every event, and none of them can miss one even if that service is down for maintenance. How should I structure the messaging so every one of these services reliably receives every event on its own?","should_trigger":true,"must_pass":true}, {"skill":"solace-application-development","prompt":"We've standardized on the Solace JMS API. How do I create a JMS ConnectionFactory and a MessageProducer to send to a queue in Java?","should_trigger":false}, - {"skill":"solace-application-development","prompt":"I'm writing my Solace client in Python with the solace-pubsubplus library. How do I set up the messaging service and a persistent receiver?","should_trigger":false}, - {"skill":"solace-application-development","prompt":"We've got a warehouse-robotics system where two Python services communicate through our Solace event broker using the solace-pubsubplus library. A RobotDispatcher publishes task assignments and a RobotController consumes them and must not lose any while a robot reboots. How should I structure the messaging between these two Python services?","should_trigger":false}, + {"skill":"solace-application-development","prompt":"I'm writing my Solace client in Python with the solace-pubsubplus library. How do I set up the messaging service and a persistent receiver?","should_trigger":true}, + {"skill":"solace-application-development","prompt":"We've got a warehouse-robotics system where two Python services communicate through our Solace event broker using the solace-pubsubplus library. A RobotDispatcher publishes task assignments and a RobotController consumes them and must not lose any while a robot reboots. How should I structure the messaging between these two Python services?","should_trigger":true,"must_pass":true}, {"skill":"solace-application-development","prompt":"Show me how to publish to a Solace topic from C# using the .NET messaging API.","should_trigger":false}, + {"skill":"solace-application-development","prompt":"I'm writing a Go service with the Solace PubSub+ Messaging API for Go. How do I build a persistent message publisher and handle publish receipts?","should_trigger":false}, + {"skill":"solace-application-development","prompt":"In our browser dashboard we use the solclientjs library. How do I subscribe to a Solace topic from JavaScript and update the page when messages arrive?","should_trigger":false}, + {"skill":"solace-application-development","prompt":"I have a Python asyncio service that calls a few REST APIs concurrently. How do I limit it to five requests in flight at once with a semaphore?","should_trigger":false}, + {"skill":"solace-application-development","prompt":"I'm using confluent-kafka in Python. How do I write a consumer that commits offsets only after each message has been processed?","should_trigger":false}, {"skill":"solace-application-development","prompt":"I need a Kafka producer and consumer in Java for our clickstream pipeline. How do I configure the KafkaProducer with the right key and value serializers?","should_trigger":false}, {"skill":"solace-application-development","prompt":"How do I provision a durable queue named q.orders.intake and set its max-spool-usage on our Solace broker through the SEMP API and PubSub+ Manager? I'm configuring the broker, not writing a client.","should_trigger":false}, {"skill":"solace-application-development","prompt":"How do I model our order and shipment events and the relationships between them in Solace Event Portal?","should_trigger":false}, diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/SKILL.md b/plugins/solace-messaging-skills/skills/solace-application-development/SKILL.md index a291599..3176997 100644 --- a/plugins/solace-messaging-skills/skills/solace-application-development/SKILL.md +++ b/plugins/solace-messaging-skills/skills/solace-application-development/SKILL.md @@ -1,6 +1,6 @@ --- name: solace-application-development -description: "Use when the user mentions JCSMP, the com.solacesystems:sol-jcsmp library, JCSMPSession, publishing to a Solace topic, consuming from a Solace queue, or guaranteed messaging, OR asks broadly to build or design a Solace messaging app in Java (publisher, consumer, pub/sub). This is the single front door for Solace API development: it grounds the work in canonical Solace docs and routes the request to the right API (JCSMP today), helping choose a messaging pattern or generate a runnable Maven app. Do NOT trigger for an explicitly named non JCSMP API, which beats the generic Java signal: Solace JMS, or the Solace Python, Go, .NET, or JavaScript APIs. Also do NOT trigger for Apache Kafka, broker administration and configuration, Solace Event Portal event modeling, Schema Registry, OpenTelemetry tracing, or topic-architecture and topic-hierarchy design questions (use solace-topic-best-practices)." +description: "Use when the user mentions JCSMP, the com.solacesystems:sol-jcsmp library, JCSMPSession, the Solace Python API or its solace-pubsubplus library, MessagingService, PersistentMessageReceiver, publishing to a Solace topic, consuming from a Solace queue, or guaranteed messaging, OR asks broadly to build or design a Solace messaging app in Java or Python (publisher, consumer, pub/sub). This is the single front door for Solace API development: it grounds the work in canonical Solace docs and routes to the right API (JCSMP or Python), helping choose a messaging pattern or generate a runnable app. Do NOT trigger for an explicitly named other Solace API, which beats the generic Java signal: Solace JMS, or the Solace Go, .NET, or JavaScript APIs. Also do NOT trigger for Apache Kafka (any language), broker administration and configuration, Solace Event Portal event modeling, Schema Registry, OpenTelemetry tracing, or topic-architecture and topic-hierarchy design questions (use solace-topic-best-practices)." allowed-tools: Read Write Edit Bash Grep Glob WebFetch --- @@ -13,6 +13,7 @@ This skill is the front door for building Solace messaging applications grounded **Do NOT read all reference files upfront. Read ONLY what you need, when you need it.** - User wants a JCSMP application (design help or code generation) then read `references/jcsmp.md`, which selects the right mode within the JCSMP API. +- User wants a Solace Python API application (design help or code generation) then read `references/python.md`, which selects the right mode within the Python API. - Read a mode file only at the moment a step needs it. - Most requests need the API's entry file plus one mode file, not the whole tree. @@ -31,21 +32,24 @@ Determine which Solace API the developer is building against, then read that API | User intent | API | What to do | |---|---|---| | Anything about JCSMP, the `com.solacesystems:sol-jcsmp` library, a Java publisher or consumer, publishing to a topic, or consuming from a queue with guaranteed messaging | **JCSMP** | Read `references/jcsmp.md` | +| Anything about the Solace Python API, the `solace-pubsubplus` library, `MessagingService`, `PersistentMessageReceiver`, or a Python publisher or consumer | **Python** | Read `references/python.md` | -If the API is unclear but the request is clearly about Solace messaging in Java, default to **JCSMP**. Mode detection (Design, Implement) then happens in that API's entry file. +If the API is unclear but the request is clearly about Solace messaging in Java, default to **JCSMP**. If the API is unclear but the request is clearly about Solace messaging in Python, default to **Python**. If the request names no language at all (for example "build me a Solace publisher"), ask one question before anything else: JCSMP (Java) or Python? Files in the workspace (a `pom.xml`, `.py` files, a `requirements.txt`) are facts to report while you ask, never the answer. Mode detection (Design, Implement) then happens in that API's entry file. ## Invariants Non-negotiable rules that apply across every API. Apply all. 1. **Content sourcing**: Every named entity (API, parameter, configuration value) must be traceable to a canonical Solace source. Do not assert behavior, defaults, or best practices that are not in the grounding documentation. -2. **WebFetch-on-demand doc grounding**: This skill bundles no documentation. When a step needs documentation content to ground a generation or design decision, WebFetch the live canonical page on demand (its `docs.solace.com` `.md` URL, the same link the reference files carry), then quote or summarize the fetched page. Do not answer from memory and do not paraphrase guidance the page does not contain. Do not restate doc content in the skill; link the live `.md` URL. Never substitute another channel for that WebFetch: grounding taken from a docs chatbot, a search tool, or decompiling a jar does not satisfy this invariant, and a page never fetched in the session must never be cited as grounding. A short list of references stays live for the same reason it always has (Javadoc HTML, the JCSMP API Release Notes, Maven Central, the tutorials, and the GitHub samples) because those have no `docs.solace.com` `.md` form. -3. **No hardcoded versions**: Never pin a library version anywhere in skill content. The generated build resolves the latest release of every dependency at generation time from the AUTHORITATIVE Maven repository metadata; each API's entry file names the exact coordinate and carries the exact lookup command. Never read a version from the legacy `search.maven.org/solrsearch` index: it lags the repository metadata and reports stale versions. Security floors (a minimum version below which known CVEs live) are enforced as checks on the resolved result, never as pins. -4. **AI-assisted disclaimer header**: Every source file generated under this skill starts with this exact line: `AI-assisted code. Review before production use.` Directly below it: `See the verification checklist: solace-verification-checklist.md`. This applies to EVERY generated source file, including custom builds, web apps, and embedded shapes that adapt the reference samples outside the canonical generator. The pom and other non-source artifacts are exempt; the bundled reference samples themselves carry no header. -5. **Verification artifacts, always**: Any session that generates Solace code from this skill writes a tailored `solace-verification-checklist.md` beside the code as generation output, copies the bundled `verify.sh` (plus its generated `verify-hooks.sh`) into the project, and ends in a real verify run or the compile-only fallback with the exact handed-back commands. The `VERIFY:` markers are non-optional in generated Solace messaging code, whatever the app shape. An improvised check (for example curl against the app's own HTTP API) may TRIGGER traffic but never renders the verdict; the verdict comes from the markers and the verify exit code. -6. **Live-broker heads-up**: Immediately before starting ANY process that will connect to a live broker — verify.sh, `java -jar`, a `mvn` run, a run script, anything — print one line that names the broker host and states the actual broker-side effects of this run (the queues it provisions, the connections it opens, the messages it publishes), scaled to the real app. Environment discovery is never consent and never an answer: a running local broker, an existing config file, or found credentials are facts to report, not answers to consume; report them, then still ask which broker the developer wants to target. +2. **WebFetch-on-demand doc grounding**: This skill bundles no documentation. When a step needs documentation content to ground a generation or design decision, WebFetch the live canonical page on demand (its `docs.solace.com` `.md` URL, the same link the reference files carry), then quote or summarize the fetched page. Do not answer from memory and do not paraphrase guidance the page does not contain. Do not restate doc content in the skill; link the live `.md` URL. Never substitute another channel for that WebFetch: grounding taken from a docs chatbot, a search tool, or decompiling a jar does not satisfy this invariant, and a page never fetched in the session must never be cited as grounding. A short list of references stays live for the same reason it always has (the JCSMP Javadoc and the Python API reference, the JCSMP and Python API Release Notes, Maven Central and PyPI, the tutorials, and the GitHub samples) because those have no `docs.solace.com` `.md` form. Each API's entry file lists its own live exceptions. +3. **No hardcoded versions**: Never pin a library version anywhere in skill content. The generated build resolves the latest release of every dependency at generation time from the API's AUTHORITATIVE registry: the Maven repository metadata for JCSMP, the PyPI JSON `info.version` for Python. Each API's entry file names the exact coordinate and carries the exact lookup command. Never read a JCSMP version from the legacy `search.maven.org/solrsearch` index: it lags the repository metadata and reports stale versions. Security floors (a minimum version below which known CVEs live) are enforced as checks on the resolved result, never as pins. +4. **AI-assisted disclaimer header**: Every source file generated under this skill starts with this exact line: `AI-assisted code. Review before production use.` Directly below it: `See the verification checklist: solace-verification-checklist.md`. This applies to EVERY generated source file, including custom builds, web apps, and embedded shapes that adapt the reference samples outside the canonical generator. Build files (the pom, `requirements.txt`) and other non-source artifacts are exempt; the bundled reference samples themselves carry no header. +5. **Verification artifacts, always**: Any session that generates Solace code from this skill writes a tailored `solace-verification-checklist.md` beside the code as generation output, copies the API's own bundled verify script (plus its generated `verify-hooks.sh`) into the project, and ends in a real verify run or the compile-only fallback with the exact handed-back commands. The API's entry file names its verify script. An API that does not bundle a verify script yet (Python, until its `verify.sh` lands) ends in the compile-only fallback every time, and its implement-mode file defines that fallback; never claim a verified run without the script. The `VERIFY:` markers are non-optional in generated Solace messaging code, whatever the app shape and whatever the API. An improvised check (for example curl against the app's own HTTP API) may TRIGGER traffic but never renders the verdict; the verdict comes from the markers and the verify exit code. +6. **Live-broker heads-up**: Immediately before starting ANY process that will connect to a live broker — verify.sh, `java -jar`, a `mvn` run, a `python` run, a run script, anything — print one line that names the broker host and states the actual broker-side effects of this run (the queues it provisions, the connections it opens, the messages it publishes), scaled to the real app. Environment discovery is never consent and never an answer: a running local broker, an existing config file, or found credentials are facts to report, not answers to consume; report them, then still ask which broker the developer wants to target. ## Reference Files (read on-demand only) - `references/jcsmp.md`: the JCSMP entry file (mode detection, the JCSMP coordinate, and the live JCSMP doc links); read this for any JCSMP request. - `references/jcsmp/`: all JCSMP API content (mode files, the reference samples, the verify script, and the compile fixture), reached through `references/jcsmp.md`. +- `references/python.md`: the Python entry file (mode detection, the `solace-pubsubplus` coordinate, and the live Python doc links); read this for any Solace Python API request. +- `references/python/`: all Python API content (mode files and the reference samples), reached through `references/python.md`. diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python.md new file mode 100644 index 0000000..dbaeadb --- /dev/null +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python.md @@ -0,0 +1,69 @@ +# Python API + +The Python entry file of the Solace API development skill. The Solace Messaging API for Python is published on PyPI as `solace-pubsubplus`. This file detects the developer's intent and selects the right Python mode; the detailed guidance lives in the lazy-loaded mode files under `python/`. + +The cross-cutting invariants (content sourcing, WebFetch-on-demand doc grounding, and no hardcoded versions) are owned by the umbrella `SKILL.md`. Apply them here; this file does not restate them. The Python-specific points below are the package to depend on and the canonical pages to ground in. + +## Mode Detection + +Determine the user's intent and enter the appropriate mode. Routing is keyed on the design contract, not on the phrasing of the request: Implement mode never starts without a confirmed design (the design-contract gate below), so a "build me..." prompt is NOT an Implement signal by itself. + +| User intent | Mode | What to do | +|---|---|---| +| "Help me choose the Solace messaging pattern for a new Python app" / "Topic or queue?" / "What delivery semantics should I use?" | **Design** | Read `python/design-mode.md` | +| Build or generate a NEW app or messaging component with NO valid design contract yet ("Build me a Solace Python publisher/consumer", "Generate a Python project with solace-pubsubplus", a web app / dashboard / service, or any embedded shape where the Python messaging layer lives inside a larger application) | **Design first** | Read `python/design-mode.md`. A prompt that already states the design resolves on its fully-specified path in ONE confirm; a bare prompt walks the tree. The confirmed summary then feeds Implement. | +| A build request WITH a valid design contract (see the design-contract gate below) | **Implement** | Read `python/implement-mode.md` | +| An edit to an EXISTING app that changes the messaging topology (the topology rule below) | **Design first** | Re-enter `python/design-mode.md` to re-confirm the affected summary fields, then Implement applies the change | +| A mechanical edit to an EXISTING app with no topology change (reconnect handling, payload format, logging, renames, ack tuning) | **Implement** | Read `python/implement-mode.md`; no design pass | +| "My Python app is throwing on connect" / "Why is my receiver not binding the queue?" | **Debug** | Debug mode is not yet available in this release. Redirect the user to the canonical Solace Python API documentation: [Python API Home](https://docs.solace.com/API/Messaging-APIs/Python-API/python-home.md). The redirect is the ENTIRE debug answer: acknowledge the problem in one line, link the page, and stop. Do not generate debugging guidance from memory. That prohibition covers root-cause hypotheses, likely causes, interim checks or "things worth checking in the meantime", workarounds, and configuration or code suggestions. Restating this rule does not license adding such guidance after the redirect. | + +If unclear, default to **Design**. Understand the messaging problem before generating code. + +**The topology rule** decides between the two existing-app rows. An edit changes the messaging topology when it changes the structure the design summary records: a new app or messaging component (a new publisher, receiver, requestor, or replier, including a leaf change such as single-service growing into fan-out), a pattern change, a delivery-mode change, a consumption-endpoint change, or a queue access-type change. That list is the core; also treat an unlisted change as topology when your judgment says it alters the message flow between the apps and the broker. Everything else is a mechanical edit. + +Three gates hold on every path: + +- **The design-contract gate.** Implement mode never starts without a design contract, and never invents design values. Exactly three sources satisfy it: (1) a summary Design mode confirmed in this session: use it as-is, with no re-confirm; (2) an explicit summary the developer supplied in chat (the eight fields or an equivalent statement of them): use it directly; (3) a saved `solace-design.md` in the project: restate it in one line and proceed on confirm. A prose build request that merely mentions pattern details is NOT a contract: route it through Design mode, whose fully-specified path derives the summary, echoes it, and takes one confirm. The one carve-out is the mechanical-edit row above. An experienced developer therefore never has to walk the design tree, but never skips the contract either. +- **Implement mode opens with a mandatory door question.** Ask Quickstart, Solace Suggested, or Custom, before you request or accept ANY broker details (`python/implement-mode.md` Step 0). Ask it even when a `config.json` with credentials already exists, even when a local broker is already running, and even when the requested app is bigger than the canonical generated shape; none of those answers the question. A confirmed design summary does not answer it either: the door question always follows the design contract, never merges into it. +- **Environment discovery never answers a question.** A running broker container, an existing config file, or found credentials are facts to report, not answers to consume. Report what you found, then still ask which broker the developer wants to target. + +## Python package + +The single Solace dependency for this API is the PyPI package `solace-pubsubplus`, imported as `solace.messaging`. Resolve the latest release at generation time (the no-hardcoded-versions invariant); never pin a `solace-pubsubplus` version in skill content. Resolve it from the authoritative PyPI JSON API with exactly this command, and read `info.version`: + +```bash +curl -s https://pypi.org/pypi/solace-pubsubplus/json \ + | python3 -c 'import json, sys; print(json.load(sys.stdin)["info"]["version"])' +``` + +The package's `requires_python` value on PyPI is an install floor, not a support statement. Solace supports only the Python versions that are in active support and also have security update support (see `python/prerequisites.md`). + +## Canonical doc links + +Python-specific grounding. Each page below is a live `docs.solace.com` `.md` URL, WebFetched on demand; the live exceptions are listed separately because they have no `docs.solace.com` `.md` form. + +- [Python API Home](https://docs.solace.com/API/Messaging-APIs/Python-API/python-home.md): entry point for the Python API. +- [Python API Developer Guide](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Dev-Guide.md): the index of the developer guide pages. +- [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md): building and connecting the `MessagingService`. +- [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) and [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md): the guaranteed pub/sub pages. +- [Creating Queues with the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Create-Queues.md): queue provisioning from the client. +- [Supported Environments](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-supported-Environments.md): Python versions, platforms, and OpenSSL. +- [Solace C API Best Practices](https://docs.solace.com/API/API-Developer-Guide/C-API-Best-Practices.md): the Python API has no best practices page of its own. It wraps the C API, so the reference samples ground their callback, reconnect, acknowledgement, and time-to-live practices here. +- For the broader Solace grounding, see the shared [Solace Core Concepts](https://docs.solace.com/Get-Started/event-mesh-basics.md) page. + +Live exceptions (these have no `docs.solace.com` `.md` form; use the live URL directly): + +- [Python API reference](https://docs.solace.com/API-Developer-Online-Ref-Documentation/python/index.html) (Sphinx HTML, one large page per sub-package) +- [Python API Release Notes](https://products.solace.com/download/PYTHON_API_RN) +- [solace-pubsubplus on PyPI](https://pypi.org/project/solace-pubsubplus/) (its project description carries the `multiprocessing` restriction, which no `docs.solace.com` page states) + +## Mode and reference files (read on-demand only) + +- `python/prerequisites.md`: broker acquisition and the supported environments (route here first if the developer has no reachable broker). +- `python/design-mode.md`: choose and confirm the messaging pattern and topology before any code is generated; every new build without a design contract routes here first. +- `python/implement-mode.md`: generate a runnable Python project from a confirmed design; its Step 4 dispatches onto the per-leaf wiring files below. +- `python/implement-guaranteed-pubsub.md`: the Guaranteed Pub/Sub leaf wiring (read from implement-mode Step 4). +- `python/python-guaranteed-publisher-sample.py`: best-practices publisher sample (basic-auth connect, PERSISTENT publish to a topic, publish receipt handling, graceful shutdown). +- `python/python-guaranteed-subscriber-sample.py`: best-practices receiver sample (provisions a durable queue plus a topic subscription, CLIENT-ack receiver). +- `python/python-solace-connection-config.py`: the shared connection-config helper every generated app uses. +- `python/python-direct-*-sample.py`: the direct pub/sub and direct request-reply samples. Implement mode does not generate those leaves yet (see `python/implement-mode.md` Step 4). diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/design-mode.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/design-mode.md new file mode 100644 index 0000000..9975cdb --- /dev/null +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/design-mode.md @@ -0,0 +1,108 @@ +# Design Mode + +Help the developer choose the right Solace messaging pattern and topology for a Python application before any code is generated, grounded in canonical Solace docs. Work through the steps in order. Read this file's grounding links only as each step needs them; link the canonical doc and state the decision in one line, never paraphrase doc content. + +The output of this mode is a filled-in design summary the developer can carry into Implement mode. + +## Step 0: Confirm broker access and grounding + +Before triaging anything, confirm the developer has access to a reachable broker. Confirm that they HAVE one; Design mode only records the broker TYPE (for example, Solace Cloud) in the summary, so do NOT ask them to paste connection details (host, message VPN, client username, password) into the chat. Those values are handled later in Implement mode, where they go into a gitignored `config.json` or, on Quickstart, the command line. A broker discovered running in the environment (a local container, an existing config file) is a fact to report, never an answer: still confirm which broker the developer wants to target. + +If the developer has no reachable broker, route through `prerequisites.md` first (broker acquisition, Solace Cloud recommended), then return here. + +## Step 1: Read the prompt for stated intent before asking anything + +Design mode is ask-or-recommend, not interrogate-first. Before walking the tree, read what the developer already told you and try to resolve a pattern up front. Match the prompt against this signal table first; it is the deterministic path. + +| Stated intent in the prompt | Recommended leaf (or branch to lock if partial) | +|---|---| +| "request a reply", "needs a correlated response", "ask and wait for an answer" | Request-Reply (Direct) (the delivery question still applies; see the no-guaranteed-request-reply rule below) | +| "fan out to many services", "broadcast to several independent apps", "every team gets its own copy" | Guaranteed Pub/Sub (fan-out) | +| "competing workers", "share the load across consumers", "scale out processing of one stream" | Guaranteed Pub/Sub (single service, non-exclusive) | +| "one active consumer", "strict ordering", "only one worker at a time" | Guaranteed Pub/Sub (single service, exclusive) | +| "fire-and-forget telemetry", "high-rate metrics, occasional loss is fine", "live ticks, no persistence" | Direct Pub/Sub | +| "must not lose messages", "survive consumer downtime", "persistent delivery" | a Guaranteed leaf (let the remaining questions pick which) | + +If a row matches, or if your own judgment maps an unmatched prompt cleanly to one of the five leaves, take the recommend path. Do not default to a full interrogation just because the prompt is not a verbatim table row; fall back to your own judgment to pick the closest leaf, then confirm. + +**Recommend path (intent is clear).** Recommend the leaf in ONE line and confirm, for example "Sounds like Guaranteed Pub/Sub (fan-out), confirm?", then proceed on the developer's confirm. One line plus one confirm, not a rationale paragraph and not a silent zero-confirm jump. + +**Fully-specified path (the prompt answers every applicable tree question).** When the prompt resolves the leaf AND answers every Step 2 question that applies to that leaf, skip the separate leaf confirm. Derive the full summary, present it (Step 4), and let the close-out question carry the single confirm: one confirm covers the leaf and the design. Do not ask a leaf confirm and then a close-out confirm; that is two questions where the prompt earned one. + +**Partial-intent path (intent is partial).** When the prompt answers some branches but not all, lock the branches the prompt already answers and ask ONLY the remaining tree questions, in the Step 2 order, skipping every question the prompt already settled. Reach a leaf, then confirm it the same one-line way. + +**Unclear path (no stated intent).** When the prompt states no usable intent, walk all four tree questions in Step 2 order to reach a leaf. + +**Variant of a leaf (the prompt states a requirement the base shape does not meet).** A leaf is a pattern FAMILY, not a fixed topology. Alongside the intent match, read the prompt for reliability or topology requirements that the chosen leaf's DEFAULT shape does not satisfy (for example "the consumer must dedupe redeliveries"). When you find one, the leaf still resolves to one of the five strings verbatim, but the design is a VARIANT of that leaf: its default sample shape is adapted to meet the requirement. Name the deviation in the same one-line confirm and carry the actual topology into the Step 4 summary. Keep a variant within the same pattern family and to topology or reliability knobs the canonical docs ground; if a requirement leaves the Solace topic and queue publish and subscribe model, or needs behavior the docs cannot ground, say so and treat it as off-catalog rather than inventing it. + +## Step 2: Walk the decision tree to a leaf + +Ask only the questions the prompt left open, in this order. Before you answer each question you do walk, WebFetch its grounding page first (even when the prompt pre-answers the question), then link the canonical doc and state the decision in one line, quoting one line from the fetched page; never paraphrase guidance the page does not contain. A page you did not fetch in this session must not appear in the summary's Grounding docs field. + +1. **Interaction shape: request-reply or publish/subscribe?** Grounded in [Message Exchange Patterns](https://docs.solace.com/Get-Started/message-exchange-patterns.md). Decision: a request that expects a correlated reply is Request-Reply; an event fanned out to one or many independent consumers is publish/subscribe. +2. **Delivery guarantee: direct or guaranteed?** This ONE question applies to both interaction shapes, so ask it next regardless of the answer to question 1. Grounded in [Message Delivery Modes](https://docs.solace.com/Get-Started/message-delivery-modes.md). Decision: direct is fire-and-forget with no broker-side persistence, for high-rate flows that tolerate occasional loss; guaranteed (persistent) is persisted and survives consumer downtime. +3. **Number of consuming services (guaranteed publish/subscribe branch only).** Decision: a single consuming service is one logical consumer of the stream; fan-out is several independent services that each need their own full copy of every event. +4. **Competing consumers (guaranteed publish/subscribe, single service branch only).** Grounded in [Topic Endpoints and Queues](https://docs.solace.com/Get-Started/topic-endpoints-queues.md). Decision: exclusive gives one active consumer at a time (strict ordering, hot standby); non-exclusive lets multiple competing workers share the queue and split the load. + +Questions 3 and 4 apply ONLY on the guaranteed publish/subscribe branch. Request-reply and direct publish/subscribe resolve at the delivery-guarantee answer and never reach them. + +**No guaranteed request-reply in the Python API.** When question 1 resolves to Request-Reply and question 2 resolves to guaranteed, there is no Python leaf for it. WebFetch [Request-Reply Messaging in the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Request-Reply.md) and quote its note that request-reply messaging can only be used with direct messages in the Python API. Then give the developer the choice in one question: Request-Reply (Direct), which keeps the request-reply shape and accepts direct delivery, or a Guaranteed Pub/Sub leaf, which keeps persistence and leaves the reply correlation to the application. Never invent a guaranteed request-reply leaf and never record `Request-Reply (Guaranteed)` in a Python summary. + +## Step 3: Resolve to one of the five leaves + +The tree resolves to exactly five leaves. Spell the chosen leaf in the summary exactly as written here; these strings are the contract Implement mode reads. + +- **Request-Reply (Direct)**. Request-reply over direct messaging. +- **Direct Pub/Sub**. Publish/subscribe over direct messaging, fire-and-forget. +- **Guaranteed Pub/Sub (single service, exclusive)** GENERATED. One durable queue, exclusive access, one active consumer at a time. +- **Guaranteed Pub/Sub (single service, non-exclusive)** GENERATED. One durable queue, non-exclusive access, competing workers share the load. +- **Guaranteed Pub/Sub (fan-out)** SNIPPET. One durable queue plus a topic subscription per independent consuming service. Implement mode reuses the single-service guaranteed generator, so there is no dedicated fan-out sample; the leaf is realized by repeating the single-service queue-and-subscription setup once per consuming service. + +The two direct leaves are valid designs, and Design mode confirms and saves them like any other leaf, but Implement mode does not generate them in this release (see `implement-mode.md` Step 4). When the confirmed leaf is one of them, say so in the Step 4 close-out instead of offering to move into Implement mode. + +Inside the guaranteed publish/subscribe single-service leaf, the exclusive versus non-exclusive choice is realized as a queue-factory SNIPPET, not a separate sample: the same generated app builds an exclusive or a non-exclusive durable queue based on the developer's answer to question 4. + +**A leaf can carry a variant.** The five strings name the pattern family; a design may adapt a leaf's default topology to a stated requirement (the variant path in Step 1). In every case the `Pattern` string stays one of the five verbatim, and the deviation is recorded in the topology fields of the Step 4 summary (for example the `Consumption endpoint`), never by inventing a sixth leaf string. + +**Topic hierarchy and queue name.** Always publish to topics; never address a queue directly. Do NOT ask the developer to choose or design the topic string. Instead, apply the co-installed solace-topic-best-practices skill (which reads the canonical Topic Architecture Best Practices page live) to the developer's use case, derive the recommended topic hierarchy, and recommend it in ONE line; use that recommended topic without making the developer pick it. Do not design the hierarchy here by hand and do not restate that skill's guidance. Likewise, for any leaf that consumes from a durable queue, derive the queue name automatically rather than asking: follow the `q.`-prefixed dotted convention (for example the topic `acme/orders/new` maps to the queue `q.acme.orders`), recommend it in one line, and record it. The developer overrides the recommended topic or queue name ONLY if they explicitly ask to. + +**Traps to avoid:** + +- **Publishing direct-to-queue instead of pub/sub.** Publish to a topic; let the durable queue carry a topic subscription. ([Topic Endpoints and Queues](https://docs.solace.com/Get-Started/topic-endpoints-queues.md).) +- **Text payload instead of binary.** Default to a binary (`bytearray`) payload; the receiver reads it back with `get_payload_as_bytes()`. +- **Direct where guaranteed is required.** Use persistent delivery whenever the design cannot tolerate message loss or must survive consumer downtime. ([Message Delivery Modes](https://docs.solace.com/Get-Started/message-delivery-modes.md).) +- **A topic endpoint instead of a queue-with-topic-subscription.** Promote the durable queue. ([Topic Endpoints and Queues](https://docs.solace.com/Get-Started/topic-endpoints-queues.md).) +- **A guaranteed request-reply leaf.** The Python API has none; follow the rule at the end of Step 2. + +## Step 4: Emit the design summary + +Fill in this ONE unified design summary every run, in chat, with the confirmed values. The same eight fields appear every run regardless of which leaf the tree reached, so Implement mode reads one stable structure. Use explicit `n/a` for any field the chosen pattern does not apply. The title names the API, so a saved `solace-design.md` routes back to the Python entry file. + +``` +Solace Python Design Summary +- Pattern: +- Delivery: +- Access type: +- Topic: +- Consumption endpoint: +- Auth: Basic username/password +- Broker: Solace Cloud +- Grounding docs: +``` + +The eight fields are fixed: Pattern, Delivery, Access type, Topic, Consumption endpoint, Auth, Broker, Grounding docs. Formatting is at your discretion, but always present all eight, every run, with explicit `n/a` where a field does not apply. The `Pattern` value stays one of the five leaf strings verbatim even for a variant; the deviation lives in the topology fields, never in a new leaf string. + +Field semantics: + +- **Pattern.** Exactly one of the five leaf strings, spelled verbatim: `Request-Reply (Direct)`, `Direct Pub/Sub`, `Guaranteed Pub/Sub (single service, exclusive)`, `Guaranteed Pub/Sub (single service, non-exclusive)`, `Guaranteed Pub/Sub (fan-out)`. This is the contract Implement mode reads, so the spelling is not cosmetic. +- **Delivery.** `Direct` for the two direct leaves; `Guaranteed (persistent)` for the three guaranteed leaves. +- **Access type.** `Exclusive` or `Non-exclusive` for the two guaranteed single-service leaves; `n/a` for every other leaf. Surface it explicitly even though it is also encoded in the Pattern string. +- **Consumption endpoint.** The pattern-specific place the consuming side reads from. Record the ACTUAL topology the design requires; the defaults are `direct topic subscription` for Direct Pub/Sub, `durable queue + topic subscription` for the three guaranteed leaves, and `API-managed reply (RequestReplyMessagePublisher)` for Request-Reply (Direct). For the durable-queue leaves, include the auto-derived queue name (for example `durable queue q.acme.orders + topic subscription`) so Implement mode reads the concrete queue name here. For a variant, record the variant topology here with a short inline reason. +- **Topic.** The recommended topic string or hierarchy, derived by applying the solace-topic-best-practices skill to the use case. +- **Auth.** Basic username/password. +- **Broker.** Default to `Solace Cloud` and record it without asking. Switch to another type (Software Broker or Appliance) ONLY if the developer explicitly insists. +- **Grounding docs.** ONLY the pages this session actually WebFetched. Never list a page you did not fetch. Write `none fetched` when no page was fetched. + +After presenting the summary in chat, close Design mode explicitly so the developer knows exactly what happens next; do NOT just display the summary and stop. On the fully-specified path (Step 1) this close-out is the ONLY confirm of the run; it carries the leaf confirmation too. If the design is a variant of its leaf, state the deviation and its rationale in one line here too. Ask them directly, in one step, both whether they are happy with this design AND whether to save it to `solace-design.md` in their project (for example: "Happy with this design? If so, I can save it to `solace-design.md` and move into Implement mode to generate the runnable Python project."). Write the file only on their OK; never write it unprompted. Once they confirm, state the next step plainly: the work moves into Implement mode, which generates the runnable Python project from this summary. Implement mode OPENS with its own Step 0 door question (Quickstart, Solace Suggested, or Custom), asked before any broker details are requested or accepted; an existing `config.json` or a running broker does not answer it, so ask it on the way in. + +For a direct leaf (Direct Pub/Sub or Request-Reply (Direct)), the close-out differs: offer to save the summary to `solace-design.md`, and say in one line that generating a Python project for this leaf is not yet available in this release, so the saved design is ready for when it is. Do not offer to move into Implement mode, do not generate code from the direct samples by hand, and do not offer to write the code another way (from scratch, "outside the generator", or as unverified code). When the developer asks for the code anyway, repeat that generation for this leaf is not yet available and point them at the [Python tutorials](https://tutorials.solace.dev/python/); write no code. diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-guaranteed-pubsub.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-guaranteed-pubsub.md new file mode 100644 index 0000000..bc4e62a --- /dev/null +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-guaranteed-pubsub.md @@ -0,0 +1,53 @@ +# Implement: Guaranteed Pub/Sub leaves + +The per-leaf wiring for the three Guaranteed Pub/Sub leaves of the Python API. Read this file from `implement-mode.md` Step 4 when the `Pattern` field is `Guaranteed Pub/Sub (single service, exclusive)`, `Guaranteed Pub/Sub (single service, non-exclusive)`, or `Guaranteed Pub/Sub (fan-out)`. The shared Step 4 rules in `implement-mode.md` (the disclaimer header, the shared connection helper, the fixed file names, the concurrency rules, the variant discipline, the demo-harness rule, the comments rule, and the Quickstart fail-fast rule) apply here unchanged. + +## Guaranteed pub/sub flow + +The skill ships two best-practices reference samples for this flow. Read them by their exact relative paths and adapt each into its own generated script. There is no merge and no third skeleton: `guaranteed_publisher.py` is a near-verbatim adaptation of the publisher sample, and `guaranteed_subscriber.py` is a near-verbatim adaptation of the subscriber sample. + +- `python-guaranteed-publisher-sample.py`: basic-auth connect, persistent binary publish to a topic from a message template that sets the time-to-live once, publish receipt (ACK/NACK) handling, capacity-bounded back pressure, service event listeners registered before `connect()`, and a graceful SIGINT/SIGTERM shutdown that drains outstanding receipts. +- `python-guaranteed-subscriber-sample.py`: basic-auth connect, durable queue plus topic subscription provisioned at start, a client-ack receiver that ACKs only after processing, receiver state and termination handling, service event listeners registered before `connect()`, and a graceful SIGINT/SIGTERM shutdown. + +Each script builds its OWN `MessagingService` (two services, one per script, matching the samples). Keep the samples' structure: the shared state dataclass, `setup_solace(...)` that builds the service and the receiver or publisher before any connection exists, `connect_solace(...)`, the main loop, and `teardown_solace(...)` in `main()`'s `finally`. Keep every listener class; they are part of the best-practices contract and must not be stripped during adaptation. + +**Ground before you write.** Before writing either script, WebFetch [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) and [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) in this session (SKILL.md Invariant 2). The second page also grounds the concurrency rules in `implement-mode.md` Step 4. + +The three guaranteed pub/sub leaves generate the same publisher and subscriber. The exclusive versus non-exclusive choice is one queue-factory line (read the `Access type` summary field). The fan-out leaf reuses the single-service subscriber once per consuming service (see Fan-out below). + +## Subscriber script (`guaranteed_subscriber.py`) + +The subscriber provisions the durable queue and the topic subscription, and it is the long-running process the developer runs FIRST. The publisher does NOT provision anything. Start the file with the disclaimer header, then wire the subscriber in this sequence, each step grounded in the pages linked in `implement-mode.md`'s Grounding references: + +1. Build the connection properties with `load_service_properties(args, APP_NAME)`, then the service with `MessagingService.builder().from_properties(properties).build()` (on Quickstart without the reconnection strategy call, per the fail-fast rule). +2. Build the queue from the design summary's `Consumption endpoint` queue name and `Access type` field (see the access-type snippet below). +3. Build the persistent receiver as the sample does: `create_persistent_message_receiver_builder()` with `with_missing_resources_creation_strategy(MissingResourcesCreationStrategy.CREATE_ON_START)`, `with_subscriptions([TopicSubscription.of(topic)])` for the design summary's topic, `with_message_client_acknowledgement()`, and `with_activation_passivation_support(...)`, then `build(queue)`. Keep the receiver termination listener and register the three service event listeners BEFORE `connect()`. See [Creating Queues with the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Create-Queues.md). +4. `messaging_service.connect()`. Immediately after `connect()` returns, emit the connect marker: `trace("VERIFY: CONNECTED")`. +5. `receiver.receive_async(handler)` BEFORE `receiver.start()`, as the sample orders them, then `receiver.start()`. Immediately after `start()` returns without raising, emit the queue-bound marker: `trace("VERIFY: QUEUE_BOUND")`. Keep the sample's `PubSubPlusClientError` branch around `start()`: a client that may not create endpoints fails there, and the error message tells the developer what to do. +6. In the handler's `on_message`, keep the sample's try/except/finally and call `receiver.ack(message)` only after processing. Immediately after `ack()` in the `finally`, emit the round-trip marker: `trace("VERIFY: MESSAGE_RECEIVED")`. See [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md). +7. KEEP the long-running wait loop, the SIGINT/SIGTERM handler that only sets the shutdown event, and `teardown_solace(...)` with `terminate(grace_period)` and `disconnect()`. The subscriber is the SIGINT target, and its shutdown line (`Shutdown signal received ...`) stays a `trace(...)` call. + +**Access type (exclusive vs non-exclusive): one line, not a separate sample.** The `Access type` field picks the queue factory: + +```python +queue = Queue.durable_exclusive_queue(queue_name) # one active consumer / ordered failover +# queue = Queue.durable_non_exclusive_queue(queue_name) # competing consumers share the load +``` + +When the `Access type` field reads exclusive, keep the `durable_exclusive_queue` line: only one bound receiver is active at a time, and the receiver state listener reports the passive standby. When it reads non-exclusive, use the `durable_non_exclusive_queue` line instead, so several subscriber processes bound to the same queue share the messages. Emit exactly one of the two lines. Ground the choice in [Topic Endpoints and Queues](https://docs.solace.com/Get-Started/topic-endpoints-queues.md). + +**Fan-out.** On the fan-out leaf, every consuming service gets its own durable queue with the same topic subscription, so each service receives its own full copy of every event. Generate one subscriber script per consuming service: `guaranteed_subscriber.py` for the first service, and `guaranteed_subscriber_.py` for each further service, each with its own queue name from the design summary and an exclusive durable queue. Hand back one run command per subscriber in Step 5, and add each script to the import smoke. + +## Publisher script (`guaranteed_publisher.py`) + +The publisher only connects and publishes; it does NOT provision the queue or the subscription. It must exit on its own after the broker acknowledges its messages, so the developer does not have to interrupt it. Start the file with the disclaimer header, then wire the publisher in this sequence: + +1. Build the connection properties and the service exactly as the subscriber does (step 1 above), then the publisher with `create_persistent_message_publisher_builder().on_back_pressure_wait(PUBLISH_BUFFER_CAPACITY).build()`. Register the three service event listeners BEFORE `connect()`. +2. `messaging_service.connect()`. Immediately after `connect()` returns, emit the connect marker: `trace("VERIFY: CONNECTED")`. +3. `publisher.start()`, then `publisher.set_message_publish_receipt_listener(...)` AFTER `start()`, as the sample orders them. In the receipt handler's ACK branch, emit the publish-ACK marker: `trace("VERIFY: PUBLISH_ACKED")`. CRITICAL: the sample logs the ACK with `logger.debug`, which the INFO-level logging never shows, so the marker would never appear. Emit it through `trace(...)`, not through the logger. In the NACK branch, keep the sample's warning and also set the shared state's `exit_code` to 1, so a rejected message makes the publisher exit non-zero. Pass the shared state to the handler to do this. +4. Publish persistent messages to the design summary's topic as the sample does: one `message_builder()` template with `PERSISTENT_TIME_TO_LIVE` set once, `build(payload, additional_message_properties={APPLICATION_MESSAGE_ID: msg_id})` per message, and `publish(message, topic, user_context=msg_id)`. See [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md). +5. EXIT on its own: replace the sample's continuous publish loop with the application's own messages, or with one message on a try-it run, then return from the publish step. `teardown_solace(...)` in `main()`'s `finally` calls `terminate(grace_period)`, which sends what is buffered and waits for the broker's receipt of every published message, so the `VERIFY: PUBLISH_ACKED` marker prints before the script exits. Drop the stats thread with the loop. Keep the SIGINT/SIGTERM handler, so Ctrl-C during a back pressure wait still reaches teardown. + +## The marker contract + +These four `VERIFY:` marker strings (`VERIFY: CONNECTED`, `VERIFY: PUBLISH_ACKED`, `VERIFY: QUEUE_BOUND`, `VERIFY: MESSAGE_RECEIVED`) are the same strings the JCSMP guaranteed pub/sub leaf emits, character for character. They are read PER PROCESS: the subscriber output carries `QUEUE_BOUND` and `MESSAGE_RECEIVED`, the publisher output carries `PUBLISH_ACKED`, and both print `CONNECTED`. Until the Python verify script lands, the developer reads them from the two terminals (`implement-mode.md` Step 5); the verify script will read the same strings, so never change one. diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-mode.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-mode.md new file mode 100644 index 0000000..a545c0f --- /dev/null +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-mode.md @@ -0,0 +1,250 @@ +# Implement Mode + +Generate a complete, runnable Python project from a confirmed design summary. The canonical output is TWO scripts (a publisher and a subscriber) plus the shared connection-config helper in ONE project directory with its own virtual environment. Each script is a near-verbatim adaptation of its reference sample. Near-verbatim adaptation of two samples is more reliable than merging them into one program, and a real deployment lifts each script into its own service or project. + +Implement mode is NOT only for the canonical shape. A web app, a dashboard, a service, or any embedded shape where the Python messaging layer lives inside a larger application follows the SAME steps: the same door question, the same leaf wiring for the messaging layer, the same generation outputs, and the same verification fallback. Building an off-catalog shape never waives a step in this file. + +Work through the steps in order. Read this file's grounding links only as each step needs them. Link the canonical doc and state the decision in one line, never paraphrase doc content. Resolve every dependency version at generation time; pin nothing in skill content (SKILL.md Invariant 3). + +**Coverage in this release.** Implement mode generates the three Guaranteed Pub/Sub leaves on the Quickstart door. The Direct Pub/Sub and Request-Reply (Direct) leaves (Step 4) and the Solace Suggested and Custom doors (Step 0) are not yet available for Python. The Python API does not bundle a verify script yet, so every run ends in the compile-only fallback of Step 5. + +## Generation outputs: the file contract + +Every Implement run writes ALL of these as generation output, on every app shape. None of them is gated on consent, and none of them waits for the end of the session: + +1. The generated scripts for the chosen leaf, plus the shared `solace_connection_config.py` helper. Every source file starts with the AI-assisted disclaimer header (Step 4). +2. `requirements.txt` with the `solace-pubsubplus` version resolved per Step 3. +3. `config.example.json` (placeholders) and a `.gitignore` that ignores `config.json` and the virtual environment (Step 4). +4. A tailored `solace-verification-checklist.md` at the project root (tailoring rules in Step 6; the WRITE happens here at generation time, like the scripts). + +There is no `verify.sh` and no `verify-hooks.sh` in a Python project yet. Do not copy the JCSMP `verify.sh` into a Python project: it drives Maven and cannot run Python scripts. + +## Step 0: Ask Quickstart, Solace Suggested, or Custom, before any broker details + +This is the FIRST question, and you ask it before you request or accept ANY broker connection details. Ask the developer plainly which of three doors they want: Quickstart, Solace Suggested, or Custom. Ask it as a direct question and wait for the answer. Frame the three doors honestly: Quickstart is a learning and try-it path to see a round-trip work, NOT a deployment target; Solace Suggested is the hardened baseline Solace suggests as a starting point (TLS, dead message queue eligibility, HA failover, separate projects), and Solace suggests it without guaranteeing it is ready for deployment; Custom lets the developer tick exactly which hardening knobs to include. State in the same question that Solace Suggested and Custom are not yet available for the Python API in this release. + +Three things do NOT answer this question, and none of them waives it: + +- **An existing `config.json`**, even one that already holds real credentials. Ask anyway. +- **A running local broker discovered in the environment.** Report what you found, then ask both this question and which broker to target. +- **An app shape larger than the generated two-script project** (a web app, a dashboard, a service). The doors still govern the messaging layer and the credential placement. Ask anyway. + +The doors: + +- **Quickstart.** Each script builds its connection through the shared `solace_connection_config.py` helper, which reads the connection properties from a `config.json` in the working directory when present and otherwise falls back to the command-line arguments (` [password]`). Quickstart adds no TLS hardening and has no TLS requirement: it connects with whatever scheme the developer's host carries, and a plaintext `tcp://` dev broker is a normal Quickstart target. Generate the scripts without per-stage preview gates. Quickstart output fails fast (the fail-fast rule in Step 4). +- **Solace Suggested or Custom.** Not yet available for Python. Say so plainly, without listing hardening steps from memory. Offer the developer two honest choices: Quickstart now, framed as a learning run, or stop here with the design saved to `solace-design.md` for when the hardened doors are available. Never generate a Quickstart project under the Solace Suggested or Custom name. + +State plainly that a real deployment lifts each script into its own service or project. Basic username/password is the only auth this journey generates; ground it in the authentication section of [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md). + +## Step 1: Confirm broker access and grounding + +With the door chosen, confirm the developer has access to a reachable broker, and confirm WHICH broker they want to target. Confirm that they HAVE one; do NOT ask them to paste connection details into the chat. When Design mode already confirmed broker access in this session (its Step 0), do not re-ask whether a broker exists; ask only WHICH broker to target. A broker discovered in the environment is a fact to report while asking, never an answer. The credential VALUES reach the app through a `config.json` when present, otherwise as command-line arguments at run time; the skill itself never needs the values typed into the chat. + +If the developer has no reachable broker, route through `prerequisites.md` first, then return here. + +Grounding catch-up: if the design summary names a page in its Grounding docs field that this session has not WebFetched, fetch it now before generating against it (SKILL.md Invariant 2). + +## Step 2: Establish the design as the input contract + +Implement mode works from the unified design summary of eight fields that `design-mode.md` defines: Pattern, Delivery, Access type, Topic, Consumption endpoint, Auth, Broker, Grounding docs. The `Pattern` field is one of the five Python leaf strings. + +The summary is a HARD precondition (the design-contract gate in `python.md`): Implement mode never proceeds past this step without one, and never invents design values. Exactly three sources satisfy it: + +- A summary Design mode confirmed in this session: use it as-is, with no re-confirm. +- An explicit summary the developer supplied in chat (the eight fields or an equivalent statement of them): use it directly. If it omits a derivable field, derive it rather than asking (the topic via the solace-topic-best-practices skill, the queue name via the `q.`-prefixed convention), the same way Design mode does. +- A saved `solace-design.md` in the developer's project: read it, restate it in one line, and proceed on their confirm. A saved summary titled `Solace JCSMP Design Summary` belongs to the JCSMP API: say so and ask whether the developer wants a Python project from it, then re-confirm it through Design mode, because a JCSMP leaf such as `Request-Reply (Guaranteed)` has no Python equivalent. + +If none of the three exists, route through Design mode (`design-mode.md`) and return with its confirmed summary. The only carve-out is the topology rule in `python.md`: a mechanical edit to an existing app proceeds without a summary. + +## Step 3: Resolve the version, then write requirements.txt and the virtual environment + +### Resolve the latest solace-pubsubplus version at generation time + +Never hardcode a `solace-pubsubplus` version in this skill or carry one in your memory. Resolve the latest release from the AUTHORITATIVE PyPI JSON API at generation time: + +```bash +curl -s https://pypi.org/pypi/solace-pubsubplus/json \ + | python3 -c 'import json, sys; print(json.load(sys.stdin)["info"]["version"])' +``` + +If the network query fails, do not guess a version. Ask the developer for the version they want, or write the bare requirement `solace-pubsubplus` and let pip resolve it, and tell the developer which path you took. + +### Write requirements.txt and create the virtual environment + +- **Single Solace dependency: `solace-pubsubplus`.** Write it as `solace-pubsubplus==`, so the generated project installs exactly the release it was generated against. That pin is generation output resolved at generation time, the same way the JCSMP pom carries a resolved `sol-jcsmp` version; it is never a version written in skill content. Add no other dependency: the samples need only the standard library besides the Solace API. +- **Python version.** Check `python3 --version` in the developer's environment. When it reports a Python that is no longer in active or security support, tell the developer, link `prerequisites.md`'s Supported environments section, and do not choose an older interpreter for them. +- **Virtual environment.** Create it at the project root and install into it: `python3 -m venv .venv`, then `.venv/bin/pip install -r requirements.txt` (on Windows, `.venv\Scripts\pip`). Every run command this mode hands back uses the virtual environment's interpreter (`.venv/bin/python`), never the system `python3`. +- **Logging.** The samples log API events through the standard `logging` module, configured once with `logging.basicConfig(level=logging.INFO, ...)` in the `__main__` block. Keep that configuration in every generated script. Never raise the level above INFO, because the API's own connect and reconnect diagnostics travel the same channel. + +## Step 4: Dispatch on the Pattern leaf, then generate the scripts + +Generation is keyed on the `Pattern` leaf read in Step 2. Read ONLY the leaf file the design chose, and do not read samples for a leaf the design did not choose. + +| `Pattern` leaf (verbatim) | Leaf wiring file | Samples it reads | +|---------------------------|------------------|------------------| +| `Guaranteed Pub/Sub (single service, exclusive)` | `implement-guaranteed-pubsub.md` | `python-guaranteed-publisher-sample.py` + `python-guaranteed-subscriber-sample.py` | +| `Guaranteed Pub/Sub (single service, non-exclusive)` | `implement-guaranteed-pubsub.md` | `python-guaranteed-publisher-sample.py` + `python-guaranteed-subscriber-sample.py` | +| `Guaranteed Pub/Sub (fan-out)` | `implement-guaranteed-pubsub.md` | `python-guaranteed-publisher-sample.py` + `python-guaranteed-subscriber-sample.py` | +| `Direct Pub/Sub` | not yet available | none | +| `Request-Reply (Direct)` | not yet available | none | + +**The two direct leaves stop here.** Say in one line that generating a Python project for this leaf is not yet available in this release. Offer to save the confirmed summary to `solace-design.md` if it is not saved yet, and stop. Do not read the direct samples and do not adapt them by hand: without a wiring file there is no marker contract and no conformance check for the result. Do not offer to write the code another way (from scratch, "outside the generator", or as unverified code), and do not write it when the developer asks again: the stop is the ENTIRE answer, the same way the Debug redirect is. Point the developer at the saved design and at the [Python tutorials](https://tutorials.solace.dev/python/) instead. + +**Every leaf also generates the shared connection helper.** Read `python-solace-connection-config.py` and generate it as `solace_connection_config.py` (a module name needs underscores). Each script builds its connection dict by calling `load_service_properties(args, APP_NAME)` from that module and passes it to `MessagingService.builder().from_properties(...)`, rather than setting the host, VPN, username, or password inline. + +**Fixed file names.** Generate the scripts as `guaranteed_publisher.py` and `guaranteed_subscriber.py`, the names each sample's docstring gives, next to `solace_connection_config.py` (the fan-out leaf adds one subscriber script per further consuming service, named in `implement-guaranteed-pubsub.md`). The run commands in Step 5 and the checklist address these names; the names of functions, constants, and the `APP_NAME` value inside each script stay your discretion. + +**Disclaimer header on EVERY generated source file.** Stamp the top of each generated `.py` file with these two comment lines, before the module docstring and before any import: + +```python +# AI-assisted code. Review before production use. +# See the verification checklist: solace-verification-checklist.md +``` + +Stamp the header AT WRITE TIME, as the first content of each new file. `requirements.txt`, `config.example.json`, and `.gitignore` are not source and carry no header. The reference samples themselves carry NO disclaimer. This rule is SKILL.md Invariant 4. + +**Embedded and web-app shapes.** When the Python messaging layer sits inside a larger application, the chosen leaf's wiring still governs the messaging layer: the same connection helper, the same service, receiver, and publisher listeners, the same `VERIFY:` markers emitted through a stdout-reaching `trace(...)`, and the disclaimer header on every generated file, messaging or not. The `VERIFY:` markers are NOT demo harness. + +**Write the verification artifacts in this same step.** Alongside the scripts, write the tailored `solace-verification-checklist.md` at the project root (Step 6), `config.example.json`, and `.gitignore`. A session that ends early still leaves the checklist in the project. + +### Concurrency: threads, not asyncio, and no multiprocessing + +The Python API calls application code on its own threads, and the generated code must respect that model. WebFetch [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) before generating a receiver and ground these rules in it: + +- **`receive_async()` is not asyncio.** The page states that `receive_async()` is not an asynchronous coroutine or generator, is not `asyncio` compatible, returns immediately, and invokes the callback on a new Python thread for every message. Never `await` the API and never call it from inside a coroutine. In an asyncio application, keep the Solace objects on their own threads and hand each received message to the event loop with the standard library's `loop.call_soon_threadsafe(...)`; acknowledge the message only after the application has processed it. +- **Handlers return promptly.** Every listener and message handler runs on an API thread. Return promptly and hand heavy work to the application's own thread or queue, as the samples' handler docstrings state. +- **No `multiprocessing`.** The Solace Python API cannot be used in an application that uses the Python `multiprocessing` module (the [solace-pubsubplus PyPI page](https://pypi.org/project/solace-pubsubplus/) states this). To scale consumption, run more independent processes of the subscriber on a non-exclusive queue, each started on its own; never fork workers from one process with `multiprocessing`. + +### Adapting a sample to a variant of its leaf + +The samples are grounded REFERENCE for the API idioms, not templates that must be reproduced line for line. Near-verbatim adaptation is the default WHEN the design summary matches the leaf's base shape. When the summary describes a variant, ADAPT the sample's idioms to honor the approved design; do NOT snap the variant back to the sample's default shape, and do NOT frame the deviation as a "conflict" with the skill. + +Hold every invariant while adapting: keep the shared connection helper, the service event listeners registered before `connect()`, the receipt, termination, and state listeners, the disclaimer header, and the leaf's `VERIFY:` markers. Any NEW named entity the variant introduces must be traceable to a canonical Solace source per Invariant 1: WebFetch the grounding page for it. + +**Guardrail: variant versus off-catalog.** Adapt freely WITHIN the chosen pattern family when the change is a topology or reliability knob that the canonical docs ground and that still emits the leaf's `VERIFY:` markers. If a requirement leaves the Solace topic and queue publish and subscribe model, or needs behavior the canonical docs cannot ground, STOP and tell the developer it is off-catalog rather than inventing it. + +**Preserve the marker contract.** A variant adapts the SHAPE, never the marker strings. It MUST still emit its leaf's `VERIFY:` markers, character for character. Never rename or drop a marker to fit a variant. + +**Demo harness is not application logic.** The samples carry demo-harness elements that exist only to make a standalone run observable: the once-per-second stats loop or thread, the continuous publish loop, and the rotating example payload. When the developer's request describes a real application domain, do NOT carry these into the generated scripts: replace the example payload and the publish loop with the application's real messages and triggers, never add a delay or rate limit between sends that the application does not ask for, and keep only the lifecycle pieces the leaf's steps call for (the SIGINT/SIGTERM shutdown on the long-running role, the self-exit on the foreground role). When the request IS a demo or a try-it run, keeping the harness is fine. Two things are NOT demo harness: the `trace(...)` narration function (keep it; applications replace its single body to route narration elsewhere) and the `VERIFY:` markers, which are emitted through `trace(...)`. The generated `trace(...)` body MUST stay `print(message, flush=True)`, so the markers reach stdout without buffering delays; rerouting it is the developer's step after verification. + +**Comments follow their code.** The samples' comments carry the documented best practices into the code a developer reads long after this session. A generated script that carries a construct from a sample carries that construct's comment too, with only the names adapted. A dropped construct takes its comment with it. Fresh messaging code with no sample twin that applies a documented practice gets a SHORT comment naming the practice, in the samples' comment style. The one-line comments this skill mandates elsewhere (the Quickstart fail-fast posture below) always apply on their paths. + +### Quickstart fail-fast reconnect defaults + +On the Quickstart door, do not carry the samples' reconnect-tuning block into the generated scripts. The samples set a reconnection budget (`with_reconnection_retry_strategy(RetryStrategy.parametrized_retry(...))` with the `RECONNECT_RETRIES` and `RECONNECT_RETRY_INTERVAL_MS` constants) and carry an HA failover comment block; Quickstart generation OMITS the builder call, the two constants, the HA comment block, and the then-unused `RetryStrategy` import, leaving connect and reconnect behavior at the API defaults, so a wrong host, port, or credential in this learning setup fails immediately instead of retrying. Drop ONE short comment at the `MessagingService.builder()` site in each generated script stating that posture: connect and reconnect settings are at the API defaults so failures surface immediately; see the [Solace C API Best Practices](https://docs.solace.com/API/API-Developer-Guide/C-API-Best-Practices.md) for reconnect tuning beyond Quickstart. + +### Scaffold the config files + +Generate two small project files next to the scripts: + +- A committed `config.example.json` that carries placeholder values for the four keys the shared helper requires, spelled as the Solace Python API property names the helper documents. The placeholder values must be obviously fake: + + ```json + { + "solace.messaging.transport.host": "tcp://HOST:55555", + "solace.messaging.service.vpn-name": "YOUR_VPN", + "solace.messaging.authentication.basic.username": "YOUR_USERNAME", + "solace.messaging.authentication.basic.password": "YOUR_PASSWORD" + } + ``` + +- A `.gitignore` at the project root that ignores `config.json`, `.venv/`, and `__pycache__/`, so the real credential-bearing file and the local environment are never committed. + +Tell the developer they opt in to the config file by copying `config.example.json` to `config.json` and filling in their real broker values. Both scripts then read it from the working directory, so they run from the project root. + +## Step 5: Check the project, then hand back the run commands + +The Python API does not bundle a verify script yet, so Implement mode ends in the compile-only fallback on every run, whatever the contents of `config.json` (SKILL.md Invariant 5). Never fabricate a run result and never call a check below a verified round trip. + +1. **Compile check.** Run `.venv/bin/python -m py_compile` on every generated `.py` file. A failure is a code failure: fix it and re-run, up to 3 attempts, then report the evidence and hand the decision to the developer. +2. **Import smoke.** From the project root, run `.venv/bin/python -c "import solace_connection_config, guaranteed_publisher, guaranteed_subscriber"`, adding every further generated module (for example each fan-out subscriber). Each script keeps its `if __name__ == "__main__":` guard, so the import resolves every `solace.messaging` name the scripts use without connecting to a broker. A failure (for example an `ImportError` on a misspelled module path) is a code failure under the same fix loop. +3. **Report and hand back.** Report both results, then hand the developer the exact commands to run against their own broker from the project root, subscriber first so the durable queue and its subscription exist before the first publish: + + ``` + # terminal 1: provisions the queue, binds, and waits (Ctrl-C stops it) + .venv/bin/python guaranteed_subscriber.py [ [password]] + # terminal 2: publishes, waits for the broker's acknowledgement, and exits + .venv/bin/python guaranteed_publisher.py [ [password]] + ``` + + The arguments are needed only when there is no `config.json`. Tell the developer what a passing run prints: the subscriber prints `VERIFY: CONNECTED` and then `VERIFY: QUEUE_BOUND`; the publisher prints `VERIFY: CONNECTED` and `VERIFY: PUBLISH_ACKED` and exits with status 0; the subscriber then prints `VERIFY: MESSAGE_RECEIVED`; Ctrl-C on the subscriber prints `Shutdown signal received` and it exits with status 0. + +**When the developer asks you to run it.** If the developer explicitly asks you to run the two scripts against their broker, print the live-broker heads-up first (SKILL.md Invariant 6: name the host, the one durable queue the subscriber provisions, the two connections, and the messages the publisher sends). Then start the subscriber in the background with its output captured, wait for `VERIFY: QUEUE_BOUND`, run the publisher in the foreground, wait for `VERIFY: MESSAGE_RECEIVED` in the subscriber output, and stop the subscriber with SIGINT. Report the markers you observed as evidence of a manual run. It is not a verify verdict: record it in the checklist as a manual run, and never describe it as a passed verify stage. + +**Close every run with the next steps, stated plainly.** Tell the developer, in order: (1) copy `config.example.json` to `config.json` and fill in their real broker values; (2) run the subscriber, then the publisher, with the commands above, and check the markers. Step 4 already wrote the tailored `solace-verification-checklist.md`; invite the developer to add their own items in its Your additional items section and to record any skill-undelivered concerns under Developer-owned items. + +### Traps to avoid + +- **Improvised verdicts.** A connect that does not raise, or a publish call that returns, proves nothing about the round trip. Judge only by the markers in each script's output. +- **Publishing direct-to-queue instead of pub/sub.** Publish to a topic; let the durable queue carry a topic subscription. See [Creating Queues with the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Create-Queues.md). +- **DIRECT delivery where Guaranteed is required.** Use the persistent publisher for the guaranteed pub/sub journey. See [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md). +- **Seconds instead of milliseconds.** `RetryStrategy.parametrized_retry(retries, retry_interval)` takes its interval in MILLISECONDS per the API reference; a value of `3` means 3 milliseconds. Whenever a generated script sets a retry interval, write it in milliseconds, as the samples' named constants do. +- **Hardcoded credentials.** Never inline the host, VPN, username, or password. Every script builds its properties through the shared helper. +- **Hardcoded versions.** Resolve the `solace-pubsubplus` version at generation time (Step 3); never carry a version from memory, from an old `requirements.txt`, or from the upstream samples repository. +- **Wildcard imports.** Keep every import explicit, as the samples do; never write `from solace.messaging... import *`. +- **The system interpreter.** Run and check everything with `.venv/bin/python`, so the project uses the version it installed. +- **Running the publisher before the subscriber has provisioned the queue.** A persistent message published before the queue subscribes to its topic is dropped and never received. Run the subscriber first. +- **Treating a sample as a spec that overrides the approved design.** See "Adapting a sample to a variant of its leaf" above. + +## Step 6: Tailor the checklist (written at Step 4) and report + +Step 4 writes the tailored `solace-verification-checklist.md` into the generated project as generation output; this step defines its content and adds the report in chat. The Python API has no master checklist template yet; until it lands, write the file from the list below. Resolve each item for the generated code: an item the generation did not deliver moves to Your responsibility. Write it as a Markdown task list (`- [ ]`), ticking only what this run actually checked. + +**Delivered by this generation** + +- Basic-auth connection through the shared `solace_connection_config.py` helper; credentials live only in a gitignored `config.json` or on the command line. +- Service reconnection and interruption listeners registered before `connect()`. +- Persistent publish to a topic with a publish receipt listener; a NACK is logged with the message id. +- Capacity-bounded back pressure on the publisher (`on_back_pressure_wait`). +- Message time-to-live set once on the publisher's message template. +- Durable queue plus topic subscription provisioned at start (`CREATE_ON_START`), with the access type from the design summary (one durable queue per consuming service on fan-out). +- Client acknowledgement only after processing. +- Graceful SIGINT/SIGTERM shutdown: `terminate(grace_period)` and then `disconnect()` on every exit path. + +**Your responsibility (not delivered here)** + +- TLS secure session (`tcps://` with certificate validation); Quickstart does not generate it. +- Dead message queue eligibility on published messages and a dead message queue on the broker. +- Reconnect tuning for HA failover; Quickstart leaves connect and reconnect behavior at the API defaults. +- Separate projects or deployments per role. +- `respect-ttl` enabled on the queue, without which the broker ignores the message time-to-live. +- An administrator-provisioned queue, or the endpoint-create permission for this client, in production. +- A Python version in active or security support, OpenSSL installed on Linux for TLS, and no `multiprocessing` in the application (`prerequisites.md`). + +**Verified by the round-trip** (developer-run until the Python verify script lands) + +- The subscriber prints `VERIFY: CONNECTED` and `VERIFY: QUEUE_BOUND`. +- The publisher prints `VERIFY: CONNECTED` and `VERIFY: PUBLISH_ACKED`, then exits with status 0. +- The subscriber prints `VERIFY: MESSAGE_RECEIVED`. +- Ctrl-C on the subscriber prints `Shutdown signal received` and it exits with status 0. + +**Generation conformance** + +- The disclaimer header is the first two lines of every generated `.py` file. +- `requirements.txt` pins the `solace-pubsubplus` version that PyPI reported as `info.version` at generation time. +- `py_compile` and the import smoke passed (Step 5), with the result recorded here. +- Comments follow their code (Step 4). +- Verification: record `verify script not yet bundled for Python; round trip handed back`, or `manual run` with the observed markers when the developer asked you to run it. + +**Developer-owned items (not delivered by this skill)**: any item the skill does not deliver, recorded as developer-owned rather than skill-satisfied. A consented deviation is recorded here. + +**Your additional items**: open space for the developer to keep growing the list. + +The emitted checklist is an artifact, not source, so it carries NO disclaimer header. Report the same resolution in chat: what landed under each group, the compile and import results, and that the round trip is handed back. + +## Grounding references + +Live `docs.solace.com` `.md` pages (WebFetch on demand): + +- [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md) +- [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) +- [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) +- [Creating Queues with the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Create-Queues.md) +- [Supported Environments](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-supported-Environments.md) +- [Solace C API Best Practices](https://docs.solace.com/API/API-Developer-Guide/C-API-Best-Practices.md) + +Public URLs (not docs.solace.com doc pages; use the live URL directly): + +- [Python API reference](https://docs.solace.com/API-Developer-Online-Ref-Documentation/python/index.html) +- [Python API Release Notes](https://products.solace.com/download/PYTHON_API_RN) +- [solace-pubsubplus on PyPI](https://pypi.org/project/solace-pubsubplus/) diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/prerequisites.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/prerequisites.md new file mode 100644 index 0000000..66c52a8 --- /dev/null +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/prerequisites.md @@ -0,0 +1,29 @@ +# Prerequisites + +Read this when a developer does not yet have a broker to build against, or when the developer's Python environment is in question. Both Design mode and Implement mode route here first if the developer lacks a reachable broker. + +## Table of contents + +- Obtain a broker +- Supported environments +- Learn the Python API basics + +## Obtain a broker + +You need one reachable broker. The primary, recommended choice is [Solace Cloud](https://docs.solace.com/Get-Started/Getting-Started-Try-Broker.md): it is the simplest path to a running broker for a greenfield app and for the run-and-observe round-trip. Default to it unless the developer has a reason not to. + +As brief alternatives, the same page also documents a self-hosted Software Broker (run locally via container or VM) and an Appliance (existing hardware the developer already operates) for developers who cannot use Solace Cloud. + +The skill does not provision or configure the broker. It assumes the broker is reachable and that the developer has connection details (host, message VPN, client username, password). A broker discovered running in the environment (for example a local container) is a fact to report, never an answer: still ask which broker the developer wants to target. + +## Supported environments + +WebFetch [Supported Environments for the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-supported-Environments.md) when the developer's environment is in question, and state the answer from the fetched page. Three points matter for every generated project: + +- **Python version.** Solace supports the Python versions that are in active support and also have security update support; the page links the python.org list. The `requires_python` floor on PyPI is an install floor, not a support statement, so never recommend a Python version that is past its end of life because the package still installs on it. +- **OpenSSL.** TLS connections need OpenSSL. The page says which platforms and package versions bundle it and that the bundled libraries are for developer convenience only, so a Linux deployment installs its own. +- **No `multiprocessing`.** The Solace Python API cannot be used in an application that uses the Python `multiprocessing` module. Only the [solace-pubsubplus PyPI page](https://pypi.org/project/solace-pubsubplus/) states this; cite that page, not a `docs.solace.com` page. + +## Learn the Python API basics + +For a tutorial-style, code-first introduction to connecting, publishing, and receiving with the Python API, point the developer at the official Python tutorials: https://tutorials.solace.dev/python/. These walk the same connect, publish, and subscribe flow the skill builds on. From 64d57b781771a3fcf048ca6d08eb80eddd5e8a21 Mon Sep 17 00:00:00 2001 From: adiel-sammak <71227923+aelsammak@users.noreply.github.com> Date: Fri, 2 Oct 2026 12:34:21 -0400 Subject: [PATCH 2/2] EBP-3105: Apply review fixes to the Python routing and Quickstart path - Scope the JCSMP routing row to Java, so a request with no language gets the JCSMP or Python question - Let a saved solace-design.md title select the API, and restore the input contract sentence in Python design mode - Exclude the Solace Messaging API for Java, which shares MessagingService and PersistentMessageReceiver with Python; add a negative trigger case - Add the environment failure rule to the manual run, so a broker or credential failure never leads to code changes - Scale the live broker heads-up, run commands, and round trip items to every fan-out subscriber; require an identifier for - Keep the requirements.txt pin when PyPI is unreachable - Add header, checklist, and version drift checks to Step 5 - Align the interim checklist with the JCSMP master: doc link per item, command line credentials under Your responsibility, stronger auth and grounding items - Ground the asyncio ack rule in the Consuming Persistent Messages page, and add traps for the parametrized_retry doc comment and PUBLISH_ACKED without delivery - Name the missing verify script and the Request-Reply page in python.md - Fix the Try Broker description in both prerequisites files to match the page --- .../evals/trigger-evals.json | 1 + .../solace-application-development/SKILL.md | 6 +- .../references/jcsmp/prerequisites.md | 2 +- .../references/python.md | 5 ++ .../references/python/design-mode.md | 4 +- .../python/implement-guaranteed-pubsub.md | 4 +- .../references/python/implement-mode.md | 81 ++++++++++++------- .../references/python/prerequisites.md | 2 +- 8 files changed, 65 insertions(+), 40 deletions(-) diff --git a/plugins/solace-messaging-skills/evals/trigger-evals.json b/plugins/solace-messaging-skills/evals/trigger-evals.json index d6f61d9..bcc5107 100644 --- a/plugins/solace-messaging-skills/evals/trigger-evals.json +++ b/plugins/solace-messaging-skills/evals/trigger-evals.json @@ -16,6 +16,7 @@ {"skill":"solace-application-development","prompt":"We've got a warehouse-robotics system where two Python services communicate through our Solace event broker using the solace-pubsubplus library. A RobotDispatcher publishes task assignments and a RobotController consumes them and must not lose any while a robot reboots. How should I structure the messaging between these two Python services?","should_trigger":true,"must_pass":true}, {"skill":"solace-application-development","prompt":"Show me how to publish to a Solace topic from C# using the .NET messaging API.","should_trigger":false}, {"skill":"solace-application-development","prompt":"I'm writing a Go service with the Solace PubSub+ Messaging API for Go. How do I build a persistent message publisher and handle publish receipts?","should_trigger":false}, + {"skill":"solace-application-development","prompt":"I'm on the Solace Messaging API for Java (com.solace:solace-messaging-client). How do I set up a MessagingService and a PersistentMessageReceiver in my Java service to consume from a durable queue?","should_trigger":false}, {"skill":"solace-application-development","prompt":"In our browser dashboard we use the solclientjs library. How do I subscribe to a Solace topic from JavaScript and update the page when messages arrive?","should_trigger":false}, {"skill":"solace-application-development","prompt":"I have a Python asyncio service that calls a few REST APIs concurrently. How do I limit it to five requests in flight at once with a semaphore?","should_trigger":false}, {"skill":"solace-application-development","prompt":"I'm using confluent-kafka in Python. How do I write a consumer that commits offsets only after each message has been processed?","should_trigger":false}, diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/SKILL.md b/plugins/solace-messaging-skills/skills/solace-application-development/SKILL.md index 3176997..b2c0cf5 100644 --- a/plugins/solace-messaging-skills/skills/solace-application-development/SKILL.md +++ b/plugins/solace-messaging-skills/skills/solace-application-development/SKILL.md @@ -1,6 +1,6 @@ --- name: solace-application-development -description: "Use when the user mentions JCSMP, the com.solacesystems:sol-jcsmp library, JCSMPSession, the Solace Python API or its solace-pubsubplus library, MessagingService, PersistentMessageReceiver, publishing to a Solace topic, consuming from a Solace queue, or guaranteed messaging, OR asks broadly to build or design a Solace messaging app in Java or Python (publisher, consumer, pub/sub). This is the single front door for Solace API development: it grounds the work in canonical Solace docs and routes to the right API (JCSMP or Python), helping choose a messaging pattern or generate a runnable app. Do NOT trigger for an explicitly named other Solace API, which beats the generic Java signal: Solace JMS, or the Solace Go, .NET, or JavaScript APIs. Also do NOT trigger for Apache Kafka (any language), broker administration and configuration, Solace Event Portal event modeling, Schema Registry, OpenTelemetry tracing, or topic-architecture and topic-hierarchy design questions (use solace-topic-best-practices)." +description: "Use when the user mentions JCSMP, the com.solacesystems:sol-jcsmp library, JCSMPSession, the Solace Python API (solace-pubsubplus), MessagingService, PersistentMessageReceiver, publishing to a Solace topic, consuming from a Solace queue, or guaranteed messaging, OR asks broadly to build or design a Solace messaging app in Java or Python (publisher, consumer, pub/sub). This is the single front door for Solace API development: it grounds the work in canonical Solace docs and routes to the right API, helping choose a messaging pattern or generate a runnable app. Do NOT trigger for an explicitly named other Solace API, which beats the generic Java signal: Solace JMS, the Solace Messaging API for Java, or the Solace Go, .NET, or JavaScript APIs. Also do NOT trigger for Apache Kafka (any language), broker administration and configuration, Solace Event Portal event modeling, Schema Registry, OpenTelemetry tracing, or topic-architecture and topic-hierarchy design questions (use solace-topic-best-practices)." allowed-tools: Read Write Edit Bash Grep Glob WebFetch --- @@ -31,10 +31,10 @@ Determine which Solace API the developer is building against, then read that API | User intent | API | What to do | |---|---|---| -| Anything about JCSMP, the `com.solacesystems:sol-jcsmp` library, a Java publisher or consumer, publishing to a topic, or consuming from a queue with guaranteed messaging | **JCSMP** | Read `references/jcsmp.md` | +| Anything about JCSMP, the `com.solacesystems:sol-jcsmp` library, `JCSMPSession`, or a Java publisher or consumer (publishing to a topic, or consuming from a queue with guaranteed messaging) | **JCSMP** | Read `references/jcsmp.md` | | Anything about the Solace Python API, the `solace-pubsubplus` library, `MessagingService`, `PersistentMessageReceiver`, or a Python publisher or consumer | **Python** | Read `references/python.md` | -If the API is unclear but the request is clearly about Solace messaging in Java, default to **JCSMP**. If the API is unclear but the request is clearly about Solace messaging in Python, default to **Python**. If the request names no language at all (for example "build me a Solace publisher"), ask one question before anything else: JCSMP (Java) or Python? Files in the workspace (a `pom.xml`, `.py` files, a `requirements.txt`) are facts to report while you ask, never the answer. Mode detection (Design, Implement) then happens in that API's entry file. +If the API is unclear but the request is clearly about Solace messaging in Java, default to **JCSMP**. If the API is unclear but the request is clearly about Solace messaging in Python, default to **Python**. If the request names no language at all (for example "build me a Solace publisher"), ask one question before anything else: JCSMP (Java) or Python? Files in the workspace (a `pom.xml`, `.py` files, a `requirements.txt`) are facts to report while you ask, never the answer. The one exception is a saved `solace-design.md` that the developer asks you to build from: its title (`Solace JCSMP Design Summary` or `Solace Python Design Summary`) names the API, so read that API's entry file, whose design-contract gate restates the summary and takes the developer's confirm. The Solace Messaging API for Java (`com.solace:solace-messaging-client`) shares class names such as `MessagingService` and `PersistentMessageReceiver` with the Python API, but it is neither JCSMP nor Python: when a Java request names that API or its `com.solace` package, say in one line that this skill does not cover it yet, and stop. Mode detection (Design, Implement) then happens in that API's entry file. ## Invariants diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/jcsmp/prerequisites.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/jcsmp/prerequisites.md index f817f62..0902c14 100644 --- a/plugins/solace-messaging-skills/skills/solace-application-development/references/jcsmp/prerequisites.md +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/jcsmp/prerequisites.md @@ -11,7 +11,7 @@ Read this when a developer does not yet have a broker to build against. Both Des You need one reachable broker. The primary, recommended choice is [Solace Cloud](https://docs.solace.com/Get-Started/Getting-Started-Try-Broker.md): it is the simplest path to a running broker for a greenfield app and for the run-and-observe round-trip. Default to it unless the developer has a reason not to. -As brief alternatives, the same page also documents a self-hosted Software Broker (run locally via container or VM) and an Appliance (existing hardware the developer already operates) for developers who cannot use Solace Cloud. +As a brief alternative, the same page also covers setting up a free broker locally, for developers who cannot use Solace Cloud. The skill does not provision or configure the broker. It assumes the broker is reachable and that the developer has connection details (host, message VPN, client username, password). A broker discovered running in the environment (for example a local container) is a fact to report, never an answer: still ask which broker the developer wants to target. diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python.md index dbaeadb..d22ee78 100644 --- a/plugins/solace-messaging-skills/skills/solace-application-development/references/python.md +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python.md @@ -38,6 +38,10 @@ curl -s https://pypi.org/pypi/solace-pubsubplus/json \ The package's `requires_python` value on PyPI is an install floor, not a support statement. Solace supports only the Python versions that are in active support and also have security update support (see `python/prerequisites.md`). +## Verify script + +The Python API does not bundle a verify script yet. Every Implement run ends in the compile-only fallback that `python/implement-mode.md` Step 5 defines, and never claims a verified run (SKILL.md Invariant 5). + ## Canonical doc links Python-specific grounding. Each page below is a live `docs.solace.com` `.md` URL, WebFetched on demand; the live exceptions are listed separately because they have no `docs.solace.com` `.md` form. @@ -46,6 +50,7 @@ Python-specific grounding. Each page below is a live `docs.solace.com` `.md` URL - [Python API Developer Guide](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Dev-Guide.md): the index of the developer guide pages. - [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md): building and connecting the `MessagingService`. - [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) and [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md): the guaranteed pub/sub pages. +- [Request-Reply Messaging in the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Request-Reply.md): request-reply in the Python API, which uses direct messages only. - [Creating Queues with the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Create-Queues.md): queue provisioning from the client. - [Supported Environments](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-supported-Environments.md): Python versions, platforms, and OpenSSL. - [Solace C API Best Practices](https://docs.solace.com/API/API-Developer-Guide/C-API-Best-Practices.md): the Python API has no best practices page of its own. It wraps the C API, so the reference samples ground their callback, reconnect, acknowledgement, and time-to-live practices here. diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/design-mode.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/design-mode.md index 9975cdb..32ac8ff 100644 --- a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/design-mode.md +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/design-mode.md @@ -76,7 +76,7 @@ Inside the guaranteed publish/subscribe single-service leaf, the exclusive versu ## Step 4: Emit the design summary -Fill in this ONE unified design summary every run, in chat, with the confirmed values. The same eight fields appear every run regardless of which leaf the tree reached, so Implement mode reads one stable structure. Use explicit `n/a` for any field the chosen pattern does not apply. The title names the API, so a saved `solace-design.md` routes back to the Python entry file. +Fill in this ONE unified design summary every run, in chat, with the confirmed values. The same eight fields appear every run regardless of which leaf the tree reached, so Implement mode reads one stable structure. Use explicit `n/a` for any field the chosen pattern does not apply. The title names the API, so a saved `solace-design.md` routes back to the Python entry file (the saved-design exception in SKILL.md API Selection). ``` Solace Python Design Summary @@ -103,6 +103,6 @@ Field semantics: - **Broker.** Default to `Solace Cloud` and record it without asking. Switch to another type (Software Broker or Appliance) ONLY if the developer explicitly insists. - **Grounding docs.** ONLY the pages this session actually WebFetched. Never list a page you did not fetch. Write `none fetched` when no page was fetched. -After presenting the summary in chat, close Design mode explicitly so the developer knows exactly what happens next; do NOT just display the summary and stop. On the fully-specified path (Step 1) this close-out is the ONLY confirm of the run; it carries the leaf confirmation too. If the design is a variant of its leaf, state the deviation and its rationale in one line here too. Ask them directly, in one step, both whether they are happy with this design AND whether to save it to `solace-design.md` in their project (for example: "Happy with this design? If so, I can save it to `solace-design.md` and move into Implement mode to generate the runnable Python project."). Write the file only on their OK; never write it unprompted. Once they confirm, state the next step plainly: the work moves into Implement mode, which generates the runnable Python project from this summary. Implement mode OPENS with its own Step 0 door question (Quickstart, Solace Suggested, or Custom), asked before any broker details are requested or accepted; an existing `config.json` or a running broker does not answer it, so ask it on the way in. +After presenting the summary in chat, close Design mode explicitly so the developer knows exactly what happens next; do NOT just display the summary and stop. On the fully-specified path (Step 1) this close-out is the ONLY confirm of the run; it carries the leaf confirmation too. If the design is a variant of its leaf, state the deviation and its rationale in one line here too. Ask them directly, in one step, both whether they are happy with this design AND whether to save it to `solace-design.md` in their project (for example: "Happy with this design? If so, I can save it to `solace-design.md` and move into Implement mode to generate the runnable Python project."). Write the file only on their OK; never write it unprompted. Once they confirm, state the next step plainly: the work moves into Implement mode, which generates the runnable Python project from this summary. Implement mode treats this summary, whether it lives in the chat or in the saved `solace-design.md`, as its input contract. Implement mode OPENS with its own Step 0 door question (Quickstart, Solace Suggested, or Custom), asked before any broker details are requested or accepted; an existing `config.json` or a running broker does not answer it, so ask it on the way in. For a direct leaf (Direct Pub/Sub or Request-Reply (Direct)), the close-out differs: offer to save the summary to `solace-design.md`, and say in one line that generating a Python project for this leaf is not yet available in this release, so the saved design is ready for when it is. Do not offer to move into Implement mode, do not generate code from the direct samples by hand, and do not offer to write the code another way (from scratch, "outside the generator", or as unverified code). When the developer asks for the code anyway, repeat that generation for this leaf is not yet available and point them at the [Python tutorials](https://tutorials.solace.dev/python/); write no code. diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-guaranteed-pubsub.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-guaranteed-pubsub.md index bc4e62a..2eed182 100644 --- a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-guaranteed-pubsub.md +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-guaranteed-pubsub.md @@ -11,7 +11,7 @@ The skill ships two best-practices reference samples for this flow. Read them by Each script builds its OWN `MessagingService` (two services, one per script, matching the samples). Keep the samples' structure: the shared state dataclass, `setup_solace(...)` that builds the service and the receiver or publisher before any connection exists, `connect_solace(...)`, the main loop, and `teardown_solace(...)` in `main()`'s `finally`. Keep every listener class; they are part of the best-practices contract and must not be stripped during adaptation. -**Ground before you write.** Before writing either script, WebFetch [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) and [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) in this session (SKILL.md Invariant 2). The second page also grounds the concurrency rules in `implement-mode.md` Step 4. +**Ground before you write.** Before writing either script, WebFetch [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) and [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) in this session (SKILL.md Invariant 2). The second page also grounds the `receive_async()` and acknowledgement rules in `implement-mode.md` Step 4's concurrency section. The three guaranteed pub/sub leaves generate the same publisher and subscriber. The exclusive versus non-exclusive choice is one queue-factory line (read the `Access type` summary field). The fan-out leaf reuses the single-service subscriber once per consuming service (see Fan-out below). @@ -36,7 +36,7 @@ queue = Queue.durable_exclusive_queue(queue_name) # one active consumer / When the `Access type` field reads exclusive, keep the `durable_exclusive_queue` line: only one bound receiver is active at a time, and the receiver state listener reports the passive standby. When it reads non-exclusive, use the `durable_non_exclusive_queue` line instead, so several subscriber processes bound to the same queue share the messages. Emit exactly one of the two lines. Ground the choice in [Topic Endpoints and Queues](https://docs.solace.com/Get-Started/topic-endpoints-queues.md). -**Fan-out.** On the fan-out leaf, every consuming service gets its own durable queue with the same topic subscription, so each service receives its own full copy of every event. Generate one subscriber script per consuming service: `guaranteed_subscriber.py` for the first service, and `guaranteed_subscriber_.py` for each further service, each with its own queue name from the design summary and an exclusive durable queue. Hand back one run command per subscriber in Step 5, and add each script to the import smoke. +**Fan-out.** On the fan-out leaf, every consuming service gets its own durable queue with the same topic subscription, so each service receives its own full copy of every event. Generate one subscriber script per consuming service: `guaranteed_subscriber.py` for the first service, and `guaranteed_subscriber_.py` for each further service, where `` is a lowercase Python identifier (letters, digits, and underscores, for example `billing` or `audit_log`) so the import smoke can import the module, each with its own queue name from the design summary and an exclusive durable queue. Hand back one run command per subscriber in Step 5, and add each script to the import smoke. ## Publisher script (`guaranteed_publisher.py`) diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-mode.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-mode.md index a545c0f..5b9eff4 100644 --- a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-mode.md +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/implement-mode.md @@ -32,7 +32,7 @@ Three things do NOT answer this question, and none of them waives it: The doors: - **Quickstart.** Each script builds its connection through the shared `solace_connection_config.py` helper, which reads the connection properties from a `config.json` in the working directory when present and otherwise falls back to the command-line arguments (` [password]`). Quickstart adds no TLS hardening and has no TLS requirement: it connects with whatever scheme the developer's host carries, and a plaintext `tcp://` dev broker is a normal Quickstart target. Generate the scripts without per-stage preview gates. Quickstart output fails fast (the fail-fast rule in Step 4). -- **Solace Suggested or Custom.** Not yet available for Python. Say so plainly, without listing hardening steps from memory. Offer the developer two honest choices: Quickstart now, framed as a learning run, or stop here with the design saved to `solace-design.md` for when the hardened doors are available. Never generate a Quickstart project under the Solace Suggested or Custom name. +- **Solace Suggested or Custom.** Not yet available for Python. Say so plainly, and do not describe the hardened doors beyond the one-line framing in the Step 0 question. Offer the developer two honest choices: Quickstart now, framed as a learning run, or stop here with the design saved to `solace-design.md` for when the hardened doors are available. Never generate a Quickstart project under the Solace Suggested or Custom name. State plainly that a real deployment lifts each script into its own service or project. Basic username/password is the only auth this journey generates; ground it in the authentication section of [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md). @@ -67,11 +67,11 @@ curl -s https://pypi.org/pypi/solace-pubsubplus/json \ | python3 -c 'import json, sys; print(json.load(sys.stdin)["info"]["version"])' ``` -If the network query fails, do not guess a version. Ask the developer for the version they want, or write the bare requirement `solace-pubsubplus` and let pip resolve it, and tell the developer which path you took. +If the network query fails, do not guess a version and do not write an unpinned requirement. Ask the developer for the version they want, pin that version, and tell the developer that PyPI was unreachable, so the pin is their choice and not a resolved release. ### Write requirements.txt and create the virtual environment -- **Single Solace dependency: `solace-pubsubplus`.** Write it as `solace-pubsubplus==`, so the generated project installs exactly the release it was generated against. That pin is generation output resolved at generation time, the same way the JCSMP pom carries a resolved `sol-jcsmp` version; it is never a version written in skill content. Add no other dependency: the samples need only the standard library besides the Solace API. +- **Single Solace dependency: `solace-pubsubplus`.** Write it as `solace-pubsubplus==` (or the developer's version when PyPI was unreachable), so the generated project installs exactly the release it was generated against. That pin is generation output resolved at generation time, the same way the JCSMP pom carries a resolved `sol-jcsmp` version; it is never a version written in skill content. Add no other dependency: the samples need only the standard library besides the Solace API. - **Python version.** Check `python3 --version` in the developer's environment. When it reports a Python that is no longer in active or security support, tell the developer, link `prerequisites.md`'s Supported environments section, and do not choose an older interpreter for them. - **Virtual environment.** Create it at the project root and install into it: `python3 -m venv .venv`, then `.venv/bin/pip install -r requirements.txt` (on Windows, `.venv\Scripts\pip`). Every run command this mode hands back uses the virtual environment's interpreter (`.venv/bin/python`), never the system `python3`. - **Logging.** The samples log API events through the standard `logging` module, configured once with `logging.basicConfig(level=logging.INFO, ...)` in the `__main__` block. Keep that configuration in every generated script. Never raise the level above INFO, because the API's own connect and reconnect diagnostics travel the same channel. @@ -109,9 +109,9 @@ Stamp the header AT WRITE TIME, as the first content of each new file. `requirem ### Concurrency: threads, not asyncio, and no multiprocessing -The Python API calls application code on its own threads, and the generated code must respect that model. WebFetch [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) before generating a receiver and ground these rules in it: +The Python API calls application code on its own threads, and the generated code must respect that model. WebFetch [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) before generating a receiver. That page grounds the `receive_async()` rule and the acknowledgement rule; the handler rule comes from the samples' handler docstrings and the C API Best Practices they cite, and the `multiprocessing` rule comes from the PyPI page: -- **`receive_async()` is not asyncio.** The page states that `receive_async()` is not an asynchronous coroutine or generator, is not `asyncio` compatible, returns immediately, and invokes the callback on a new Python thread for every message. Never `await` the API and never call it from inside a coroutine. In an asyncio application, keep the Solace objects on their own threads and hand each received message to the event loop with the standard library's `loop.call_soon_threadsafe(...)`; acknowledge the message only after the application has processed it. +- **`receive_async()` is not asyncio.** The page states that `receive_async()` is not an asynchronous coroutine or generator, is not `asyncio` compatible, returns immediately, and invokes the callback on a new Python thread for every message. Never `await` the API and never call it from inside a coroutine. In an asyncio application, keep the Solace objects on their own threads and hand each received message to the event loop with the standard library's `loop.call_soon_threadsafe(...)` (a standard library call, not a Solace API). Acknowledge the message only after the application has processed it and before the receiver terminates: the page states that any routine can acknowledge messages at any time as long as the receiver has not been terminated. - **Handlers return promptly.** Every listener and message handler runs on an API thread. Return promptly and hand heavy work to the application's own thread or queue, as the samples' handler docstrings state. - **No `multiprocessing`.** The Solace Python API cannot be used in an application that uses the Python `multiprocessing` module (the [solace-pubsubplus PyPI page](https://pypi.org/project/solace-pubsubplus/) states this). To scale consumption, run more independent processes of the subscriber on a non-exclusive queue, each started on its own; never fork workers from one process with `multiprocessing`. @@ -158,7 +158,18 @@ The Python API does not bundle a verify script yet, so Implement mode ends in th 1. **Compile check.** Run `.venv/bin/python -m py_compile` on every generated `.py` file. A failure is a code failure: fix it and re-run, up to 3 attempts, then report the evidence and hand the decision to the developer. 2. **Import smoke.** From the project root, run `.venv/bin/python -c "import solace_connection_config, guaranteed_publisher, guaranteed_subscriber"`, adding every further generated module (for example each fan-out subscriber). Each script keeps its `if __name__ == "__main__":` guard, so the import resolves every `solace.messaging` name the scripts use without connecting to a broker. A failure (for example an `ImportError` on a misspelled module path) is a code failure under the same fix loop. -3. **Report and hand back.** Report both results, then hand the developer the exact commands to run against their own broker from the project root, subscriber first so the durable queue and its subscription exist before the first publish: +3. **Conformance checks.** From the project root, run these three checks and record each result in the checklist. A missing header or a missing checklist is a code failure under the same fix loop; a version mismatch is reported to the developer, not fixed silently. + + ```bash + # every generated .py file starts with the disclaimer header + for f in *.py; do head -n 1 "$f" | grep -qxF '# AI-assisted code. Review before production use.' || echo "MISSING HEADER: $f"; done + # the tailored checklist exists + test -f solace-verification-checklist.md || echo "MISSING CHECKLIST" + # the pin matches the release PyPI reports now (skip and record it when PyPI is unreachable) + grep -qxF "solace-pubsubplus==$(curl -s https://pypi.org/pypi/solace-pubsubplus/json | python3 -c 'import json, sys; print(json.load(sys.stdin)["info"]["version"])')" requirements.txt || echo "VERSION DRIFT" + ``` + +4. **Report and hand back.** Report the compile, import, and conformance results, then hand the developer the exact commands to run against their own broker from the project root, subscriber first so the durable queue and its subscription exist before the first publish: ``` # terminal 1: provisions the queue, binds, and waits (Ctrl-C stops it) @@ -167,9 +178,13 @@ The Python API does not bundle a verify script yet, so Implement mode ends in th .venv/bin/python guaranteed_publisher.py [ [password]] ``` + On the fan-out leaf, hand back one subscriber command per subscriber script (`guaranteed_subscriber.py` and each `guaranteed_subscriber_.py`), each in its own terminal and all started before the publisher; every subscriber prints its own markers. + The arguments are needed only when there is no `config.json`. Tell the developer what a passing run prints: the subscriber prints `VERIFY: CONNECTED` and then `VERIFY: QUEUE_BOUND`; the publisher prints `VERIFY: CONNECTED` and `VERIFY: PUBLISH_ACKED` and exits with status 0; the subscriber then prints `VERIFY: MESSAGE_RECEIVED`; Ctrl-C on the subscriber prints `Shutdown signal received` and it exits with status 0. -**When the developer asks you to run it.** If the developer explicitly asks you to run the two scripts against their broker, print the live-broker heads-up first (SKILL.md Invariant 6: name the host, the one durable queue the subscriber provisions, the two connections, and the messages the publisher sends). Then start the subscriber in the background with its output captured, wait for `VERIFY: QUEUE_BOUND`, run the publisher in the foreground, wait for `VERIFY: MESSAGE_RECEIVED` in the subscriber output, and stop the subscriber with SIGINT. Report the markers you observed as evidence of a manual run. It is not a verify verdict: record it in the checklist as a manual run, and never describe it as a passed verify stage. +**When the developer asks you to run it.** If the developer explicitly asks you to run the two scripts against their broker, print the live-broker heads-up first (SKILL.md Invariant 6: name the host, every durable queue the subscribers provision, one connection per script, and the messages the publisher sends, scaled to the generated scripts). Then start each subscriber in the background with its output captured, wait for `VERIFY: QUEUE_BOUND` from each, run the publisher in the foreground, wait for `VERIFY: MESSAGE_RECEIVED` in each subscriber's output, and stop the subscribers with SIGINT. Report the markers you observed as evidence of a manual run. It is not a verify verdict: record it in the checklist as a manual run, and never describe it as a passed verify stage. + +**Environment failure on a manual run: do NOT modify the code.** When the captured output shows that the broker is unreachable, the credentials or VPN are wrong, or the client may not create the queue (the subscriber's `Could not bind to queue` error), the code is not the problem. Report the matched output line as evidence and STOP: do not enter the fix loop, and do not change the code. The fix is the developer's broker, credentials, or client permissions. Fix the code only when the output shows a code failure (an exception from the scripts' own logic, or a marker that never appears after a clean connect and bind), up to 3 attempts, then hand the decision to the developer. **Close every run with the next steps, stated plainly.** Tell the developer, in order: (1) copy `config.example.json` to `config.json` and fill in their real broker values; (2) run the subscriber, then the publisher, with the commands above, and check the markers. Step 4 already wrote the tailored `solace-verification-checklist.md`; invite the developer to add their own items in its Your additional items section and to record any skill-undelivered concerns under Developer-owned items. @@ -178,59 +193,63 @@ The Python API does not bundle a verify script yet, so Implement mode ends in th - **Improvised verdicts.** A connect that does not raise, or a publish call that returns, proves nothing about the round trip. Judge only by the markers in each script's output. - **Publishing direct-to-queue instead of pub/sub.** Publish to a topic; let the durable queue carry a topic subscription. See [Creating Queues with the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Create-Queues.md). - **DIRECT delivery where Guaranteed is required.** Use the persistent publisher for the guaranteed pub/sub journey. See [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md). -- **Seconds instead of milliseconds.** `RetryStrategy.parametrized_retry(retries, retry_interval)` takes its interval in MILLISECONDS per the API reference; a value of `3` means 3 milliseconds. Whenever a generated script sets a retry interval, write it in milliseconds, as the samples' named constants do. +- **Seconds instead of milliseconds.** `RetryStrategy.parametrized_retry(retries, retry_interval)` takes its interval in MILLISECONDS per the API reference; a value of `3` means 3 milliseconds. The sample code on the Messaging Service page comments `parametrized_retry(20, 3)` as "over an interval of 3 seconds"; that comment is wrong, and the API reference wins. Whenever a generated script sets a retry interval, write it in milliseconds, as the samples' named constants do. - **Hardcoded credentials.** Never inline the host, VPN, username, or password. Every script builds its properties through the shared helper. - **Hardcoded versions.** Resolve the `solace-pubsubplus` version at generation time (Step 3); never carry a version from memory, from an old `requirements.txt`, or from the upstream samples repository. - **Wildcard imports.** Keep every import explicit, as the samples do; never write `from solace.messaging... import *`. - **The system interpreter.** Run and check everything with `.venv/bin/python`, so the project uses the version it installed. -- **Running the publisher before the subscriber has provisioned the queue.** A persistent message published before the queue subscribes to its topic is dropped and never received. Run the subscriber first. +- **Running the publisher before the subscriber has provisioned the queue.** A persistent message published before the queue subscribes to its topic is dropped and never received. By default the broker still ACKs it, so the publisher can print `VERIFY: PUBLISH_ACKED` for a message no subscriber gets; the publisher sees a NACK only when the client profile rejects messages that match no subscription. Judge the round trip by `VERIFY: MESSAGE_RECEIVED` in the subscriber output, never by `PUBLISH_ACKED` alone. Run the subscriber first. - **Treating a sample as a spec that overrides the approved design.** See "Adapting a sample to a variant of its leaf" above. ## Step 6: Tailor the checklist (written at Step 4) and report -Step 4 writes the tailored `solace-verification-checklist.md` into the generated project as generation output; this step defines its content and adds the report in chat. The Python API has no master checklist template yet; until it lands, write the file from the list below. Resolve each item for the generated code: an item the generation did not deliver moves to Your responsibility. Write it as a Markdown task list (`- [ ]`), ticking only what this run actually checked. +Step 4 writes the tailored `solace-verification-checklist.md` into the generated project as generation output; this step defines its content and adds the report in chat. The Python API has no master checklist template yet; until it lands, write the file from the list below. Resolve each item for the generated code: an item the generation did not deliver moves to Your responsibility. Write it as a Markdown task list (`- [ ]`), ticking only what this run actually checked. Each item in the first two groups carries one canonical doc link, as the master JCSMP template does; the round-trip and conformance items carry none because they check skill conformance, not Solace guidance. **Delivered by this generation** -- Basic-auth connection through the shared `solace_connection_config.py` helper; credentials live only in a gitignored `config.json` or on the command line. -- Service reconnection and interruption listeners registered before `connect()`. -- Persistent publish to a topic with a publish receipt listener; a NACK is logged with the message id. -- Capacity-bounded back pressure on the publisher (`on_back_pressure_wait`). -- Message time-to-live set once on the publisher's message template. -- Durable queue plus topic subscription provisioned at start (`CREATE_ON_START`), with the access type from the design summary (one durable queue per consuming service on fan-out). -- Client acknowledgement only after processing. -- Graceful SIGINT/SIGTERM shutdown: `terminate(grace_period)` and then `disconnect()` on every exit path. +- Basic-auth connection through the shared `solace_connection_config.py` helper; credentials live only in a gitignored `config.json`. [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md) +- Service reconnection and interruption listeners registered before `connect()`. [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md) +- Persistent publish to a topic with a publish receipt listener; a NACK is logged with the message id. [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) +- Capacity-bounded back pressure on the publisher (`on_back_pressure_wait`). [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) +- Message time-to-live set once on the publisher's message template. [Publishing Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Publish.md) +- Durable queue plus topic subscription provisioned at start (`CREATE_ON_START`), with the access type from the design summary (one durable queue per consuming service on fan-out). [Creating Queues with the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Create-Queues.md) +- Client acknowledgement only after processing. [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) +- Graceful SIGINT/SIGTERM shutdown: `terminate(grace_period)` and then `disconnect()` on every exit path. [Consuming Persistent Messages](https://docs.solace.com/API/API-Developer-Guide-Python/Python-PM-Receive.md) **Your responsibility (not delivered here)** -- TLS secure session (`tcps://` with certificate validation); Quickstart does not generate it. -- Dead message queue eligibility on published messages and a dead message queue on the broker. -- Reconnect tuning for HA failover; Quickstart leaves connect and reconnect behavior at the API defaults. -- Separate projects or deployments per role. -- `respect-ttl` enabled on the queue, without which the broker ignores the message time-to-live. -- An administrator-provisioned queue, or the endpoint-create permission for this client, in production. -- A Python version in active or security support, OpenSSL installed on Linux for TLS, and no `multiprocessing` in the application (`prerequisites.md`). +- TLS secure session (`tcps://` with certificate validation); Quickstart does not generate it. [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md) +- Credentials handled safely: `config.json` is never committed, and credentials are not passed as command-line arguments, where they land in shell history and process listings (the handed-back run commands accept them only for convenience). [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md) +- Authentication stronger than basic username/password (for example a client certificate or Kerberos) where the environment requires it; this journey generates basic username/password only. [Messaging Service](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Messaging-Service.md) +- Dead message queue eligibility on published messages and a dead message queue on the broker. [Configuring Dead Message Queues](https://docs.solace.com/Messaging/Guaranteed-Msg/Setting-Dead-Msg-Queues.md) +- Reconnect tuning for HA failover; Quickstart leaves connect and reconnect behavior at the API defaults. [Solace C API Best Practices](https://docs.solace.com/API/API-Developer-Guide/C-API-Best-Practices.md) +- Separate projects or deployments per role (skill guidance: a real deployment lifts each script into its own service or project; no Solace doc page states it). +- `respect-ttl` enabled on the queue, without which the broker ignores the message time-to-live. [Configuring Queues](https://docs.solace.com/Messaging/Guaranteed-Msg/Configuring-Queues.md) +- An administrator-provisioned queue, or the endpoint-create permission for this client, in production. [Creating Queues with the Solace Python API](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-Create-Queues.md) +- A Python version in active or security support and OpenSSL installed on Linux for TLS ([Supported Environments](https://docs.solace.com/API/API-Developer-Guide-Python/Python-API-supported-Environments.md)), and no `multiprocessing` in the application ([solace-pubsubplus on PyPI](https://pypi.org/project/solace-pubsubplus/)). **Verified by the round-trip** (developer-run until the Python verify script lands) -- The subscriber prints `VERIFY: CONNECTED` and `VERIFY: QUEUE_BOUND`. +- Every subscriber prints `VERIFY: CONNECTED` and `VERIFY: QUEUE_BOUND`. - The publisher prints `VERIFY: CONNECTED` and `VERIFY: PUBLISH_ACKED`, then exits with status 0. -- The subscriber prints `VERIFY: MESSAGE_RECEIVED`. -- Ctrl-C on the subscriber prints `Shutdown signal received` and it exits with status 0. +- Every subscriber prints `VERIFY: MESSAGE_RECEIVED` (`PUBLISH_ACKED` alone does not prove delivery). +- Ctrl-C on each subscriber prints `Shutdown signal received` and it exits with status 0. **Generation conformance** -- The disclaimer header is the first two lines of every generated `.py` file. -- `requirements.txt` pins the `solace-pubsubplus` version that PyPI reported as `info.version` at generation time. +- The disclaimer header is the first two lines of every generated `.py` file (Step 5 header check). +- `requirements.txt` pins the `solace-pubsubplus` version that PyPI reported as `info.version` at generation time, or the developer's version when PyPI was unreachable (Step 5 drift check). +- This tailored checklist exists at the project root (Step 5 checklist check). - `py_compile` and the import smoke passed (Step 5), with the result recorded here. - Comments follow their code (Step 4). +- Every page named in the design summary's Grounding docs field was WebFetched in this session (`none fetched` is an honest value; an unfetched citation is not). - Verification: record `verify script not yet bundled for Python; round trip handed back`, or `manual run` with the observed markers when the developer asked you to run it. **Developer-owned items (not delivered by this skill)**: any item the skill does not deliver, recorded as developer-owned rather than skill-satisfied. A consented deviation is recorded here. **Your additional items**: open space for the developer to keep growing the list. -The emitted checklist is an artifact, not source, so it carries NO disclaimer header. Report the same resolution in chat: what landed under each group, the compile and import results, and that the round trip is handed back. +The emitted checklist is an artifact, not source, so it carries NO disclaimer header. Report the same resolution in chat: what landed under each group, the compile, import, and conformance results, and that the round trip is handed back. ## Grounding references diff --git a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/prerequisites.md b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/prerequisites.md index 66c52a8..b81bb69 100644 --- a/plugins/solace-messaging-skills/skills/solace-application-development/references/python/prerequisites.md +++ b/plugins/solace-messaging-skills/skills/solace-application-development/references/python/prerequisites.md @@ -12,7 +12,7 @@ Read this when a developer does not yet have a broker to build against, or when You need one reachable broker. The primary, recommended choice is [Solace Cloud](https://docs.solace.com/Get-Started/Getting-Started-Try-Broker.md): it is the simplest path to a running broker for a greenfield app and for the run-and-observe round-trip. Default to it unless the developer has a reason not to. -As brief alternatives, the same page also documents a self-hosted Software Broker (run locally via container or VM) and an Appliance (existing hardware the developer already operates) for developers who cannot use Solace Cloud. +As a brief alternative, the same page also covers setting up a free broker locally, for developers who cannot use Solace Cloud. The skill does not provision or configure the broker. It assumes the broker is reachable and that the developer has connection details (host, message VPN, client username, password). A broker discovered running in the environment (for example a local container) is a fact to report, never an answer: still ask which broker the developer wants to target.