pi-jscpd 0.2.1 → 0.2.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.
@@ -1,4 +1,4 @@
1
- # Compatibility policy
1
+ # ✅ Compatibility policy
2
2
 
3
3
  `pi-jscpd` deliberately has a narrow, tested host contract. The package uses
4
4
  Pi's extension, event, tool, custom-message, and TUI APIs directly, so an open
@@ -22,10 +22,10 @@ source and package are MIT licensed; see the [release policy](release.md).
22
22
 
23
23
  | Component | Supported range | Tested fixture | Notes |
24
24
  | --- | --- | --- | --- |
25
- | Node.js | `>=22.19.0 <23 || >=24 <25` | `22.19.0`, `24.12.0` | Node 22.19.0 is Pi 0.84.4's minimum; Node 24 is the second supported LTS line. |
26
- | `@earendil-works/pi-coding-agent` | `>=0.84.4 <0.85.0` | `0.84.4` | The supported 0.84 patch line only. |
27
- | `@earendil-works/pi-ai` | `>=0.84.4 <0.85.0` | `0.84.4` | Kept on the same tested Pi release line. |
28
- | `@earendil-works/pi-tui` | `>=0.84.4 <0.85.0` | `0.84.4` | Required by the interactive overlay. |
25
+ | Node.js | `>=22.19.0 <23 || >=24 <25` | `22.19.0`, `24.12.0` | Node 22.19.0 is Pi's minimum; Node 24 is the second supported LTS line. |
26
+ | `@earendil-works/pi-coding-agent` | `>=0.84.4 <0.85.0` or `>=0.85.1 <0.86.0` | `0.85.1` | Excludes the 0.85.0 SDK import regression. |
27
+ | `@earendil-works/pi-ai` | `>=0.84.4 <0.85.0` or `>=0.85.1 <0.86.0` | `0.85.1` | Kept on the same tested Pi release line. |
28
+ | `@earendil-works/pi-tui` | `>=0.84.4 <0.85.0` or `>=0.85.1 <0.86.0` | `0.85.1` | Required by the interactive overlay. |
29
29
  | `typebox` | `>=1.3.7 <2` | `1.3.7` | Required by the agent-tool schema. |
30
30
  | `effect` | Exact `3.22.1` | `3.22.1` | Reviewed MIT runtime for scoped process/analyzer, bounded-filesystem, lifecycle domain-state, scheduling, automatic delivery, application workflows, and the single managed Pi runtime. |
31
31
  | `jscpd` | Compatible v5 | `5.1.2` | Exact runtime dependency and fallback analyzer. |
@@ -49,9 +49,50 @@ and integrity, and the pinned jscpd runtime before type checking and tests.
49
49
  The supported ranges cover the Node 22 and 24 LTS lines, not the intervening
50
50
  non-LTS Node 23 line. They are a contract, not a claim that every patch
51
51
  combination was run separately. The minimum Node release and the current Node 24 fixture receive
52
- the full project check; Pi 0.84.4 is the API fixture. A future Pi `0.85` release,
53
- Node 25 release, or TypeBox 2 release requires an explicit compatibility review
54
- and range update rather than being accepted automatically.
52
+ the full project check; Pi 0.85.1 is the exact API fixture, while 0.84.4 remains
53
+ the supported lower bound established by the previous certification. A future
54
+ Pi `0.86` release, Node 25 release, or TypeBox 2 release requires an explicit
55
+ compatibility review and range update rather than being accepted automatically.
56
+
57
+ ## Pi 0.85 compatibility review
58
+
59
+ The 0.85.0 and 0.85.1 changelogs, published documentation, and public type
60
+ declarations were compared with 0.84.4 before widening support. No breaking
61
+ change affects the extension APIs used by `pi-jscpd`: command/tool registration,
62
+ `agent_settled`, `session_shutdown`, `ctx.mode`, `ctx.hasUI`, `ui.custom()`, tool
63
+ renderers, `CONFIG_DIR_NAME`, `RpcClient`, and the imported TUI width/key/input
64
+ utilities retain compatible contracts.
65
+
66
+ Relevant host changes are bounded:
67
+
68
+ - TUI components may now implement an optional normalized `handleMouse()` method
69
+ in fullscreen mode. The jscpd overlay remains keyboard-driven; it does not
70
+ implement the new optional method or claim mouse-navigation support.
71
+ - Pi's default editor embeds its working indicator; custom editors may opt in to
72
+ that behavior. `pi-jscpd` provides an overlay rather than replacing the editor,
73
+ so no migration is required.
74
+ - RPC `abort` now waits for the session to become idle, and 0.85.0 fixed aborting
75
+ manual compaction. This strengthens deterministic shutdown for RPC probes
76
+ without changing the commands used by certification.
77
+ - Skill loading can fall back to Bash when Read is unavailable. The packaged
78
+ `jscpd` skill and its explicit `/skill:jscpd` discovery contract are unchanged.
79
+ - Pi 0.85.0 accidentally published unsupported experimental SDK paths; 0.85.1
80
+ removed those paths while preserving the supported local SDK and stdio RPC
81
+ APIs. Certification therefore pins 0.85.1 and peer ranges explicitly exclude
82
+ 0.85.0 across the aligned Pi package set.
83
+
84
+ The 0.85 line also changes fullscreen transcript controls and fixes built-in
85
+ tools to honor `ctx.cwd`; neither changes the extension's public contract. The
86
+ exact 0.85.1 fixture passed type checking, transcript and narrow-width component
87
+ tests, RPC/JSON/print command paths, packaged skill/tool loading, active-scan
88
+ shutdown, and temporary-report cleanup on both supported Node fixtures.
89
+
90
+ A tmux-backed Pi 0.85.1 TUI smoke also opened the real `/jscpd` overlay at 50,
91
+ 80, and 120 columns. Each render stayed within the terminal width, keyboard
92
+ close remained responsive, and a second smoke cancelled an active synthetic
93
+ jscpd process tree from the overlay with no remaining child process or report
94
+ directory. The synthetic analyzer validates host interaction and owned cleanup;
95
+ it is not evidence about production analyzer latency or finding quality.
55
96
 
56
97
  ## Packed-artifact certification
57
98
 
@@ -65,14 +106,14 @@ through npm just as a user installation would; runtime checks remain offline. It
65
106
  - installs that exact tarball with lifecycle scripts disabled in a restrictive
66
107
  disposable location and verifies exact, importable Effect `3.22.1` plus jscpd
67
108
  `5.1.2` dependencies;
68
- - uses the locked Pi `0.84.4` CLI with isolated home, agent, session, and
109
+ - uses the locked Pi `0.85.1` CLI with isolated home, agent, session, and
69
110
  temporary directories and only the explicit installed package enabled;
70
111
  - verifies that Pi discovers exactly one packaged `/skill:jscpd`, then verifies
71
112
  `/jscpd` discovery and provider-free help/status behavior through RPC,
72
- exercises the registered `jscpd_run` contract, Effect-owned analyzer resources,
73
- and installed overlay component, proves the installed artifact resolves and
74
- probes bundled jscpd `5.1.2`, and
75
- checks JSON, print, and non-TUI fallback paths; and
113
+ exercises the registered `jscpd_run` contract, compact and expanded transcript
114
+ renderers, Effect-owned analyzer resources, and installed overlay component at
115
+ 50, 80, and 120 columns, proves the installed artifact resolves and probes
116
+ bundled jscpd `5.1.2`, and checks JSON, print, and non-TUI fallback paths; and
76
117
  - separately places a deterministic fake jscpd v5 executable on the disposable
77
118
  `PATH`, then stops Pi during an active scan and asserts that the process tree
78
119
  and every `pi-jscpd-*` report directory are gone.
@@ -1,4 +1,4 @@
1
- # Effect architecture and conformance
1
+ # 🧬 Effect architecture and conformance
2
2
 
3
3
  Status: **implemented and recertified for the first public release**
4
4
 
@@ -112,7 +112,7 @@ and failed automatic checks stay out of model context.
112
112
  ## Conformance evidence
113
113
 
114
114
  The final non-publishing gate passes on Node 22.19.0 and 24.12.0 with the pinned
115
- Pi 0.84.4, TypeBox 1.3.7, Effect 3.22.1, and jscpd 5.1.2 fixtures. Evidence covers:
115
+ Pi 0.85.1, TypeBox 1.3.7, Effect 3.22.1, and jscpd 5.1.2 fixtures. Evidence covers:
116
116
 
117
117
  - strict TypeScript and Biome checks;
118
118
  - architecture, documentation-link, and repository-hygiene gates;
@@ -1,4 +1,4 @@
1
- # Fallow coexistence policy
1
+ # 🤝 Fallow coexistence policy
2
2
 
3
3
  Status: implemented with Effect-owned policy evaluation
4
4
 
Binary file
@@ -0,0 +1,389 @@
1
+ # 📋 M8 real-project validation
2
+
3
+ Status: **complete for the local milestone decision** — [issue #99](https://github.com/revazi/pi-jscpd/issues/99).
4
+ These observations do not activate feature candidates #103–#106. Residual limits
5
+ are recorded below and do not change the decision.
6
+
7
+ ## Scope and privacy
8
+
9
+ Measurements used the Pi 0.85.1 / Node 24.12.0 / jscpd 5.1.2 development
10
+ fixtures on macOS arm64. Project B is a user-approved working tree with an active
11
+ refactor; its private paths, source, branch name, and raw reports are not retained
12
+ here. Projects A and B fall in the 100–999 analyzed-source bucket. Project B is in
13
+ the 10,000–49,999 analyzed-line bucket. Public project C fills the 1,000–9,999
14
+ source and 100,000–499,999 line buckets. Project B includes JavaScript, Python,
15
+ and Bash according to the analyzer.
16
+
17
+ No original source or configuration was changed. Pi runs used isolated home,
18
+ agent, and temporary directories, offline mode, no session persistence, no
19
+ project trust approval, and only explicitly loaded measurement resources. No
20
+ remote provider was contacted. Later transcript and mutation checks used a local
21
+ scripted stream, explicitly distinguished from model-selected behavior below.
22
+ Analyzer reports existed only in owned temporary workspaces; no raw reports or
23
+ terminal captures are retained.
24
+
25
+ ## Observed results
26
+
27
+ ### Preliminary CLI measurements
28
+
29
+ Three sequential processes per scope; approximate wall times include process
30
+ startup and bounded Python wait overhead, but exclude report decoding/cleanup.
31
+ Normal ignore rules were retained. Project A is the public pi-jscpd checkout at
32
+ `801f27c`, using its `.jscpd.json`. Project B had no jscpd configuration or local
33
+ analyzer; it used the pinned analyzer from the extension checkout and defaults.
34
+
35
+ | Project / scope | First / later runs (ms) | Clone pairs per run |
36
+ | --- | --- | --- |
37
+ | A, full project | 184 / 129 / 130 | 48 |
38
+ | A, source directory | 73 / 74 / 73 | 1 |
39
+ | B, full project | 179 / 175 / 176 | 58 |
40
+ | B, Python implementation | 70 / 74 / 72 | 15 |
41
+ | B, JavaScript extensions | 75 / 76 / 72 | 0 |
42
+
43
+ Counts were stable within each scope. All temporary report directories were
44
+ removed. A scoped scan omits matches outside its target and is not equivalent
45
+ to changed-only full-project comparison.
46
+
47
+ ### Real Pi runtime, project B
48
+
49
+ A temporary instrumentation extension composed the production capability,
50
+ analyzer, and baseline services through `registerJscpdExtension`'s service seam.
51
+ An Effect tap measured baseline start to completion, without replacing analyzer
52
+ results or bypassing normalization. A local RPC command called the registered
53
+ `jscpd_run.execute` callback; this is a real host/service measurement, not a
54
+ model-selected tool call or an automatic checkpoint.
55
+
56
+ | Operation | Observations |
57
+ | --- | --- |
58
+ | Baseline | Accepted in 219, 214, 226 ms across three fresh Pi processes |
59
+ | Full scan, session 1 | 161, 163, 174 ms |
60
+ | Full scan, session 2 | 149, 157, 170 ms |
61
+ | Full scan, session 3 | 155, 172, 170 ms |
62
+ | Full-scan findings | 58 analyzer pairs, 10 surfaced, 48 omitted in every run |
63
+ | JavaScript scoped scan | Clean in 51, 46, 49 ms |
64
+ | Changed without tracked mutations | Clean, zero surfaced/omitted, approximately 1 ms |
65
+ | Cancellation requested at 50 ms | Returned `scan-cancelled` at 56, 57, 58 ms from invocation |
66
+ | Coexistence status | Ambiguous evidence; automatic checks allowed |
67
+ | Diagnostics | Zero extension errors, provider turns, or stderr output |
68
+ | Cleanup | Zero report directories remaining after each Pi shutdown |
69
+
70
+ Baseline timings exclude Pi startup before baseline invocation. Scan callback
71
+ wall times include application normalization and finalization. The cancellation
72
+ timer was scheduled for 50 ms; actual timer delivery was not separately recorded,
73
+ so 6–8 ms is **not** a precise cancellation-latency measurement. Direct process-tree
74
+ enumeration was not collected for these real-analyzer runs. No-mutation changed
75
+ results do not prove clean automatic-checkpoint silence.
76
+
77
+ ### Real TUI findings smoke, project B
78
+
79
+ The uninstrumented source extension was loaded into the real Pi CLI in regular
80
+ TUI mode in isolated tmux sessions at 50, 80, and 120 columns (32 rows). At each
81
+ width, an explicit project scan displayed the 58-result findings view. Down/Enter,
82
+ search, a Python filter, clear-filter/PageDown, and close inputs changed the
83
+ rendered frame; no report directories remained after shutdown.
84
+
85
+ This is a keyboard-response smoke, not human usability acceptance. Captured
86
+ frames were no wider than the terminal, but tmux capture clipping alone cannot
87
+ prove component width correctness; component bounds are separately checked by
88
+ [package certification](compatibility.md). An initial probe expected the word
89
+ “duplicate” at every width; the wider layout instead displayed “findings”. The
90
+ probe predicate was corrected. That was a measurement-script error, not an
91
+ extension defect. Follow-up live transcript and handoff checks are below.
92
+
93
+ ### Follow-up cancellation, failures, and cleanup: project B
94
+
95
+ Three further real scans received an AbortSignal scheduled for 80 ms after tool
96
+ invocation. Monotonic timestamps immediately before `abort()` and immediately
97
+ after the tool callback settled produced:
98
+
99
+ | Run | Actual abort delivery from invocation | Abort to settlement |
100
+ | --- | --- | --- |
101
+ | 1 | 84 ms | 6.51 ms |
102
+ | 2 | 83 ms | 7.85 ms |
103
+ | 3 | 81 ms | 3.33 ms |
104
+
105
+ Every result was `scan-cancelled`; each cancellation left zero report directories.
106
+ An external observer sampled only PID, PPID, process group, and executable-name
107
+ metadata. Across the full cancellation/failure/recovery sequence it observed 11
108
+ child PIDs in seven groups. None of those children or group members remained at
109
+ the end of the sequence, either before or after Pi shutdown. IDs and raw process
110
+ listings were discarded. Sampling does not prove the absence of an arbitrarily
111
+ short-lived unobserved descendant or establish each group's exact exit time.
112
+
113
+ An initial probe ran synchronous process enumeration inside Pi's event loop and
114
+ substantially distorted timer delivery and settlement. Those latency samples
115
+ were rejected; the accepted measurements moved observation outside Pi. They
116
+ still include normal scheduler and system-load variation, not a latency SLA.
117
+
118
+ A controlled config-service seam set the production scan timeout to 100 ms only
119
+ after baseline acceptance. The real analyzer returned `scan-timed-out` at 106 ms.
120
+ A nonexistent target returned `unsupported-path`. A subsequent JavaScript scope
121
+ scan completed cleanly. These are real-host fail-open observations with a
122
+ controlled timeout, not project-config trust acceptance. There were no extension
123
+ errors, remote provider calls, stderr output, or remaining report directories.
124
+
125
+ ### Live transcripts and editor handoff: project B
126
+
127
+ A local scripted provider emitted one predetermined `jscpd_run` scan call and a
128
+ completion message. Pi executed the actual tool through its normal event pipeline
129
+ and displayed its actual result. The script performed no network I/O, read no
130
+ credentials, and made no decisions about the findings. This validates live host
131
+ rendering and dispatch, **not** LLM judgment or a production provider transport.
132
+ Only `jscpd_run` was available for the scripted action; built-in tools were off.
133
+
134
+ Regular and experimental fullscreen modes were each exercised at 50, 80, and
135
+ 120 columns, with 36 rows. An instrumented result component counted its real
136
+ rendered lines without changing text or retaining output:
137
+
138
+ | Terminal width | Component width | Compact lines | Expanded lines (both modes) |
139
+ | --- | --- | --- | --- |
140
+ | 50 | 48 | 1 | 81 |
141
+ | 80 | 78 | 1 | 54 |
142
+ | 120 | 118 | 1 | 50 |
143
+
144
+ Ctrl+O expanded and collapsed the live transcript at every size. All six real
145
+ scans returned findings: 10 surfaced, 48 omitted, no tool error. In each session,
146
+ `/jscpd` project scan followed by `s` displayed a one-finding selection marker;
147
+ `e` filled the editor with the duplicate-block review prompt. The prompt was
148
+ cleared rather than submitted. Local stream-call counts remained unchanged
149
+ across the handoff, verifying that it did not start another turn. Each session
150
+ shut down normally and left zero report directories.
151
+
152
+ This is stronger than frame-change-only smoke evidence, but is still automated
153
+ keyboard acceptance rather than a human navigation/usefulness assessment.
154
+
155
+ ### Controlled mutation and positive coexistence: public synthetic fixture
156
+
157
+ Two fresh temporary projects contained a generated Python implementation, not a
158
+ copy of a private repository. The same local scripted stream used Pi's real
159
+ built-in `write` tool to add a short unique note, then an identical Python copy.
160
+ No production lifecycle event or analyzer report was fabricated.
161
+
162
+ | Observation | Normal defaults | Controlled positive Fallow signal |
163
+ | --- | --- | --- |
164
+ | Unique-note checkpoint | Last check clean; zero automatic messages | Not attempted; zero automatic messages |
165
+ | Duplicate-copy checkpoint | One finding; one automatic message | Not attempted; zero automatic messages |
166
+ | Subsequent explicit changed scan | Clean: already acknowledged automatically | One finding, zero omitted |
167
+ | Coexistence | No positive signal supplied | Detected; automatic checks disabled |
168
+
169
+ For the positive case, the fixture had `.fallowrc.json` with
170
+ `duplicates.enabled: true`. Only the coexistence service's input trust flag was
171
+ supplied through its existing injection seam so the real parser could inspect
172
+ this generated file; Pi itself still ran with `--no-approve`. **This is controlled
173
+ real-host workflow evidence, not acceptance of an actual trusted project's
174
+ configuration or a running second analyzer.** No Fallow process was launched.
175
+ Explicit jscpd analysis remained available while automatic warnings were
176
+ suppressed. Both sessions had zero extension errors or stderr output; all owned
177
+ reports and generated sources were removed.
178
+
179
+ The fixture establishes clean silence, new-finding delivery, acknowledgement,
180
+ and positive-signal suppression through real tool/lifecycle dispatch. It does
181
+ not satisfy the representative-real-repository or finding-usefulness matrix.
182
+
183
+ ### Current-checkout usefulness triage: project B
184
+
185
+ The approved working tree continued evolving between validation sessions. A fresh
186
+ read-only review found **60 pairs across 141 sources**, rather than the earlier
187
+ 58/134. Analyzed lines ranged from 43,766 to 43,776 between invocations; duplicated
188
+ lines remained 591. This is a later working-tree observation, **not** a measured
189
+ session delta or a regression. Before/after in-memory content fingerprints matched
190
+ within each review invocation and across the subsequent three-session host run.
191
+ No paths, identifiers, source fragments, AST dumps, or fingerprints were retained.
192
+
193
+ | Pair location | Count |
194
+ | --- | --- |
195
+ | Python implementation on both sides | 16 |
196
+ | Implementation matched to a test | 1 |
197
+ | Tests on both sides | 38 |
198
+ | Documentation and other scripts/resources | 5 |
199
+ | **Total** | **60** |
200
+
201
+ Pair formats were Python (46), JavaScript (10), Markdown (3), and Bash (1).
202
+ A bounded structural triage covered all 60 locations and examined Python
203
+ statement/function context in memory. It produced the following **provisional
204
+ review priorities**, not a semantic audit or maintainer-approved refactor list:
205
+
206
+ | Assessment | Pairs | Evidence and action |
207
+ | --- | --- | --- |
208
+ | Production inspection candidates | 4 | Identical nonempty complete-statement lists inside distinct implementation functions; inspect before considering extraction |
209
+ | Likely expected test repetition; lower priority | 26 | Both enclosing Python functions are tests containing assertions; preserve independent test readability by default |
210
+ | Uncertain | 30 | Remaining implementation, test helpers/JS, cross-boundary, documentation, and script matches lack enough semantic evidence for a recommendation |
211
+
212
+ The four production candidates span 8, 7, 7, and 11 lines: three same-file pairs
213
+ and one cross-file pair. Their repeated operations involve list construction
214
+ and iteration, path resolution/validation, and conditional input guards. These
215
+ are useful leads, not four independent ready-to-apply refactors: surrounding
216
+ context managers, policy differences, free variables, and error behavior still
217
+ matter. The enclosing function bodies are not identical.
218
+
219
+ Of the 26 lower-priority Python test pairs, 25 join distinct test functions; one
220
+ repeats within a test. This supports leaving those patterns alone during the
221
+ current refactor, **not** claiming the owner intended every duplicate or that
222
+ tests should be ignored globally. No extraction is confirmed safe, and no
223
+ maintainer-intent count is claimed. In particular, 38 test/test pairs are not
224
+ 38 proven false positives.
225
+
226
+ Other implementation matches include fragments of string constants, dictionary
227
+ construction, function boundaries, and repeated branches. Token duplication alone
228
+ is insufficient to recommend merging those contracts. AST inspection was only a
229
+ local review aid; it did not replace jscpd detection, filter results, change
230
+ ranking, or introduce a Python dependency into the extension.
231
+
232
+ ### Current real-host scope and installed-Fallow checks: project B
233
+
234
+ Three fresh isolated Pi 0.85.1 processes explicitly loaded both the source jscpd
235
+ extension and installed pi-fallow 0.5.1. Both tools registered successfully.
236
+ No Fallow analyzer process, mutation tool, or provider was invoked; all results
237
+ below came from the real registered jscpd tool and production services.
238
+
239
+ | Operation | Runs (ms) | Results in every run |
240
+ | --- | --- | --- |
241
+ | Baseline | 255, 266, 239 | Accepted |
242
+ | Full project | 177, 163, 161 | 60 pairs; 10 surfaced, 50 omitted |
243
+ | Python implementation | 65, 61, 59 | 16 pairs; 10 surfaced, 6 omitted |
244
+ | JavaScript extensions | 57, 53, 50 | Clean |
245
+ | Two Bash files with an existing match | 58, 57, 53 | One pair; one surfaced, zero omitted |
246
+ | No-mutation changed | 1, 1, 1 | Clean; zero surfaced/omitted |
247
+
248
+ This adds explicit real-host Python and Bash scope coverage, not merely detected
249
+ format names. The Bash scope was selected from the real full-project report,
250
+ without changing ignore rules or thresholds; it is intentionally not a random
251
+ sample or a whole-repository Bash census.
252
+
253
+ The checkout contains JSONC Fallow policy. With Pi project trust left unapproved,
254
+ coexistence remained **ambiguous / automatic allowed**, even with the actual
255
+ Fallow tool registered. This is successful two-extension loading and conservative
256
+ ambiguous-policy behavior, not positive configured-duplication acceptance. The
257
+ separate positive-signal fixture above remains the evidence for suppression.
258
+ All three sessions had zero extension errors, stderr output, provider turns, or
259
+ remaining report directories. The original working-tree contents were unchanged.
260
+
261
+ ### Large public repository: project C (`vitejs/vite`)
262
+
263
+ Public TypeScript monorepo `vitejs/vite` at `bd3a3a9`, cloned read-only into an
264
+ owned temporary directory. No project-local jscpd policy. Same host, Node 24.12.0,
265
+ Pi 0.85.1 fixture, and pinned jscpd 5.1.2 as above. Analyzer-reported size: 1,107
266
+ sources and 172,464 lines (1,000–9,999 source bucket; 100,000–499,999 line bucket).
267
+ Counts were stable across three CLI samples per scope. Every report directory was
268
+ removed.
269
+
270
+ | Scope | First / later runs (ms) | Clone pairs per run |
271
+ | --- | --- | --- |
272
+ | Project | 362 / 210 / 249 | 524 |
273
+ | `packages` | 117 / 114 / 111 | 247 |
274
+ | `docs` | 60 / 59 / 61 | 66 |
275
+
276
+ Three fresh isolated Pi 0.85.1 RPC sessions loaded only the source extension,
277
+ offline, with discovery and built-in tools disabled. Explicit `/jscpd scan`
278
+ returned the same 524-pair summary in 447, 382, and 391 ms. Each notify was 47
279
+ lines and 2,809 characters. A `/jscpd scan packages` run returned 247 pairs in
280
+ 259 ms. No extension stderr and no leftover report directories.
281
+
282
+ This is real-host slash-command evidence on a public 1,000+ source tree, not a
283
+ TUI overlay run against 524 findings and not a session-delta usefulness review.
284
+ Sub-second full scans do not activate persistent or incremental transport.
285
+
286
+ ## Reproduction procedure
287
+
288
+ 1. Use an explicitly approved target; reuse its existing authorization while
289
+ scope remains unchanged. Record only a repository alias,
290
+ size bucket, format mix, versions, and whether the working tree is dirty.
291
+ Do not alter its existing detection policy.
292
+ 2. Run the pinned local analyzer with the target as cwd and argument array
293
+ `[scope, "--reporters", "json", "--output", temporaryDirectory,
294
+ "--silent", "--no-colors", "--no-tips"]`. For project A explicitly select
295
+ `.jscpd.json`. Own the process group, impose a 30-second deadline, discard
296
+ stdout/stderr, decode a bounded report, keep numeric statistics only, and
297
+ remove the temporary directory in a finalizer. Repeat each scope three times.
298
+ 3. For host timings, load a local instrumentation extension into the exact Pi
299
+ fixture. Supply production capability/analyzer/baseline services, wrap
300
+ baseline `startEffect` with a monotonic timer and Effect tap, and call the
301
+ captured registered tool from an explicit local command after acceptance.
302
+ Measure status, no-mutation changed, three full scans, a JavaScript scoped
303
+ scan, then a full scan with a signal aborted by a 50-ms timer. Preserve all
304
+ normal production result handling. Record only status, timing, counts, and
305
+ coexistence enums. Use three fresh Pi processes and bound each at 60 seconds.
306
+ 4. Launch the uninstrumented CLI with `--offline --no-session --no-approve`,
307
+ disable extension/skill/prompt/theme/context discovery, and load only the
308
+ local jscpd extension with `-e`. Set isolated home/agent/temp directories.
309
+ In regular TUI mode at each width, open `/jscpd`, wait for readiness, press
310
+ `s`, then exercise Down/Enter, `/`, a Python filter, Enter, `x`, PageDown,
311
+ and `q`. Quit normally; inspect owned reports and child processes before
312
+ removing the isolated workspace. Keep aggregate observations, not captures.
313
+ 5. For precise cancellation, timestamp actual `abort()` delivery and callback
314
+ settlement in Pi. Enumerate its descendants and process groups from an
315
+ external process, not synchronous callbacks in the measured event loop.
316
+ Retain counts only; check the observed IDs/groups before and after shutdown.
317
+ For a controlled timeout, wrap the production config service's `current()`
318
+ result to set `timeoutMs: 100` after baseline acceptance, without changing
319
+ target policy. Restore it before testing absent-target failure and recovery.
320
+ 6. For live transcripts, register a no-I/O local scripted provider using Pi's
321
+ `streamSimple` API. It emits one predetermined `jscpd_run` tool call followed
322
+ by a stop message. Do not replace tool execution. Count the original result
323
+ component's rendered lines for compact/expanded states, press Ctrl+O twice,
324
+ and repeat in regular/fullscreen modes at each width. Select one overlay
325
+ finding and load it into the editor; clear it without submission and confirm
326
+ stream-call counts did not change. Disable all built-in tools for real targets.
327
+ 7. For synthetic mutation checks, create a temporary project containing one
328
+ generated Python function exceeding jscpd's normal minimum thresholds. Permit
329
+ only built-in `write` and `jscpd_run` in the local scripted session. Write a
330
+ short unique note, await the automatic clean result, then write an identical
331
+ Python file and await the automatic finding. Check acknowledgement with an
332
+ explicit changed scan. Repeat in a fresh fixture with the controlled positive
333
+ Fallow signal described above; expect no automatic attempts but an explicit
334
+ changed finding. Record aggregate states only, then remove the fixture.
335
+ 8. Never enable a remote provider or automatically submit a finding handoff.
336
+ Do not infer project trust for a child from the parent session. Mutation
337
+ scenarios belong only in disposable, explicitly scoped fixtures or copies.
338
+ Launch drivers with an allowlisted environment, isolated home/agent/temp
339
+ directories, no discovery, offline mode, and bounded run deadlines.
340
+
341
+ 9. For privacy-preserving triage, consume the bounded real report in memory.
342
+ Count pair formats and coarse source/test/documentation areas. For Python,
343
+ parse the existing source without importing or executing it; find the smallest
344
+ enclosing function covering each occurrence, count complete statements within
345
+ the reported line span, and compare their `ast.dump` values in memory. Treat
346
+ nonempty identical lists in distinct implementation functions as inspection
347
+ candidates, not extraction approvals. Check test-function names and assertions
348
+ before assigning lower priority; leave unsupported semantic judgments uncertain.
349
+ Emit only aggregate counts and generic structural descriptions. Discard every
350
+ raw report and fingerprint, and never turn this local aid into a detection rule.
351
+ 10. To reproduce installed-extension coexistence, explicitly load a reviewed local
352
+ pi-fallow entrypoint alongside jscpd into the isolated RPC host, with all
353
+ discovery and built-in tools disabled. Check both registrations and jscpd
354
+ status; do not run Fallow merely to establish tool presence. Repeat full,
355
+ implementation, clean JavaScript, and report-selected Bash scopes in three
356
+ fresh processes. Keep trust unchanged and distinguish ambiguous from positive
357
+ policy evidence.
358
+ 11. For the public large-repository sample, clone `vitejs/vite` at `bd3a3a9` into
359
+ an owned temporary directory. Use the pinned analyzer without `--config`.
360
+ Repeat project, `packages`, and `docs` CLI scopes three times with a 120-second
361
+ bound. Then load the source extension into three isolated Pi 0.85.1 RPC hosts
362
+ and time `/jscpd scan` until the notify summary arrives. Record only timings,
363
+ pair counts, notify line/character bounds, and cleanup. Delete the clone.
364
+ Do not retain template paths from the notify body.
365
+
366
+ ## Remaining acceptance and decision
367
+
368
+ - Small and large source-count buckets now have real analyzer evidence: projects A
369
+ and B (100–999 sources) plus public project C (1,107 sources / 172,464 lines).
370
+ Full scans stayed well under one second. This does not claim a 10,000-source
371
+ result and does not activate persistent or incremental transport.
372
+ - Cancellation, observed process-group cleanup, controlled timeout/failure, live
373
+ compact/expanded transcripts, fullscreen UI, and selection/handoff have real-host
374
+ observations against project B. Project C adds RPC slash-command scan timings,
375
+ not a 524-finding TUI overlay rerun.
376
+ - Positive coexistence and mutation/clean-checkpoint behavior have controlled
377
+ real-host observations. Representative trusted-project Fallow duplication policy
378
+ was not accepted on project B; the synthetic positive-signal fixture remains the
379
+ suppression evidence. That gap does not activate team-policy work.
380
+ - Usefulness has a bounded triage on project B: four production inspection
381
+ candidates, 26 likely expected test repetitions, and 30 uncertain pairs in the
382
+ 60-pair snapshot. Safe extraction and maintainer intent remain unproven.
383
+
384
+ **Decision: no demonstrated product problem should drive a new feature milestone.**
385
+ Keep advisory, quiet defaults and current thresholds. Do not start a performance,
386
+ navigation, team-policy, or noise feature on this evidence. Candidates #103–#106
387
+ stay deferred until new evidence appears.
388
+
389
+ No extension defect was established, so no speculative defect issue was filed.
@@ -1,4 +1,4 @@
1
- # `/jscpd` overlay interaction contract
1
+ # 🖥️ `/jscpd` overlay interaction contract
2
2
 
3
3
  Status: **implemented with managed-runtime action execution**
4
4
 
package/docs/release.md CHANGED
@@ -1,4 +1,4 @@
1
- # Release and publication policy
1
+ # 🏷️ Release and publication policy
2
2
 
3
3
  `pi-jscpd` releases are explicitly authorized by
4
4
  [Revaz Zakalashvili](https://github.com/revazi) and published from reviewed tags
@@ -23,6 +23,13 @@ compatibility, strict types, Biome, the network-free test suite, the exact packe
23
23
  and installed artifact, and the package dry run. CI repeats these checks on Node
24
24
  22.19.0 and 24.12.0.
25
25
 
26
+ The reviewed version literal lives in `scripts/approved-release-version.mjs`.
27
+ Repository hygiene, package certification, the manual readiness workflow, and
28
+ tagged publication compare `package.json`, `package-lock.json`, and the
29
+ changelog heading against that value and fail closed on mismatch. Tests and
30
+ workflows must not embed a second copy. Updating the file does not authorize a
31
+ tag or npm publish.
32
+
26
33
  The manual **Release readiness (no publish)** workflow validates an exact
27
34
  40-character commit that must resolve to `origin/main`. It has read-only
28
35
  repository permission, receives no registry credential, retains no package
@@ -62,7 +69,8 @@ Published versions follow Semantic Versioning. For each approved release:
62
69
 
63
70
  1. Move relevant entries from `Unreleased` to
64
71
  `## [MAJOR.MINOR.PATCH] - YYYY-MM-DD` and update comparison links.
65
- 2. Update `package.json` and `package-lock.json` together.
72
+ 2. Set `scripts/approved-release-version.mjs` to the approved version and update
73
+ `package.json` and `package-lock.json` to the same value.
66
74
  3. Confirm the package is publishable and `publishConfig` still requests public
67
75
  access and provenance.
68
76
  4. Merge the reviewed release commit to `main` and wait for both supported-Node
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-jscpd",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "A Pi-native, polyglot duplication guardrail powered by jscpd.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -61,20 +61,21 @@
61
61
  ]
62
62
  },
63
63
  "peerDependencies": {
64
- "@earendil-works/pi-ai": ">=0.84.4 <0.85.0",
65
- "@earendil-works/pi-coding-agent": ">=0.84.4 <0.85.0",
66
- "@earendil-works/pi-tui": ">=0.84.4 <0.85.0",
64
+ "@earendil-works/pi-ai": ">=0.84.4 <0.85.0 || >=0.85.1 <0.86.0",
65
+ "@earendil-works/pi-coding-agent": ">=0.84.4 <0.85.0 || >=0.85.1 <0.86.0",
66
+ "@earendil-works/pi-tui": ">=0.84.4 <0.85.0 || >=0.85.1 <0.86.0",
67
67
  "typebox": ">=1.3.7 <2"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@biomejs/biome": "2.5.11",
71
- "@earendil-works/pi-ai": "0.84.4",
72
- "@earendil-works/pi-coding-agent": "0.84.4",
73
- "@earendil-works/pi-tui": "0.84.4",
71
+ "@earendil-works/pi-ai": "0.85.1",
72
+ "@earendil-works/pi-coding-agent": "0.85.1",
73
+ "@earendil-works/pi-tui": "0.85.1",
74
74
  "@types/node": "22.20.1",
75
75
  "typebox": "1.3.7",
76
76
  "typescript": "5.9.3",
77
- "vitest": "4.1.11"
77
+ "vitest": "4.1.11",
78
+ "yaml": "2.9.0"
78
79
  },
79
80
  "engines": {
80
81
  "node": ">=22.19.0 <23 || >=24 <25"
@@ -5,15 +5,15 @@ import { fileURLToPath } from "node:url";
5
5
  const projectRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
6
6
  const manifest = await readJson(join(projectRoot, "package.json"));
7
7
  const expectedNodeRange = ">=22.19.0 <23 || >=24 <25";
8
- const expectedPiVersion = "0.84.4";
8
+ const expectedPiVersion = "0.85.1";
9
9
  const expectedEffectVersion = "3.22.1";
10
10
  const expectedEffectIntegrity =
11
11
  "sha512-TNoXushmPOBAjJlthF5d2QwnX2xBPEtcNJr5XKNKbRLbDvBcOYkXlYDfvGfSA0zriwLFuCll5MDtNMAdZL17PQ==";
12
12
  const expectedJscpdVersion = "5.1.2";
13
13
  const expectedPeerRanges = Object.freeze({
14
- "@earendil-works/pi-ai": ">=0.84.4 <0.85.0",
15
- "@earendil-works/pi-coding-agent": ">=0.84.4 <0.85.0",
16
- "@earendil-works/pi-tui": ">=0.84.4 <0.85.0",
14
+ "@earendil-works/pi-ai": ">=0.84.4 <0.85.0 || >=0.85.1 <0.86.0",
15
+ "@earendil-works/pi-coding-agent": ">=0.84.4 <0.85.0 || >=0.85.1 <0.86.0",
16
+ "@earendil-works/pi-tui": ">=0.84.4 <0.85.0 || >=0.85.1 <0.86.0",
17
17
  typebox: ">=1.3.7 <2",
18
18
  });
19
19
 
@@ -8,7 +8,7 @@ metadata:
8
8
  version: 1.0.0
9
9
  ---
10
10
 
11
- # jscpd duplication guardrail
11
+ # 🛡️ jscpd duplication guardrail
12
12
 
13
13
  Use `pi-jscpd` for deterministic duplicate-code analysis across jscpd-supported
14
14
  languages and embedded formats. jscpd remains authoritative for tokenization,