Skip to content

Release v0.29.0.6: native session control and fleet maintenance - #17

Merged
swear01 merged 16 commits into
mainfrom
release/v0.29.0.6
Sep 6, 2026
Merged

Release v0.29.0.6: native session control and fleet maintenance#17
swear01 merged 16 commits into
mainfrom
release/v0.29.0.6

Conversation

@swear01

@swear01 swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Rebuild maintained HAPI v0.29.0.6 from upstream 980a921ba15665c54998a6ddb658103d467ff4cb. The fresh 162-PR audit selects 58 carries, 100 deferrals, and 4 drops.

Compared with v0.29.0.5, this integrates native skill and atomic session control (tiann#1771), cascade-safe group deletion (tiann#1607), rewind window preservation (tiann#1766), inactive-notice alignment (tiann#1770), confirmed current-device mark-all-read (tiann#1773), and stable sidebar viewport (tiann#1776). The bundled hapi-session-runtime skill coexists with user-managed hapi-session-control installations. Existing upgrade generations, workspace guards, and permission boundaries remain enforced.

Integration fixes cover interrupted and same-version skill installation, delayed runtime readiness, worktree containment, fresh-session validation, effort capabilities, and remit retry/result consistency. Completed remits are never redelivered; ambiguous spawn failures retain their retry ID; only a new reservation with a proven undispatched RPC can bypass process cleanup. Codex cumulative snapshots return one final answer per stream. Reused remit IDs with conflicting payloads return HTTP 409; operational spawn failures retain HTTP 502. Remit polling retries transient failures within its deadline and rejects crashed partial results. Bulk mark-as-read reports local storage failures through the existing confirmation dialog. Same-batch user acknowledgments do not end remit results. HTML app shells consistently use no-store headers in source and compiled Hub serving modes.

Validation: fresh full typecheck and all package tests after excluding tiann#1424 (8,138 passed, 16 existing skips, zero failures); Playwright scroll/terminal/composer 17/17; maintenance tests 42/42; fixture regeneration leaves tracked fixtures unchanged. The fourteen-patch queue reproduces source tree 354d69d4e8b9ae94a5dd6cd0ade2276a55387f96. CLI/Web suites use two workers with unchanged test timeouts. Earlier integration gates passed Runner 14/1 skipped and Swift 627 tests; latest-head CI rechecks platform builds and screenshot baselines.

Personal-policy exceptions: tiann#1662/tiann#1436 maintainer-policy-exception; tiann#1468 status-checks-failed, maintainer-policy-exception. tiann#1771's 151-file scope is mapped in the audit and its upstream latest-head HAPI Bot review is clean.

Release metadata: tools/maintenance/releases/v0.29.0.6/; curated notes: .github/release-notes/v0.29.0.6.md. After exact-head review and all CI pass, publish this exact SHA to fork main using the captured lease, wait for main CI, then tag and deploy. No production service has been changed yet.

Temporarily exclude upstream tiann#1424 at the operator’s request because stale running jobs pin inactive sessions indefinitely. Remove the job CLI/API/UI and restore active-session pinning, retaining schema V29 and legacy data for a safe upgrade.

Retain the independent CLI argument-normalization and Cursor ambient-thinking fixes previously bundled with tiann#1424; removing them reproduced two unrelated regressions. The Jobs feature remains excluded.

Latest head cc45252848ba0033d1927e24c1121f821eb5b68d: all eight mechanical CI checks passed. External review remains unmet: Gemini reports daily quota exhaustion (comment 5557178312); Codex reports review usage exhaustion (5555175596); Cursor dashboard requires login, so repository enablement cannot be verified. Earlier Swear runs repeatedly timed out; the b111788 incremental run remained pending for over 30 minutes before the final CLI fix superseded it. The current automatic full review is still pending. No tag, Release, main update, or production deployment has been performed.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces extensive updates to the HAPI CLI and agent runner, including a comprehensive suite of peer session control commands, support for session-attached jobs, platform-scoped Agy hook carriers with liveness-based sweeps, native rewind/resume capabilities for Claude and Codex, and durable upgrade delegation for runner restarts. The reviewer identified several critical issues where synchronous operations (such as 'execFileSync' and synchronous file system calls) block the Node.js event loop in asynchronous contexts, which can freeze active sessions in multi-session runner environments. Additionally, a missing rejection handler in 'AcpSdkBackend.ts' was flagged as a potential cause for unhandled promise rejections.

I am having trouble creating individual review comments. Click here to see my feedback.

cli/src/agent/backends/acp/AcpSdkBackend.ts (1323)

high

If queuedUpdates rejects, calling .then(() => true) without a rejection handler will cause the promise returned by Promise.race to reject. This can propagate out of waitForQueueSettled and potentially crash the prompt turn or cause an unhandled promise rejection. Consider appending a .catch() or providing a second argument to .then() to handle rejections gracefully.

                    queuedUpdates.then(() => true, () => false),

cli/src/claude/runClaude.ts (278-284)

high

Using execFileSync synchronously blocks the Node.js event loop for up to 10 seconds if the command hangs or runs slowly. In a multi-session runner environment, this will freeze all other active sessions on the same runner. Since runClaude is already an asynchronous function, consider using the asynchronous execFile (promisified) to avoid blocking the event loop.

    let claudeVersionOutput: string | null = null;
    try {
        const { execFile } = await import('node:child_process');
        const { promisify } = await import('node:util');
        const execFileAsync = promisify(execFile);
        const { stdout } = await execFileAsync(getDefaultClaudeCodePath(), ['--version'], {
            timeout: 10_000
        });
        claudeVersionOutput = stdout.trim();
    } catch (error) {

cli/src/codex/utils/codexSessionScanner.ts (71-125)

high

Using synchronous file system operations (statSync, openSync, readSync, closeSync) inside an asynchronous function blocks the Node.js event loop. In a multi-session runner, this can cause significant latency and freeze other active sessions while performing disk I/O. Since node:fs/promises is already imported, please use the asynchronous stat, open, and file handle read operations instead.

    let handle;
    try {
        const fileStats = await stat(transcriptPath);
        const size = fileStats.size;
        if (size <= 0) return [];

        handle = await open(transcriptPath, 'r');
        let position = size;
        const scanStart = Math.max(0, size - maxBytes);
        let incompletePrefix = Buffer.alloc(0);

        while (position > scanStart && !replayUsageComplete(accumulator)) {
            const length = Math.min(position - scanStart, readChunkBytes);
            position -= length;
            const buffer = Buffer.alloc(length);
            const { bytesRead } = await handle.read(buffer, 0, length, position);
            const combined = Buffer.concat([buffer.subarray(0, bytesRead), incompletePrefix]);

            let complete = combined;
            if (position > scanStart) {
                const firstNewline = combined.indexOf(0x0a);
                if (firstNewline < 0) {
                    incompletePrefix = combined;
                    continue;
                }
                incompletePrefix = combined.subarray(0, firstNewline);
                complete = combined.subarray(firstNewline + 1);
            } else {
                incompletePrefix = Buffer.alloc(0);
            }

            const lines = complete.toString('utf8').split(/\r?\n/);
            // Newest lines are at the end of this chunk window.
            for (let index = lines.length - 1; index >= 0; index -= 1) {
                noteUsageFromTranscriptLine(lines[index] ?? '', accumulator, threadId);
                if (replayUsageComplete(accumulator)) break;
            }
        }

        if (
            scanStart === 0
            && incompletePrefix.length > 0
            && !replayUsageComplete(accumulator)
        ) {
            noteUsageFromTranscriptLine(incompletePrefix.toString('utf8'), accumulator, threadId);
        }

        return orderedReplayUsagePayloads(accumulator).reverse();
    } catch (error) {
        logger.debug(`[codex-session-scanner] Failed to reverse-scan usage from ${transcriptPath}: ${error}`);
        return [];
    } finally {
        if (handle !== undefined) {
            try { await handle.close(); } catch { /* ignore */ }
        }
    }

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Swear Review is still running after a 20-minute wait on a82cb24. Recording this as a review-wait timeout and proceeding with the configured fallback; this does not claim the remote job failed or cancel it.

Context for the latest-head review: the PR now has the previous maintained release 7a89dee as an ancestor, and the reviewed change is that release to a82cb24. All CI checks for the source have passed.

I checked the earlier Gemini observations against callers: both assignments to AcpSdkBackend.sessionUpdateQueue attach rejection handlers; the queue is initialized resolved. Claude sessions run in separate detached processes, so the startup version probe does not block other runner sessions. The Codex usage tail scan is bounded to 4 MiB in 64 KiB reads. Please assess any remaining actionable correctness issues against the actual current diff and these call paths.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request replaces HAPI-owned prompt injection with a bundled native hapi-session-runtime skill and introduces deterministic JSON CLI commands for exact-ID session control (such as spawn-peer, wait-peer, stop-peer, and archive-peer). It also removes the old sessionSummaryContract prompt setting and updates SQLite deletion cascades. One high-severity issue was identified in the skill installation logic, where an empty pre-existing directory can trigger a false-positive "Refusing to overwrite user-managed skill" error and block session startup.

Comment thread cli/src/modules/common/hapiSessionControlSkill.ts
@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

The empty-directory startup failure is fixed in b09df51. The new regression failed before the fix; all 19 native-skill tests now pass, including preserving a nonempty unmanaged directory without SKILL.md. The implementation claims only a new or empty directory while holding the existing cross-process lock, and preserves all symlink and foreign-content guards.

Fresh verification passed: full typecheck; all 8,205 unit tests (16 existing skips, Web run with two workers under local load); Runner integration 14 passed/1 skipped; maintenance checks 42/42; four-patch replay matches the tested source tree. Latest-head CI is running.

Swear Review's previous full run was explicitly cancelled by the service as timed out at 2026-09-05 18:26:55 UTC after its 45-minute hard limit. Its automatic new run remains separate; continuing with the configured Gemini fallback for the current head.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request upgrades HAPI to version 0.29.0.6, introducing a prompt-free session control workflow. It replaces HAPI-owned prompt injection with a bundled native hapi-session-runtime skill and deterministic JSON CLI commands. It adds new exact-ID peer commands (such as spawn-peer, wait-peer, and ping-peer) and corresponding MCP tools, enforcing exact UUID boundaries and rejecting prefix-based matching. Additionally, it introduces atomic session spawning and remit delivery with automatic compensating cleanup on failure, while removing the plan permission mode from Grok and OpenCode. The review comments correctly identify a discrepancy where the custom UUID validation regexes (EXACT_SESSION_ID_RE and UUID_RE) are more restrictive than standard Zod UUID validation, which could cause valid UUIDs of other versions or variants to be rejected at runtime.

Comment thread cli/src/modules/pingPeer/pingPeer.ts
Comment thread cli/src/modules/spawnPeer/spawnPeer.ts
@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

The latest Gemini findings assert that UUIDv3/v5 and nonstandard variants pass the pinned Zod validator but fail the runtime regex. I verified this against the installed Zod from cli/: UUIDv3 and UUIDv5 both pass both validators; a non-RFC variant and version 9 both fail both validators. [1-8] includes versions 3 and 5. The suggested replacement would remove existing version/variant validation. The production session IDs are generated by randomUUID, and exact-ID lookup remains intentional; no validation weakening was applied.

The prior empty-directory finding is fixed in b09df51, with regression coverage. All latest-head CI checks have now passed. Gemini's terminal review still contains the disproven UUID findings, so proceeding to the next configured provider under the OR review policy.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

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

ℹ️ 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 hub/src/sync/syncEngine.ts Outdated
Comment thread cli/src/modules/spawnPeer/spawnPeer.ts Outdated
Comment thread hub/src/sync/messageService.ts Outdated
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 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-05T22:14:00.366485Z ff95684 Manual request
ℹ️ 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.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Codex acknowledged the latest-head review request but has not produced a terminal result after 20 minutes; that request remains pending. The Cursor Bugbot dashboard redirects to login, so repository enablement cannot be verified and Bugbot was not triggered. Rechecking the available Gemini review with the concrete UUID counterexamples already recorded above.

For b09df51, the exact installed-Zod/runtime results are:

  • 12345678-1234-3123-8123-123456789abc (v3): true / true
  • 12345678-1234-5123-8123-123456789abc (v5): true / true
  • 12345678-1234-4123-0123-123456789abc (non-RFC variant): false / false
  • 12345678-1234-9123-8123-123456789abc (version 9): false / false

Both runtime regexes accept versions 1 through 8 and RFC variants 8/9/a/b, matching the pinned validator for these normal UUIDs. Please reassess the reported mismatch against the actual installed Zod behavior and review any remaining actionable correctness issues in the current PR. All latest-head CI and the native-skill recovery regressions have passed.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request upgrades HAPI to version 0.29.0.6 and introduces a prompt-free session control workflow via the new hapi-session-runtime skill, removing HAPI-owned prompt injections across all agent adapters. It adds several new CLI commands and MCP tools for atomic session spawning, waiting, inspecting, and lifecycle management (stop, archive, delete) using exact UUIDs. Additionally, a bulk 'mark all as read' action has been added to the web UI. Feedback is provided regarding a potential security and directory mismatch issue in spawnSessionWithRemit where worktree base paths are not strictly validated against the requested directory.

Comment thread hub/src/sync/syncEngine.ts Outdated
@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Review fixes are now on 92ff3e0:

  • Codex: wait within the existing readiness deadline for settings after the initial active webhook; preserve remit_conflict in CLI JSON; reject changed schedule and queue-to-steer localId reuse while preserving the existing safe steer-to-queue retry.
  • Gemini: worktree basePath must contain the requested directory using Node POSIX/Windows path semantics. Legitimate repository subdirectories and Windows drive case normalization remain supported; unrelated paths, sibling prefixes, traversal, relative bases, and different drives are covered.
  • Newly opened personal PR fix(web): preserve sidebar viewport across pin updates tiann/hapi#1776 is integrated with ten passing browser regressions; maintained session dragging and project actions remain intact.

Fresh full typecheck and all package tests passed: 8216 passed, 16 existing skips, zero failures. Runner integration 14 passed/1 skipped; browser 17/17; maintenance 42/42. The five-patch replay matches canonical source tree 5eb42eab259638f9711b326db9c9be16d0bed62b. Production remains unchanged. The previous-head Swear Review reached terminal timeout; requesting Gemini re-review of the fixes and final candidate.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Gemini returned its terminal daily-quota limit. Proceeding to Codex on current head f964e71. This head only corrects the post-test artifact fingerprint in the release audit; executable source and canonical tree remain those validated above.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

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

ℹ️ 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 cli/src/modules/spawnPeer/spawnPeer.ts Outdated
Comment thread cli/src/modules/spawnPeer/spawnPeer.ts
@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Fixed both latest Codex findings in this head: preserve generated remit IDs for intermediary HTTP 502/504 and all post-spawn failure paths; derive effort support from the shared capability table, covering Kimi/Copilot. The 20 spawn tests and fresh full typecheck/package gate pass (8218 passed, 16 existing skips, zero failures). Six-patch replay matches c4acd769ed5d00b7acaa0760a244e7952943ee15. Please re-review the final candidate; Gemini remains quota-limited and prior Swear runs timed out.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Both latest findings are fixed: invoked localId retries validate the payload and return success without re-emitting to the CLI; uninvoked retries still deliver. A restarted MessageService regression failed before the fix and now passes. Kimi/Copilot effort guidance is aligned in the canonical skill, CLI help, and README. Fresh full typecheck/package gate: 8225 passed, 16 existing skips, zero failures; eight-patch source replay matches e8c6b84cbbea5bd28ffed83499e963e31d5aa5ec. Requesting latest-head re-review.

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@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: 184f356f17

ℹ️ 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 hub/src/sync/syncEngine.ts
Comment thread cli/src/modules/pingPeer/pingPeer.ts

@swear-review swear-review 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.

Swear Review — Full PR review · batch 1/1 · OCR v1.9.0 · deepseek-v4-flash

message: z.string().min(1).describe('Required first user message'),
name: z.string().trim().min(1).max(SESSION_NAME_MAX_LENGTH).optional().describe('Session display name'),
machineId: z.string().trim().min(1).optional().describe('Exact runner machine id'),
agent: z.enum(CREATABLE_AGENT_FLAVORS as unknown as [string, ...string[]]).optional()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

z.enum(CREATABLE_AGENT_FLAVORS as unknown as [string, ...string[]]) uses an unsafe double cast. CREATABLE_AGENT_FLAVORS is typed as readonly Exclude<AgentFlavor, 'gemini'>[] (a computed filter result), not a literal tuple, so the cast is needed to satisfy the zod enum API — but it silently discards the enumeration values at the type level while still working at runtime since zod accepts a readonly array. This is identical to the established pattern used in startHappyServer.ts, but here the cast crosses through an intermediate type that isn't assignable, so a cleaner alternative is z.enum([...CREATABLE_AGENT_FLAVORS] as [string, ...string[]]) or importing AgentFlavorSchema/defining a dedicated tuple to keep the allowed values type-checked.

low · maintainability

Swear Review

Comment on lines +248 to +249
agent: z.enum(CREATABLE_AGENT_FLAVORS as unknown as [string, ...string[]]).optional()
.describe('Agent flavor; defaults to claude'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

spawn_peer is newly added to DEFAULT_TOOL_NAMES, but the underlying spawnPeer request is routed through the session's own runner auth (same machineId/directory as this session by default) and agent defaults to 'claude'. If codex itself is the current session, registering a tool whose default agent is 'claude' is inconsistent with the current runtime. More importantly, this bridge is used by non-codex flavors too (it's the generic STDIO bridge invoked via commands/mcp.ts), so at minimum the description should state the default explicitly. The description text 'defaults to claude' is the only source of truth here since the schema is passed through without defaults.

medium · documentation

Swear Review

? JSON.stringify({ ok: false, error: { code, message } })
: `${chalk.red('hapi machines:')} ${message}`
useJson ? console.log(output) : console.error(output)
process.exit(error instanceof TokenInitializationError ? 2 : error instanceof SpawnPeerError ? exitCodeForSpawnPeerError(error) : 1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nested ternary expression. Per the code-quality rules, nested ternaries are not allowed; they hurt readability. Extract the exit code with an if/else chain (or a helper) and pass it to process.exit.

Suggested change
process.exit(error instanceof TokenInitializationError ? 2 : error instanceof SpawnPeerError ? exitCodeForSpawnPeerError(error) : 1)
let exitCode = 1
if (error instanceof TokenInitializationError) exitCode = 2
else if (error instanceof SpawnPeerError) exitCode = exitCodeForSpawnPeerError(error)
process.exit(exitCode)

low · style

Swear Review

Comment on lines +387 to +391
agent: args.agent as Parameters<typeof spawnPeer>[0]['agent'],
model: args.model,
effort: args.effort,
sessionType: args.sessionType,
permissionMode: args.permissionMode as Parameters<typeof spawnPeer>[0]['permissionMode'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The handler args type agent?: string / permissionMode?: string forces these unsafe casts to satisfy spawnPeer's types. The MCP SDK validates args against the zod inputSchema (and spawnPeer re-validates both fields), so the cast is semantically safe today, but it bypasses compiler checks — if the schema is ever loosened or a future caller skips validation, an arbitrary string would be passed straight through to spawnPeer. Consider typing the handler parameters with z.infer-based types (or narrowing to AgentFlavor / PermissionMode) so the casts become unnecessary and the compiler enforces the contract.

Suggested change
agent: args.agent as Parameters<typeof spawnPeer>[0]['agent'],
model: args.model,
effort: args.effort,
sessionType: args.sessionType,
permissionMode: args.permissionMode as Parameters<typeof spawnPeer>[0]['permissionMode'],
agent: args.agent ?? 'claude',
model: args.model,
effort: args.effort,
sessionType: args.sessionType,
permissionMode: args.permissionMode,

medium · maintainability

Swear Review

message: z.string().min(1).describe('Required first user message'),
name: z.string().trim().min(1).max(SESSION_NAME_MAX_LENGTH).optional().describe('Session display name'),
machineId: z.string().trim().min(1).optional().describe('Exact runner machine id'),
agent: z.enum(CREATABLE_AGENT_FLAVORS as unknown as [string, ...string[]]).optional()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

z.enum(CREATABLE_AGENT_FLAVORS as unknown as [string, ...string[]]) relies on a double cast (as unknown as) to satisfy Zod's non-empty-tuple typing. It works only because the runtime filter() result currently has 10 entries. If CREATABLE_AGENT_FLAVORS is ever emptied or widened, z.enum([]) throws at server startup or silently accepts values spawnPeer doesn't support (it would then fail with bad_args). Prefer deriving the schema from a typed non-empty tuple or a z.enum factory that validates the array at construction, and let the compiler check the element type instead of casting to string[].

Suggested change
agent: z.enum(CREATABLE_AGENT_FLAVORS as unknown as [string, ...string[]]).optional()
agent: z.enum([...CREATABLE_AGENT_FLAVORS] as [Exclude<AgentFlavor, 'gemini'>, ...Exclude<AgentFlavor, 'gemini'>[]]).optional()

low · maintainability

Swear Review

{
type: 'text' as const,
text: formatInspectPeerReport(result),
text: `Spawned ${result.sessionId} remit=${result.remitId} (${result.name})`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Success text uses remit= while error text uses remitId= (same for the ping_peer handler). The model reads both to reason about idempotency retries; inconsistent labels make output parsing ambiguous. Use remitId= consistently in both success and error messages.

Suggested change
text: `Spawned ${result.sessionId} remit=${result.remitId} (${result.name})`,
text: `Spawned ${result.sessionId} remitId=${result.remitId} (${result.name})`,

low · style

Swear Review

Comment on lines +72 to +73
if (error instanceof PingPeerError || error instanceof TokenInitializationError) {
const output = commandArgs.includes('--json')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The catch block decides JSON vs. text output using commandArgs.includes('--json'), the raw argument list, rather than the parsed result. Side effects: (1) format/routing logic is duplicated four times and can silently diverge; (2) a malformed invocation like hapi inspect-peer --limit --json — where --json appears as the (consumed) value for --limit in parseInspectPeerArgs — still triggers JSON output even though the user never successfully passed a --json flag, which is confusing for scripts. Better: capture the parsed json flag out of handleInspectPeerCommand (e.g. re-parse args in the catch, or have the handler annotate the parsed args) and route on that single value consistently, as machines.ts does with const useJson = json || commandArgs.includes('--json').

Suggested change
if (error instanceof PingPeerError || error instanceof TokenInitializationError) {
const output = commandArgs.includes('--json')
if (error instanceof PingPeerError || error instanceof TokenInitializationError) {
const useJson = commandArgs.includes('--json') && !commandArgs.includes('--limit')

medium · bug

Swear Review

Comment on lines +40 to +41
} else if (arg.startsWith('--message-file=')) {
result.messageFile = arg.slice('--message-file='.length)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The --message-file=<value> and --remit-id=<value> forms skip the - prefix guard applied to the space-separated forms. So hapi ping-peer <id> --message-file=-foo is accepted as a literal path while ... --message-file -foo errors out with 'requires a path or -'. Align the validation across both syntaxes so parsing behavior is consistent regardless of which flag form the user picks.

Suggested change
} else if (arg.startsWith('--message-file=')) {
result.messageFile = arg.slice('--message-file='.length)
} else if (arg.startsWith('--message-file=')) {
const value = arg.slice('--message-file='.length)
if (!value || (value.startsWith('-') && value !== '-')) throw new PingPeerError('bad_args', '--message-file requires a path or -')
result.messageFile = value

low · maintainability

Swear Review

error: { code: error.code, message: error.message }
})
: `${chalk.red('hapi ping-peer:')} ${error.message}`
commandArgs.includes('--json') ? console.log(output) : console.error(output)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A ternary is used as a statement purely for its side effects (? console.log(output) : console.error(output)), discarding the expression result, and this pattern is duplicated in both error branches. Since process.exit immediately follows, output routing is easy to miss. An explicit if/else would make the stdout-vs-stderr decision clearer and easier to maintain.

Suggested change
commandArgs.includes('--json') ? console.log(output) : console.error(output)
if (commandArgs.includes('--json')) {
console.log(output)
} else {
console.error(output)
}

low · style

Swear Review

if (result.messageFile !== undefined && !result.messageFile) {
throw new PingPeerError('bad_args', '--message-file requires a path or -')
}
if (result.waitActiveSecs !== undefined && (!Number.isFinite(result.waitActiveSecs) || result.waitActiveSecs <= 0 || result.waitActiveSecs > 300)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This change introduces a new 300-second upper bound for --wait and HAPI_WAIT_ACTIVE_SECS, but the updated help text does not document the option or the 1–300s range at all. Users who previously relied on larger wait values will now get unexpected validation errors without guidance on the new limit; the range should be mentioned in the usage/help output.

low · documentation

Swear Review

@swear-review

swear-review Bot commented Sep 5, 2026

Copy link
Copy Markdown

Swear Review

Mode: Full PR
Model: deepseek-v4-flash
OCR: v1.9.0
Commit: 184f356

Findings:

  • Maintainability: 4
  • Documentation: 2
  • Style: 3
  • Bug: 1

10 findings total
10 inline comments

Status: ✅ Completed

@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@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: 05c1182ba6

ℹ️ 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 hub/src/web/routes/machines.ts Outdated
@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@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: 7812c02ec7

ℹ️ 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 cli/src/modules/pingPeer/pingPeer.ts Outdated
Comment thread web/src/lib/sessionLastSeen.ts Outdated
Comment thread cli/src/modules/pingPeer/pingPeer.ts Outdated
@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

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

ℹ️ 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 cli/src/modules/pingPeer/pingPeer.ts
@swear01

swear01 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@swear01

swear01 commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

/swear-review incremental

@swear-review

swear-review Bot commented Sep 6, 2026

Copy link
Copy Markdown

Swear Review: incremental review queued (184f356b111788, job tiann#817).

@swear01

swear01 commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

/swear-review status

@swear-review

swear-review Bot commented Sep 6, 2026

Copy link
Copy Markdown

Swear Review — status

Current head SHA: b111788
Last reviewed SHA: 184f356
Last successful full review: 184f356
Last successful review (any): 184f356
Current job status: running
Gate mode: off
OCR version: 1.9.0
Concurrency: 4
Model: deepseek-v4-flash

@swear01

swear01 commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@swear01

swear01 commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request bumps HAPI to version 0.29.0.6 and introduces a prompt-free session control workflow. It adds a suite of new peer-session control commands (such as spawn-peer, wait-peer, and inspect-peer) and installs a bundled native hapi-session-runtime skill on the execution host. Additionally, it removes the previously integrated 'session-attached jobs' (hapi job) functionality and HAPI-owned prompt injection prose, refines permission modes for Grok and OpenCode, and ensures HTML app shells are never cached. The feedback identifies a potential ENOENT error when attempting to acquire a file lock on a directory that does not yet exist during a fresh installation, and suggests creating the target directory before invoking the lock.

Comment on lines +97 to +99
await mkdir(skillRoot, { recursive: true, mode: 0o700 })
await withSettingsFileLock(targetDir, async () => {
const targetDirStat = await lstat(targetDir).catch(() => null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The withSettingsFileLock function is called with targetDir as the lock target before targetDir is guaranteed to exist (it is created inside the lock callback on line 109). Depending on the underlying file-locking implementation, this can throw an ENOENT error on a fresh installation where the directory does not yet exist. Creating targetDir beforehand ensures the lock can be acquired safely.

    await mkdir(skillRoot, { recursive: true, mode: 0o700 })
    await mkdir(targetDir, { recursive: true, mode: 0o700 }).catch(() => null)
    await withSettingsFileLock(targetDir, async () => {
        const targetDirStat = await lstat(targetDir).catch(() => null)

@swear01

swear01 commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Operator authorized publishing v0.29.0.6 and deploying the fleet after reviewing the external-review status. Exact release head: cc45252. All eight mechanical CI checks passed. Swear Review timed out; Gemini reviewed this head and its sole target-directory ENOENT finding was checked against withSettingsFileLock (realpath: false, sibling .hapi.lock) and all 20 runtime-skill installation tests passed. No code change was needed. This is operator authorization to proceed, not a claim of an unconditional clean bot result.

@swear01
swear01 merged commit cc45252 into main Sep 6, 2026
9 of 10 checks passed
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