@tech-leads-club/harness-toolkit 0.4.2 → 0.4.3
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 +49 -11
- package/bin/tlc-cli.ts +25 -0
- package/dist/compact-before.mjs +73 -73
- package/dist/doctor.mjs +74 -74
- package/dist/init-project.mjs +78 -78
- package/dist/install-runtime.mjs +73 -73
- package/dist/lessons-cli.mjs +75 -75
- package/dist/obs-cli.mjs +73 -73
- package/dist/price-lookup.mjs +2 -2
- package/dist/prompt-submit.mjs +73 -73
- package/dist/refresh-model-prices.mjs +73 -73
- package/dist/response-after.mjs +73 -73
- package/dist/run.mjs +72 -72
- package/dist/session-end.mjs +78 -78
- package/dist/session-start.mjs +80 -80
- package/dist/shim.mjs +72 -72
- package/dist/stop.mjs +81 -81
- package/dist/subagent-start.mjs +73 -73
- package/dist/subagent-stop.mjs +74 -74
- package/dist/support.mjs +78 -78
- package/dist/tlc-cli.mjs +82 -82
- package/dist/tool-after.mjs +73 -73
- package/dist/tool-before.mjs +73 -73
- package/dist/tool-failure.mjs +73 -73
- package/dist/uninstall-runtime.mjs +3 -3
- package/docs/concepts.md +11 -0
- package/docs/log.md +1 -0
- package/package.json +2 -1
- package/skills/harness-init/SKILL.md +34 -2
- package/src/core/core.facade.ts +9 -1
- package/src/platform/paths.ts +16 -2
- package/tools/doctor.ts +15 -5
- package/src/core/floor/index.ts +0 -1
package/README.md
CHANGED
|
@@ -65,6 +65,7 @@ To give one project its own rules, open it and say **"setup harness"** to the ag
|
|
|
65
65
|
- [Tier 1 — the floor](#tier-1--the-floor-no-configuration-reaches-it)
|
|
66
66
|
- [Tier 2 — always on, no switch](#tier-2--always-on-no-switch)
|
|
67
67
|
- [Tier 3 — the rails you choose](#tier-3--the-rails-you-choose)
|
|
68
|
+
- [One rail takes a rule you write](#one-rail-takes-a-rule-you-write)
|
|
68
69
|
3. [How to see any of it](#how-to-see-any-of-it)
|
|
69
70
|
4. [How to explain a decision](#how-to-explain-a-decision)
|
|
70
71
|
5. [Providers](#providers)
|
|
@@ -180,6 +181,46 @@ Each rail's full benefit and trade-off — the long form, as the init wizard rea
|
|
|
180
181
|
the agent surfaces and what earns an interruption. It switches no gate on and weakens no verification —
|
|
181
182
|
the evidence bar is identical at all three ([`docs/decisions/ad-025.md`](docs/decisions/ad-025.md)).
|
|
182
183
|
|
|
184
|
+
### One rail takes a rule you write
|
|
185
|
+
|
|
186
|
+
Every other capability is a switch. **Operator rules** is a switch plus a file, because the trigger and the
|
|
187
|
+
proof are yours: *on this trigger, this must have happened, otherwise do that.* No pull request without a
|
|
188
|
+
review, for instance:
|
|
189
|
+
|
|
190
|
+
```markdown
|
|
191
|
+
---
|
|
192
|
+
on: pr-open
|
|
193
|
+
require:
|
|
194
|
+
- subagent(the-jury) since HEAD
|
|
195
|
+
otherwise: deny
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
Convene the jury on this branch. Checklist: docs/review-checklist.md
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Save it as one markdown file per rule in `.tlc/harness/rules/` for this repository, or in the runtime home's
|
|
202
|
+
`rules/` for every repository on this machine, and set `rules.enabled: true`. The proof has to be something
|
|
203
|
+
the harness observed — a subagent that ran, a command that completed, a gate that passed, a file that changed
|
|
204
|
+
— so the agent cannot write one: that store is under the project state directory, which the floor refuses it.
|
|
205
|
+
|
|
206
|
+
`tlc harness doctor` lists every rule it loaded, and says so when the switch is on and no file was found.
|
|
207
|
+
The full grammar — six triggers, four proof kinds, four verdicts and how each lands at the stop — is
|
|
208
|
+
`tlc harness help rules`.
|
|
209
|
+
|
|
210
|
+
**Why not the editor's own rules?** Because a rule file there is context, not a gate: activation is a mode
|
|
211
|
+
you pick, always-apply defaults to off, and one of the modes leaves it to the model to decide whether to load
|
|
212
|
+
the rule at all. This is not a replacement for that file — it is the layer underneath it. The editor's rule
|
|
213
|
+
says what the agent should do; this one says what must be provably true before it may.
|
|
214
|
+
|
|
215
|
+
The fair comparison is with the editors' **hooks**, which are deterministic too. Three things separate them
|
|
216
|
+
from this, taken from their own reference rather than from opinion: there is no prerequisite mechanism and no
|
|
217
|
+
state store, so "X requires Y to have happened" is custom logic you write per event; the state you would keep
|
|
218
|
+
lives in an ordinary repository path the agent can write, while a proof here lives where the floor refuses it;
|
|
219
|
+
and one host's stop hook cannot refuse completion at all, so the same requirement has to mean something
|
|
220
|
+
different on each — which is what the capability table and the degrade step already do. A hook is a program
|
|
221
|
+
per host per repository, fail-open unless it opts out. A rule is five lines of frontmatter, two tiers, both
|
|
222
|
+
hosts ([`docs/decisions/ad-100.md`](docs/decisions/ad-100.md)).
|
|
223
|
+
|
|
183
224
|
## How to see any of it
|
|
184
225
|
|
|
185
226
|
Every row above names a command in its last column. These are those commands.
|
|
@@ -276,21 +317,18 @@ each provider it finds, because a provider only reads its own.
|
|
|
276
317
|
|
|
277
318
|
1. Materialises the runtime at `~/.tlc/harness` from the installed package
|
|
278
319
|
2. Creates `config.json` from `config.example.json` when missing
|
|
279
|
-
3. Links
|
|
280
|
-
4.
|
|
320
|
+
3. Links `tlc` into `~/.local/bin`, and says so if that directory is not on `PATH`
|
|
321
|
+
4. Links the init skill into each detected provider's `skills/harness-init`
|
|
322
|
+
5. Wires user-level hooks for every provider it detects installed, in that provider's resolved config
|
|
281
323
|
directory
|
|
282
324
|
|
|
283
|
-
The `tlc` command itself comes from npm, not from this step: `npm i -g` generates the shim for the platform it
|
|
284
|
-
runs on, and `npm link` does the same from a clone. If `tlc` is not found after installing, the shim is in the
|
|
285
|
-
`bin` directory of the Node version npm installed under — which is not on `PATH` when a version manager later
|
|
286
|
-
switches versions. A link solves it for good:
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
ln -s ~/.tlc/harness/bin/tlc ~/.local/bin/tlc
|
|
290
|
-
```
|
|
291
|
-
|
|
292
325
|
Update with `tlc harness update`, which bumps the package and re-materialises the runtime.
|
|
293
326
|
|
|
327
|
+
Every release is published from CI with a short-lived OIDC credential — no token is stored in the repository — and
|
|
328
|
+
carries a provenance attestation you can check on npmjs.com. What gates it is the test suite on four platforms plus
|
|
329
|
+
an install of the packed tarball into a clean container, driven as a real command
|
|
330
|
+
([`docs/decisions/ad-102.md`](docs/decisions/ad-102.md)).
|
|
331
|
+
|
|
294
332
|
Overrides: `TLC_HOME`, `TLC_REPO_URL`, `TLC_BIN_DIR`.
|
|
295
333
|
|
|
296
334
|
### Running a development copy
|
package/bin/tlc-cli.ts
CHANGED
|
@@ -1296,6 +1296,15 @@ export type TestStep = { label: string; bin: string; args: string[] };
|
|
|
1296
1296
|
// against the real repository — green from a shell, red from inside a hook.
|
|
1297
1297
|
export const TEST_ENV_IMPORT = ["--import", "./tools/test-env.mjs"];
|
|
1298
1298
|
|
|
1299
|
+
/**
|
|
1300
|
+
* The number of unused exports this repository carries today.
|
|
1301
|
+
*
|
|
1302
|
+
* why a number in code and not a snapshot file: it is a debt, and a debt that has to be edited down in a reviewed
|
|
1303
|
+
* commit is one somebody looks at. Lowering it is the point; raising it needs an argument in the diff
|
|
1304
|
+
* ([/decisions/ad-102.md](/decisions/ad-102.md)).
|
|
1305
|
+
*/
|
|
1306
|
+
export const KNIP_EXPORTS_CEILING = 76;
|
|
1307
|
+
|
|
1299
1308
|
export function buildTestSteps(): TestStep[] {
|
|
1300
1309
|
return [
|
|
1301
1310
|
// why: `--error-on-warnings`. A warn-level rule does not change biome's exit code, so three fixable warnings
|
|
@@ -1307,6 +1316,22 @@ export function buildTestSteps(): TestStep[] {
|
|
|
1307
1316
|
{ label: "tsc --noEmit", bin: "npx", args: ["tsc", "--noEmit"] },
|
|
1308
1317
|
{ label: "src suite", bin: "node", args: [...TEST_ENV_IMPORT, "--test", "src/**/__test__/*.test.ts"] },
|
|
1309
1318
|
{ label: "tools suite", bin: "node", args: [...TEST_ENV_IMPORT, "--test", "tools/__test__/*.test.ts"] },
|
|
1319
|
+
/**
|
|
1320
|
+
* why two knip steps and not one: `files` and `dependencies` are already at zero, so they block. `exports` has a
|
|
1321
|
+
* backlog of legitimately-exported-for-tests symbols, and a step that reports without failing is a signal that
|
|
1322
|
+
* never fires — so it blocks on *growth* instead, which is the published way to adopt this without a sweep
|
|
1323
|
+
* ([/decisions/ad-102.md](/decisions/ad-102.md)).
|
|
1324
|
+
*
|
|
1325
|
+
* hazard: `observe` was exported, wired into the facade and called by nothing, so no proof could ever exist and
|
|
1326
|
+
* every operator rule denied for ever. 113 tests passed. This is the check that sees that class
|
|
1327
|
+
* ([/decisions/ad-100.md](/decisions/ad-100.md)).
|
|
1328
|
+
*/
|
|
1329
|
+
{ label: "knip: dead files and dependencies", bin: "npx", args: ["knip", "--files", "--dependencies"] },
|
|
1330
|
+
{
|
|
1331
|
+
label: "knip: unused exports do not grow",
|
|
1332
|
+
bin: "npx",
|
|
1333
|
+
args: ["knip", "--exports", "--max-issues", String(KNIP_EXPORTS_CEILING)],
|
|
1334
|
+
},
|
|
1310
1335
|
{ label: "check-boundaries", bin: "node", args: ["tools/dev/check-boundaries.ts"] },
|
|
1311
1336
|
// why: `--error-on-warnings` above cannot see a rule that was suppressed rather than fixed, and biome accepts
|
|
1312
1337
|
// any text after the colon. This is what makes the reason a reason ([/decisions/ad-051.md](/decisions/ad-051.md)).
|