pi-jscpd 0.1.0

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/CONTRIBUTING.md +144 -0
  3. package/LICENSE +21 -0
  4. package/README.md +231 -0
  5. package/SECURITY.md +93 -0
  6. package/docs/automatic-checkpoint.md +235 -0
  7. package/docs/compatibility.md +119 -0
  8. package/docs/effect-architecture.md +128 -0
  9. package/docs/fallow-coexistence.md +120 -0
  10. package/docs/overlay-interaction.md +347 -0
  11. package/docs/release.md +115 -0
  12. package/package.json +86 -0
  13. package/scripts/check-compatibility.mjs +103 -0
  14. package/skills/jscpd/SKILL.md +90 -0
  15. package/src/acknowledgements.ts +268 -0
  16. package/src/automatic.ts +396 -0
  17. package/src/baseline.ts +400 -0
  18. package/src/capability.ts +569 -0
  19. package/src/changed-files.ts +372 -0
  20. package/src/changed.ts +548 -0
  21. package/src/clone-identity.ts +373 -0
  22. package/src/config.ts +414 -0
  23. package/src/contract.ts +39 -0
  24. package/src/dispatch.ts +90 -0
  25. package/src/effect/clock.ts +10 -0
  26. package/src/effect/errors.ts +311 -0
  27. package/src/effect/filesystem.ts +240 -0
  28. package/src/effect/runtime-boundary.ts +25 -0
  29. package/src/effect/runtime-contract.ts +18 -0
  30. package/src/effect/services.ts +131 -0
  31. package/src/extension.ts +708 -0
  32. package/src/fallow.ts +479 -0
  33. package/src/finding-presentation.ts +73 -0
  34. package/src/index.ts +8 -0
  35. package/src/jscpd-report.ts +819 -0
  36. package/src/jscpd.ts +748 -0
  37. package/src/overlay.ts +1166 -0
  38. package/src/parser.ts +189 -0
  39. package/src/path-utils.ts +44 -0
  40. package/src/presentation.ts +232 -0
  41. package/src/process.ts +425 -0
  42. package/src/registry.ts +102 -0
  43. package/src/scan.ts +441 -0
  44. package/src/scheduler.ts +434 -0
  45. package/src/session-state.ts +229 -0
  46. package/src/status.ts +534 -0
  47. package/src/types.ts +334 -0
  48. package/src/value-utils.ts +14 -0
  49. package/src/verification.ts +220 -0
@@ -0,0 +1,235 @@
1
+ # Automatic advisory checkpoint decision
2
+
3
+ Status: **implemented with Effect-owned scheduling and automatic delivery**
4
+
5
+ Scope: lifecycle and process model
6
+
7
+ The completed Effect source migration preserves this trigger, generation,
8
+ quiet-delivery, cancellation, and shutdown contract. Scheduling/background work
9
+ uses scoped fibers and Effect transactions, application workflows compose native
10
+ programs, and Pi host adapters use one managed runtime.
11
+
12
+ ## Decision
13
+
14
+ Use Pi's `agent_settled` event as the only automatic-checkpoint eligibility
15
+ signal. The handler must enqueue at most one generation-bound background check
16
+ and return immediately; it must not await a jscpd process.
17
+
18
+ Successful built-in `write` and `edit` results continue to mark the session
19
+ changed and increment a mutation generation. They do not start scans. At
20
+ `agent_settled`, the scheduler may start one check only when all of these remain
21
+ true:
22
+
23
+ - the session is enabled and trusted configuration has been restored;
24
+ - at least one attributable mutation is newer than the last terminal automatic
25
+ attempt;
26
+ - the pre-session baseline is accepted;
27
+ - no automatic check already owns that mutation generation; and
28
+ - the extension runtime is still active and Pi is idle.
29
+
30
+ This makes the checkpoint **settled-triggered, dirty-coalesced, asynchronous,
31
+ and latest-generation-only**. Multiple writes in one agent run produce one
32
+ candidate check, not one process per write or model turn.
33
+
34
+ ## Evidence
35
+
36
+ The decision was evaluated against Pi 0.84.4's installed extension
37
+ documentation, public type definitions, and `AgentSession` runtime.
38
+
39
+ Pi documents and types distinguish the candidate events as follows:
40
+
41
+ - `tool_result` occurs after each tool execution and can interleave for parallel
42
+ tools.
43
+ - `turn_end` occurs after one assistant response and its tool results; another
44
+ model/tool turn may start immediately.
45
+ - `agent_end` ends one low-level agent run, but retry, automatic compaction, or a
46
+ queued continuation may still follow.
47
+ - `agent_settled` occurs only after retry, compaction retry, and queued steering
48
+ or follow-up work are exhausted.
49
+ - `session_shutdown` is the teardown boundary for quit, reload, new, resume, and
50
+ fork flows.
51
+
52
+ The installed `AgentSession` runtime drains post-run retries, compaction, and
53
+ queued messages before emitting `agent_settled`. Extension handlers are awaited,
54
+ so doing the scan directly in the handler would extend prompt settlement by as
55
+ much as the configured scan timeout. The handler therefore only schedules
56
+ owned background work and returns.
57
+
58
+ ### Trigger cardinality
59
+
60
+ For one logical user request with `W` successful attributable writes, `T` model
61
+ turns, and `R` low-level runs caused by retry/compaction/queued continuations:
62
+
63
+ | Candidate | Trigger opportunities | Settled tree? | Result |
64
+ | --- | ---: | --- | --- |
65
+ | `tool_result` | `W` | No | Reject: scan stampede and parallel-tool races |
66
+ | `message_end` | user, assistant, and tool-result messages | No | Reject: noisy and not mutation-specific |
67
+ | `turn_end` | `T` | No | Reject: can delay or race the next model turn |
68
+ | `agent_end` | `R` | No | Reject: follow-ups and recovery may still mutate files |
69
+ | `agent_settled` | 1 after the logical run settles | Yes | **Select as eligibility signal** |
70
+ | next `before_agent_start` | 1 per later prompt | Previous run only | Reject as primary: delays the next user request |
71
+ | idle/debounce timer | timing-dependent | Not guaranteed | Reject: duplicates Pi's settled lifecycle and adds timer races |
72
+ | `session_shutdown` | 1 per teardown | Yes | Reject as scan trigger: too late to advise and conflicts with cleanup |
73
+
74
+ `agent_settled` gives the lowest safe trigger cardinality. A dirty generation is
75
+ still required because settled can occur after read-only work and because
76
+ automatic work may be cancelled and retried later.
77
+
78
+ ## Runtime process model
79
+
80
+ ### While Pi is active
81
+
82
+ 1. A successful verified built-in mutation records its canonical project path,
83
+ invalidates acknowledgements touching that path, increments the mutation
84
+ generation, and marks automatic work dirty.
85
+ 2. No automatic scan runs during assistant streaming, tool execution, retries,
86
+ compaction recovery, steering, or queued follow-ups.
87
+ 3. `agent_settled` asks the scheduler to cover the latest dirty generation and
88
+ returns immediately.
89
+ 4. The scheduler reuses the existing bounded capability probe, accepted
90
+ baseline, full-project changed comparison, one serialized jscpd adapter, and
91
+ temporary-report ownership.
92
+ 5. A completion is accepted only if its runtime generation, branch scope,
93
+ mutation generation, and project identity are still current.
94
+ 6. Record a current terminal outcome—clean, findings, unavailable, timed out, or
95
+ failed—as the automatic attempt for the covered generation. Do not retry the
96
+ same unchanged generation after every later read-only prompt. Cancellation,
97
+ supersession, or a stale completion does not consume the generation; a newer
98
+ attributable mutation always becomes eligible.
99
+
100
+ No interval, filesystem watcher, long-lived jscpd server, or process starts from
101
+ the extension factory.
102
+
103
+ ### A new run during a background check
104
+
105
+ A new accepted prompt or agent run supersedes an automatic check. Abort its
106
+ owned signal, leave that generation unattempted, and discard any late completion.
107
+ Cancellation should be requested before the new run can mutate files; cleanup
108
+ may finish through the serialized adapter without blocking the prompt.
109
+
110
+ Explicit `/jscpd` and `jscpd_run` operations also take priority over automatic
111
+ work. They may cancel the automatic owner and then use the same serialized
112
+ adapter. The automatic scheduler must never create a second process owner.
113
+
114
+ ### Queued steering and follow-ups
115
+
116
+ Do nothing at intermediate `agent_end` events. Pi drains automatic retries,
117
+ automatic compaction retries, steering, and follow-up messages before
118
+ `agent_settled`, so all attributable mutations from that logical run coalesce
119
+ into the same latest dirty generation. Follow-up delivery mode does not create
120
+ an extra checkpoint between queued tasks.
121
+
122
+ ### Reload, session replacement, branch navigation, and shutdown
123
+
124
+ - On reload, new, resume, or fork shutdown: close the scheduler first, abort its
125
+ owned check, invalidate queued work, and await existing adapter cleanup. The
126
+ replacement runtime restores only its active-branch state and creates a new
127
+ scheduler generation.
128
+ - On `/tree` navigation: invalidate automatic work before restoring the target
129
+ branch. A completion from the abandoned branch must never update or
130
+ acknowledge the target branch.
131
+ - On quit: cancel and clean up only. Never start a final scan from
132
+ `session_shutdown`.
133
+ - Shutdown remains idempotent and bounded by the existing child-process
134
+ termination and cleanup model.
135
+
136
+ ## Context and presentation policy
137
+
138
+ A clean automatic check must not call `pi.sendMessage()` and must not append a
139
+ message entry. It may update bounded internal session state and terminal status,
140
+ which stay outside model context.
141
+
142
+ A failed automatic check is also quiet by default. `/jscpd status` remains the
143
+ place to inspect bounded failure state; missing binaries stay dormant rather
144
+ than producing one warning per settled run.
145
+
146
+ For a new actionable finding, append one bounded custom finding message while
147
+ Pi is still idle with `triggerTurn: false`. Pi persists that message and includes
148
+ it in the next model context without starting a surprise LLM turn. It may also
149
+ be displayed immediately by supported UI modes. Before appending, recheck the
150
+ runtime, branch, mutation generation, and idle state; otherwise discard the
151
+ completion and keep the generation dirty.
152
+
153
+ Automatic acknowledgement must happen only after that durable finding message
154
+ is successfully appended. The explicit changed executor's current
155
+ "acknowledge on presentation" side effect therefore must not be reused blindly
156
+ by a background scheduler. Omitted findings remain unacknowledged.
157
+
158
+ ## Rejected alternatives
159
+
160
+ ### Scan on every successful write/edit result
161
+
162
+ This has the best proximity to a mutation but the worst multiplicity. Parallel
163
+ or rapid tool batches can request overlapping scans while the agent is still
164
+ working, and the result can become stale before it is presented.
165
+
166
+ ### Scan at every turn end
167
+
168
+ A tool-using response can have many turns. Waiting in `turn_end` adds scan
169
+ latency directly between model turns; detaching the work introduces races with
170
+ the next tool batch. Dirty coalescing does not make an unsettled tree safe.
171
+
172
+ ### Scan at agent end
173
+
174
+ `agent_end` is not a logical idle boundary. Pi may retry a provider error,
175
+ compact and retry, or continue queued messages after it. Triggering there can
176
+ scan intermediate source and duplicate work.
177
+
178
+ ### Scan before the next user prompt
179
+
180
+ This sees the previous tree at rest but makes the user pay the full checkpoint
181
+ latency before work starts, and no check occurs if the session ends. It remains
182
+ a cancellation boundary, not the primary trigger.
183
+
184
+ ### Debounce timer after mutation
185
+
186
+ A timer guesses idleness and must race streaming, queued messages, session
187
+ replacement, and process cleanup. Pi already exposes the stronger settled
188
+ signal, so an additional wall-clock debounce adds complexity without a better
189
+ safety guarantee.
190
+
191
+ ### Scan during shutdown
192
+
193
+ The result has no reliable advisory destination, can prolong exit, and opposes
194
+ the invariant that shutdown only cancels and cleans owned work.
195
+
196
+ ## Implementation boundary
197
+
198
+ M4.2 adds the bounded scheduler primitive: attributable mutations advance a
199
+ generation, repeated automatic requests coalesce to one active plus one latest
200
+ pending generation, explicit scans cancel only automatic ownership, deferred
201
+ completions remain retryable, and reset or shutdown rejects stale work.
202
+
203
+ M4.3 wires `agent_settled` to a detached automatic changed check. It reuses the
204
+ configured capability, baseline, timeout, finding cap, changed-file set, and
205
+ serialized adapter; cancellation or baseline-pending outcomes remain retryable,
206
+ while other terminal failures are consumed once per unchanged generation.
207
+ Lifecycle transitions cancel owned work and shutdown awaits bounded adapter
208
+ cleanup. Automatic findings are deliberately not acknowledged or injected yet.
209
+
210
+ M4.4 implements the presentation boundary. Automatic checks cap delivery at
211
+ five findings, prioritize groups whose two locations changed in the session,
212
+ and preserve deterministic size/location ordering within that priority. Clean
213
+ and failed outcomes update only bounded session/footer status. A current
214
+ finding result sends one custom message with `triggerTurn: false`, then commits
215
+ only the delivered acknowledgement identities; omitted findings remain
216
+ eligible. Non-TUI mode uses the same model-context message with transcript
217
+ display disabled. Delivery failure, queued work, a newer mutation, or a stale
218
+ lifecycle scope leaves the generation retryable without acknowledging it.
219
+
220
+ M7.5 replaces the scheduler's mutable Promise/microtask owner with scoped Effect
221
+ fibers, composes automatic checks directly through the scheduler's native Effect
222
+ path, and moves acknowledgement staging plus Pi delivery ordering to Effect. M7.7
223
+ supplies the managed host runtime, and M7.8 removes obsolete facade paths. See
224
+ [Effect architecture and conformance](effect-architecture.md).
225
+
226
+ Focused tests cover coalescing, scheduler freshness, clean and failure status,
227
+ bounded finding delivery, repeated-finding suppression, non-TUI behavior,
228
+ scoped interruption, and acknowledgement-after-delivery ordering.
229
+
230
+ M6 must pin a Pi compatibility range that includes the documented
231
+ `agent_settled` contract. If that contract is unavailable or changes, remain
232
+ on-demand and revisit this decision; do not silently fall back to `agent_end`.
233
+
234
+ The lifecycle decision adds no separate configuration and does not change the
235
+ public command/tool contract.
@@ -0,0 +1,119 @@
1
+ # Compatibility policy
2
+
3
+ `pi-jscpd` deliberately has a narrow, tested host contract. The package uses
4
+ Pi's extension, event, tool, custom-message, and TUI APIs directly, so an open
5
+ peer range would imply compatibility that has not been verified.
6
+
7
+ ## Package identity and ownership
8
+
9
+ The canonical package name is **`pi-jscpd`**. The source and package metadata are
10
+ owned and maintained by [Revaz Zakalashvili](https://github.com/revazi) at
11
+ [`revazi/pi-jscpd`](https://github.com/revazi/pi-jscpd). A public npm lookup
12
+ returned no existing package when this name was selected, but a lookup does not
13
+ reserve a name.
14
+
15
+ The first supported release is `0.1.0`. The completed runtime design is
16
+ documented in [Effect architecture and conformance](effect-architecture.md).
17
+ The manual readiness workflow has no publication permission or credentials;
18
+ tagged publication is handled separately by the protected release workflow. The
19
+ source and package are MIT licensed; see the [release policy](release.md).
20
+
21
+ ## Supported and tested matrix
22
+
23
+ | Component | Supported range | Tested fixture | Notes |
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. |
29
+ | `typebox` | `>=1.3.7 <2` | `1.3.7` | Required by the agent-tool schema. |
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
+ | `jscpd` | Compatible v5 | `5.1.2` | Exact runtime dependency and fallback analyzer. |
32
+
33
+ Effect `3.22.1` was the npm registry's current stable 3.x release when foundation
34
+ issue [#64](https://github.com/revazi/pi-jscpd/issues/64) selected it. Installed
35
+ package metadata confirms its MIT license, and the lockfile preserves the
36
+ reviewed registry integrity. See
37
+ [Effect architecture and conformance](effect-architecture.md) for dependency,
38
+ service/error, resource, and runtime-boundary policy. No open range or automatic upgrade is implied.
39
+
40
+ The Pi and TypeBox packages remain peer dependencies so the extension uses the
41
+ host Pi installation instead of bundling a second runtime. Development
42
+ dependencies pin the exact tested fixtures. Effect and jscpd are normal, exact
43
+ runtime dependencies; installing `pi-jscpd` provides the reviewed composition
44
+ foundation and analyzer without a later network request.
45
+ `npm run compatibility:check` verifies the active Node version, peer ranges,
46
+ aligned Pi fixture versions, installed fixture versions, locked Effect metadata
47
+ and integrity, and the pinned jscpd runtime before type checking and tests.
48
+
49
+ The supported ranges cover the Node 22 and 24 LTS lines, not the intervening
50
+ non-LTS Node 23 line. They are a contract, not a claim that every patch
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.
55
+
56
+ ## Packed-artifact certification
57
+
58
+ `npm run pack:certify` tests the artifact that would be published, not the source
59
+ checkout alone. Its disposable install resolves the exact declared jscpd version
60
+ through npm just as a user installation would; runtime checks remain offline. It:
61
+
62
+ - compares `npm pack --json` output with the complete tracked runtime, license,
63
+ and public-document allowlist, including regular-file modes and unsafe/private
64
+ path rejection;
65
+ - installs that exact tarball with lifecycle scripts disabled in a restrictive
66
+ disposable location and verifies exact, importable Effect `3.22.1` plus jscpd
67
+ `5.1.2` dependencies;
68
+ - uses the locked Pi `0.84.4` CLI with isolated home, agent, session, and
69
+ temporary directories and only the explicit installed package enabled;
70
+ - verifies that Pi discovers exactly one packaged `/skill:jscpd`, then verifies
71
+ `/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
76
+ - separately places a deterministic fake jscpd v5 executable on the disposable
77
+ `PATH`, then stops Pi during an active scan and asserts that the process tree
78
+ and every `pi-jscpd-*` report directory are gone.
79
+
80
+ The generated probe and fake executable remain certification fixtures for
81
+ controlled output and process-tree behavior; the bundled capability check runs
82
+ the real pinned jscpd release. CI runs this certification on both supported Node
83
+ fixtures. The certification script itself is development tooling and is
84
+ deliberately excluded from the publishable file allowlist.
85
+
86
+ ## Unsupported-version behavior
87
+
88
+ - npm uses `engines.node` and peer dependency ranges during installation. Based
89
+ on the user's npm configuration, an unsupported Node or peer version may
90
+ produce a warning or reject dependency resolution.
91
+ - The extension does not add a second runtime version detector or compatibility
92
+ shim. A forced install outside the declared ranges is unsupported and may fail
93
+ to load if Pi's API or Node's runtime behavior changed.
94
+ - Once the extension loads on a supported host, its ordinary operational errors
95
+ remain advisory and fail open. That runtime policy does not turn an
96
+ unsupported host into a supported one.
97
+ - The adapter first checks compatible project-local and `PATH` installations,
98
+ then falls back to the package-owned jscpd `5.1.2` dependency. A missing or
99
+ damaged bundled dependency is reported without breaking the Pi session; the
100
+ extension never invokes `npx` or downloads an analyzer at runtime.
101
+
102
+ ## Updating compatibility
103
+
104
+ A support-range or Effect-runtime change must:
105
+
106
+ 1. update `engines`, peer ranges, exact development fixtures, and the exact
107
+ reviewed Effect dependency as applicable;
108
+ 2. run `npm install` so the lockfile records those fixtures;
109
+ 3. pass `npm run check` at the minimum supported Node version and the current
110
+ supported Node LTS fixture;
111
+ 4. run `npm run pack:certify` to smoke-test the exact tarball, extension loading,
112
+ representative command/tool/UI behavior, and shutdown cleanup with the
113
+ candidate Pi version; and
114
+ 5. update this matrix, Effect architecture checks, and release notes; and
115
+ 6. run the non-publishing `npm run release:check` gate documented in
116
+ [the release policy](release.md).
117
+
118
+ Do not widen peer ranges solely because installation succeeds. Pi lifecycle and
119
+ TUI behavior must be exercised before a new release line is declared supported.
@@ -0,0 +1,128 @@
1
+ # Effect architecture and conformance
2
+
3
+ Status: **implemented and recertified for the first public release**
4
+
5
+ `pi-jscpd` uses Effect 3.22.1 for fallible asynchronous work, resource ownership,
6
+ cancellation, concurrency, and shared service workflows. jscpd remains the source
7
+ of truth for clone detection, while deterministic parsing, normalization,
8
+ comparison, registry data, and presentation remain plain TypeScript.
9
+
10
+ This document describes the final architecture. Migration issue history belongs
11
+ in Git and the local maintainer roadmap rather than the published package.
12
+
13
+ ## Runtime topology
14
+
15
+ Each extension instance owns exactly one `ManagedRuntime` created in
16
+ `src/effect/runtime-boundary.ts`. Its production layer graph supplies:
17
+
18
+ - `JscpdProcess` for shell-free bounded child-process execution;
19
+ - `JscpdFileSystem` for canonical paths, metadata, bounded reads/writes,
20
+ restrictive temporary directories, and cleanup; and
21
+ - `JscpdClock` for timeouts and scheduling.
22
+
23
+ Pi tool, slash-command, lifecycle, and overlay adapters execute native programs
24
+ through that runtime. Infrastructure, domain, and application modules never call
25
+ `Effect.run*` or create independent runtimes. Isolated test execution lives under
26
+ `test/support/`.
27
+
28
+ Promise workflow orchestration is intentionally limited to three reviewed files:
29
+
30
+ - `src/extension.ts` — imperative Pi callbacks and tracked shutdown coordination;
31
+ - `src/overlay.ts` — the callback-driven TUI adapter; and
32
+ - `src/effect/filesystem.ts` — Node filesystem promises wrapped by typed effects
33
+ and bracketed handle ownership.
34
+
35
+ The AST architecture gate enforces these boundaries and rejects Promise service
36
+ contracts, nested Effect runners, unmanaged timers, and direct filesystem access
37
+ from migrated modules.
38
+
39
+ ## Services and layers
40
+
41
+ Infrastructure services own effects and resources:
42
+
43
+ - analyzer resolution and capability probing;
44
+ - bounded process-tree execution;
45
+ - report workspace creation, reading, and removal;
46
+ - filesystem/configuration access;
47
+ - lifecycle cancellation and time; and
48
+ - narrow Pi persistence/delivery callbacks.
49
+
50
+ Domain owners maintain one bounded state implementation each:
51
+
52
+ - baseline generation and deferred completion;
53
+ - changed-file project generation and canonical file set;
54
+ - acknowledgement branch scope and revision;
55
+ - verification checkpoints;
56
+ - session mode and last-check status; and
57
+ - Fallow coexistence policy and notice state.
58
+
59
+ Application programs compose scan, changed comparison, status/session controls,
60
+ automatic checkpoints, scheduling, and refactor verification. Service effects
61
+ retain their process/filesystem requirements until the host runtime supplies the
62
+ production layers.
63
+
64
+ ## Resource and cancellation ownership
65
+
66
+ Every analyzer child is acquired and finalized by `JscpdProcess`. Execution uses
67
+ argument arrays with `shell: false`, bounds combined output and time, propagates
68
+ interruption, sends TERM before bounded KILL escalation, checks remaining process
69
+ roots, destroys abandoned streams, and detaches listeners.
70
+
71
+ Each analyzer run is serialized by an Effect semaphore. It creates a mode-0700
72
+ workspace outside the project, supplies one fixed report path, reads only a
73
+ bounded regular non-symlink artifact, consumes the report before cleanup, and
74
+ removes the workspace on success, failure, timeout, cancellation, invalidation,
75
+ or disposal. Cleanup uncertainty overrides an otherwise valid result.
76
+
77
+ Bounded file reads use `acquireUseRelease`. Acquisition is bracketed, an in-flight
78
+ read settles before the handle closes, and a handle acquired after interruption
79
+ is closed without starting the read. Typed filesystem and size failures map to
80
+ bounded report/configuration outcomes.
81
+
82
+ Automatic checks run as scoped scheduler fibers. The scheduler owns one active
83
+ and one latest pending generation, coalesces through `JscpdClock`, interrupts only
84
+ its own automatic work when explicit scans take priority, and leaves interrupted,
85
+ stale, or deferred generations retryable. Task construction and result delivery
86
+ are suspended inside failure/finalizer boundaries so defects cannot strand active
87
+ ownership.
88
+
89
+ Quiet baseline capture is host-launched but explicitly tracked. Branch changes
90
+ invalidate and await prior baseline settlement before replacement work starts;
91
+ shutdown awaits baseline finalizers before disposing scheduler, capability,
92
+ analyzer, and runtime resources. Shutdown is idempotent.
93
+
94
+ ## Failure and privacy contract
95
+
96
+ Expected operational failures use tagged Effect errors for analyzer, process,
97
+ filesystem, cancellation, timeout, limits, invalid input, stale work, workspace,
98
+ persistence, and delivery conditions. Application and host boundaries translate
99
+ them to the existing bounded fail-open result unions.
100
+
101
+ Unexpected defects are contained only at reviewed outer workflow/host boundaries.
102
+ Public output never includes raw exceptions, child output, environment values,
103
+ source fragments, internal fingerprints, or temporary paths. Interruption remains
104
+ distinct from ordinary failure so resource finalizers run and stale work cannot
105
+ commit state.
106
+
107
+ Automatic delivery rechecks generation, branch, idle state, and pending messages;
108
+ appends findings with `triggerTurn: false`; commits acknowledgements only after
109
+ successful delivery; and treats persistence/footer failures as advisory. Clean
110
+ and failed automatic checks stay out of model context.
111
+
112
+ ## Conformance evidence
113
+
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:
116
+
117
+ - strict TypeScript and Biome checks;
118
+ - architecture, documentation-link, and repository-hygiene gates;
119
+ - more than 400 network-free tests, including cancellation and finalizer regressions;
120
+ - Fallow dead-code, architecture, security, and full-audit checks;
121
+ - a production dependency audit with no reported vulnerabilities; and
122
+ - exact release tarball installation and runtime certification through RPC,
123
+ skill, tool, TUI-compatible, JSON, and print paths, package-owned jscpd probing,
124
+ active process-tree shutdown, and temporary-report cleanup.
125
+
126
+ Certification proves only the reviewed source and artifact state. Versioning,
127
+ tagging, and publication remain explicit maintainer decisions under the
128
+ [release policy](release.md).
@@ -0,0 +1,120 @@
1
+ # Fallow coexistence policy
2
+
3
+ Status: implemented with Effect-owned policy evaluation
4
+
5
+ Applies to: automatic `pi-jscpd` changed checks only
6
+
7
+ Effect-owned policy evaluation uses the shared bounded filesystem service. These
8
+ detection signals and coexistence outcomes remain product contracts and require a
9
+ separate product decision to change.
10
+
11
+ ## Purpose
12
+
13
+ Fallow and jscpd both detect duplication. When Pi Fallow or a project Fallow
14
+ command already runs duplication analysis, two automatic warnings for the same
15
+ change are noisy. `pi-jscpd` therefore detects only a small set of conservative
16
+ signals and, on a high-confidence match, keeps its automatic changed checks on
17
+ demand. Explicit `/jscpd` commands and `jscpd_run` remain available.
18
+
19
+ No coexistence state disables Fallow, changes either analyzer's detection
20
+ semantics, edits project configuration, or changes jscpd's own ignore and format
21
+ policy.
22
+
23
+ ## Configuration
24
+
25
+ `.pi/jscpd-guardrail.json` and its local override accept:
26
+
27
+ ```json
28
+ {
29
+ "fallowCoexistence": "auto"
30
+ }
31
+ ```
32
+
33
+ - `auto` (default): apply conservative detection. A detected overlap suppresses
34
+ automatic jscpd changed checks and explains the choice once. Ambiguous or
35
+ absent evidence leaves automatic checks enabled.
36
+ - `on-demand`: explicitly suppress automatic jscpd changed checks. Explicit
37
+ changed, project, and scoped scans remain available.
38
+ - `allow`: explicitly allow both automatic analyzers, even when Fallow is
39
+ available.
40
+
41
+ The normal trust-gated, strict, atomic extension configuration rules apply.
42
+
43
+ ## Supported signals
44
+
45
+ Under `auto`, these are treated as high confidence:
46
+
47
+ 1. an active Pi tool named `fallow_run` together with project Fallow evidence
48
+ (a config file or `fallow`/`pi-fallow` dependency), because Pi Fallow's
49
+ default aggregate includes Fallow duplication;
50
+ 2. the first supported strict-JSON Fallow config (`.fallowrc` or
51
+ `.fallowrc.json`) contains a `duplicates` object whose `enabled` value is
52
+ `true` or omitted; or
53
+ 3. a direct `package.json` script command invokes `fallow`'s combined root,
54
+ `dupes`, `audit`, `all`, `check-changed`, or `review` command. Commands must
55
+ occur at a shell command-segment boundary; prose such as
56
+ `echo fallow dupes` is not accepted.
57
+
58
+ A readable `duplicates.enabled: false` is authoritative for detection and keeps
59
+ automatic jscpd checks enabled, even when the Fallow tool or script is present.
60
+ This only observes Fallow's setting; it does not alter it.
61
+
62
+ The following evidence is deliberately ambiguous and never suppresses an
63
+ automatic check:
64
+
65
+ - an active `fallow_run` tool with no project Fallow evidence;
66
+ - a `fallow` or `pi-fallow` package dependency without an active tool or direct
67
+ duplication command;
68
+ - a Fallow config with no `duplicates` section and no active Pi Fallow tool;
69
+ - JSONC or TOML Fallow configuration, which this adapter does not partially
70
+ parse;
71
+ - malformed, oversized, unreadable, non-regular, or out-of-project signal
72
+ files; and
73
+ - an untrusted project, because project-use evidence is not inspected.
74
+
75
+ Signal reads are trust-gated, no-follow, project-contained, and capped at 64
76
+ KiB. Only fixed signal identifiers enter status; file contents and absolute
77
+ paths do not.
78
+
79
+ ## User-visible behavior
80
+
81
+ A detected automatic overlap emits one informational notice per Pi session:
82
+
83
+ ```text
84
+ Fallow duplication analysis appears active. To avoid duplicate warnings,
85
+ automatic jscpd changed checks are on demand; no configuration was changed.
86
+ Use /jscpd changed or a scoped /jscpd scan <target>.
87
+ ```
88
+
89
+ `/jscpd status` and the overlay report whether overlap is absent, ambiguous,
90
+ detected/on-demand, or explicitly configured. Pending footer state says
91
+ `jscpd: on demand (Fallow overlap)` instead of implying that an automatic check
92
+ will run.
93
+
94
+ The quiet pre-session jscpd baseline remains available so an explicit
95
+ `/jscpd changed` request can still classify session deltas. Opening the overlay
96
+ still performs status only. Automatic Fallow suppression does not acknowledge
97
+ jscpd findings or consume scheduler generations.
98
+
99
+ ## Division of responsibility
100
+
101
+ - **Fallow** is the broader JavaScript/TypeScript codebase analyzer: duplication,
102
+ dead code, complexity, architecture, security candidates, and related
103
+ evidence in one workflow.
104
+ - **jscpd** remains the polyglot tokenization, clone-detection, format, and
105
+ duplication-statistics authority for `pi-jscpd`.
106
+ - **pi-jscpd** adds Pi session attribution, changed-only comparisons,
107
+ acknowledgement, verification, and a jscpd-focused overlay. It is useful on
108
+ demand for polyglot or embedded-format coverage and for repositories whose
109
+ existing jscpd policy is authoritative.
110
+
111
+ When both are desired, set `fallowCoexistence` to `allow`. Otherwise keep the
112
+ default detected on-demand behavior and use scoped jscpd scans where its
113
+ polyglot coverage adds value.
114
+
115
+ ## Known limitations
116
+
117
+ Detection intentionally does not inspect arbitrary CI YAML, recursively resolve
118
+ npm scripts, infer globally installed CLI binaries, parse shell expansions, or
119
+ interpret JSONC/TOML. These omissions avoid false positives. Users can select
120
+ `on-demand` or `allow` explicitly when unsupported project policy is known.