pattern-mcp 0.7.0 → 0.8.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 +314 -155
- package/dist/index.js +631 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,15 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/donaldrichard19-LVD/pattern-mcp/actions/workflows/publish.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/pattern-mcp)
|
|
5
|
-
[](https://www.npmjs.com/package/pattern-mcp)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
|
-
Pattern
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Pattern solves a simple problem: agents don't always follow your design
|
|
9
|
+
guidance. Instead of relying on the agent to interpret and follow
|
|
10
|
+
instructions, Pattern turns that guidance into a checkable process. It
|
|
11
|
+
evaluates UI components from external libraries or your own design
|
|
12
|
+
system against a requirements checklist, then tells the agent whether to
|
|
13
|
+
use an existing component or build one from scratch using a concrete
|
|
14
|
+
design reference.
|
|
11
15
|
|
|
12
16
|
[Website](https://usepattern.sh) · [npm](https://www.npmjs.com/package/pattern-mcp) · [Report an issue](https://github.com/donaldrichard19-LVD/pattern-mcp/issues/new/choose)
|
|
13
17
|
|
|
18
|
+
<details>
|
|
19
|
+
<summary><strong>Contents</strong> (click to expand)</summary>
|
|
20
|
+
|
|
21
|
+
- [Install](#install) · [What Pattern Does](#what-pattern-does) · [How it works](#how-it-works) · [Quick Start](#quick-start) · [Try it](#try-it) · [Validation examples](#validation-examples)
|
|
22
|
+
- **Make the judgment call:** [`recommend_component`](#tool-recommend_component) · [`extract_requirements`](#tool-extract_requirements)
|
|
23
|
+
- **Track cost and outcome:** [`record_component_decision`](#tool-record_component_decision) · [`read_ledger`](#tool-read_ledger) · [`report_build_cost`](#tool-report_build_cost) · [`report_outcome_proxy`](#tool-report_outcome_proxy) · [Feature cost attribution](#feature-cost-attribution) · [Outcome proxies](#outcome-proxies) · [Per-project judgment ledger](#per-project-judgment-ledger)
|
|
24
|
+
- **Verify and export old decisions:** [`check_ledger_liveness`](#tool-check_ledger_liveness) · [`sweep_ledger_liveness`](#tool-sweep_ledger_liveness) · [`export_ledger_provenance`](#tool-export_ledger_provenance) · [`backfill_ledger_snapshot_ref`](#tool-backfill_ledger_snapshot_ref) · [`post_ledger_provenance_to_github`](#tool-post_ledger_provenance_to_github) · [Ledger integrity and decision provenance](#ledger-integrity-and-decision-provenance) (design overview — start here for how the five fit together)
|
|
25
|
+
- [Per-project decision memory](#per-project-decision-memory) · [Security and privacy](#security-and-privacy) · [Telemetry](#telemetry)
|
|
26
|
+
- **Cost:** [The `_meta` field](#the-_meta-field) · [Prompt caching](#prompt-caching) · [Measured cache and fetch behavior](#measured-cache-and-fetch-behavior) · [Search limits](#search-limits) · [Ensemble cost](#ensemble-cost-boundary-risk-cases-only) · [Session call cap](#session-call-cap)
|
|
27
|
+
- [Local call log](#local-call-log) · [Known limitations](#known-limitations)
|
|
28
|
+
|
|
29
|
+
</details>
|
|
30
|
+
|
|
14
31
|
## Install
|
|
15
32
|
|
|
16
33
|
```bash
|
|
@@ -32,13 +49,18 @@ whether to:
|
|
|
32
49
|
|
|
33
50
|
Pattern is designed for agents to use **while they are building**.
|
|
34
51
|
|
|
35
|
-
It exposes eleven tools:
|
|
52
|
+
It exposes eleven tools, in three groups:
|
|
53
|
+
|
|
54
|
+
**Make the judgment call.**
|
|
36
55
|
|
|
37
56
|
- `recommend_component` — evaluates a UI component need and returns a
|
|
38
57
|
structured recommendation.
|
|
39
58
|
- `extract_requirements` — runs just the requirement-extraction step on
|
|
40
59
|
its own, so you can inspect or hand-edit the checklist before
|
|
41
60
|
`recommend_component` spends its search+score budget on it.
|
|
61
|
+
|
|
62
|
+
**Track what it cost and what actually happened.**
|
|
63
|
+
|
|
42
64
|
- `record_component_decision` — records what the agent actually did so
|
|
43
65
|
future recommendations in the same project can take that decision into
|
|
44
66
|
account.
|
|
@@ -54,26 +76,27 @@ It exposes eleven tools:
|
|
|
54
76
|
merge, kept-vs-replaced) for one feature, deliberately independent of
|
|
55
77
|
Pattern's own verdict -- see [Outcome
|
|
56
78
|
proxies](#outcome-proxies).
|
|
79
|
+
|
|
80
|
+
**Verify old decisions still hold up, and export a record of them.** See
|
|
81
|
+
[Ledger integrity and decision
|
|
82
|
+
provenance](#ledger-integrity-and-decision-provenance) for how these five
|
|
83
|
+
fit together.
|
|
84
|
+
|
|
57
85
|
- `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).
|
|
60
|
-
- `export_ledger_provenance` — formats one ledger entry as a stable
|
|
61
|
-
markdown block (checklist, candidates, verdict, `snapshot_ref`) you can
|
|
62
|
-
paste into a PR or issue by hand -- see [Tool:
|
|
63
|
-
`export_ledger_provenance`](#tool-export_ledger_provenance).
|
|
64
|
-
- `post_ledger_provenance_to_github` — posts that same artifact as a real
|
|
65
|
-
comment on a GitHub PR/issue, idempotently -- see [Tool:
|
|
66
|
-
`post_ledger_provenance_to_github`](#tool-post_ledger_provenance_to_github).
|
|
67
|
-
The one tool here with a real, visible side effect outside your own
|
|
68
|
-
machine; confirm with the user before calling it.
|
|
86
|
+
`file_path` still exists and still references its `chosen_candidate`.
|
|
69
87
|
- `sweep_ledger_liveness` — batch version of `check_ledger_liveness`
|
|
70
88
|
across a whole project (or every project in the ledger), plus
|
|
71
|
-
dangling-cluster detection
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
dangling-cluster detection. Meant to be invoked by your own cron/CI, not
|
|
90
|
+
something Pattern schedules itself.
|
|
91
|
+
- `export_ledger_provenance` — formats one ledger entry as a stable
|
|
92
|
+
markdown block (checklist, candidates, verdict, `snapshot_ref`) you can
|
|
93
|
+
paste into a PR or issue by hand.
|
|
74
94
|
- `backfill_ledger_snapshot_ref` — best-effort `snapshot_ref`
|
|
75
|
-
reconstruction for entries written before that field existed
|
|
76
|
-
|
|
95
|
+
reconstruction for entries written before that field existed.
|
|
96
|
+
- `post_ledger_provenance_to_github` — posts an `export_ledger_provenance`
|
|
97
|
+
artifact as a real comment on a GitHub PR/issue, idempotently. The one
|
|
98
|
+
tool here with a real, visible side effect outside your own machine;
|
|
99
|
+
confirm with the user before calling it.
|
|
77
100
|
|
|
78
101
|
## How it works
|
|
79
102
|
|
|
@@ -577,6 +600,114 @@ The calling agent should:
|
|
|
577
600
|
|
|
578
601
|
See [SECURITY.md](./SECURITY.md) for more details.
|
|
579
602
|
|
|
603
|
+
## Tool: `register_design_system`
|
|
604
|
+
|
|
605
|
+
Points `recommend_component` at *this project's own* design system instead
|
|
606
|
+
of shadcn/ui, 21st.dev, and ReUI -- for a solo dev with their own component
|
|
607
|
+
library or design spec who wants Pattern's coverage scoring against
|
|
608
|
+
candidates they'll actually use, not external libraries they won't. This is
|
|
609
|
+
the Solo Dev architecture from `pattern-solo-design-system-architecture.md`:
|
|
610
|
+
local, per-project, one-or-the-other -- registering a design system for a
|
|
611
|
+
`project_id` **replaces** external-library scoring for that project
|
|
612
|
+
entirely, it does not add to it. There's no shared/remote ledger, no
|
|
613
|
+
multi-user attribution, and no team auth in this scope -- those are
|
|
614
|
+
deliberately deferred to a team phase, only if this use case proves out.
|
|
615
|
+
|
|
616
|
+
### Input
|
|
617
|
+
|
|
618
|
+
Exactly one of `manifest_path` or `directory_path` is required, both
|
|
619
|
+
relative to the project root (`PATTERN_PROJECT_ROOT`, defaults to this
|
|
620
|
+
server's working directory) -- never an absolute path.
|
|
621
|
+
|
|
622
|
+
```json
|
|
623
|
+
{
|
|
624
|
+
"project_id": "my-booking-app",
|
|
625
|
+
"directory_path": "src/components"
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
- **`manifest_path`** -- a components manifest. Two recognized shapes:
|
|
630
|
+
- A hand-authored JSON array of `{name, props, description,
|
|
631
|
+
usage_example}` objects, optionally wrapped in `{"components": [...]}`.
|
|
632
|
+
- A Storybook-exported `stories.json`/`index.json` file (an object with a
|
|
633
|
+
top-level `entries` or `stories` map). Component names only in this
|
|
634
|
+
case -- Storybook's basic export doesn't carry prop data, so candidates
|
|
635
|
+
from this path start with an empty `props` list.
|
|
636
|
+
- **`directory_path`** -- a directory of real component source files,
|
|
637
|
+
scanned recursively for `.jsx`/`.tsx`/`.js`/`.ts` files (excluding
|
|
638
|
+
`node_modules`/`dist`/`build`/`.git` and `.test.`/`.spec.`/`.stories.`
|
|
639
|
+
files). Each exported, uppercase-named function or const component found
|
|
640
|
+
becomes a candidate, with props read in priority order from a
|
|
641
|
+
`<Name>Props` interface/type, a `.propTypes` block, or (last resort) the
|
|
642
|
+
component's own destructured parameters. This is a heuristic scan, not a
|
|
643
|
+
full parser -- a sparse or partial props list for some components is
|
|
644
|
+
expected, not a bug, especially on plain JS with no prop typing at all.
|
|
645
|
+
|
|
646
|
+
### Output
|
|
647
|
+
|
|
648
|
+
```json
|
|
649
|
+
{
|
|
650
|
+
"status": "registered",
|
|
651
|
+
"registration": {
|
|
652
|
+
"project_id": "my-booking-app",
|
|
653
|
+
"source_kind": "directory_scan",
|
|
654
|
+
"source_path": "src/components",
|
|
655
|
+
"registered_at": "2026-09-03T18:04:11.201Z",
|
|
656
|
+
"candidate_count": 29,
|
|
657
|
+
"candidates": [
|
|
658
|
+
{ "name": "ReferralBanner", "props": ["code", "bonusAmount"], "description": null, "usage_example": null, "file_path": "rewards/ReferralBanner.jsx" }
|
|
659
|
+
]
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Registering overwrites (does not merge with) any prior registration for the
|
|
665
|
+
same `project_id`. Once registered, `recommend_component` scores ONLY
|
|
666
|
+
against these candidates for calls with this `project_id` -- no separate
|
|
667
|
+
flag needed, it's automatic based on `project_id` alone, and step 3's live
|
|
668
|
+
web search is skipped entirely (`web_search` is still available, but
|
|
669
|
+
reserved for a `custom_build` verdict's Mobbin/Figma Community reference
|
|
670
|
+
grounding, same as the external-library path). A `use_existing` verdict
|
|
671
|
+
scored this way always carries `"source": "design_system"` on the
|
|
672
|
+
resulting ledger entry, set server-side regardless of what the model wrote,
|
|
673
|
+
so `read_ledger` and `export_ledger_provenance` can match on it reliably.
|
|
674
|
+
|
|
675
|
+
This only writes local config to `~/.pattern/design_systems.json` (override
|
|
676
|
+
with `PATTERN_DESIGN_SYSTEMS_PATH`) -- it never calls the Anthropic API.
|
|
677
|
+
Registration is a point-in-time snapshot, not a live link: re-run this
|
|
678
|
+
whenever the design system's own components change meaningfully.
|
|
679
|
+
|
|
680
|
+
### A safety net for a missed match
|
|
681
|
+
|
|
682
|
+
The model can occasionally say `custom_build`/`no_candidates_found`
|
|
683
|
+
against a registered design system even when a real match is sitting
|
|
684
|
+
right there in its own prompt -- a reading-comprehension miss over its own
|
|
685
|
+
known-complete candidate list, not evidence the list was actually empty.
|
|
686
|
+
When this happens, `recommend_component`'s response may carry a
|
|
687
|
+
`design_system_recall_check` field: a deterministic, zero-cost, local
|
|
688
|
+
keyword-overlap check (component name, props, description/usage_example
|
|
689
|
+
vs. `component_need`/`domain`) run automatically whenever reason is
|
|
690
|
+
`no_candidates_found` in this mode.
|
|
691
|
+
|
|
692
|
+
```json
|
|
693
|
+
{
|
|
694
|
+
"verdict": "custom_build",
|
|
695
|
+
"reason": "no_candidates_found",
|
|
696
|
+
"design_system_recall_check": {
|
|
697
|
+
"possible_missed_candidates": [
|
|
698
|
+
{ "name": "ReferralBanner", "shared_keywords": ["referral", "bonus"] }
|
|
699
|
+
],
|
|
700
|
+
"note": "These registered design-system candidates share keywords with this component_need but were not selected as a match -- the verdict may have missed a real one. This is a weak, keyword-only signal, not proof of an actual match: double-check these candidates yourself (or re-run this call) before trusting custom_build here."
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
This never overrides the verdict -- a shared keyword is weak evidence, not
|
|
706
|
+
proof of a real match -- it only surfaces the risk so you (or the calling
|
|
707
|
+
agent) know to double-check before accepting a `custom_build` verdict at
|
|
708
|
+
face value. Absent entirely when there's no overlap, or outside
|
|
709
|
+
design-system mode.
|
|
710
|
+
|
|
580
711
|
## Tool: `extract_requirements`
|
|
581
712
|
|
|
582
713
|
Runs only the requirement-extraction step `recommend_component` normally
|
|
@@ -981,123 +1112,6 @@ there's nothing to check. Results here are also layered onto
|
|
|
981
1112
|
entries afterward -- `check_ledger_liveness` is the only thing that
|
|
982
1113
|
advances those fields past their write-time defaults.
|
|
983
1114
|
|
|
984
|
-
## Tool: `export_ledger_provenance`
|
|
985
|
-
|
|
986
|
-
Formats one ledger entry -- requirements checklist, candidates compared,
|
|
987
|
-
verdict, confidence, `snapshot_ref` -- as a single markdown block: a
|
|
988
|
-
stable, portable record of that decision you can paste into a PR
|
|
989
|
-
description or issue by hand. See [Ledger integrity and decision
|
|
990
|
-
provenance](#ledger-integrity-and-decision-provenance) for the full
|
|
991
|
-
design and its deliberate limits.
|
|
992
|
-
|
|
993
|
-
Pure and deterministic: the same entry always produces byte-identical
|
|
994
|
-
markdown, since the function reads nothing but its input (no live system
|
|
995
|
-
time, no disk state). This only formats and returns text -- it does not
|
|
996
|
-
post anything to GitHub or anywhere else; that's a separate action, not
|
|
997
|
-
yet built.
|
|
998
|
-
|
|
999
|
-
### Input
|
|
1000
|
-
|
|
1001
|
-
```json
|
|
1002
|
-
{
|
|
1003
|
-
"project_id": "my-booking-app",
|
|
1004
|
-
"ledger_entry_id": "a1b2c3d4-..."
|
|
1005
|
-
}
|
|
1006
|
-
```
|
|
1007
|
-
|
|
1008
|
-
Both fields are required -- unlike `check_ledger_liveness`, there's no
|
|
1009
|
-
"every entry for this project" mode, since a provenance artifact is
|
|
1010
|
-
inherently about one specific decision.
|
|
1011
|
-
|
|
1012
|
-
### Output
|
|
1013
|
-
|
|
1014
|
-
```json
|
|
1015
|
-
{
|
|
1016
|
-
"ledger_entry_id": "a1b2c3d4-...",
|
|
1017
|
-
"markdown": "## Pattern decision: cancellation policy display with refund tiers by date\n\n- **Verdict:** use_existing (confidence: high)\n- **Reason:** scored\n- **Coverage:** 5/8 (62.5%)\n- **Domain:** Airbnb-style rental marketplace\n- **Framework:** React + Tailwind\n- **Snapshot:** `9f3a1c7e2b0d4f5a6b7c8d9e0f1a2b3c4d5e6f70`\n- **Judged at:** 2026-08-29T19:50:47.073Z\n\n### Requirements checked\n- ...\n\n### Candidates compared\n| Source | Name | Coverage | Chosen |\n| --- | --- | --- | --- |\n| ReUI (reui.io) | Timeline | 62.5 | ✓ |\n\n_Generated by Pattern (`export_ledger_provenance`) from ledger entry `a1b2c3d4-...`._"
|
|
1018
|
-
}
|
|
1019
|
-
```
|
|
1020
|
-
|
|
1021
|
-
Errors (as `isError: true`, not a thrown exception) when `ledger_entry_id`
|
|
1022
|
-
doesn't match any entry for that `project_id` -- including when the id is
|
|
1023
|
-
real but belongs to a different project, since entries are always scoped
|
|
1024
|
-
per `project_id`.
|
|
1025
|
-
|
|
1026
|
-
For a `custom_build` verdict, the candidates section explains that gap in
|
|
1027
|
-
prose instead of an empty table -- Pattern doesn't persist the
|
|
1028
|
-
custom-build reference (Mobbin/Figma) to the ledger (see
|
|
1029
|
-
[`distillCandidate`](#data-minimization)), so it can't reproduce it here.
|
|
1030
|
-
A `null` `snapshot_ref` (project root wasn't a git repository at judgment
|
|
1031
|
-
time) renders as prose too, not the literal word `null`.
|
|
1032
|
-
|
|
1033
|
-
## Tool: `post_ledger_provenance_to_github`
|
|
1034
|
-
|
|
1035
|
-
Posts one ledger entry's provenance artifact (the same content
|
|
1036
|
-
`export_ledger_provenance` produces) as a real comment on a GitHub PR or
|
|
1037
|
-
issue. **This is the one tool in this server with a real, visible side
|
|
1038
|
-
effect on a third-party service** -- every other tool here only ever
|
|
1039
|
-
touches local files. Confirm with the user before calling it, the same
|
|
1040
|
-
way you're expected to confirm before running a suggested
|
|
1041
|
-
`install_command` (see [Installation commands are not
|
|
1042
|
-
trusted](#installation-commands-are-not-trusted) and SECURITY.md).
|
|
1043
|
-
|
|
1044
|
-
GitHub treats a PR and an issue identically for comments (both use the
|
|
1045
|
-
same `/issues/{number}/comments` endpoint), so there's one input shape
|
|
1046
|
-
for both -- no separate "is this a PR" flag.
|
|
1047
|
-
|
|
1048
|
-
### Auth: `GITHUB_TOKEN`, not a GitHub App
|
|
1049
|
-
|
|
1050
|
-
This resolves the open question left in [Ledger integrity and decision
|
|
1051
|
-
provenance](#ledger-integrity-and-decision-provenance)'s earlier writeup
|
|
1052
|
-
in favor of a **personal access token**, read from the `GITHUB_TOKEN`
|
|
1053
|
-
environment variable -- the same convention every GitHub Action and the
|
|
1054
|
-
`gh` CLI itself already use. Needs `repo` scope. A GitHub App was the
|
|
1055
|
-
alternative on the table, but it needs a hosted installation flow and a
|
|
1056
|
-
webhook receiver, which contradicts this project's entire distribution
|
|
1057
|
-
model (a local npm package, no hosted infrastructure -- see [Ledger
|
|
1058
|
-
integrity and decision provenance](#ledger-integrity-and-decision-provenance)
|
|
1059
|
-
and the Pattern Primer's build-order principle). Pattern manages no
|
|
1060
|
-
GitHub credential of its own, the same way it manages no git credential
|
|
1061
|
-
for `snapshot_ref` -- it just reads what's already in your environment.
|
|
1062
|
-
|
|
1063
|
-
### Input
|
|
1064
|
-
|
|
1065
|
-
```json
|
|
1066
|
-
{
|
|
1067
|
-
"project_id": "my-booking-app",
|
|
1068
|
-
"ledger_entry_id": "a1b2c3d4-...",
|
|
1069
|
-
"repo": "my-org/my-booking-app",
|
|
1070
|
-
"issue_number": 42
|
|
1071
|
-
}
|
|
1072
|
-
```
|
|
1073
|
-
|
|
1074
|
-
All four fields are required.
|
|
1075
|
-
|
|
1076
|
-
### Output
|
|
1077
|
-
|
|
1078
|
-
```json
|
|
1079
|
-
{
|
|
1080
|
-
"posted": true,
|
|
1081
|
-
"comment_url": "https://github.com/my-org/my-booking-app/pull/42#issuecomment-...",
|
|
1082
|
-
"comment_id": 123456789
|
|
1083
|
-
}
|
|
1084
|
-
```
|
|
1085
|
-
|
|
1086
|
-
### Idempotent by construction
|
|
1087
|
-
|
|
1088
|
-
Every posted comment is prefixed with a hidden HTML marker keyed to the
|
|
1089
|
-
ledger entry's id (`<!-- pattern-ledger-provenance:<id> -->`). A call
|
|
1090
|
-
first checks the thread's existing comments (most recent 100 -- full
|
|
1091
|
-
pagination isn't handled yet) for that marker; if found, it returns
|
|
1092
|
-
`{ "posted": false, "reason": "already_posted", "comment_url": "..." }`
|
|
1093
|
-
pointing at the existing comment instead of creating a duplicate. A
|
|
1094
|
-
repeat call is always safe to make.
|
|
1095
|
-
|
|
1096
|
-
Errors (`isError: true`) clearly on: no `GITHUB_TOKEN` set, a malformed
|
|
1097
|
-
`repo` (not `owner/repo`), an unknown `ledger_entry_id`, or a GitHub API
|
|
1098
|
-
error (bad credentials, repo/issue not found, rate limit) -- the error
|
|
1099
|
-
message includes the real HTTP status and GitHub's own error text.
|
|
1100
|
-
|
|
1101
1115
|
## Tool: `sweep_ledger_liveness`
|
|
1102
1116
|
|
|
1103
1117
|
Batch version of [`check_ledger_liveness`](#tool-check_ledger_liveness):
|
|
@@ -1164,6 +1178,56 @@ live -- all 12 flag `dangling`, the 13th doesn't. Also tested at 200 and
|
|
|
1164
1178
|
(both complete in well under a second -- this is `fs.existsSync` calls
|
|
1165
1179
|
and in-memory grouping, not API calls).
|
|
1166
1180
|
|
|
1181
|
+
## Tool: `export_ledger_provenance`
|
|
1182
|
+
|
|
1183
|
+
Formats one ledger entry -- requirements checklist, candidates compared,
|
|
1184
|
+
verdict, confidence, `snapshot_ref` -- as a single markdown block: a
|
|
1185
|
+
stable, portable record of that decision you can paste into a PR
|
|
1186
|
+
description or issue by hand. See [Ledger integrity and decision
|
|
1187
|
+
provenance](#ledger-integrity-and-decision-provenance) for the full
|
|
1188
|
+
design and its deliberate limits.
|
|
1189
|
+
|
|
1190
|
+
Pure and deterministic: the same entry always produces byte-identical
|
|
1191
|
+
markdown, since the function reads nothing but its input (no live system
|
|
1192
|
+
time, no disk state). This only formats and returns text -- it does not
|
|
1193
|
+
post anything anywhere; see
|
|
1194
|
+
[`post_ledger_provenance_to_github`](#tool-post_ledger_provenance_to_github)
|
|
1195
|
+
below for that.
|
|
1196
|
+
|
|
1197
|
+
### Input
|
|
1198
|
+
|
|
1199
|
+
```json
|
|
1200
|
+
{
|
|
1201
|
+
"project_id": "my-booking-app",
|
|
1202
|
+
"ledger_entry_id": "a1b2c3d4-..."
|
|
1203
|
+
}
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
Both fields are required -- unlike `check_ledger_liveness`, there's no
|
|
1207
|
+
"every entry for this project" mode, since a provenance artifact is
|
|
1208
|
+
inherently about one specific decision.
|
|
1209
|
+
|
|
1210
|
+
### Output
|
|
1211
|
+
|
|
1212
|
+
```json
|
|
1213
|
+
{
|
|
1214
|
+
"ledger_entry_id": "a1b2c3d4-...",
|
|
1215
|
+
"markdown": "## Pattern decision: cancellation policy display with refund tiers by date\n\n- **Verdict:** use_existing (confidence: high)\n- **Reason:** scored\n- **Coverage:** 5/8 (62.5%)\n- **Domain:** Airbnb-style rental marketplace\n- **Framework:** React + Tailwind\n- **Snapshot:** `9f3a1c7e2b0d4f5a6b7c8d9e0f1a2b3c4d5e6f70`\n- **Judged at:** 2026-08-29T19:50:47.073Z\n\n### Requirements checked\n- ...\n\n### Candidates compared\n| Source | Name | Coverage | Chosen |\n| --- | --- | --- | --- |\n| ReUI (reui.io) | Timeline | 62.5 | ✓ |\n\n_Generated by Pattern (`export_ledger_provenance`) from ledger entry `a1b2c3d4-...`._"
|
|
1216
|
+
}
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
Errors (as `isError: true`, not a thrown exception) when `ledger_entry_id`
|
|
1220
|
+
doesn't match any entry for that `project_id` -- including when the id is
|
|
1221
|
+
real but belongs to a different project, since entries are always scoped
|
|
1222
|
+
per `project_id`.
|
|
1223
|
+
|
|
1224
|
+
For a `custom_build` verdict, the candidates section explains that gap in
|
|
1225
|
+
prose instead of an empty table -- Pattern doesn't persist the
|
|
1226
|
+
custom-build reference (Mobbin/Figma) to the ledger (see
|
|
1227
|
+
[`distillCandidate`](#data-minimization)), so it can't reproduce it here.
|
|
1228
|
+
A `null` `snapshot_ref` (project root wasn't a git repository at judgment
|
|
1229
|
+
time) renders as prose too, not the literal word `null`.
|
|
1230
|
+
|
|
1167
1231
|
## Tool: `backfill_ledger_snapshot_ref`
|
|
1168
1232
|
|
|
1169
1233
|
Best-effort reconstruction of `snapshot_ref` for ledger entries written
|
|
@@ -1224,6 +1288,74 @@ attempt fails fast and reports `null` rather than throwing), and a
|
|
|
1224
1288
|
read-only run against this project's own real `coop-commerce` ledger
|
|
1225
1289
|
entries, per the spec's own test plan.
|
|
1226
1290
|
|
|
1291
|
+
## Tool: `post_ledger_provenance_to_github`
|
|
1292
|
+
|
|
1293
|
+
Posts one ledger entry's provenance artifact (the same content
|
|
1294
|
+
`export_ledger_provenance` produces) as a real comment on a GitHub PR or
|
|
1295
|
+
issue. **This is the one tool in this server with a real, visible side
|
|
1296
|
+
effect on a third-party service** -- every other tool here only ever
|
|
1297
|
+
touches local files. Confirm with the user before calling it, the same
|
|
1298
|
+
way you're expected to confirm before running a suggested
|
|
1299
|
+
`install_command` (see [Installation commands are not
|
|
1300
|
+
trusted](#installation-commands-are-not-trusted) and SECURITY.md).
|
|
1301
|
+
|
|
1302
|
+
GitHub treats a PR and an issue identically for comments (both use the
|
|
1303
|
+
same `/issues/{number}/comments` endpoint), so there's one input shape
|
|
1304
|
+
for both -- no separate "is this a PR" flag.
|
|
1305
|
+
|
|
1306
|
+
### Auth: `GITHUB_TOKEN`, not a GitHub App
|
|
1307
|
+
|
|
1308
|
+
This resolves the open question left in [Ledger integrity and decision
|
|
1309
|
+
provenance](#ledger-integrity-and-decision-provenance)'s earlier writeup
|
|
1310
|
+
in favor of a **personal access token**, read from the `GITHUB_TOKEN`
|
|
1311
|
+
environment variable -- the same convention every GitHub Action and the
|
|
1312
|
+
`gh` CLI itself already use. Needs `repo` scope. A GitHub App was the
|
|
1313
|
+
alternative on the table, but it needs a hosted installation flow and a
|
|
1314
|
+
webhook receiver, which contradicts this project's entire distribution
|
|
1315
|
+
model (a local npm package, no hosted infrastructure -- see [Ledger
|
|
1316
|
+
integrity and decision provenance](#ledger-integrity-and-decision-provenance)
|
|
1317
|
+
and the Pattern Primer's build-order principle). Pattern manages no
|
|
1318
|
+
GitHub credential of its own, the same way it manages no git credential
|
|
1319
|
+
for `snapshot_ref` -- it just reads what's already in your environment.
|
|
1320
|
+
|
|
1321
|
+
### Input
|
|
1322
|
+
|
|
1323
|
+
```json
|
|
1324
|
+
{
|
|
1325
|
+
"project_id": "my-booking-app",
|
|
1326
|
+
"ledger_entry_id": "a1b2c3d4-...",
|
|
1327
|
+
"repo": "my-org/my-booking-app",
|
|
1328
|
+
"issue_number": 42
|
|
1329
|
+
}
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
All four fields are required.
|
|
1333
|
+
|
|
1334
|
+
### Output
|
|
1335
|
+
|
|
1336
|
+
```json
|
|
1337
|
+
{
|
|
1338
|
+
"posted": true,
|
|
1339
|
+
"comment_url": "https://github.com/my-org/my-booking-app/pull/42#issuecomment-...",
|
|
1340
|
+
"comment_id": 123456789
|
|
1341
|
+
}
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
### Idempotent by construction
|
|
1345
|
+
|
|
1346
|
+
Every posted comment is prefixed with a hidden HTML marker keyed to the
|
|
1347
|
+
ledger entry's id (`<!-- pattern-ledger-provenance:<id> -->`). A call
|
|
1348
|
+
first checks the thread's existing comments (most recent 100 -- full
|
|
1349
|
+
pagination isn't handled yet) for that marker; if found, it returns
|
|
1350
|
+
`{ "posted": false, "reason": "already_posted", "comment_url": "..." }`
|
|
1351
|
+
pointing at the existing comment instead of creating a duplicate. A
|
|
1352
|
+
repeat call is always safe to make.
|
|
1353
|
+
|
|
1354
|
+
Errors (`isError: true`) clearly on: no `GITHUB_TOKEN` set, a malformed
|
|
1355
|
+
`repo` (not `owner/repo`), an unknown `ledger_entry_id`, or a GitHub API
|
|
1356
|
+
error (bad credentials, repo/issue not found, rate limit) -- the error
|
|
1357
|
+
message includes the real HTTP status and GitHub's own error text.
|
|
1358
|
+
|
|
1227
1359
|
## Feature cost attribution
|
|
1228
1360
|
|
|
1229
1361
|
Every `recommend_component` call that writes to the ledger -- a fresh
|
|
@@ -1345,25 +1477,33 @@ Two gaps in the ledger, surfaced from user feedback: it tracks that a
|
|
|
1345
1477
|
decision was made, but not whether the thing it decided about is still
|
|
1346
1478
|
live in your codebase, and it stores the checklist/verdict but not a
|
|
1347
1479
|
version pin or an exportable artifact you can attach to a PR or issue.
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
the
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1480
|
+
Both are now fully addressed, across five tools:
|
|
1481
|
+
[`check_ledger_liveness`](#tool-check_ledger_liveness) and
|
|
1482
|
+
[`sweep_ledger_liveness`](#tool-sweep_ledger_liveness) close the first gap;
|
|
1483
|
+
[`export_ledger_provenance`](#tool-export_ledger_provenance),
|
|
1484
|
+
[`backfill_ledger_snapshot_ref`](#tool-backfill_ledger_snapshot_ref), and
|
|
1485
|
+
[`post_ledger_provenance_to_github`](#tool-post_ledger_provenance_to_github)
|
|
1486
|
+
close the second. See `pattern-ledger-integrity-and-provenance-spec.md`
|
|
1487
|
+
for the original phased plan this was built against.
|
|
1488
|
+
|
|
1489
|
+
**This required the one deliberate exception** to Pattern otherwise having
|
|
1490
|
+
[no filesystem/git access to your repo](#per-project-judgment-ledger) at
|
|
1491
|
+
all (the principle `report_build_cost`/`report_outcome_proxy` are built
|
|
1492
|
+
around). Still narrow, still all read-only, and still nothing here ever
|
|
1493
|
+
writes to your repo or runs an arbitrary git/shell command:
|
|
1494
|
+
|
|
1495
|
+
- `git rev-parse HEAD`, on every ledger write, to capture `snapshot_ref`.
|
|
1496
|
+
- `git log --before=<timestamp> -1 --format=%H`, only inside
|
|
1497
|
+
`backfill_ledger_snapshot_ref`, to reconstruct a best-effort
|
|
1498
|
+
`snapshot_ref` for an entry that predates it.
|
|
1361
1499
|
- `fs.existsSync` plus a plain-text read of one file, only for a
|
|
1362
1500
|
`file_path` you explicitly passed to `recommend_component`, only inside
|
|
1363
|
-
`PROJECT_ROOT` (see below)
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1501
|
+
`PROJECT_ROOT` (see below) -- what
|
|
1502
|
+
[`check_ledger_liveness`](#tool-check_ledger_liveness)/[`sweep_ledger_liveness`](#tool-sweep_ledger_liveness)
|
|
1503
|
+
check. `post_ledger_provenance_to_github` additionally makes a real,
|
|
1504
|
+
visible network call to the GitHub API -- see that tool's own docs,
|
|
1505
|
+
it's a materially different kind of exception (a third-party service,
|
|
1506
|
+
not your local machine) from the four above.
|
|
1367
1507
|
|
|
1368
1508
|
### `PROJECT_ROOT`
|
|
1369
1509
|
|
|
@@ -1975,6 +2115,25 @@ search or code problem.
|
|
|
1975
2115
|
|
|
1976
2116
|
The boundary-risk ensemble exists to detect and surface this uncertainty.
|
|
1977
2117
|
|
|
2118
|
+
### A missed match in your own design system isn't automatically re-checked
|
|
2119
|
+
|
|
2120
|
+
In [design-system mode](#tool-register_design_system), the model can say
|
|
2121
|
+
`custom_build`/`no_candidates_found` even when a real, relevant candidate
|
|
2122
|
+
is sitting right in its own prompt -- a reading-comprehension miss over a
|
|
2123
|
+
fully-known candidate list, not a live-search gap. The boundary-risk
|
|
2124
|
+
ensemble above doesn't catch this: it only re-checks a `"scored"` result
|
|
2125
|
+
near the 40%/80% threshold, never a `"no_candidates_found"` verdict.
|
|
2126
|
+
|
|
2127
|
+
The [keyword-overlap safety net](#a-safety-net-for-a-missed-match) flags
|
|
2128
|
+
this risk (`design_system_recall_check` on the response) but does not fix
|
|
2129
|
+
it -- it's a detection layer, not a re-check. The actual fix (re-running
|
|
2130
|
+
the model on a suspicious `no_candidates_found` verdict, the same way a
|
|
2131
|
+
close `"scored"` call already gets re-checked) is scoped but not built.
|
|
2132
|
+
`custom_build` ledger entries in this mode also record zero candidates
|
|
2133
|
+
(`candidates_evaluated: []`), same as the external-library path, so a
|
|
2134
|
+
genuine miss and a correct "nothing here" still look identical in
|
|
2135
|
+
`read_ledger` afterward unless the recall check happened to catch it.
|
|
2136
|
+
|
|
1978
2137
|
### A staged pipeline was evaluated and not adopted
|
|
1979
2138
|
|
|
1980
2139
|
To address the variance above, an alternative architecture was built and
|