kin: auto-commit after pipeline

This commit is contained in:
Gros Frumos 2026-03-17 18:23:33 +02:00
parent c767c6157a
commit 348aa07fec
3 changed files with 74 additions and 2 deletions

View file

@ -40,7 +40,27 @@ You receive:
## Output format ## Output format
Return ONLY valid JSON (no markdown, no explanation): Return TWO sections in your response:
### Section 1 — `## Verdict` (human-readable, in Russian)
2-3 sentences in plain Russian for the project director: what was checked, whether everything is OK, are there any issues, can the task be closed. No JSON, no technical terms, no code snippets.
Example:
```
## Verdict
Реализация проверена — логика корректна, безопасность соблюдена. Найдено одно незначительное замечание по документации, не блокирующее. Задачу можно закрывать.
```
Another example (with issues):
```
## Verdict
Проверка выявила критическую проблему: SQL-запрос уязвим к инъекциям. Также отсутствуют тесты для нового эндпоинта. Задачу нельзя закрывать до исправления.
```
### Section 2 — `## Details` (JSON block for agents)
The full technical output in JSON, wrapped in a ```json code fence:
```json ```json
{ {
@ -71,6 +91,25 @@ If verdict is "changes_requested", findings must be non-empty with actionable su
If verdict is "revise", include `"target_role": "..."` and findings must be non-empty with actionable suggestions. If verdict is "revise", include `"target_role": "..."` and findings must be non-empty with actionable suggestions.
If verdict is "blocked", include `"blocked_reason": "..."` (e.g. unable to read files). If verdict is "blocked", include `"blocked_reason": "..."` (e.g. unable to read files).
**Full response example:**
```
## Verdict
Реализация проверена — логика корректна, безопасность соблюдена. Найдено одно незначительное замечание по документации, не блокирующее. Задачу можно закрывать.
## Details
```json
{
"verdict": "approved",
"findings": [...],
"security_issues": [],
"conventions_violations": [],
"test_coverage": "adequate",
"summary": "..."
}
` ` `
```
## Verdict definitions ## Verdict definitions
### verdict: "revise" ### verdict: "revise"

View file

@ -516,6 +516,39 @@ class TestSSHBuildCmd:
assert "cd" not in full_cmd_arg assert "cd" not in full_cmd_arg
assert full_cmd_arg == "git pull" assert full_cmd_arg == "git pull"
def test_deploy_path_with_semicolon_injection_is_escaped(self):
"""Path containing ';' must be quoted so it cannot inject a second shell command."""
project = {
"deploy_host": "myserver",
"deploy_path": "/srv/api; rm -rf /",
}
cmd = _build_ssh_cmd(project, "git pull")
full_cmd_arg = cmd[-1]
# The dangerous path must appear only as a quoted argument, not as a bare shell fragment.
assert "cd /srv/api; rm -rf /" not in full_cmd_arg
# shlex.quote wraps it in single quotes — the semicolon is inside the quotes.
assert shlex.quote("/srv/api; rm -rf /") in full_cmd_arg
def test_deploy_restart_cmd_is_not_shlex_quoted(self):
"""deploy_restart_cmd must reach SSH as a plain shell command, not as a single quoted arg.
shlex.quote would turn 'docker compose restart worker' into a literal string
which the remote shell would refuse to execute. Admin-controlled field no quoting.
"""
project = {
"deploy_host": "myserver",
"deploy_path": "/srv/api",
"deploy_runtime": "docker",
"deploy_restart_cmd": "docker compose restart worker",
}
# Build steps manually and feed one step into _build_ssh_cmd.
restart_cmd = "docker compose restart worker"
cmd = _build_ssh_cmd(project, restart_cmd)
full_cmd_arg = cmd[-1]
# The command must appear verbatim (not as a single quoted token).
assert "docker compose restart worker" in full_cmd_arg
assert full_cmd_arg != shlex.quote("docker compose restart worker")
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# 9. deploy_with_dependents — cascade deploy unit tests # 9. deploy_with_dependents — cascade deploy unit tests

View file

@ -38,7 +38,7 @@ describe('api.projectLinks', () => {
it('возвращает массив ProjectLink', async () => { it('возвращает массив ProjectLink', async () => {
const links = [ const links = [
{ id: 1, from_project: 'KIN', to_project: 'BRS', link_type: 'depends_on', description: null, created_at: '2026-01-01' }, { id: 1, from_project: 'KIN', to_project: 'BRS', type: 'depends_on', description: null, created_at: '2026-01-01' },
] ]
mockFetch(links) mockFetch(links)
const result = await api.projectLinks('KIN') const result = await api.projectLinks('KIN')