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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions project/TICKETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,4 +461,6 @@ project analysis or human-owned ticket inputs.
| **ticket-463** | [`README.md`](./ticket-463/README.md) | - | - | - | - | - |
| **ticket-464** | [`README.md`](./ticket-464/README.md) | - | - | - | - | - |
| **ticket-465** | [`README.md`](./ticket-465/README.md) | - | - | - | - | - |
| **ticket-466** | [`README.md`](./ticket-466/README.md) | - | - | - | - | - |
| **ticket-467** | [`README.md`](./ticket-467/README.md) | - | - | - | - | - |
<!-- AUTO:TICKET_INDEX:END -->
22 changes: 22 additions & 0 deletions project/ticket-467/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Ticket 467: fix(queue): prevent redact_secrets from corrupting code excerpts and repair missing + on closing delimiters (#728)

- **ID**: ticket-467
- **Owner**: agent:antigravity
- **Status**: IN_PROGRESS
- **Workflow state**: EDIT
- **Created**: 2026-10-19

SESSION_EXECUTION_AUTHORIZATION: User authorized autonomous resilience and prevention repairs in `semcod/koru` (`#728`) observed while delegating tickets through `willman` / `willops`.

## Goal and scope

Prevent `redact_secrets` in `src/koru/queue/patch_mode.py` from corrupting code expressions (`===`, `new Set([`, `decryptJson(...)`, `this.key = encryptionKey`) when quoting files in `current_file_excerpt` and `build_verify_retry_prompt`, and recover unmarked closing delimiters (`}`, `};`, `)`, `);`, `]`, `];`) plus trailing-blank `pre_ctx` anchors in `src/koru/queue/diff_repair.py`.

## Acceptance criteria

- [x] AC-01: `PYTHONPATH=src pytest -q -o addopts='' tests/test_planfile_queue.py -k 'redact or context_excerpt' tests/test_queue_evidence.py -k 'secrets'`, `python3 -m ruff check src/koru/queue/patch_mode.py src/koru/queue/diff_repair.py tests/test_planfile_queue.py`, and `./project/governance-check.sh` pass.

## Tracking boundary

This directory contains the minimal reviewed intent. Optional participant prose
and raw command logs are not required delivery output.
79 changes: 79 additions & 0 deletions project/ticket-467/intent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"schema": "new-project.intent/v3",
"ticket": "ticket-467",
"summary": "fix(queue): prevent redact_secrets from corrupting code excerpts and repair missing + on closing delimiters (#728)",
"workstream": "application",
"classification": {
"kind": "BUG",
"priority": "P1",
"origin": "health"
},
"allowedPaths": [
"project/ticket-467/**",
"TODO.md",
"project/TICKETS.md",
"src/koru/queue/diff_repair.py",
"src/koru/queue/patch_mode.py",
"src/koru/queue/transaction/preflight.py",
"tests/test_planfile_queue.py"
],
"forbiddenPaths": [
"project/ticket-*/user-*.md"
],
"stacks": [],
"dependsOn": [],
"conflictsWith": [],
"integrationTicket": null,
"delivery": {
"acceptedBaseSha": "8b00aaa907526dad25f88295147cdb459338141b",
"targetBranch": "main",
"outcome": "Prevent redact_secrets in src/koru/queue/patch_mode.py from corrupting code expressions in current_file_excerpt and build_verify_retry_prompt, and recover unmarked closing delimiters and trailing-blank pre_ctx anchors in src/koru/queue/diff_repair.py (#728).",
"nonGoals": [
"Do not weaken credential redaction for quoted or env-style secret assignments."
],
"complexity": "S",
"estimatedMinutes": 20,
"budgets": {
"maxImplementationFiles": 5,
"maxAffectedComponents": 1,
"maxPublicInterfaceChanges": 0,
"maxRuntimeDependencies": 0
},
"architecture": {
"status": "accepted",
"decision": "Narrow _SECRET_PATTERNS in src/koru/queue/patch_mode.py to quoted and env-style secret assignments and add _try_promote_unmarked_closing_delimiter plus non-empty pre_ctx anchor resolution in src/koru/queue/diff_repair.py.",
"components": [
{
"name": "queue-patch-repair",
"paths": [
"src/koru/queue/patch_mode.py",
"src/koru/queue/diff_repair.py",
"src/koru/queue/transaction/preflight.py",
"tests/test_planfile_queue.py"
]
}
],
"responsibilityChanges": false,
"interfaceChanges": [],
"dataChanges": [],
"ui": {
"impact": "none",
"states": [],
"evidence": []
},
"rollback": "Revert commit for ticket-467."
},
"runtimeDependencies": [],
"validation": [
{
"criterion": "AC-01",
"commands": [
"PYTHONPATH=src pytest -q -o addopts='' tests/test_planfile_queue.py -k 'redact or context_excerpt' tests/test_queue_evidence.py -k 'secrets'",
"python3 -m ruff check src/koru/queue/patch_mode.py src/koru/queue/diff_repair.py tests/test_planfile_queue.py",
"./project/governance-check.sh"
],
"evidence": "project/ticket-467/README.md"
}
]
}
}
61 changes: 57 additions & 4 deletions src/koru/queue/diff_repair.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,15 +521,22 @@ def _apply_edit_subblocks_fallback(
else:
ins_pos: int | None = None
if pre_ctx:
anchor = pre_ctx[-1].strip()
if anchor:
non_empty = [
(idx, line.strip())
for idx, line in enumerate(pre_ctx)
if line.strip()
]
if non_empty:
last_idx, anchor = non_empty[-1]
trailing_blanks = len(pre_ctx) - 1 - last_idx
best_dist = len(working) + 1000
for cand, w_line in enumerate(working):
if w_line.strip() == anchor:
dist = abs(cand + 1 - expected_start)
target_pos = min(len(working), cand + 1 + trailing_blanks)
dist = abs(target_pos - expected_start)
if dist < best_dist:
best_dist = dist
ins_pos = cand + 1
ins_pos = target_pos
if ins_pos is None and post_ctx:
anchor = post_ctx[0].strip()
if anchor:
Expand All @@ -552,6 +559,45 @@ def _apply_edit_subblocks_fallback(
return working, changed_any


_CLOSING_DELIMITER_STRIPS = frozenset({"}", "};", ")", ");", "]", "];"})


def _try_promote_unmarked_closing_delimiter(
working: list[str],
new_chunk_template: list[tuple[str, str]],
expected_start: int,
) -> tuple[list[tuple[str, str]], list[str], int] | None:
"""Recover when a model omits the leading '+' on a closing delimiter right after '+' lines."""
candidates: list[list[tuple[str, str]]] = []
for idx in range(1, len(new_chunk_template)):
kind, text = new_chunk_template[idx]
if kind == " " and text.strip() in _CLOSING_DELIMITER_STRIPS and new_chunk_template[idx - 1][0] == "+":
promoted = list(new_chunk_template)
promoted[idx] = ("+", text)
candidates.append(promoted)
if idx + 1 < len(new_chunk_template) and new_chunk_template[idx + 1] == (" ", ""):
promoted_with_blank = list(promoted)
promoted_with_blank[idx + 1] = ("+", "")
candidates.append(promoted_with_blank)
for cand_tpl in candidates:
cand_old = [t for k, t in cand_tpl if k in (" ", "-")]
m_len = len(cand_old)
if not m_len or m_len > len(working):
continue
cand_strip = [x.strip() for x in cand_old]
best_dist = len(working) + 1
best_pos: int | None = None
for i in range(len(working) - m_len + 1):
if [w.strip() for w in working[i : i + m_len]] == cand_strip:
dist = abs(i - expected_start)
if dist < best_dist:
best_dist = dist
best_pos = i
if best_pos is not None:
return cand_tpl, cand_old, best_pos
return None


def _apply_hunks_with_fuzzy_fallback(
orig_lines: list[str], hunk_blocks: list[list[str]]
) -> tuple[list[str], bool] | None:
Expand Down Expand Up @@ -623,6 +669,13 @@ def _apply_hunks_with_fuzzy_fallback(
if dist < best_dist:
best_dist = dist
match_pos = i
if match_pos is None:
promoted_delim = _try_promote_unmarked_closing_delimiter(
working, new_chunk_template, expected_start
)
if promoted_delim is not None:
new_chunk_template, old_chunk, match_pos = promoted_delim
n = len(old_chunk)

if match_pos is not None and match_pos >= 0:
matched_disk_lines = working[match_pos : match_pos + n]
Expand Down
9 changes: 7 additions & 2 deletions src/koru/queue/patch_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,15 @@ def wrap_reply_in_proposal_envelope(


_SECRET_PATTERNS = (
# Assignments: API_KEY=..., token: "...", password = '...'
# Quoted secret assignments: API_KEY="...", token: "...", password = '...'
re.compile(
r"(?i)\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL)[A-Z0-9_]*)"
r"(\s*[:=]\s*)(\"[^\"]*\"|'[^']*'|\S+)",
r"(?<![=!<>:])(\s*[:=](?![=>:])\s*)(\"[^\"\n]{4,}\"|'[^'\n]{4,}')",
),
# Unquoted env/CLI secret assignments: OPENAI_API_KEY=sk-live-...
re.compile(
r"(?i)\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL)[A-Z0-9_]*)"
r"(?<![=!<>:])(=)(?![=>])([A-Za-z0-9_\-./+=]{6,})(?=$|\s|[,;\"'])",
),
# Bearer / Authorization headers.
re.compile(r"(?i)\b(authorization|bearer)(\s*:?\s+)(\S+)"),
Expand Down
1 change: 0 additions & 1 deletion src/koru/queue/transaction/preflight.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
from koru.queue.transaction.result import PatchPlan
from koru.queue.types import CommandResult


#: A ticket opted into ``inputs.strict_files`` and the patch touches files it did not declare.
PATCH_TARGETS_OUTSIDE_DECLARED_FILES = "patch_targets_outside_declared_files"

Expand Down
44 changes: 44 additions & 0 deletions tests/test_planfile_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -2859,6 +2859,50 @@ def test_retry_feedback_is_length_bounded(self) -> None:
self.assertLess(len(cleaned), 2_200)
self.assertIn("truncated", cleaned)

def test_redact_secrets_preserves_code_expressions_and_repairs_closing_brace(self) -> None:
from koru.queue.diff_repair import extract_unified_diff
from koru.queue.patch_mode import redact_secrets

code_excerpt = (
"const SECRET_FIELDS = new Set(['username', 'password']);\n"
"this.key = encryptionKey;\n"
"return {...entry, secrets: decryptJson(entry.envelope, this.key)};\n"
"if (nextKey === this.key) return {rotated: false};\n"
)
self.assertEqual(redact_secrets(code_excerpt), code_excerpt)

with tempfile.TemporaryDirectory() as tmp:
project = Path(tmp)
target = project / "src" / "vault.mjs"
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(
" async metadata(id, requestedOrigin) {\n"
" return {...entry, fields: Object.keys(secrets).sort()};\n"
" }\n"
"\n"
" async rotateEncryptionKey(nextKey) {\n"
" if (nextKey === this.key) return {rotated: false};\n"
" }\n",
encoding="utf-8",
)
raw_diff = (
"diff --git a/src/vault.mjs b/src/vault.mjs\n"
"--- a/src/vault.mjs\n"
"+++ b/src/vault.mjs\n"
"@@ -1,6 +1,12 @@\n"
" return {...entry, fields: Object.keys(secrets).sort()};\n"
" }\n"
" \n"
"+ async verifyKeyReadiness() {\n"
"+ return {ok: true};\n"
" }\n"
" \n"
" async rotateEncryptionKey(nextKey) {\n"
)
repaired = extract_unified_diff(raw_diff, project=project, known_files=("src/vault.mjs",))
assert repaired is not None
self.assertIn("+ }\n+\n+ async verifyKeyReadiness() {\n+ return {ok: true};\n }\n", repaired)

def test_patch_creating_a_new_file_is_promoted(self) -> None:
from koru.queue.patch_transaction import apply_proposed_patch

Expand Down
Loading