Productize neural-mesh as a Samsarix release candidate - #2
Conversation
Summary by CodeRabbit
WalkthroughThe repository is converted into a typed, dependency-free Changesneural-mesh productization
Estimated code review effort: 4 (Complex) | ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 73-80: Update the wheel inspection and installation steps to
derive the artifact path from the built files or package metadata instead of
hardcoding neural_mesh-0.2.0-py3-none-any.whl. Reuse the derived path for both
python -m zipfile -l and pip install, while preserving the existing smoke-test
commands in the Install wheel outside the checkout step.
In @.github/workflows/release.yml:
- Around line 61-64: Document the sensitive permissions in the attestation and
publish jobs of the workflow: add concise inline rationale comments for each
id-token: write and attestations: write entry, including the publish job’s
id-token: write. Keep the existing permission values and scopes unchanged.
- Around line 26-30: Remove the cache: pip setting from the actions/setup-python
step in the release workflow, while retaining the Python version and
cache-dependency-path configuration only if still valid without caching. Do not
alter unrelated CI workflows or release build steps.
In `@neural_mesh/consensus.py`:
- Around line 723-738: Update _usage_record so task_sha256 is derived with a
per-installation secret via keyed HMAC rather than bare hashlib.sha256, using
the existing configuration or secret-management mechanism and preserving the
hexadecimal digest format expected by UsageRecord. Ensure the same stable secret
is reused across records while avoiding persistence of the raw task label.
- Around line 285-297: Move semaphore creation out of ConsensusEngine.__init__
and into run(), creating a fresh asyncio.Semaphore using
self.config.max_concurrency for each invocation. Update run() and any related
helpers to use that per-call semaphore, ensuring separate asyncio.run() event
loops never share the same semaphore.
In `@pyproject.toml`:
- Around line 100-104: Update the Ruff configuration in the [tool.ruff.lint]
section so the tests/** per-file-ignore for S101 is meaningful: either add the S
category to select if Bandit checks are intended, or remove the S101 ignore
entry if they are not.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: fd12bb47-9255-4dd9-93e8-5ba85faabdb7
📒 Files selected for processing (29)
.github/dependabot.yml.github/workflows/ci.yml.github/workflows/release.yml.gitignoreCHANGELOG.mdCONTRIBUTING.mdLICENSEMANIFEST.inREADME.mdROADMAP.mdSECURITY.mddocs/PRODUCTIZATION.mddocs/RELEASING.mdexamples/basic_consensus.pyneural_mesh/__init__.pyneural_mesh/__main__.pyneural_mesh/cli.pyneural_mesh/consensus.pyneural_mesh/multi_ai_consensus.pyneural_mesh/py.typedneural_mesh/usage.pypyproject.tomlrequirements-dev.txtrequirements.txtsetup.pytests/test_cli.pytests/test_consensus.pytests/test_public_api.pytests/test_usage.py
💤 Files with no reviewable changes (1)
- setup.py
| run: python -m zipfile -l dist/neural_mesh-0.2.0-py3-none-any.whl | ||
| - name: Install wheel outside the checkout | ||
| run: | | ||
| python -m venv "${RUNNER_TEMP}/neural-mesh-smoke" | ||
| "${RUNNER_TEMP}/neural-mesh-smoke/bin/python" -m pip install --no-deps dist/neural_mesh-0.2.0-py3-none-any.whl | ||
| cd "${RUNNER_TEMP}" | ||
| "${RUNNER_TEMP}/neural-mesh-smoke/bin/python" -c "import neural_mesh; print(neural_mesh.__version__)" | ||
| "${RUNNER_TEMP}/neural-mesh-smoke/bin/neural-mesh" --version |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Derive the wheel path instead of hardcoding 0.2.0.
The job builds the version declared by package metadata but inspects and installs only neural_mesh-0.2.0-py3-none-any.whl. The next version bump will make CI fail before validating the newly built artifact.
Use a metadata- or glob-derived wheel path for both inspection and installation.
As per the release workflow contract, package versions are read from pyproject.toml rather than fixed in automation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci.yml around lines 73 - 80, Update the wheel inspection
and installation steps to derive the artifact path from the built files or
package metadata instead of hardcoding neural_mesh-0.2.0-py3-none-any.whl. Reuse
the derived path for both python -m zipfile -l and pip install, while preserving
the existing smoke-test commands in the Install wheel outside the checkout step.
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | ||
| with: | ||
| python-version: "3.11" | ||
| cache: pip | ||
| cache-dependency-path: requirements-dev.txt |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/release.yml ]; then
nl -ba .github/workflows/release.yml | sed -n '1,220p'
else
echo ".github/workflows/release.yml not found"
fi
echo
echo "== all release workflow references =="
rg -n "setup-python|cache=|pip|build|attest|publish|permissions:" .github/workflows/release.yml || true
echo
echo "== zizmor references if any =="
rg -n "zizmor|cache|poison|pip cache" -S . || true
echo
echo "== git status/stat =="
git status --short
git diff --statRepository: Deathcharge/neural-mesh
Length of output: 1932
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== repo availability =="
pwd
git rev-parse --show-toplevel || true
git status --short 2>/dev/null || true
git ls-files 2>/dev/null | head -50 || true
echo
echo "== workflow excerpt =="
if [ -f .github/workflows/release.yml ]; then
nl -ba .github/workflows/release.yml | sed -n '1,220p'
else
echo ".github/workflows/release.yml not found"
fi
echo
echo "== setup/cache/build references in release workflow =="
rg -n "setup-python|cache=|pip|build|attest|publish|permissions:" .github/workflows/release.yml || true
echo
echo "== zizmor references in repo =="
rg -n "zizmor|cache poisoning|cache-poisoning|pip cache|cache-dependency-path" -S . || trueRepository: Deathcharge/neural-mesh
Length of output: 1932
🌐 Web query:
GitHub Actions pip cache poisoning setup-python cache cache-dependency-path cache key cache poisoning
💡 Result:
GitHub Actions cache poisoning occurs when an untrusted workflow (such as one triggered by a Pull Request) writes malicious or corrupted dependencies into the shared action cache [1][2]. Subsequent, more privileged workflows (like those on the main branch) may then restore this poisoned cache, potentially leading to unauthorized code execution [1][2]. For actions/setup-python, the cache input enables built-in caching for package managers like pip, pipenv, and poetry [3][4]. By default, this action calculates a cache key based on the hash of dependency files (e.g., requirements.txt or pyproject.toml) [3][5]. The cache-dependency-path input allows you to specify custom dependency files or directories to include in this hash, providing more granular control over what triggers a cache miss or hit [3][6]. To mitigate cache poisoning risks, follow these practices: 1. Use Read-Only Mode: When running workflows on untrusted code (e.g., Pull Requests), configure the action to use the cache without writing to it [7]. The actions/setup-python action supports a cache-write input (or similar mechanisms depending on the version) that can be set to false for PRs [7][2]. Example configuration: - uses: actions/setup-python@v6 with: python-version: "3.12" cache: pip cache-write: ${{ github.event_name!= 'pull_request' }} 2. Separate Caches: Ensure that sensitive, privileged workflows (like those that publish packages or artifacts) do not share write access to the same cache keys used by PR-triggered workflows [8][2]. 3. Validate Dependencies: Always review code changes in PRs before labeling them as safe for execution in environments where secrets or sensitive permissions are present [1]. By disabling cache writes on untrusted PRs, you prevent them from modifying the shared cache that the main branch relies on, effectively neutralizing the poisoning vector [7][2].
Citations:
- 1: https://codeql.github.com/codeql-query-help/actions/actions-cache-poisoning-direct-cache/
- 2: https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/cicd-github-cache-poisoning/
- 3: https://github.com/actions/setup-python
- 4: https://github.com/actions/setup-python/blob/main/README.md
- 5: https://github.com/actions/setup-python/blob/main/src/cache-distributions/pip-cache.ts
- 6: https://github.com/actions/setup-python/blob/main/docs/adrs/0000-caching-dependencies.md
- 7: Add cache-write input for read-only cache mode actions/setup-python#1284
- 8: https://docs.openrewrite.org/recipes/github/security/cachepoisoning
🌐 Web query:
actions/setup-python cache-write input version 7.0.0
💡 Result:
The actions/setup-python action does not currently have a cache-write input. While a pull request was proposed to add a cache-write input to support read-only cache mode [1][2], this feature has not been merged into the official release as of version 7.0.0 [3][4][5]. In version 7.0.0, which was released on July 20, 2026, the action continues to manage caching automatically based on the cache input and the cache-dependency-path configuration [3][5]. Recent updates in this version include migrating to ESM, removing the pip-install input, and upgrading the underlying @actions/cache dependency to version 6.2.0 [3]. If you are looking to control cache behavior in GitHub Actions to mitigate risks like cache poisoning, the current recommended practice remains using standard GitHub Actions caching patterns, and monitoring official repository updates for future changes to input parameters [1].
Citations:
- 1: Add cache-write input for read-only cache mode actions/setup-python#1284
- 2: https://github.com/actions/setup-python/pulls
- 3: https://github.com/actions/setup-python/releases/tag/v7.0.0
- 4: actions/setup-python@v6.2.0...v7.0.0
- 5: https://git.homeinfra.org/actions/setup-python/src/tag/v7.0.0/action.yml
🌐 Web query:
zizmor cache-poisoning setup-python cache pip cache-dependency-path
💡 Result:
zizmor is a static analysis tool for GitHub Actions that includes a specific audit rule called cache-poisoning [1][2]. This rule flags potential vulnerabilities where release workflows—typically triggered by events like release or push (tags)—leverage cached build states that could be manipulated by attackers to achieve unauthorized code execution during the release process [1]. When using actions/setup-python, enabling caching via cache: pip can trigger this finding because it creates a cache entry that might be persisted across different workflow runs [3][1]. Key points regarding the interaction between zizmor and actions/setup-python: 1. The Risk: An attacker with write access to the repository (e.g., via a compromised GITHUB_TOKEN or a malicious PR) could potentially poison the GitHub Actions cache [1]. If a release workflow subsequently restores and uses that poisoned cache to build or publish artifacts, it could result in supply chain compromises [3][1]. 2. Using cache-dependency-path: This input allows you to specify exactly which dependency files (e.g., requirements.txt, poetry.lock) dictate the cache key [4][5]. While cache-dependency-path is best practice for ensuring cache accuracy, it does not inherently prevent cache-poisoning [6]. 3. Remediation: To resolve zizmor cache-poisoning findings in release workflows, the primary strategy is to disable cache restoration when the workflow is running in a release context [1]. You can do this by: * Conditional Execution: Use an if condition on the step or job to skip caching when the event is a release or tag [1]. * Lookup-Only: For some actions, setting a flag like lookup-only (or equivalent) to true when a release/tag is detected can prevent the action from writing to the cache while still allowing it to look up existing entries [7]. * Disabling Caching: Simply remove caching entirely from workflows that produce official release artifacts [3][1]. If you are seeing a false positive (e.g., a release workflow that is already safely guarded), you can often suppress the finding by using appropriate conditional logic that zizmor understands, such as checking github.ref or github.event_name to explicitly disable caching during release events [7][1][8].
Citations:
- 1: https://docs.zizmor.sh/audits/
- 2: http://www.github.com/zizmorcore/zizmor
- 3: Add zizmor to pre-commit and fix potential cache-poisoning in wheels workflow python-pillow/Pillow#8638
- 4: https://github.com/actions/setup-python/blob/main/docs/adrs/0000-caching-dependencies.md
- 5: https://github.com/actions/setup-python/blob/a26af69b/README.md
- 6: chore(ci): harden GitHub Actions workflows with zizmor TRaSH-Guides/Guides#2740
- 7: Feature: cache-poisoning for heuristic for release event zizmorcore/zizmor#2071
- 8: [BUG]: zizmor currently flags maturin's generated workflow for
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}being vulnerable to a cache poisoning attack zizmorcore/zizmor#1940
Remove pip caching from the release artifact build.
cache: pip can restore dependency files whose cache key is not bound to this release commit, so the build may use poisoned cached packages before attestation/publishing. Keep caching in CI if needed, but disable it for this release workflow or enforce provenance/content hashes for cached dependency artifacts.
🧰 Tools
🪛 zizmor (1.28.0)
[error] 26-26: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): this step
(cache-poisoning)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 26 - 30, Remove the cache: pip
setting from the actions/setup-python step in the release workflow, while
retaining the Python version and cache-dependency-path configuration only if
still valid without caching. Do not alter unrelated CI workflows or release
build steps.
Source: Linters/SAST tools
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| attestations: write |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Document the sensitive release permissions.
Add inline rationale for id-token: write and attestations: write in the attestation job, and for id-token: write in the publish job. This makes future least-privilege reviews explicit and prevents accidental permission expansion.
As per static analysis, zizmor reported undocumented permissions at these locations.
Also applies to: 83-85
🧰 Tools
🪛 zizmor (1.28.0)
[warning] 62-62: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment
(undocumented-permissions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 61 - 64, Document the sensitive
permissions in the attestation and publish jobs of the workflow: add concise
inline rationale comments for each id-token: write and attestations: write
entry, including the publish job’s id-token: write. Keep the existing permission
values and scopes unchanged.
Source: Linters/SAST tools
| def __init__( | ||
| self, | ||
| providers: Sequence[Provider], | ||
| config: ConsensusConfig | None = None, | ||
| *, | ||
| usage_store: UsageStore | None = None, | ||
| ) -> None: | ||
| self.config = config or ConsensusConfig() | ||
| self._providers = tuple(providers) | ||
| self._usage_store = usage_store | ||
| self._provider_by_name = _validate_providers(self._providers, self.config) | ||
| self._semaphore = asyncio.Semaphore(self.config.max_concurrency) | ||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
asyncio Semaphore acquire lazy loop binding contended path Python 3.10 source
💡 Result:
In Python 3.10, the asyncio.Semaphore implementation manages its internal state primarily through an internal counter (self._value) and a deque of waiters (self._waiters) [1][2]. Regarding the specific mechanisms in the contended path: 1. Lazy Initialization: The _waiters deque is initialized eagerly in the init method for the standard Python 3.10 release (as seen in the relevant CPython source for that version) [1][2]. The pattern of "lazy initialization" (setting self._waiters = None and allocating it only upon the first contention) was introduced in later versions of Python (e.g., in the main development branch, GH-97545) to improve performance, but it was not present in the initial Python 3.10 stable release [3][4]. 2. Contended Path: When the semaphore is contended (i.e., self._value <= 0 or there are existing waiters), the acquire coroutine creates a new asyncio.Future, appends it to the self._waiters deque, and awaits it [1][2]. The loop continues to wait until the task is resumed. 3. Loop Binding: In Python 3.10, the loop parameter was deprecated and removed from the Semaphore constructor, meaning the semaphore binds to the running loop at the time of creation (or at the time of future creation) using asyncio.get_event_loop or similar internal mechanisms [5][1]. The internal logic relies on the loop's ability to schedule and resolve these futures [1][2]. 4. FIFO and Re-acquisition: Python 3.10 contains logic (specifically bpo-45997) designed to improve FIFO ordering and handle re-acquisition, often involving flags like _wakeup_scheduled to prevent race conditions where a task might be woken up but fail to acquire the semaphore before another task steals it [6][7]. In summary, for Python 3.10, the semaphore does not use lazy initialization for the waiter deque; it uses an eager initialization strategy, and the contended path involves managing a deque of futures that are awaited by blocked coroutines [1][2][3].
Citations:
- 1: https://github.com/python/cpython/blob/68526fe258da8c01196fd7cf48e8e5f1280bf8fd/Lib/asyncio/locks.py
- 2: https://github.com/python/cpython/blob/243fd01047ddce1a7eb0f99a49732d123e942c63/Lib/asyncio/locks.py
- 3: asyncio: Inconsistent lazy initialization of _waiters in Event and Condition python/cpython#148489
- 4: https://github.com/python/cpython/blob/main/Lib/asyncio/locks.py
- 5: https://docs.python.org/3.10/library/asyncio-sync.html
- 6: https://github.com/python/cpython/blob/eace09e63ed7978dbdfeb1ae537fac505e6b5b0e/Lib/asyncio/locks.py
- 7: python/cpython@32e7715
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^|/)consensus\.py$|pyproject\.toml|uv\.lock|poetry\.lock|requirements.*\.txt' || true
echo "== consensus outline =="
ast-grep outline neural_mesh/consensus.py --view expanded | sed -n '1,220p' || true
echo "== init and run slices =="
sed -n '260,345p' neural_mesh/consensus.py | cat -n
echo "== config/semaphore references =="
rg -n "max_concurrency|Semaphore|_semaphore|asyncio\.Semaphore|acquire\(" neural_mesh tests .github 2>/dev/null || true
echo "== event loop/runtime references =="
rg -n "asyncio\.run|create_task|async with .*Semaphore|_invoke_provider" neural_mesh tests 2>/dev/null || true
echo "== python sem sample source excerpt if installed =="
python3 - <<'PY'
import inspect, asyncio
print(asyncio.__version__ if hasattr(asyncio, '__version__') else "python", __import__('sys').version)
src = inspect.getsource(asyncio.Semaphore).__splitlines = src.splitlines()
for i,line in enumerate(src[:80], 1):
if "def _get_loop" in line or "_get_loop" in line or "create_future" in line or "Future(" in line:
print(src[max(0,i-4):i+5])
print("---")
PYRepository: Deathcharge/neural-mesh
Length of output: 1932
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== behavioral probe: Semaphore reused across asyncio.run calls under contention =="
python3 - <<'PY'
import asyncio, sys, traceback
sem = asyncio.Semaphore(1)
async def first_loop():
task = asyncio.create_task(sem.acquire())
await asyncio.sleep(0) # wait for loop binding/waiter future setup
return task
async def second_loop():
try:
await sem.acquire()
return "ok"
except Exception as exc:
return type(exc).__name__ + ": " + str(exc)
print(sys.version)
loop1 = asyncio.run(first_loop())
try:
loop1.cancel()
asyncio.run(loop1)
except asyncio.CancelledError:
pass
second = asyncio.run(second_loop())
print(second)
print("\ntwo runs both acquire without contention:")
for _ in range(3):
try:
asyncio.run(sem.acquire())
except RuntimeError as exc:
print("RuntimeError:", str(exc))
PYRepository: Deathcharge/neural-mesh
Length of output: 1932
🌐 Web query:
Python asyncio Semaphore RuntimeError is bound to a different event loop reuse across asyncio.run Semaphore acquire loop binding
💡 Result:
The RuntimeError: "is bound to a different event loop" occurs when an asyncio synchronization primitive (like asyncio.Semaphore, Lock, or Event) is instantiated outside of an active event loop, causing it to "bind" to a default or stale event loop [1][2][3]. When you later attempt to use that primitive within a new event loop—most commonly triggered by calling asyncio.run()—the primitive detects a mismatch between its bound loop and the current running loop, resulting in this error [1][2]. ### Why this happens Historically, asyncio primitives required a reference to an event loop upon initialization [1][3]. If no loop was explicitly provided, they would attempt to capture the current running loop using asyncio.get_event_loop() [1][4]. Because asyncio.run() creates a brand-new event loop instance every time it is called, any primitive initialized at the module level (before asyncio.run() is invoked) becomes incompatible with the new, ephemeral loop [1][2]. ### How to fix it The recommended solution is to ensure the Semaphore (or other primitive) is initialized only after the event loop has started [1][2]. 1. Initialize inside the entry point: Move the instantiation of the Semaphore inside your async main() function or the coroutine passed to asyncio.run() [2]. python async def main(): # Initialize inside the loop sem = asyncio.Semaphore(10) await sem.acquire() # ... asyncio.run(main()) 2. Pass the primitive as an argument: If you need to share the Semaphore across different tasks, initialize it within the main coroutine and pass it explicitly to other functions rather than relying on a global variable [2]. ### Note on Python 3.10+ In Python 3.10 and later, the loop parameter was removed from the constructors of asyncio synchronization primitives [5][6]. While this change simplified the API, the underlying requirement for primitives to be bound to a single, active event loop remains [3][7]. If you encounter this error in modern Python versions, it is almost certainly because the primitive was still instantiated before the event loop was available (e.g., at the module global scope) [2][3].
Citations:
- 1: https://stackoverflow.com/questions/55918048/asyncio-semaphore-runtimeerror-task-got-future-attached-to-a-different-loop
- 2: https://stackoverflow.com/questions/67152371/controlling-the-concurrency-of-http-requests-using-pythons-asyncio-semaphore
- 3: https://stackoverflow.com/questions/73599594/asyncio-works-in-python-3-10-but-not-in-python-3-8
- 4: https://github.com/python/cpython/blob/4f100fe9f1c691145e3fa959ef324646e303cdf3/Lib/asyncio/locks.py
- 5: https://github.com/python/cpython/blob/main/Doc/library/asyncio-sync.rst
- 6: https://docs.python.org/3/library/asyncio-sync.html
- 7: [document removal of] the deprecated 'loop' parameter asyncio API in 3.10 python/cpython#86558
Avoid reusing the semaphore across event loops.
self._semaphore is created once in __init__, before any run() call knows which event loop will execute it. If the same ConsensusEngine is used from separate asyncio.run() calls, its semaphore can fail when contention creates a waiting future for a different loop and raises RuntimeError: ... is bound to a different event loop. Create the semaphore inside run() so it is scoped to that call’s loop.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@neural_mesh/consensus.py` around lines 285 - 297, Move semaphore creation out
of ConsensusEngine.__init__ and into run(), creating a fresh asyncio.Semaphore
using self.config.max_concurrency for each invocation. Update run() and any
related helpers to use that per-call semaphore, ensuring separate asyncio.run()
event loops never share the same semaphore.
| def _usage_record(result: ConsensusResult) -> UsageRecord: | ||
| task_digest = hashlib.sha256(result.task.encode("utf-8")).hexdigest() | ||
| return UsageRecord( | ||
| timestamp=result.created_at, | ||
| task_sha256=task_digest, | ||
| agreement_level=result.agreement_level.value, | ||
| providers_queried=result.providers_queried, | ||
| providers_succeeded=result.providers_succeeded, | ||
| requested_max_tokens=result.requested_max_tokens, | ||
| reported_input_tokens=result.reported_input_tokens, | ||
| reported_output_tokens=result.reported_output_tokens, | ||
| token_reporting_complete=result.token_reporting_complete, | ||
| reported_cost_usd=result.reported_cost_usd, | ||
| cost_reporting_complete=result.cost_reporting_complete, | ||
| duration_ms=result.duration_ms, | ||
| ) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win
Unsalted task_sha256 weakens the "privacy-minimized" guarantee for low-entropy task labels.
task_sha256 is a bare hashlib.sha256 of the caller-supplied task string. Since task is a short free-form label (validated only up to 500 chars in _validate_request), an unsalted hash is trivially reversible via a rainbow table for any predictable/guessable set of task names, which undercuts the privacy goal stated in usage.py's UsageRecord docstring. Consider a keyed HMAC (with a per-installation secret) instead of a bare hash if the intent is to prevent recovery of the original task string from the persisted record.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@neural_mesh/consensus.py` around lines 723 - 738, Update _usage_record so
task_sha256 is derived with a per-installation secret via keyed HMAC rather than
bare hashlib.sha256, using the existing configuration or secret-management
mechanism and preserving the hexadecimal digest format expected by UsageRecord.
Ensure the same stable secret is reused across records while avoiding
persistence of the raw task label.
| [tool.ruff.lint] | ||
| select = ["B", "E", "F", "I", "RUF", "SIM", "UP"] | ||
|
|
||
| [tool.ruff.lint.per-file-ignores] | ||
| "tests/**" = ["S101"] |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Dead per-file-ignore: S101 has no effect.
select only includes ["B", "E", "F", "I", "RUF", "SIM", "UP"]; the S (flake8-bandit) category is never enabled, so the tests/** ignore for S101 is a no-op. Either add "S" to select (if assert-based bandit checks are actually wanted) or drop the now-meaningless ignore entry.
♻️ Option: enable S and keep the ignore meaningful
[tool.ruff.lint]
-select = ["B", "E", "F", "I", "RUF", "SIM", "UP"]
+select = ["B", "E", "F", "I", "RUF", "S", "SIM", "UP"]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| [tool.ruff.lint] | |
| select = ["B", "E", "F", "I", "RUF", "SIM", "UP"] | |
| [tool.ruff.lint.per-file-ignores] | |
| "tests/**" = ["S101"] | |
| [tool.ruff.lint] | |
| select = ["B", "E", "F", "I", "RUF", "S", "SIM", "UP"] | |
| [tool.ruff.lint.per-file-ignores] | |
| "tests/**" = ["S101"] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pyproject.toml` around lines 100 - 104, Update the Ruff configuration in the
[tool.ruff.lint] section so the tests/** per-file-ignore for S101 is meaningful:
either add the S category to select if Bandit checks are intended, or remove the
S101 ignore entry if they are not.
What changed
neural-meshCLI with version reporting and a credential-free offline demoSECURITY.md, a release runbook, and a commit-pinned trusted-publishing workflow with separate build, provenance, and protected OIDC publication jobsWhy
The previous repository was an incomplete extraction: it depended on private sibling code, declared unavailable and unrelated dependencies, did not ship runtime code in its wheel, and treated successful provider calls as consensus. This change turns it into an independently installable and auditable release candidate while keeping provider credentials, networking, retries, and persistence under explicit application control.
Developer and user impact
Consumers can install the package, supply ordinary sync or async provider adapters, receive inspectable per-provider outcomes, and run
neural-mesh demowithout credentials or network access. The package remains version0.2.0and is not published to PyPI by this PR. Publication still requires the protectedpypiGitHub environment, matching PyPI trusted-publisher coordinates, a signedv0.2.0tag, and explicit release approval.The checked-in license remains a custom Business Source License 1.1 variant. Its substantive production threshold, Change Date, and Change License were preserved; current identity fields now name
neural-meshand Samsarix LLC.Verification