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.
- package/CHANGELOG.md +23 -1
- package/CONTRIBUTING.md +30 -2
- package/README.md +122 -55
- package/SECURITY.md +1 -1
- package/docs/adoption.md +131 -0
- package/docs/automatic-checkpoint.md +4 -3
- package/docs/benchmarks.md +78 -0
- package/docs/compatibility.md +54 -13
- package/docs/effect-architecture.md +2 -2
- package/docs/fallow-coexistence.md +1 -1
- package/docs/images/jscpd-findings.png +0 -0
- package/docs/m8-validation.md +389 -0
- package/docs/overlay-interaction.md +1 -1
- package/docs/release.md +10 -2
- package/package.json +9 -8
- package/scripts/check-compatibility.mjs +4 -4
- package/skills/jscpd/SKILL.md +1 -1
- package/src/jscpd-report.ts +19 -8
- package/src/presentation.ts +2 -2
package/docs/compatibility.md
CHANGED
|
@@ -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
|
|
26
|
-
| `@earendil-works/pi-coding-agent` | `>=0.84.4 <0.85.0` | `0.
|
|
27
|
-
| `@earendil-works/pi-ai` | `>=0.84.4 <0.85.0` | `0.
|
|
28
|
-
| `@earendil-works/pi-tui` | `>=0.84.4 <0.85.0` | `0.
|
|
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.
|
|
53
|
-
|
|
54
|
-
|
|
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.
|
|
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,
|
|
73
|
-
|
|
74
|
-
|
|
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.
|
|
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;
|
|
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.
|
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.
|
|
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.
|
|
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.
|
|
72
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
73
|
-
"@earendil-works/pi-tui": "0.
|
|
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.
|
|
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
|
|
package/skills/jscpd/SKILL.md
CHANGED
|
@@ -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,
|