Skip to content

Probe skill lifecycle, resume and interactive mode across harnesses - #991

Merged
alexeyzimarev merged 21 commits into
alexeyzimarev/ai-2829-verify-repo-local-skill-discovery-and-startup-orderingfrom
alexeyzimarev/ai-2829-skills-discovery-pass-2
Sep 17, 2026
Merged

alexeyzimarev merged 21 commits into
alexeyzimarev/ai-2829-verify-repo-local-skill-discovery-and-startup-orderingfrom
alexeyzimarev/ai-2829-skills-discovery-pass-2

Conversation

@alexeyzimarev

Copy link
Copy Markdown
Member

Closes #961 — AI-2829

What & why

Pass 1 answered where a repo-local skill has to be to reach a session's first request. Pass 2 answers what happens afterwards: whether a session already running sees a skill added, rewritten or deleted, whether a resumed session does, which tree a harness searches when it starts in a subdirectory or a linked worktree, and whether one session sees another's startup hook. It adds a third launch mode that drives each vendor's own interactive UI on a pseudo-terminal, since that is where a reload command exists at all. Nine harnesses, three modes, two runs per arm. No production code changes.

Where to look

findings.md "Consequences for #778 and #962". Writing into a live session works only on Codex and Kiro; a rewrite also reaches Pi, Cursor and OpenCode, while Copilot serves the body it read at startup and its only live route is /skills reload, which exists in the interactive UI alone. Claude keeps serving a rewritten skill's old body and refuses the question outright after a resume. Pi, Kiro and Cursor ignore the repository root when the session starts in a subdirectory, so a materializer anchored there is invisible to three of nine. Every harness reads its own linked worktree and none reads a sibling's, and one info/exclude covers them all.

A run that produces no answer is recorded as a failure to measure, never as a negative. That rule is what separates Claude's refusals and Codex's rejected resume flags from a skill that was genuinely absent.

Verification

python3 docs/probes/2026-09-16-skills-discovery/selftest.py    # Ran 113 tests, OK, also under -W error::ResourceWarning
python3 docs/probes/2026-09-16-skills-discovery/probe.py --emit  # 642 rows
Model turns recorded across both passes 803
Harnesses measured interactively 8 of 9 (Gemini blocked on credentials)
Arms still untested, with a recorded reason 85 rows

🤖 Generated with Claude Code

alexeyzimarev and others added 19 commits September 17, 2026 12:43
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Hook and plugin install paths stay on sb.repo; only ask()'s launch dir moves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ns (#961)

capability-matrix.md regenerated: existing rows gain seven n/a (not run) cells.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Each daemon driver now spawns once and takes several ask() turns on the same process, and the fake adapter grows a resumable session and a frozen-catalogue /reload for the TUI driver in the next task.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The fake tui vendor's reply now matches the queried skill's name against its catalogue, since a stale entry for a different skill would otherwise be mistaken for the answer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eam (#961)

A turn parsed from its raw stream must not see the previous turn's frames, or a silent second turn would inherit the first turn's token.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…961)

Codex deletes its last-message file before each run: a resumed turn would otherwise read the previous turn's answer from it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tor (#961)

FakeAdapter's startup-hook path also moves from a scalar to a per-sandbox map: one adapter instance now runs every scenario's arms in turn, and a stale hook path from an earlier arm's already-torn-down sandbox otherwise crashes the next one's turn.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#961)

A vendor streams its answer beside a spinner that redraws in place, so the concatenated bytes interleave the two; the interactive prompt also has to ask for a marked line, and a screen without one is a failure to measure rather than a skill that was absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude redraws the trust list after first painting it: a selection moved during that paint is reset, and the Enter behind it then confirms the default, which is to quit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…961)

Claude's resumed turns come back as refusals with no output and Codex rejects the sandbox flags on resume: both were landing as 'the skill was not there'. A later turn now asks for the token the skill carries now, so a session answering from its own context is wrong rather than lucky.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…961)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#961)

Cursor repaints its whole transcript, so the previous turn's reply is still on screen after the clear; a later turn now asks for a mark only it carries. Its composer also swallows an Enter sent straight behind the text, and a failed screen keeps its log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hook captured stdin for two seconds to survive a vendor that never closes it; on a terminal that input is the UI's, and OpenCode sat waiting for a keystroke the hook had eaten. Claude also raises an auto-mode wizard between turns, which took the next prompt as its own input.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A service left behind by an earlier sandbox leaves the interactive UI drawing nothing at all, and would serve that sandbox's catalogue to this one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A live session sees a new skill only on Codex and Kiro; a rewrite also reaches Pi, Cursor and OpenCode, while Copilot serves what it read at startup. Three harnesses ignore the repository root when launched from a subdirectory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Sep 17, 2026

Copy link
Copy Markdown

AI-2829

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-17T17:04:05.729131Z 4e80f16 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Extend skill probes for lifecycle, resume, and interactive sessions

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add lifecycle, resume, nested-directory, worktree, and peer-session skill discovery scenarios.
• Drive vendor interactive UIs through persistent PTY and daemon sessions.
• Record cross-harness outcomes with expanded tests, findings, and capability reporting.
Diagram

graph TD
  A["Probe CLI"] --> B{"Mode Router"} --> C["Scenario Arms"] --> D["Vendor Adapters"] --> E["Session Drivers"] --> F["Skill Trees"] --> G["Verdict Engine"] --> H["Matrix Reports"]
Loading
High-Level Assessment

The chosen approach is appropriate: a shared Session abstraction preserves real multi-turn vendor state, while a PTY is necessary to exercise reload commands and interactive-only behavior. Reusing one-turn launches would invalidate lifecycle measurements, and mocked terminal behavior would not capture vendor redraws, dialogs, or command availability; both alternatives were therefore correctly limited to self-tests rather than measurement.

Files changed (30) +9182 / -1693

Enhancement (16) +1296 / -115
agy.pyAdd Antigravity TUI and resume support +56/-6

Add Antigravity TUI and resume support

• Launches Antigravity from the sandbox working directory, adds interactive and conversation-resume commands, and wraps sessions to preserve slash-prefixed skill invocation and hook cleanup.

docs/probes/2026-09-16-skills-discovery/harness/agy.py

base.pyIntroduce the shared persistent-session contract +68/-1

Introduce the shared persistent-session contract

• Adds 'Session' and 'ClassifiedSession' abstractions plus adapter hooks for TUI launches, reloads, session identifiers, and resume commands. The base adapter now constructs PTY-backed interactive sessions.

docs/probes/2026-09-16-skills-discovery/harness/base.py

claude.pySupport Claude interactive and resumed probes +33/-11

Support Claude interactive and resumed probes

• Adds Claude session-id extraction, resume launches, TUI arguments, and automated handling for trust, browser-tool, and auto-mode dialogs. Vendor processes now launch from the scenario-specific working directory.

docs/probes/2026-09-16-skills-discovery/harness/claude.py

codex.pyKeep Codex daemon sessions alive across turns +47/-6

Keep Codex daemon sessions alive across turns

• Adds persistent app-server sessions, interactive launch support, thread resume handling, and per-turn tool classification. It also adapts resume flags and prevents stale last-message files from becoming answers.

docs/probes/2026-09-16-skills-discovery/harness/codex.py

copilot.pyAdd Copilot sessions, resume, and skill reload +45/-11

Add Copilot sessions, resume, and skill reload

• Adds persistent ACP and interactive launches, session-id parsing, print-mode resume, and '/skills reload' support. Tool classification is retained across multi-turn sessions.

docs/probes/2026-09-16-skills-discovery/harness/copilot.py

cursor.pyAdd Cursor persistent, resumed, and TUI launches +38/-6

Add Cursor persistent, resumed, and TUI launches

• Introduces persistent ACP sessions, interactive invocation, session-id extraction, and resumed print runs. All vendor launches now honor the sandbox's scenario-specific current directory.

docs/probes/2026-09-16-skills-discovery/harness/cursor.py

gemini.pyAdd Gemini session, resume, and TUI wiring +36/-7

Add Gemini session, resume, and TUI wiring

• Adds persistent ACP sessions, interactive launch arguments, session-id fallback to 'latest', and resumed print invocation. Launches now use the selected sandbox working directory.

docs/probes/2026-09-16-skills-discovery/harness/gemini.py

kiro.pySupport Kiro persistent and resumed conversations +37/-7

Support Kiro persistent and resumed conversations

• Adds persistent ACP and interactive chat sessions, directory-scoped resume behavior, and onboarding dialog handling. Shared agent arguments are applied consistently across launch modes.

docs/probes/2026-09-16-skills-discovery/harness/kiro.py

opencode_v1.pyAdd OpenCode v1 lifecycle launch support +39/-6

Add OpenCode v1 lifecycle launch support

• Adds persistent ACP sessions, interactive launches, session-id extraction, and resumed print runs. Tool classification and version-specific arguments remain consistent across modes.

docs/probes/2026-09-16-skills-discovery/harness/opencode_v1.py

pi.pyAdd persistent Pi RPC and resume support +36/-6

Add persistent Pi RPC and resume support

• Adds multi-turn RPC sessions, interactive launches, session-id extraction, and resumed JSON-mode requests. Per-turn Pi tool classification remains available through a session wrapper.

docs/probes/2026-09-16-skills-discovery/harness/pi.py

isolation.pySupport scenario-specific cwd and linked worktrees +15/-0

Support scenario-specific cwd and linked worktrees

• Adds a launch working directory to each sandbox and a helper for creating isolated linked worktrees. Repository-owned hook and plugin paths remain anchored to the main sandbox repository.

docs/probes/2026-09-16-skills-discovery/lib/isolation.py

probe_skill.pyAdd mutation-safe and TUI-safe probe prompts +47/-3

Add mutation-safe and TUI-safe probe prompts

• Adds same-name skill variants with fresh tokens and marked follow-up prompts that cannot reuse earlier answers. Interactive reply extraction ignores echoed prompts and unrelated screen content.

docs/probes/2026-09-16-skills-discovery/lib/probe_skill.py

pty_driver.pyIntroduce a PTY-backed interactive session driver +387/-0

Introduce a PTY-backed interactive session driver

• Adds a compact terminal emulator and pseudo-terminal session manager that handles ANSI redraws, dialogs, delayed submission, reload commands, timeouts, logs, and process-group cleanup.

docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py

verdict.pyClassify live, stale, and revoked skills +27/-1

Classify live, stale, and revoked skills

• Adds lifecycle verdicts and judges for rewritten or deleted skills. Unmeasurable runs no longer trigger a third disagreement-breaking attempt.

docs/probes/2026-09-16-skills-discovery/lib/verdict.py

probe.pyOrchestrate lifecycle scenarios S5 through S10 +346/-40

Orchestrate lifecycle scenarios S5 through S10

• Adds mode-aware scenario routing for live mutation, startup mutation, resume, nested cwd, linked worktrees, and peer hooks. It persists first-turn evidence, drives interactive sessions, and treats answerless runs as unmeasurable.

docs/probes/2026-09-16-skills-discovery/probe.py

report.pySummarize lifecycle results by mode and scenario +39/-4

Summarize lifecycle results by mode and scenario

• Adds report columns for every pass-two capability and keeps daemon live-catalogue outcomes separate from interactive-only measurements.

docs/probes/2026-09-16-skills-discovery/report.py

Bug fix (2) +52 / -9
opencode_v2.pyIsolate OpenCode v2 background services +43/-4

Isolate OpenCode v2 background services

• Stops stale background services before launches and after daemon sessions to prevent cross-sandbox catalogue reuse. It also defines v2-specific ACP and interactive commands.

docs/probes/2026-09-16-skills-discovery/harness/opencode_v2.py

hook_script.pyPrevent startup hooks from consuming terminal input +9/-5

Prevent startup hooks from consuming terminal input

• Skips bounded stdin capture when the hook is attached to a TTY, preserving keystrokes for interactive vendor UIs.

docs/probes/2026-09-16-skills-discovery/lib/hook_script.py

Refactor (3) +226 / -129
acp_driver.pyRefactor ACP turns into persistent sessions +70/-32

Refactor ACP turns into persistent sessions

• Replaces the one-shot coroutine with an 'AcpSession' that keeps one agent process and session identifier across prompts. Raw events and tool counts are isolated per turn while the legacy helper remains a wrapper.

docs/probes/2026-09-16-skills-discovery/lib/acp_driver.py

appserver_driver.pyRefactor Codex app-server into a session driver +86/-55

Refactor Codex app-server into a session driver

• Encapsulates initialization, hook-trust seeding, thread creation, repeated turns, and cleanup in 'AppServerSession'. Each result contains only fresh turn evidence while preserving one-shot compatibility.

docs/probes/2026-09-16-skills-discovery/lib/appserver_driver.py

pirpc_driver.pyRefactor Pi RPC into a multi-turn session +70/-42

Refactor Pi RPC into a multi-turn session

• Introduces 'PiRpcSession' with sequential request identifiers, per-turn event extraction, tool counting, and explicit cleanup. The existing one-shot helper now delegates to the session implementation.

docs/probes/2026-09-16-skills-discovery/lib/pirpc_driver.py

Tests (2) +662 / -15
selftest.pyExpand probe coverage to 113 tests +620/-14

Expand probe coverage to 113 tests

• Adds tests for lifecycle verdicts, worktrees, resumes, persistent protocol sessions, PTY rendering, dialog handling, answerless failures, report columns, and every vendor's pass-two launch contract.

docs/probes/2026-09-16-skills-discovery/selftest.py

selftest_servers.pyAdd a fake interactive vendor server +42/-1

Add a fake interactive vendor server

• Implements a terminal-oriented fake vendor with startup hooks, trust dialogs, live or frozen catalogues, marked answers, screen redraws, and reload behavior.

docs/probes/2026-09-16-skills-discovery/selftest_servers.py

Documentation (5) +6880 / -1416
capability-matrix.mdPublish lifecycle and interactive capability columns +18/-18

Publish lifecycle and interactive capability columns

• Regenerates the capability table with live-catalogue, startup rewrite, resume, nested-cwd, worktree, peer-hook, and interactive outcomes. It also records updated versions and blocked Gemini measurements.

docs/probes/2026-09-16-skills-discovery/capability-matrix.md

findings.mdDocument pass-two measurements and consequences +131/-3

Document pass-two measurements and consequences

• Adds the lifecycle and interactive methodology, cross-vendor findings, materialization consequences, and PTY integration lessons. It distinguishes failed measurements from genuine negative discovery results.

docs/probes/2026-09-16-skills-discovery/findings.md

matrix.jsonRecord pass-two cross-vendor evidence +4940/-1387

Record pass-two cross-vendor evidence

• Regenerates the machine-readable matrix with S5–S10 and TUI measurements, updated vendor versions, repeated-run evidence, and explicit reasons for untested rows.

docs/probes/2026-09-16-skills-discovery/matrix.json

2026-09-17-ai2829-skills-discovery-pass-2.mdDocument the pass-two implementation plan +1720/-0

Document the pass-two implementation plan

• Adds the task-by-task architecture, test strategy, vendor launch details, scenario implementation, measurement budget, and regeneration procedure for the lifecycle probe expansion.

docs/superpowers/plans/2026-09-17-ai2829-skills-discovery-pass-2.md

2026-09-16-skills-discovery-probes-design.mdSpecify lifecycle and interactive probe semantics +71/-8

Specify lifecycle and interactive probe semantics

• Expands the design with persistent-session rules, S5–S10 definitions, new verdicts, per-mode coverage, PTY behavior, worktree isolation, and measurement costs.

docs/superpowers/specs/2026-09-16-skills-discovery-probes-design.md

Other (2) +66 / -9
.gitignoreIgnore interactive probe logs +1/-0

Ignore interactive probe logs

• Adds the generated '*-tui.log' pattern so PTY screen captures remain local evidence rather than committed artifacts.

docs/probes/2026-09-16-skills-discovery/.gitignore

fake.pyModel live and frozen fake-vendor sessions +65/-9

Model live and frozen fake-vendor sessions

• Extends the fake adapter with persistent sessions, resume behavior, PTY launches, reloadable frozen catalogues, and sandbox-scoped hooks. It provides deterministic coverage for lifecycle semantics.

docs/probes/2026-09-16-skills-discovery/harness/fake.py

@qodo-code-review

qodo-code-review Bot commented Sep 17, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. One conclusion includes untested results ✓ Resolved 🐞 Bug ≡ Correctness
Description
The S6 findings claim that startup-hook rewrites reach every harness except Claude while the same
paragraph records OpenCode 2.x as untested throughout. Because both OpenCode 2.x rewrite rows lack a
measurement, the published universal conclusion is broader than the matrix evidence supports.
Code

docs/probes/2026-09-16-skills-discovery/findings.md[R352-353]

+Rewriting an existing skill's body from the startup hook reaches the first request on every harness
+except Claude, which keeps serving the body it indexed (`stale`). Deleting the skill is honoured
Relevance

●●● Strong

Recent precedent accepts correcting documentation that overstates behavior beyond measured evidence.

PR-#767

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The prose makes the universal claim and immediately acknowledges that OpenCode 2.x has no result.
The generated capability matrix and underlying rows classify its startup rewrite as not run or
untested, so they cannot support inclusion in the claim.

docs/probes/2026-09-16-skills-discovery/findings.md[350-357]
docs/probes/2026-09-16-skills-discovery/capability-matrix.md[21-21]
docs/probes/2026-09-16-skills-discovery/matrix.json[10494-10526]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The startup-rewrite conclusion includes OpenCode 2.x despite its rewrite experiment being untested in every applicable mode.

## Fix Focus Areas
- docs/probes/2026-09-16-skills-discovery/findings.md[350-357]
- docs/probes/2026-09-16-skills-discovery/capability-matrix.md[21-21]
- docs/probes/2026-09-16-skills-discovery/matrix.json[10494-10526]

## Recommended Fix
Rewrite the conclusion to say that all measured harnesses except Claude observed the rewritten body, and explicitly exclude OpenCode 2.x because its startup plugin never loaded. Keep the statement aligned with the capability matrix's not-run status.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Interactive measurements read damaged screens ✓ Resolved 🐞 Bug ☼ Reliability
Description
PtySession._read() passes each arbitrary decoded os.read() chunk directly to Terminal.feed(),
whose parser consumes incomplete ESC, CSI, or OSC sequences without retaining them for the next
chunk. When the operating system splits cursor-motion or erase sequences across reads, the
continuation is rendered as screen text, corrupting the reconstructed terminal state so
extract_tui_reply() can miss or misread the marked response.
Code

docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[R258-260]

+            with self._lock:
+                self.term.feed(self._decoder.decode(chunk))
+                self._seen = True
Relevance

●● Moderate

Terminal-fragmentation correctness concern is technically specific, but lacks close repository
precedent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PTY reads do not guarantee that terminal control sequences align with chunk boundaries, but CSI and
OSC matching requires a complete sequence within the current input string, and an unmatched escape
advances the parser by two characters even at the end of a chunk. Because every os.read() result
is decoded and fed independently, the cursor-motion sequences emitted by the self-test TUI can be
fragmented, altering the reconstructed screen later passed to extract_tui_reply().

docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[52-76]
docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[250-260]
docs/probes/2026-09-16-skills-discovery/selftest_servers.py[139-147]
docs/probes/2026-09-16-skills-discovery/lib/probe_skill.py[116-120]
docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[48-77]
docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[250-263]
docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[310-338]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description

`PtySession` feeds arbitrary decoded PTY read chunks directly into the terminal emulator even though ANSI control sequences may be split at any byte boundary. The emulator consumes incomplete ESC, CSI, or OSC prefixes and renders their continuation as ordinary text, corrupting the reconstructed screen used to extract probe replies.

## Fix Focus Areas

- docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[48-77]
- docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[250-263]
- docs/probes/2026-09-16-skills-discovery/selftest.py[1350-1361]

## Recommended Fix

Make the terminal parser incremental by retaining an incomplete ESC, CSI, or OSC suffix between reads and prepending it to the next decoded chunk, so only complete terminal-control sequences are processed. Add tests that split each supported control sequence, including cursor-movement and erase sequences, at multiple chunk boundaries and verify both the final reconstructed screen and the extracted reply.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. A test docstring repeats its coverage ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
AdapterPassTwoTests adds a docstring that only enumerates the three adapter behaviors exercised by
the class. The test methods already expose resume, session-ID, and interactive-launch coverage, so
the sentence adds no non-obvious constraint and must be updated whenever that coverage changes.
Code

docs/probes/2026-09-16-skills-discovery/selftest.py[1718]

+    """Every adapter's resume launch, session id reader and interactive launch."""
Relevance

●●● Strong

Recent documentation-maintainability precedent accepts removing redundant explanatory narration.

PR-#692

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2762993 restricts comments to non-obvious, behavior-critical constraints and
rejects comments that duplicate information apparent from the surrounding code. The added class
docstring only lists the test coverage represented by the class and its methods.

Rule 2762993: Restrict comments to documenting non-obvious, behavior‑critical constraints
docs/probes/2026-09-16-skills-discovery/selftest.py[1718-1718]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`AdapterPassTwoTests` has a docstring that merely restates the behaviors covered by its test methods rather than documenting a non-obvious constraint.

## Fix Focus Areas
- docs/probes/2026-09-16-skills-discovery/selftest.py[1718-1718]

## Recommended Fix
Remove the redundant class docstring. If the class requires documentation, replace it with a concise explanation of a behavior-critical constraint that cannot be inferred from the test names.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. A deletion erases the whole skill root ✓ Resolved 🐞 Bug ≡ Correctness
Description
arm_s6() activates write_hook_script(..., delete=True), whose delete action removes
skill_file.parent rather than the requested skill path. For Antigravity's flat layout that parent
is the shared native skills root, so the deletion arm tests removal of the entire catalogue
directory instead of removal of one skill.
Code

docs/probes/2026-09-16-skills-discovery/probe.py[493]

+            script = write_hook_script(sb.config_root, target, "", stamp_path(sb.config_root), delete=True)
Relevance

●●● Strong

Collateral deletion of a shared root is an obvious correctness defect in the newly added experiment.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Flat adapters represent a skill as <root>/<name>.md, making the file's parent the entire native
root. The newly added S6 delete arm requests the pre-existing directory-removal behavior and then
validates only that the target disappeared, so collateral root removal is accepted as a successful
experiment.

docs/probes/2026-09-16-skills-discovery/probe.py[481-511]
docs/probes/2026-09-16-skills-discovery/lib/hook_script.py[10-18]
docs/probes/2026-09-16-skills-discovery/harness/base.py[142-146]
docs/probes/2026-09-16-skills-discovery/harness/agy.py[23-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The startup-hook deletion arm removes the complete native skills directory for flat-layout adapters instead of deleting only the probe skill.

## Fix Focus Areas
- docs/probes/2026-09-16-skills-discovery/probe.py[481-511]
- docs/probes/2026-09-16-skills-discovery/lib/hook_script.py[10-18]
- docs/probes/2026-09-16-skills-discovery/harness/base.py[142-146]

## Recommended Fix
Pass the intended deletion target explicitly or make `write_hook_script` remove `skill_file` for flat layouts and the per-skill directory for directory layouts. Add a test with a sibling skill and assert that the sibling and native root remain after S6 deletion.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Failed interactive startup leaks processes ✓ Resolved 🐞 Bug ☼ Reliability
Description
PtySession.start() starts the child and reader thread before calling wait_ready(), while
Adapter.open_session() does not close the partially started session if readiness raises. A
timeout, silent client, or premature exit occurs before _open() returns and before the runner
enters its finally block, reaching the child process group, PTY descriptor, reader thread, and log
handle without cleanup.
Code

docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[R246-248]

+        self._reader = threading.Thread(target=self._read, daemon=True)
+        self._reader.start()
+        self.wait_ready(self.timeout)
Relevance

●● Moderate

Potential PTY startup leak is plausible, but historical searches found no closely matching team
precedent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The cited PTY code starts the child and reader before wait_ready(), which can explicitly raise on
timeout or early exit, while PtySession.close() contains the required process-group, descriptor,
thread, and log cleanup. The adapter returns the session only after start() succeeds, and _ask()
enters its cleanup block only after _open() returns, so neither caller can close the session on a
readiness exception; the current timeout test masks this integration path by manually calling
close().

docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[230-248]
docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[297-308]
docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[363-387]
docs/probes/2026-09-16-skills-discovery/harness/base.py[127-133]
docs/probes/2026-09-16-skills-discovery/probe.py[269-275]
docs/probes/2026-09-16-skills-discovery/selftest.py[1406-1413]
docs/probes/2026-09-16-skills-discovery/harness/base.py[127-132]
docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[246-248]
docs/probes/2026-09-16-skills-discovery/probe.py[268-274]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Interactive session readiness failures occur after the vendor process, PTY, reader thread, and log handle are allocated but before `Adapter.open_session()` returns the session to the runner, so the runner's `finally` block cannot clean them up.

## Fix Focus Areas
- docs/probes/2026-09-16-skills-discovery/harness/base.py[127-132]
- docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[230-248]
- docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py[363-387]
- docs/probes/2026-09-16-skills-discovery/selftest.py[1406-1413]

## Recommended Fix
Catch readiness/startup exceptions and invoke the idempotent session cleanup path before re-raising. Ensure `close()` is safe for a partially started session and terminates the process group while closing all PTY, thread, and log resources; update the timeout coverage to exercise `Adapter.open_session()` and verify that the child and resources are cleaned up immediately after failure without manually calling `close()` first.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (1)
6. Failed session startup leaks processes ✓ Resolved 🐞 Bug ☼ Reliability
Description
AcpSession._start() and AppServerSession.start() spawn their child processes before performing
fallible protocol initialization, but neither closes the child when initialization raises. Daemon
adapters call start() before returning a session to the probe, so initialization timeouts,
malformed responses, or early exits bypass the caller's cleanup and leave vendor processes and file
descriptors alive.
Code

docs/probes/2026-09-16-skills-discovery/lib/acp_driver.py[R113-115]

+        await self.client.start()
+        await self.client.request("initialize", INIT_PARAMS, timeout=90)
+        new = await self.client.request("session/new", {"cwd": str(self.cwd), "mcpServers": []}, timeout=120)
Relevance

●● Moderate

Potential resource leak is specific and plausible, but no closely matching accepted or rejected
precedent was found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both session implementations spawn before issuing initialization requests, while daemon adapters
invoke start() before returning the object. The probe's cleanup only calls close() after
_open() returns, so an exception during startup has no owning session available to clean up.

docs/probes/2026-09-16-skills-discovery/lib/acp_driver.py[109-118]
docs/probes/2026-09-16-skills-discovery/lib/acp_driver.py[147-153]
docs/probes/2026-09-16-skills-discovery/lib/appserver_driver.py[77-108]
docs/probes/2026-09-16-skills-discovery/lib/appserver_driver.py[143-145]
docs/probes/2026-09-16-skills-discovery/harness/codex.py[50-56]
docs/probes/2026-09-16-skills-discovery/probe.py[438-468]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
ACP and app-server sessions can leave spawned vendor processes alive when protocol initialization fails before the session is returned to its caller.

## Fix Focus Areas
- docs/probes/2026-09-16-skills-discovery/lib/acp_driver.py[109-118]
- docs/probes/2026-09-16-skills-discovery/lib/appserver_driver.py[77-108]

## Recommended Fix
Wrap each post-spawn initialization sequence in exception handling that shuts down the child and closes the event loop or other owned resources before re-raising. Preserve the existing successful-start behavior and make cleanup safe after partial initialization.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 64 rules
✅ Cross-repo context — repo relationships
  Explored: repo: kurrent-io/cloud-meta (sha: 15782900)
Review mode: 🧠 Deep: This is a highly logic-dense probe-system change spanning shared drivers, nine harnesses, PTY interaction, lifecycle/resume behavior, isolation, reporting, and extensive tests, creating many independent paths where a second review could catch subtle defects.

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread docs/probes/2026-09-16-skills-discovery/selftest.py Outdated
Comment thread docs/probes/2026-09-16-skills-discovery/lib/acp_driver.py
Comment thread docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py Outdated
Comment thread docs/probes/2026-09-16-skills-discovery/probe.py Outdated
Comment thread docs/probes/2026-09-16-skills-discovery/findings.md Outdated
Comment thread docs/probes/2026-09-16-skills-discovery/lib/pty_driver.py

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e80f165ad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +33 to +34
"if [ ! -t 0 ]; then\n"
" { exec 3<&0; } 2>/dev/null || exec 3</dev/null\n"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Guard the terminal check against closed stdin

When a vendor invokes the startup hook with fd 0 closed, this test -t condition causes /bin/sh to exit with status 2 before the fallback on the next line can run. This is reproduced by HookScriptTests.test_script_tolerates_a_closed_stdin, which now fails; because the mutation and fired stamp are written before this point, lifecycle scenarios can record the hook as fired even though the vendor received a failing hook and may change or abort startup behavior. Make the terminal test itself tolerate an invalid descriptor.

Useful? React with 👍 / 👎.

alexeyzimarev and others added 2 commits September 17, 2026 19:15
)

A read ends wherever the kernel split it, so a cursor sequence cut in half was rendered as screen text. A session whose startup failed is closed by the starter, since nobody else holds it, and a hook proves fd 0 is readable before exec dups it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The workspaceOpen hook writes into a plugin directory it returns, so the arm was checking a path nobody creates and could only ever record untested. Both the project file and the user file deliver the skill.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@alexeyzimarev
alexeyzimarev merged commit 8e40b6c into alexeyzimarev/ai-2829-verify-repo-local-skill-discovery-and-startup-ordering Sep 17, 2026
8 checks passed
@alexeyzimarev
alexeyzimarev deleted the alexeyzimarev/ai-2829-skills-discovery-pass-2 branch September 17, 2026 19:33
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