Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ jobs:
run: npm run test:model-catalog
- name: Build production bundles once
run: npm run build
- name: Verify foreground file tools in pinned Electron
run: npm run test:foreground-file-tools:electron
- name: Verify production-profile diagnostics support workflow
run: npm run test:e2e:diagnostics:production:run

Expand Down Expand Up @@ -269,8 +271,18 @@ jobs:
-configuration Debug
-destination 'platform=iOS Simulator,id=${{ steps.simulator.outputs.udid }}'
-derivedDataPath '${{ runner.temp }}/AidenOnTheGoSimulatorDerivedData'
-resultBundlePath '${{ runner.temp }}/AidenOnTheGoSimulator.xcresult'
CODE_SIGNING_ALLOWED=NO

- name: Preserve failed iOS simulator test results
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: ios-simulator-results-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/AidenOnTheGoSimulator.xcresult
retention-days: 7
if-no-files-found: warn

android:
name: Android build and APK
needs: changes
Expand Down
117 changes: 117 additions & 0 deletions .memory/foreground-file-tool-bounds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Foreground file-tool bounds — 2026-09-28

Scope: audit X03/X04/X05, based on a9baa4aa3027893e5455043083465c34b4c8b4ac.
All 36 then-open PRs rechecked with paginated file inventories (#85: 295 files,
#37: 209); none touched coding-tools.ts or its suite. Workspace/Git lanes are separate.

Parent read_file now opens/validates a regular descriptor, reads at most 200,001
bytes (including overflow probe), closes in finally, observes cancellation, and
omits an incomplete UTF-8 trailing sequence. Parent policy intentionally remains
broader than child policy: hidden metadata and credential names remain readable
except .env secrets; in-root links resolve, outside-root links fail. No subagent
credential/RE2 rules were transplanted into parent tools.

Parent list/glob/grep enumerate with opendir(bufferSize: 1), at most 10,000 entries
and 5 seconds per invocation. Over-budget directories are omitted rather than
allowing OS enumeration order to select a subset; prior complete-directory
results survive. List retains at most 500 results, glob 500, grep 200; collection
and output bounds have explicit notices (20,000 output chars). Grep skips hidden,
linked, dependency/build directories as before, reads at most 512,001 bytes per
file and 10 MiB total (including probes), and skips a growing oversized file.

JavaScript RegExp remains native JS, including lookbehind and backreferences,
inside a fixed-source owned worker. Model patterns are workerData, never code.
Glob parses bounded patterns with pinned minimatch 9.0.9 using Node fs.glob
options, then tracks glob segment positions and linked traversal states in the
worker. Host-side filesystem access remains bounded and confined. Traversal
rules are adapted from Node.js (MIT notice included). Native-glob differential fixtures cover ordinary patterns,
braces, extglobs, hidden paths, absolute paths, directory roots and symlinks.
Patterns have a new explicit 1,000-character ceiling. At most four matchers can
run concurrently; excess searches report busy. Cancellation, errors and deadlines
terminate and await the worker before releasing capacity; no persistent worker.
The small-search cost of worker startup is intentional and measured.

No Remote DTO, transcript/activity UI, native implementation or onboarding
capability changed. iOS/Android consumers were inspected: they use unchanged tool
names/labels, not filesystem scanning/matching internals. No plan status changed.
Evidence and repeatable commands: docs/performance/foreground-file-tools-2026-09-28/.
First independent Astra review found safe-link/brace and glob-dependent ..
compatibility gaps in the initial flattened-path matcher. Replaced that approach
with segment-state traversal and added differential fixtures before publication.
Re-review and hosted exact-head CI/bot review remain delivery gates.


PR #288 Pullfrog follow-up: derive each brace-expanded glob arm's root independently;
native differential tests cover mixed absolute/relative alternatives and reject an
outside-root arm before opening its directory. Foreground read/list/glob/grep now
share four operation owners. Caller abort and search deadline settle independently
of pending filesystem I/O; the original operation retains its slot through late
I/O and cleanup. An issued syscall itself is not cancellable. Late handles close
without further reads; failed cleanup quarantines admission. Root verification
awaits both started metadata requests even if one fails. Worker termination starts
on lifetime abort/deadline while its traversal callback may still be pending.
The stalled-I/O deadline result is an explicit notice without partial output.

Completion audit also caught the expanded-root offset using host separators even
though minimatch globParts are slash-normalized. Count normalized slashes so
Windows drive and UNC roots skip exactly their parsed prefix. Production-worker
VM fixtures use path.win32 and minimatch platform win32, covering slash/backslash
drive spelling, UNC, and mixed absolute/relative arms; prior code fails the fixture.


The assertion-based Electron smoke is registered as
`test:foreground-file-tools:electron` and required in the existing Desktop build
and diagnostics CI lane. Its Node runner bundles an isolated entry (Electron
explicitly external), owns workspace/profile fixtures, clears ELECTRON_RUN_AS_NODE,
and waits for child close after success/error/30s deadline/SIGINT/SIGTERM before
cleanup. CI policy coverage enforces this mandatory package-script invocation.

A later Pullfrog run found bare UNC share roots without a terminal slash. Consume
the complete platform root, and use an empty terminal segment for directory-self
matching when that consumes the whole pattern. Existing Windows worker fixtures
now cover drive roots, bare UNC shares with/without slash, and mixed relative arms.

Published Electron smoke receipts normalize the synthetic workspace prefix to
`<workspace>` while assertions retain real absolute paths. The counter receipts
already use repository-relative module paths and contain no author-local root.

Hosted CI follow-up reused the narrowly scoped consumer-gate correction already
present in PRs #278/#280: hold the exact recovery stream endpoint, then deliberately
run unrelated progress before sending. Reverting only the exact endpoint to
`/events` reproduces a progress-observer timeout in the simulator. The separate
completed-upload failure remains unreproduced (unchanged full chat suite passed
208/208 locally); per-mode assertion labels and failed-CI xcresult preservation
provide diagnostic evidence without relaxing assertions or timeouts. Parent audit
papercuts retain both original failing run attempts and the unresolved upload flake.

Integrated origin/main 137ce6bd1 by ordinary merge after the parent verified the
PR's pre-integration head 30b042b4 green. Only papercut append sections conflicted;
kept both. Root script names/test chains retain both parents' entries, and the
reused exact iOS stream gate remains alongside main's catalog/publication tests.
The Remote revision is unchanged by this branch. Advisor drift coverage passes;
a broader direct CLI extension invocation lacked its required built dist/app,
so its process-launch failure is not runtime validation.

Integration smoke revealed that sequential caller cancellations can fill retained
cleanup slots under host load. The smoke now observes and joins original operation
promises before each next sample and after the deadline sample; the production
owner contract, caller timing, and process hard deadline are unchanged. Merged iOS
chat XCTest passed 214/214 on the selected iOS 27 simulator.
The observer retains late cleanup errors and asserts none; fault injection after
real worker termination confirms cleanup quarantine fails the smoke even after
caller cancellation has already settled.

A later actual papercut conflict required merging main ea65d03c3 (workspace
metadata PR #286 plus catalog refresh). Preserved both records and consolidated
this lane's notes in an interior section to avoid repeated EOF append conflicts.
Workspace recursive metadata/legacy identity batching keeps its separate FIFO
four-inspection budget; foreground coding tools retain their independent four
operation owners. Neither budget is a cap on all process filesystem syscalls.
No Remote revision, native source, or foreground production code changed here.
Independent Astra integration review confirmed no nested admission or shared release
path between the budgets. Combined foreground/generation tests passed 56/56;
workspace/Remote suites passed 43 with one existing platform skip after building
the required native worktree-file-io test helper (initial ENOENT was a missing
fixture prerequisite). Type-check, 47 CI-policy tests, and the joined Electron
smoke with zero worker ports passed. Prior 214 iOS tests still cover unchanged
native source; exact new-head hosted gates remain required.
27 changes: 27 additions & 0 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,30 @@ FluidAudio source: https://github.com/FluidInference/FluidAudio/tree/87a39dfe406
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

## Node.js glob traversal rules

The segment traversal rules in `main/services/coding-tool-glob-worker.ts` are
adapted from Node.js `lib/internal/fs/glob.js` (Node 22.22.3), with host-owned
bounded filesystem access. Node.js is licensed under the MIT license:

Copyright Node.js contributors. All rights reserved.
Copyright Joyent, Inc. and other Node contributors. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
95 changes: 95 additions & 0 deletions docs/performance/foreground-file-tools-2026-09-28/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Foreground file tools: X03/X04/X05

Baseline: `a9baa4aa3027893e5455043083465c34b4c8b4ac`. Same macOS arm64 host,
Node 22.22.3, and clean `npm ci --ignore-scripts` dependencies (Pi **0.87.1**)
for both source-counter runs. No provider traffic, catalog fetch or user profiles.
These are deterministic filesystem counters and synthetic timings, not energy or
packaged UI latency measurements. Raw receipts: [before](before.json),
[after](after.json), [Electron runtime](electron.json).

| Fixture | Before | After |
| --- | ---: | ---: |
| 16 MiB read_file: actual bytes read | 16,777,216 | 200,001 |
| Same read: output characters | 200,014 | 200,014 |
| Already-aborted grep | Returns match; reads 7 bytes | Rejects; reads 0 bytes |
| 10,100-entry no-match scan: entries enumerated | 10,100 | 10,000, explicit incomplete notice |
| Wide list output | 200,989 characters | Bounded incomplete notice; over-cap directory omitted |

Grep has a 10 MiB aggregate input ceiling, including each overflow probe; tests
exercise growth after stat and exact byte counts. List/glob/grep have 10,000-entry,
5-second work limits; an over-cap directory is omitted for deterministic results.
Collection limits are 500/500/200 and search/list output is at most 20,000 chars.
Read_file retains its existing 200,000-byte content cap and truncation suffix,
but no longer returns a broken trailing UTF-8 character.

Native JavaScript regex semantics remain supported, including lookbehind and
backreferences. No RE2 fallback changes the accepted language. Patterns above
1,000 characters now fail explicitly; a pathological pattern stops at the search
deadline with an incomplete notice. A fixed-source worker receives model input as
data and is terminated/awaited on every exit. Four concurrent workers are allowed;
additional searches return a busy error. This adds roughly 17 ms per tiny grep on
the quiet fixture run (see raw samples), versus sub-millisecond baseline calls.
It provides cancellable matching and removes regex execution from Electron's main
thread. The worker is not retained between calls.

A foreground operation scope covers pending filesystem I/O as well as matching.
Cancellation rejects the caller promptly; the five-second search deadline returns
an explicit incomplete notice even if a syscall is still pending. An issued
kernel syscall cannot be cancelled in JavaScript: its owner keeps one of four
process-wide admission slots until the original operation and descriptor cleanup
settle. Late acquisitions close without another read, late errors remain observed,
and a failed close quarantines capacity rather than admitting unbounded work.
Worker termination begins on cancellation/deadline even while traversal is pending.
Deferred-I/O tests cover acquisition/read cancellation across all four tools,
concurrent and repeated cancellation, blocked cleanup, and capacity recovery.
A deadline while I/O is pending returns only the notice, without partial results.


Parent hidden-file/credential policy remains distinct from the stricter child
policy. Read_file still supports safe symlinks and metadata, excludes .env secrets,
and rejects outside-root targets. Grep retains hidden/symlink/dependency ignores.
Glob uses pinned minimatch 9.0.9 with Node fs.glob parser options. It tracks
segment positions and link traversal states without prematurely normalizing
`**/..`. Filesystem access stays on the bounded host. The first independent review
found gaps in a flattened-path matcher; the replacement has differential tests
for absolute paths, mixed absolute/relative brace arms, braces/extglobs, globstars, linked prefixes, linked wildcard
paths, and glob-dependent parent segments. Worker-engine fixtures also exercise
Windows drive and UNC roots with Windows path/parser semantics. Node traversal attribution is in
THIRD_PARTY_NOTICES.md.

Pinned Electron 43.1.1 / Node 24.18.0 smoke uses a bundled entry and real app main
process. It verifies native glob compatibility, JS lookbehind/backreferences,
six invalid-pattern failures and six catastrophic-pattern cancellations followed
by successful calls, deadline settlement, and zero retained worker message ports.
150 ms cancellation timers settled at 150–154 ms in the recorded run.

Reproduce counters (the temporary baseline copy stays in this worktree):

```sh
npm ci --ignore-scripts
git show a9baa4aa3027893e5455043083465c34b4c8b4ac:main/services/coding-tools.ts > main/services/.foreground-baseline.ts
npx tsx scripts/benchmark-foreground-file-tools.ts main/services/.foreground-baseline.ts
npx tsx scripts/benchmark-foreground-file-tools.ts
rm main/services/.foreground-baseline.ts
```

Run behavioral acceptance and the Electron smoke:

```sh
npx tsx --test main/services/coding-tools.test.ts main/services/generation-runtime.test.ts
npm run type-check
npx eslint main/services/coding-tools.ts main/services/coding-tool-matcher.ts main/services/coding-tool-glob-worker.ts main/services/foreground-read-scope.ts main/services/coding-tools.test.ts scripts/benchmark-foreground-file-tools.ts scripts/smoke-foreground-file-tools.ts
node node_modules/electron/install.js
npm run test:foreground-file-tools:electron
npm run test:ci-policy
```

The existing coding-tools test file is already in the root test chain and CI
registry. The Electron smoke has its own package command and runs as a required
step in the existing Desktop build and diagnostics CI lane. Its runner bundles
into an isolated directory, uses the installed pinned Electron, owns the synthetic
workspace and user-data profile, and waits for process close before cleanup. A
30-second hard process deadline also catches module-load failures before the
smoke's own error handler can run; SIGINT/SIGTERM terminate its owned process group.
The CI policy suite verifies the package command remains a required CI step. No shared server/native contract or transcript UI changed; no mobile
implementation or plan-status update is needed for these internal tools.
49 changes: 49 additions & 0 deletions docs/performance/foreground-file-tools-2026-09-28/after.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"modulePath": "main/services/coding-tools.ts",
"node": "v22.22.3",
"platform": "darwin",
"arch": "arm64",
"piVersion": "0.87.1",
"read": {
"inputBytes": 16777216,
"bytesRead": 200001,
"outputChars": 200014
},
"abortedGrep": {
"rejected": true,
"bytesRead": 0
},
"smallGrepMs": [
18.926792000000034,
17.84620799999999,
17.246708999999953,
17.875292,
17.257499999999993,
17.86224999999996,
17.177625000000035,
17.380792000000042,
17.254166999999995,
17.109041999999988
],
"wideFixtureEntries": 10100,
"scans": [
{
"name": "list_dir",
"enumeratedEntries": 10000,
"outputChars": 58,
"incompleteNotice": true
},
{
"name": "glob",
"enumeratedEntries": 10000,
"outputChars": 49,
"incompleteNotice": true
},
{
"name": "grep",
"enumeratedEntries": 10000,
"outputChars": 49,
"incompleteNotice": true
}
]
}
49 changes: 49 additions & 0 deletions docs/performance/foreground-file-tools-2026-09-28/before.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"modulePath": "main/services/.foreground-baseline.ts",
"node": "v22.22.3",
"platform": "darwin",
"arch": "arm64",
"piVersion": "0.87.1",
"read": {
"inputBytes": 16777216,
"bytesRead": 16777216,
"outputChars": 200014
},
"abortedGrep": {
"rejected": false,
"bytesRead": 7
},
"smallGrepMs": [
0.3235419999999749,
0.2934579999999869,
0.29383300000000645,
0.3069160000000011,
0.5061660000000074,
0.239750000000015,
0.26924999999999955,
0.3658750000000168,
0.192875000000015,
0.17037499999997863
],
"wideFixtureEntries": 10100,
"scans": [
{
"name": "list_dir",
"enumeratedEntries": 10100,
"outputChars": 200989,
"incompleteNotice": false
},
{
"name": "glob",
"enumeratedEntries": 10100,
"outputChars": 12,
"incompleteNotice": false
},
{
"name": "grep",
"enumeratedEntries": 10100,
"outputChars": 12,
"incompleteNotice": false
}
]
}
Loading
Loading