S00-012: Record blocked task and remediation handoff

Changes: Preserve incomplete work and blocker metadata for S00-012. This checkpoint does not mark implementation accepted.
Validation: Session ended with status blocked.
Blocker: Submit-transition descendant regression fails: repeated stop_process_tree after parent reap raises EPERM at os.killpg; launch OSError handling then attempts invalid escalation from review state.

Task: S00-012
Agent: auto-ci-cd-28868645424b
Role: ci-cd
Action: claim
Status: blocked

Task title: Fix runner prompt delivery after partial stdin timeout
This commit is contained in:
wt committed 2026-10-15 01:45:17 +07:00
1 parent b6691d9c8b
commit 49c054aaf9
14 files changed
+295 -20

No files matched your search

+1 -1
View File
@@ -6,7 +6,7 @@
Зафиксировать Web scope, browser targets и проверенные кандидаты, не устанавливая непроверенный framework по догадке.
Задач: 13; entry: нет; exit: S00-010.
Задач: 14; entry: нет; exit: S00-010.
## [S01: Предметные контракты](stages/S01.md)
+3 -2
View File
@@ -1,6 +1,6 @@
# Матрица задач
Generated; state revision **17**. Canonical status: state/tasks.json.
Generated; state revision **19**. Canonical status: state/tasks.json.
| ID | Этап | Оператор | Reviewer | Статус | Owner | Зависимости | Задача |
| --- | --- | --- | --- | --- | --- | --- | --- |
@@ -14,8 +14,9 @@ Generated; state revision **17**. Canonical status: state/tasks.json.
| [S00-008](tasks/S00-008.md) | S00 | ci-cd | qa | todo | | S00-003, S00-004 | Описать WSS/upload/TURN/push topology |
| [S00-009](tasks/S00-009.md) | S00 | orchestrator | security | todo | | S00-003, S00-004, S00-005, S00-006, S00-007, S00-008 | Согласовать результаты spikes |
| [S00-011](tasks/S00-011.md) | S00 | ci-cd | security | done | auto-ci-cd-d105e8027730 | S00-001 | Restore verified npm registry access for the stack spike |
| [S00-012](tasks/S00-012.md) | S00 | ci-cd | qa | todo | | S00-001, S00-011, S00-013 | Fix runner prompt delivery after partial stdin timeout |
| [S00-012](tasks/S00-012.md) | S00 | ci-cd | qa | blocked | auto-ci-cd-28868645424b | S00-001, S00-011, S00-013, S00-014 | Fix runner prompt delivery after partial stdin timeout |
| [S00-013](tasks/S00-013.md) | S00 | ci-cd | qa | done | auto-ci-cd-5f68f03dd33b | S00-001, S00-011 | Bound oversized S21-006 runner context without truncating requirements |
| [S00-014](tasks/S00-014.md) | S00 | ci-cd | qa | todo | | S00-001, S00-011, S00-013 | Fix repeated runner cleanup after a submitted parent is reaped |
| [S00-010](tasks/S00-010.md) | S00 | qa | orchestrator | todo | | S00-001, S00-002, S00-003, S00-004, S00-005, S00-006, S00-007, S00-008, S00-009 | Gate: Профиль и проверка стека |
| [S01-001](tasks/S01-001.md) | S01 | orchestrator | security | todo | | S00-010 | Описать identity/model contracts |
| [S01-002](tasks/S01-002.md) | S01 | orchestrator | security | todo | | S00-010, S01-001 | Описать async account context |
+1 -1
View File
@@ -204,7 +204,7 @@ Allocated означает наличие плана/приёмки, а не р
| NFR-004 | web | Web requirement; cross-platform clauses apply only in Web context. | S02-012, S09-012, S21-007 |
| NFR-005 | web | Web requirement; cross-platform clauses apply only in Web context. | S00-006, S02-012, S09-012, S20-006, S21-011 |
| NFR-006 | web | Web requirement; cross-platform clauses apply only in Web context. | S02-012, S09-012 |
| NFR-007 | web | Web requirement; cross-platform clauses apply only in Web context. | S00-002, S00-011, S00-012, S00-013, S02-001, S02-002, S09-012, S21-008, S21-009 |
| NFR-007 | web | Web requirement; cross-platform clauses apply only in Web context. | S00-002, S00-011, S00-012, S00-013, S00-014, S02-001, S02-002, S09-012, S21-008, S21-009 |
| NFR-008 | web | Web requirement; cross-platform clauses apply only in Web context. | S02-004, S02-005, S02-006, S09-012, S21-009 |
| NFR-009 | web | Web requirement; cross-platform clauses apply only in Web context. | S02-012, S09-012, S19-006, S20-003, S21-010 |
| UI-001 | web | Web requirement; cross-platform clauses apply only in Web context. | S03-002, S20-001, S21-006 |
+2 -1
View File
@@ -16,8 +16,9 @@
- [S00-008](../tasks/S00-008.md): Описать WSS/upload/TURN/push topology — ci-cd; todo.
- [S00-009](../tasks/S00-009.md): Согласовать результаты spikes — orchestrator; todo.
- [S00-011](../tasks/S00-011.md): Restore verified npm registry access for the stack spike — ci-cd; done.
- [S00-012](../tasks/S00-012.md): Fix runner prompt delivery after partial stdin timeout — ci-cd; todo.
- [S00-012](../tasks/S00-012.md): Fix runner prompt delivery after partial stdin timeout — ci-cd; blocked.
- [S00-013](../tasks/S00-013.md): Bound oversized S21-006 runner context without truncating requirements — ci-cd; done.
- [S00-014](../tasks/S00-014.md): Fix repeated runner cleanup after a submitted parent is reaped — ci-cd; todo.
- [S00-010](../tasks/S00-010.md): Gate: Профиль и проверка стека — qa; todo.
## Граница этапа
+2 -1
View File
@@ -9,8 +9,9 @@ S00-007,S00,security,orchestrator,todo,,"S00-004, S00-005",Утвердить pa
S00-008,S00,ci-cd,qa,todo,,"S00-003, S00-004",Описать WSS/upload/TURN/push topology
S00-009,S00,orchestrator,security,todo,,"S00-003, S00-004, S00-005, S00-006, S00-007, S00-008",Согласовать результаты spikes
S00-011,S00,ci-cd,security,done,auto-ci-cd-d105e8027730,S00-001,Restore verified npm registry access for the stack spike
S00-012,S00,ci-cd,qa,todo,,"S00-001, S00-011, S00-013",Fix runner prompt delivery after partial stdin timeout
S00-012,S00,ci-cd,qa,blocked,auto-ci-cd-28868645424b,"S00-001, S00-011, S00-013, S00-014",Fix runner prompt delivery after partial stdin timeout
S00-013,S00,ci-cd,qa,done,auto-ci-cd-5f68f03dd33b,"S00-001, S00-011",Bound oversized S21-006 runner context without truncating requirements
S00-014,S00,ci-cd,qa,todo,,"S00-001, S00-011, S00-013",Fix repeated runner cleanup after a submitted parent is reaped
S00-010,S00,qa,orchestrator,todo,,"S00-001, S00-002, S00-003, S00-004, S00-005, S00-006, S00-007, S00-008, S00-009",Gate: Профиль и проверка стека
S01-001,S01,orchestrator,security,todo,,S00-010,Описать identity/model contracts
S01-002,S01,orchestrator,security,todo,,"S00-010, S01-001",Описать async account context
1 id stage operator reviewer status owner depends_on title
9 S00-008 S00 ci-cd qa todo S00-003, S00-004 Описать WSS/upload/TURN/push topology
10 S00-009 S00 orchestrator security todo S00-003, S00-004, S00-005, S00-006, S00-007, S00-008 Согласовать результаты spikes
11 S00-011 S00 ci-cd security done auto-ci-cd-d105e8027730 S00-001 Restore verified npm registry access for the stack spike
12 S00-012 S00 ci-cd qa todo blocked auto-ci-cd-28868645424b S00-001, S00-011, S00-013 S00-001, S00-011, S00-013, S00-014 Fix runner prompt delivery after partial stdin timeout
13 S00-013 S00 ci-cd qa done auto-ci-cd-5f68f03dd33b S00-001, S00-011 Bound oversized S21-006 runner context without truncating requirements
14 S00-014 S00 ci-cd qa todo S00-001, S00-011, S00-013 Fix repeated runner cleanup after a submitted parent is reaped
15 S00-010 S00 qa orchestrator todo S00-001, S00-002, S00-003, S00-004, S00-005, S00-006, S00-007, S00-008, S00-009 Gate: Профиль и проверка стека
16 S01-001 S01 orchestrator security todo S00-010 Описать identity/model contracts
17 S01-002 S01 orchestrator security todo S00-010, S01-001 Описать async account context
+57 -1
View File
@@ -685,7 +685,8 @@
"depends_on": [
"S00-001",
"S00-011",
"S00-013"
"S00-013",
"S00-014"
],
"requirements": [
"NFR-007"
@@ -777,6 +778,61 @@
],
"context_budget_chars": 48000
},
{
"id": "S00-014",
"stage": "S00",
"kind": "blocker",
"root_task": "S00-002",
"blocker_depth": 2,
"unblocks": [
"S00-012"
],
"role": "ci-cd",
"reviewer_role": "qa",
"title": "Fix repeated runner cleanup after a submitted parent is reaped",
"outcome": "Reliable repeated lifecycle cleanup after submit with a reaped parent and SIGTERM-ignoring descendant, preserving actual process-tree termination and valid workflow state.",
"depends_on": [
"S00-001",
"S00-011",
"S00-013"
],
"requirements": [
"NFR-007"
],
"scope": [
"scripts/runner.py",
"tests/tooling/test_automation.py"
],
"inputs": [
"docs/WEB_PROFILE.md",
"docs/ARCHITECTURE.md",
"docs/CONTRACTS.md",
"docs/TESTING.md",
"roles/orchestrator.md",
"docs/decisions/README.md",
"state/blockers/S00-012.md",
"state/blockers/S00-014.md"
],
"spec_sections": [],
"ui_sections": [],
"steps": [
"Read S00-012 evidence and preserved descendant regressions.",
"Reproduce repeated cleanup EPERM in the supplied execution harness without launching a model.",
"Correct only the repeated-cleanup lifecycle failure; retain fail-closed handling for inaccessible live descendants and terminal/deadline monitoring.",
"Run descendant, delayed stdin and existing lifecycle checks plus make check; record evidence and submit."
],
"acceptance": [
"Submit-transition descendant regression completes without invalid escalation or delayed source writes.",
"Timeout and terminal-transition paths terminate descendants, including SIGTERM-ignoring children.",
"Repeated cleanup does not hide a failure to terminate a live process; stdin delivery and prompt budgets remain intact."
],
"checks": [
"Both preserved descendant regressions.",
"Delayed-reader UTF-8/EOF, timeout, transition, scope and fresh-session runner checks.",
"make check"
],
"context_budget_chars": 48000
},
{
"id": "S00-010",
"stage": "S00",
+1 -1
View File
@@ -7,7 +7,7 @@
- Этап: S00
- Оператор: ci-cd
- Reviewer: qa
- Зависимости: S00-001, S00-011, S00-013
- Зависимости: S00-001, S00-011, S00-013, S00-014
- Требования: NFR-007
## Один результат
+60
View File
@@ -0,0 +1,60 @@
# S00-014: Fix repeated runner cleanup after a submitted parent is reaped
- Remediation для: S00-012; root: S00-002; depth: 2
> Generated from planning/tasks.json; status is in state/tasks.json.
- Этап: S00
- Оператор: ci-cd
- Reviewer: qa
- Зависимости: S00-001, S00-011, S00-013
- Требования: NFR-007
## Один результат
Reliable repeated lifecycle cleanup after submit with a reaped parent and SIGTERM-ignoring descendant, preserving actual process-tree termination and valid workflow state.
## Разрешённые пути
- `scripts/runner.py`
- `tests/tooling/test_automation.py`
Не изменять другие modules, shared contracts или spec snapshots без изменения плана. Stem без расширения разрешает принятые stack-specific extensions и focused test рядом.
## Прочитать
- docs/WEB_PROFILE.md
- docs/ARCHITECTURE.md
- docs/CONTRACTS.md
- docs/TESTING.md
- roles/orchestrator.md
- docs/decisions/README.md
- state/blockers/S00-012.md
- state/blockers/S00-014.md
## Шаги
1. Read S00-012 evidence and preserved descendant regressions.
2. Reproduce repeated cleanup EPERM in the supplied execution harness without launching a model.
3. Correct only the repeated-cleanup lifecycle failure; retain fail-closed handling for inaccessible live descendants and terminal/deadline monitoring.
4. Run descendant, delayed stdin and existing lifecycle checks plus make check; record evidence and submit.
## Приёмка
1. Submit-transition descendant regression completes without invalid escalation or delayed source writes.
2. Timeout and terminal-transition paths terminate descendants, including SIGTERM-ignoring children.
3. Repeated cleanup does not hide a failure to terminate a live process; stdin delivery and prompt budgets remain intact.
## Focused checks
1. Both preserved descendant regressions.
2. Delayed-reader UTF-8/EOF, timeout, transition, scope and fresh-session runner checks.
3. make check
## Блокер и завершение запуска
Для всех ролей scope — только эта задача. При блокере создать issue JSON по state/ISSUE_TEMPLATE.json, вызвать escalate с текущим ID/role/agent, проверить status=blocked и resolution_task, затем немедленно завершить запуск. Не решать blocker и не выбирать следующую задачу самому. Новый агент назначается runner. Правки metadata через CLI — единственное исключение scope.
## Передача
Сохранить evidence/handoff; submit → review и завершить запуск. Reviewer также выполняет только эту задачу, включая обязательное escalate при блокере.
+22
View File
@@ -0,0 +1,22 @@
# Blocker S00-014
- Original task: S00-012
- Root task: S00-002
- Reporter: auto-ci-cd-28868645424b
- Role: ci-cd
- Original owner: auto-ci-cd-28868645424b
- Waits for user/external condition: False
## Cause
Submit-transition descendant regression fails: repeated stop_process_tree after parent reap raises EPERM at os.killpg; launch OSError handling then attempts invalid escalation from review state.
## One remediation outcome
Reliable repeated lifecycle cleanup after submit with a reaped parent and SIGTERM-ignoring descendant, preserving actual process-tree termination and valid workflow state.
## Evidence / reproduction
python3 -m unittest discover -s tests/tooling -p test_automation.py -v; test_submit_transition_stops_source_writing_descendant errors. In-memory trace: first cleanup returns parent -15; repeated cleanup raises PermissionError at runner.py:340. Evidence: state/evidence/S00-012-auto-ci-cd-28868645424b.md
The reporting agent must stop now. A new agent implements only S00-014.
@@ -0,0 +1,29 @@
# S00-012 executor blocker evidence
Agent: auto-ci-cd-28868645424b; role: ci-cd; date: 2026-10-15.
No submission or acceptance is claimed.
Input: assigned card/packet, startup instructions, prior partial handoff and diagnostic evidence, exact NFR-007, accepted scope ADR, S00-001/S00-011/S00-013 dependency evidence/reviews/handoffs. Output attempted: complete stdin delivery with verified lifecycle cleanup.
## Preserved implementation and current changes
The checkpoint c28412a already contains the stdin correction and delayed-reader regression. It supplies the persisted binary UTF-8 prompt file as stdin, polls wait at .2-second intervals, and retains deadlines/terminal-transition/process-group cleanup. S00-013 subsequently corrected prompt budgeting independently. No runner source changes were made in this session.
Added two real subprocess regressions in tests/tooling/test_automation.py. A descendant installs SIGTERM ignore, records readiness, then attempts a delayed source write. The parent either times out or submits and remains alive. Tests require cleanup to prevent the write. These tests are preserved as partial work; the submit-transition test does not pass.
## Actual commands/results
- tasks.py resume/show/packet: exit 0 each; S00-012 already owned, no repeated claim.
- git status --short at entry: exit 0; only coordinator state/tasks.json claim metadata modified.
- git log for S00-012 and git show c28412a scoped source: exit 0; prior checkpoint inspected.
- Synthetic legacy transport reproduction: exit 0; Python 3.9.6, 140000 UTF-8 bytes, .5-second delayed reader, communicate(payload, timeout=.2) then communicate(None, timeout=1) both time out. Child killed/reaped in finally.
- python3 -m unittest discover -s tests/tooling -p test_automation.py -v: exit 1; 26 tests in 9.489s, 25 passed, submit descendant test errored. Complete 140024-byte delayed UTF-8 prompt/EOF, timeout descendant, fresh-session chain, scope, escalation transition and both prompt-budget regressions passed.
- Isolated RunnerTests._check_descendant_cleanup(True) invocation: exit 1; same error reproduced.
- Same isolated invocation with an in-memory tracing wrapper around stop_process_tree: exit 1; first stop_process_tree succeeded and parent returncode was -15; second invocation with parent already reaped raised PermissionError [Errno 1] Operation not permitted at runner.py:340, os.killpg(process.pid, SIGTERM). No persistent tracing/source change.
- git diff --check and git diff --stat: exit 0; only tests/tooling/test_automation.py plus coordinator state metadata changed.
## Blocker and limits
After submit, stop_process_tree runs during transition handling and again in finally. With a SIGTERM-ignoring descendant and reaped parent, the second process-group signal is denied in this managed execution environment. The broad launch OSError handler then treats this as executable startup failure; escalation from an already submitted executor raises ValueError: Only current executor or assigned-role reviewer may escalate its current task. Timeout descendant test passes; transition descendant cleanup cannot be declared passed. The precise sandbox/OS policy causing EPERM is not proven beyond the observed syscall result.
Stop source edits and remediate this one lifecycle failure in a new session. Preserve real descendant termination, deadline/transition monitoring and fail-closed behavior for inaccessible live processes; do not simply suppress permission errors. make check was not run after establishing the blocker, and the automation suite is not passed. No actual model, product/provider integration, Git mutation, immutable spec or shared-contract change occurred.
+3 -3
View File
@@ -1,7 +1,7 @@
# S00-012 partial handoff
Executor: manual-ci-cd-stdin-20261015b, ci-cd. Partial stdin transport fix and regression test are in scripts/runner.py and tests/tooling/test_automation.py. New delayed UTF-8 reader test failed before the fix and passed after it. Existing timeout/terminal-transition tests pass. No acceptance/submission is claimed.
Executor: auto-ci-cd-28868645424b, ci-cd. Existing stdin fix and delayed UTF-8 regression from checkpoint c28412a were inspected. Complete 140024-byte delivery/EOF and current prompt budgets pass. No runner source changes this session.
Blocker: the existing all-prompts budget test fails because S21-006 prompt exceeds even the 90000-character diagnostic maximum. Separate context-budget remediation is required; it must retain complete exact task requirements and bounded relevant inputs, without increasing limits beyond contract or suppressing validation. Evidence: state/evidence/S00-012-manual-ci-cd-stdin-20261015b.md.
Added two descendant cleanup regressions in tests/tooling/test_automation.py; timeout scenario passes, submit-transition scenario errors. A SIGTERM-ignoring descendant records readiness and would write a source file after 1.5 seconds. Parent submits and sleeps. First stop_process_tree succeeds; its repeated finally invocation after parent is reaped (-15) raises EPERM at os.killpg(SIGTERM). Broad OSError handling then tries invalid escalation from review state. No acceptance or submission claimed.
After reviewed remediation, a fresh S00-012 executor should inspect the preserved diff, rerun regression and make check, write evidence and commit message, then submit. Coordinator must checkpoint the preserved partial work and pending prior blocker metadata before any automated new agent; current checkout is dirty. Current reporter ends after escalate and cannot continue remediation.
Evidence: state/evidence/S00-012-auto-ci-cd-28868645424b.md. Full automation run: 25/26 passed, exit 1. make check not run following this blocker. Next fresh remediation must resolve this bounded repeated-cleanup lifecycle failure without weakening process-tree termination or suppressing real live-process permission failures. After independently reviewed remediation, a fresh S00-012 executor must rerun regressions/make check and submit. Coordinator owns checkpoint; this reporter stops after escalate.
@@ -0,0 +1,30 @@
{
"title": "Fix repeated runner cleanup after a submitted parent is reaped",
"reason": "Submit-transition descendant regression fails: repeated stop_process_tree after parent reap raises EPERM at os.killpg; launch OSError handling then attempts invalid escalation from review state.",
"reproduction": "python3 -m unittest discover -s tests/tooling -p test_automation.py -v; test_submit_transition_stops_source_writing_descendant errors. In-memory trace: first cleanup returns parent -15; repeated cleanup raises PermissionError at runner.py:340. Evidence: state/evidence/S00-012-auto-ci-cd-28868645424b.md",
"role": "ci-cd",
"reviewer_role": "qa",
"scope": [
"scripts/runner.py",
"tests/tooling/test_automation.py"
],
"outcome": "Reliable repeated lifecycle cleanup after submit with a reaped parent and SIGTERM-ignoring descendant, preserving actual process-tree termination and valid workflow state.",
"steps": [
"Read S00-012 evidence and preserved descendant regressions.",
"Reproduce repeated cleanup EPERM in the supplied execution harness without launching a model.",
"Correct only the repeated-cleanup lifecycle failure; retain fail-closed handling for inaccessible live descendants and terminal/deadline monitoring.",
"Run descendant, delayed stdin and existing lifecycle checks plus make check; record evidence and submit."
],
"acceptance": [
"Submit-transition descendant regression completes without invalid escalation or delayed source writes.",
"Timeout and terminal-transition paths terminate descendants, including SIGTERM-ignoring children.",
"Repeated cleanup does not hide a failure to terminate a live process; stdin delivery and prompt budgets remain intact."
],
"checks": [
"Both preserved descendant regressions.",
"Delayed-reader UTF-8/EOF, timeout, transition, scope and fresh-session runner checks.",
"make check"
],
"depends_on": [],
"requires_user_input": false
}
+41 -9
View File
@@ -1,6 +1,6 @@
{
"schema_version": 1,
"revision": 17,
"revision": 19,
"tasks": {
"S00-001": {
"status": "done",
@@ -1136,15 +1136,17 @@
"completed_at": "2026-10-14T18:11:33+00:00"
},
"S00-012": {
"status": "todo",
"owner": null,
"status": "blocked",
"owner": "auto-ci-cd-28868645424b",
"external_wait": false,
"reason": null,
"started_at": "2026-10-14T18:33:49+00:00",
"updated_at": "2026-10-14T18:42:03+00:00",
"blocked_by": [],
"reason": "Submit-transition descendant regression fails: repeated stop_process_tree after parent reap raises EPERM at os.killpg; launch OSError handling then attempts invalid escalation from review state.",
"started_at": "2026-10-14T18:42:03+00:00",
"updated_at": "2026-10-14T18:45:16+00:00",
"blocked_by": [
"S00-014"
],
"resume_status": "in_progress",
"blocker_report": "state/blockers/S00-013.md",
"blocker_report": "state/blockers/S00-014.md",
"handoff": "state/handoffs/S00-012.md",
"previous_owner": "manual-ci-cd-stdin-20261015b",
"resumed_after_blocker": true
@@ -1161,6 +1163,12 @@
"reviewer": "auto-qa-869b22acd478",
"review_evidence": "state/evidence/S00-013-review.md",
"completed_at": "2026-10-14T18:42:03+00:00"
},
"S00-014": {
"status": "todo",
"owner": null,
"external_wait": false,
"reason": null
}
},
"events": [
@@ -1375,11 +1383,35 @@
"to": "done",
"reason": null,
"evidence": "state/evidence/S00-013-review.md"
},
{
"revision": 18,
"at": "2026-10-14T18:42:03+00:00",
"action": "claim",
"task": "S00-012",
"agent": "auto-ci-cd-28868645424b",
"role": "ci-cd",
"from": "todo",
"to": "in_progress",
"reason": null
},
{
"revision": 19,
"at": "2026-10-14T18:45:16+00:00",
"action": "escalate",
"task": "S00-012",
"resolution_task": "S00-014",
"agent": "auto-ci-cd-28868645424b",
"role": "ci-cd",
"from": "in_progress",
"to": "blocked",
"reason": "Submit-transition descendant regression fails: repeated stop_process_tree after parent reap raises EPERM at os.killpg; launch OSError handling then attempts invalid escalation from review state."
}
],
"retired_agents": [
"auto-orchestrator-3a4f07103316",
"manual-security-stdin-20261015a",
"manual-ci-cd-stdin-20261015b"
"manual-ci-cd-stdin-20261015b",
"auto-ci-cd-28868645424b"
]
}
+43
View File
@@ -276,6 +276,49 @@ else:
self.assertEqual(result['stop'], 'session_limit')
self.assertEqual(self.tracker.state()['tasks']['S00-001']['status'], 'blocked')
def _check_descendant_cleanup(self, transition):
import time
ready = self.root / 'state/descendant-ready'
late_write = self.root / 'src/late-descendant-write.txt'
child_code = (
"import pathlib,signal,time;signal.signal(signal.SIGTERM,signal.SIG_IGN);"
f"pathlib.Path({str(ready)!r}).write_text('ready');"
"time.sleep(1.5);"
f"pathlib.Path({str(late_write)!r}).write_text('unexpected')"
)
startup = (
"import subprocess,time\n"
f"subprocess.Popen([sys.executable,'-c',{child_code!r}])\n"
f"while not pathlib.Path({str(ready)!r}).exists():time.sleep(.01)\n"
)
fake = self.root / 'fake_agent.py'
source = fake.read_text().replace(
"prompt=sys.stdin.read();assert 'fresh session' in prompt",
startup + ("prompt=sys.stdin.read();assert 'fresh session' in prompt"
if transition else "time.sleep(10)"))
fake.write_text(source + '\nimport time;time.sleep(10)\n')
(self.root / 'state/first-blocker').write_text('skip fixture blocker')
runner = RUN_MODULE.Runner(self.root, dict(self.config, max_sessions=1,
timeout_seconds=2 if transition else .5))
result = runner.run()
self.assertEqual(result['launched'], 1)
self.assertTrue(ready.is_file(), 'Descendant must start before cleanup')
record = json.loads(next((self.root / 'state/runs').glob('*/run.json')).read_text())
self.assertEqual(record['stopped_after_transition'], transition)
self.assertEqual(self.tracker.state()['tasks']['S00-001']['status'],
'review' if transition else 'blocked')
time.sleep(1.6)
self.assertFalse(late_write.exists(), 'Descendant survived process-group cleanup')
self.assertEqual(record['scope_violations'], [])
@unittest.skipUnless(sys.platform != 'win32', 'POSIX process-group cleanup')
def test_timeout_stops_source_writing_descendant(self):
self._check_descendant_cleanup(transition=False)
@unittest.skipUnless(sys.platform != 'win32', 'POSIX process-group cleanup')
def test_submit_transition_stops_source_writing_descendant(self):
self._check_descendant_cleanup(transition=True)
def test_scope_matcher_checks_modified_created_deleted_files(self):
self.assertEqual(RUN_MODULE.scope_violations({'a': 'old', 'gone': 'old'}, {'a': 'new', 'created': 'new'}, ['a']), ['created', 'gone'])