@bompus/house-rules 0.11.0 → 0.14.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/CODE_OF_CONDUCT.md +83 -0
- package/README.md +102 -63
- package/compose.mjs +8 -2
- package/composition.mjs +11 -3
- package/config-view.mjs +18 -3
- package/config.mjs +74 -13
- package/docs/assets/overview.png +0 -0
- package/docs/configuration.md +85 -9
- package/package.json +4 -2
- package/rules/core.md +118 -209
- package/rules/modifiers/backlog-outline.md +25 -0
- package/rules/modifiers/backlog-visual.md +16 -0
- package/rules/modifiers/coded-offers.md +47 -17
- package/rules/modifiers/cross-session-relay.md +23 -0
- package/rules/modifiers/low-quota-handoff.md +4 -3
- package/rules/modifiers/model-default-questions.md +33 -0
- package/rules/modifiers/multi-agent.md +6 -2
- package/rules/modifiers/no-attribution.md +7 -1
- package/rules/modifiers/periodic-updates.md +25 -0
- package/rules/modifiers/plan-files.md +35 -3
- package/rules/modifiers/release-batching.md +4 -2
- package/rules/modifiers/shared-host-load.md +13 -42
- package/rules/modifiers/squash-landing.md +1 -1
- package/rules/modifiers/upstream-contributions.md +45 -0
- package/rules/modifiers/web-image-formats.md +35 -0
- package/rules/references/backlog-view.md +154 -0
- package/rules/references/landing.md +14 -14
- package/rules/references/multi-agent.md +6 -2
- package/rules/references/release-batching.md +37 -4
- package/rules/references/squash-landing.md +10 -4
- package/rules/references/thread-visuals.md +45 -0
- package/setup.mjs +87 -17
- package/skills/hr-agent-guidance-audit/SKILL.md +10 -5
- package/skills/hr-agent-guidance-audit/references/prune-workflow.md +72 -0
- package/skills/hr-agent-guidance-audit/references/records-and-lessons.md +1 -1
- package/skills/hr-agent-guidance-audit/references/retirement-verdicts.md +49 -0
- package/skills/hr-agent-guidance-refresh/SKILL.md +25 -57
- package/skills/hr-agent-guidance-refresh/references/verification.md +1 -1
- package/skills/hr-audit-choices/SKILL.md +1 -1
- package/skills/hr-benchmarking/SKILL.md +122 -159
- package/skills/hr-benchmarking/references/javascript.md +113 -0
- package/skills/hr-benchmarking/references/tools.md +62 -57
- package/skills/hr-better-accessibility/SKILL.md +1 -1
- package/skills/hr-better-accessibility/screen-readers.md +1 -0
- package/skills/hr-code-review/SKILL.md +19 -3
- package/skills/hr-code-review/agents/openai.yaml +1 -1
- package/skills/hr-code-review/references/acp-client.md +43 -0
- package/skills/{hr-change-impact/SKILL.md → hr-code-review/references/impact.md} +2 -12
- package/skills/hr-code-review/references/panel-adjudication.md +58 -0
- package/skills/hr-code-review/references/panel-plan.md +61 -0
- package/skills/hr-code-review/references/panel-recommendations.json +105 -0
- package/skills/hr-code-review/references/panel-recommendations.md +46 -0
- package/skills/hr-code-review/references/panel-setup.md +54 -0
- package/skills/hr-code-review/references/reviewer-role.md +31 -0
- package/skills/hr-code-review/scripts/acp-panel-client.mjs +299 -0
- package/skills/hr-code-review/scripts/check-receipts.mjs +263 -0
- package/skills/hr-code-review/scripts/panel-plan.mjs +69 -0
- package/skills/hr-code-review/scripts/panel-preferences.mjs +220 -0
- package/skills/hr-design-exploration/SKILL.md +24 -1
- package/skills/hr-design-exploration/agents/openai.yaml +2 -2
- package/skills/hr-diagnosing-bugs/SKILL.md +2 -2
- package/skills/hr-diagnosing-bugs/references/reporting.md +29 -4
- package/skills/hr-diagnosing-bugs/scripts/check-pr-offer.mjs +47 -0
- package/skills/hr-explain-code/SKILL.md +1 -1
- package/skills/hr-extract-shared-steps/SKILL.md +1 -1
- package/skills/hr-forgotten-work-audit/SKILL.md +130 -0
- package/skills/hr-forgotten-work-audit/references/brief-template.md +56 -0
- package/skills/hr-forgotten-work-audit/references/result-format.md +46 -0
- package/skills/hr-handoff/LICENSE +21 -0
- package/skills/hr-handoff/NOTICE.md +29 -0
- package/skills/hr-handoff/SKILL.md +24 -1
- package/skills/hr-handoff/references/reconciliation.md +25 -6
- package/skills/hr-handoff/references/successor-thread.md +76 -0
- package/skills/hr-handoff/scripts/check-reconciliation.mjs +48 -6
- package/skills/hr-house-rules-setup/SKILL.md +22 -8
- package/skills/hr-lean-plan/SKILL.md +4 -0
- package/skills/hr-maintainability-review/SKILL.md +14 -4
- package/skills/hr-maintainability-review/agents/openai.yaml +1 -1
- package/skills/hr-manual-qa/SKILL.md +1 -1
- package/skills/hr-ordering-tests/SKILL.md +1 -1
- package/skills/hr-plain-prose/SKILL.md +7 -4
- package/skills/hr-pr-followup/SKILL.md +6 -3
- package/skills/hr-pr-followup/references/review-helpers.md +40 -7
- package/skills/hr-pr-followup/scripts/check-landing.py +204 -0
- package/skills/hr-pr-followup/scripts/check-pr-review.ts +19 -8
- package/skills/hr-pr-followup/scripts/merge-pr.sh +100 -0
- package/skills/hr-pr-followup/scripts/verify-landed.sh +184 -0
- package/skills/hr-read-reddit/SKILL.md +1 -1
- package/skills/hr-research-shared-links/SKILL.md +103 -0
- package/skills/hr-sqlite/SKILL.md +174 -0
- package/skills/hr-sqlite/agents/openai.yaml +2 -0
- package/skills/hr-sqlite/references/evidence.md +94 -0
- package/skills/hr-sqlite/references/tool-decisions.md +39 -0
- package/skills/hr-sqlite/scripts/inspect-sqlite.py +245 -0
- package/skills/hr-stock-ui-audit/SKILL.md +1 -1
- package/skills/hr-test-audit/LICENSE +21 -0
- package/skills/hr-test-audit/NOTICE.md +67 -0
- package/skills/hr-usage-report/SKILL.md +90 -0
- package/skills/hr-usage-report/agents/openai.yaml +2 -0
- package/skills/hr-usage-report/assets/example-report.json +125 -0
- package/skills/hr-usage-report/assets/report-template.html +377 -0
- package/skills/hr-usage-report/scripts/render-report.ts +86 -0
- package/skills/hr-usage-report/scripts/usage-by-model.ts +499 -0
- package/skills/hr-what-next/SKILL.md +100 -10
- package/skills/hr-what-next/references/reporting.md +13 -22
- package/skills/hr-what-next/scripts/check-open-work.mjs +711 -0
- package/skills/hr-writing-pr/SKILL.md +29 -14
- package/skills/hr-writing-pr/scripts/benefit-chart.py +200 -0
- package/docs/npm-release.md +0 -45
- package/rules/modifiers/swarmail.md +0 -12
- package/skills/hr-benchmarking/references/priority-findings-20261006.md +0 -115
- package/skills/hr-change-impact/agents/openai.yaml +0 -5
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Read-only inspection of a SQLite database file and, optionally, the source
|
|
3
|
+
that opens it.
|
|
4
|
+
|
|
5
|
+
inspect-sqlite.py DATABASE [--source PATH ...] [--quick-check] [--json]
|
|
6
|
+
|
|
7
|
+
Reports what the file itself records (journal mode, page and freelist counts,
|
|
8
|
+
planner statistics, FTS tables, WAL size) plus the filesystem it sits on. It
|
|
9
|
+
never writes to the database. SQLite may create an empty `-shm` file beside a
|
|
10
|
+
WAL database that no process has open.
|
|
11
|
+
|
|
12
|
+
Connection settings such as busy_timeout, synchronous, foreign_keys and
|
|
13
|
+
cache_size are not stored in the file, so no database inspection can show what
|
|
14
|
+
an application sets. `--source` is a text scan: for each mention of
|
|
15
|
+
journal_mode it reports whether a busy_timeout mention comes earlier in the
|
|
16
|
+
same file. It does not establish run order or which connection each belongs
|
|
17
|
+
to. Treat those lines as places to read, not verdicts. A source path that cannot
|
|
18
|
+
be read is listed as not scanned and the exit status is 2.
|
|
19
|
+
"""
|
|
20
|
+
import argparse
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import pathlib
|
|
24
|
+
import re
|
|
25
|
+
import sqlite3
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
NETWORK_FILESYSTEMS = {"9p", "drvfs", "cifs", "smb3", "smbfs", "nfs", "nfs4", "fuse.sshfs", "virtiofs"}
|
|
29
|
+
SHADOW_SUFFIXES = ("_data", "_idx", "_content", "_docsize", "_config", "_segments", "_segdir", "_stat", "_node", "_parent", "_rowid")
|
|
30
|
+
SOURCE_SUFFIXES = {".py", ".js", ".mjs", ".cjs", ".ts", ".tsx", ".rs", ".go", ".rb", ".java", ".kt", ".cs", ".c", ".cc", ".cpp", ".swift", ".php", ".sql"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def mount_for(path):
|
|
34
|
+
"""Return (mount point, filesystem type, options) for the longest mount prefix."""
|
|
35
|
+
try:
|
|
36
|
+
with open("/proc/self/mountinfo", encoding="utf-8", errors="replace") as handle:
|
|
37
|
+
lines = handle.read().splitlines()
|
|
38
|
+
except OSError:
|
|
39
|
+
return None
|
|
40
|
+
real = os.path.realpath(path)
|
|
41
|
+
best = None
|
|
42
|
+
for line in lines:
|
|
43
|
+
left, _, right = line.partition(" - ")
|
|
44
|
+
fields = left.split()
|
|
45
|
+
if len(fields) < 6 or len(right.split()) < 3:
|
|
46
|
+
continue
|
|
47
|
+
point = fields[4].replace("\\040", " ")
|
|
48
|
+
if real == point or real.startswith(point.rstrip("/") + "/") or point == "/":
|
|
49
|
+
if best is None or len(point) > len(best[0]):
|
|
50
|
+
kind, _, super_options = right.split()[:3]
|
|
51
|
+
best = (point, kind, ",".join([fields[5], super_options]))
|
|
52
|
+
return best
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def scan_source(paths):
|
|
56
|
+
"""For each file that mentions journal_mode, note where busy_timeout appears.
|
|
57
|
+
|
|
58
|
+
Returns (findings, errors). A path that cannot be read is an error, so a
|
|
59
|
+
failed scan never looks like a scan that found nothing.
|
|
60
|
+
"""
|
|
61
|
+
findings = []
|
|
62
|
+
errors = []
|
|
63
|
+
files = []
|
|
64
|
+
|
|
65
|
+
def walk_error(error):
|
|
66
|
+
errors.append({"file": str(error.filename), "error": error.strerror or str(error)})
|
|
67
|
+
|
|
68
|
+
for entry in paths:
|
|
69
|
+
if os.path.isdir(entry):
|
|
70
|
+
for root, dirs, names in os.walk(entry, onerror=walk_error):
|
|
71
|
+
dirs[:] = [d for d in dirs if d not in {".git", "node_modules", ".venv", "target", "dist"}]
|
|
72
|
+
files.extend(
|
|
73
|
+
os.path.join(root, n) for n in names if os.path.splitext(n)[1] in SOURCE_SUFFIXES
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
files.append(entry)
|
|
77
|
+
for file in sorted(files):
|
|
78
|
+
try:
|
|
79
|
+
with open(file, encoding="utf-8", errors="replace") as handle:
|
|
80
|
+
lines = handle.read().splitlines()
|
|
81
|
+
except OSError as error:
|
|
82
|
+
errors.append({"file": file, "error": error.strerror or str(error)})
|
|
83
|
+
continue
|
|
84
|
+
journals = [i + 1 for i, l in enumerate(lines) if re.search(r"journal_mode", l, re.I)]
|
|
85
|
+
timeouts = [i + 1 for i, l in enumerate(lines) if re.search(r"busy_timeout|busyTimeout|set_busy_timeout", l, re.I)]
|
|
86
|
+
for journal in journals:
|
|
87
|
+
earlier = [t for t in timeouts if t < journal]
|
|
88
|
+
if earlier:
|
|
89
|
+
status = "busy_timeout at line %d comes before journal_mode at line %d" % (earlier[-1], journal)
|
|
90
|
+
elif timeouts:
|
|
91
|
+
status = "journal_mode at line %d comes before busy_timeout at line %d" % (journal, timeouts[0])
|
|
92
|
+
else:
|
|
93
|
+
status = "journal_mode at line %d; no busy_timeout in this file" % journal
|
|
94
|
+
findings.append({"file": file, "line": journal, "status": status, "ok": bool(earlier)})
|
|
95
|
+
return findings, errors
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def inspect(path, quick_check=False):
|
|
99
|
+
result = {"path": os.path.abspath(path)}
|
|
100
|
+
size = os.path.getsize(path)
|
|
101
|
+
result["fileBytes"] = size
|
|
102
|
+
uri = pathlib.Path(result["path"]).as_uri() + "?mode=ro"
|
|
103
|
+
connection = sqlite3.connect(uri, uri=True, timeout=2)
|
|
104
|
+
try:
|
|
105
|
+
connection.execute("PRAGMA busy_timeout = 2000")
|
|
106
|
+
|
|
107
|
+
def pragma(name):
|
|
108
|
+
row = connection.execute("PRAGMA " + name).fetchone()
|
|
109
|
+
return row[0] if row else None
|
|
110
|
+
|
|
111
|
+
result["library"] = {"version": sqlite3.sqlite_version}
|
|
112
|
+
options = [r[0] for r in connection.execute("PRAGMA compile_options")]
|
|
113
|
+
result["library"]["memstatusDisabled"] = "DEFAULT_MEMSTATUS=0" in options
|
|
114
|
+
result["library"]["threadsafe"] = next((o for o in options if o.startswith("THREADSAFE")), None)
|
|
115
|
+
result["journalMode"] = pragma("journal_mode")
|
|
116
|
+
result["pageSize"] = pragma("page_size")
|
|
117
|
+
result["pageCount"] = pragma("page_count")
|
|
118
|
+
result["freelistCount"] = pragma("freelist_count")
|
|
119
|
+
result["autoVacuum"] = pragma("auto_vacuum")
|
|
120
|
+
result["userVersion"] = pragma("user_version")
|
|
121
|
+
result["encoding"] = pragma("encoding")
|
|
122
|
+
names = connection.execute(
|
|
123
|
+
"SELECT name, sql FROM sqlite_master WHERE type = 'table' ORDER BY name"
|
|
124
|
+
).fetchall()
|
|
125
|
+
tables = [n for n, _ in names if not n.startswith("sqlite_")]
|
|
126
|
+
virtual = {n: s for n, s in names if s and s.upper().startswith("CREATE VIRTUAL TABLE")}
|
|
127
|
+
shadow = {n for table in virtual for n in tables if n in {table + s for s in SHADOW_SUFFIXES}}
|
|
128
|
+
plain = [n for n in tables if n not in virtual and n not in shadow]
|
|
129
|
+
result["tables"] = len(plain)
|
|
130
|
+
result["indexes"] = connection.execute(
|
|
131
|
+
"SELECT count(*) FROM sqlite_master WHERE type = 'index' AND name NOT LIKE 'sqlite_%'"
|
|
132
|
+
).fetchone()[0]
|
|
133
|
+
result["virtualTables"] = {
|
|
134
|
+
n: (re.search(r"USING\s+(\w+)", s, re.I) or [None, "unknown"])[1].lower()
|
|
135
|
+
for n, s in virtual.items()
|
|
136
|
+
}
|
|
137
|
+
has_stat = any(n == "sqlite_stat1" for n, _ in names)
|
|
138
|
+
analyzed = set()
|
|
139
|
+
if has_stat:
|
|
140
|
+
analyzed = {r[0] for r in connection.execute("SELECT DISTINCT tbl FROM sqlite_stat1")}
|
|
141
|
+
result["statistics"] = {
|
|
142
|
+
"sqliteStat1": has_stat,
|
|
143
|
+
"rows": connection.execute("SELECT count(*) FROM sqlite_stat1").fetchone()[0] if has_stat else 0,
|
|
144
|
+
"tablesWithoutStatistics": [n for n in plain if n not in analyzed],
|
|
145
|
+
}
|
|
146
|
+
if quick_check:
|
|
147
|
+
result["quickCheck"] = [r[0] for r in connection.execute("PRAGMA quick_check(10)")]
|
|
148
|
+
finally:
|
|
149
|
+
connection.close()
|
|
150
|
+
for suffix, key in (("-wal", "walBytes"), ("-shm", "shmBytes")):
|
|
151
|
+
side = result["path"] + suffix
|
|
152
|
+
result[key] = os.path.getsize(side) if os.path.exists(side) else None
|
|
153
|
+
mount = mount_for(result["path"])
|
|
154
|
+
if mount:
|
|
155
|
+
point, kind, options = mount
|
|
156
|
+
result["filesystem"] = {"mount": point, "type": kind, "options": options}
|
|
157
|
+
else:
|
|
158
|
+
result["filesystem"] = None
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def notes(result):
|
|
163
|
+
out = []
|
|
164
|
+
if result["journalMode"] != "wal":
|
|
165
|
+
out.append("journal_mode is %s, not wal; in a rollback journal a writer's commit waits for readers and blocks new ones while it commits" % result["journalMode"])
|
|
166
|
+
stats = result["statistics"]
|
|
167
|
+
if not stats["sqliteStat1"]:
|
|
168
|
+
out.append("no sqlite_stat1: the planner has no statistics; run PRAGMA optimize at a time you choose (the first run on a large file can take over a minute and hold the write lock)")
|
|
169
|
+
elif stats["tablesWithoutStatistics"]:
|
|
170
|
+
out.append("tables with no statistics rows: " + ", ".join(stats["tablesWithoutStatistics"][:8]))
|
|
171
|
+
pages = result["pageCount"] or 0
|
|
172
|
+
if pages and result["freelistCount"] and result["freelistCount"] / pages > 0.2:
|
|
173
|
+
out.append("more than 20%% of pages are free (%d of %d); the file is larger than its content" % (result["freelistCount"], pages))
|
|
174
|
+
if result["walBytes"] and result["walBytes"] > result["fileBytes"]:
|
|
175
|
+
out.append("the WAL file is larger than the database; look for a long-running reader or a blocked checkpoint")
|
|
176
|
+
fs = result["filesystem"]
|
|
177
|
+
if fs:
|
|
178
|
+
if fs["type"] in NETWORK_FILESYSTEMS:
|
|
179
|
+
out.append("filesystem %s: WAL needs shared memory on one host and does not work reliably here" % fs["type"])
|
|
180
|
+
if re.search(r"(^|,)discard(,|$)", fs["options"]):
|
|
181
|
+
out.append("mounted with online discard; this stalled fsync on one host (see the evidence), so check fsync latency")
|
|
182
|
+
if "quickCheck" in result and result["quickCheck"] != ["ok"]:
|
|
183
|
+
out.append("quick_check reported: " + "; ".join(result["quickCheck"][:3]))
|
|
184
|
+
return out
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def render(result, source, source_errors=()):
|
|
188
|
+
lines = ["database: %s" % result["path"]]
|
|
189
|
+
lines.append("size: %d bytes; %s pages of %s bytes; %s free" % (
|
|
190
|
+
result["fileBytes"], result["pageCount"], result["pageSize"], result["freelistCount"]))
|
|
191
|
+
lines.append("journal_mode: %s; auto_vacuum: %s; user_version: %s; encoding: %s" % (
|
|
192
|
+
result["journalMode"], result["autoVacuum"], result["userVersion"], result["encoding"]))
|
|
193
|
+
lines.append("tables: %d; indexes: %d; virtual tables: %s" % (
|
|
194
|
+
result["tables"], result["indexes"],
|
|
195
|
+
", ".join("%s (%s)" % kv for kv in result["virtualTables"].items()) or "none"))
|
|
196
|
+
stats = result["statistics"]
|
|
197
|
+
lines.append("statistics: sqlite_stat1 %s, %d rows" % ("present" if stats["sqliteStat1"] else "absent", stats["rows"]))
|
|
198
|
+
lines.append("wal file: %s; shm file: %s" % (result["walBytes"], result["shmBytes"]))
|
|
199
|
+
fs = result["filesystem"]
|
|
200
|
+
lines.append("filesystem: %s" % ("%s at %s (%s)" % (fs["type"], fs["mount"], fs["options"]) if fs else "not available on this platform"))
|
|
201
|
+
lines.append("inspector library: SQLite %s, %s" % (result["library"]["version"], result["library"]["threadsafe"]))
|
|
202
|
+
lines.append("not in the file: busy_timeout, synchronous, foreign_keys, cache_size, mmap_size, temp_store; read the code that opens the connection")
|
|
203
|
+
for note in notes(result):
|
|
204
|
+
lines.append("note: " + note)
|
|
205
|
+
if source is not None:
|
|
206
|
+
lines.append("source scan (journal_mode mentions): %d" % len(source))
|
|
207
|
+
for item in source:
|
|
208
|
+
lines.append(" %s: %s%s" % (item["file"], item["status"], "" if item["ok"] else " <- read this"))
|
|
209
|
+
for item in source_errors:
|
|
210
|
+
lines.append(" not scanned: %s (%s)" % (item["file"], item["error"]))
|
|
211
|
+
return "\n".join(lines)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def main(argv=None):
|
|
215
|
+
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
|
|
216
|
+
parser.add_argument("database")
|
|
217
|
+
parser.add_argument("--source", nargs="+", metavar="PATH", help="files or directories to scan for busy_timeout order")
|
|
218
|
+
parser.add_argument("--quick-check", action="store_true", help="also run PRAGMA quick_check (reads the whole file)")
|
|
219
|
+
parser.add_argument("--json", action="store_true")
|
|
220
|
+
args = parser.parse_args(argv)
|
|
221
|
+
if not os.path.isfile(args.database):
|
|
222
|
+
print("error: not a file: %s" % args.database, file=sys.stderr)
|
|
223
|
+
return 2
|
|
224
|
+
try:
|
|
225
|
+
result = inspect(args.database, args.quick_check)
|
|
226
|
+
except (sqlite3.Error, OSError) as error:
|
|
227
|
+
print("error: %s" % error, file=sys.stderr)
|
|
228
|
+
return 2
|
|
229
|
+
source, source_errors = scan_source(args.source) if args.source else (None, [])
|
|
230
|
+
if args.json:
|
|
231
|
+
payload = dict(result)
|
|
232
|
+
payload["notes"] = notes(result)
|
|
233
|
+
if source is not None:
|
|
234
|
+
payload["source"] = source
|
|
235
|
+
payload["sourceErrors"] = source_errors
|
|
236
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
237
|
+
else:
|
|
238
|
+
print(render(result, source, source_errors))
|
|
239
|
+
for item in source_errors:
|
|
240
|
+
print("error: could not scan %s: %s" % (item["file"], item["error"]), file=sys.stderr)
|
|
241
|
+
return 2 if source_errors else 0
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if __name__ == "__main__":
|
|
245
|
+
sys.exit(main())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hr-stock-ui-audit
|
|
3
|
-
description: Find template-default styling
|
|
3
|
+
description: Find template-default styling nobody chose for this product (stock violet accents, purple-to-blue gradients, one stock font, emoji icons, glow and blur effects), triage each hit in context, and report or fix the real ones. Use when asked to audit or remove generic or AI-looking styling in existing UI. It does not set a visual direction.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Stock UI audit
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aaron Queen
|
|
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.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Source notices
|
|
2
|
+
|
|
3
|
+
This notice accompanies the hr-test-audit bundle when copied on its own.
|
|
4
|
+
Each source below is published under the MIT License. The licence text at the
|
|
5
|
+
end applies to each of them, with the copyright line shown for that source.
|
|
6
|
+
|
|
7
|
+
## obra/superpowers
|
|
8
|
+
|
|
9
|
+
Source: https://github.com/obra/superpowers (compared at revision `8ca22db`)
|
|
10
|
+
|
|
11
|
+
- `SKILL.md`: cites `writing-good-tests.md`.
|
|
12
|
+
|
|
13
|
+
Copyright (c) 2025 Jesse Vincent
|
|
14
|
+
|
|
15
|
+
## addyosmani/agent-skills
|
|
16
|
+
|
|
17
|
+
Source: https://github.com/addyosmani/agent-skills (compared at revision `9d0c60d`)
|
|
18
|
+
|
|
19
|
+
- `scripts/floor-guard.ts`: adapted from constraint-driven-development.
|
|
20
|
+
|
|
21
|
+
Copyright (c) 2025 Addy Osmani
|
|
22
|
+
|
|
23
|
+
## openclaw/openclaw
|
|
24
|
+
|
|
25
|
+
Source: https://github.com/openclaw/openclaw (linked at revision `65f1e4d`, compared at `1fda703`)
|
|
26
|
+
|
|
27
|
+
- `SKILL.md`: the authoring gate, junk patterns, retention bar and evidence
|
|
28
|
+
fields are adapted from `.agents/skills/test-audit/`.
|
|
29
|
+
|
|
30
|
+
Copyright (c) 2026 OpenClaw Foundation
|
|
31
|
+
|
|
32
|
+
## no-human-ai/no_human
|
|
33
|
+
|
|
34
|
+
Source: https://github.com/no-human-ai/no_human (compared at revision `9b50bf6`)
|
|
35
|
+
|
|
36
|
+
- `SKILL.md`: the rule that weakening a test counts as removing it is adapted
|
|
37
|
+
from its tamper guard.
|
|
38
|
+
|
|
39
|
+
Copyright (c) 2026 Eyal Golan
|
|
40
|
+
|
|
41
|
+
## omkamal/pypict-claude-skill
|
|
42
|
+
|
|
43
|
+
Source: https://github.com/omkamal/pypict-claude-skill (compared at revision `fbda212`)
|
|
44
|
+
|
|
45
|
+
- `SKILL.md`: the pairwise-coverage guidance is adapted from this skill.
|
|
46
|
+
|
|
47
|
+
Copyright (c) 2025 pypict-claude-skill contributors
|
|
48
|
+
|
|
49
|
+
## MIT License text
|
|
50
|
+
|
|
51
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
52
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
53
|
+
in the Software without restriction, including without limitation the rights
|
|
54
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
55
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
56
|
+
furnished to do so, subject to the following conditions:
|
|
57
|
+
|
|
58
|
+
The above copyright notice and this permission notice shall be included in all
|
|
59
|
+
copies or substantial portions of the Software.
|
|
60
|
+
|
|
61
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
62
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
63
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
64
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
65
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
66
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
67
|
+
SOFTWARE.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hr-usage-report
|
|
3
|
+
description: Measure where Claude Code, Codex and OpenCode usage went by provider, model and role, and chart model comparisons as HTML. Use for quota or cost questions and to show a model or role recommendation visually.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Usage report
|
|
7
|
+
|
|
8
|
+
Answer quota and model-role questions from measured usage and published
|
|
9
|
+
evidence, then show the result as charts and tables. Run the scripts from this
|
|
10
|
+
skill's directory with Bun.
|
|
11
|
+
|
|
12
|
+
## 1. Measure where usage went
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bun scripts/usage-by-model.ts --days 7 [--providers claude,codex,opencode] [--claude-projects <dir,dir>] [--prices <api.json>] [--json]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
It reads Claude Code transcripts (`projects` under `~/.claude` and under every
|
|
19
|
+
other `~/.claude_*` or `~/.claude-*` configuration directory, so sessions run
|
|
20
|
+
with a separate `CLAUDE_CONFIG_DIR` count; `--claude-projects` replaces that
|
|
21
|
+
list), Codex rollouts
|
|
22
|
+
(`~/.codex/sessions`) and the OpenCode database, all read-only and local. It
|
|
23
|
+
prices each request at [models.dev](https://models.dev) list rates, including
|
|
24
|
+
long-context tiers, and splits usage by provider, model and role: main session,
|
|
25
|
+
subagent, advisor. Without `--prices` it downloads `https://models.dev/api.json`;
|
|
26
|
+
that request carries no local data. Use `--since <ISO>` for a fixed start.
|
|
27
|
+
|
|
28
|
+
Read the output beside the provider's live quota readings:
|
|
29
|
+
|
|
30
|
+
- **Shares, not dollars.** List price stands in for subscription quota; no
|
|
31
|
+
provider publishes the conversion. Say so in the report.
|
|
32
|
+
- **Cache reads.** When cache reads dominate a row's cost, context length, not
|
|
33
|
+
the model's output, drives usage. Compare the main-request context median
|
|
34
|
+
with the session-start median: the gap is conversation growth; the start is
|
|
35
|
+
fixed overhead (system prompt, rules, tool schemas).
|
|
36
|
+
- **Advisor rows.** Each advisor call re-reads the whole transcript uncached at
|
|
37
|
+
the advisor's rates; a few calls can outweigh many main turns.
|
|
38
|
+
- **Cross-provider work.** The same table shows what the author's sessions and
|
|
39
|
+
another vendor's reviewer each cost.
|
|
40
|
+
- **Unpriced models.** The summary lists models models.dev has no price for;
|
|
41
|
+
report them rather than dropping them.
|
|
42
|
+
|
|
43
|
+
## 2. Pick evidence by question
|
|
44
|
+
|
|
45
|
+
| Question | Evidence | Read |
|
|
46
|
+
| ------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
47
|
+
| Quality per dollar for implementation | Agentic coding boards that publish effort, harness and cost per task | Score against cost at every effort; cost per solved task |
|
|
48
|
+
| Review and bug finding | Bug-finding boards with run counts per row | The effort curve; treat single runs as noisy |
|
|
49
|
+
| Raw speed | Output tokens per second | Generation speed only |
|
|
50
|
+
| Task speed | Boards that time whole tasks | Time to finish; a model that generates faster can still take longer |
|
|
51
|
+
| Fit for your own work | A known-answer trial on your own tasks (`hr-benchmarking`) | Correctness and model time per task shape |
|
|
52
|
+
|
|
53
|
+
Compare models at the same effort and harness, or say they differ. A vendor
|
|
54
|
+
table that sets one model's top effort against another's lower effort is not a
|
|
55
|
+
matched comparison. Keep token speed and task time in separate charts; never
|
|
56
|
+
infer one from the other.
|
|
57
|
+
|
|
58
|
+
## 3. Render the report
|
|
59
|
+
|
|
60
|
+
Write a data file, then:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
bun scripts/render-report.ts <data.json> <out.html>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The page is self-contained, follows the host's theme variables when it is
|
|
67
|
+
embedded, and falls back to a dark surface. `assets/example-report.json` uses
|
|
68
|
+
every section type.
|
|
69
|
+
|
|
70
|
+
The data file declares `series` once, so each model keeps one color across
|
|
71
|
+
every chart in the report: up to three colored series with slots 0 to 2, and at
|
|
72
|
+
most one `"reference": true` series drawn dashed and neutral. The renderer
|
|
73
|
+
rejects a fourth colored series because no fourth hue passes the color-vision
|
|
74
|
+
check on the dark surface against the other three. Compare more models in a
|
|
75
|
+
second report file.
|
|
76
|
+
|
|
77
|
+
Section types:
|
|
78
|
+
|
|
79
|
+
- `curve`: points with `x`, `y`, `label`, `tip` per series; `xLog`, domains,
|
|
80
|
+
ticks and axis affixes. Use for score against cost across efforts.
|
|
81
|
+
- `ladder`: `categories` (usually efforts) and `values` per series, `null` for a
|
|
82
|
+
missing row, optional `notes` such as run counts.
|
|
83
|
+
- `bars`: `rows` of `label`, `key`, `value`, optional `display` and `tip`.
|
|
84
|
+
- `pair`: two sections side by side, such as token speed beside task time.
|
|
85
|
+
- `table`: `columns` and `rows`; a cell may be `{ "text", "verdict": "yes" | "maybe" | "no" }`;
|
|
86
|
+
`numeric` lists right-aligned column indexes.
|
|
87
|
+
|
|
88
|
+
End with one verdict table per model or role: role, key evidence with numbers,
|
|
89
|
+
verdict. Name the evidence date in the title, and apply any model or effort
|
|
90
|
+
limits the user has set before recommending a configuration.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Example: three models across effort levels (illustrative data)",
|
|
3
|
+
"series": {
|
|
4
|
+
"small": { "name": "Model S", "slot": 0 },
|
|
5
|
+
"mid": { "name": "Model M", "slot": 1 },
|
|
6
|
+
"large": { "name": "Model L", "slot": 2 },
|
|
7
|
+
"previous": { "name": "Model L (previous)", "reference": true }
|
|
8
|
+
},
|
|
9
|
+
"sections": [
|
|
10
|
+
{
|
|
11
|
+
"type": "curve",
|
|
12
|
+
"title": "Score against cost per task, every effort level",
|
|
13
|
+
"sub": "Illustrative values. Each line runs low to max on a log cost axis.",
|
|
14
|
+
"xLog": true,
|
|
15
|
+
"xDomain": [0.05, 20],
|
|
16
|
+
"yDomain": [20, 60],
|
|
17
|
+
"xTicks": [0.1, 0.3, 1, 3, 10],
|
|
18
|
+
"xPrefix": "$",
|
|
19
|
+
"yTicks": [20, 30, 40, 50, 60],
|
|
20
|
+
"ySuffix": "%",
|
|
21
|
+
"xLabel": "cost per task (log)",
|
|
22
|
+
"series": [
|
|
23
|
+
{
|
|
24
|
+
"key": "small",
|
|
25
|
+
"points": [
|
|
26
|
+
{ "label": "low", "x": 0.08, "y": 28, "tip": "28% · $0.08/task" },
|
|
27
|
+
{ "label": "medium", "x": 0.2, "y": 34, "tip": "34% · $0.20/task" },
|
|
28
|
+
{ "label": "max", "x": 1, "y": 45, "tip": "45% · $1.00/task" }
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"key": "mid",
|
|
33
|
+
"points": [
|
|
34
|
+
{ "label": "low", "x": 0.5, "y": 35, "tip": "35% · $0.50/task" },
|
|
35
|
+
{ "label": "medium", "x": 0.8, "y": 40, "tip": "40% · $0.80/task" },
|
|
36
|
+
{ "label": "max", "x": 8, "y": 54, "tip": "54% · $8.00/task" }
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"key": "large",
|
|
41
|
+
"points": [
|
|
42
|
+
{ "label": "low", "x": 1.2, "y": 44, "tip": "44% · $1.20/task" },
|
|
43
|
+
{ "label": "medium", "x": 3, "y": 52, "tip": "52% · $3.00/task" },
|
|
44
|
+
{ "label": "max", "x": 12, "y": 57, "tip": "57% · $12.00/task" }
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"key": "previous",
|
|
49
|
+
"points": [
|
|
50
|
+
{ "label": "low", "x": 5, "y": 43, "tip": "43% · $5.00/task" },
|
|
51
|
+
{ "label": "max", "x": 16, "y": 50, "tip": "50% · $16.00/task" }
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "ladder",
|
|
58
|
+
"title": "Review: bugs found per effort level",
|
|
59
|
+
"sub": "Illustrative values; notes carry run counts.",
|
|
60
|
+
"categories": ["low", "medium", "high", "max"],
|
|
61
|
+
"yDomain": [0, 50],
|
|
62
|
+
"yTicks": [0, 25, 50],
|
|
63
|
+
"series": [
|
|
64
|
+
{
|
|
65
|
+
"key": "small",
|
|
66
|
+
"values": [10, 12, 16, null],
|
|
67
|
+
"notes": ["1 run", "1 run", "2 runs", "no row"]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"key": "mid",
|
|
71
|
+
"values": [18, 23, 34, 45],
|
|
72
|
+
"notes": ["3 runs", "3 runs", "3 runs", "3 runs"]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"key": "large",
|
|
76
|
+
"values": [22, 30, 32, 41],
|
|
77
|
+
"notes": ["3 runs", "3 runs", "3 runs", "3 runs"]
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"type": "pair",
|
|
83
|
+
"items": [
|
|
84
|
+
{
|
|
85
|
+
"type": "bars",
|
|
86
|
+
"title": "Generation speed: output tokens/s",
|
|
87
|
+
"max": 400,
|
|
88
|
+
"rows": [
|
|
89
|
+
{ "label": "Model S", "key": "small", "value": 380 },
|
|
90
|
+
{ "label": "Model M", "key": "mid", "value": 220 },
|
|
91
|
+
{ "label": "Model L", "key": "large", "value": 125 }
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"type": "bars",
|
|
96
|
+
"title": "Task time: minutes per task",
|
|
97
|
+
"max": 3,
|
|
98
|
+
"rows": [
|
|
99
|
+
{ "label": "Model S", "key": "small", "value": 2.6, "display": "2:36" },
|
|
100
|
+
{ "label": "Model M", "key": "mid", "value": 1, "display": "1:00" },
|
|
101
|
+
{ "label": "Model L", "key": "large", "value": 2.1, "display": "2:06" }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"type": "table",
|
|
108
|
+
"title": "Model S: where it fits",
|
|
109
|
+
"columns": ["Role", "Key evidence", "Verdict"],
|
|
110
|
+
"rows": [
|
|
111
|
+
[
|
|
112
|
+
"Narrow read-only lookups",
|
|
113
|
+
"Equal accuracy on known-answer lookups at a fraction of the cost",
|
|
114
|
+
{ "text": "Yes", "verdict": "yes" }
|
|
115
|
+
],
|
|
116
|
+
[
|
|
117
|
+
"Mechanical edits",
|
|
118
|
+
"Matches Model M medium at lower cost; untested on your work",
|
|
119
|
+
{ "text": "Try on a named task", "verdict": "maybe" }
|
|
120
|
+
],
|
|
121
|
+
["Review", "Finds about half of what Model L finds", { "text": "No", "verdict": "no" }]
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|