omakit 0.1.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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +229 -0
  3. package/bin/omakit +3 -0
  4. package/package.json +40 -0
  5. package/skills/omarchy-plugin-submit/SKILL.md +87 -0
  6. package/skills/omarchy-plugin-validation-watch/SKILL.md +62 -0
  7. package/tests/parity/corpus.mjs +62 -0
  8. package/tests/parity/run.mjs +194 -0
  9. package/tools/marketplace/README.md +64 -0
  10. package/tools/marketplace/agent-control.mjs +72 -0
  11. package/tools/marketplace/banner.mjs +307 -0
  12. package/tools/marketplace/cli.mjs +283 -0
  13. package/tools/marketplace/completion.mjs +263 -0
  14. package/tools/marketplace/doctor.mjs +156 -0
  15. package/tools/marketplace/effect.mjs +115 -0
  16. package/tools/marketplace/form.mjs +209 -0
  17. package/tools/marketplace/github.mjs +206 -0
  18. package/tools/marketplace/issue.mjs +80 -0
  19. package/tools/marketplace/local-transport.mjs +162 -0
  20. package/tools/marketplace/parity-output.mjs +26 -0
  21. package/tools/marketplace/paths.mjs +10 -0
  22. package/tools/marketplace/pin.mjs +203 -0
  23. package/tools/marketplace/plugin.mjs +64 -0
  24. package/tools/marketplace/preflight.mjs +164 -0
  25. package/tools/marketplace/progress.mjs +96 -0
  26. package/tools/marketplace/registry.mjs +216 -0
  27. package/tools/marketplace/report.mjs +192 -0
  28. package/tools/marketplace/run-baseline.mjs +72 -0
  29. package/tools/marketplace/setup.mjs +136 -0
  30. package/tools/marketplace/style.mjs +443 -0
  31. package/tools/marketplace/submit.mjs +341 -0
  32. package/tools/marketplace/tree.mjs +50 -0
  33. package/tools/marketplace/upgrade.mjs +179 -0
  34. package/tools/marketplace/usage.mjs +191 -0
  35. package/tools/marketplace/verify.mjs +70 -0
  36. package/tools/marketplace/watch.mjs +262 -0
  37. package/tools/marketplace/yaml.mjs +164 -0
  38. package/tools/subject/resolve.mjs +124 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maarten Tolhuijs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ <p align="center">
2
+ <img src="docs/media/banner.gif" alt="omakit" width="440">
3
+ </p>
4
+
5
+ **Everything knowable about an Omarchy Quattro plugin submission, checked
6
+ before you post it:** the tree, the manifest, the form, the commit, and the
7
+ marketplace's own security baseline with its outcome reported as it is. A
8
+ submission is judged at one exact commit and drifts from it the moment you
9
+ push; `watch` says when that has happened. All of it runs on your own machine
10
+ and publishes nothing: no issue, no comment, no label, nobody's attention spent
11
+ until you choose to.
12
+
13
+ ![omakit submit refusing a plugin with no license, a README that never says how to uninstall, and a reserved plugin id](docs/media/submit.gif)
14
+
15
+ The plugin above is refused for three things the marketplace itself refuses,
16
+ and warned about a fourth: it ships instruction files an agent will read once
17
+ installed. **103 marketplace issues mention exactly that, and no automated check
18
+ reports it, so today an author finds out from a human review round.** It is a
19
+ warning and not a refusal, because the marketplace does list plugins that ship
20
+ them: 6 of 34 inspected do, at the commit that was listed.
21
+
22
+ ```bash
23
+ omakit submit <plugin-repo> --category Widgets --tags bar,quickshell
24
+ ```
25
+
26
+ Fifteen checks, each naming its source and, when it fails, the measured reason it
27
+ exists. A blocking failure produces no submission body at all, because a refusal
28
+ that still hands you the body is only a suggestion.
29
+
30
+ ## Why it exists
31
+
32
+ Four numbers, all measured on public marketplace data on 2026-09-12. Method,
33
+ limits and the rest of the figures: [docs/MEASUREMENTS.md](docs/MEASUREMENTS.md).
34
+
35
+ | Measured | Consequence |
36
+ | --- | --- |
37
+ | 39 submissions fell out on the title prefix alone, and 11 more are malformed in the body, one by a single word | the format is generated from the pinned form and judged by the marketplace's own parser |
38
+ | 1,215 of the 2,916 listings with a recorded baseline needed a human to look, because of a capability | the official baseline runs locally on the exact commit first, and names the capability |
39
+ | 103 issues mention agent-control files, which no automated check reports | submit names every one with its remedy, before a reviewer has to |
40
+ | 73% of parked submissions have a HEAD the marketplace never saw; 46% of the maintainer's own revalidation requests never produced one | a validation watch that names the one action which re-runs validation |
41
+
42
+ It does not claim to unblock the maintainer. His review writing barely repeats,
43
+ his median time from submission to publication is hours, and the queue waiting on
44
+ him is a median half a day old. The honest size of what this saves him is the
45
+ staleness paragraph he has written by hand on 358 issues, roughly 5.5% of his
46
+ review writing. The rest of the benefit is the submitter's.
47
+ [docs/MARKETPLACE.md](docs/MARKETPLACE.md) states that in full.
48
+
49
+ ## Install
50
+
51
+ On Omarchy, or any Arch:
52
+
53
+ ```bash
54
+ yay -S omakit
55
+ omakit setup
56
+ ```
57
+
58
+ Anywhere with Node 22 or newer:
59
+
60
+ ```bash
61
+ npm install --global omakit
62
+ omakit setup
63
+ ```
64
+
65
+ Or read what you run:
66
+
67
+ ```bash
68
+ git clone --depth 1 https://github.com/mtolhuys/omakit ~/.local/share/omakit
69
+ ln -s ~/.local/share/omakit/bin/omakit ~/.local/bin/omakit
70
+ omakit setup
71
+ ```
72
+
73
+ | Needs | Why |
74
+ | --- | --- |
75
+ | Node 22 or newer | the tool is plain ESM with no dependencies and no build step. A stock Omarchy has it through `mise`, along with `git`, `yay`, `gh` and `ttfx` |
76
+ | `git` | the pin, and reading a subject's tree at an exact commit |
77
+ | network, once | `omakit pin`. After that, `submit` and `verify` on a local repository need none at all |
78
+ | 15 MB on disk | the pinned checkout, in `$XDG_CACHE_HOME/omakit/marketplace`, or `~/.cache/omakit/marketplace` |
79
+
80
+ Updates come from whichever installer you used: `omarchy-update` or `yay -Syu`
81
+ for the package, `npm i -g omakit@latest` for npm, `omakit upgrade` for a
82
+ clone. Nothing in omakit updates itself, and `omarchy-mise-install npm:omakit`
83
+ would, so it is not the way in.
84
+
85
+ ## Watch
86
+
87
+ ![omakit watch reporting that a validated commit has fallen behind](docs/media/watch.gif)
88
+
89
+ ```bash
90
+ omakit watch <submission-issue-url>
91
+ ```
92
+
93
+ That submission passed validation and passed the security baseline with zero
94
+ findings. It is stuck because the marketplace validated one exact commit, and
95
+ the only action that makes it validate a newer one is editing the issue body. Pushing the fix does
96
+ nothing. Commenting "fixed in `abc123`" does nothing. **73% of the 464
97
+ submissions parked in their author's court have a default-branch HEAD the
98
+ marketplace never saw.**
99
+
100
+ ## Commands
101
+
102
+ Agent-first: the expected user is a coding agent submitting a plugin on an
103
+ owner's behalf. Zero dependencies, plain ESM, one entry point, no build step.
104
+
105
+ ```bash
106
+ omakit setup # the environment, the pin, tab completion, and what to try first
107
+ omakit submit <plugin-repo> --category <c> --tags <a,b>
108
+ omakit watch <issue-url> # the commit the marketplace validated, against the plugin's current HEAD
109
+ omakit verify <plugin-repo> # the official security baseline over the local transport, reported verbatim
110
+ omakit parity # the baseline over GitHub versus the local transport, on real listings; writes the evidence
111
+ omakit doctor # what is installed, what is pinned, and what has moved
112
+ omakit pin # what setup does for the pin, on its own
113
+ omakit upgrade # fast-forwards this checkout of omakit itself
114
+ omakit help --agent # the operating instructions, for the agent running this
115
+ ```
116
+
117
+ ![omakit setup checking the environment and fetching the pinned checkout](docs/media/setup.gif)
118
+
119
+ `omakit setup` checks the environment, fetches the marketplace checkout that
120
+ every rule is read from, installs tab completion for the shell you run it from
121
+ (bash, zsh or fish, read from `$SHELL`), and tells you what to try first. It is
122
+ idempotent. The fetch takes about 2 seconds and 15 MB, because it takes only the
123
+ seven files omakit reads out of that repository rather than the 325 MB it is at
124
+ that commit. The completion script knows the subcommands and their flags,
125
+ completes a directory for `<target>`, and offers the categories and tags the
126
+ pin's submission form actually has.
127
+
128
+ There is nothing to authenticate. If you have `gh auth login` done, omakit
129
+ reads that credential for GET requests and stores nothing; a token in
130
+ `GH_TOKEN` or `GITHUB_TOKEN` reaches it the same way, because `gh` honours
131
+ those itself. Without either, `watch` and `parity` share GitHub's
132
+ 60-requests-an-hour unauthenticated allowance, and `submit` and `verify` on a
133
+ local repository do not touch the network at all (a `<url>@<sha>` target is
134
+ fetched once, over git, into the cache). omakit reads no environment variable of its own,
135
+ and `omakit doctor` names the credential source it found, or that it found
136
+ none.
137
+
138
+ Every colour omakit prints is an ANSI palette index, so your Omarchy theme
139
+ decides what it looks like, and nothing is said by colour alone. What the
140
+ terminal shows and why is [docs/TUI.md](docs/TUI.md); which index each role
141
+ gets, measured over all 32 installed themes, is
142
+ [docs/PALETTE.md](docs/PALETTE.md).
143
+
144
+ ## What it is doing
145
+
146
+ Nothing about the submission format is written down in this repository. The
147
+ title prefix, the six form headings in order, the nine categories, the thirteen
148
+ tags and the exact text of the five checklist items are all read from
149
+ `.github/ISSUE_TEMPLATE/submit-plugin.yml` in a marketplace checkout pinned to an
150
+ exact commit. The rendered body is then handed to the marketplace's own
151
+ `parseCurrentSubmission` from that same commit. If it accepts the body here, it
152
+ accepts it there.
153
+
154
+ The security baseline is the marketplace's own code, imported unmodified and run
155
+ over a local snapshot with no network. Omakit adds no rule, renames no outcome,
156
+ and never restates the result as a safety claim: the baseline does no data-flow
157
+ analysis and is not a security review, and the output says so in the
158
+ marketplace's own words.
159
+
160
+ Every check is labelled. `[marketplace-pin]` is the marketplace's rule, read from
161
+ the pin. `[omakit]` is this project's own check, derived from public issue data.
162
+ Those are not marketplace policy and do not claim to be.
163
+
164
+ ## Updating
165
+
166
+ Two different things could mean "upgrade" here, and only one of them may ever
167
+ move on its own. That distinction is now enforced rather than argued.
168
+
169
+ **The tool:**
170
+
171
+ ```bash
172
+ omakit upgrade # fast-forwards this checkout of omakit itself
173
+ omakit upgrade --dry-run
174
+ ```
175
+
176
+ It refuses a dirty tree, a detached HEAD, a remote that is not this repository,
177
+ and anything that is not a fast-forward, and it names what to run yourself in
178
+ each case. It is not a self-updater of the kind this repository warns other
179
+ people about: it fast-forwards a Git checkout you cloned, from the remote you
180
+ cloned it from, and it touches nothing else. On a package install it says so and
181
+ names the installer's own command: `npm i -g omakit@latest`, or
182
+ `sudo pacman -Syu omakit` for the Arch package. `git -C ~/.local/share/omakit pull` still works
183
+ and does the same thing.
184
+
185
+ **The pin** does not move by itself, ever, and `omakit upgrade` does not move it
186
+ either: a test asserts that its source does not so much as mention the pin or
187
+ the cache. Bumping it changes where the submission contract and the baseline
188
+ policy are read from, and the procedure in
189
+ [docs/UPSTREAM_CONTRACT.md](docs/UPSTREAM_CONTRACT.md) ends in re-proving
190
+ transport parity and committing the evidence. `omakit doctor` tells you when the
191
+ pin is behind the marketplace's current branch and then leaves it alone. That the
192
+ pin can go stale unnoticed is the same defect class `omakit watch` reports, so it
193
+ would be poor form to hide it here.
194
+
195
+ ## Evidence, not claims
196
+
197
+ ```bash
198
+ npm test # 147 tests, node --test, no dependencies
199
+ ```
200
+
201
+ | Claim | Proof |
202
+ | --- | --- |
203
+ | The local transport produces the marketplace's own result | 30 of 30 identical, [docs/evidence/parity/](docs/evidence/parity/) |
204
+ | A local run touches no network | run inside `unshare -rn`, [docs/evidence/offline/](docs/evidence/offline/) |
205
+ | The generated body is well formed | the marketplace's own parser, `tests/unit/issue.test.mjs` |
206
+ | Nothing writes to the marketplace | `tests/unit/read-only.test.mjs`, over every source file |
207
+ | No agent-control file can reach a plugin | `tests/unit/self-containment.test.mjs` |
208
+ | The GIFs above are real output | captures and renderer in [docs/media/](docs/media/) |
209
+
210
+ Committed evidence records a digest of each side rather than the results
211
+ themselves: findings about a specific third-party plugin are not this project's
212
+ to publish.
213
+
214
+ ## Documentation
215
+
216
+ | Document | For |
217
+ | --- | --- |
218
+ | [docs/SUBMIT.md](docs/SUBMIT.md) | every check and what it decides |
219
+ | [docs/VALIDATION_WATCH.md](docs/VALIDATION_WATCH.md) | the validation watch: what the marketplace validated, and what moves it |
220
+ | [docs/MEASUREMENTS.md](docs/MEASUREMENTS.md) | every number, its method and its limits |
221
+ | [docs/UPSTREAM_CONTRACT.md](docs/UPSTREAM_CONTRACT.md) | the seam, the pin, the boundaries |
222
+ | [docs/MARKETPLACE.md](docs/MARKETPLACE.md) | who this actually helps |
223
+ | [docs/PALETTE.md](docs/PALETTE.md) | every installed Omarchy theme measured, and which palette index each role gets |
224
+ | [docs/TUI.md](docs/TUI.md) | what the terminal shows, and why it looks that way |
225
+ | [AGENTS.md](AGENTS.md) | changing this repository |
226
+
227
+ MIT. Derived work built on public data from
228
+ `omacom/omarchy-plugin-marketplace`; not affiliated with or endorsed by that
229
+ project.
package/bin/omakit ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ // The single executable entry point. No build step, no dependencies.
3
+ import "../tools/marketplace/cli.mjs"
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "omakit",
3
+ "version": "0.1.0",
4
+ "description": "The safe place to find out: everything knowable about an Omarchy Quattro plugin submission before you post it, on your own machine. Agent-first, read-only, posts nothing, zero dependencies.",
5
+ "license": "MIT",
6
+ "author": "Maarten Tolhuijs",
7
+ "keywords": [
8
+ "omarchy",
9
+ "quattro",
10
+ "plugin",
11
+ "marketplace",
12
+ "preflight",
13
+ "cli",
14
+ "agent"
15
+ ],
16
+ "type": "module",
17
+ "engines": {
18
+ "node": ">=22"
19
+ },
20
+ "bin": {
21
+ "omakit": "bin/omakit"
22
+ },
23
+ "scripts": {
24
+ "pin": "node tools/marketplace/cli.mjs pin",
25
+ "test": "node --test tests/unit/*.test.mjs",
26
+ "parity": "node tools/marketplace/cli.mjs parity --count 30",
27
+ "doctor": "node tools/marketplace/cli.mjs doctor"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/mtolhuys/omakit.git"
32
+ },
33
+ "files": [
34
+ "bin",
35
+ "tools",
36
+ "skills",
37
+ "tests/parity/corpus.mjs",
38
+ "tests/parity/run.mjs"
39
+ ]
40
+ }
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: omarchy-plugin-submit
3
+ description: Submit an Omarchy Quattro plugin to the plugin marketplace on its owner's behalf. Use when asked to submit, list or publish a plugin to the Omarchy marketplace, or to check whether a plugin is ready to submit. Runs every pre-submission check, produces the exact issue title and body, and posts nothing.
4
+ ---
5
+
6
+ # Submitting an Omarchy plugin
7
+
8
+ ## The one thing to get right
9
+
10
+ **You do not create the issue.** This tool produces a title and a body. Creating
11
+ the issue is a separate, explicit act that needs the plugin owner's approval
12
+ first, which is what the marketplace's own agent instructions require. Show the
13
+ owner the title, the body and the verdicts, ask, and only then post it.
14
+
15
+ ## Run it
16
+
17
+ ```bash
18
+ omakit pin # once, and after any pin change: fetches the pinned marketplace checkout
19
+ omakit submit <path-to-the-plugin-repo> --category <category> --tags <a,b>
20
+ ```
21
+
22
+ The plugin's name and id come from the root `manifest.json`. The repository URL
23
+ comes from `origin`. You supply the category and the tags, because nobody else
24
+ can: they are an editorial choice about where the plugin belongs.
25
+
26
+ If you do not know which category and tags are allowed, run the command without
27
+ them. The failing checks print the controlled lists, read from the pinned form.
28
+
29
+ Useful flags: `--notes` for the Maintainer notes field, `--suggest-tag` for the
30
+ optional suggestion, `--name` when the manifest has no name, `--json` for a
31
+ machine-readable result, `--offline` to skip the one check that needs the network.
32
+
33
+ ## Reading the result
34
+
35
+ Exit code 0 means every blocking check passed and the output contains the issue
36
+ title and body. Exit code 1 means it refused, and no body was produced.
37
+
38
+ Each check names a source. `[marketplace-pin]` is the marketplace's own rule, read
39
+ from a pinned checkout. `[omakit]` is this tool's own check, derived from public
40
+ issue data. Do not describe those to the owner as marketplace requirements.
41
+
42
+ A failing check prints the failing paths, a remedy and the measured reason it
43
+ exists. Fix the cause, do not work around the check.
44
+
45
+ ## The two refusals people argue with
46
+
47
+ **`tree.agent-control`.** Agent-control files anywhere in the installable tree
48
+ (`AGENTS.md`, `CLAUDE.md`, `SKILL.md`, `.mcp.json`, anything under `.claude/` or
49
+ `.codex/`, instruction files under `skills/`) are read by whatever agent the user
50
+ runs next, and 103 marketplace issues mention them. This is a warning, not a
51
+ refusal: the marketplace lists plugins that ship them (6 of 34 inspected at
52
+ their listed commit), so the body is still produced. Tell the owner, and offer
53
+ the remedy: move the guidance to a non-agent filename such as `DEVELOPMENT.md`,
54
+ untrack the originals so they leave the installable tree, and keep a recursive
55
+ check in the release process so they cannot return. The owner decides; do not
56
+ strip files from their tree on your own.
57
+
58
+ **`submission.validation-commit`.** The marketplace validates the commit it
59
+ resolves when the issue is opened or edited, which is the pushed default-branch
60
+ HEAD, not whatever is checked out locally. Push first, then submit. 73% of submissions
61
+ parked in their author's court have a HEAD the marketplace never saw.
62
+
63
+ ## What the baseline result means
64
+
65
+ `baseline.preflight` runs the marketplace's own security baseline over a local
66
+ snapshot of the exact commit and reports it verbatim.
67
+
68
+ - `passed`: nothing in the baseline holds the submission back.
69
+ - `review-required`: no findings, but one or more of the seven capabilities is
70
+ present, so a maintainer must look at this exact commit. Not a defect. Worth
71
+ explaining in `--notes` rather than hiding.
72
+ - `needs-fixes`: findings. Only `sudoers-dangerous-passwordless-command` and
73
+ `privileged-process-control-from-shared-temp` block publication under the
74
+ current enforcement mode; the rest a maintainer may accept for that commit.
75
+ Fixing them first avoids a human round either way.
76
+
77
+ Never restate any of this as a safety claim. The baseline performs no data-flow
78
+ analysis and is not a security review; the output says so in the marketplace's own
79
+ words, and so should you.
80
+
81
+ ## After the issue exists
82
+
83
+ Tell the owner the one thing nobody tells them: the marketplace validated one
84
+ exact commit, and **editing the issue body** is the only action that makes it
85
+ validate a newer one. Pushing a fix does nothing. Commenting "fixed in `abc123`" does nothing.
86
+ Then use `omakit watch <issue-url>` to check it later, and see
87
+ `skills/omarchy-plugin-validation-watch/SKILL.md`.
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: omarchy-plugin-validation-watch
3
+ description: Diagnose an Omarchy marketplace plugin submission that has gone quiet or is stuck waiting. Use when a submission issue has had no progress, when a reviewer asked for a fresh validation, or when fixes were pushed but nothing happened. Checks whether the validated commit has fallen behind the repository and names the one action that re-runs validation.
4
+ ---
5
+
6
+ # A submission that has gone quiet
7
+
8
+ ## The mechanism, first
9
+
10
+ The marketplace validates **one exact commit**, and the review that follows is
11
+ of that commit. The only action that makes it validate a newer one is **editing
12
+ the issue body**.
13
+
14
+ - The only workflow with a direct `issues` trigger fires on
15
+ `opened, edited, reopened, labeled, unlabeled`.
16
+ - There is no `issue_comment` trigger anywhere in the marketplace.
17
+ - The daily HEAD comparison covers plugins that are already listed, not open
18
+ submissions.
19
+
20
+ So pushing a fix does nothing, and commenting "fixed in `abc123`" does nothing.
21
+ Both feel like progress. Neither is. This is the single most common reason a
22
+ submission sits still: of the 464 submissions parked in their author's court, 73%
23
+ have a default-branch HEAD the marketplace never saw, and 82% of the authors whose
24
+ push came after a review comment had also commented: engaged, and stuck.
25
+
26
+ ## Check it
27
+
28
+ ```bash
29
+ omakit watch https://github.com/omacom/omarchy-plugin-marketplace/issues/<number>
30
+ ```
31
+
32
+ Read-only. It does not comment, label, or edit anything, and it cannot. It reads
33
+ the default branch through the REST API when a credential is available, taking
34
+ it from the operator's `gh` login (which itself honours `GH_TOKEN` and
35
+ `GITHUB_TOKEN`); without one it falls back to the public commit feed. Never ask the operator for a token: run
36
+ `omakit doctor` and read the `github.auth` line, which names the source.
37
+
38
+ ## Acting on each verdict
39
+
40
+ **`stale`.** The validated commit is behind the repository. Tell the owner plainly: the marketplace has not
41
+ seen the newer commit, and the fix is to **edit the issue body** (any edit
42
+ re-triggers validation and the baseline against the new commit). Do not advise
43
+ them to push again, and do not advise them to comment. If you are asked to do it,
44
+ that is an edit to their issue: get their explicit approval and do it yourself,
45
+ outside this tool.
46
+
47
+ **`current`.** The validated commit is the current HEAD. The submission is genuinely waiting
48
+ on a person, or on a fix the reviewer asked for that has not been made. Read the
49
+ review comments and address the substance. Do not edit the issue to "bump" it: a
50
+ fresh validation clears the reviewer's human decision and costs him a complete
51
+ re-read of the plugin.
52
+
53
+ **`unknown`.** There is no validated commit to compare, the last baseline did not
54
+ complete, or the repository's HEAD could not be read. The output says which. Never
55
+ report this as `current`.
56
+
57
+ ## Before you conclude that the reviewer is behind
58
+
59
+ Check the assumption. The maintainer's own queue is a median half a day old, and
60
+ his median time from submission to publication is hours. If a submission has been
61
+ still for five days, the overwhelmingly likely reason is that it is waiting on its
62
+ author, not on him.
@@ -0,0 +1,62 @@
1
+ // Deterministic parity corpus: listed marketplace repositories at their exact
2
+ // listing-validated commits, read from the pinned marketplace registry so the
3
+ // set is reproducible and never hand-picked for a favourable result.
4
+ //
5
+ // The registry records the outcome the marketplace itself computed for each
6
+ // listing (`automatedSecurityBaseline.outcome`). The corpus is stratified on
7
+ // that outcome so a run always contains repositories whose official result is
8
+ // not `passed`: for a corpus of n, ceil(n/8) needs-fixes, ceil(n/4)
9
+ // review-required, the rest passed, each stratum sampled by a fixed stride
10
+ // over the repositories sorted by URL. `offset` shifts every stratum.
11
+
12
+ import { readFileSync } from "node:fs"
13
+ import { join } from "node:path"
14
+
15
+ const STRATA = ["needs-fixes", "review-required", "passed"]
16
+
17
+ function registrySources(pinDir) {
18
+ const registry = JSON.parse(readFileSync(join(pinDir, "registry.json"), "utf8"))
19
+ const list = Array.isArray(registry.sources) ? registry.sources : Object.values(registry.sources)
20
+ return list
21
+ .filter((source) => typeof source?.repo === "string" && /^[a-f0-9]{40}$/i.test(source?.listingValidatedCommit || ""))
22
+ .map((source) => {
23
+ const recorded = source.automatedSecurityBaseline || null
24
+ return {
25
+ repo: source.repo,
26
+ commit: source.listingValidatedCommit.toLowerCase(),
27
+ type: source.type,
28
+ registryOutcome: recorded?.outcome || null,
29
+ registryOutcomeCommit: recorded?.commit ? String(recorded.commit).toLowerCase() : null,
30
+ registryBaselineVersion: recorded?.version || null,
31
+ }
32
+ })
33
+ .sort((a, b) => a.repo.localeCompare(b.repo))
34
+ }
35
+
36
+ export function strataSizes(count) {
37
+ const needsFixes = Math.ceil(count / 8)
38
+ const reviewRequired = Math.ceil(count / 4)
39
+ return { "needs-fixes": needsFixes, "review-required": reviewRequired, passed: Math.max(0, count - needsFixes - reviewRequired) }
40
+ }
41
+
42
+ function sample(list, count, offset) {
43
+ if (count <= 0 || list.length === 0) return []
44
+ const step = Math.max(1, Math.floor(list.length / count))
45
+ const picked = []
46
+ for (let i = offset % Math.max(1, list.length); picked.length < count && picked.length < list.length; i = (i + step) % list.length) {
47
+ if (picked.includes(list[i])) break
48
+ picked.push(list[i])
49
+ }
50
+ return picked
51
+ }
52
+
53
+ export function parityCorpus(pinDir, count = 30, offset = 0) {
54
+ const sources = registrySources(pinDir)
55
+ const sizes = strataSizes(count)
56
+ const picked = []
57
+ for (const stratum of STRATA) {
58
+ const pool = sources.filter((s) => s.registryOutcome === stratum)
59
+ picked.push(...sample(pool, sizes[stratum], offset))
60
+ }
61
+ return picked.sort((a, b) => a.repo.localeCompare(b.repo))
62
+ }