Skip to content

sync: mirror from thyn-ai/codna@2b631dec6d3487d8d9c873d7215670ecc764e9ba - #40

Closed
0xamlab wants to merge 1 commit into
mainfrom
sync/2b631dec6d3487d8d9c873d7215670ecc764e9ba
Closed

0xamlab wants to merge 1 commit into
mainfrom
sync/2b631dec6d3487d8d9c873d7215670ecc764e9ba

Conversation

@0xamlab

@0xamlab 0xamlab commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Automated mirror of cli/ (flattened), the root allowlist (server.json, glama.json, LICENSE, .gitleaks.toml) and the public-root/ community tree from thyn-ai/codna at 2b631dec6d3487d8d9c873d7215670ecc764e9ba. Merges itself via auto-merge once the mirror's required checks (codna review, Analyze (python)) pass and the codna-ai review approves; if a later sync lands first, this PR is closed as superseded -- every sync PR is a full snapshot of the exported surface.

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

Thanks for opening your first pull request in the Codna CLI! Please make sure the checks are green and that the change follows CONTRIBUTING.md. A maintainer will review it shortly.

@codna-ai codna-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codna review — 3 finding(s): 3 inline, 0 in summary.

Reply @codna fix on a finding to have Codna open a verified, test-green, risk-gated fix PR.

⏸ Not approved: 3 medium/high finding(s) in this review.

Comment on lines +378 to +385
out = git_ok(
runtime,
tree,
["log", "--all", "--fixed-strings", "--format=%H", f"--grep={message}", "-n", "1"],
)
if not out or not out.strip():
return None
return out.strip().splitlines()[0].strip() or None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 HIGH · correctness — git log --grep substring match causes false idempotency hits

In _existing_commit, the commit message for turn N (e.g. "codna: turn run:task:1") is searched with git log --all --fixed-strings --grep=<message> -n 1. Since --grep performs a substring match (even with --fixed-strings), the message for turn 1 ("...task:1") is a prefix substring of the message for turn 10 ("...task:10"). If turn 10 has already been committed and turn 1 is retried by Temporal, _existing_commit will return turn 10's sha instead of None, causing checkpoint_turn to skip creating turn 1's real commit and instead assign turn 10's sha to turn 1's ref. This corrupts the audit trail: turn 1's ref points to turn 10's tree, and future operations build on the wrong commit.

Suggested change
out = git_ok(
runtime,
tree,
["log", "--all", "--fixed-strings", "--format=%H", f"--grep={message}", "-n", "1"],
)
if not out or not out.strip():
return None
return out.strip().splitlines()[0].strip() or None
To fix, verify the returned sha's commit message matches exactly: after finding a candidate sha, check `git log -1 --format=%s <sha>` equals the expected message. Alternatively, append a unique terminator to the message (e.g., a NUL byte or a suffix like `\n[codna-checkpoint]`) but that changes history format.

Reply @codna fix and Codna opens a verified, test-green, risk-gated fix PR.

Comment thread codna/durable/ledger.py
Comment on lines +284 to +307
def _reload_tail_under_lock(self) -> None:
"""Refresh the cached tail from the FILE, under the lock, before computing `seq`.

Called on every append. On the first call it is exactly `_ensure_loaded`; on later calls it is the
difference between a linear chain and a fork, because another instance may have appended since this one
loaded. The dedupe set is rebuilt too: an event this instance never saw may already be in the file, and
honouring the idempotency key is the other half of what the lock is for.

Deliberately re-reads rather than trusting in-memory state — the file is the source of truth and it is
shared. The cost is one sequential read of a local append-only file, which is what `_iter_records` already
does for the first load.
"""
last_hash = GENESIS_HASH
last_seq = 0
seen: set[tuple[str, int]] = set()
for record in _iter_records(self._path):
last_hash = record.hash
last_seq = record.seq
if record.session_id is not None and record.event_index is not None:
seen.add((record.session_id, record.event_index))
self._last_hash = last_hash
self._last_seq = last_seq
self._seen = seen
self._loaded = True

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM · performance — Full ledger file scan on every append causes O(n²) append cost

_reload_tail_under_lock reads the entire ledger file from the beginning on every single call to append(). For a long run with N ledger records, each append does O(N) work, making the overall append cost O(N²). For a run with thousands of turn events (a full LangGraph task can produce hundreds of events), this becomes significant: a 10,000-record ledger does 10,000 × full-scan on the last append. The comment acknowledges the full re-read but frames it as "one small tail read", which is incorrect — it re-reads from record 1 every time.

Reply @codna fix and Codna opens a verified, test-green, risk-gated fix PR.

Comment thread codna/config_file.py
Comment on lines +135 to +137
path = parts.path.rstrip("/")
netloc = parts.netloc
return f"{parts.scheme}://{netloc}{path}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM · security — URL reconstruction uses parts.netloc which preserves empty userinfo (@host)

In _validated_base_url, the credential check on line 131 tests parts.username or parts.password. For a URL like https://@host/path, parts.username is '' (empty string, falsy) and parts.password is None (falsy), so the check passes. However, parts.netloc is '@host', and line 137 reconstructs the URL as https://@host/path. The resulting URL with @ prefix in the netloc is an improperly formed URL that could be misinterpreted by some HTTP clients (treating the empty string before @ as userinfo). The URL should be reconstructed from parts.hostname and parts.port to strip any residual userinfo.

Suggested change
path = parts.path.rstrip("/")
netloc = parts.netloc
return f"{parts.scheme}://{netloc}{path}"
Replace lines 135-137 with:
path = parts.path.rstrip('/')
host = parts.hostname
port = f':{parts.port}' if parts.port else ''
return f"{parts.scheme}://{host}{port}{path}"

Reply @codna fix and Codna opens a verified, test-green, risk-gated fix PR.

@0xamlab

0xamlab commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #41 (a later thyn-ai/codna commit). Every sync PR is a full snapshot of the exported surface, so this one carries nothing #41 does not. Closing.

@0xamlab 0xamlab closed this Nov 4, 2026
auto-merge was automatically disabled November 4, 2026 23:00

Pull request was closed

@0xamlab
0xamlab deleted the sync/2b631dec6d3487d8d9c873d7215670ecc764e9ba branch November 4, 2026 23:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant