Merge upstream v0.7.3 into the fork - #13
Merged
Merged
Conversation
A host that caps MCP tool-call duration backgrounds the call past its cap and hands back a task id of its own. Feeding that id to bash(id=...) can only fail, and the error named neither the id-space nor a next move: "unknown shell session: <id>". Read the managed_bash rows this MCP process already tracks, name the live handles, and flag when the unknown id has the shape of a host background-task id (use TaskStop/TaskOutput instead). Hint only: the shape check never rejects, so an unrecognised id fails exactly as before with the same result. Ports lemoncrow-lab#48 (wongk). Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01HMn8KmxZia7saNMuLJyBqy Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
_acquire_smart_state_flock took fcntl.flock(LOCK_EX) with no timeout on a lock file shared by every MCP process and daemon on the host. One stalled holder parks every tool call in every other process behind it, with no error and no deadline, even though the parked call has already done its real work and is queued only to record an advisory counter. Poll LOCK_NB to a deadline (5s, tunable via LEMONCROW_SMART_STATE_LOCK_TIMEOUT) with exponential backoff, and give up rather than wait forever. Giving up degrades to the same unsynchronised path non-POSIX platforms already take. Ports lemoncrow-lab#47 (wongk). Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01HMn8KmxZia7saNMuLJyBqy Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
edit never learned a session's git worktree cwd (only bash calls carry an explicit cwd), so a relative edit path resolved against the wrong root. The mis-resolved path still passed write-confinement (still under the workspace root), so the call silently wrote to and reported success against a file the caller never named; a blind retry then applied the same edit twice. Record the cwd of bash calls in the dispatcher, and resolve relative edit paths against the linked worktree it names, detected via the .git-file gitdir: pointer (no git spawned). Every uncertain case — no recorded cwd, a plain directory, a worktree of another repo — returns None and resolution is byte-for-byte what it was. Because the redirect is an inference it is never silent: resolved_against rides the result. Ports lemoncrow-lab#46 (wongk). Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01HMn8KmxZia7saNMuLJyBqy Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
src/lemoncrow/pro is no longer withheld from the public mirror. It ships as readable source under PolyForm Noncommercial 1.0.0 (free for personal, study, research, education, charity and government use; commercial use needs a separate license). Everything else stays Apache-2.0. - release/public-paths.txt: drop the !src/lemoncrow/pro deny - hatch_build.py: drop the pro .py leak guard; the remaining wheel check is only "no .so/.py twin" (_assert_no_source_leak -> _assert_no_duplicate_source) - new src/lemoncrow/pro/LICENSE, force-included in every wheel - LICENSE/NOTICE/README/CONTRIBUTING/docs: state the split; SPDX is "Apache-2.0 AND PolyForm-Noncommercial-1.0.0" Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
An incremental run applies only the paths each new commit touched, so removing the !src/lemoncrow/pro deny published just the 5 pro files that happened to change in that commit -- the public tree kept a fragment of the subtree. --resync rebuilds the full filtered tree and lands the difference as one fast-forward commit (--force still works, but rewrites every public SHA). Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
Publishes every path the allowlist now allows. Incremental mirroring only applies the paths each commit touched, so files that became public without changing were missing from the public tree.
The Makefile only passed FORCE_ARG, so the documented `make mirror ARGS="--resync"` silently ran a plain incremental mirror. Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
Removing the `!src/lemoncrow/pro` deny left `src/` allowed wholesale with no check on what the allowlist would actually publish, and the wheel-side guard it replaced only ever protected pro source. Two tests now run the real release/public-paths.txt over the real tracked tree: private trees (services/, tools/, deploy/, signatures/, ...) must stay unpublishable, and no file inside a public path may contain credential-shaped content. Verified against planted controls (an AKIA literal under src/). Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
The engine stays published; only the PolyForm Noncommercial carve-out for src/lemoncrow/pro is reverted. Guarding against a commercial fork is not worth excluding every commercial adopter of a project that needs adoption first, and tightening later is still available while the reverse is not. Restores LICENSE/NOTICE/CONTRIBUTING/docs/legal to their single-license form, drops src/lemoncrow/pro/LICENSE and its wheel force-include, and puts pyproject back to "Apache-2.0". Kept from the split work: mirror --resync, the public-tree guard tests, `make mirror ARGS=`, and the simplified wheel check. Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
States the positioning explicitly: the ecosystem optimizes one hop of the agent loop each and benchmarks itself on that hop, which mostly relocates cost rather than removing it. LemonCrow owns every hop and reports dollars per completed task over whole runs. Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
lc team is real but local-only (no sync, and sso.py has no CLI surface), so the previous line overclaimed shared workspaces and SSO. States what ships locally, and that the hosted service is separate and paid. Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
lc team is real but local-only (no sync, and sso.py has no CLI surface), so the previous line overclaimed shared workspaces and SSO. States what ships locally, and that the hosted service is separate and paid. Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
The site still sells Lite/Pro/Enterprise through a live Stripe checkout while the runtime grants every feature to everyone, so a paying customer receives what a free user receives. Records the decisions needed, the open-client / paid-service shape, the concrete file edits, and draft copy. Nothing implemented; landing lane 1 (false open-core and account claims) shipped in the submodule as 9447a8e. Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
First column of both Terminal-Bench 2.1 rows now links directly to the run's Harbor Hub job (Opus 4.8 matched / Opus 5 standalone). Shortens the results-table footnote, dropping the inline Harbor URLs now redundant with the row links. Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01HMn8KmxZia7saNMuLJyBqy Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
- Cut restated mechanism list in the tagline (already said one line up). - Drop the now-redundant third BENCHMARKS.md mention in Results; keep the unique docs/benchmarks/results.md pointer. - Merge the Cursor CLI/IDE caveat and its inference-caveat NOTE into one paragraph; fixes a missing blank line that ran straight into the next heading. - Fix typos and tighten the ChatGPT persistent-connection quirk note. - Fix typos and tighten "Why I built this"; same claims, same numbers, same links. Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01HMn8KmxZia7saNMuLJyBqy Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
Co-Authored-By: WOZCODE <contact@withwoz.com> Claude-Session: https://claude.ai/code/session_01MD4crunrTerM1GRLvMVw1t Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
Plan Mode gates independently of permissions.allow and only auto-passes Claude Code's built-in read tools, so mcp__lc__read & co. prompted on every call. A PreToolUse hook returning permissionDecision:allow is the only thing that suppresses it in every mode. Scoped to lookup-only tools; edit/bash/sql/ codemod/memory/verify stay gated, and the op-dispatcher 'tool' is excluded. Refs lemoncrow-lab#45
The account push (maybe_report_usage) had exactly one background caller: the savings reconciler thread started by the API service. On a release install the stack supervisor died before spawning uvicorn -- its frontend preflight demands a package.json, but the tarball ships a prebuilt bundle -- so nothing ever pushed and lemoncrow.com/savings only advanced on 'account login'. - stack: accept prebuilt bundles; serve them via a new stdlib static server with SPA fallback and /api proxying, no npm/vite needed - servicectl tick: push usage too, so the always-on controller is a real second channel; surface last_usage_report_at in status - worker run-once: add --json, the flag the tick's drain loop already passed (queue never drained; a retention_cleanup job sat pending for a month) - servicectl run: self-register its pidfile so status/stop see controllers started directly by launchd/systemd Refs lemoncrow-lab#44
Session/host/skill ids arriving from HTTP routes reached the store as raw path segments, so a crafted id could escape the store root. safe_segment() accepts one traversal-free segment and raises rather than sanitising; find/flat session lookups degrade to 'no such session'. Committed on the author's behalf from a concurrent session.
'with _Server(...)' aborted the release wheel with an internal mypyc error -- it cannot resolve the __enter__ inherited from socketserver.BaseServer. The ubuntu-arm job failed and cancelled the whole v0.7.3 matrix.
mypyc rejects nested class definitions, so the closure-based handler factory failed the release wheel. The handler is now module level and takes its directory + proxy target as __init__ keywords bound via functools.partial. Verified by compiling the module with mypyc directly, not just mypy.
Brings in v0.7.1, v0.7.2 and v0.7.3, including the three fixes we upstreamed (worktree edit paths, bounded smart_state flock, unknown-session hint), which now arrive as upstream's own commits. Upstream relicensed the whole tree Apache-2.0 and published src/lemoncrow/pro, so 276 engine source files are tracked here for the first time. The local .git/info/exclude workaround that hid that directory is removed -- it was what made the first merge attempt silently skip every one of those files. The compiled .so artifacts stay ignored via .gitignore's own *.so rule. Conflicts, both in code we had upstreamed: - tests/gateway/test_mcp_tool_handlers.py: took upstream's flock test. Ours asserted only elapsed time, which passes against the unfixed blocking implementation because BlockingIOError is an OSError and the broad handler swallows it. Upstream's asserts LOCK_NB on every attempt and that polling actually retries. - src/lemoncrow/gateway/adapters/mcp_server.py: kept our edit-phase timing block, which is fork-local and has its own coverage; upstream never carried it because it was stripped from the flock PR as unrelated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ckb7QXjUgcjvaMwY6TcyEH Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
| if initial_pub_parent is None: | ||
| print("ERROR: --resync needs an existing public HEAD (no watermark found).", file=sys.stderr) | ||
| return 1 | ||
| index_path = Path(tempfile.mktemp(prefix="lemoncrow-mirror-index-")) |
|
|
||
|
|
||
| def _widen_query(query: str) -> str: | ||
| without_quotes = re.sub(r"(['\"]).*?\1", " ", query.lower()) |
| """ | ||
| key = str(path) | ||
| try: | ||
| st = path.stat() |
| if cached is not None and cached[0] == stamp: | ||
| return cached[1] | ||
| try: | ||
| parsed = json.loads(path.read_text("utf-8")) |
v0.7.3's safe_segment() (upstream 9365c48) validates path segments against [A-Za-z0-9][A-Za-z0-9._-]{0,127}, and session_dir() runs the host through it. Agent identifiers are namespaced with a colon -- lemoncrow:code -- and host is a literal directory segment (sessions/YYYY/MM/DD/<host>/<session_id>/), so every MCP tool call died with "invalid host: 'lemoncrow:code'" and the server was completely unusable. install_claude.sh seeds that identifier itself, so a stock install reaches the broken path. The traversal fix was aimed at ids arriving from HTTP routes, but it was wired into the shared session_dir() helper that every internal caller also goes through. Admitting ':' restores them without weakening the guard: a colon cannot escape a directory, and '/', '.', '..', NUL, spaces and absolute prefixes stay rejected. The regression test pins both halves at once, beside the existing traversal cases, so a future tightening cannot silently take the colon back out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ckb7QXjUgcjvaMwY6TcyEH Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Merges upstream
v0.7.1,v0.7.2andv0.7.3.Our three fixes are now upstream
All three PRs we opened were merged and arrive here as upstream's own commits:
111d8e466993646c01426169mcp_server.pyandtest_edit_mcp_handler.pymerged to content identical to ourmain, which confirms the worktree fix went upstream verbatim.The engine source is now public
Upstream relicensed the whole tree Apache-2.0 (
b943e1b7thenb9296d31) and publishedsrc/lemoncrow/pro. 276 engine source files are tracked in this repo for the first time, which retires the "checkout has no pro engine" ceiling we have worked around since v0.6.1.The local
.git/info/excludeentry hiding/src/lemoncrow/pro/has been removed. That entry was not merely stale — it silently caused the first merge attempt to skip all 276 files while reporting success, becausegit mergewill not stage paths that are ignored. The compiled.soartifacts remain ignored through.gitignore's own*.sorule, so nothing else changes.All 275 vendored
.pyfiles were verified byte-identical to upstream's before being brought under tracking, so this is a bookkeeping change with zero content churn.Conflicts, both in code we had upstreamed
tests/gateway/test_mcp_tool_handlers.py— took upstream's version. Our fork's flock test asserted only elapsed time, which passes against the unfixed blocking implementation:BlockingIOErroris anOSError, so the broad handler swallows it and the blocking version also returnsNoneimmediately. Upstream's version assertsLOCK_NBon every attempt and that polling actually retries. Taking upstream here back-ports the stronger test, closing a known gap in our own coverage.src/lemoncrow/gateway/adapters/mcp_server.py— kept ours. The three hunks are the fork-local edit-phase timing block, which upstream never carried because it was stripped from the flock PR as unrelated. It is still live and has its own coverage intest_edit_mcp_handler.py, so dropping it would have broken those tests.Verification
test_mcp_tool_handlers.py+test_edit_mcp_handler.py: 12 failed, 217 passed — an exact match, name for name, against pre-mergemainrunning the same engine. Zero merge regressions.test_mirror_denylist.py: 2 failures, bothFileNotFoundError: release/public-paths.txt. That file is absent fromupstream/mainas well, so these tests cannot pass on the public mirror — an upstream defect, not a merge regression.🤖 Generated with Claude Code
https://claude.ai/code/session_01Ckb7QXjUgcjvaMwY6TcyEH