stilltrue 0.3.0 → 0.4.1
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 +115 -68
- package/dist/cli.js +2 -1
- package/dist/config.d.ts +0 -2
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -2,105 +2,152 @@
|
|
|
2
2
|
|
|
3
3
|
> Your tests check your code. **stilltrue** checks your facts.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
|
|
5
|
+
## The problem
|
|
6
|
+
|
|
7
|
+
Say you've built an app or chatbot that answers questions using facts you
|
|
8
|
+
collected — a list of school board members, office hours, tax rates,
|
|
9
|
+
whatever. You checked those facts when you wrote them down. But the world
|
|
10
|
+
keeps moving: a board member resigns, a rate changes, a first day of school
|
|
11
|
+
rolls over. **Your app doesn't know.** It keeps confidently repeating
|
|
12
|
+
yesterday's truth, and nothing alerts you.
|
|
13
|
+
|
|
14
|
+
stilltrue is the alarm. Once a week (or whenever you like), it re-reads the
|
|
15
|
+
official web pages your facts came from and checks that every fact you
|
|
16
|
+
recorded still appears there. If something vanished, your build fails and
|
|
17
|
+
tells you exactly which fact went stale — so a human can go verify and
|
|
18
|
+
update it.
|
|
19
|
+
|
|
20
|
+
## What you get
|
|
21
|
+
|
|
22
|
+
| Command | What it does |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `npx stilltrue drift` | Checks your saved facts against their live sources. Fails loudly if a fact has gone stale. |
|
|
25
|
+
| `npx stilltrue report` | Turns past check runs into a simple web page — green/red grid, what broke and when. |
|
|
26
|
+
| `verify` | For developers: a checkpoint that inspects AI-generated answers before they're shown to users. |
|
|
27
|
+
|
|
28
|
+
## Getting started
|
|
29
|
+
|
|
30
|
+
You'll need [Node.js](https://nodejs.org) (version 20 or newer) and a
|
|
31
|
+
project folder with your facts saved in a JSON file.
|
|
32
|
+
|
|
33
|
+
**1. Install it** — in your project folder, run:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install --save-dev stilltrue
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**2. Describe a check** — create a file called `stilltrue.config.mjs` in
|
|
40
|
+
the same folder. Each check answers three questions: *where do I fetch the
|
|
41
|
+
truth from* (`source`), *what do I believe* (`expect`), and *how do I
|
|
42
|
+
compare the two* (`compare`):
|
|
43
|
+
|
|
44
|
+
```js
|
|
39
45
|
import { defineStilltrue, corpus, json, surname } from 'stilltrue';
|
|
40
46
|
|
|
41
47
|
export default defineStilltrue({
|
|
42
48
|
drift: [
|
|
43
49
|
{
|
|
44
50
|
name: 'school-board-roster',
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
// Where the truth lives: the district's own pages.
|
|
52
|
+
source: corpus(['https://www.myschooldistrict.org/board']),
|
|
53
|
+
// What you believe: read your saved file, keep just the last names.
|
|
54
|
+
expect: json('./data/school-board.json', (data) =>
|
|
55
|
+
data.members.map((m) => surname(m.name))
|
|
56
|
+
),
|
|
57
|
+
// How to compare: every name must still appear on those pages.
|
|
47
58
|
compare: 'contains-all',
|
|
48
59
|
},
|
|
49
60
|
],
|
|
50
61
|
});
|
|
51
62
|
```
|
|
52
63
|
|
|
64
|
+
This example says: *fetch the board page, and make sure every last name in
|
|
65
|
+
my `school-board.json` file still appears somewhere on it.*
|
|
66
|
+
|
|
67
|
+
**3. Run it:**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx stilltrue drift
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
You'll see one line per check:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
✓ school-board-roster — pass (1204ms)
|
|
77
|
+
|
|
78
|
+
1 pass, 0 rot, 0 error (errors warn, never fail)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## The three results (and why there are three, not two)
|
|
82
|
+
|
|
83
|
+
- **pass** — every saved fact still appears at its source. All good.
|
|
84
|
+
- **rot** — a fact is gone from the source. The world changed; your data is
|
|
85
|
+
stale. This *fails the run* so you notice and fix the data.
|
|
86
|
+
- **error** — the website couldn't be reached (down, timeout, blocked).
|
|
87
|
+
This only *warns*. An unreachable site doesn't mean your facts are wrong,
|
|
88
|
+
and an alarm that cries wolf gets ignored — so outages never fail the build.
|
|
89
|
+
|
|
90
|
+
## Running it automatically every week
|
|
91
|
+
|
|
92
|
+
If your project is on GitHub, add this file as
|
|
93
|
+
`.github/workflows/drift.yml` and GitHub will run the check every Wednesday
|
|
94
|
+
and email you if anything rotted:
|
|
95
|
+
|
|
53
96
|
```yaml
|
|
54
|
-
|
|
97
|
+
name: Fact drift check
|
|
55
98
|
on:
|
|
56
|
-
schedule:
|
|
99
|
+
schedule:
|
|
100
|
+
- cron: '17 14 * * 3' # Wednesdays, ~10am US Eastern
|
|
101
|
+
workflow_dispatch: # adds a manual "Run workflow" button
|
|
57
102
|
jobs:
|
|
58
103
|
drift:
|
|
59
104
|
runs-on: ubuntu-latest
|
|
60
105
|
steps:
|
|
61
|
-
- uses: actions/checkout@
|
|
62
|
-
- uses: actions/setup-node@
|
|
106
|
+
- uses: actions/checkout@v5
|
|
107
|
+
- uses: actions/setup-node@v5
|
|
63
108
|
with: { node-version: '22' }
|
|
64
109
|
- run: npm ci
|
|
65
110
|
- run: npx stilltrue drift
|
|
66
111
|
```
|
|
67
112
|
|
|
68
|
-
|
|
69
|
-
deterministic either way). `expect` is your curated file. Rot fails the run
|
|
70
|
-
with one message per vanished fact; unreachable sources only warn.
|
|
113
|
+
## The report
|
|
71
114
|
|
|
72
|
-
|
|
115
|
+
Add `--record` to save each run, then generate the web page:
|
|
73
116
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
not verdicts: `failPolicy: 'open'` (default) warns and continues — a judge
|
|
79
|
-
outage must never block your app.
|
|
117
|
+
```bash
|
|
118
|
+
npx stilltrue drift --record
|
|
119
|
+
npx stilltrue report
|
|
120
|
+
```
|
|
80
121
|
|
|
81
|
-
|
|
82
|
-
|
|
122
|
+
That writes `stilltrue-report.html` — open it in any browser. It shows a
|
|
123
|
+
timeline grid (one row per check, one column per run, green ✓ / red ✗ /
|
|
124
|
+
amber ⚠) and a log of everything that ever rotted, with details.
|
|
83
125
|
|
|
84
|
-
|
|
85
|
-
zodStage('parse_schema', AnswerSchema),
|
|
86
|
-
{ name: 'grounding', timeoutMs: 10_000, check: llmJudge }, // may return a revision
|
|
87
|
-
{ name: 'chase-sim', tier: 'ci', check: expensiveSimulation }, // skipped inline
|
|
88
|
-
];
|
|
126
|
+
## One rule underneath everything
|
|
89
127
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
128
|
+
**AI may extract, but never decides.** The drift check itself uses no AI at
|
|
129
|
+
all — it fetches, reads, and compares text, the same way every time, with
|
|
130
|
+
no API key and no cost. Deterministic alarms are trustworthy alarms. (If
|
|
131
|
+
*your* fact-gathering step wants to use AI to pull structure out of a messy
|
|
132
|
+
page, that's fine — it happens inside your `source` function, and the
|
|
133
|
+
comparison stays mechanical.)
|
|
93
134
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
135
|
+
## For developers: `verify`
|
|
136
|
+
|
|
137
|
+
`verify` is a library for teams shipping AI-generated answers: an ordered
|
|
138
|
+
series of checkpoints (a schema parse, a rules check, an AI judge — your
|
|
139
|
+
choice) that each answer must pass before publishing. Rejections say
|
|
140
|
+
exactly which checkpoint failed and why, in a format you can feed back to
|
|
141
|
+
the model for a retry. A broken checker never blocks your app — it fails
|
|
142
|
+
open. See the [project docs](docs/BRIEF.md) and the typed API in
|
|
143
|
+
`stilltrue`'s exports (`runVerify`, `zodStage`, `generateVerified`).
|
|
97
144
|
|
|
98
|
-
##
|
|
145
|
+
## What about testing prompts?
|
|
99
146
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
147
|
+
That's a different job with a great existing tool:
|
|
148
|
+
[promptfoo](https://promptfoo.dev) tests your prompts; stilltrue tests your
|
|
149
|
+
facts. How they fit together: [docs/GOLDEN.md](docs/GOLDEN.md).
|
|
103
150
|
|
|
104
151
|
## License
|
|
105
152
|
|
|
106
|
-
MIT
|
|
153
|
+
MIT — free to use, modify, and share.
|
package/dist/cli.js
CHANGED
|
@@ -95,7 +95,8 @@ try {
|
|
|
95
95
|
process.exit(await report(rest));
|
|
96
96
|
break;
|
|
97
97
|
case 'golden':
|
|
98
|
-
console.error('stilltrue
|
|
98
|
+
console.error('stilltrue deliberately has no golden runner — promptfoo tests your prompts; stilltrue tests your facts.\n' +
|
|
99
|
+
'Use https://promptfoo.dev for prompt/answer regression evals. Doctrine + pairing guide: docs/GOLDEN.md');
|
|
99
100
|
process.exit(1);
|
|
100
101
|
break;
|
|
101
102
|
default:
|
package/dist/config.d.ts
CHANGED
|
@@ -58,8 +58,6 @@ export interface StilltrueConfig {
|
|
|
58
58
|
drift?: DriftCheck<any, any>[];
|
|
59
59
|
/** Named verify pipelines (see src/verify.ts) — usable from library code. */
|
|
60
60
|
verify?: Record<string, import('./verify.ts').VerifyStage<any>[]>;
|
|
61
|
-
/** golden is a later phase — see docs/BRIEF.md build plan. */
|
|
62
|
-
golden?: unknown;
|
|
63
61
|
}
|
|
64
62
|
/** Identity function for typed configs: `export default defineStilltrue({...})`. */
|
|
65
63
|
export declare function defineStilltrue(config: StilltrueConfig): StilltrueConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stilltrue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Your tests check your code. stilltrue checks your facts. CI checks for AI-in-the-loop apps: drift (curated facts vs live sources), golden (regression evals), verify (judge answers against sources).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"drift",
|
|
@@ -37,5 +37,13 @@
|
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=20"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/sg-bfoster/stilltrue.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/sg-bfoster/stilltrue#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/sg-bfoster/stilltrue/issues"
|
|
40
48
|
}
|
|
41
49
|
}
|