@tech-leads-club/harness-toolkit 0.2.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/CHANGELOG.md +95 -0
- package/LICENSE +84 -0
- package/NOTICE +9 -0
- package/README.md +480 -0
- package/bin/tlc +10 -0
- package/bin/tlc-build +80 -0
- package/bin/tlc-cli.ts +1509 -0
- package/bin/tlc-exec +10 -0
- package/bin/tlc-exec.cmd +4 -0
- package/bin/tlc-exec.d.mts +60 -0
- package/bin/tlc-exec.mjs +248 -0
- package/bin/tlc.cmd +4 -0
- package/bin/tlc.mjs +7 -0
- package/bin/write-user-hooks.d.mts +40 -0
- package/bin/write-user-hooks.mjs +123 -0
- package/capabilities/catalog.json +418 -0
- package/config.example.json +51 -0
- package/dist/compact-before.mjs +7839 -0
- package/dist/doctor.mjs +8263 -0
- package/dist/help-topic.mjs +82 -0
- package/dist/init-project.mjs +993 -0
- package/dist/install-runtime.mjs +969 -0
- package/dist/lessons-cli.mjs +7258 -0
- package/dist/obs-cli.mjs +7025 -0
- package/dist/price-lookup.mjs +221 -0
- package/dist/prompt-submit.mjs +7860 -0
- package/dist/refresh-model-prices.mjs +158 -0
- package/dist/response-after.mjs +7879 -0
- package/dist/run.mjs +7830 -0
- package/dist/session-end.mjs +7937 -0
- package/dist/session-start.mjs +8056 -0
- package/dist/shim.mjs +52 -0
- package/dist/stop.mjs +8494 -0
- package/dist/subagent-start.mjs +7861 -0
- package/dist/subagent-stop.mjs +7851 -0
- package/dist/support.mjs +7045 -0
- package/dist/tlc-cli.mjs +8063 -0
- package/dist/tool-after.mjs +8113 -0
- package/dist/tool-before.mjs +7985 -0
- package/dist/tool-failure.mjs +7850 -0
- package/dist/uninstall-runtime.mjs +1307 -0
- package/docs/architecture.md +270 -0
- package/docs/concepts.md +562 -0
- package/docs/coverage.md +55 -0
- package/docs/decisions/ad-001.md +32 -0
- package/docs/decisions/ad-002.md +51 -0
- package/docs/decisions/ad-003.md +30 -0
- package/docs/decisions/ad-004.md +37 -0
- package/docs/decisions/ad-005.md +36 -0
- package/docs/decisions/ad-006.md +49 -0
- package/docs/decisions/ad-007.md +36 -0
- package/docs/decisions/ad-008.md +54 -0
- package/docs/decisions/ad-009.md +61 -0
- package/docs/decisions/ad-010.md +45 -0
- package/docs/decisions/ad-011.md +59 -0
- package/docs/decisions/ad-012.md +71 -0
- package/docs/decisions/ad-013.md +87 -0
- package/docs/decisions/ad-014.md +56 -0
- package/docs/decisions/ad-015.md +33 -0
- package/docs/decisions/ad-016.md +98 -0
- package/docs/decisions/ad-017.md +65 -0
- package/docs/decisions/ad-018.md +77 -0
- package/docs/decisions/ad-019.md +75 -0
- package/docs/decisions/ad-020.md +88 -0
- package/docs/decisions/ad-021.md +57 -0
- package/docs/decisions/ad-022.md +120 -0
- package/docs/decisions/ad-023.md +87 -0
- package/docs/decisions/ad-024.md +85 -0
- package/docs/decisions/ad-025.md +92 -0
- package/docs/decisions/ad-026.md +131 -0
- package/docs/decisions/ad-027.md +90 -0
- package/docs/decisions/ad-028.md +90 -0
- package/docs/decisions/ad-029.md +73 -0
- package/docs/decisions/ad-030.md +97 -0
- package/docs/decisions/ad-031.md +94 -0
- package/docs/decisions/ad-032.md +83 -0
- package/docs/decisions/ad-033.md +89 -0
- package/docs/decisions/ad-034.md +86 -0
- package/docs/decisions/ad-035.md +86 -0
- package/docs/decisions/ad-036.md +68 -0
- package/docs/decisions/ad-037.md +47 -0
- package/docs/decisions/ad-038.md +52 -0
- package/docs/decisions/ad-039.md +69 -0
- package/docs/decisions/ad-040.md +89 -0
- package/docs/decisions/ad-041.md +98 -0
- package/docs/decisions/ad-042.md +82 -0
- package/docs/decisions/ad-043.md +79 -0
- package/docs/decisions/ad-044.md +61 -0
- package/docs/decisions/ad-045.md +94 -0
- package/docs/decisions/ad-046.md +111 -0
- package/docs/decisions/ad-047.md +96 -0
- package/docs/decisions/ad-048.md +85 -0
- package/docs/decisions/ad-049.md +66 -0
- package/docs/decisions/ad-050.md +94 -0
- package/docs/decisions/ad-051.md +69 -0
- package/docs/decisions/ad-052.md +69 -0
- package/docs/decisions/ad-053.md +78 -0
- package/docs/decisions/ad-054.md +98 -0
- package/docs/decisions/ad-055.md +74 -0
- package/docs/decisions/ad-056.md +85 -0
- package/docs/decisions/ad-057.md +68 -0
- package/docs/decisions/ad-058.md +97 -0
- package/docs/decisions/ad-059.md +82 -0
- package/docs/decisions/ad-060.md +75 -0
- package/docs/decisions/ad-061.md +68 -0
- package/docs/decisions/ad-062.md +72 -0
- package/docs/decisions/ad-063.md +84 -0
- package/docs/decisions/ad-064.md +79 -0
- package/docs/decisions/ad-065.md +81 -0
- package/docs/decisions/ad-066.md +111 -0
- package/docs/decisions/ad-067.md +64 -0
- package/docs/decisions/ad-068.md +79 -0
- package/docs/decisions/ad-069.md +74 -0
- package/docs/decisions/ad-070.md +86 -0
- package/docs/decisions/ad-071.md +93 -0
- package/docs/decisions/ad-072.md +82 -0
- package/docs/decisions/ad-073.md +102 -0
- package/docs/decisions/ad-074.md +91 -0
- package/docs/decisions/ad-075.md +79 -0
- package/docs/decisions/ad-076.md +102 -0
- package/docs/decisions/ad-077.md +94 -0
- package/docs/decisions/ad-078.md +84 -0
- package/docs/decisions/ad-079.md +73 -0
- package/docs/decisions/ad-080.md +86 -0
- package/docs/decisions/ad-081.md +70 -0
- package/docs/decisions/ad-082.md +79 -0
- package/docs/decisions/ad-083.md +88 -0
- package/docs/decisions/index.md +111 -0
- package/docs/diagnose.md +248 -0
- package/docs/index.md +52 -0
- package/docs/init.md +101 -0
- package/docs/lessons.md +294 -0
- package/docs/log.md +139 -0
- package/docs/measure.md +178 -0
- package/docs/providers/claude-code.md +118 -0
- package/docs/providers/cursor.md +110 -0
- package/docs/providers/index.md +71 -0
- package/docs/troubleshooting.md +138 -0
- package/model-aliases.json +12 -0
- package/model-prices.cursor.json +410 -0
- package/model-prices.json +1 -0
- package/package.json +57 -0
- package/skills/harness-init/SKILL.md +241 -0
- package/skills/harness-init/evals/trigger_evals.json +86 -0
- package/skills/harness-init/references/capabilities.md +113 -0
- package/src/contracts/capabilities.ts +30 -0
- package/src/contracts/decision.ts +21 -0
- package/src/contracts/effort.ts +15 -0
- package/src/contracts/harness-event.ts +87 -0
- package/src/contracts/index.ts +6 -0
- package/src/contracts/policy-defaults.ts +14 -0
- package/src/contracts/wiring.ts +20 -0
- package/src/core/attest/attest.service.ts +0 -0
- package/src/core/capability/capability.service.ts +62 -0
- package/src/core/capability/capability.store.ts +51 -0
- package/src/core/capability/capability.types.ts +56 -0
- package/src/core/comment-policy/comment-policy.service.ts +307 -0
- package/src/core/comment-policy/comment-policy.types.ts +6 -0
- package/src/core/comment-policy/comment-resolvability.ts +134 -0
- package/src/core/comment-policy/comment-syntax.catalog.ts +310 -0
- package/src/core/comment-policy/comment-syntax.store.ts +65 -0
- package/src/core/comment-policy/comment-syntax.types.ts +24 -0
- package/src/core/core.facade.ts +461 -0
- package/src/core/duplication/duplication.service.ts +219 -0
- package/src/core/floor/floor.catalog.ts +54 -0
- package/src/core/floor/floor.paths.ts +90 -0
- package/src/core/floor/floor.policy-surface.ts +311 -0
- package/src/core/floor/floor.service.ts +306 -0
- package/src/core/floor/floor.tokenize.ts +179 -0
- package/src/core/floor/floor.verb.ts +28 -0
- package/src/core/floor/index.ts +1 -0
- package/src/core/gate/gate.artifact.ts +157 -0
- package/src/core/gate/gate.command.ts +130 -0
- package/src/core/gate/gate.findings.ts +201 -0
- package/src/core/gate/gate.inputs.ts +0 -0
- package/src/core/gate/gate.lock.ts +283 -0
- package/src/core/gate/gate.service.ts +28 -0
- package/src/core/gate/gate.types.ts +67 -0
- package/src/core/handoff/handoff.service.ts +43 -0
- package/src/core/handoff/handoff.store.ts +59 -0
- package/src/core/handoff/handoff.types.ts +89 -0
- package/src/core/index.ts +52 -0
- package/src/core/integrity/state-seal.ts +81 -0
- package/src/core/lesson/lesson.authored.ts +96 -0
- package/src/core/lesson/lesson.credit.ts +51 -0
- package/src/core/lesson/lesson.garden.ts +283 -0
- package/src/core/lesson/lesson.link.ts +93 -0
- package/src/core/lesson/lesson.score.ts +61 -0
- package/src/core/lesson/lesson.select.ts +209 -0
- package/src/core/lesson/lesson.service.ts +110 -0
- package/src/core/lesson/lesson.store.ts +317 -0
- package/src/core/lesson/lesson.sync.ts +70 -0
- package/src/core/lesson/lesson.types.ts +92 -0
- package/src/core/lesson/lesson.validity.ts +44 -0
- package/src/core/observability/observability.report.ts +326 -0
- package/src/core/observability/observability.service.ts +328 -0
- package/src/core/observability/observability.store.ts +269 -0
- package/src/core/observability/observability.types.ts +193 -0
- package/src/core/observability/observability.why.ts +206 -0
- package/src/core/observe/observe.service.ts +76 -0
- package/src/core/plan/plan.detect.ts +47 -0
- package/src/core/plan/plan.service.ts +81 -0
- package/src/core/plan/plan.types.ts +25 -0
- package/src/core/policy/policy.defaults.ts +103 -0
- package/src/core/policy/policy.guard.ts +34 -0
- package/src/core/policy/policy.integrity.ts +238 -0
- package/src/core/policy/policy.loader.ts +118 -0
- package/src/core/policy/policy.operator.ts +82 -0
- package/src/core/policy/policy.posture.ts +79 -0
- package/src/core/policy/policy.rails.ts +45 -0
- package/src/core/policy/policy.types.ts +157 -0
- package/src/core/presence/presence.service.ts +111 -0
- package/src/core/presence/presence.store.ts +59 -0
- package/src/core/presence/presence.types.ts +9 -0
- package/src/core/release/release.decisions.ts +147 -0
- package/src/core/release/release.seen.ts +38 -0
- package/src/core/shell-policy/shell-policy.service.ts +215 -0
- package/src/core/shell-policy/shell-policy.stall.ts +57 -0
- package/src/core/shell-policy/shell-policy.types.ts +26 -0
- package/src/core/ship/ship.ledger.ts +76 -0
- package/src/core/ship/ship.service.ts +137 -0
- package/src/core/ship/ship.types.ts +19 -0
- package/src/core/stagnation/stagnation.resolution.ts +87 -0
- package/src/core/stagnation/stagnation.service.ts +20 -0
- package/src/core/stagnation/stagnation.store.ts +49 -0
- package/src/core/stagnation/stagnation.types.ts +12 -0
- package/src/core/subagent-policy/subagent-policy.parent-model.ts +202 -0
- package/src/core/subagent-policy/subagent-policy.service.ts +139 -0
- package/src/core/subagent-policy/subagent-policy.types.ts +22 -0
- package/src/core/supply-chain/supply-chain.catalog.ts +51 -0
- package/src/core/supply-chain/supply-chain.service.ts +217 -0
- package/src/core/turn/turn.activity.ts +94 -0
- package/src/core/turn/turn.autopilot.ts +131 -0
- package/src/core/turn/turn.failure-signals.ts +193 -0
- package/src/core/turn/turn.loop-counter.ts +71 -0
- package/src/core/turn/turn.types.ts +14 -0
- package/src/core/untrusted/untrusted.detect.ts +53 -0
- package/src/core/untrusted/untrusted.recall.ts +87 -0
- package/src/core/untrusted/untrusted.service.ts +129 -0
- package/src/core/untrusted/untrusted.store.ts +75 -0
- package/src/core/untrusted/untrusted.types.ts +42 -0
- package/src/entrypoints/compact-before.ts +14 -0
- package/src/entrypoints/prompt-submit.ts +25 -0
- package/src/entrypoints/response-after.ts +60 -0
- package/src/entrypoints/run.ts +179 -0
- package/src/entrypoints/session-end.ts +58 -0
- package/src/entrypoints/session-start.ts +172 -0
- package/src/entrypoints/shim.ts +46 -0
- package/src/entrypoints/stop.ts +876 -0
- package/src/entrypoints/subagent-start.ts +16 -0
- package/src/entrypoints/subagent-stop.ts +32 -0
- package/src/entrypoints/support.ts +182 -0
- package/src/entrypoints/tool-after.ts +111 -0
- package/src/entrypoints/tool-before.ts +186 -0
- package/src/entrypoints/tool-failure.ts +26 -0
- package/src/platform/backoff.ts +49 -0
- package/src/platform/cli-output.ts +30 -0
- package/src/platform/env-scope.ts +22 -0
- package/src/platform/fs-atomic.ts +142 -0
- package/src/platform/fs-jsonl.ts +24 -0
- package/src/platform/git.ts +187 -0
- package/src/platform/paths.ts +80 -0
- package/src/platform/pricing.ts +229 -0
- package/src/platform/process.ts +64 -0
- package/src/platform/sanitize.ts +23 -0
- package/src/platform/screen.ts +46 -0
- package/src/platform/style.ts +103 -0
- package/src/providers/claude/claude.capabilities.ts +25 -0
- package/src/providers/claude/claude.detect.ts +14 -0
- package/src/providers/claude/claude.inbound.ts +252 -0
- package/src/providers/claude/claude.lessons-view.ts +33 -0
- package/src/providers/claude/claude.outbound.ts +65 -0
- package/src/providers/claude/claude.policy-defaults.ts +9 -0
- package/src/providers/claude/claude.transcript.ts +62 -0
- package/src/providers/claude/claude.wiring.ts +245 -0
- package/src/providers/claude/index.ts +17 -0
- package/src/providers/cursor/cursor.capabilities.ts +28 -0
- package/src/providers/cursor/cursor.detect.ts +14 -0
- package/src/providers/cursor/cursor.inbound.ts +203 -0
- package/src/providers/cursor/cursor.lessons-view.ts +30 -0
- package/src/providers/cursor/cursor.outbound.ts +43 -0
- package/src/providers/cursor/cursor.policy-defaults.ts +9 -0
- package/src/providers/cursor/cursor.wiring.ts +214 -0
- package/src/providers/cursor/index.ts +17 -0
- package/src/providers/index.ts +9 -0
- package/src/providers/provider.degrade.ts +152 -0
- package/src/providers/provider.port.ts +20 -0
- package/src/providers/provider.registry.ts +28 -0
- package/tools/doctor.ts +631 -0
- package/tools/help-topic.ts +74 -0
- package/tools/init-project.ts +277 -0
- package/tools/install-runtime.ts +164 -0
- package/tools/lessons-cli.ts +474 -0
- package/tools/obs-cli.ts +175 -0
- package/tools/price-lookup.ts +57 -0
- package/tools/refresh-model-prices.ts +210 -0
- package/tools/test-env.mjs +28 -0
- package/tools/test-env.names.d.mts +1 -0
- package/tools/test-env.names.mjs +14 -0
- package/tools/uninstall-runtime.ts +463 -0
package/docs/diagnose.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Runbook
|
|
3
|
+
title: "Diagnose"
|
|
4
|
+
description: "Checklist for hooks not firing, Node vs Bun runtime confusion, stale runtime, subagent denials, cost showing null, and double hooks — for both Cursor and Claude Code."
|
|
5
|
+
tags: [runbook, diagnose, troubleshooting]
|
|
6
|
+
timestamp: "2026-07-29"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Diagnose
|
|
10
|
+
|
|
11
|
+
Run `tlc harness doctor` first. Then walk this checklist.
|
|
12
|
+
|
|
13
|
+
## Hooks not firing
|
|
14
|
+
|
|
15
|
+
**Cursor**
|
|
16
|
+
|
|
17
|
+
1. Confirm the Cursor config directory's `hooks.json` invokes `node …/tlc-exec.mjs <handler>` (installers
|
|
18
|
+
write this). `tlc harness doctor` prints the resolved path; `CURSOR_CONFIG_DIR` overrides the default.
|
|
19
|
+
2. `dist/*.mjs` must exist (`tlc harness build`).
|
|
20
|
+
3. Reload Cursor after editing hooks.
|
|
21
|
+
4. Open **View → Output → Hooks** for exit errors.
|
|
22
|
+
5. Project shim should call `tlc-exec shim <handler>`; with the global `sessionStart` hook set,
|
|
23
|
+
`TLC_ACTIVE=1` makes the shim no-op (expected).
|
|
24
|
+
|
|
25
|
+
**Claude Code**
|
|
26
|
+
|
|
27
|
+
1. Confirm the Claude config directory's `settings.json` (resolved, `CLAUDE_CONFIG_DIR` overrides the
|
|
28
|
+
default) has a `hooks` block with entries whose `command` is `node` and whose
|
|
29
|
+
`args` start with the launcher path (see [/providers/claude-code.md](/providers/claude-code.md)).
|
|
30
|
+
2. `dist/*.mjs` must exist (`tlc harness build`).
|
|
31
|
+
3. Restart the Claude Code session after editing `settings.json`.
|
|
32
|
+
4. Project shim should call `tlc-exec shim <handler>`.
|
|
33
|
+
|
|
34
|
+
On Windows, Cursor hooks use `cmd /c node "…\tlc-exec.mjs" …`; Claude Code hooks stay exec-form
|
|
35
|
+
(`node …`) on every platform.
|
|
36
|
+
|
|
37
|
+
## Node vs Bun
|
|
38
|
+
|
|
39
|
+
- Preferred: **Bun** on PATH — every hook runs the TypeScript source directly, ~1 ms per invocation.
|
|
40
|
+
- Guaranteed fallback: **Node 24+** + `tlc harness build` (`dist/*.mjs`), ~27 ms per invocation.
|
|
41
|
+
- `tlc harness doctor` reports the resolved runtime as `OK` (Bun found) or `WARN` (Node fallback, with the
|
|
42
|
+
measured cost of the gap and the one-line fix). See [/decisions/ad-012.md](/decisions/ad-012.md).
|
|
43
|
+
- Missing dist with Node present: run `tlc harness build` (needs Bun or esbuild once to compile).
|
|
44
|
+
|
|
45
|
+
## `update` aborts on `dist/` and keeps aborting
|
|
46
|
+
|
|
47
|
+
The one manual step, once, on every platform:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm i -g @tech-leads-club/harness-toolkit@latest
|
|
51
|
+
tlc harness install
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Why it has to be that and not `update`.** `update` runs from the installed runtime, and the fix for `update` is in
|
|
55
|
+
the revision `update` has to fetch — so a stuck install cannot deliver its own fix. The registry serves the package
|
|
56
|
+
independently of what is installed, which makes it the only route that does not depend on the thing that is stuck
|
|
57
|
+
([/decisions/ad-048.md](/decisions/ad-048.md)).
|
|
58
|
+
|
|
59
|
+
Installing over a managed checkout moves it to `origin/main` with a hard reset. `config.json` and `state/` are gitignored,
|
|
60
|
+
so your policy, global lessons and obs history survive it. A **linked** runtime — a symlink to your own clone — is
|
|
61
|
+
left completely alone.
|
|
62
|
+
|
|
63
|
+
There is no `--force`: a managed runtime is already reset, and a linked clone is never written to.
|
|
64
|
+
|
|
65
|
+
## Stale runtime / need latest main
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
tlc harness update
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then reload/restart the provider session.
|
|
72
|
+
|
|
73
|
+
**What update may write depends on what the runtime path is.** `tlc harness doctor` prints it as
|
|
74
|
+
`runtime ownership` ([/decisions/ad-046.md](/decisions/ad-046.md)):
|
|
75
|
+
|
|
76
|
+
| Kind | What update does |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `managed checkout` | fetches and moves it to upstream with a hard reset — the harness owns its contents, so a local change there is never yours |
|
|
79
|
+
| `link to a working clone` | **nothing** in the clone. Refreshes only the CLI link, the skill link and provider hooks. Pull that clone yourself |
|
|
80
|
+
| `installed from npm` | bumps the package to `@latest` and re-materialises the runtime. No git command runs against it |
|
|
81
|
+
| `not a git checkout` | nothing to pull — install the package and run `tlc harness install` |
|
|
82
|
+
|
|
83
|
+
Update never rebuilds `dist/` when every bundle is present. It used to, and because Bun and esbuild emit different
|
|
84
|
+
bytes for the same source, the rebuild left the checkout permanently dirty and every later update failed. If you see
|
|
85
|
+
`update: dist/ complete — no rebuild`, that is the fix working.
|
|
86
|
+
|
|
87
|
+
After update, `tlc harness doctor` reports non-blocking `WARN:` lines for off/missing opt-ins (and for
|
|
88
|
+
default-on features you explicitly set to `false`). They do not fail doctor by themselves. A missing
|
|
89
|
+
`.tlc/harness/config.json` still fails the project-policy check until you init.
|
|
90
|
+
|
|
91
|
+
## `tlc: command not found`
|
|
92
|
+
|
|
93
|
+
Re-run the platform installer, or ensure the CLI shim is on PATH:
|
|
94
|
+
|
|
95
|
+
- Unix: `~/.local/bin/tlc` → `~/.tlc/harness/bin/tlc`
|
|
96
|
+
- Windows: `%USERPROFILE%\.local\bin\tlc.cmd`
|
|
97
|
+
|
|
98
|
+
## Obs empty / no signal
|
|
99
|
+
|
|
100
|
+
1. `observability.enabled` must not be `false` in config.
|
|
101
|
+
2. Happy-path tool/shell events are **debug** — enable `debugEnabled` or look for signal kinds only.
|
|
102
|
+
3. Confirm `.tlc/harness/state/` is writable in the project.
|
|
103
|
+
4. `tlc harness obs live` after a prompt submit / stop / denial.
|
|
104
|
+
|
|
105
|
+
## The floor blocked a command that only reads harness state
|
|
106
|
+
|
|
107
|
+
Reading is ordinary work and the bootstrap asks for it, so a refusal there means the *verb* could not be proven to
|
|
108
|
+
only read — not that reading is forbidden. The refusal now says so and names the way through
|
|
109
|
+
([/decisions/ad-047.md](/decisions/ad-047.md)):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
tlc harness handoff # handoff state, no shell needed
|
|
113
|
+
tlc harness policy # the resolved policy
|
|
114
|
+
tlc harness handoff --json # same, for a script
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Proven readers on the policy surface: `cat`, `head`, `tail`, `less`, `more`, `grep`, `rg`, `jq`, `ls`, `stat`,
|
|
118
|
+
`file`, `wc`, `cmp`, `diff`, `od`, `xxd`, `strings`, `md5sum`, `sha256sum`, `echo`, `printf`, `test`, `[`, and
|
|
119
|
+
`git show|diff|log|status|ls-files|cat-file|blame`.
|
|
120
|
+
|
|
121
|
+
`awk` and `sort` are **not** readers, on purpose — `awk '{print > f}'` and `sort -o f` write a file the head verb
|
|
122
|
+
never reveals. A redirect onto the surface is denied whatever the verb, so `test -f x > config.json` still fails.
|
|
123
|
+
|
|
124
|
+
## The gate runs on a turn that changed nothing
|
|
125
|
+
|
|
126
|
+
It does not any more, and this is how to confirm it. A verdict is keyed on a content hash of the gate command and
|
|
127
|
+
the files it ran against; a match reuses the verdict without executing the command
|
|
128
|
+
([/decisions/ad-045.md](/decisions/ad-045.md)).
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
tlc harness obs report # the Gate time table has a Reused column
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Reading | Meaning |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `Runs` climbing on every turn | the inputs really are changing, or the hash is incomplete |
|
|
137
|
+
| `Reused` climbing | the verdict stood and the command did not run |
|
|
138
|
+
| both zero | the gate never ran; check `grind.enabled` and `codePaths` |
|
|
139
|
+
|
|
140
|
+
The hash is **incomplete** — so the gate always runs — when an input cannot be read, which includes a tracked file
|
|
141
|
+
that was deleted, or when the changed set exceeds 400 files or 12 MB. A gate that depends on something outside the
|
|
142
|
+
changed files (a database, a service, an environment variable) can also reuse a verdict that no longer holds.
|
|
143
|
+
|
|
144
|
+
## A lesson is not reaching the turn
|
|
145
|
+
|
|
146
|
+
`tlc harness lessons list` answers it directly — a lesson that is being withheld is marked `WITHHELD` and the
|
|
147
|
+
notes on its line say why.
|
|
148
|
+
|
|
149
|
+
| Note | Meaning | What to do |
|
|
150
|
+
| --- | --- | --- |
|
|
151
|
+
| `stale=path-missing` / `symbol-missing` | a `--ref` no longer resolves | restore or rename the ref, then `tlc harness lessons garden` |
|
|
152
|
+
| `validity=expired` | past its `--until` | the next garden prunes it; write a new one |
|
|
153
|
+
| `validity=pending` | its window has not opened | wait, or rewrite without `--from` |
|
|
154
|
+
| `validity=invalid` | an unparseable bound, so it fails closed | rewrite with an ISO date |
|
|
155
|
+
| `WITHHELD` with no note | a **global** lesson whose refs do not resolve in this repository | expected — it applies where it came from |
|
|
156
|
+
|
|
157
|
+
Nothing withheld and still absent? Then it lost on rank or budget, not on health:
|
|
158
|
+
|
|
159
|
+
1. `enabled=true` on the last line of `lessons list`.
|
|
160
|
+
2. `status` must be `active` for session injection; a `candidate` only shows on a matching retry.
|
|
161
|
+
3. **The budget usually binds.** `maxCharsSession` defaults to 900 and fits about two blocks while
|
|
162
|
+
`maxInjectSession` says five. The injected block names what it dropped; raising `maxCharsSession` is the fix.
|
|
163
|
+
4. **A rule you consider non-negotiable should be pinned, not ranked** — `lessons add … --pin` puts it ahead of
|
|
164
|
+
every scored lesson ([/decisions/ad-043.md](/decisions/ad-043.md)).
|
|
165
|
+
|
|
166
|
+
`tlc harness doctor` carries the same facts as one row (`lesson health`), and warns separately about stale,
|
|
167
|
+
out-of-window and unproven lessons. See [/lessons.md](/lessons.md).
|
|
168
|
+
|
|
169
|
+
## Two agents in one checkout, and the grind
|
|
170
|
+
|
|
171
|
+
A turn no longer blocks because a neighbour session is mid-gate. It resolves in this order
|
|
172
|
+
([/decisions/ad-073.md](/decisions/ad-073.md)):
|
|
173
|
+
|
|
174
|
+
1. **A recorded verdict whose inputs hash matches is reused**, and the lock is never taken. Two sessions editing
|
|
175
|
+
one tree usually land here, because the hash covers the command and the files rather than the session.
|
|
176
|
+
2. **Otherwise the turn waits**, up to ten seconds — bounded so the wait plus the gate still fit inside the
|
|
177
|
+
`Stop` hook's 120-second timeout.
|
|
178
|
+
3. **If the wait expires the gate defers.** The turn ends, the handoff records `last_gate_result: skipped` naming
|
|
179
|
+
the holder, and a `gate.outcome` carrying `deferred_to` reaches the record, so `tlc harness why` shows it.
|
|
180
|
+
|
|
181
|
+
Deferring is safe because both sessions share the tree: the neighbour's commands cover this turn's edits too. If
|
|
182
|
+
they pass, this turn was legitimate. If they fail, the neighbour is blocked holding the failure and the tree is
|
|
183
|
+
still broken, so the next stop in either session blocks on it. What is given up is only *which* turn is told.
|
|
184
|
+
|
|
185
|
+
A dead or stale holder never causes even the wait: the runtime reclaims past `GATE_LOCK_STALE_MS` (30 minutes) by
|
|
186
|
+
age, immediately when the pid is gone, and after a five-second grace window when the body cannot be read or names
|
|
187
|
+
no holder — a truncated write, a zero-length file, or JSON without `provider` / `session` / `pid`. Deleting the
|
|
188
|
+
file by hand is never necessary.
|
|
189
|
+
|
|
190
|
+
**An agent cannot switch the grind off from inside a session**, and that is correct: those subcommands are policy
|
|
191
|
+
surface ([/decisions/ad-022.md](/decisions/ad-022.md)). Run them from your own terminal.
|
|
192
|
+
|
|
193
|
+
## `status` disagrees with what a hook does
|
|
194
|
+
|
|
195
|
+
It no longer can: `status` reads `loadPolicy`, the same resolution a hook performs, and prints where the mode
|
|
196
|
+
came from — `[from config]`, `[from file]` or `[from flag]`. If the origin is `flag` or `file` and you expected
|
|
197
|
+
`config`, a leftover `tlc harness mode` or `grind on` is winning; clear it with `tlc harness mode solo` or
|
|
198
|
+
`tlc harness grind off`.
|
|
199
|
+
|
|
200
|
+
## A shim hook points at a path that does not exist
|
|
201
|
+
|
|
202
|
+
`tlc harness init` writes the install path (`~/.tlc/harness/bin/tlc-exec.mjs`), not the checkout behind its
|
|
203
|
+
symlink. A shim naming a checkout directory was written by an older runtime — re-run `tlc harness init` to
|
|
204
|
+
regenerate it. These files stay untracked on purpose: they carry an absolute path that is only valid on the
|
|
205
|
+
machine that generated them.
|
|
206
|
+
|
|
207
|
+
## Doctor says a provider is "detected but not wired" while hooks fire
|
|
208
|
+
|
|
209
|
+
The launcher path is compared by the file it resolves to, not by the string. If this warning appears while
|
|
210
|
+
hooks demonstrably run, check that the path recorded in the provider's config still exists — a moved or
|
|
211
|
+
deleted checkout is a real break, whereas reaching the same file through a symlink is not and no longer
|
|
212
|
+
warns.
|
|
213
|
+
|
|
214
|
+
## Grind not looping
|
|
215
|
+
|
|
216
|
+
1. `tlc harness status` — grind must be ON.
|
|
217
|
+
2. Gates must not be PAUSED.
|
|
218
|
+
3. Project `.tlc/harness/config.json` needs `grind.lintCommand` / `grind.testCommand` if you expect those
|
|
219
|
+
gates.
|
|
220
|
+
4. On failure, inspect `.tlc/harness/state/last-gate.json` (`findings`, `exitCode`, `outputTail`) before
|
|
221
|
+
trusting chat follow-up text.
|
|
222
|
+
5. Concurrent agents: wait for `.tlc/harness/state/grind.lock` or stop the other grind.
|
|
223
|
+
6. Stop status must be `completed` (aborted/error skips).
|
|
224
|
+
|
|
225
|
+
## Subagent model denied
|
|
226
|
+
|
|
227
|
+
Allowlist + blocked `*-fast`-shaped patterns (provider-specific — see
|
|
228
|
+
[/providers/index.md](/providers/index.md)). Check `subagents.allowedModels` in user config and project
|
|
229
|
+
`.tlc/harness/config.json`. Dual gate: `subagentStart` + `preToolUse` on a spawn tool. Optional
|
|
230
|
+
`subagents.blockParentFast` denies spawns while sticky parent state is Fast
|
|
231
|
+
(`.tlc/harness/state/parent-model.json`, see [/decisions/ad-001.md](/decisions/ad-001.md)).
|
|
232
|
+
|
|
233
|
+
## Cost always null
|
|
234
|
+
|
|
235
|
+
1. `tlc harness help prices`.
|
|
236
|
+
2. `tlc harness prices refresh` (or `refresh cursor` if you only need the primary catalog).
|
|
237
|
+
3. `tlc harness prices lookup <model> [provider]` — if null, the id is missing from that provider's
|
|
238
|
+
catalog + LiteLLM.
|
|
239
|
+
4. Add an alias in `~/.tlc/harness/model-aliases.json` if the provider's model slug ≠ catalog key.
|
|
240
|
+
5. Optional override in `model-prices.json` (local).
|
|
241
|
+
6. Events need input/output token counts; duration-only events yield null USD even when the catalog has the
|
|
242
|
+
model.
|
|
243
|
+
|
|
244
|
+
## Double hooks / slow turns
|
|
245
|
+
|
|
246
|
+
If both user and project hooks run the same heavy logic without shim no-op, fix shim / `TLC_ACTIVE`. Global
|
|
247
|
+
observability hooks should stay in the user-level hook file only (the resolved provider config
|
|
248
|
+
directory's `hooks.json` or `settings.json`), not duplicated into the project shim.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
okf_version: "0.1"
|
|
3
|
+
title: "agent-harness documentation bundle"
|
|
4
|
+
description: "OKF v0.1 documentation bundle for the tlc harness — architecture, concepts, runbooks, provider adapters, and the full architectural decision log."
|
|
5
|
+
tags: [index, okf]
|
|
6
|
+
timestamp: "2026-07-29"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# agent-harness docs
|
|
10
|
+
|
|
11
|
+
This is an [Open Knowledge Format v0.1](/decisions/ad-013.md) bundle: every non-reserved document below
|
|
12
|
+
carries YAML frontmatter with a `type` from the closed vocabulary `Concept | Runbook | Provider | Decision
|
|
13
|
+
| Capability | Aggregate`, plus `title`, `description`, `tags`, and `timestamp`. Cross-links are absolute
|
|
14
|
+
and bundle-relative (e.g. `/decisions/ad-010.md`), never relative.
|
|
15
|
+
|
|
16
|
+
See [/log.md](/log.md) for every decision in date order, and [/decisions/index.md](/decisions/index.md) for
|
|
17
|
+
the same records by number. Both are rendered from `docs/decisions/`, so neither can drift from it.
|
|
18
|
+
|
|
19
|
+
## Concepts
|
|
20
|
+
|
|
21
|
+
| Doc | Description |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| [/architecture.md](/architecture.md) | Ports-and-adapters shape of the harness: contracts, core, providers, entrypoints, and how the tlc CLI and runtime home fit together. |
|
|
24
|
+
| [/concepts.md](/concepts.md) | The operator-facing concepts behind the harness: grind, pause/resume, shipGate, subagent allowlist, comment policy, duplication, catastrophic shell, shell stall, the intelligence rails, observability planes, and cost estimates. |
|
|
25
|
+
| [/coverage.md](/coverage.md) | A self-assessment against a published agentic-risk taxonomy: which of the harness's own rules answer each risk, and what each one still leaves open. Control names are generated from the catalogs and checked by the gate. |
|
|
26
|
+
| [/lessons.md](/lessons.md) | Durable, ranked lessons that keep the agent from repeating gate failures — three tiers, staleness against a named reference, a validity window, effectiveness measured after injection, lifecycle, config, ranking, and the per-provider rendered view. |
|
|
27
|
+
|
|
28
|
+
## Runbooks
|
|
29
|
+
|
|
30
|
+
| Doc | Description |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| [/init.md](/init.md) | What tlc harness init creates, what it deliberately does not do, and the mandatory harness-init wizard's capability menu — including the Bun-vs-Node hook runtime question. |
|
|
33
|
+
| [/troubleshooting.md](/troubleshooting.md) | How to tell a harness decision from model behaviour, in the moment and after the fact: the one command that answers it, what each rule name means, and the four symptoms that are not the harness at all. |
|
|
34
|
+
| [/diagnose.md](/diagnose.md) | Checklist for hooks not firing, Node vs Bun runtime confusion, stale runtime, subagent denials, cost showing null, and double hooks — for both Cursor and Claude Code. |
|
|
35
|
+
| [/measure.md](/measure.md) | How to read harness observability: status, live signal, raw signal, session reports, audit trail, price catalogs, and the on-disk project state files, all provider-tagged. |
|
|
36
|
+
|
|
37
|
+
## Providers
|
|
38
|
+
|
|
39
|
+
| Doc | Description |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| [/providers/index.md](/providers/index.md) | Index of the provider adapters — Cursor and Claude Code — and the port they both implement. |
|
|
42
|
+
| [/providers/cursor.md](/providers/cursor.md) | The Cursor adapter — capability descriptor, event mapping, and wiring target for Cursor's hooks.json. |
|
|
43
|
+
| [/providers/claude-code.md](/providers/claude-code.md) | The Claude Code adapter — capability descriptor, event mapping, and wiring target for the settings.json hooks block in Claude Code's resolved config directory. |
|
|
44
|
+
|
|
45
|
+
## Decisions
|
|
46
|
+
|
|
47
|
+
| Doc | Description |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| [/decisions/index.md](/decisions/index.md) | Index of every architectural decision (AD-001…AD-016) made while building the multi-provider harness. |
|
|
50
|
+
|
|
51
|
+
Each individual decision (`/decisions/ad-001.md` … `/decisions/ad-016.md`) is listed with its own
|
|
52
|
+
description in [/decisions/index.md](/decisions/index.md).
|
package/docs/init.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Runbook
|
|
3
|
+
title: "Init (project bootstrap)"
|
|
4
|
+
description: "What tlc harness init creates, what it deliberately does not do, and the mandatory harness-init wizard's capability menu — including the Bun-vs-Node hook runtime question."
|
|
5
|
+
tags: [runbook, init, wizard]
|
|
6
|
+
timestamp: "2026-07-29"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Init (project bootstrap)
|
|
10
|
+
|
|
11
|
+
## What init creates (project-agnostic)
|
|
12
|
+
|
|
13
|
+
| File | Purpose |
|
|
14
|
+
|------|---------|
|
|
15
|
+
| `.tlc/harness/config.json` | Policy for this repo only |
|
|
16
|
+
| `.cursor/hooks.json` | Cursor shim to the global runtime — only written when `~/.cursor` is detected |
|
|
17
|
+
| `.claude/settings.json` (`hooks` block, merged) | Claude Code shim — only written when `~/.claude` is detected |
|
|
18
|
+
| `.gitignore` entries | `.tlc/harness/state/` |
|
|
19
|
+
|
|
20
|
+
`tlc harness init` detects which providers are installed (presence of `~/.cursor`, `~/.claude`) and wires
|
|
21
|
+
only those — it never assumes Cursor. See `tools/init-project.ts` and
|
|
22
|
+
[/providers/index.md](/providers/index.md).
|
|
23
|
+
|
|
24
|
+
## What init does NOT do
|
|
25
|
+
|
|
26
|
+
- Does not install the global runtime (assumes `~/.tlc/harness` already present)
|
|
27
|
+
- Does not assume Biome, Vitest, npm, or any stack
|
|
28
|
+
- Does not enable grind/shipGate/etc. unless the user opts in during discovery
|
|
29
|
+
|
|
30
|
+
## Wizard rule
|
|
31
|
+
|
|
32
|
+
The **harness-init skill is mandatory** for project setup. **Each capability is optional** — the wizard
|
|
33
|
+
must present benefit / trade-off / default and ask before enabling.
|
|
34
|
+
|
|
35
|
+
## Step 1b: hook runtime (ask once, never block)
|
|
36
|
+
|
|
37
|
+
Before the capability menu, the wizard checks whether **Bun** is on `PATH` and presents the measured
|
|
38
|
+
trade-off from [/decisions/ad-012.md](/decisions/ad-012.md):
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Hook runtime: Node (Bun not found)
|
|
42
|
+
Bun runs each hook in ~1ms; Node needs ~27ms.
|
|
43
|
+
At ~30 hooks per turn that is ~30ms vs ~810ms of startup.
|
|
44
|
+
Install: curl -fsSL https://bun.sh/install | bash
|
|
45
|
+
Node works fine — just slower. Continue on Node?
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Rules: never block (Node is fully supported), never ask twice (the answer is recorded in project state so
|
|
49
|
+
later runs skip this step; `doctor` keeps reporting it as a non-failing `WARN`), always give the exact
|
|
50
|
+
numbers, and re-probe if the user installs Bun mid-wizard rather than trusting the earlier check.
|
|
51
|
+
|
|
52
|
+
## Capability menu (for the skill wizard)
|
|
53
|
+
|
|
54
|
+
| Capability | Keys | Default | Benefit | Trade-off |
|
|
55
|
+
|------------|------|---------|---------|-----------|
|
|
56
|
+
| format | `format.*` | off | Consistent style on Write | Needs a correct format command |
|
|
57
|
+
| grind | `grind.*` | off | Lint/test follow-ups on stop | Turn cost; flaky commands thrash |
|
|
58
|
+
| shipGate | `shipGate.*` + `HARNESS_SHIP_CLAIM` | off | Honest ship after protocol claim | Needs evidence workflow; free English ignored |
|
|
59
|
+
| emptyDiffAntiShip | `shipGate.emptyDiffAntiShip` | off | No claim on empty tree | Annoys when zero-diff is correct |
|
|
60
|
+
| comments | `comments.*` | off | Junk-comment follow-ups | Noise on dirty trees |
|
|
61
|
+
| subagents | `subagents.*` | off | Model allowlist / require model | Maintain the list |
|
|
62
|
+
| blockParentFast | `subagents.blockParentFast` | off | Deny Task/subagent while parent is Fast | Needs sticky parent hooks; blocks intentional Fast parent |
|
|
63
|
+
| catastrophic shell | `shell.catastrophicAsk` | on | Ask before destructive shell | Extra prompts |
|
|
64
|
+
| shell stall | `shell.stallDetection` | off | Block repeated identical commands | False positives on retries |
|
|
65
|
+
| gap feedback | `intelligence.gapFeedback` | on | Structured PREVIOUS_GAPS | Longer follow-ups |
|
|
66
|
+
| failure classification | `intelligence.failureClassification` | on | Failure categories in handoff | Extra fields |
|
|
67
|
+
| progressive handoff | `intelligence.progressiveHandoff` | on | Gaps injected on next session | Slightly longer bootstrap |
|
|
68
|
+
| progressive context | `intelligence.progressiveContext` | on | Escalates context on each stop retry | Longer follow-ups on thrash |
|
|
69
|
+
| autopilot | `intelligence.autopilot` | on | Runtime decides ordered next steps | Agent must follow AUTOPILOT block |
|
|
70
|
+
| lessons | `intelligence.lessons.*` | off | Durable ranked lessons + decay | Uses context; enable when you want cross-session memory of gate fails |
|
|
71
|
+
| budget continue | `intelligence.budgetContinue` | off | Keep working under pressure if unfinished | Can delay clean stops |
|
|
72
|
+
| observability | usually global | signal on | Measure/diagnose | Disk under `.tlc/harness/state/` |
|
|
73
|
+
| mcpPrime / bootstrapExtra | arrays | empty | Project rails at sessionStart | Context cost |
|
|
74
|
+
|
|
75
|
+
Stagnation fingerprinting is built into grind gate fails (no separate toggle).
|
|
76
|
+
|
|
77
|
+
## CLI flags
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
tlc harness init --dry-run
|
|
81
|
+
tlc harness init --write [--stdin-json] [--force]
|
|
82
|
+
tlc harness init --minimal
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`--minimal` writes a safe, provider-agnostic stub (grind/ship off) plus whichever provider shims are
|
|
86
|
+
detected. Prefer the harness-init skill for full discovery.
|
|
87
|
+
|
|
88
|
+
## After write
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
tlc harness status
|
|
92
|
+
tlc harness doctor
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Next agent turn should set `TLC_ACTIVE` via the global `sessionStart` hook. Use `tlc harness help` for
|
|
96
|
+
concepts.
|
|
97
|
+
|
|
98
|
+
## Slash / global commands
|
|
99
|
+
|
|
100
|
+
Keep provider-native slash commands (e.g. Cursor's `~/.cursor/commands/`) global. Init must not create
|
|
101
|
+
`<repo>/.cursor/commands/` or an equivalent project-local commands directory for harness toggles or help.
|