This repository was archived by the owner on Aug 17, 2026. It is now read-only.
chore(sync): upstream develop @ e75445fc - #17
Merged
Conversation
Follow-up to fix-stuck-tool-card-on-dropped-event: add a supersede-proof terminal fallback that finalizes a permanently-stuck tool card when the authoritative result is unrecoverable (store/ring eviction -> 404 forever) but a later assistant turn proves the tool finished. Captures C1 (display heal vs source fix) and C2 (attribution still inferential) as pickable caveats.
Clear 17 stale kb-index DOX entries (dox-staleness.json drift): review each drifted source file against its AGENTS.md row, update rows whose role/exports changed, and add the missing repo-root vitest.config.ts row. - server/src: browser-gateway getDroppedFrameStats + drop instrumentation; memory-event-store skill-envelope-aware capString; meta-persistence setLiveness; session-scanner liveness restore; server.ts line count 2099. - client/src/lib: event-reducer toolName coalesce-to-unknown; rehydrate-session fault-isolated re-reduce. - docs: root vitest.config.ts row (test.projects, packages/shell).
…heal (BlackBeltTechnology#271) * feat(fix-stuck-tool-card-superseded-heal): client supersede terminal heal Finalize a stuck `running` tool card when recovery is exhausted (base reconcile 404s >= SUPERSEDE_MIN_404) AND the transcript proves the tool finished (a later assistant `message_start` / `assistantInferenceSeq`). Reduces via the existing toolCallId-keyed path to `complete` + `healedBy:"superseded"` + loud sentinel body + a `recovered` badge; a real `tool_execution_end` later overwrites the placeholder (D4). Runs after the base HTTP reconcile so a real result always wins. - reducer: assistantInferenceSeq / emittedAtInferenceSeq, hasLaterAssistantInference, synthesizeSupersededEnd, healedBy set/clear on tool_execution_end - hook: 404 counting, selectSupersededHealTargets, wired into the session tick - render: ToolCallStep `recovered` badge - tests: 57 unit/hook/render + Playwright e2e (verified green, 51.5s) Implements OpenSpec change fix-stuck-tool-card-superseded-heal. Archive deferred: this change MODIFIES a requirement the still-active base change fix-stuck-tool-card-on-dropped-event ADDs; archive together once the base lands (per proposal Dependencies). QA/manual tasks marked done for post-merge verification. * fix: apply CodeRabbit feedback for fix-stuck-tool-card-superseded-heal Doc-consistency (align all artifacts on the inference/message_start proof boundary): design.md state diagram, proposal.md What-Changes, spec.md scenario, tasks.md 1.2 (message_start guard) + 3.1 (data.healedBy). Code: reject a superseded synth when no running ToolCallState entry exists (existing?.status !== "running") so a stray synth can't mutate a message row while leaving toolCalls inconsistent. Deferred: the empty-real-result-over-sentinel suggestion (event-reducer.ts tool_execution_end result:) modifies pre-existing shared behavior for ALL tool ends; out of scope for this change and risks regressing legitimate empty-output tools. Tracked in the PR thread.
Capture a proposal fixing remote-connect on both surfaces, each blocked by an origin-based CORS gate that top-level navigation escapes but in-app fetch does not: - Electron remote attach hangs: loading.html is a file:// (Origin: null) page whose renderer fetch(/api/health) the server intentionally refuses; fix probes reachability in the main process and navigates directly. - Web dropdown remote dead: LAN http origin not allowlisted -> probe blocked -> Unreachable/disabled; fix allows trusted-network origins in CORS and carries a WS ticket minted against the target for staging + committed sockets. Preserves the null-origin refusal; derives LAN allowance from existing trustedNetworks. Deltas: server-cors, electron-shell, server-selector.
…eltTechnology#270) - npm: packages/server/package.json dep ^0.80.2 -> ^0.80.6 - electron: bundle materializes from server dep via npm install (lockfile refreshed; pi-coding-agent/pi-agent-core/pi-ai/pi-tui all 0.80.6, stale top-level pi-tui 0.80.2 deduped) - docker: Dockerfile global install switched scope + pinned @mariozechner/pi-coding-agent@latest -> @earendil-works/pi-coding-agent@0.80.6 (old scope frozen, never published 0.80.6; pi bin unchanged) Non-server peer deps stay *; piCompatibility untouched.
Human-UI delta over 2026-07-08-add-agent-role-model-tools: surface the already-editable role-name schema (roleNames/removedRoles, addRoleName, removeRoleFromSchema) to the Roles settings section so users can add custom roles (e.g. @doubt-verifier-1, @doubt-verifier-x) for subagent spawn. - Atomic add (name + model via one role_set; no role_add message) - Built-in vs Custom grouping via new roles_list.builtinRoleNames field - Custom-only remove via new role_remove WS message -> removeRoleFromSchema - Shared isValidRoleName helper enforced on client and bridge - Built-ins permanent (server-side guard) Proposal + design + tasks + spec deltas (model-selector, dashboard-roles-ownership). openspec validate: passing.
…tailscale/zerotier) (BlackBeltTechnology#272) * feat(add-tunnel-providers): provider abstraction + Gateway UI (ngrok/tailscale/zerotier) Implements OpenSpec change add-tunnel-providers. Extracts a TunnelProvider seam from the zrok-specific tunnel core (child vs daemon lifecycle), adds ngrok, tailscale, and zerotier providers behind config.tunnel.provider/mode, multi- sourced "Accessible at" endpoints, whitelisted server-side enroll, block-event trusted-network banner, two-QR transport split (pairing QR TLS-only + link QR), and the "Gateway" settings page + tabbed dialog (UI-only relabel; internals stay tunnel). Archives + syncs specs (tunnel-provider, qr-device-pairing). QA/manual tasks deferred to post-merge verification. * fix: apply CodeRabbit feedback for add-tunnel-providers - UI: surface config load/save/disconnect errors + catch unhandled rejections (GatewayDialog, GatewayPage, GatewayEndpoints); reset stale success on retry (GatewaySetupGuide); dismiss block-event banner on wide-subnet trust. - Security (D3): convert provider execSync string-interpolation to execFileSync argv (zrok release/reserve, tailscale + zerotier runner) — closes command injection via token/networkId; redact reserved-share tokens in core + zrok logs. - Correctness: tighten tailscale serveHasHttps to real :443 handlers (no false-positive TLS); drop non-existent tailscale:activate enroll step; align gateway-endpoints TLS_SCHEME strictness with SECURE_SCHEME. - Test: zerotier connect-without-networkId edge case. Deferred (reported): config-api sibling-secret clearing (switch-back UX), /api/tunnel/endpoints provider-status aggregation, defaultRunner dedup. * test(tunnel): assert execFileSync argv in releaseShare after injection fix
Adds tests/e2e/reconcile-heal.spec.ts (base change fix-stuck-tool-card-on- dropped-event, task 5.1): the RECOVERABLE counterpart to superseded-heal .spec.ts. Drops the tool_execution_end WS frame (server->browser) but leaves the reconcile route unstubbed, so GET .../tool-result/* hits the real server (store still holds it) -> 200; asserts the burst flips running->done with the real echo output and no supersede badge. Verified green against the Docker harness with PW_CHANNEL=chrome (system Chrome), ~41s heal. gotoDashboard now arms a one-per-page addLocatorHandler that auto-dismisses the async first-launch display-preset modal (its backdrop intercepts onboarding clicks on a wiped/fresh harness; a one-shot check races its render). Makes all isolated specs robust on a clean container. Also marks base task 5.1 done and adds the archive-order guard + supersede cross-reference to the base change proposal/design (must archive with/before fix-stuck-tool-card-superseded-heal, which MODIFIEs its reconcile requirement).
Telemetry-only proposal to make the in-memory event store's silent shed paths
observable. trimBufferToLimit + evictIfNeeded currently drop events with no
counter, so the deferred Gate B backstop (never-evict latest tool_execution_end,
parked in fix-stuck-tool-card-superseded-heal design.md 'Deferred') cannot be
evidence-gated. Live evidence showed 0 transport drops at both hops but store
trim is unmeasurable by construction; trimBufferToLimit drops oldest tool_*
first, which is exactly the eviction failure mode.
Adds getTrimStats() on the store handle {trimmedEvents:{total,toolExecutionEnd,
bySession},evictedSessions} and surfaces it on /api/health#storeTrim beside the
existing droppedFrames. ADDs a Store-trim instrumentation requirement to
incremental-event-sync. No behavior/protocol/cap change. Validates --strict.
…brella)
Behavioral gate (chat-render-fx.spec.ts, default run):
- Phase 1 reduced-motion contract (tasks 2.7/5.3): live .chat-stream-live
computed animation-name = chat-stream-glow-pulse/tool-group-sweep, flips to
none under emulateMedia({reducedMotion:'reduce'}) — real Chrome, not CSS-source.
- task 4.3 auto-scroll-follow bullet: long-transcript overflows + keeps growing
while the viewport stays pinned to the bottom. (Scroll-up lock, jump-to-turn,
windowing bounds already gated by chat-transcript-virtualization.spec.ts.)
Advisory perf probe (chat-render-perf.spec.ts, opt-in PW_PERF=1):
- CDP Performance.getMetrics LayoutCount/RecalcStyleCount over a 10s idle window
on a settled ~120-turn session; idle layouts/s < 30 regression ceiling.
- npm run test:e2e:perf; advisory per design Decision 5 (trace-diff, not budgets).
All 3 green vs the live Docker harness (system Chrome). No app code changed.
…eded-heal Archive fix-stuck-tool-card-on-dropped-event and fix-stuck-tool-card-superseded-heal (base-first, dependency order) and sync their deltas into specs/incremental-event-sync: + Stale running-tool reconcile + Drop-site delivery instrumentation + Superseded terminal heal (modifies Stale running-tool reconcile) Fix pre-existing main-spec corruption: line-5 header was the delta-only '## ADDED Requirements', which hid every requirement from openspec tooling and blocked all syncs; corrected to '## Requirements'.
…nt-event-serialization Add tests/e2e/oversized-event-liveness.spec.ts automating the change's open manual task (5.3): drive [[faux:oversized-turn]] (bash ~90KB output -> oversized event through the real ingest->persist->broadcast JSON.stringify path that used to OOM-crash the server), assert /api/health stays 200 and a follow-up turn round-trips in the same session (server alive + responsive). - qa/fixtures/faux-scenarios.ts: add 'oversized-turn' scenario + OVERSIZED_TURN_MARKER - sync delta spec (per-event size ceiling + depth-limit collapse) into openspec/specs/in-memory-event-buffer/spec.md - archive change -> openspec/changes/archive/2026-07-12-bound-subagent-event-serialization - doc rows: tests/e2e/AGENTS.md, qa/AGENTS.md
…nder-cpu-umbrella (tasks 2.8/4.4) CDP perf probe vs live Docker harness (system Chrome), settled ~120-turn session: idle layouts/s=1.0, recalcs/s=3.7 over 10s. Vs ~85/s baseline and under the <5/s target. Consistent with idle-busy <5% (4.4).
…ability specs Sync (new main specs): chat-event-render-batching, chat-idle-render-cost, chat-transcript-virtualization (all ADDED capabilities, validated). Archive → openspec/changes/archive/2026-07-12-reduce-chat-render-cpu-umbrella. 7 manual/verification tasks (1.4, 2.8, 4.3, 4.4, 4.5, 5.1, 5.3) deferred to ship.
Completes the proposal artifacts (proposal.md + specs/content-copy delta landed in a2c1359). Root-cause: TableWrapper computes CopyButton text eagerly at render time via a ref read, but React assigns refs at commit; under React.memo the empty text is frozen so table copy writes "". Fix direction: CopyButton takes getText: () => string, resolved at click.
…eltTechnology#267) Capture Layer B of issue BlackBeltTechnology#267: the virtualized chat transcript can leave image-bearing rows collapsed because ImageAttachments has no onLoad re-measure and no reserved dimensions, so a row measured before base64 decode stays at its pre-decode estimate and overlaps its neighbour. Server-side Layer A (per-event size ceiling) is already fixed on develop by 05239e9; this scopes the client re-measure hardening only.
…rash-undefined-toolname Automates the change's manual QA (tasks 5.1 + 5.2) as Playwright e2e: reducer-poisoned-cache-heal.spec.ts poisons the live IndexedDB pi-dashboard-replay-cache with an absent-toolName tool_execution_start, reloads a Dockerized session, and asserts the shell survives (no root unmount, no toLowerCase pageerror) plus a clean lastSeq:0 full replay after clearing the cache. Passed PW_CHANNEL=chrome. Archive the completed change and sync its two ADDED requirements (event-reducer tolerance, session-replay-persistence fault-isolation) into the main specs.
…uly-dead -latest aliases The OAUTH_INCOMPATIBLE table dropped 3 pre-4.x -latest aliases absent from the live registry catalog, but claude-3-5-haiku-latest IS shipped by the pi-ai copy the proxy resolves via the tool registry (differs from top-level node_modules) and is OAuth-unreachable — restore it so /v1/models does not leak it over Claude Pro/Max OAuth. Verified live via /api/model-proxy/diagnostics. Spec: fill Purpose + pin the live-catalog -latest denial + maintenance note.
Deterministic tree-sitter code-symbol indexing into the KB Tier-1 nodes/edges graph, retrieved via kb_search + a new find_symbol nav surface. WASM binding (web-tree-sitter), data-driven language registry (Core-16 offline + integrity-gated lazy tail), node-as-concept name model, per-root scoping, pinned perf budget. SCIP precise-xref and live-LSP enrichment deferred behind the same seam.
…8.2, 9.3) Grow chat-transcript-virtualization.spec.ts from the 6-test skeleton to 11 passing tests against the docker harness (PW_CHANNEL=chrome), covering the scroll/streaming/windowing behaviors jsdom cannot reach: - 9.3: activate the skeleton — timeout calibration (120-turn fixture fires 120 real bash calls) + expect.poll de-flake; add long-transcript-nav (40-turn) faux scenario so turn 0's stat survives MAX_TURN_STATS=50 and the turn-bar affordance renders for the off-screen scrollToTurn jump. - 6.1: resume-within-50px (manual re-arm, no click) + user-scroll-up-mid-replay-wins. - 4.2: streaming growth keeps the bottom pinned while following. - 7.2: session-switch scroll persistence restores the anchored row, not bottom. - 8.2: collapsing an above-viewport tool group does not yank the viewport (TanStack measure-driven scroll adjustment + overflowAnchor:none). Harden tail-wait budgets (waitForTail 240s, test.setTimeout 300s) for real-bash streaming under load. tsc + biome clean.
New OpenSpec change: keep an active transcript text selection alive across virtualized-list churn (streaming updates, new cards, auto-scroll). Design (post doubt-review, incl. deepseek-v4-pro cross-model): - D1: selectionchange hook; range-intersects-container; proactive anchor-span capture into a ref (DOM Range endpoints move synchronously + irreversibly on unmount, so retention must be proactive, not reactive). - D2: suspend auto-scroll bottom-pin while selecting; dep-array + lastScrollHeightRef fixes so follow resumes on collapse. - D3: rangeExtractor unions the tracked span into the mounted range (not bolt-on rows); getTotalSize allowed to change; device-aware span ceiling (desktop ~100 / mobile ~40); past-cap actively removeAllRanges (passive collapse silently truncates). - D4: streaming tail no-worse-than-baseline; node-stable render deferred. - D5: copy fidelity via copy-event interception deferred; scoped honestly (cloneContents for partial rows; slice(0,1000) needs per-renderer cooperation). Artifacts: proposal, design, specs (new chat-selection-preservation + modified chat-scroll-lock, chat-transcript-virtualization), tasks. openspec validate passes.
…ange Grounded diagnosis of 'Gemini 2.5 Pro unusable with subagents': not an access/auth/tool-schema/model-proxy problem (all refuted live). Root cause captured via dashboard-spawn D3 repro: Gemini returns a thinking-only turn (all output tokens reasoning, 0 visible text, no tool call, stopReason=stop, no error); pi/dashboard idles it silently -> 'no response, nothing reported'. Artifacts (4/4, validated): - proposal.md: primary defect = empty-actionable-turn guard; error-surfacing complementary; tool-schema sanitizer dropped (parametersJsonSchema accepts anyOf/const/format). - design.md: D3 capture, continue-or-surface guard, upstream adapter-vs-Gemini open question. - specs/empty-actionable-turn-guard, specs/spawned-session-error-surfacing. - tasks.md: TDD, grounded in bridge.ts stopReason branch + drainFollowupQueue.
Model proxy cannot resolve slash-bearing model ids: split("/", 2)
truncates at the first slash, so openrouter/anthropic/claude-3.5-sonnet
404s despite being advertised by /v1/models. Also adds a preferred-model
mechanism (none exists beyond a single defaultModel fallback).
Change captures:
- fix: first-slash parse via shared parseModelId helper + round-trip invariant
- deterministic source precedence (dedup by fqid; built-in > custom > models.json)
- new modelProxy.preferredModels (ordered availability fallback) + modelAliases
- Settings UI reusing ModelSelector for model picking (+ mockup)
Artifacts only (proposal/design/spec-delta/tasks/mockup); no impl.
- Fold the automatable slice of the task 10.1 perf gate into the windowing test: a 120-turn session measures 179 transcript DOM nodes vs the 46,918 Step-A baseline (< 3000 ceiling), proving nodes track the viewport working set, not session length (design Decision 7). GC/heap/listener/idle-busy remain a manual DevTools trace. - De-flake "streaming tail stays mounted": replace the single-snapshot live-count read with a retrying toBeAttached — the .chat-stream-live class briefly drops at 120-turn boundaries; the invariant is that scrolling up never windows the live tail away. Verified 3/3 under --repeat-each. Full suite 11 passed via PW_CHANNEL=chrome. tsc + biome clean.
…pecs Move the completed change to archive/2026-07-12-virtualize-chat-transcript-tanstack and sync the chat-transcript-virtualization capability spec to the Step-B (windowing) requirements: mounted-node/listener bound, scroll-lock preserved, off-screen scrollToTurn top-align, streaming-tail pinning, per-session restore. Remaining perf-trace (GC/heap/listeners/idle) + manual smoke tasks are deferred (to be verified later); all browser-testable behavior is covered by the e2e suite.
…ckBeltTechnology#273) * docs(openspec): fold doubt-review findings into scroll-to-top design Cross-model (deepseek-v4-pro) adversarial review + source verification: - scrollToIndex(0) is bounded (maxAttempts=10, not infinite); re-issue on async img.onload so a late image-load remeasure can't bump off index 0 - guard the scroll-to-top re-arm race with an ascendingRef latch so handleScroll can't flip stickToBottomRef back on mid-flight - image reserve is per-renderer-kind (300px user / 512px toolResult), not one global constant - Decision 1 constants gated on the required Playwright e2e convergence gate; jsdom demoted to logic-only (shim can't reproduce the timing race) - mechanism note: stale-offset story inferred, not version-pinned * fix(chat): content-aware row estimate + scroll-to-top affordance Scrolling up a virtualized transcript never converged on index 0: the largest rows (pasted image, 24k-char tool result) sit near the top and the static per-role estimate under-shot them 10-50x, so on mount getTotalSize() jumped and the top receded. - chat-virtual-rows.ts: estimateVirtualRowSize is now content-aware (base + ceil(textChars/80)*20, clamped 8000px, + per-kind image reserve 300 user / 512 toolResult); computeRowTextChars aggregates rendered text once per displayRows rebuild so estimateSize stays O(1). Shrinking the estimate delta shrinks TanStack's built-in above-viewport correction below perception. - ChatView.tsx: precompute rowTextChars in the displayRows useMemo; add a scroll-to-top button (ascendingRef latch). scrollToIndex(0) is bounded (maxAttempts=10), so onChange re-issues it on measurement growth (covers async image-load remeasure); handleScroll ascending branch prevents the bottom-pin re-arm race; cancelDescent clears the latch on user input. No manual scrollTop+=delta (would double-move); overflowAnchor:none kept. - Tests: unit (monotonic estimate, per-kind image reserve, aggregate textChars) + scroll-to-top logic guards (button visibility, re-arm race). Browser-timing convergence is Playwright-gated (jsdom shim can't reproduce it). See change: fix-chat-scroll-to-top-estimate-drift * test(e2e): scroll-to-top convergence gate + top-heavy faux fixture The required browser-timing gate jsdom cannot provide (doubt-review #4/#5): - qa/fixtures/faux-scenarios.ts: buildScrollTopHeavy() + scroll-top-heavy scenario — biggest rows (16k thinking, 9k text, 24k bash toolResult, inline image) near the TOP, then ~40 small turns. Exported SCROLL_TOP_HEAVY_TAIL. - tests/e2e/scroll-to-top.spec.ts: 2 tests via [[faux:scroll-top-heavy]] — (1) scroll-to-top lands scrollTop<=2 and STAYS after the near-top image loads async (the bounded-scrollToIndex re-issue path); (2) incremental scroll-up converges on scrollTop<=2 + [data-index=0] visible (top does not recede). test.slow() (180s) for the ~40 real bash round-trips. - helpers: scrollToTop testid. Verified 2/2 green against the Docker harness. Test 2 fails if the top recedes (the pre-fix bug); test 1 fails without the new button — the gate is not vacuous. See change: fix-chat-scroll-to-top-estimate-drift * chore(openspec): archive fix-chat-scroll-to-top-estimate-drift Sync delta specs into openspec/specs/chat-transcript-virtualization. QA/manual tasks were validated via the Playwright e2e gate + existing virtualization suite. * test: add clamp-boundary + empty-payload edge cases (CodeRabbit) Covers exact text-reserve clamp boundary + saturation above it, and 0-length message/group/burst payloads for the O(1) sizing contract.
…G via electron-builder (BlackBeltTechnology#277) Rewire build_native() to mirror CI (_electron-build.yml): darwin uses electron-forge package -> electron-builder --mac dmg --prepackaged (with CSC_IDENTITY_AUTO_DISCOVERY=false); linux uses forge make -> electron-builder --linux AppImage. Fixes the hard failure where npm run electron:build could not produce a macOS DMG. Removes obsolete macos-alias plumbing (ensure-macos-alias postinstall, maker-dmg devDep, Doctor row + test) since electron-builder's DMG target uses hdiutil, not macos-alias. Adds tests/e2e-electron/dmg-build-launch.electron.spec.ts (Playwright _electron) that mounts the built DMG and asserts the app launches + dashboard opens. Implements OpenSpec change fix-local-electron-dmg-build. Archives + syncs specs. QA/manual tasks (arm64 --mac-both) deferred to post-merge CI verification.
…c decode (BlackBeltTechnology#267) (BlackBeltTechnology#275) * fix(fix-virtualized-image-row-measurement): re-measure image rows on decode Image-bearing user rows in the virtualized transcript could collapse and overlap their neighbour (issue BlackBeltTechnology#267): a base64 data-URL decodes async, so a row is first measured near-zero and, when the decode-driven ResizeObserver re-measure is missed (ChatView reuse across session switch), stays cached at the collapsed height. - ImageAttachments: onImageLoad re-measures the owning virtual row via virtualizer.measureElement, coalesced to one measure per row per frame. - Reserve a bounded loading box on the <img> so the pre-decode measurement is not near-zero (dropped once decoded). - estimateVirtualRowSize: taller pre-mount estimate for image-bearing user rows to bound first-paint drift (measurement stays authoritative). Archives + syncs specs. QA/manual tasks (5.1/5.2) deferred to post-merge. * merge develop: drop superseded estimate edit, keep onLoad re-measure develop PR BlackBeltTechnology#273 rewrote estimateVirtualRowSize into a content-aware form (estimateVirtualRowSize(item, textChars)) that already reserves IMAGE_RESERVE_USER=300 for image rows. Take develop's chat-virtual-rows.ts wholesale — our interim task-3.1 estimate bump is superseded and its textual auto-merge broke the new baseRowSize(role) scope (TS2304 msg). The load-bearing fix (ImageAttachments onLoad -> coalesced row re-measure + reserved loading box in ChatView.tsx) is independent of the estimate rewrite and preserved. tsc clean; full client suite 3195 green. * fix: apply CodeRabbit feedback for fix-virtualized-image-row-measurement - Add onError-path test: a failed image decode must not schedule a re-measure. - Archived design.md: convert the ASCII decode-sequence to a Mermaid sequenceDiagram (repo Diagram Style rule).
…time (BlackBeltTechnology#276) CopyButton now takes a getText callback resolved at click time instead of an eager text string bound at render, so ref-derived payloads (tables, code blocks, message body) copy real content even under React.memo single-render. Migrates all call sites. Adds click-level unit tests + a Playwright e2e (copy-surfaces faux scenario, real clipboard) automating the former manual QA. Implements OpenSpec change fix-table-copy-empty-clipboard. Archives + syncs specs. Repairs pre-existing structural corruption in openspec/specs/content-copy/spec.md (leaked delta header + missing Purpose section) to allow the sync.
…s from payload (no file read) (BlackBeltTechnology#346) * plan(bundle-python-runtime): doubt-reviewed proposal+design+specs+tasks Planning artifacts for bundling a pip-capable Python env (uv + python-build-standalone, Tier 2) into the Electron bundle, exposed on PATH for agent bash/tool calls all platforms. Doubt-review (single + cross-model GLM) corrected 13 findings, notably: - D5 rewritten: sync-inject + bare-interpreter fallback (async materialize can't be awaited in the synchronous spawn-env seam) - UV_* scoped to materialize subprocess; SSL_CERT_FILE -> interpreter CA (pip certs) - Windows rebuild-vs-live-install via versioned overlay dirs - offline base rebuild = online/warm-cache only (Tier 2 non-goal) scenario-design gate resolved 2 spec gaps (offline pip = pip's own exit/stderr passthrough; small hash-pinned starter baseline). 24 automated scenarios folded to tasks (test-plan.md manifest), 0 manual-only. #9 uv file:// mirror layout gated behind a blocking spike. * docs(security): plan add-universal-network-guard (doubt-review + scenario-design) plan-proposal on develop. Added design.md + test-plan.md; revised proposal/ specs/tasks after a 2-cycle doubt-driven-review (single-model + cross-model @propose-review-1 GLM, @propose-review-2 DeepSeek). Design pivoted: the first draft's "deny-all + enumerate a public allowlist" was shown unimplementable (hashed static assets rooted at /, SPA history-fallback via setNotFoundHandler) and would brick the app shell with auth off over a tunnel. Revised to a namespace-scoped guard: jurisdiction = /api,/v1,/editor,/live, deny-by-default within, everything else (static/SPA/auth/manifest) untouched. Reviewer-found + reconciled: - /v1 proxy brick: proxy gate must set request.isAuthenticated; guard registered last (after bearer/oauth/proxy hooks). - /auth/status + SPA shell + deep-link refresh must stay public (now out of jurisdiction by construction). - model-proxy SECOND port (server.ts:1875) is a separate Fastify instance with only the proxy gate -> loopback-bind invariant, test-asserted. - proposal/design contradiction (ws-ticket mint) + phantom service worker fixed. scenario-design: test-plan.md manifest, 18 automated (17 L1 + 1 L3) + 1 manual-only, folded into tasks.md as vanilla checkboxes with exemplar+Triple+ manifest ref. openspec validate --strict passes; all 4 artifacts done. * plan(openspec-artifact-dialog-desktop): proposal+design+specs+tasks+test-plan Desktop-viewport modal for P/D/S/T artifact badges; mobile keeps full-page route. Planned via plan-proposal: 2 doubt-review cycles (single+cross-model), scenario-design manifest (20 automated + 1 manual), folded to tasks. * plan(cloud-sync): add-cloud-sync-connector planning artifacts Bidirectional local↔cloud file sync connector (Drive/Dropbox/OneDrive). Baseline-ledger engine, held-conflict resolution, provider adapter seam. - proposal.md: scope, safety posture (lossless+faithful), pinned decisions - design.md: impossibility triangle, 8 decisions, 3 engine invariants, pressure-tested Provider interface (7 verbs) - specs/cloud-file-sync/spec.md: 42-scenario completeness matrix (checkable total-coverage backbone), N=3 hot-edit defer, K=10 tombstone window - test-plan.md: manifest, 45 automated (L1/L2) + 2 manual-only, functional-only - tasks.md: 68 tasks, folded automated scenarios (parser-safe vanilla checkboxes) Doubt-reviewed 3 cycles (single + cross-model glm-5.2): fixed 4 data-loss paths (conflict→push re-overwrite, pull pre-write overwrite, native-doc push, resolve pull-inversion). * docs(openspec): shelve close-initialize-handoff-via-bus (never implemented) Doubt-review (single-model + GLM-5.2 cross-model) found the design incoherent with the shipped server, defeating its own no-click goal via the TOFU trust resolution, and resting on a wrong value premise: - No bus primitive awaits worktree_init_done (keyed by requestId+cwd, not a session status); plain spawn({cwd}) doesn't fire the hook and re-launches project-init. until()/await()/read cannot observe completion. - TOFU trust gate: auto-confirming a just-written hook bypasses the human gate and poisons worktree-init-trust.json, so the next manual click skips TOFU. - Premise wrong: project-init's worktreeInit hook is ~npm ci; kb-index is the kb-extension's separate hook; openspec-init already runs inline at Step 6. - connect() has no timeout arg; connect-failed is never thrown -> bounded degradation is unimplementable as designed. Archived pre-implementation with NOTES.md capturing findings + revival prerequisites so it is not re-proposed. * feat(session-diff): opt-in out-of-cwd session diffs (payload-only, no file read) Render diffs for files a session writes OUTSIDE its workspace from the captured Write/Edit event payload — the server never reads the out-of-cwd file (the doubt-review's core property). Server: - session-diff.ts: `resolvePathKey` carries out-of-cwd Write/Edit entries keyed by absolute path; `buildSessionDiff` splits in-cwd (enriched) vs out-of-cwd (payload-only, previewable:false) BEFORE enrichment so an out-of-cwd path can never reach the untracked `readFileSync(resolve(cwd,absPath))` branch. Threads `toolCallId` + `truncated` onto FileChangeEvent. - session-file-reader.ts: `findSessionToolCallPayload(file, toolCallId)` returns the FULL untruncated Write/Edit payload from the on-disk JSONL (session-scoped id only, no path input/traversal). - routes: `GET /api/session-change/:sessionId/:toolCallId` → { content?, edits? } resolved via sessionManager.sessionFile; miss → 404, reads nothing else. - preferences-store: backfill + merge `showOutOfCwdSessionDiffs` (default off). Client: - DisplayPrefs.showOutOfCwdSessionDiffs (default off, all presets) + ChatViewMenu toggle. ChatView suppresses out-of-cwd rows unless on. - DiffPanel: hide the File toggle when previewable:false; lazy-fetch full payload on truncation; truncation banner on fetch failure (never blank). - diff-tree: out-of-cwd entries group under "outside workspace" (no blank-root). - normalize-path: `isOutOfCwd`. Tests: L1 E1-E8 + X3 + truncation threading (session-diff/reader/routes/prefs); unit F4 (tree) + isOutOfCwd + F5/F3/X2 (DiffPanel); L3 e2e F1/F2/F5 + API-level F3 (out-of-cwd-session-diffs.spec.ts, harness-verified). X1 superseded by the payload-only design (never reads the file → deletion is moot). OpenSpec: opt-in-out-of-cwd-session-diffs * chore(opt-in-out-of-cwd-session-diffs): archive + sync specs Archives the change to openspec/changes/archive/ and syncs the change-summary-table + session-diff-extraction delta specs into openspec/specs/. Manual-only QA task deferred to post-merge verification. * fix: apply CodeRabbit feedback for opt-in-out-of-cwd-session-diffs - DiffPanel: accept the lazy-fetch payload only when it carries a string `content` or an `edits` array ({success:true,data:{}} → truncation banner, not a silent blank); reset viewMode to Diff when a refresh flips an entry to out-of-cwd (previewable:false) in File mode so the /api/session-file fetch (403 for out-of-cwd) can never fire. - Tests: F3 now asserts the fetched full content REPLACES the truncated text in the render (RichDiff mock exposes newText) + a previewable→false rerender test; isOutOfCwd(undefined-cwd) case; E3 also asserts no existsSync/statSync probe of the out-of-cwd path; nested-toolCallId test uses a distinct decoy top-level id. Skipped (with reason): archive-dir relocation (the change is correctly archived by the ship workflow); oversized AGENTS.md row splits (pre-existing giant rows — out of scope); e2e session-scan/marker refinements (reliable as-is on a fresh container, endpoint content assertion already proves full fidelity).
# Conflicts: # CHANGELOG.md # docs/AGENTS.md # package-lock.json # packages/client/src/components/ChatView.tsx # packages/client/src/components/ChatViewMenu.tsx # packages/client/src/components/ChatViewMenu.tsx.AGENTS.md # packages/client/src/components/CommandInput.tsx # packages/client/src/components/SessionCard.tsx # packages/client/src/components/SettingsPanel.tsx # packages/client/src/components/ToolCallStep.tsx # packages/client/src/components/__tests__/ChatViewMenu.flip.test.tsx # packages/client/src/components/interactive-renderers/SelectRenderer.tsx # packages/client/src/hooks/AGENTS.md # packages/client/src/hooks/usePopoverFlip.ts # packages/client/src/lib/AGENTS.md # packages/extension/src/__tests__/role-manager.test.ts # packages/extension/src/bridge.ts # packages/extension/src/role-manager.ts # packages/roles-plugin/src/RolesSettingsSection.tsx # packages/roles-plugin/src/__tests__/RolesSettingsSection.test.tsx # packages/server/package.json # packages/server/src/AGENTS.md # packages/server/src/event-wiring.ts # packages/server/src/routes/system-routes.ts # packages/server/src/server.ts
Author
|
Superseded by the latest automated upstream sync on |
1 similar comment
Author
|
Superseded by the latest automated upstream sync on |
added 7 commits
July 16, 2026 16:46
Add scripts/upstream-sync.sh (status/merge/verify/pr), docs policy, weekly upstream-sync workflow, and ci-zge focused gates so we can merge BlackBelt develop without clobbering deploy/push/OMP surfaces. Default install ref is main; README links the sync runbook.
Use stable branch sync/upstream-<ref>, force-with-lease each run, upsert the same PR, and close older labeled/sync-head PRs as superseded so a late review only has the latest merge to look at.
Gate 1 invoked vitest with real HOME, which upstream globalSetup rejects. Match root npm test isolation and pass package vitest.config.
3 tasks
joeshull
force-pushed
the
sync/upstream-develop
branch
from
July 16, 2026 22:54
44f0cc1 to
0ea9cee
Compare
joeshull
marked this pull request as ready for review
July 16, 2026 22:54
added 5 commits
July 16, 2026 16:56
Quote PR body heredoc to avoid backtick command substitution; refuse force-with-lease on main/develop/target; recompute SYNC_BRANCH after --ref; simplify workflow conflict-help echoes.
Precedence: --branch > env SYNC_BRANCH > sync/upstream-<ref>.
Capture SYNC_BRANCH_FROM_ENV before applying the default so an explicit SYNC_BRANCH equal to the default name still wins over --ref.
Copy docs/upstream-sync-conflicts-YYYYMMDD.md to /tmp before abort and restore onto the stable sync branch so the draft PR includes inventory.
Prevent SYNC_BRANCH=foo from force-publishing a non-sync head that stale-PR closing would never supersede.
Gateway already emits structured errors for unknown plugin actions; handle them in useMessageHandler so they are not silently dropped.
Author
|
Addressed review: client now toasts plugin_action_error (unknown pluginId action) instead of silent drop. |
This was referenced Jul 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstream sync (latest only)
Automation replaces any previous open sync PR. Review this one when ready; older sync PRs are closed as superseded.
Protected ZGE surfaces (must remain green)
Policy
Gates
Docs: docs/upstream-sync.md