@tduyng/pi-timbre 0.0.0-stage → 0.0.2
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/LICENSE +178 -0
- package/README.md +156 -2
- package/package.json +45 -4
- package/src/README.md +385 -0
- package/src/code.ts +154 -0
- package/src/config.ts +21 -0
- package/src/guidance/catalog.md +17 -0
- package/src/guidance/review.md +438 -0
- package/src/guide.ts +26 -0
- package/src/index.ts +695 -0
- package/src/memory.ts +238 -0
- package/src/patterns.ts +255 -0
- package/src/register.ts +35 -0
- package/src/report.ts +205 -0
- package/src/rules.ts +37 -0
- package/src/scan.ts +328 -0
- package/src/segments.ts +81 -0
- package/src/stats.ts +112 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
Use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
Copyright 2026 tduyng
|
package/README.md
CHANGED
|
@@ -1,3 +1,157 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tduyng/pi-timbre
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Constrains the agent's prose and code before tokens are generated, then measures what still gets through.
|
|
4
|
+
|
|
5
|
+
For full design rationale, see `src/README.md`.
|
|
6
|
+
|
|
7
|
+
## What it adds
|
|
8
|
+
|
|
9
|
+
`before_agent_start`: injects a constraint block into the system prompt each turn, which is the `Never` and `Code` sections of the catalog and nothing else; the five judgment sections are served on demand by `timbre_review_guide`.
|
|
10
|
+
|
|
11
|
+
`tool_call`: records the path a write touched, for the once-per-turn code check below, and notices a prose write once per file per session. It refuses nothing.
|
|
12
|
+
|
|
13
|
+
There was a preflight here that blocked the write, and this paragraph used to say so. It was removed in `0df54db3` because it repeated the report the result already carried, after `7f69c7ab` had already replaced the refusal with a note for the same reason. A blocked write discards the tool call and costs a full regeneration, which is more than the tell it prevented is worth.
|
|
14
|
+
|
|
15
|
+
The write is never refused, because refusing it discards the tool call and costs a full regeneration. One note per file per session: the model edits the spans the note names, and that edit is another write, which is another read.
|
|
16
|
+
|
|
17
|
+
`tool_result` note: appends a tell report to write and edit results for prose and TypeScript files.
|
|
18
|
+
|
|
19
|
+
### What the score was worth on `english`, and why the note is gone
|
|
20
|
+
|
|
21
|
+
It was a label, not a discriminator, on the checkpoint serving, and no reformulation of it fixed that. Seventeen samples, nine written by people and eight of this model's own replies: pi's vendor documentation, this repository's `AGENTS.md`, `GUIDE.md` from its first commit, and assistant replies from real sessions. All seventeen came back level 2. It is not reading length, since 200 characters of `AGENTS.md` scores the same as 3,500, and the argmax is not hiding a usable threshold either: the probability of level 0 separates the two classes at 0.167 AUC and `1 - p(level 2)` at 0.250, where chance is 0.5.
|
|
22
|
+
|
|
23
|
+
Three question shapes were measured on those same seventeen samples. The one as shipped scored 0.264. Rewriting the criteria so each level describes what it means, rather than naming it, scored 0.250. Replacing the three-level score with a single continuous judgement scored 0.514. None separates, so this is the checkpoint's ceiling and not the wording. `PLAN.md` records the same wall from the other side: making an instruction more precise collapsed its spread from 0.380 to 0.199, and the vaguer phrasing carried more signal than the precise one.
|
|
24
|
+
|
|
25
|
+
The one place a session does record who wrote a paragraph is the transcript itself: a user message is human and an assistant message is machine. Over eighty real passages from this repository's sessions, forty of each, the score reaches 0.512 AUC, at means 0.706 against 0.764 with the wrong one on top, and the note would have fired on 30 of the 40 machine passages and 26 of the 40 human ones. Scored on markdown files instead, thirteen same-file pairs where the agent had rewritten an earlier version, it reaches 0.500, though both sides of those pairs are agent-written and the test cannot settle much. The note was three quarters true and three quarters false at the same time, which is the worst of both: it spends 110ms of server time per prose write to tell the model to rewrite prose that a person wrote.
|
|
26
|
+
|
|
27
|
+
So the label is gone, and with it the switch, the shadow records, the turn-end drain and the write-path call. `PLAN.md`'s own rule gives the reason: a question earns a call only when the decision it reads exists in the transcript, and the note was reading a question the transcript does not answer.
|
|
28
|
+
|
|
29
|
+
`message_end`: silently records tell density per assistant reply.
|
|
30
|
+
|
|
31
|
+
`timbre_review_guide` tool: serves the judgment procedure on request.
|
|
32
|
+
|
|
33
|
+
`/timbre` subcommands: `on`, `off`, `fix <paths>`, `reset`, `status`, `stats`.
|
|
34
|
+
|
|
35
|
+
`ctrl+alt+t` shortcut: toggles prevention with the same persistence as the command. The status bar shows `on` or `off`.
|
|
36
|
+
|
|
37
|
+
## Token costs
|
|
38
|
+
|
|
39
|
+
The constraint block sits in the system prompt every turn. Pi marks it cacheable, and Anthropic bills cache writes at 1.25x and reads at 0.1x:
|
|
40
|
+
|
|
41
|
+
- The catalog, 986 tokens, and it is the only standing cost this package has
|
|
42
|
+
- Per turn, cache warm: 99 tokens
|
|
43
|
+
- Per turn, cache cold: 1,085 tokens
|
|
44
|
+
|
|
45
|
+
The constraint block is the only part of the rules the model reads before it writes, so it is built whenever prevention is on, . The read judges what landed and fails open, and a rule the model only meets afterwards is a rule it already broke.
|
|
46
|
+
|
|
47
|
+
Code is checked once per turn, not once per write. An oxlint spawn is about 130ms of fixed cost and a file adds 2-3ms, so a per-write check paid that fixed cost again for every file and still made the write wait for it. Batched at the end of the turn it is one spawn for everything the turn touched, off the write's path. It runs wherever the session runs, so there is no per-project command to remember.
|
|
48
|
+
|
|
49
|
+
Two arrangements came before this one and both failed. Sent as a follow-up, the note cost a whole model request, and by the time the model read one it had usually finished: sessions show it replying that the reports were stale and described states it had already replaced and committed. Fixing a finding also changes the file's signature, which earned a second follow-up for the tell the fix uncovered, so one file could produce a stream of them. Moved onto the next write or edit result, the note was unambiguous but dropped whenever a turn's last act was the write itself.
|
|
50
|
+
|
|
51
|
+
The note goes into the session as an entry at the turn's end. It used to ride the next write or edit result, which was unambiguous but dropped the note whenever a turn's last act was the write itself; a live check wrote `export function go(): unknown`, the scan found it, and the model never heard about it. An entry costs no model request, keeps its own provenance instead of borrowing a command's output, and is always delivered. Nothing else stands in for it, because nothing blocks a write. What remains is the deterministic tell report on the write and edit result, which covers the mechanical tells in the text the model wrote, so what is given up is the code check's end-of-task report rather than the prose report.
|
|
52
|
+
|
|
53
|
+
A 30-turn session costs about 3k tokens with a 1-hour cache TTL. The same session costs 33k if every turn goes cold, which is what Pi's default 5-minute TTL produces.
|
|
54
|
+
|
|
55
|
+
## What prevention is worth
|
|
56
|
+
|
|
57
|
+
Two runs, same model, scratch projects, prevention seeded through the extension's own persisted setting, replies scored by the deterministic scan. Counts are raw rather than per 1,000 characters: in the three-arm run the arms wrote within 7 percent of each other per reply, so normalising by length only re-expressed the same ordering.
|
|
58
|
+
|
|
59
|
+
Mechanical tells are the em dashes, semicolons and invisible characters. Judgment tells are the wall paragraphs and the monotone cadence.
|
|
60
|
+
|
|
61
|
+
| run | arm | replies | mechanical | judgment |
|
|
62
|
+
| --- | ------------------------- | ------: | ---------: | -------: |
|
|
63
|
+
| 1 | prevention on | 12 | 1 | 9 |
|
|
64
|
+
| 1 | prevention off | 19 | 14 | 22 |
|
|
65
|
+
| 2 | full catalog, prevention on | 12 | 2 | 8 |
|
|
66
|
+
| 2 | `Never` and `Code` only, prevention on | 12 | 3 | 11 |
|
|
67
|
+
| 2 | prevention off | 14 | 15 | 7 |
|
|
68
|
+
|
|
69
|
+
The mechanical result is established and replicated. Prevention takes them from 14 and 15 down to 1, 2 and 3, and the catalog that does it is the 117-token `Never` section: the reduced build carries the whole effect, so the other five prose sections are not what suppresses an em dash. These are the rules a machine can state exactly, and the model follows them when told.
|
|
70
|
+
|
|
71
|
+
The judgment result is not established, and this table contradicts the earlier reading of it. Run 1 put judgment tells at 9 against 22 and looked like a 22 percent improvement per 1,000 characters. Run 2 put them at 8 and 11 against 7, which is no effect in either direction. The first reading came from normalising by length across arms that wrote different amounts; the second does not need that correction, and the honest summary is that the catalog's prose sections have no demonstrated effect on the tells this scanner can count.
|
|
72
|
+
|
|
73
|
+
That is not evidence they are useless, because the scanner is blind to what they address. `Name the actor`, `Claims`, `Voice` and `Register` are about attribution, sourcing and register, and none of the 22 patterns looks for any of those. So the 660 tokens they cost, about two thirds of the injected block, are being measured by an instrument that cannot see their subject. Closing that needs either patterns that detect what the sections forbid, or a reader scoring the prose, and neither exists. Until then the number to watch is the mechanical one, which is real.
|
|
74
|
+
|
|
75
|
+
### A reader scoring the prose does not work either
|
|
76
|
+
|
|
77
|
+
The obvious way to measure the prose sections is to ask the model to read the prose, which is the one thing a regex cannot do. So: score both arms of the A/B with a question about the writing rather than the authorship. Not "was this machine-written", which is flat at chance above, but "how well does this paragraph hold a reader's attention and say something specific", on the scale `restsates what the reader already knows` to `earns every sentence it spends`.
|
|
78
|
+
|
|
79
|
+
Ten replies per arm, 18,660 characters. Prevention on averaged 0.493 on the top criterion, prevention off 0.537. **AUC 0.390**, against chance at 0.500. The off arm scored slightly higher, which is the same below-chance result the authorship question gave, from a different angle.
|
|
80
|
+
|
|
81
|
+
That is four attempts on this checkpoint, three question shapes for authorship and a quality read on top, all at or below chance. The boundary is now clear enough to state: this checkpoint does coarse judgements about session state and does not do judgements about prose. The compaction question answers 0.8 or 0.013; the bulk-read relevance answer sits at 0.603 and is not flat. Nothing written about a paragraph has moved off chance.
|
|
82
|
+
|
|
83
|
+
The relevance is not in question even though the measurement is. Every one of the 500 prose samples this project has recorded is prose over the 40-character floor, and 19 percent of them tripped a pattern, so this agent writes prose constantly and the sections describe prose it is producing. What is missing is any way to know whether it is following them.
|
|
84
|
+
|
|
85
|
+
So the honest accounting for the 660 tokens is this. They are not measured to help, and the instrument that could measure them is the one thing that does not work. They now sit in the on-demand guide rather than in front of every request, which is the middle option of the three: the rules still exist and still read the same, they just wait for `timbre_review_guide`. That took the injected block from 986 tokens to 326 and the suite's per-request overhead from about 3,627 to about 2,967.
|
|
86
|
+
|
|
87
|
+
### The 30 patterns that were removed, and why that is not the same gap
|
|
88
|
+
|
|
89
|
+
The scanner had 52 patterns. `37ec6428` cut 30 that had never fired, leaving the 19 measured ones plus three forensic catchers, and the reasoning at the time was that they cost a regex each per reply while measuring nothing.
|
|
90
|
+
|
|
91
|
+
That reasoning is weaker than it looks, because prevention was on for every sample behind it: all 500 prose samples this project had recorded were `prevented: true`, so a pattern for a tell the model had stopped producing could not have fired. Never firing is what a working guard looks like from the inside.
|
|
92
|
+
|
|
93
|
+
Re-testing them settled it anyway, twice. Scoring both arms of the run above with all 30 restored, across 24 replies and about 20,000 characters of technical prose, **not one of them fired in either arm**.
|
|
94
|
+
|
|
95
|
+
The obvious objection is that this workload never writes an essay, so of course the essay patterns are quiet, and this repository does have a `prose` skill for exactly that. So the second test asked for the genre directly: five article-shaped prompts per arm, a blog post arguing for deletion, an opinion piece on estimates, a reflective post on trunk-based development, an article opening against on-call heroics, and a piece on error messages. Ten replies, about 14,000 characters, prevention on and off. **Zero of the 30 fired again.** The only patterns that fired were `em-dash` and `semicolon`, both of which are still in the set.
|
|
96
|
+
|
|
97
|
+
That is two independent corpora and 10,703 recorded samples with the same answer. They target essayistic tells: `rhetorical-hook`, `stock-opener`, `upbeat-closer`, `strawman-objection`, `fractal-summary`, `question-heading`. Ten replies is a thin sample on its own, but asking for the genre and getting silence is a stronger signal than never asking. The removal stands, and the honest reason is narrower than "they measure nothing": they measure a genre that does not appear here even when requested.
|
|
98
|
+
|
|
99
|
+
So the blindness above is real but it is not a broken instrument. The two claims are different. The 660 tokens teach rules for prose this agent does not write, which is a question about the catalog, and it is why they moved to the guide. The scanner's missing patterns would detect those rules in a genre that does not appear here, which is a question about nothing.
|
|
100
|
+
|
|
101
|
+
## Configuration
|
|
102
|
+
|
|
103
|
+
| Variable | Default | Meaning |
|
|
104
|
+
| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
105
|
+
| `TIMBRE` | `1` | Master switch |
|
|
106
|
+
| `TIMBRE_PREVENT` | `1` | Layer 0. `0` measures without injecting |
|
|
107
|
+
| `TIMBRE_WATCH_CHAT` | `1` | Measure assistant replies, silently |
|
|
108
|
+
|
|
109
|
+
Set `PI_CACHE_RETENTION=long` in your environment. Pi's default cache TTL is 5 minutes. Any pause longer than that forces a full prefix rewrite on the next turn.
|
|
110
|
+
|
|
111
|
+
## Memory and adaptation
|
|
112
|
+
|
|
113
|
+
One JSON file per project lives under `$XDG_STATE_HOME/timbre/`, keyed by a hash of the project path. The file holds a rolling window of up to 500 samples plus any live overrides set by `/timbre`. Writes are atomic (temp file, then rename), and two sessions in one repository merge rather than lock: a simultaneous write can still drop one sample, which is cheap next to a lock file that strands the extension.
|
|
114
|
+
|
|
115
|
+
A persisted setting outlives the session that set it. On session start, timbre reads the state file, loads any carried-over overrides, and announces the ones that differ from the env default. A subagent shares no memory with its parent session, only this file, so the state file is the only channel a `/timbre off` toggle has to reach it.
|
|
116
|
+
|
|
117
|
+
The prevention block is computed once per project and frozen for the session. Memory changes on every reply and every file write, so the block would shift mid-session. One changed word in the system prompt invalidates the entire cached prefix. Frozen, timbre adapts across sessions, which is the timescale a writing habit moves on.
|
|
118
|
+
|
|
119
|
+
## Register classification
|
|
120
|
+
|
|
121
|
+
A commit message, an API reference, and a blog post do not share a correct voice. Path classifies the register, with no model call:
|
|
122
|
+
|
|
123
|
+
| Path pattern | Register | Terse is correct | Voice allowed |
|
|
124
|
+
| -------------------------------------------------------------------------------------- | --------- | ---------------- | ------------- |
|
|
125
|
+
| `blog/`, `posts/`, `essays/`, `drafts/` | narrative | no | yes |
|
|
126
|
+
| `CHANGELOG.md`, `HISTORY.md`, `RELEASE_NOTES.md`, `COMMIT_EDITMSG` | changelog | yes | no |
|
|
127
|
+
| `adr/`, `rfc/`, `spec/`, `reference/`, `api/`, `docs/`, `README.md`, `CONTRIBUTING.md` | reference | yes | no |
|
|
128
|
+
| anything else | unknown | no | no |
|
|
129
|
+
|
|
130
|
+
The register determines how the rhythm signal is interpreted. A flat cadence in a changelog is correct. The same cadence in a blog post is a tell.
|
|
131
|
+
|
|
132
|
+
## Rhythm detection
|
|
133
|
+
|
|
134
|
+
Layer 2 adds two signals the regex patterns cannot see:
|
|
135
|
+
|
|
136
|
+
- Monotone cadence: uniform sentence lengths across the whole piece. Fires only when fewer than 30% of non-empty lines are structured (lists, headings) and the piece has at least one blank line, so technical docs stay quiet.
|
|
137
|
+
- Staccato run: four or more short sentences in a row. One short sentence is emphasis. A run is engineering.
|
|
138
|
+
|
|
139
|
+
Both are suppressed for registers where terse and even cadence is correct (reference, changelog, chat).
|
|
140
|
+
|
|
141
|
+
## What it does not do
|
|
142
|
+
|
|
143
|
+
- Does not rewrite text after generation (prevention replaces cleanup).
|
|
144
|
+
- Does not use a corpus or model call for detection (deterministic regex). A local model asked which of two passages reads more machine-generated picked the same one in both orderings, and one asked to label a whole file read human and machine text at chance over 80 passages, so nothing here is blocked or detected on one.
|
|
145
|
+
- Does not recompute the prompt block per turn (frozen once, adapts across sessions).
|
|
146
|
+
|
|
147
|
+
Everything fails open: a broken scan or unwritable state leaves the turn untouched, and `/timbre` names a missing `guidance/` directory rather than reporting "prevention on" over an empty block.
|
|
148
|
+
|
|
149
|
+
The code check needed the same treatment and did not have it. Both its inputs live outside this package: the `oxlint` binary is a dependency, but the rules are read from `~/.agents/tools/oxlint/anti-slop.config.ts` by absolute path, and they are not in `files`. A spawn whose config is missing returns `null`, which is indistinguishable from a clean file, so on any machine without that directory the check reported nothing and said nothing. `/timbre` now names a missing binary or a missing rule config, which is the only reason to notice. A published install still has no rules of its own: it degrades to the prompt block and says so.
|
|
150
|
+
|
|
151
|
+
## Install
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pi install npm:@tduyng/pi-timbre
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Local checkout: `pi install /path/to/dotfiles/pi/agent/extensions/pi-timbre`
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tduyng/pi-timbre",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Constrain the agent prose and code before it is written: inject the rules, block the undeniable tells, measure the reply, and rewrite what is already on disk.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi",
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi-extension",
|
|
9
|
+
"pi-coding-agent",
|
|
10
|
+
"writing",
|
|
11
|
+
"prose",
|
|
12
|
+
"lint",
|
|
13
|
+
"anti-slop"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://gitlab.com/tduyng/pi-agent/-/tree/main/extensions/pi-timbre#readme",
|
|
16
|
+
"bugs": "https://gitlab.com/tduyng/pi-agent/-/issues",
|
|
17
|
+
"license": "Apache-2.0",
|
|
18
|
+
"author": "tduyng",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://gitlab.com/tduyng/pi-agent.git",
|
|
22
|
+
"directory": "extensions/pi-timbre"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"files": [
|
|
26
|
+
"src",
|
|
27
|
+
"!src/**/*.test.ts",
|
|
28
|
+
"README.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"ata-validator": "^1.62.1",
|
|
33
|
+
"oxlint": "1.82.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@earendil-works/pi-ai": "*",
|
|
37
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"pi": {
|
|
43
|
+
"extensions": [
|
|
44
|
+
"./src/index.ts"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|