Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Documentation

on:
push:
branches:
- main

concurrency:
group: documentation-${{ github.ref }}
cancel-in-progress: false

env:
DEVELOPER_DIR: /Applications/Xcode_26.6.0.app/Contents/Developer
EXPECTED_XCODE_VERSION_LINE: Xcode 26.6
EXPECTED_XCODE_BUILD_LINE: Build version 17F113

jobs:
build:
name: Build DocC
runs-on: macos-26
timeout-minutes: 15
permissions:
contents: read
pages: read
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Verify GitHub Actions pinning
run: ruby scripts/verify-github-actions-pinning.rb

- name: Verify pinned Xcode
run: scripts/verify-ci-xcode.sh

- name: Configure GitHub Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0

- name: Build documentation
run: scripts/generate-docc-site.sh "$RUNNER_TEMP/codexreviewkit-docc" CodexReviewKit

- name: Upload GitHub Pages artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: ${{ runner.temp }}/codexreviewkit-docc

deploy:
name: Deploy GitHub Pages
needs: build
runs-on: ubuntu-24.04
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy GitHub Pages
id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
40 changes: 40 additions & 0 deletions Docs/DocCSite/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>CodexReviewKit Documentation</title>
<style>
body {
box-sizing: border-box;
max-width: 52rem;
margin: 0 auto;
padding: 4rem 1.5rem;
font: 1rem/1.55 system-ui, sans-serif;
}
h1 { line-height: 1.15; }
ul { padding-left: 1.25rem; }
li { margin: 0.8rem 0; }
a { font-weight: 600; }
code { font-family: ui-monospace, monospace; }
</style>
</head>
<body>
<main>
<h1>CodexReviewKit Documentation</h1>
<p>API documentation for the public Swift package products.</p>
<ul>
<li><a href="CodexAppServerKit/"><code>CodexAppServerKit</code></a> — Codex app-server domain APIs.</li>
<li><a href="CodexAppServerKitTesting/"><code>CodexAppServerKitTesting</code></a> — Deterministic app-server test runtime.</li>
<li><a href="CodexDataKit/"><code>CodexDataKit</code></a> — Observable models and fetch APIs.</li>
<li><a href="CodexReviewKit/"><code>CodexReviewKit</code></a> — Review domain models and storage.</li>
<li><a href="CodexReviewHost/"><code>CodexReviewHost</code></a> — Review host composition.</li>
<li><a href="ReviewUI/"><code>ReviewUI</code></a> — Review user interface.</li>
<li><a href="ReviewUIPreviewSupport/"><code>ReviewUIPreviewSupport</code></a> — Preview and fixture support.</li>
<li><a href="TextTransitions/"><code>TextTransitions</code></a> — Text transition components.</li>
</ul>
<p><a href="https://github.com/lynnswap/CodexReviewKit">Source and installation</a></p>
</main>
</body>
</html>
2 changes: 1 addition & 1 deletion Fixtures/CodexReviewKitProductConsumer/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let package = Package(
),
],
dependencies: [
.package(path: "../.."),
.package(name: "CodexReviewKit", path: "../.."),
],
targets: [
.executableTarget(
Expand Down
20 changes: 19 additions & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-nio.git", from: "2.97.1"),
.package(url: "https://github.com/lynnswap/ObservationBridge.git", .upToNextMinor(from: "0.12.0")),
.package(url: "https://github.com/apple/swift-async-algorithms", from: "1.1.0"),
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.5.0"),
],
targets: [
.target(
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ used by that Claude Code session.

## More Detail

- [API documentation](https://lynnswap.github.io/CodexReviewKit/): generated
DocC for every public library product.
- [Architecture](Docs/architecture.md): package boundaries, runtime flow, and
test responsibilities.
- [CodexKit integration design](Docs/codexkit-integration.md): the canonical
Expand Down
2 changes: 2 additions & 0 deletions Sources/CodexAppServerKit/CodexThreadOperations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ extension CodexThread {
/// - Parameters:
/// - prompt: The structured prompt to send.
/// - options: Per-turn generation options.
/// - timeout: The maximum duration to wait for a terminal outcome, or
/// `nil` to wait without a deadline.
/// - Returns: The completed response collected from app-server events.
public func respond(
to prompt: CodexPrompt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ public final class CodexAppServerTestDeadlineClock: Sendable {
///
/// This type does not launch `codex` or any external process. Tests enqueue
/// responses and emit notifications through ``transport`` while exercising the
/// same public ``CodexAppServer`` API that production code uses.
/// same public `CodexAppServer` API that production code uses.
public struct CodexAppServerTestRuntime: Sendable {
/// The app-server domain container under test.
public let server: CodexAppServer
Expand Down Expand Up @@ -678,7 +678,7 @@ public struct CodexAppServerTestRuntime: Sendable {
/// Creates a test runtime without launching a real app-server process.
///
/// The runtime automatically enqueues the `initialize` response required by
/// ``CodexAppServer`` startup.
/// `CodexAppServer` startup.
///
/// - Parameters:
/// - transport: The in-memory transport to use.
Expand Down Expand Up @@ -721,7 +721,7 @@ public struct CodexAppServerTestRuntime: Sendable {

/// Creates a test runtime whose app-server thread APIs are backed by a mutable store.
///
/// The returned runtime still exercises the public ``CodexAppServer`` API,
/// The returned runtime still exercises the public `CodexAppServer` API,
/// while callers can mutate `threadStore` after startup:
///
/// ```swift
Expand Down Expand Up @@ -751,7 +751,7 @@ public struct CodexAppServerTestRuntime: Sendable {

/// Creates a test runtime whose thread APIs are backed by validated opaque fixtures.
///
/// The returned runtime still exercises the public ``CodexAppServer`` API.
/// The returned runtime still exercises the public `CodexAppServer` API.
/// Higher-level code can build its normal data container from ``server``:
///
/// ```swift
Expand Down
27 changes: 18 additions & 9 deletions Sources/CodexReviewHost/AccountRuntimeTransitionCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ final class AccountRuntimeTransitionCoordinator {
fileprivate let id: UUID
}

typealias PrimaryReconciliationFinishAction = @MainActor @Sendable () -> Void

private enum ActiveTransition {
enum RuntimeAuthPhase {
case reading
Expand Down Expand Up @@ -171,7 +173,7 @@ final class AccountRuntimeTransitionCoordinator {
let completion: PrimaryReconciliationCompletion
let operation: @MainActor @Sendable (
PrimaryReconciliationReservation
) async -> Void
) async -> PrimaryReconciliationFinishAction?
}

private var pendingPrimaryReconciliation: PendingPrimaryReconciliation?
Expand Down Expand Up @@ -300,7 +302,7 @@ final class AccountRuntimeTransitionCoordinator {
_ admission: LoginAdmission,
operation: @escaping @MainActor @Sendable (
PrimaryReconciliationReservation
) async -> Void
) async -> PrimaryReconciliationFinishAction?
) -> PrimaryLoginReconciliationHandoff {
guard case .primaryLogin(let id) = activeTransition,
id == admission.id else {
Expand Down Expand Up @@ -591,7 +593,7 @@ final class AccountRuntimeTransitionCoordinator {
func admitPrimaryReconciliation(
_ operation: @escaping @MainActor @Sendable (
PrimaryReconciliationReservation
) async -> Void
) async -> PrimaryReconciliationFinishAction?
) -> PrimaryReconciliationAdmission {
if case .account = activeTransition {
guard pendingPrimaryReconciliation == nil else {
Expand All @@ -613,7 +615,7 @@ final class AccountRuntimeTransitionCoordinator {
func performStoppedPrimaryReconciliation(
_ operation: @escaping @MainActor @Sendable (
PrimaryReconciliationReservation
) async -> Void
) async -> PrimaryReconciliationFinishAction?
) async {
let pending = makePendingPrimaryReconciliation(operation)
if case .account = activeTransition {
Expand Down Expand Up @@ -802,7 +804,10 @@ final class AccountRuntimeTransitionCoordinator {
}
}

private func finishTransition(id: UUID) {
private func finishTransition(
id: UUID,
primaryReconciliationFinishAction: PrimaryReconciliationFinishAction? = nil
) {
guard activeTransition?.id == id else {
preconditionFailure("Only the active account runtime transition can finish.")
}
Expand All @@ -820,6 +825,7 @@ final class AccountRuntimeTransitionCoordinator {
nil
}
activeTransition = nil
primaryReconciliationFinishAction?()
primaryReconciliationCompletion?.resolve()
let waiters = transitionCompletionWaiters
transitionCompletionWaiters.removeAll(keepingCapacity: false)
Expand All @@ -832,15 +838,15 @@ final class AccountRuntimeTransitionCoordinator {
private func startPrimaryReconciliation(
_ operation: @escaping @MainActor @Sendable (
PrimaryReconciliationReservation
) async -> Void
) async -> PrimaryReconciliationFinishAction?
) {
startPrimaryReconciliation(makePendingPrimaryReconciliation(operation))
}

private func makePendingPrimaryReconciliation(
_ operation: @escaping @MainActor @Sendable (
PrimaryReconciliationReservation
) async -> Void
) async -> PrimaryReconciliationFinishAction?
) -> PendingPrimaryReconciliation {
let id = UUID()
return .init(
Expand All @@ -860,8 +866,11 @@ final class AccountRuntimeTransitionCoordinator {
completion: pending.completion
)
Task { @MainActor [weak self] in
await pending.operation(pending.reservation)
self?.finishTransition(id: id)
let finishAction = await pending.operation(pending.reservation)
self?.finishTransition(
id: id,
primaryReconciliationFinishAction: finishAction
)
}
}
}
Loading