pattern-mcp 0.3.0 → 0.5.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.
package/README.md CHANGED
@@ -32,7 +32,7 @@ whether to:
32
32
 
33
33
  Pattern is designed for agents to use **while they are building**.
34
34
 
35
- It exposes four tools:
35
+ It exposes seven tools:
36
36
 
37
37
  - `recommend_component` — evaluates a UI component need and returns a
38
38
  structured recommendation.
@@ -44,7 +44,19 @@ It exposes four tools:
44
44
  account.
45
45
  - `read_ledger` — lists past `recommend_component` judgments for a
46
46
  `project_id`, including any that were served from the ledger cache (see
47
- [Per-project judgment ledger](#per-project-judgment-ledger)).
47
+ [Per-project judgment ledger](#per-project-judgment-ledger)); pass
48
+ `feature_id` instead of browsing by keyword to get a full cost rollup for
49
+ one feature (see [Tool: `report_build_cost`](#tool-report_build_cost)).
50
+ - `report_build_cost` — self-reports the end-to-end build cost for one
51
+ feature, so cost incurred after Pattern's own verdict (the actual
52
+ scaffold/install/build) is still attributable back to it.
53
+ - `report_outcome_proxy` — self-reports a value signal (rework, time to
54
+ merge, kept-vs-replaced) for one feature, deliberately independent of
55
+ Pattern's own verdict -- see [Outcome
56
+ proxies](#outcome-proxies).
57
+ - `check_ledger_liveness` — checks whether a ledger entry's recorded
58
+ `file_path` still exists and still references its `chosen_candidate` --
59
+ see [Tool: `check_ledger_liveness`](#tool-check_ledger_liveness).
48
60
 
49
61
  ## How it works
50
62
 
@@ -409,6 +421,27 @@ Leave `checklist` out to keep today's default behavior: `recommend_component`
409
421
  extracts its own checklist internally, exactly as before this option
410
422
  existed.
411
423
 
424
+ #### `feature_id`
425
+
426
+ `feature_id` is optional -- a stable identifier for the feature this
427
+ component need belongs to (e.g. a ticket id or branch name). Its only use
428
+ is joining this call's cost with a later
429
+ [`report_build_cost`](#tool-report_build_cost) call for the same feature.
430
+ Omit it to have one derived deterministically from `project_id` +
431
+ `component_need`; only meaningful together with `project_id`. See
432
+ [Feature cost attribution](#feature-cost-attribution).
433
+
434
+ #### `file_path`
435
+
436
+ `file_path` is optional -- path (relative to `PROJECT_ROOT`) where this
437
+ component decision is expected to be implemented, if already known.
438
+ Usually not known yet at call time, since the decision typically precedes
439
+ the file existing. When provided, it's stored on the resulting ledger
440
+ entry and [`check_ledger_liveness`](#tool-check_ledger_liveness) can later
441
+ confirm the file still exists and still references `chosen_candidate`. It
442
+ cannot currently be attached to an entry after the fact -- see [Ledger
443
+ integrity and decision provenance](#ledger-integrity-and-decision-provenance).
444
+
412
445
  **Is the checklist actually skipped, not just re-derived?** Checked, not
413
446
  assumed. `breakdown_ms.extract` for a `checklist`-provided call is smaller
414
447
  than the default path's, but not near-zero -- which raised the question of
@@ -667,6 +700,10 @@ came back with `served_from_ledger: true`.
667
700
  match, no embeddings) against stored entries' `component_need`. Omit to
668
701
  list everything for the project.
669
702
  - `limit` is optional, defaults to 20. Most recent entries first.
703
+ - `feature_id` is optional. When provided, `component_need` and `limit`
704
+ are ignored and the response is a full cost rollup for that one feature
705
+ instead of a keyword listing -- see [Feature cost
706
+ attribution](#feature-cost-attribution).
670
707
 
671
708
  ### Output
672
709
 
@@ -678,6 +715,7 @@ came back with `served_from_ledger: true`.
678
715
  "id": "a1b2c3d4-...",
679
716
  "timestamp": "2026-08-29T19:50:47.073Z",
680
717
  "project_id": "my-booking-app",
718
+ "feature_id": "3f9a21c0",
681
719
  "component_need": "cancellation policy display with refund tiers by date",
682
720
  "domain": "Airbnb-style rental marketplace",
683
721
  "framework": "React + Tailwind",
@@ -691,24 +729,291 @@ came back with `served_from_ledger: true`.
691
729
  "confidence": "low",
692
730
  "reason": "scored",
693
731
  "coverage": "5/8 (62.5%)",
694
- "project_conventions_snapshot": "9f3a1c7e2b0d4f5a"
732
+ "cost_usd": 0.087,
733
+ "cache_hit": false,
734
+ "project_conventions_snapshot": "9f3a1c7e2b0d4f5a",
735
+ "file_path": null,
736
+ "snapshot_ref": "a1b2c3d4e5f6...",
737
+ "last_verified_live": null,
738
+ "live_status": "unknown"
695
739
  }
696
740
  ]
697
741
  }
698
742
  ```
699
743
 
744
+ `file_path`/`snapshot_ref`/`last_verified_live`/`live_status` are the
745
+ ledger integrity + decision provenance fields -- see [Ledger integrity and
746
+ decision provenance](#ledger-integrity-and-decision-provenance) and [Tool:
747
+ `check_ledger_liveness`](#tool-check_ledger_liveness). Entries written
748
+ before this feature shipped read back with `file_path`/`snapshot_ref`/
749
+ `last_verified_live` as `null` and `live_status` as `"unknown"` rather
750
+ than missing keys.
751
+
752
+ Passing `feature_id` instead returns:
753
+
754
+ ```json
755
+ {
756
+ "project_id": "my-booking-app",
757
+ "feature_id": "3f9a21c0",
758
+ "verdict_entries": [ "...same shape as above, filtered to this feature_id..." ],
759
+ "build_records": [
760
+ { "id": "...", "timestamp": "...", "project_id": "my-booking-app", "feature_id": "3f9a21c0", "tokens_used": 9000, "cost_usd": 1.25, "outcome": "shipped" }
761
+ ],
762
+ "total_cost_usd": 1.34,
763
+ "outcome_proxy": { "time_to_merge_hours": 3.5, "reworked": true, "days_to_rework": 12, "status_at_30d": "kept" },
764
+ "outcome_proxy_history": [ "...every raw report_outcome_proxy record for this feature_id, oldest first..." ]
765
+ }
766
+ ```
767
+
768
+ `outcome_proxy` is `null` (and `outcome_proxy_history` an empty array)
769
+ when no `report_outcome_proxy` calls have been made for this feature yet
770
+ -- see [Outcome proxies](#outcome-proxies).
771
+
700
772
  Each entry holds only distilled fields -- `candidates_evaluated` never
701
773
  contains raw HTML, full prop tables, or the per-requirement evidence text
702
774
  `recommend_component` itself returns. See
703
775
  [Data minimization](#data-minimization) below.
704
776
 
777
+ ## Tool: `report_build_cost`
778
+
779
+ Self-reports the end-to-end build cost for one feature. Pattern only ever
780
+ sees the cost of judging *what* to use (`recommend_component`'s own
781
+ `_meta.estimated_cost_usd`); everything past that -- the actual scaffold,
782
+ install, or custom build -- happens outside Pattern entirely and Pattern
783
+ has no way to observe it. Call this once, after the calling agent's build
784
+ for a feature is actually complete (shipped, abandoned, or replaced), not
785
+ on every verdict.
786
+
787
+ ### Input
788
+
789
+ ```json
790
+ {
791
+ "feature_id": "3f9a21c0",
792
+ "project_id": "my-booking-app",
793
+ "tokens_used": 9000,
794
+ "cost_usd": 1.25,
795
+ "outcome": "shipped"
796
+ }
797
+ ```
798
+
799
+ - `feature_id` is required -- either a value you explicitly passed to an
800
+ earlier `recommend_component` call for this feature, or (if you didn't)
801
+ the same value `recommend_component` derives on its own:
802
+ `sha256(project_id + "::" + component_need, lowercased/trimmed)`
803
+ truncated to 8 hex characters. When in doubt, call `read_ledger` with
804
+ just `project_id` and copy the `feature_id` off the relevant entry
805
+ rather than re-deriving it by hand.
806
+ - `project_id` is optional but recommended -- without it, this record
807
+ still joins to a `recommend_component` entry by `feature_id` alone, but
808
+ `read_ledger`'s rollup can't scope it to one project.
809
+ - `tokens_used` is optional.
810
+ - `cost_usd` is required -- your own real number, not Pattern's.
811
+ - `outcome` is required: `"shipped"`, `"abandoned"`, or
812
+ `"replaced_with_existing"`.
813
+
814
+ ### Output
815
+
816
+ ```json
817
+ {
818
+ "status": "recorded",
819
+ "record": {
820
+ "id": "c5706b47-...",
821
+ "timestamp": "2026-09-02T01:25:29.653Z",
822
+ "project_id": "my-booking-app",
823
+ "feature_id": "3f9a21c0",
824
+ "tokens_used": 9000,
825
+ "cost_usd": 1.25,
826
+ "outcome": "shipped"
827
+ }
828
+ }
829
+ ```
830
+
831
+ This only appends a local record to `~/.pattern/build_ledger.jsonl`
832
+ (override with `PATTERN_BUILD_LEDGER_PATH`) -- it never re-runs any
833
+ judgment and never calls the Anthropic API.
834
+
835
+ ## Tool: `report_outcome_proxy`
836
+
837
+ Self-reports a value signal for one feature, deliberately independent of
838
+ Pattern's own verdict -- the whole point is a signal that could
839
+ *contradict* the verdict, so nothing on this path ever reads
840
+ `coverage_pct`, `confidence`, or any other Pattern-produced field. Compute
841
+ `reworked`/`days_to_rework` and `time_to_merge_hours` from your own repo's
842
+ real git history (e.g. `git log --follow` against the files this
843
+ feature's build touched) rather than relying on Pattern -- rework rate and
844
+ time-to-merge need real git *history*, a materially bigger surface than
845
+ the one narrow, read-only exception described in [Ledger integrity and
846
+ decision provenance](#ledger-integrity-and-decision-provenance) below.
847
+ Report `status_at_30d` only once a real ~30-day-post-merge horizon has
848
+ actually passed.
849
+
850
+ Safe to call more than once for the same `feature_id` as more signal
851
+ becomes available over time -- e.g. `time_to_merge_hours` right after
852
+ merge, `reworked` on a later re-check, `status_at_30d` at the 30-day mark.
853
+ `read_ledger`'s `feature_id` rollup merges every report into one
854
+ latest-value-per-field view (a later report only overwrites the specific
855
+ fields it includes, never the others).
856
+
857
+ ### Input
858
+
859
+ ```json
860
+ {
861
+ "feature_id": "3f9a21c0",
862
+ "project_id": "my-booking-app",
863
+ "reworked": true,
864
+ "days_to_rework": 12
865
+ }
866
+ ```
867
+
868
+ - `feature_id` is required.
869
+ - `project_id` is optional but recommended, same reasoning as
870
+ `report_build_cost`.
871
+ - `reworked`, `days_to_rework`, `time_to_merge_hours`, `status_at_30d` are
872
+ all individually optional, but **at least one is required** -- an empty
873
+ report is rejected rather than silently recording nothing.
874
+
875
+ ### Output
876
+
877
+ ```json
878
+ {
879
+ "status": "recorded",
880
+ "record": {
881
+ "id": "8a2f1e0c-...",
882
+ "timestamp": "2026-09-16T18:04:12.881Z",
883
+ "project_id": "my-booking-app",
884
+ "feature_id": "3f9a21c0",
885
+ "reworked": true,
886
+ "days_to_rework": 12
887
+ }
888
+ }
889
+ ```
890
+
891
+ This only appends a local record to `~/.pattern/outcome_proxies.jsonl`
892
+ (override with `PATTERN_OUTCOME_PROXY_PATH`) -- it never calls the
893
+ Anthropic API.
894
+
895
+ ## Tool: `check_ledger_liveness`
896
+
897
+ Checks whether ledger entries for a `project_id` are still **live** --
898
+ does the `file_path` recorded on the entry (if any, see
899
+ [`file_path`](#tool-recommend_component)) still exist, and does it still
900
+ mention `chosen_candidate`. See [Ledger integrity and decision
901
+ provenance](#ledger-integrity-and-decision-provenance) for the full design
902
+ and its deliberate limits.
903
+
904
+ This is the **one exception** to Pattern otherwise having no filesystem
905
+ access to your repo (see [Outcome proxies](#outcome-proxies) above) --
906
+ scoped narrowly to read-only `fs.existsSync`/file-read calls against
907
+ `PROJECT_ROOT` (defaults to this server's own working directory; override
908
+ with `PATTERN_PROJECT_ROOT`). It never writes to your repo and never runs
909
+ an arbitrary shell command.
910
+
911
+ ### Input
912
+
913
+ ```json
914
+ {
915
+ "project_id": "my-booking-app",
916
+ "ledger_entry_id": "a1b2c3d4-..."
917
+ }
918
+ ```
919
+
920
+ - `project_id` is required.
921
+ - `ledger_entry_id` is optional -- check just that one entry instead of
922
+ every entry for `project_id` that has a `file_path` set.
923
+
924
+ ### Output
925
+
926
+ ```json
927
+ {
928
+ "project_id": "my-booking-app",
929
+ "checked": 1,
930
+ "total_entries": 2,
931
+ "results": [
932
+ {
933
+ "ledger_entry_id": "a1b2c3d4-...",
934
+ "component_need": "cancellation policy display with refund tiers by date",
935
+ "file_path": "src/components/CancellationPolicy.tsx",
936
+ "live_status": "live",
937
+ "checked_at": "2026-09-02T20:11:03.442Z",
938
+ "note": null
939
+ },
940
+ {
941
+ "ledger_entry_id": "e5f6a7b8-...",
942
+ "component_need": "gallery",
943
+ "file_path": null,
944
+ "live_status": "unknown",
945
+ "checked_at": null,
946
+ "note": "no file_path recorded on this entry -- nothing to check"
947
+ }
948
+ ]
949
+ }
950
+ ```
951
+
952
+ `live_status` is one of `"live"`, `"orphaned"`, `"unknown"`, or
953
+ (reserved, not yet produced -- see [Ledger integrity and decision
954
+ provenance](#ledger-integrity-and-decision-provenance)) `"dangling"`.
955
+ Entries with no `file_path` are listed but never checked or written to
956
+ `ledger_liveness.jsonl` -- their status is permanently `"unknown"` since
957
+ there's nothing to check. Results here are also layered onto
958
+ `read_ledger`'s `live_status`/`last_verified_live` fields for the same
959
+ entries afterward -- `check_ledger_liveness` is the only thing that
960
+ advances those fields past their write-time defaults.
961
+
962
+ ## Feature cost attribution
963
+
964
+ Every `recommend_component` call that writes to the ledger -- a fresh
965
+ judgment *or* a $0 [ledger cache hit](#the-cache-hit-exception) -- now
966
+ carries a `feature_id`, plus its own `cost_usd` and `cache_hit`. Pair that
967
+ with `report_build_cost`'s build-time record and `read_ledger`'s
968
+ `feature_id` rollup, and total spend on a feature (judgment + build,
969
+ across however many calls) is queryable end to end, not just the cost of
970
+ one verdict call.
971
+
972
+ `feature_id` defaults to a deterministic derivation --
973
+ `sha256(project_id + "::" + component_need)` truncated to 8 hex chars --
974
+ so repeat calls for the same feature land under the same id automatically,
975
+ with no coordination needed between `recommend_component` and
976
+ `report_build_cost` calls. Pass your own `feature_id` explicitly (e.g. a
977
+ ticket id or branch name) if you'd rather key on something stable on your
978
+ own side.
979
+
980
+ ## Outcome proxies
981
+
982
+ Cost data alone (`feature cost attribution` above) can't answer whether a
983
+ cheaper build was actually *worth it* -- comparing it against Pattern's
984
+ own verdict/`coverage_pct` would be circular, since that's the very thing
985
+ being evaluated. `report_outcome_proxy` attaches a cheap, non-circular
986
+ value signal per `feature_id` instead:
987
+
988
+ - **`reworked` / `days_to_rework`** (primary proxy) -- was any file this
989
+ feature's build touched modified again after the original merge, and if
990
+ so, how soon? Computed from real git history, not Pattern's own data.
991
+ - **`time_to_merge_hours`** (secondary proxy) -- how long the feature
992
+ took from first commit to merge.
993
+ - **`status_at_30d`** (tertiary, longer-horizon proxy) -- at a ~30-day
994
+ horizon, does the component Pattern recommended still exist in the
995
+ codebase, unchanged in kind (`"kept"`), was it swapped for a different
996
+ approach (`"replaced"`), or removed entirely (`"removed"`)?
997
+
998
+ `read_ledger`'s `feature_id` rollup returns both `outcome_proxy` (the
999
+ merged latest-value-per-field view) and `outcome_proxy_history` (every
1000
+ raw report, in case the timeline itself matters) alongside the cost
1001
+ figures from [Feature cost attribution](#feature-cost-attribution) above
1002
+ -- so "what did this feature cost end to end, and did it hold up?" is
1003
+ answerable from one `read_ledger` call.
1004
+
705
1005
  ## Per-project judgment ledger
706
1006
 
707
1007
  Distinct from [per-project decision memory](#per-project-decision-memory)
708
1008
  below -- that file only gains an entry when `record_component_decision` is
709
1009
  explicitly called. The ledger instead gains one entry automatically for
710
- **every** `recommend_component` call that reaches the API with a
711
- `project_id` and lands on reason `"scored"` or `"no_candidates_found"`.
1010
+ **every** `recommend_component` call with a `project_id` that lands on
1011
+ reason `"scored"` or `"no_candidates_found"` -- whether that's a fresh
1012
+ call that reached the API, or a $0 [ledger cache
1013
+ hit](#the-cache-hit-exception) served without one (`cache_hit: true`,
1014
+ `cost_usd: 0`), so a feature's total cost still rolls up correctly even
1015
+ once most of its later calls are free. See [Feature cost
1016
+ attribution](#feature-cost-attribution).
712
1017
 
713
1018
  Pattern stores it locally in:
714
1019
 
@@ -769,6 +1074,96 @@ convention: a raw or extended object throws rather than silently
769
1074
  persisting. Run `node scripts/verify-ledger-boundary.mjs` (after
770
1075
  `npm run build`) to check this boundary directly.
771
1076
 
1077
+ ## Ledger integrity and decision provenance
1078
+
1079
+ Two gaps in the ledger, surfaced from user feedback: it tracks that a
1080
+ decision was made, but not whether the thing it decided about is still
1081
+ live in your codebase, and it stores the checklist/verdict but not a
1082
+ version pin or an exportable artifact you can attach to a PR or issue.
1083
+ This section covers what's shipped so far -- **P0/P1 of both halves**, not
1084
+ the full spec. See `pattern-ledger-integrity-and-provenance-spec.md` for
1085
+ the complete phased plan; P2/P3 (a scheduled/batch sweep, dangling-cluster
1086
+ detection, the provenance-artifact exporter, and GitHub PR/issue posting)
1087
+ are not built yet.
1088
+
1089
+ **This is the one deliberate exception** to Pattern otherwise having [no
1090
+ filesystem/git access to your repo](#per-project-judgment-ledger) at all
1091
+ (the principle `report_build_cost`/`report_outcome_proxy` are built
1092
+ around). It's narrow on purpose:
1093
+
1094
+ - `git rev-parse HEAD` (read-only, never touches repo state) to capture
1095
+ `snapshot_ref` on every ledger write.
1096
+ - `fs.existsSync` plus a plain-text read of one file, only for a
1097
+ `file_path` you explicitly passed to `recommend_component`, only inside
1098
+ `PROJECT_ROOT` (see below), to answer `check_ledger_liveness`.
1099
+
1100
+ Nothing here runs an arbitrary git or shell command, and nothing writes to
1101
+ your repo.
1102
+
1103
+ ### `PROJECT_ROOT`
1104
+
1105
+ Defaults to `process.cwd()` -- for a locally-run stdio MCP server, that's
1106
+ normally the consuming repo's root, since MCP hosts typically launch the
1107
+ server with the project directory as its working directory. Override with
1108
+ `PATTERN_PROJECT_ROOT` if that assumption doesn't hold for your setup.
1109
+
1110
+ A `file_path` that's absolute or escapes `PROJECT_ROOT` via `../` resolves
1111
+ to `live_status: "unknown"` rather than being read -- belt-and-suspenders,
1112
+ since the calling agent already has real filesystem access to its own
1113
+ machine regardless.
1114
+
1115
+ ### Decision provenance: `snapshot_ref`
1116
+
1117
+ Every ledger entry -- fresh judgment or [ledger cache
1118
+ hit](#the-cache-hit-exception) -- now carries `snapshot_ref`: the commit
1119
+ SHA of `PROJECT_ROOT` at the moment that line was written, or `null` when
1120
+ `PROJECT_ROOT` isn't a git repo (or `git` isn't installed, or the call
1121
+ times out) -- this never fails the underlying `recommend_component` call.
1122
+ Entries written before this shipped read back with `snapshot_ref: null`.
1123
+
1124
+ A cache-hit entry's `snapshot_ref` reflects the codebase state *when that
1125
+ cache-hit line was written*, not the original judgment's -- to see the
1126
+ original judgment's snapshot, look up the entry named in its
1127
+ `ledger_entry_id`/`original_verdict_timestamp` fields instead.
1128
+
1129
+ Not yet built (P1-P3 of Feature 2): a markdown export of one entry's full
1130
+ record for attaching to a PR/issue, and the GitHub posting action itself.
1131
+
1132
+ ### Referential integrity: `file_path` / `live_status`
1133
+
1134
+ `recommend_component` optionally accepts `file_path` (see [Tool:
1135
+ `recommend_component`](#tool-recommend_component)) -- usually not known at
1136
+ call time, since the decision typically precedes the file existing. When
1137
+ set, [`check_ledger_liveness`](#tool-check_ledger_liveness) can later
1138
+ check whether that file still exists and still mentions
1139
+ `chosen_candidate`:
1140
+
1141
+ - **`live`** -- the file exists and mentions `chosen_candidate`.
1142
+ - **`orphaned`** -- `file_path` is set but the file no longer exists.
1143
+ - **`unknown`** -- no `file_path` was ever recorded, the path escapes
1144
+ `PROJECT_ROOT`, or the file exists but `chosen_candidate` can't be
1145
+ confirmed in it. Deliberately the default outcome for anything
1146
+ ambiguous: a false `"orphaned"` is worse than a lingering `"unknown"`.
1147
+ - **`dangling`** -- reserved, not yet produced. Feature 1's second
1148
+ staleness type (a cluster of entries that only reference each other,
1149
+ with no live anchor anywhere) is graph-level analysis across the whole
1150
+ ledger, not a single-entry check -- P3, not built here.
1151
+
1152
+ Checks are on-demand only right now (call `check_ledger_liveness`
1153
+ yourself, or on whatever schedule you want) -- there's no automatic
1154
+ sweep. `live_status`/`last_verified_live` start `"unknown"`/`null` on
1155
+ every entry at write time and only ever advance via a
1156
+ `check_ledger_liveness` call; results are stored append-only in
1157
+ `~/.pattern/ledger_liveness.jsonl` (override with
1158
+ `PATTERN_LEDGER_LIVENESS_PATH`, same "append, never mutate the source
1159
+ line, most recent record wins at read time" convention as
1160
+ `outcome_proxies.jsonl`, see [Outcome proxies](#outcome-proxies)) and
1161
+ layered onto `ledger.jsonl`'s own entries at read time -- the ledger line
1162
+ itself is never rewritten.
1163
+
1164
+ Not yet built (P2-P3 of Feature 1): a scheduled/batch sweep across an
1165
+ entire ledger, and dangling-cluster detection.
1166
+
772
1167
  ## Per-project decision memory
773
1168
 
774
1169
  Pattern stores confirmed decisions locally in:
@@ -837,9 +1232,75 @@ recommendations.
837
1232
  Local project memory and the local call log are stored on the machine
838
1233
  running Pattern. They are not sent anywhere by Pattern itself.
839
1234
 
1235
+ The one exception is opt-in telemetry, off by default -- see
1236
+ [Telemetry](#telemetry) below for exactly what it sends and how to turn
1237
+ it on or off.
1238
+
840
1239
  Review [SECURITY.md](./SECURITY.md) before putting sensitive information
841
1240
  into fields such as `component_need`, `domain`, or project IDs.
842
1241
 
1242
+ ## Telemetry
1243
+
1244
+ Off by default. Nothing is sent anywhere for telemetry purposes unless
1245
+ you explicitly set:
1246
+
1247
+ ```
1248
+ PATTERN_TELEMETRY=1
1249
+ ```
1250
+
1251
+ **The one-time notice.** The first time you run this version of Pattern
1252
+ -- whether it's a brand-new install or an upgrade from a version before
1253
+ telemetry existed -- it prints a short notice to stderr explaining all of
1254
+ this and how to opt in. It prints exactly once, ever (tracked by a marker
1255
+ file at `~/.pattern/telemetry_notice_shown`), then never again, regardless
1256
+ of whether you act on it. There's no interactive y/n prompt: Pattern's
1257
+ stdin is the MCP JSON-RPC channel the client uses to talk to it, so
1258
+ blocking on stdin for a keypress would fight the protocol handshake
1259
+ instead of showing a dialog -- a stderr notice is the safe equivalent for
1260
+ a stdio MCP server.
1261
+
1262
+ **Why it exists.** Two things about real usage can't be answered from
1263
+ this repo alone: whether people actually come back and use Pattern on a
1264
+ second or third project on their own, and how often a BYO Anthropic key
1265
+ actually hits a rate limit or runs out of credit in real sessions, not
1266
+ just the one time that happened during manual testing (see
1267
+ [Known limitations](#known-limitations)). Telemetry answers both without
1268
+ requiring anyone to fill out a survey.
1269
+
1270
+ **What gets sent, when enabled:**
1271
+
1272
+ - An anonymous, randomly generated install ID -- a UUID created once and
1273
+ stored at `~/.pattern/install_id` (overridable via
1274
+ `PATTERN_INSTALL_ID_PATH`), never derived from your machine, username,
1275
+ or any other identifying information. This is the only thing that ties
1276
+ two events together as "the same install."
1277
+ - A one-way SHA-256 hash of `project_id`, truncated to 16 hex characters
1278
+ -- never the raw `project_id` string. The hash lets Pattern count how
1279
+ many *distinct* projects one install has used, without ever seeing what
1280
+ those projects are named.
1281
+ - On every `recommend_component` call that reaches the API or the ledger
1282
+ cache-hit shortcut: `verdict`, `confidence`, `reason`,
1283
+ `ensemble_triggered`, `estimated_cost_usd`, and `served_from_ledger` --
1284
+ the same distilled shape already written to the
1285
+ [local call log](#local-call-log), not new information.
1286
+ - On a failed Anthropic API call specifically: the HTTP status code and a
1287
+ coarse classification (`rate_limit`, `insufficient_credit`, or `other`)
1288
+ -- never the request or response body.
1289
+
1290
+ **What never gets sent, telemetry on or off:** `component_need`,
1291
+ `domain`, `framework`, `existing_stack`, `requirements_checked` evidence,
1292
+ the raw `project_id`, or your Anthropic API key.
1293
+
1294
+ **Where it goes.** Events go to Pattern's PostHog project via its public,
1295
+ write-only project key (safe to ship in source -- it can send events, it
1296
+ cannot read data back). Set `PATTERN_POSTHOG_KEY` /
1297
+ `PATTERN_POSTHOG_HOST` to point at a different project, e.g. for
1298
+ self-hosting.
1299
+
1300
+ **Turning it off** is the default -- just don't set `PATTERN_TELEMETRY`.
1301
+ If you'd previously enabled it, unset the variable (or set it to `0`) to
1302
+ go back to fully local.
1303
+
843
1304
  ## Cost
844
1305
 
845
1306
  Pattern uses the Anthropic API, so `recommend_component` has a cost.