autotel-eventcatalog 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/CONTRIBUTING.md +212 -0
  3. package/README.md +307 -0
  4. package/action.yml +155 -0
  5. package/dist/cli.cjs +1071 -0
  6. package/dist/cli.cjs.map +1 -0
  7. package/dist/cli.d.cts +2 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +1065 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/index.cjs +794 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.cts +267 -0
  14. package/dist/index.d.ts +267 -0
  15. package/dist/index.js +764 -0
  16. package/dist/index.js.map +1 -0
  17. package/docs/CONTRACT.md +280 -0
  18. package/docs/EXTENDING.md +248 -0
  19. package/docs/TROUBLESHOOTING.md +220 -0
  20. package/docs/UPGRADING.md +202 -0
  21. package/package.json +78 -0
  22. package/schemas/README.md +44 -0
  23. package/schemas/drift-report-v0.1.0.json +107 -0
  24. package/schemas/drift-report-v0.2.0.json +137 -0
  25. package/schemas/drift-summary-v0.1.0.json +74 -0
  26. package/schemas/drift-summary-v0.2.0.json +74 -0
  27. package/schemas/stamp-summary-v0.1.0.json +54 -0
  28. package/src/__fixtures__/drift-report-all.golden.json +33 -0
  29. package/src/__fixtures__/drift-summary-clean.golden.json +17 -0
  30. package/src/__fixtures__/drift-summary-drifty.golden.json +17 -0
  31. package/src/__fixtures__/stamp-summary-noop.golden.json +10 -0
  32. package/src/catalog.test.ts +63 -0
  33. package/src/catalog.ts +169 -0
  34. package/src/cli.e2e.test.ts +310 -0
  35. package/src/cli.ts +402 -0
  36. package/src/contract.test.ts +395 -0
  37. package/src/diff-vs-base.test.ts +145 -0
  38. package/src/diff-vs-base.ts +242 -0
  39. package/src/diff.test.ts +384 -0
  40. package/src/diff.ts +296 -0
  41. package/src/index.ts +73 -0
  42. package/src/policy.test.ts +75 -0
  43. package/src/policy.ts +41 -0
  44. package/src/renderers/index.ts +35 -0
  45. package/src/renderers/json.ts +33 -0
  46. package/src/renderers/markdown.ts +223 -0
  47. package/src/renderers/renderers.test.ts +79 -0
  48. package/src/renderers/terminal.ts +30 -0
  49. package/src/renderers/types.ts +26 -0
  50. package/src/report.test.ts +205 -0
  51. package/src/report.ts +27 -0
  52. package/src/snapshot.ts +25 -0
  53. package/src/stamp.test.ts +283 -0
  54. package/src/stamp.ts +232 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,196 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - de31d28: The "get to 10" pass. Closes the review-board's outstanding watch items
8
+ and a couple they didn't surface.
9
+
10
+ **Public JSON contract.** Three versioned JSON Schemas ship with the
11
+ package: `schemas/drift-report-v0.1.0.json`,
12
+ `schemas/drift-summary-v0.1.0.json`, and
13
+ `schemas/stamp-summary-v0.1.0.json`. Each output envelope carries a
14
+ `spec:` field downstream consumers can read to refuse unknown major
15
+ versions. New `contract.test.ts` validates emitted output against the
16
+ schemas AND byte-compares against committed golden fixtures, so a PR
17
+ that silently changes a published field name now fails CI with a clear
18
+ message.
19
+
20
+ **Renderers as adapters.** The Markdown / Terminal / JSON renderers
21
+ moved into `src/renderers/` with a small `Renderer` interface and a
22
+ `getRenderer(name)` registry. The CLI's `--format` flag now dispatches
23
+ through the registry instead of hardcoding two cases — adding SARIF,
24
+ Slack-flavoured markdown, GitHub Check Runs API, etc. is a single new
25
+ file. The core diff and policy modules don't move and never reference
26
+ the registry. Backwards-compatible: `import { renderMarkdown } from
27
+ 'autotel-eventcatalog'` still works.
28
+
29
+ **Action hardening.**
30
+ - `marocchino/sticky-pull-request-comment` pinned to a specific commit
31
+ SHA (supply-chain control).
32
+ - A failure of the comment step is downgraded to a `::warning::` so the
33
+ drift signal is never lost to a flaky GitHub API call. Behind
34
+ `continue-on-comment-failure` input (defaults to `true`; set to
35
+ `false` to keep the old behaviour).
36
+ - `package-version` default changed from `latest` to `^0` so a new
37
+ major release doesn't silently land on user CI.
38
+
39
+ **README.** Opens with the dual-tool framing (drift verifier + stamp
40
+ generator). New "What this package does NOT do" boundary statement. New
41
+ "Public JSON contract" and "Renderers (advanced)" sections.
42
+
43
+ Backwards-compatible for every existing CLI and library consumer.
44
+
45
+ Total tests: 78 unit + 11 e2e (was 58 + 11 before this changeset).
46
+
47
+ - de31d28: Initial release of `autotel-eventcatalog`. Diffs an autotel architecture
48
+ snapshot against an EventCatalog and reports the drift: events observed but
49
+ undocumented, events documented but never observed, field paths in payloads
50
+ not declared in the schema, and producers / channels seen but not catalogued.
51
+
52
+ Ships as a library and a CLI:
53
+
54
+ ```bash
55
+ autotel-eventcatalog drift \
56
+ --snapshot ./services/test/snapshot.json \
57
+ --catalog ./catalog \
58
+ --output drift.md \
59
+ --fail-on-drift # exit 1 when drift is detected — wire this into CI
60
+ ```
61
+
62
+ v0 covers existence drift and field-path drift. Type drift, value drift, and
63
+ auto-writing missing catalog entries are deferred to later releases.
64
+
65
+ - de31d28: Three small follow-ups from review notes, applied:
66
+ 1. **Count extraction moved next to the diff types**. `countDriftReport`
67
+ (in `diff.ts`) and `countDriftEntries` / `countDriftDelta` (in
68
+ `diff-vs-base.ts`) produce the per-category and total counts that
69
+ dashboards, CI summaries, and reports need. `cli.ts`'s `buildSummary`
70
+ no longer duplicates the extraction logic. `hasDrift` is now a thin
71
+ wrapper around `countDriftReport(...).total > 0`. The new `total`
72
+ field matches the dashboard's existing `countDrift()` semantic
73
+ (individual paths inside `fieldDrift` are counted, not just events).
74
+ New `DriftCounts` type exported from the library.
75
+ 2. **`stamp --summary-output` ships**. Mirrors the existing `drift
76
+ --summary-output` so CI can answer "did this PR need to re-stamp?"
77
+ from a structured JSON file rather than parsing stderr. The summary
78
+ carries a versioned `spec: 'autotel-eventcatalog-stamp-summary/v0.1.0'`
79
+ marker and reports `attempted / inserts / replaces / changedFiles /
80
+ skipped / hadChanges`. `StampUpdate` gains a `changed: boolean` flag
81
+ so re-stamping with identical content correctly reports as a no-op
82
+ replace.
83
+ 3. **`renderTerminal` becomes a real export**. Tests cover the
84
+ structure-preserved-decorations-stripped contract. Added
85
+ `renderDeltaTerminal` for symmetric coverage of the PR-style delta
86
+ view. Both useful for Slack messages, log files, and anywhere a
87
+ plain-text rendering beats markdown.
88
+
89
+ 13 new tests, all passing (58 unit + 11 e2e total).
90
+
91
+ - de31d28: Add `--base-snapshot` mode and a composite GitHub Action for PR drift checking.
92
+
93
+ `autotel-eventcatalog drift --base-snapshot <path> --snapshot <path> --catalog <path>`
94
+ reports only the drift the PR introduces, ignoring pre-existing drift. New
95
+ `compareDriftReports()` and `renderDeltaMarkdown()` library exports do the
96
+ same thing programmatically.
97
+
98
+ The new `action.yml` ships in the package so any repository can wire drift
99
+ checking into its PR pipeline with one step:
100
+
101
+ ```yaml
102
+ - uses: jagreehal/autotel-eventcatalog@v0
103
+ with:
104
+ snapshot: ./services/test/snapshot.json
105
+ catalog: ./catalog
106
+ base-ref: origin/${{ github.base_ref }}
107
+ fail-on-drift: true
108
+ comment-on-pr: true
109
+ ```
110
+
111
+ The action runs the CLI, posts a sticky comment with the drift report on
112
+ the PR, and fails the check only when the PR introduces _new_ drift.
113
+
114
+ - de31d28: Add a `stamp` subcommand to `autotel-eventcatalog` that writes a runtime
115
+ evidence block into each event mdx between
116
+ `<!-- autotel:stamp-start -->` and `<!-- autotel:stamp-end -->` markers.
117
+
118
+ The block carries everything autotel observed in the snapshot for that
119
+ event — volume, last-seen, producer, channel, and the full list of dotted
120
+ field paths — so the static catalog page itself shows runtime evidence
121
+ alongside the hand-written narrative, not in a separate dashboard.
122
+
123
+ ```bash
124
+ autotel-eventcatalog stamp \
125
+ --snapshot ./services/test/snapshot.json \
126
+ --catalog ./catalog
127
+ ```
128
+
129
+ Subsequent runs are idempotent: content between the markers is replaced,
130
+ not duplicated. `--dry-run` prints the update plan without writing files,
131
+ suitable for a CI sanity check. The library also exposes `stampCatalog()`
132
+ and `buildStampBlock()` for programmatic use.
133
+
134
+ ### Patch Changes
135
+
136
+ - de31d28: Two correctness fixes flagged in code review:
137
+
138
+ **P0**: The CLI only exits non-zero on drift when `--fail-on-drift` is set.
139
+ The bundled GitHub Action's internal script captured `STATUS=$?` from the
140
+ CLI without passing `--fail-on-drift`, so the action's `drift-detected`
141
+ output and the downstream "Fail on drift" step never fired even when
142
+ drift was present. Fixed by always passing `--fail-on-drift` from the
143
+ action's internal script — the user-facing `fail-on-drift` input now
144
+ governs whether the workflow step fails, but the action's drift signal
145
+ is no longer gated on it.
146
+
147
+ **P1**: `readCatalogState` matched literal `'/'` separators for
148
+ directory classification. On Windows runners the path separator is `\`,
149
+ so the `'/versioned/'` exclusion and the `services/events/channels`
150
+ classification both silently misbehaved. Paths are now normalised to a
151
+ canonical POSIX form before string-matching, so classification works
152
+ identically on every platform.
153
+
154
+ The CLI exit-code contract is now documented as:
155
+
156
+ 0 — no drift
157
+ 1 — drift detected (only when `--fail-on-drift` is set)
158
+ 2 — bad arguments
159
+ other — hard failure (surface to the user)
160
+
161
+ - de31d28: Tighten the policy layer based on a code-review pass.
162
+ - `PolicyEvaluationResult` drops the redundant `mode` field. `reason` is
163
+ retained and now used: the CLI prints it on stderr at the end of every
164
+ drift run so CI logs say _"Drift detected in current snapshot."_ or
165
+ _"No new drift introduced compared to baseline snapshot."_ — explaining
166
+ the exit code rather than just emitting it.
167
+ - The CLI no longer silently rewrites `--policy all` to `--policy
168
+ new-only` when `--base-snapshot` is present. The effective policy is
169
+ derived at the use site: explicit flag wins, otherwise default to
170
+ `new-only` when a baseline is supplied, else `all`. Same behaviour for
171
+ users who omit `--policy`; no more "we secretly mutated your flag"
172
+ surprise for users who set it explicitly.
173
+ - The JSON output (`--format json`) is now versioned with a
174
+ `spec: 'autotel-eventcatalog-report/v0.1.0'` marker on every envelope,
175
+ so downstream tooling can detect and refuse unknown major versions.
176
+ Exported as `REPORT_SPEC` from the library.
177
+ - New e2e test suite (`cli.e2e.test.ts`) spawns the built CLI against
178
+ fixture catalogs and asserts exit codes + outputs for: clean state
179
+ with `--fail-on-drift`, drift with `--fail-on-drift`, drift without
180
+ `--fail-on-drift` (the original P0 regression class), `--policy
181
+ new-only` without `--base-snapshot`, versioned JSON output, unknown
182
+ flags, and `stamp --dry-run`. Run via `pnpm test:e2e`.
183
+
184
+ - Updated dependencies [de31d28]
185
+ - autotel-subscribers@32.1.0
186
+
187
+ All notable changes to `autotel-eventcatalog` land here. Entries are
188
+ generated by [Changesets](https://github.com/changesets/changesets) at
189
+ release time from the files in `.changeset/`; this file is the
190
+ authoritative history once each release ships.
191
+
192
+ For the current scope of the package, see [README.md](README.md). For
193
+ the version policy (when shapes change, when contracts break), see
194
+ [docs/UPGRADING.md](docs/UPGRADING.md).
195
+
196
+ <!-- Future Changesets-generated entries will be inserted above this line. -->
@@ -0,0 +1,212 @@
1
+ # Contributing to autotel-eventcatalog
2
+
3
+ A short, opinionated guide. The package is small enough that you can read all
4
+ of it in an afternoon; this doc is here so you don't have to.
5
+
6
+ ## Where things live
7
+
8
+ ```
9
+ packages/autotel-eventcatalog/
10
+ ├── src/
11
+ │ ├── snapshot.ts load + type-check an autotel architecture snapshot
12
+ │ ├── catalog.ts read the catalog from disk (POSIX-normalised paths)
13
+ │ ├── diff.ts compute drift between snapshot and catalog (pure)
14
+ │ ├── diff-vs-base.ts compute drift delta (PR head vs base) (pure)
15
+ │ ├── policy.ts "should this drift fail CI?" (pure)
16
+ │ ├── stamp.ts write runtime evidence into catalog mdx files
17
+ │ ├── renderers/ render adapters
18
+ │ │ ├── types.ts the Renderer interface
19
+ │ │ ├── markdown.ts Markdown (GitHub-flavoured), the default
20
+ │ │ ├── terminal.ts plain text (decorations stripped)
21
+ │ │ ├── json.ts versioned envelope; the public JSON contract
22
+ │ │ └── index.ts registry + getRenderer(name)
23
+ │ ├── report.ts backwards-compat re-export shim over renderers/
24
+ │ ├── cli.ts argument parsing + command dispatch + I/O
25
+ │ ├── __fixtures__/ golden JSON files for contract tests
26
+ │ └── *.test.ts unit tests
27
+ │ └── cli.e2e.test.ts e2e tests (spawn the built CLI)
28
+ │ └── contract.test.ts versioned-JSON contract tests
29
+ ├── schemas/
30
+ │ ├── drift-report-v0.1.0.json
31
+ │ ├── drift-summary-v0.1.0.json
32
+ │ └── stamp-summary-v0.1.0.json
33
+ ├── action.yml composite GitHub Action (used by external repos)
34
+ ├── docs/ this folder
35
+ └── README.md
36
+ ```
37
+
38
+ ## Module dependency rules
39
+
40
+ The DAG is one-way by design:
41
+
42
+ ```
43
+ snapshot.ts ──┐
44
+ ├──► diff.ts ──► diff-vs-base.ts ──► policy.ts
45
+ catalog.ts ───┤ ▲
46
+ │ │
47
+ ├──► stamp.ts │
48
+ │ │
49
+ └────► renderers/ ◄──────────────────┘
50
+
51
+ cli.ts ─► everything (the only place all branches meet)
52
+ ```
53
+
54
+ **Rules:**
55
+
56
+ 1. `diff.ts` / `diff-vs-base.ts` / `policy.ts` import only from each other,
57
+ `snapshot.ts`, and `catalog.ts`. They never import a renderer.
58
+ 2. `renderers/*` import from the core but the core never imports a renderer.
59
+ 3. `cli.ts` is the dispatcher; nothing else imports from `cli.ts`.
60
+ 4. `stamp.ts` is independent of `diff.ts`. They share inputs but no logic.
61
+
62
+ If a future change wants to break one of these rules, that's the signal to
63
+ stop and ask whether the new feature belongs in this package.
64
+
65
+ ## Load-bearing invariants
66
+
67
+ These are the rules I'd commit to keeping. Future-me, future-contributors:
68
+ when in doubt, push back.
69
+
70
+ ### 1. No new top-level commands without a user
71
+
72
+ The CLI has two: `drift` (verify) and `stamp` (generate). Three would be
73
+ the limit before splitting `cli.ts` into per-command modules. Four is the
74
+ signal to question whether the third and fourth belong in this package
75
+ at all.
76
+
77
+ A new command needs:
78
+
79
+ - A user with a concrete workflow that demands it
80
+ - Tests proving the behaviour
81
+ - A docs entry explaining when to reach for it
82
+
83
+ A new command does NOT need:
84
+
85
+ - "It would be nice to have"
86
+ - "Just in case"
87
+ - "While I'm in here"
88
+
89
+ ### 2. No runtime servers, daemons, watchers, or LSPs
90
+
91
+ This package is a CLI plus library plus composite GitHub Action. It runs,
92
+ does its job, and exits. Anything that needs to stay running (a live
93
+ dashboard, a file watcher, an LSP server, an MCP server, a webhook
94
+ listener) lives in [`autotel-subscribers`](../autotel-subscribers/) or in
95
+ example apps.
96
+
97
+ The reason: long-running processes have a different lifecycle from
98
+ command-line tools. Process management, signal handling, restart
99
+ semantics, observability. Mixing the two doubles the operational surface
100
+ for no clarity gain.
101
+
102
+ ### 3. No domain-specific extensions to the core
103
+
104
+ `diff.ts`, `diff-vs-base.ts`, and `policy.ts` work at one abstraction:
105
+ events, services, channels, field paths. Adding a SARIF renderer should
106
+ never require a `severity` field on `DriftCounts`. Adding a Slack
107
+ renderer should never require a `messageColor` field on `DriftReport`.
108
+
109
+ If a new renderer wants information that doesn't fit the existing types,
110
+ the renderer should derive it locally, not push back into the core. If
111
+ multiple renderers want the same derived information, _then_ it earns a
112
+ place in `diff.ts`. Pause and confirm it's about drift, not about
113
+ presentation.
114
+
115
+ ## How to do common things
116
+
117
+ ### Run the tests
118
+
119
+ ```bash
120
+ pnpm test # unit tests, fast, no build required
121
+ pnpm test:e2e # builds dist/, spawns the CLI, checks exit codes
122
+ pnpm test:watch # unit tests in watch mode
123
+ pnpm quality # type-check + unit + e2e + lint + format
124
+ ```
125
+
126
+ ### Add a renderer
127
+
128
+ See [docs/EXTENDING.md](docs/EXTENDING.md) for the full walkthrough. Short
129
+ version: create `src/renderers/<name>.ts` that exports a `Renderer`,
130
+ register it in `src/renderers/index.ts`, write tests that round-trip the
131
+ shape, document it in the README.
132
+
133
+ ### Change the public JSON shape
134
+
135
+ You can't change it silently. The `schemas/*.json` files and the
136
+ byte-equal golden fixtures in `src/__fixtures__/*.golden.json` will fail
137
+ CI on any shape change. Either:
138
+
139
+ - **Add an optional field**: bump the schema's _minor_ version (`v0.1.0` →
140
+ `v0.2.0`). Add the field with sensible defaults so existing consumers
141
+ keep working. Update goldens.
142
+ - **Break the shape** (rename a field, change a type, remove a field):
143
+ bump the schema's _major_ version (`v0.1.0` → `v1.0.0`). Document the
144
+ migration. Old consumers should refuse to parse the new envelope by
145
+ checking the `spec` field.
146
+
147
+ The `spec` field on every envelope is your one-way out.
148
+
149
+ ### Add a CLI flag
150
+
151
+ Update the parser in `cli.ts`, add it to the usage string, write an e2e
152
+ test that exercises it (`cli.e2e.test.ts`). If it changes which output is
153
+ produced, also update the golden fixture for that scenario.
154
+
155
+ ### Add a new diff category
156
+
157
+ (e.g. "tags observed but undocumented"; hypothetical)
158
+
159
+ 1. Add it to `DriftReport` in `diff.ts`
160
+ 2. Compute it in `diffCatalogAgainstSnapshot`
161
+ 3. Add it to `countDriftReport` in `diff.ts` so the total stays correct
162
+ 4. Add it to every renderer in `src/renderers/`
163
+ 5. Add it to the `DriftDelta` shape in `diff-vs-base.ts`
164
+ 6. Add it to `countDriftEntries`
165
+ 7. Bump the JSON schemas (probably minor)
166
+ 8. Update golden fixtures
167
+ 9. Tests for each of the above
168
+
169
+ New categories cost a lot. The reason this list is long is to make you
170
+ pause before adding one.
171
+
172
+ ## Commit conventions
173
+
174
+ Conventional Commits. The monorepo uses changesets; every PR with a
175
+ public-API change should include a changeset file (`pnpm changeset`).
176
+
177
+ For changes inside this package:
178
+
179
+ - `feat(autotel-eventcatalog): ...` for new functionality
180
+ - `fix(autotel-eventcatalog): ...` for bug fixes
181
+ - `refactor(autotel-eventcatalog): ...` for internal restructuring
182
+ - `docs(autotel-eventcatalog): ...` for documentation only
183
+ - `chore(autotel-eventcatalog): ...` for build, deps, tooling
184
+
185
+ ## Review checklist
186
+
187
+ Before opening a PR:
188
+
189
+ - [ ] `pnpm quality` passes locally
190
+ - [ ] If the public JSON shape changed, schema + golden fixtures + spec
191
+ version are all updated together
192
+ - [ ] If a new CLI flag landed, the usage string and e2e tests reflect it
193
+ - [ ] If a renderer landed, it's in the registry and has tests
194
+ - [ ] If the action.yml changed, any input default change is documented
195
+ in the README's action snippet
196
+ - [ ] Changeset present (`pnpm changeset`)
197
+ - [ ] The README's "What this package does NOT do" boundary is still true
198
+
199
+ ## Questions worth asking before adding anything
200
+
201
+ - Could this live in a downstream consumer instead of in this package?
202
+ - Is the new code reachable from an existing path, or is it a fork in the
203
+ road? (Forks are fine, but be deliberate.)
204
+ - Will the next contributor be confused about whether to put related code
205
+ with the existing thing or with the new thing? (If yes, the new thing
206
+ is in the wrong place.)
207
+ - Does this require a long-running process? (See invariant #2.)
208
+ - Does this require breaking the renderer-as-adapter pattern? (See
209
+ invariant #3.)
210
+
211
+ If you can't comfortably answer "no" or "this is fine", open an issue
212
+ first.