diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..cb21480 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default owner for all repository changes. +* @bnquon diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..ad56d3e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,61 @@ +name: Bug report +description: Report a reproducible problem in Orson +title: '[Bug]: ' +labels: + - bug +body: + - type: markdown + attributes: + value: | + Thanks for reporting a bug. Please remove credentials and sensitive payload data from logs, screenshots, and examples. + + Reports need enough detail for someone else to reproduce and investigate the problem. Include clear steps, exact error text, and a screenshot or recording when the issue is visual or interaction-related. Reports without enough information may not be addressed immediately and could be deprioritized until the missing context is provided. + - type: textarea + id: summary + attributes: + label: What happened? + description: Describe the problem and what you were trying to do. + placeholder: Tell us what went wrong. + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce + description: Give the smallest reliable sequence of actions that shows the problem. + placeholder: | + 1. Open ... + 2. Configure ... + 3. Click ... + 4. Observe ... + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What did you expect Orson to do? + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual behavior + description: What did Orson do instead? Include the exact error text when possible. + validations: + required: true + - type: input + id: environment + attributes: + label: Environment + description: Include the commit or version, operating system, and Kafka/demo setup. + placeholder: 'commit abc123, macOS 15, local demo Kafka' + validations: + required: true + - type: textarea + id: context + attributes: + label: Scenario or run context + description: Share the relevant scenario shape, run outcome, logs, or screenshots. Redact sensitive data. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..d9e9ec9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,40 @@ +name: Feature request +description: Suggest an improvement to Orson +title: '[Feature]: ' +labels: + - enhancement +body: + - type: markdown + attributes: + value: | + Describe the user problem first. Concrete Kafka workflow examples are especially helpful. + - type: textarea + id: problem + attributes: + label: What problem would this solve? + description: Who is affected, and what is difficult or unclear today? + placeholder: When I am debugging ..., it is hard to ... + validations: + required: true + - type: textarea + id: proposal + attributes: + label: What would you like to happen? + description: Describe the behavior or workflow you are proposing. + placeholder: Orson could ... + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: What workarounds or other approaches have you tried? + validations: + required: false + - type: textarea + id: context + attributes: + label: Additional context + description: Add examples, mockups, links, or relevant scenario details. Do not include secrets. + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..95be0c8 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +## Summary + + + +## User-facing changes + + + +## Validation/testing + + + +## Screenshots + + + +## Risks, follow-ups, or deferred work + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b26b106 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,105 @@ +# Contributing to Orson + +Thanks for helping improve Orson. It is an early, local-first desktop app for +understanding what happens after publishing a Kafka event. Focus contributions +on making that debugging loop clearer, safer, and easier to reproduce. + +## Development setup + +Orson currently requires: + +- Go 1.25+ +- Node.js 20.19+ or 22.12+ +- Docker +- Wails CLI v2.15.0 + +Install the JavaScript dependencies from the repository root: + +```bash +npm install +npm --prefix frontend install +``` + +Install the matching Wails CLI if it is not already available: + +```bash +go install github.com/wailsapp/wails/v2/cmd/wails@v2.15.0 +``` + +Start the local Kafka demo from the repository root: + +```bash +docker compose -f demo/compose.yaml up --build +``` + +Then start Orson in another terminal: + +```bash +wails dev +``` + +The demo broker is available to the host at `localhost:9092`. The demo +services, fixtures, and branch behavior are documented in +[`demo/README.md`](demo/README.md) and [`demo/EVENTS.md`](demo/EVENTS.md). + +## Working on scenarios + +Scenario files are human-readable YAML intended to be shared through Git. The +canonical bundled example is [`scenarios/order-flow.yaml`](scenarios/order-flow.yaml), +and the format is documented in [`docs/scenario-format.md`](docs/scenario-format.md). + +Do not put credentials or captured run data in a scenario file. Keep payloads +representative and safe to commit. Imported and newly saved scenarios should +use the existing loader and serializer rather than introducing a second file +format. + +## Branches and changes + +Create a focused branch from `main`. The usual prefixes are: + +- `feat/` for user-facing functionality +- `fix/` for bug fixes +- `docs/` for documentation-only changes +- `refactor/` for behavior-preserving cleanup + +Keep unrelated formatting or generated-file changes out of the branch. For UI +changes, describe the user-visible behavior and include a screenshot or short +recording when it makes the change easier to review. + +## Validation + +Before opening a pull request, run the checks relevant to your change. The +full repository validation is: + +```bash +go test ./... +go vet ./... +npm run check +npm run test:frontend +npm --prefix frontend run build +``` + +`npm run check` covers formatting, linting, TypeScript, and export checks. Go +changes should also remain `gofmt`-clean. If a check cannot be run locally, +explain that in the pull request and include the reason. + +## Pull requests + +Keep the pull request focused and use the repository template. Explain: + +- what changed and why +- the user-facing behavior +- how it was tested +- screenshots for UI changes +- risks, follow-ups, or intentionally deferred work + +Small pull requests are easier to review, but a larger change is fine when it +represents one coherent user workflow. Update the relevant documentation when +behavior, commands, or scenario format changes. + +## Issues + +Use the bug report form for reproducible failures and the feature request form +for proposed behavior. Include the Orson version or commit, operating system, +Kafka/demo setup, and the smallest useful reproduction. Remove secrets and +sensitive payload data from logs or screenshots before attaching them. diff --git a/README.md b/README.md index 216a65f..823e3ec 100644 --- a/README.md +++ b/README.md @@ -132,14 +132,17 @@ The demo services use plaintext Kafka and are intended for local development onl ## Roadmap -- Multiple saved connection profiles -- TLS and SASL configuration -- More flexible correlation strategies -- Richer graph interaction and layout +See the [directional roadmap](ROADMAP.md) for current priorities. It is not +strictly followed; priorities can change as Orson is used with real Kafka +workflows. ## More documentation - [Product and architecture notes](PROJECT.md) +- [Roadmap](ROADMAP.md) +- [Contributing guide](CONTRIBUTING.md) +- [Scenario format](docs/scenario-format.md) +- [Scenario examples](scenarios/README.md) - [Kafka demo pipeline](demo/README.md) - [Demo event contracts](demo/EVENTS.md) - [Scenario example](scenarios/order-flow.yaml) diff --git a/ROADMAP.md b/ROADMAP.md index c6c5b61..b834a27 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,264 +1,37 @@ # Orson roadmap -This roadmap turns the product brief into a sequence of small, testable milestones. Each milestone should leave Orson in a demonstrable state and reduce risk for the next one. - -## Guiding approach - -- Build one complete order-flow vertical slice before generalizing configuration. -- Keep Kafka code independent from Wails so it can support a CLI later. -- Use simulated events to settle the interface before connecting it to live Kafka data. -- Keep the current run in memory for the MVP. -- Add directories and abstractions only when the milestone needs them. -- Treat scenario files as shareable configuration and captured runs as sensitive local data. - -## Start here: define the demo contract - -The first implementation PR should define the fixed order-flow contract that every later milestone will use. - -Deliverables: - -- Document the JSON shape for each demo event. -- Define which service owns each transition. -- Define the rule that chooses the successful or failed branch. -- Define how `x-correlation-id` is copied between events. -- Add `scenarios/successful-order.yaml` with the fixed topics and expected topology. -- Add a matching failed-order fixture or document which payload field triggers failure. - -Failure-path decision: - -For the MVP demo, `order.cancelled` replaces `payment.charged` when payment fails. The payment service emits one or the other, never both. This keeps `payment.charged` semantically honest without introducing a separate `payment.failed` event. - -Done when: - -- A developer can read the fixtures and predict every event produced by a successful and failed order. -- No service behavior or event shape is implicit. - -## Milestone 1: reproducible Kafka demo system - -Build the system Orson will observe before building Kafka support inside Orson. - -Deliverables: - -- Add a Docker Compose Kafka environment. -- Create the fixed demo topics. -- Add the smallest possible demo services for payment, inventory, notification, and cancellation behavior. -- Copy `x-correlation-id` unchanged to every downstream event. -- Add health checks and deterministic startup behavior. -- Add a simple manual publish script or command for exercising both branches without Orson. - -Done when: - -- One command starts the complete demo environment. -- Publishing the successful fixture produces the expected success events. -- Publishing the failed fixture produces the expected cancellation branch. -- Reusing a correlation ID makes the entire flow easy to inspect manually. - -## Milestone 2: simulated desktop experience - -Build the core interaction with fixture data before introducing Kafka timing and connection failures. - -Deliverables: - -- Create the dense desktop shell and split-pane layout. -- Add a scenario sidebar and active-environment indicator. -- Add publish, stop, replay, and timeline/flow-map controls. -- Add an observed timeline using simulated events. -- Add the fixed SVG order-flow map. -- Add an event inspector for payload, headers, key, partition, offset, and timestamps. -- Show expected events that have not arrived yet. -- Model empty, listening, completed, timed-out, and failed states. - -Done when: - -- A simulated successful run can be played from start to finish. -- A simulated failed run visibly takes a different branch. -- Selecting an event opens all inspector details. -- Missing expected events remain visible after timeout. - -## Milestone 3: Kafka core library - -Implement Kafka behavior as plain Go packages with no Wails dependencies. - -Suggested packages as they become necessary: - -```text -internal/kafka -internal/correlation -internal/run -``` - -Deliverables: - -- Add franz-go. -- Define connection and message models. -- Connect to the demo broker and verify metadata access. -- Read current end offsets for watched topic partitions. -- Publish a JSON record with headers. -- Capture records without joining or committing offsets to an application consumer group. -- Filter records by the configured correlation header. -- Support context cancellation and capture timeout. - -Done when: - -- Go tests cover correlation matching and run-state behavior. -- An integration test or small temporary command can publish and capture one correlated event against the demo environment. -- Kafka packages do not import Wails. - -## Milestone 4: first live vertical slice - -Connect the desktop app to one hardcoded live order scenario. - -Run sequence: - -1. Resolve watched topic partitions. -2. Record their current end offsets. -3. Start all readers from those offsets. -4. Confirm the run is ready. -5. Generate a new correlation ID. -6. Publish `order.created`. -7. Stream matching records to the frontend. -8. Stop on timeout, cancellation, or completion. - -Deliverables: - -- Add a Go run coordinator. -- Expose the minimum Wails methods for connect, start, and stop. -- Emit `run:ready`, `run:message`, `run:timeout`, `run:error`, and `run:completed` events. -- Replace the simulated event source without rewriting the UI state model. -- Clearly display the active Kafka connection and environment. - -Done when: - -- Clicking publish starts readers before sending the root event. -- Live correlated events appear in the desktop UI. -- Unrelated records are ignored. -- Stopping or timing out cleans up every reader. - -## Milestone 5: complete timeline, flow map, and inspection - -Turn the live vertical slice into the primary debugging experience. - -Deliverables: - -- Order the observed timeline by local observation time. -- Light up configured flow-map nodes as events arrive. -- Distinguish observed, waiting, missing, and failed states. -- Show Kafka and local timestamps without implying proven causality. -- Complete payload, header, key, partition, and offset inspection. -- Add JSON formatting and copy actions. -- Preserve keyboard focus and reduced-motion behavior. - -Done when: - -- The same live run is understandable from either timeline or flow-map view. -- A developer can inspect every Kafka detail listed in the product brief. -- Timeout behavior clearly identifies missing expected events. - -## Milestone 6: edit and replay - -Deliverables: - -- Add a root JSON payload editor and validation. -- Allow replay only from the root-event configuration. -- Generate a new correlation ID for every replay. -- Keep separate run state so records from two runs cannot mix. -- Never offer to republish captured downstream records. - -Done when: - -- Changing the fixture’s failure field and replaying produces the alternate branch. -- The previous and current correlation IDs are distinct. -- Invalid JSON cannot be published. - -## Milestone 7: local YAML scenarios - -Add reusable configuration only after the hardcoded vertical slice works. - -Suggested package: - -```text -internal/scenario -``` - -Deliverables: - -- Define the scenario model and validation rules. -- Load and save human-readable YAML. -- Add native open and save dialogs through Wails. -- Support root topic, watched topics, correlation header, payload, timeout, and expected topology. -- Ensure credentials and captured run data are never serialized into scenarios. - -Done when: - -- The included order scenario opens, runs, edits, replays, and saves. -- A saved scenario has a clean, reviewable Git diff. -- Malformed scenarios produce useful validation errors. - -## Milestone 8: general configuration - -Replace remaining hardcoded demo assumptions without expanding beyond MVP scope. - -Deliverables: - -- Configure one Kafka connection. -- Select a root topic and watched topics. -- Configure the correlation-header name and capture timeout. -- Render any scenario-defined fixed topology using plain SVG. -- Handle unavailable brokers, missing topics, authorization errors, and partial captures. -- Keep credentials in environment variables during the early MVP. - -Done when: - -- A developer can point Orson at a non-demo local or staging Kafka environment. -- No order-specific topic name remains in runtime code. -- Connection and capture failures are actionable and do not lose the current scenario edits. - -## Milestone 9: MVP hardening and validation - -Deliverables: - -- Test the complete successful and failed demo paths repeatedly. -- Audit reader cleanup, offset behavior, and correlation isolation. -- Review payload handling for accidental persistence or logging. -- Finish empty, loading, failure, and timeout states. -- Add the Orson application icon and packaging metadata. -- Document local setup and the demo flow. -- Put the app in front of Kafka developers and record where the workflow is confusing. - -Done when: - -- A new developer can run the documented demo without help. -- The full MVP success condition in `PROJECT.md` works reliably. -- Feedback confirms that Orson answers “what happened after I published this event?” faster than topic-by-topic inspection. - -## After the MVP - -Consider these only after real-user validation: - -- SQLite run history -- run comparison and diffing -- Kafka keys or JSON paths as correlation methods -- Schema Registry, Avro, or Protobuf -- OpenTelemetry context -- editable or dynamic graphs -- assertions and CI use cases -- additional brokers - -## Recommended issue order - -Create issues in this order and keep each one independently reviewable: - -1. Define demo event contracts and resolve the failure topology. -2. Add the successful and failed scenario fixtures. -3. Add Kafka Compose infrastructure and topic initialization. -4. Add the demo services and manual verification command. -5. Build the simulated desktop shell and run-state model. -6. Add the simulated timeline and event inspector. -7. Add the fixed simulated flow map and timeout states. -8. Implement the independent Go Kafka client, producer, and capture reader. -9. Add the live run coordinator and Wails event bridge. -10. Replace simulated events with the live demo flow. -11. Add root-payload editing and replay. -12. Add YAML scenario loading and saving. -13. Generalize connection, topic, header, timeout, and topology configuration. -14. Harden, package, document, and validate the MVP. +This is a directional roadmap, not a strict plan or commitment. Priorities may +change as Orson is used with real Kafka workflows and we learn which parts of +the debugging experience need the most attention. + +## Now + +- Make local scenario authoring, importing, validation, and saving dependable. +- Make live runs and historical runs easy to understand from the timeline, + flow graph, and event inspector. +- Improve connection, capture, timeout, and partial-result feedback. +- Keep the local-first workflow safe for payloads, headers, credentials, and + captured run data. + +## Next + +- Improve replay and investigation workflows for common Kafka debugging tasks. +- Make topology configuration easier to edit and explain when topics are + disconnected or ambiguous. +- Expand Kafka connection configuration beyond the current local-development + setup, including TLS and SASL where they are needed. +- Continue testing against realistic event flows and staging environments. + +## Later + +- Compare runs and inspect meaningful differences between captures. +- Support more flexible correlation strategies, such as Kafka keys or JSON + paths. +- Revisit dynamic graph layout and richer graph interaction once real scenarios + require it. +- Consider additional workspace organization and collaboration features after + the core debugging loop is validated. + +The order is intentionally flexible. Small reliability and usability work can +move ahead of a larger item whenever it improves the core question Orson is +meant to answer: “What happened after I published this event?” diff --git a/docs/scenario-format.md b/docs/scenario-format.md new file mode 100644 index 0000000..60a2f5b --- /dev/null +++ b/docs/scenario-format.md @@ -0,0 +1,116 @@ +# Scenario YAML format + +Orson scenarios are local YAML files that describe one root event and the +topics Orson should observe afterward. They are validated when imported or +saved and can be committed to Git as shareable development configuration. + +The complete canonical example is +[`scenarios/order-flow.yaml`](../scenarios/order-flow.yaml). Start there when +creating a scenario. The checkout fixtures show smaller successful and failed +branches: + +- [`scenarios/checkout/successful-order.yaml`](../scenarios/checkout/successful-order.yaml) +- [`scenarios/checkout/failed-order.yaml`](../scenarios/checkout/failed-order.yaml) + +## Shape + +A scenario uses these top-level fields: + +```yaml +name: my-scenario + +publish: + topic: events.started + key: optional-key + headers: + - key: content-type + value: application/json + payload: + example: value + +watch: + - events.completed + +correlation: + header: x-correlation-id + +capture: + timeout: 10s + +topology: + - from: events.started + to: events.completed +``` + +The snippet shows the file shape, while the bundled scenario remains the +source of truth for a complete working example. + +## Fields + +| Field | Required | Description | +| -------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------- | +| `name` | Yes | Display name for the scenario. | +| `publish.topic` | Yes | Root topic where Orson publishes the event. | +| `publish.key` | No | Kafka message key. | +| `publish.headers` | No | Additional headers to publish. Each header has a `key` and `value`. If omitted, Orson uses `content-type: application/json`. | +| `publish.payload` | Yes | JSON-compatible YAML describing the root event payload. | +| `watch` | Yes | One or more downstream topics to capture. Do not repeat the publish topic here. | +| `correlation.header` | Recommended | Header used to associate records with the current run. Orson defaults to `x-correlation-id` if it is missing. | +| `capture.timeout` | Yes | Positive duration in whole seconds, up to `300s`. | +| `topology` | No | Ordered `from`/`to` edges used to render the flow graph and connect watched topics. | + +The correlation header is managed by Orson for each run. Do not add that same +header to `publish.headers`; Orson will reject the scenario because it would +conflict with the generated correlation ID. + +## Topology and watched topics + +`watch` controls which topics Orson reads. `topology` controls how those topics +are represented in the flow graph. Every topology edge must connect the +publish topic or a watched topic, and valid edges must not form a cycle. + +Topology edges are optional. Each edge must reference the publish topic or a +watched topic, and valid edges must not form a cycle. Orson warns when a +watched topic has no valid edge, but it still retains that topic for capture. +For a connected flow graph, connect watched topics directly or through a chain +starting at the publish topic. A disconnected watched-to-watched component can +still be captured, but it will not be connected to the root in the graph. + +The topology is not automatic causal inference. A shared correlation header +shows that records belong to the same run; it does not prove that one record +directly caused another. + +## Payloads, headers, and durations + +Payloads use YAML syntax but must convert cleanly to JSON. Objects, arrays, +strings, numbers, booleans, and `null` are supported. Quote strings when YAML +could interpret their value as another type. + +Header names and values are strings. Keep credentials, tokens, and other +secrets out of scenario files. Captured payloads and headers belong to local +run history, not reusable scenario configuration. + +Timeouts use Go duration syntax such as `5s`, `10s`, or `2m`. Orson currently +requires a whole number of seconds and limits the value to five minutes. + +## Validation behavior + +Imported files must contain exactly one YAML document and only supported fields. +Unknown fields, malformed YAML, missing required values, duplicate watched +topics, cycles, and invalid payloads are reported during validation. + +Topology entries with missing topics, unknown topics, self-references, or +duplicates produce warnings and are not used as valid graph edges. The app +preserves the configured entries when saving so warning-bearing files can be +reviewed and corrected rather than silently rewritten. + +## Saving and sharing + +Use the workbench form to edit a scenario and `Save as` to write a local `.yaml` +or `.yml` file. Orson writes canonical field ordering and formatting through +the existing serializer. A newly created scenario remains frontend-only until +the save succeeds. + +For a new scenario, begin with the bundled example, copy its structure, and +replace the topics and payload with values for your system. Do not add folder +metadata or connection credentials to the file. diff --git a/frontend/src/features/workbench/components/ScenarioBrowser.tsx b/frontend/src/features/workbench/components/ScenarioBrowser.tsx index fba95f1..ecd2aec 100644 --- a/frontend/src/features/workbench/components/ScenarioBrowser.tsx +++ b/frontend/src/features/workbench/components/ScenarioBrowser.tsx @@ -2,6 +2,7 @@ import { useId, useMemo, useState, type ReactNode } from 'react'; import { CheckCircle, EmptyPage, + InfoCircle, MoreHoriz, NavArrowDown, Plus, @@ -11,6 +12,8 @@ import { } from 'iconoir-react'; import { LoadingDots } from '../../../components/LoadingDots'; import { Modal, ModalActions, ModalButton } from '../../../components/Modal'; +import '../styles/scenario.css'; +import { ScenarioGuideModal } from './ScenarioGuideModal'; import type { ApiError, ScenarioDescriptor, @@ -375,6 +378,7 @@ export function ScenarioBrowser({ const [internalExamplesDismissed, setInternalExamplesDismissed] = useState(false); const [localsExpanded, setLocalsExpanded] = useState(true); const [pendingRemoval, setPendingRemoval] = useState(null); + const [scenarioGuideOpen, setScenarioGuideOpen] = useState(false); const importDisabledDescriptionId = useId(); const newDisabledDescriptionId = useId(); const examplesExpanded = controlledExamplesExpanded ?? internalExamplesExpanded; @@ -454,7 +458,19 @@ export function ScenarioBrowser({
Scenarios -