Skip to content

feat(sdk): add external agent skill bundle - #4164

Open
Yeachan-Heo wants to merge 8 commits into
devfrom
feat/sdk-skills
Open

feat(sdk): add external agent skill bundle#4164
Yeachan-Heo wants to merge 8 commits into
devfrom
feat/sdk-skills

Conversation

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Summary

  • generate the exact five-file host-neutral gjc-sdk-* external-agent bundle
  • add direct TypeScript/Python SDK templates with fail-closed discovery, credential redaction, allowlists, and nonce-bound approval
  • enforce bundle drift, standalone template type checks, behavioral tests, docs, and changelog updates

Verification

  • bun test scripts/generate-gjc-sdk-skills.test.ts (8 pass)
  • bun run check:sdk-skills (25 gates)
  • bun run check:tools
  • Python SDK suite (36 pass, 1 skip)
  • strict mypy over SDK and generated Python template
  • bun run test:release (108 pass)
  • focused SDK regressions (10 pass)
  • public sync and diff checks

Ultragoal

  • durable goal G001 complete
  • final critic verdict: OKAY
  • final source hash: sha256:8852026a293cfe2c0838de56cc9718a96972d03ea74778d673e7413f169f074b

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Late full check:sdk-closure run completed after the implementation receipt. It passed the long SDK manifests, rollback tests, and canonicalization checks, then failed the pre-rename reference verifier on scripts/ci-risk-canary-manifest.ts:70 (packages/coding-agent/src/notifications/). That exact line is already present on origin/dev and this PR does not modify the file, so the failure is an existing baseline issue rather than a regression from the SDK skills bundle. All PR-scoped SDK skill, template, type, release, Python, and focused SDK checks remain green.

@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: 59c1586b8f

ℹ️ 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 thread scripts/generate-gjc-sdk-skills.ts Outdated
)
digest = hashlib.sha256(payload.encode("utf-8")).hexdigest()[:16]
challenge = f"APPROVE {session_id} {operation} {digest} {secrets.token_hex(8)}"
answer = input(f"Approval required: {challenge}\\nType the exact challenge: ")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the approval prompt off standard output

In Python control mode, input(prompt) writes the approval challenge to stdout before the JSON result is printed. External controllers that treat this template as a JSON-producing direct client therefore receive mixed prose and JSON on every successful control request and cannot parse the response reliably; emit the challenge on stderr and read stdin without an stdout prompt, as the TypeScript template does.

Useful? React with 👍 / 👎.

Comment thread scripts/generate-gjc-sdk-skills.ts Outdated
const ALLOWED_GLOBALS = ["session.create", "session.fork", "session.resume", "session.close"] as const;

function discoverSkill(): string {
return `---

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 Move generated skill prompts into static Markdown sources

The generator makes TypeScript template literals the authoritative source for all three skill prompts. This directly conflicts with the repository contract requiring prompt content to live in static .md files rather than being constructed inline; keep the prose in static source files and have the generator copy or validate those sources.

AGENTS.md reference: AGENTS.md:L120-L120

Useful? React with 👍 / 👎.

Comment thread scripts/generate-gjc-sdk-skills.ts Outdated
problems.push(`invalid: sdk-skills/${rel}`);
continue;
}
actual = fs.readFileSync(target, "utf8");

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 Replace synchronous filesystem access in the generator

The new generator performs its drift checks and generation with synchronous Node filesystem calls such as readFileSync, with corresponding synchronous writes and directory operations later in the same script. The repository contract requires Bun.file()/Bun.write() for file contents and node:fs/promises for directory operations, so this release/CI path should use those APIs instead.

AGENTS.md reference: AGENTS.md:L124-L132

Useful? React with 👍 / 👎.

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

NEEDS-WORK — the four-skill gate is clean, but this ships a permanent public on-disk format with no version, and the generator violates the repo's prompt-authoring contract.

The gate is clean — leading with it since the title is alarming

"add external agent skill bundle" sounds like it adds defaults. It does not:

  • Nothing under packages/coding-agent/src/defaults/gjc/skills/ or packages/coding-agent/src/prompts/agents/ is touched. The four default workflow skills and four role agents are untouched.
  • No gate script modifiedcheck-visible-definitions.ts, verify-g002-gates.ts, rebrand-inventory.ts, default-gjc-definitions.test.ts all unchanged. The PR does not widen its own gate.
  • No repo-visible .gjc default definitions committed.
  • Missing, drifted, symlinked or unexpected generated files fail the repository check.

Consistent with the precedent from #4116: skills outside the bundled defaults directory are not covered by the four-skill assertion.

Rebases cleanly. Focused TypeScript 8 pass / 0 fail, focused Python 6 pass / 0 fail.

Finding 1 — the on-disk bundle format is unversioned

docs/sdk.md:79-89 and scripts/generate-gjc-sdk-skills.test.ts:123-135 document and pin exact directory names, a five-file layout, frontmatter, and template paths. Those are permanent public contracts the moment someone authors against them — but there is no manifest and no format version, and no versioned root.

Consumers cannot distinguish a compatible bundle from a future incompatible layout, and there is no way to fail closed on an unsupported version. Adding a version field costs almost nothing now and is close to impossible to retrofit once third parties ship bundles. This is the finding I would most want fixed before merge.

Finding 2 — skill prompts are authored as inline TypeScript strings

scripts/generate-gjc-sdk-skills.ts:23-150 builds SKILL.md bodies as template literals inside functions like discoverSkill().

AGENTS.md is explicit: "Prompts live in static .md files imported with with { type: \"text\" }; never build prompts inline." Same rule, same reason — prompt text should be reviewable and diffable as content, not embedded in a generator.

Make the static Markdown files authoritative and have the generator copy and validate them.

Finding 3 — document the trust boundary plainly

To be fair to the PR, it already discloses the blast radius accurately, and the templates are correctly described as procedural controls rather than a security boundary (sdk-skills/gjc-sdk-operate/SKILL.md:8).

Worth stating explicitly in docs/sdk.md anyway, because it is easy to misread: authentication checks only possession of the endpoint token (packages/coding-agent/src/sdk/host/websocket-transport.ts:151-155). Past that, requests reach the SDK operation registry dispatcher, so a modified script with a valid token bypasses the template allowlist entirely and can reach managed bash, configuration, permissions, tools, extensions and destructive session operations (packages/coding-agent/src/sdk/protocol/operation-registry.ts:66-120).

A reader who sees a curated "skill bundle" may assume the bundle constrains what an external agent can do. It does not — the token does.

Smaller

The Python control test challenges stderr and reads stdin without a stdout prompt, matching the TypeScript implementation, but there is no subprocess-level assertion that a successful control's stdout parses directly as JSON. Worth adding — that is the actual contract an external consumer depends on.

Net

No defect in shipped behaviour and the gate is clear. Version the bundle format and move the prompts to static .md, and this is an approve.

Yeachan-Heo pushed a commit that referenced this pull request Aug 10, 2026
…from static markdown

Addresses the maintainer REQUEST_CHANGES on #4164: the on-disk bundle format
had no version root, and the generator authored skill prompts inline as
TypeScript template literals.

- sdk-skills/manifest.json is the versioned root of the bundle contract
  (formatVersion 1) and declares the exact file closure; checks fail closed on
  missing, malformed, or unsupported versions, and legacy unversioned layouts
  are rejected with a regeneration hint instead of being read ambiguously.
- The three skill prompts are authored as static markdown under
  scripts/gjc-sdk-skills/prompts/, imported with `with { type: "text" }`, and
  copied verbatim by the generator; a consistency validator keeps the static
  allowlist blocks in sync with the ALLOWED_CONTROLS/ALLOWED_GLOBALS the
  templates embed.
- docs/sdk.md documents the bundle format version contract and the token-only
  trust boundary (a modified script holding the endpoint token reaches the
  operation registry dispatcher, not just the template allowlist).
- The Python template emits the approval challenge on stderr and reads the
  answer from stdin, so a successful control's stdout parses directly as JSON;
  a real-session subprocess test asserts that exact contract.
- Tests now prove deterministic closure, four-skill default preservation, no
  extra skills, upgrade compatibility (v1 installs valid; legacy/future layouts
  fail closed), and byte-for-byte generated artifact parity.

Lore-id: pr-4164-repair
Constraint: do not weaken the four-skill gate or existing installed bundles
Rejected: inline prompt strings in the generator | violates prompt-authoring contract
Rejected: unversioned five-file layout | consumers cannot fail closed on incompatible bundles
Confidence: high
Scope-risk: narrow
Reversibility: revertible
Tested: generator suite 14 pass; python template 7 pass incl real-session; check:sdk-skills 28 gates; check:tools; check:public-sync; check-visible-definitions; rebrand-inventory --strict; default-gjc-definitions 29 pass; verify-g002-gates at baseline parity
Not-tested: full workspace check:ts aggregate and CI matrix
Supersedes: 59c1586 review findings

@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: fba0187647

ℹ️ 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".

snapshot: dict[str, Any] = {}
for query in CORE_QUERIES:
try:
response = await client.query(query, {})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bound Python SDK requests with a timeout

When a live WebSocket continues answering pings but never returns a query response, this await blocks indefinitely because the Python SdkClient and WsTransport.receive_text() impose no request timeout. The inspection template then neither preserves the remaining partial results nor reaches its finally close; the control await has the same problem after a mutation may already have been accepted. Wrap both query and control requests in a bounded asyncio.wait_for(...) (treating timed-out queries as unavailable) so the direct client terminates predictably.

Useful? React with 👍 / 👎.

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The stale review’s substantive requests are addressed: the bundle now has a fail-closed versioned manifest, the prompts are static imported Markdown, the trust boundary is explicit, and the Python subprocess test covers JSON-only stdout. I also reviewed the newest 7bf8e1f0 CI fix; marking root-test:release and release-publish-contract as native consumers matches the commands they execute, and the added assertions cover the emitted matrix contract.

Findings

Major — generator violates the repository filesystem contract

scripts/generate-gjc-sdk-skills.ts:92-97, :480-485, :505-519, and :534-539 implement the new generator/checker with node:fs synchronous APIs (lstatSync, readFileSync, readdirSync, rmSync, mkdirSync, writeFileSync). AGENTS.md explicitly requires Bun.file()/Bun.write() for file I/O and node:fs/promises for directory operations. This is the shipped generator and CI drift gate, not incidental legacy code. Convert these paths to async Bun/promises APIs and make the exported checker/generator entry points asynchronous rather than introducing a new synchronous filesystem surface.

Minor — new test uses forbidden ReturnType<>

scripts/generate-gjc-sdk-skills.test.ts:20 declares Array<ReturnType<typeof Bun.serve>>. AGENTS.md says never to use ReturnType<>; name the concrete Bun server type instead.

The new tests otherwise exercise observable behavior (manifest rejection, drift and symlink rejection, discovery ambiguity, redaction, exact approval binding/replay, endpoint revalidation, and subprocess stdout purity). I found no new placeholder tests, bare not.toThrow(), or mock.module() in the added coverage.

gajae.pr-review-verdict.v1: needs-human

@Yeachan-Heo Yeachan-Heo left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Terminal Verdict — OWNER_CONFIRMATION_REQUIRED

PR #4164 · feat(sdk): add external agent skill bundle · exact head 7bf8e1f042e6944f7b8d307092a007d8fc5fcf5a (base 7858b0ff63, current dev)

PR-scoped release/root gates — GREEN (exact-head CI)

  • root-test:releasesuccess (was the only PR-local failure on the prior head; fixed by provisioning the native addon for the release-contract shard via taskNeedsNative, commit 7bf8e1f042)
  • root-check — success · Python 3.10–3.13 matrix — success · gjc-state-gates (static/integrity/read/runtime) — success · Telegram daemon generation guard — success · Public site sync — success
  • Local gates on the same head: check:sdk-skills 28 gates, check:tools, check:public-sync, check-visible-definitions, rebrand-inventory --strict, default-gjc-definitions 29 pass, strict mypy over the generated Python template, test:release 114 pass, generator suite 14 pass, Python template suite 7 pass incl. real-session subprocess JSON-stdout contract.

The three remaining failures — baseline-reproducible, NOT caused by #4164

Reproduced identically on a clean origin/dev worktree by running the exact failing test files:

  • shard-1-of-8: session title source persistence > moveTo header patch persistence > rejects when the moved session cwd patch cannot be written
  • shard-6-of-8: ultragoal resident-cache adversarial QA > C1 … / C7 …
  • shard-7-of-8: resident cache root derivation (4 cases)

These are the known #4151 sidecar-collision family tracked by #4183 (not yet merged to dev); origin/dev is still 7858b0ff63. Affected path validation and evidence producer fail purely as the downstream aggregate of those shards.

Reviewer findings — all addressed

Versioned on-disk bundle/storage format (sdk-skills/manifest.json, formatVersion: 1, fail-closed on missing/unsupported versions, legacy-layout rejection with migration hint) · skill prompts authored as canonical static Markdown (scripts/gjc-sdk-skills/prompts/, imported with { type: "text" }, copied verbatim, allowlist consistency validator) · trust boundary documented in docs/sdk.md · Python approval challenge moved to stderr with a subprocess-level pure-JSON-stdout assertion. Four-skill gate untouched; deterministic closure, upgrade compatibility, no-extra-skills, and byte-for-byte artifact parity all covered by tests.

Why not MERGE_READY

Exact-head CI is not zero-failure (3 baseline shards + downstream aggregate). The owner must decide: wait for #4183 to land and rerun, or accept the documented baseline-red shards. No merge or release was performed.

Commit history on feat/sdk-skills: 54a368622e (PR reconstructed onto dev) → fba0187647 (review fixes) → 7bf8e1f042 (native-addon provisioning for release-contract shards). Remote lease verified before each force-with-lease push; remote branch confirmed at 7bf8e1f042.


[repo owner's gaebal-gajae (clawdbot) 🦞]

Yeachan-Heo pushed a commit that referenced this pull request Aug 11, 2026
…from static markdown

Addresses the maintainer REQUEST_CHANGES on #4164: the on-disk bundle format
had no version root, and the generator authored skill prompts inline as
TypeScript template literals.

- sdk-skills/manifest.json is the versioned root of the bundle contract
  (formatVersion 1) and declares the exact file closure; checks fail closed on
  missing, malformed, or unsupported versions, and legacy unversioned layouts
  are rejected with a regeneration hint instead of being read ambiguously.
- The three skill prompts are authored as static markdown under
  scripts/gjc-sdk-skills/prompts/, imported with `with { type: "text" }`, and
  copied verbatim by the generator; a consistency validator keeps the static
  allowlist blocks in sync with the ALLOWED_CONTROLS/ALLOWED_GLOBALS the
  templates embed.
- docs/sdk.md documents the bundle format version contract and the token-only
  trust boundary (a modified script holding the endpoint token reaches the
  operation registry dispatcher, not just the template allowlist).
- The Python template emits the approval challenge on stderr and reads the
  answer from stdin, so a successful control's stdout parses directly as JSON;
  a real-session subprocess test asserts that exact contract.
- Tests now prove deterministic closure, four-skill default preservation, no
  extra skills, upgrade compatibility (v1 installs valid; legacy/future layouts
  fail closed), and byte-for-byte generated artifact parity.

Lore-id: pr-4164-repair
Constraint: do not weaken the four-skill gate or existing installed bundles
Rejected: inline prompt strings in the generator | violates prompt-authoring contract
Rejected: unversioned five-file layout | consumers cannot fail closed on incompatible bundles
Confidence: high
Scope-risk: narrow
Reversibility: revertible
Tested: generator suite 14 pass; python template 7 pass incl real-session; check:sdk-skills 28 gates; check:tools; check:public-sync; check-visible-definitions; rebrand-inventory --strict; default-gjc-definitions 29 pass; verify-g002-gates at baseline parity
Not-tested: full workspace check:ts aggregate and CI matrix
Supersedes: 59c1586 review findings
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Repaired and rebased feat/sdk-skills onto current origin/dev (ff43aa74a48fc7f8a92c3977c95970bdcced99df).

Exact head: 349aca650aac02c80e596a3292d72bd3ef0c281b. The SDK skill generator, drift checker, verifier, and self-test now use asynchronous Bun.file/Bun.write and node:fs/promises operations; the exported validation surfaces are async, the verifier awaits them, and the test names the concrete Bun.Server<undefined> type instead of ReturnType<>.

Local exact-head evidence: bun run check:sdk-skills (28 gates), bun test scripts/generate-gjc-sdk-skills.test.ts (14 pass), bun --cwd=packages/coding-agent run check, bun run check:tools, and bun run test:py-sdk (36 passed, 2 skipped). A matching native addon was rebuilt locally before the subprocess template suite after its initial loader version mismatch; generated native output was not committed. CI is being driven on this head.

[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Updated exact head: 0de1052aa63c7f5eb13d270535a0d3d4a35ec479.

In addition to the async SDK bundle repair, this head repairs the dirty current-dev integration discovered in exact-head CI: browser runtime diagnostics had changed the eager tool schema without regenerating tool-catalog.generated.ts. The generated catalog now contains the diagnostics option and bun test packages/coding-agent/test/tools/tool-catalog.test.ts passes (3/3).

Current local evidence: bun run check:sdk-skills (28 gates), bun test scripts/generate-gjc-sdk-skills.test.ts (14/14), bun run test:py-sdk (36 passed, 2 skipped), bun run check:tools, and bun --cwd=packages/coding-agent run check. The prior exact-head shard-4 failure is reproduced identically on clean origin/dev before this generated-catalog repair; the current branch includes the deterministic generated correction. Exact-head CI is being driven on 0de1052a.

[repo owner's gaebal-gajae (clawdbot) 🦞]

@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: 0de1052aa6

ℹ️ 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 thread scripts/generate-gjc-sdk-skills.test.ts Outdated
@@ -0,0 +1,427 @@
import { afterEach, describe, expect, it } from "bun:test";
import * as fs from "node:fs";

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 Replace synchronous filesystem helpers in the new test

The newly added test suite imports node:fs and repeatedly uses rmSync, mkdtempSync, mkdirSync, writeFileSync, and readFileSync for setup, assertions, and cleanup. Repository tooling requires Bun.file()/Bun.write() for file contents and node:fs/promises for directory operations, so convert these helpers and hooks to async equivalents rather than adding a large synchronous filesystem path to the release suite.

AGENTS.md reference: AGENTS.md:L124-L132

Useful? React with 👍 / 👎.

Yeachan-Heo pushed a commit that referenced this pull request Aug 11, 2026
…from static markdown

Addresses the maintainer REQUEST_CHANGES on #4164: the on-disk bundle format
had no version root, and the generator authored skill prompts inline as
TypeScript template literals.

- sdk-skills/manifest.json is the versioned root of the bundle contract
  (formatVersion 1) and declares the exact file closure; checks fail closed on
  missing, malformed, or unsupported versions, and legacy unversioned layouts
  are rejected with a regeneration hint instead of being read ambiguously.
- The three skill prompts are authored as static markdown under
  scripts/gjc-sdk-skills/prompts/, imported with `with { type: "text" }`, and
  copied verbatim by the generator; a consistency validator keeps the static
  allowlist blocks in sync with the ALLOWED_CONTROLS/ALLOWED_GLOBALS the
  templates embed.
- docs/sdk.md documents the bundle format version contract and the token-only
  trust boundary (a modified script holding the endpoint token reaches the
  operation registry dispatcher, not just the template allowlist).
- The Python template emits the approval challenge on stderr and reads the
  answer from stdin, so a successful control's stdout parses directly as JSON;
  a real-session subprocess test asserts that exact contract.
- Tests now prove deterministic closure, four-skill default preservation, no
  extra skills, upgrade compatibility (v1 installs valid; legacy/future layouts
  fail closed), and byte-for-byte generated artifact parity.

Lore-id: pr-4164-repair
Constraint: do not weaken the four-skill gate or existing installed bundles
Rejected: inline prompt strings in the generator | violates prompt-authoring contract
Rejected: unversioned five-file layout | consumers cannot fail closed on incompatible bundles
Confidence: high
Scope-risk: narrow
Reversibility: revertible
Tested: generator suite 14 pass; python template 7 pass incl real-session; check:sdk-skills 28 gates; check:tools; check:public-sync; check-visible-definitions; rebrand-inventory --strict; default-gjc-definitions 29 pass; verify-g002-gates at baseline parity
Not-tested: full workspace check:ts aggregate and CI matrix
Supersedes: 59c1586 review findings

@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: b7a17c9390

ℹ️ 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".

#!/usr/bin/env bun

import { createHash, randomBytes } from "node:crypto";
import { lstat, readFile, readdir } from "node:fs/promises";

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 Use Bun filesystem APIs in the TypeScript template

The remaining generated TypeScript template imports the Node filesystem helpers directly and later reads endpoint records with readFile, while the repository contract requires Bun.file() for file contents and namespace imports from node:fs/promises for directory operations. Update both this generated artifact and its authoritative generator so regeneration does not restore the prohibited implementation.

AGENTS.md reference: AGENTS.md:L124-L132

Useful? React with 👍 / 👎.

import { lstat, readFile, readdir } from "node:fs/promises";
import * as path from "node:path";
import { createInterface } from "node:readline/promises";
import { SdkClient, listSdkSessionEndpoints, type SdkSessionEndpoint } from "@gajae-code/coding-agent/sdk";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Align the canonical template with the documented dependency

When an external user follows docs/sdk.md and installs only the recommended @gajae-code/bridge-client, this canonical template immediately fails because it instead resolves @gajae-code/coding-agent/sdk; a globally installed gjc package does not satisfy local module resolution. Either use the standalone client and provide discovery locally, or explicitly make installation of @gajae-code/coding-agent part of the bundle's setup contract and test the template outside the monorepo workspace.

Useful? React with 👍 / 👎.

Comment on lines +498 to +502
[path.join("gjc-sdk-discover", "SKILL.md"), discoverPrompt],
[path.join("gjc-sdk-operate", "SKILL.md"), operatePrompt],
[path.join("gjc-sdk-author", "SKILL.md"), authorPrompt],
[path.join("gjc-sdk-author", "templates", "direct-sdk.ts"), typeScriptTemplate()],
[path.join("gjc-sdk-author", "templates", "direct-sdk.py"), pythonTemplate()],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep manifest paths platform-independent

On Windows, these path.join calls produce backslash-separated logical bundle keys, so manifestFile() renders a different file list from the committed forward-slash contract and bun run check:sdk-skills reports drift; running generation there also rewrites the versioned manifest incompatibly. Use fixed POSIX-relative keys for the map and reserve path.join for converting those keys to filesystem paths.

Useful? React with 👍 / 👎.

Comment on lines +75 to +77
const discoveryDirectory = path.join(repo, ".gjc", "state", "sdk");
const directoryStat = await lstat(discoveryDirectory).catch(() => undefined);
if (!directoryStat || directoryStat.isSymbolicLink() || !directoryStat.isDirectory())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject symlinked discovery ancestors

When <repo>/.gjc or <repo>/.gjc/state is a symlink, lstat() on the final sdk directory follows that intermediate link and reports an ordinary directory, allowing the template to select and authenticate to out-of-repository discovery records despite its documented fail-closed handling for symlinked discovery. Validate every path component or compare a resolved path against the intended repository root; the Python template's final-component-only check has the same gap.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Updated exact head: b7a17c93905ab24a554aa2be8501fbf6737a3631, rebased onto origin/dev e40129516965d5c081319898deba3c695f0dc29b.

The async SDK bundle repair now has a cross-platform fail-closed read boundary: POSIX reads use O_NOFOLLOW descriptors; Windows compares regular-file path and descriptor identities before and after its descriptor read. The generated bundle test fixtures are fully async (node:fs/promises for directories, Bun.file/Bun.write for contents), and manifest/content symlink rejection is covered.

Exact-head local evidence: bun test scripts/generate-gjc-sdk-skills.test.ts (14 pass), bun run check:sdk-skills (28 gates), bun test packages/coding-agent/test/tools/tool-catalog.test.ts (3 pass), bun --cwd=packages/coding-agent run check, bun run check:tools, and bun run test:py-sdk (36 passed, 2 skipped). CI is being driven for this head.

[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

@probepark The prior filesystem-contract findings are repaired on exact head b7a17c93905ab24a554aa2be8501fbf6737a3631, with a clean exact-head architecture review and focused gates in the signed evidence above. Please re-review this head.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Exact-head CI run 31446763156 was cancelled externally while its plan job was provisioning the Rust toolchain; the downstream evidence producer then failed only because the plan artifact was never uploaded (Artifact not found for name: dev-affected-plan-31446763156). This is a bounded CI cancellation, not a code/test failure.

I reproduced and repaired every actionable code blocker locally, and re-ran failed jobs for the exact pushed head b7a17c93905ab24a554aa2be8501fbf6737a3631. The rerun is queued.

[repo owner's gaebal-gajae (clawdbot) 🦞]

Yeachan-Heo pushed a commit that referenced this pull request Aug 11, 2026
…from static markdown

Addresses the maintainer REQUEST_CHANGES on #4164: the on-disk bundle format
had no version root, and the generator authored skill prompts inline as
TypeScript template literals.

- sdk-skills/manifest.json is the versioned root of the bundle contract
  (formatVersion 1) and declares the exact file closure; checks fail closed on
  missing, malformed, or unsupported versions, and legacy unversioned layouts
  are rejected with a regeneration hint instead of being read ambiguously.
- The three skill prompts are authored as static markdown under
  scripts/gjc-sdk-skills/prompts/, imported with `with { type: "text" }`, and
  copied verbatim by the generator; a consistency validator keeps the static
  allowlist blocks in sync with the ALLOWED_CONTROLS/ALLOWED_GLOBALS the
  templates embed.
- docs/sdk.md documents the bundle format version contract and the token-only
  trust boundary (a modified script holding the endpoint token reaches the
  operation registry dispatcher, not just the template allowlist).
- The Python template emits the approval challenge on stderr and reads the
  answer from stdin, so a successful control's stdout parses directly as JSON;
  a real-session subprocess test asserts that exact contract.
- Tests now prove deterministic closure, four-skill default preservation, no
  extra skills, upgrade compatibility (v1 installs valid; legacy/future layouts
  fail closed), and byte-for-byte generated artifact parity.

Lore-id: pr-4164-repair
Constraint: do not weaken the four-skill gate or existing installed bundles
Rejected: inline prompt strings in the generator | violates prompt-authoring contract
Rejected: unversioned five-file layout | consumers cannot fail closed on incompatible bundles
Confidence: high
Scope-risk: narrow
Reversibility: revertible
Tested: generator suite 14 pass; python template 7 pass incl real-session; check:sdk-skills 28 gates; check:tools; check:public-sync; check-visible-definitions; rebrand-inventory --strict; default-gjc-definitions 29 pass; verify-g002-gates at baseline parity
Not-tested: full workspace check:ts aggregate and CI matrix
Supersedes: 59c1586 review findings
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Current-dev integration refresh completed. Exact head: 8c1fccd8f6e954902f787f476941a9ddc7a1fa4f; exact base: e8081462a5e6befb1ec6f4b805dcb787136d85aa.

The rebase preserved the SDK bundle repairs and both Unreleased entries. Post-rebase checks passed: bun test scripts/generate-gjc-sdk-skills.test.ts (14 pass), bun run check:sdk-skills (28 gates), bun test packages/coding-agent/test/tools/tool-catalog.test.ts (3 pass), bun --cwd=packages/coding-agent run check, and bun run check:tools. Exact-head CI and re-review are being driven for this refreshed branch.

[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

@probepark Please re-review exact refreshed head 8c1fccd8f6e954902f787f476941a9ddc7a1fa4f; it is rebased onto current dev and its signed verification evidence is above.

@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: 8c1fccd8f6

ℹ️ 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".

const result =
args.mode === "inspect"
? await inspect(client, endpoint.token)
: await client.control(args.operation!, args.input, { confirm: 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.

P2 Badge Reconcile uncertain prompts before another invocation

When turn.prompt is accepted but its response is lost or exceeds SdkClient's default timeout, this call rejects and the template reports only a generic failure, even though the prompt may already be executing. Because turn.prompt is ordered and non-idempotent, rerunning the template after another approval can create a duplicate agent turn; require a fresh caller-retained clientRef for prompts and reconcile turn.prompt_status after an uncertain send rather than leaving retry safety to guesswork.

Useful? React with 👍 / 👎.

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

APPROVE — every finding from my two earlier rounds is fixed, exact-head CI is clean, and the one blocker I cited last round (baseline-red shards) no longer exists. One non-blocking scope note and two nits below.

Reviewed head: 8c1fccd8f6e954902f787f476941a9ddc7a1fa4f (21 files, +2140/−7).

Previously requested changes — all verified fixed

Round Finding Status
#1 On-disk bundle format unversioned Fixed. sdk-skills/manifest.json with bundle/formatVersion: 1/files; validateBundleManifest rejects missing, malformed, wrong-bundle, non-integer, unsupported-version, and file-list-mismatch cases, each with a regeneration hint. Version check runs before content drift checks, so an unknown layout is never content-diffed.
#1 Prompts embedded in generator (violates prompt-authoring contract) Fixed. scripts/gjc-sdk-skills/prompts/{author,discover,operate}.md imported with { type: "text" } and copied verbatim; validatePromptAllowlistConsistency() proves the rendered allowlist blocks in operate.md still match ALLOWED_CONTROLS/ALLOWED_GLOBALS.
#1 Trust boundary not stated in docs Fixed. docs/sdk.md "Trust boundary" says it plainly: auth checks only token possession, no per-request capability check, dispatcher reaches managed bash/config/permissions/tools/extensions, and the allowlists are "not a security boundary".
#1 Python control test lacked stdout-JSON assertion Fixed. python/gjc-sdk/tests/test_generated_skill_template.py asserts subprocess stdout parses as pure JSON.
#2 (Major) Generator used sync node:fs against AGENTS.md Fixed. node:fs/promises + Bun.file()/Bun.write() throughout; node:fs is imported only for Stats/constants. checkSdkSkillFiles, readBundleManifest, validateInstalledBundle, writeFiles are all async now.
#2 (Minor) ReturnType<> in test Fixed. const servers: Bun.Server<undefined>[].

I re-swept every .ts/.py file this PR adds for ReturnType<>, mock.module(), bare not.toThrow(), await import(), readFileSync/writeFileSync, and : anyzero hits in the new files. (The remaining hits in the sweep are pre-existing lines in scripts/ci-dev-affected.ts/.test.ts, which this PR only touches by +2 lines each.)

Last round's blocker is gone

I withheld MERGE_READY last time solely because exact-head CI had 3 baseline-red shards from the #4151/#4183 sidecar-collision family. That is resolved:

  • #4183 is merged into dev (b8f6c2645), and git merge-base --is-ancestor origin/dev pr-4164true, so this branch already contains it.
  • Checks at 8c1fccd8f6: 46 SUCCESS / 5 SKIPPED / 0 FAILURE, 2 cargo-build jobs still in progress. No red shards.

Gate re-verification (unchanged and clean)

  • No file under packages/coding-agent/src/defaults/gjc/skills/ or src/prompts/agents/ touched — the four workflow skills and four role agents are untouched.
  • No gate script modified (check-visible-definitions.ts, verify-g002-gates.ts, rebrand-inventory.ts, default-gjc-definitions.test.ts). The PR does not widen its own gate.
  • No repo-visible .gjc/ default definitions committed.
  • check:sdk-skills is wired into both check:sdk-closure and ci:check:full, so drift is enforced rather than advisory. tsconfig.tools.json typechecks the standalone TS template; check:py-sdk now runs strict mypy over the Python template too.
  • ./sdk is a real published export (packages/coding-agent/package.json./src/sdk/index.ts, and src is in files), and gjc_sdk exports Endpoint/SdkClient/read_session_endpoint/select_live_endpoint — both templates resolve for an external consumer, not just in-repo.

Findings

Minor — the tool-catalog regeneration is unrelated scope, and it is repairing a live dev defect

packages/coding-agent/src/tools/tool-catalog.generated.ts (+5/−1, commit ae8956bc4 fix(tools): regenerate browser diagnostics catalog) has nothing to do with an SDK skill bundle. It matters more than a stray file, because dev is genuinely stale right now:

  • origin/dev:packages/coding-agent/src/tools/browser.ts:90 declares diagnostics: z… (landed in b70775c44).
  • origin/dev:…/tool-catalog.generated.ts has no "diagnostics": { under the browser entry — its only "diagnostics" at line 1012 belongs to a different tool's enum.
  • This PR adds it back at line 1188.

So b70775c44 shipped a browser parameter without regenerating the catalog, and the repair is currently trapped behind a 2100-line feature PR. Cherry-pick ae8956bc4 to its own PR so dev gets the generated surface back in sync immediately; this PR then carries only the bundle. Not blocking the approval — I'd rather this land than sit.

Nit — readRegularFile conflates "absent" with "unreadable"

scripts/generate-gjc-sdk-skills.ts:109-127 returns null from a bare catch, and checkSdkSkillFiles maps every null to missing: sdk-skills/<rel> + "Run bun run generate-sdk-skills". An EACCES, EIO, or ELOOP therefore reports as a missing file and sends the operator to a regeneration that will not fix it. The verification logic is right to fail closed; only the diagnostic is lossy. Distinguishing ENOENT from everything else (and naming the code in the other branch) would keep the same fail-closed behaviour with an honest message.

Nit — the identity tuple is weaker than it reads on Windows

isSameRegularFileIdentity includes mtimeMs, which is attacker-controllable via utimes and has coarse resolution on some filesystems. On POSIX this is belt-and-braces on top of O_NOFOLLOW + fd-stat, so it is fine; on Windows it is the only check, since the O_NOFOLLOW term is 0 there. Worth a comment stating that the Windows path is best-effort, so nobody later mistakes it for a symlink-proof guarantee.

Notes, not requests

  • writeFiles() does fs.rm(bundleDir, { recursive: true, force: true }) before rendering. Correct for a fully-owned generated directory, and the file-set self-test covers stale-file removal — just flagging that it makes sdk-skills/ strictly generator-owned, which the docs now say explicitly.
  • Both templates are symmetric on input validation (--mode enum-checked, --input rejects non-objects/arrays), approval binds sha256({sessionId, operation, input}) plus a fresh nonce, workflow.gate_answer is forced to carry expectedSessionId, and the endpoint is re-selected and compared field-by-field (including token) after approval. That is the right ordering; I could not construct a replay or endpoint-swap against it.

Verdict

gajae.pr-review-verdict.v1 merge-approved sha256:8c1fccd8f6e954902f787f476941a9ddc7a1fa4f reviewer:human evidence:https://github.com/Yeachan-Heo/gajae-code/pull/4164/checks

Reviewer is probepark, author is Yeachan-Heo — distinct accounts. If those are the same operator in practice, repo policy makes this self-approval and the verdict must be downgraded to needs-human before merge; every technical criterion I can check is otherwise satisfied. Merge after the 2 in-flight cargo jobs go green.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

MERGE_READY

Exact head 8c1fccd8f6e954902f787f476941a9ddc7a1fa4f is rebased on PR base e8081462a5e6befb1ec6f4b805dcb787136d85aa. Dev CI run 31450918438 completed success; all exact-head checks are green. probepark approved this exact head, and the current Codex review is attached to it.

The repaired branch contains the asynchronous, fail-closed cross-platform SDK bundle reader; async test fixture conversion; deterministic manifest/content symlink coverage; and the regenerated browser diagnostics tool catalog. Local exact-head verification is recorded in the preceding signed comments and Ultragoal ledger. No merge or release was performed from this lane.

[repo owner's gaebal-gajae (clawdbot) 🦞]

Yeachan-Heo and others added 2 commits August 11, 2026 02:43
External coding agents need a host-neutral, direct-SDK contract for discovering and safely operating local GJC sessions without MCP or coordinator coupling.

The generated bundle owns exactly five files, enforces deterministic drift checks, and ships credential-safe TypeScript and Python templates with fail-closed discovery and nonce-bound approval.

Lore-id: 7f1a3bd1
Constraint: generated bundle must contain exactly five gjc-sdk-* artifacts
Constraint: endpoint bearer tokens must never be rendered or persisted
Rejected: MCP or coordinator integration | superseded by direct SDK-only scope
Confidence: high
Scope-risk: medium
Reversibility: clean-revert
Tested: generated verifier 25 gates; TypeScript 8 tests; Python 36 tests; strict mypy; release 108 tests; focused SDK 10 tests
…from static markdown

Addresses the maintainer REQUEST_CHANGES on #4164: the on-disk bundle format
had no version root, and the generator authored skill prompts inline as
TypeScript template literals.

- sdk-skills/manifest.json is the versioned root of the bundle contract
  (formatVersion 1) and declares the exact file closure; checks fail closed on
  missing, malformed, or unsupported versions, and legacy unversioned layouts
  are rejected with a regeneration hint instead of being read ambiguously.
- The three skill prompts are authored as static markdown under
  scripts/gjc-sdk-skills/prompts/, imported with `with { type: "text" }`, and
  copied verbatim by the generator; a consistency validator keeps the static
  allowlist blocks in sync with the ALLOWED_CONTROLS/ALLOWED_GLOBALS the
  templates embed.
- docs/sdk.md documents the bundle format version contract and the token-only
  trust boundary (a modified script holding the endpoint token reaches the
  operation registry dispatcher, not just the template allowlist).
- The Python template emits the approval challenge on stderr and reads the
  answer from stdin, so a successful control's stdout parses directly as JSON;
  a real-session subprocess test asserts that exact contract.
- Tests now prove deterministic closure, four-skill default preservation, no
  extra skills, upgrade compatibility (v1 installs valid; legacy/future layouts
  fail closed), and byte-for-byte generated artifact parity.

Lore-id: pr-4164-repair
Constraint: do not weaken the four-skill gate or existing installed bundles
Rejected: inline prompt strings in the generator | violates prompt-authoring contract
Rejected: unversioned five-file layout | consumers cannot fail closed on incompatible bundles
Confidence: high
Scope-risk: narrow
Reversibility: revertible
Tested: generator suite 14 pass; python template 7 pass incl real-session; check:sdk-skills 28 gates; check:tools; check:public-sync; check-visible-definitions; rebrand-inventory --strict; default-gjc-definitions 29 pass; verify-g002-gates at baseline parity
Not-tested: full workspace check:ts aggregate and CI matrix
Supersedes: 59c1586 review findings
Yeachan Heo added 5 commits August 11, 2026 02:43
…ards

test:release now runs scripts/generate-gjc-sdk-skills.test.ts, whose generated
direct-sdk TypeScript recipes spawn the template as a subprocess. The template
imports @gajae-code/coding-agent/sdk, whose barrel eagerly loads the
@gajae-code/natives addon, so the recipe exits 1 when the shard lacks the
prebuilt .node file.

taskNeedsNative() covered "root-test" and "test:*" but not "root-test:release"
(or the release-publish-contract task that also runs test:release), so those
matrix entries shipped native:false and skipped the "Download native addon(s)"
step. Add both keys to the native-consumer allowlist; the affected planner
already guarantees a native build task exists whenever a native consumer is
planned (full-workspace branch and ensureNativeBuild).

Lore-id: pr-4164-repair
Rejected: skipping the template runtime tests without natives | weakens credential/control contract
Confidence: high
Scope-risk: narrow
Reversibility: revertible
Tested: test:release 114 pass; ci-dev-affected.test.ts 87 pass incl new native-flag assertions; dev-ci-guard-topology 20 pass; check:tools
Not-tested: exact-head CI rerun
Supersedes: fba0187 (root-test:release CI failure)
The generated SDK skill bundle gate used synchronous node filesystem APIs despite the repository contract.\n\nThe generator, drift checker, verifier, and self-test now use Bun and promise-based filesystem operations while preserving fail-closed symlink and manifest validation.\n\nLore-id: 9f8b7d6c\nConstraint: bundle validation must remain fail-closed for missing, drifted, or symlinked files\nConfidence: high\nScope-risk: narrow\nReversibility: straightforward\nTested: bun run check:sdk-skills; bun test scripts/generate-gjc-sdk-skills.test.ts; bun --cwd=packages/coding-agent run check; bun run check:tools; bun run test:py-sdk
The async checker still inspected a path before reading it by name, which let a symlink replacement evade the intended no-follow boundary.\n\nBundle reads now hold O_NOFOLLOW descriptors through validation and read fixture setup uses the repository asynchronous filesystem conventions.\n\nLore-id: 2a4c6e8f\nConstraint: manifest and content reads must reject symlink replacement\nConfidence: high\nScope-risk: narrow\nReversibility: straightforward\nTested: bun test scripts/generate-gjc-sdk-skills.test.ts; bun run check:sdk-skills; bun --cwd=packages/coding-agent run check
Windows does not support O_NOFOLLOW, so descriptor-only validation could follow a replacement symlink.\n\nWindows bundle reads now compare regular-file path and descriptor identities before and after reading while POSIX retains O_NOFOLLOW descriptor reads.\n\nLore-id: 1b3d5f7h\nConstraint: generated bundle reads must reject links on every supported platform\nConfidence: high\nScope-risk: narrow\nReversibility: straightforward\nTested: bun test scripts/generate-gjc-sdk-skills.test.ts; bun run check:sdk-skills; bun --cwd=packages/coding-agent run check; bun run check:tools
Fail-closed bundle validation must not hang when an expected file is replaced by a FIFO.\n\nPOSIX opens now include O_NONBLOCK alongside O_NOFOLLOW, allowing the descriptor regular-file check to reject FIFOs immediately.\n\nLore-id: 4e6f8a0b\nConstraint: bundle checks must reject special files without blocking\nConfidence: high\nScope-risk: narrow\nReversibility: straightforward\nTested: bun test scripts/generate-gjc-sdk-skills.test.ts; bun run check:sdk-skills; bun --cwd=packages/coding-agent run check
The bundle checker must reject special files without blocking.\n\nThe Linux/POSIX regression replaces an expected skill file with a FIFO and asserts the checker returns drift failure.\n\nLore-id: 5b7d9f1a\nTested: bun test scripts/generate-gjc-sdk-skills.test.ts; bun run check:sdk-skills; bun --cwd=packages/coding-agent run check

@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: 1d888bf90d

ℹ️ 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".

await fs.rm(bundleDir, { recursive: true, force: true });
for (const [rel, content] of files) {
const target = path.join(bundleDir, rel);
await fs.mkdir(path.dirname(target), { recursive: 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.

P1 Badge Let Bun.write create parent directories

Remove this explicit parent-directory creation, as Bun.write() already creates missing ancestors; the same redundant pattern also appears in runSelfTest. Besides adding repeated filesystem work, it conflicts with the repository's mandated Bun filesystem convention.

AGENTS.md reference: AGENTS.md:L132-L132

Useful? React with 👍 / 👎.

: await client.control(args.operation!, args.input, { confirm: true });
process.stdout.write(JSON.stringify(redact({ sessionId: endpoint.sessionId, result }, endpoint.token), null, 2) + "\\n");
} finally {
await client.close();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve successful controls when close times out

When the peer returns a successful control response but stalls the WebSocket close handshake, SdkClient.close() rejects from this finally, so the outer catch reports failure and sets a nonzero exit code even though the mutation succeeded and its JSON was already written. Controllers that rely on exit status may discard that result and retry the mutation; treat teardown failure as best-effort once a definitive response has been received.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Exact head 1d888bf90d13ffb62978886ea6f103708fbe3717 adds deterministic POSIX FIFO rejection coverage for the non-blocking SDK bundle reader. Local generator suite, SDK bundle gate, and coding-agent typecheck are green. Exact-head CI and re-review are being driven.

[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Exact-head CI run 31453305537 is blocked only by shard-6 test packages/coding-agent/test/tools/bash-state-exploit.test.tsrestricted BashTool state-mutation exploit > admits a canonical allowlisted command and lets it produce a real execution effect fails with Command exited with code 127 through src/tools/bash.ts:658.

This exactly matches the current-dev fixture defect owned by fix/dev-bash-state-fixture-r4; it is unrelated to the FIFO SDK bundle change. I will not duplicate the owning repair. #4164 remains non-terminal until that dev repair lands, is rebased in, and fresh exact-head CI passes.

[repo owner's gaebal-gajae (clawdbot) 🦞]

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.

2 participants