Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
dff215e
feat(reborn): add OpenAI-compatible API contracts
hanakannzashi Jun 4, 2026
035a265
fix(reborn): tighten OpenAI-compatible response contracts
hanakannzashi Jun 5, 2026
9a5d9a6
fix(reborn): address OpenAI compat contract review
hanakannzashi Jun 6, 2026
a2c2ee0
Merge origin/main into OpenAI compat contracts
think-in-universe Jun 7, 2026
4222ac5
Merge remote-tracking branch 'origin/main' into issue-4442-reborn-ope…
hanakannzashi Jun 8, 2026
5264f35
fix(reborn): satisfy merged main clippy
hanakannzashi Jun 8, 2026
9af95ed
Merge remote-tracking branch 'origin/main' into issue-4442-reborn-ope…
hanakannzashi Jun 8, 2026
f9611c7
feat(reborn): add OpenAI-compatible product refs
hanakannzashi Jun 5, 2026
3d5249b
fix(reborn): validate OpenAI-compatible ref mappings
hanakannzashi Jun 5, 2026
f02dab6
fix(reborn): address OpenAI product refs review
hanakannzashi Jun 6, 2026
6abf732
feat(reborn): route chat completions through ProductWorkflow
hanakannzashi Jun 5, 2026
b054561
fix(reborn): simplify chat completion timestamps
hanakannzashi Jun 5, 2026
d7d07ac
fix(reborn): use explicit ProductWorkflow submit door
hanakannzashi Jun 8, 2026
b96fda5
test(reborn): stabilize subagent cancellation propagation
hanakannzashi Jun 8, 2026
89f68e7
Validate durable OpenAI compat ref records
think-in-universe Jun 8, 2026
1e12d6c
Fix chat completions idempotency validation
think-in-universe Jun 8, 2026
63cfd9b
Format OpenAI compat storage review fixes
think-in-universe Jun 8, 2026
4a66e1a
Merge branch 'issue-4443-reborn-openai-product-refs' into issue-4444-…
think-in-universe Jun 8, 2026
68730db
feat(reborn): add OpenAI-compatible product refs (#4489)
hanakannzashi Jun 8, 2026
a4ea2e9
Address chat completions review comments
think-in-universe Jun 8, 2026
2d0aeb9
Persist chat completion idempotency replay metadata
think-in-universe Jun 8, 2026
4ccbe4a
Export OpenAI compat ack recording request
think-in-universe Jun 8, 2026
caf632c
Persist OpenAI compat accepted workflow acks
think-in-universe Jun 8, 2026
1a2789c
Merge main into OpenAI compatibility contracts
think-in-universe Jun 8, 2026
801d1e8
Merge OpenAI API contracts base into chat completions workflow
think-in-universe Jun 8, 2026
270ccc1
feat(reborn): route responses through ProductWorkflow
think-in-universe Jun 8, 2026
5ea0be2
fix(reborn): declare compat storage chrono dependency
think-in-universe Jun 8, 2026
fa6f010
fix(reborn): resolve chat completions review feedback
think-in-universe Jun 8, 2026
84e4882
fix(reborn): resolve chat completions review feedback
think-in-universe Jun 8, 2026
5aed75d
fix(reborn): update chat completions workflow stack
think-in-universe Jun 8, 2026
09f22e8
merge remote chat completions review fix
think-in-universe Jun 8, 2026
2cdcc89
fix(reborn): resolve responses workflow review feedback
think-in-universe Jun 8, 2026
423bb31
fix(reborn): keep responses cancel within adapter boundary
think-in-universe Jun 8, 2026
80146b5
feat(reborn): translate projection streams to OpenAI SSE
think-in-universe Jun 8, 2026
daba0c0
fix(reborn): format chat streaming workflow
think-in-universe Jun 8, 2026
23c0cdc
fix(reborn): format responses streaming workflow
think-in-universe Jun 8, 2026
e5c843d
fix(reborn): handle terminal projection stream status
think-in-universe Jun 8, 2026
4952bf2
fix(reborn): make OpenAI stream replay terminal-safe
think-in-universe Jun 8, 2026
856db24
test(reborn): close OpenAI compat security gates
think-in-universe Jun 8, 2026
0e506ce
fix(reborn): keep responses unsupported-tooling router reusable
think-in-universe Jun 8, 2026
d6b4d17
fix(openai-compat): address chat workflow review comments
think-in-universe Jun 8, 2026
c7d4250
fix(openai-compat): address responses handler review comments
think-in-universe Jun 8, 2026
14961cc
fix(openai-compat): persist responses replay state
think-in-universe Jun 8, 2026
472e564
test(openai-compat): cover responses review paths
think-in-universe Jun 8, 2026
a38ef97
fix(openai-compat): annotate optional responses router state
think-in-universe Jun 8, 2026
8dd0012
test(product-adapters): cover cancel run payload validation
think-in-universe Jun 8, 2026
b1e0a26
fix(openai-compat): address chat ref store review
think-in-universe Jun 9, 2026
addbf56
Merge branch 'pr-4495' into pr-4546
think-in-universe Jun 9, 2026
7b16121
fix(reborn): resolve chat projections through workflow
hanakannzashi Jun 9, 2026
fbcecb1
Merge remote-tracking branch 'origin/issue-4444-reborn-chat-completio…
hanakannzashi Jun 9, 2026
16524b6
fix(reborn): close responses workflow review gaps
hanakannzashi Jun 9, 2026
6ed1584
test(reborn): cover responses wait cancellation
hanakannzashi Jun 9, 2026
38e8966
Merge remote-tracking branch 'origin/issue-4445-reborn-responses-work…
hanakannzashi Jun 9, 2026
09bca16
fix(reborn): update streaming tests for projection reader
hanakannzashi Jun 9, 2026
843d845
fix(reborn): close OpenAI stream review gaps
hanakannzashi Jun 9, 2026
06439f7
fix(reborn): tighten OpenAI workflow internals
hanakannzashi Jun 9, 2026
d0bd5ea
Merge remote-tracking branch 'origin/main' into issue-4444-reborn-cha…
hanakannzashi Jun 9, 2026
d572dd4
feat(reborn): wire OpenAI chat completions route
hanakannzashi Jun 9, 2026
06464ae
Merge remote-tracking branch 'origin/issue-4444-reborn-chat-completio…
hanakannzashi Jun 9, 2026
a30f6f0
Merge remote-tracking branch 'origin/issue-4445-reborn-responses-work…
hanakannzashi Jun 9, 2026
085899e
Merge remote-tracking branch 'origin/issue-4446-reborn-openai-project…
hanakannzashi Jun 9, 2026
964bacc
fix(reborn): wire OpenAI responses composition
hanakannzashi Jun 9, 2026
1104d1e
Merge branch 'issue-4445-reborn-responses-workflow' into issue-4446-r…
hanakannzashi Jun 9, 2026
f2a0b6e
fix(reborn): wire OpenAI projection streams
hanakannzashi Jun 9, 2026
bc9aa9c
Merge branch 'issue-4446-reborn-openai-projection-streams' into issue…
hanakannzashi Jun 9, 2026
3505eee
fix(reborn): centralize chat body validation
hanakannzashi Jun 9, 2026
84b5210
Merge branch 'issue-4444-reborn-chat-completions-workflow' into issue…
hanakannzashi Jun 9, 2026
91258c0
fix(reborn): centralize responses body validation
hanakannzashi Jun 9, 2026
33b6726
Merge branch 'issue-4445-reborn-responses-workflow' into issue-4446-r…
hanakannzashi Jun 9, 2026
3532b51
Merge branch 'issue-4446-reborn-openai-projection-streams' into issue…
hanakannzashi Jun 9, 2026
ff807b3
fix(reborn): keep OpenAI compat off turns
hanakannzashi Jun 9, 2026
7906481
Merge branch 'issue-4445-reborn-responses-workflow' into issue-4446-r…
hanakannzashi Jun 9, 2026
fe9e1a9
Merge branch 'issue-4446-reborn-openai-projection-streams' into issue…
hanakannzashi Jun 9, 2026
76c6931
Merge main into OpenAI compat security tests
think-in-universe Jun 9, 2026
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
62 changes: 62 additions & 0 deletions crates/ironclaw_architecture/tests/reborn_dependency_boundaries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,68 @@ fn reborn_product_api_crates_do_not_bind_http_ingress() {
);
}

#[test]
fn reborn_openai_compat_routes_do_not_depend_on_v1_gateway_or_legacy_streams() {
let forbidden = [
ForbiddenUse {
pattern: "src/channels/web",
reason: "OpenAI-compatible Reborn routes must not route through v1 gateway handlers",
exempt: None,
},
ForbiddenUse {
pattern: "crate::channels::web",
reason: "OpenAI-compatible Reborn routes must not import v1 gateway modules",
exempt: None,
},
ForbiddenUse {
pattern: "ironclaw::channels::web",
reason: "OpenAI-compatible Reborn routes must not import v1 gateway modules",
exempt: None,
},
ForbiddenUse {
pattern: "GatewayState",
reason: "OpenAI-compatible Reborn routes must not depend on v1 gateway state",
exempt: None,
},
ForbiddenUse {
pattern: "SseManager",
reason: "OpenAI-compatible Reborn streaming must use projection-stream ports, not raw legacy SSE streams",
exempt: None,
},
ForbiddenUse {
pattern: "AppEvent",
reason: "OpenAI-compatible Reborn streaming must translate ProductProjectionItem state, not raw legacy AppEvent streams",
exempt: None,
},
ForbiddenUse {
pattern: "IncomingMessage",
reason: "OpenAI-compatible Reborn routes must enter through ProductWorkflow, not legacy channel ingress",
exempt: None,
},
ForbiddenUse {
pattern: "get_or_create_assistant_conversation",
reason: "OpenAI-compatible Reborn retrieve/cancel must use opaque refs and projection readers, not legacy conversation reconstruction",
exempt: None,
},
ForbiddenUse {
pattern: "ConversationManager",
reason: "OpenAI-compatible Reborn routes must not reconstruct legacy conversations directly",
exempt: None,
},
];

let root = workspace_root();
let compat_src = root.join("crates/ironclaw_reborn_openai_compat/src");
let mut violations = Vec::new();
collect_forbidden_uses(&compat_src, &root, &forbidden, &mut violations);

assert!(
violations.is_empty(),
"Reborn OpenAI-compatible routes must stay ProductWorkflow/projection-port backed and independent of v1 gateway handlers, legacy SSE/AppEvent streams, and legacy conversation reconstruction:\n{}",
violations.join("\n")
);
}

#[test]
fn reborn_product_auth_contract_stays_reborn_native() {
let forbidden = [
Expand Down
24 changes: 13 additions & 11 deletions crates/ironclaw_reborn_openai_compat/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# ironclaw_reborn_openai_compat

Reborn-native OpenAI-compatible API contract surface for #3283 / #4442 /
#4443 / #4444 / #4445 / #4446.

#4443 / #4444 / #4445 / #4446 / #4447.
## Boundary

This crate is a product/API route surface, not a host runtime:
Expand All @@ -13,13 +12,13 @@ This crate is a product/API route surface, not a host runtime:
directly to `ironclaw_llm`.
- Host composition owns listener binding, bearer/session auth, CORS/origin,
body/rate limits, mounting, audit, and product workflow wiring.
- Later slices should route through the channel-neutral `ProductWorkflow`
surface rather than recreating v1 `/v1/chat/completions` LLM proxy behavior.
- Chat, Responses, and streaming paths route through the channel-neutral
`ProductWorkflow` plus projection-reader/streamer ports rather than
recreating v1 `/v1/chat/completions` LLM proxy behavior.

## Opaque Refs and Idempotency

The `refs` module owns the OpenAI-compatible identity contract used before
routes are wired to ProductWorkflow:
The `refs` module owns the OpenAI-compatible identity contract:

- Public ids are typed opaque refs: `chatcmpl-*` for Chat Completions and
`resp_*` for Responses.
Expand All @@ -43,8 +42,8 @@ With `openai-compat-beta`, the default router remains fail-closed unless host
composition injects `OpenAiCompatRouterState::with_chat_completions(...)`.
`ironclaw_reborn_composition::build_openai_compat_route_mount` performs that
host wiring for `ironclaw-reborn serve` by mounting the router inside the
protected Reborn route stack. The injected `OpenAiChatCompletionsWorkflow` is
the Chat Completions slice:
protected Reborn route stack. The injected `OpenAiChatCompletionsWorkflow`
handles Chat Completions create and optional projection-backed SSE streaming:

- `POST /v1/chat/completions` parses the OpenAI-compatible DTO, reserves an
opaque `chatcmpl-*` ref with actor-scoped idempotency, and submits the user
Expand All @@ -68,9 +67,12 @@ the Chat Completions slice:
- The route requires a verified `OpenAiCompatAuthenticatedCaller` extension
minted by host auth middleware. Do not mint auth evidence in this crate's
production feature set.
- This crate still must not call v1 gateway handlers, `ironclaw_llm`,
`TurnCoordinator`, projection internals, listener APIs, secrets, DBs, or the
host runtime directly.
- Streaming create consumes a composition-supplied projection streamer and must
suppress keepalive/control frames, internal refs, projection cursors, and
sanitized backend details.
- This crate still must not call v1 gateway handlers, raw `SseManager`/
`AppEvent` streams, `ironclaw_llm`, `TurnCoordinator`, projection internals,
listener APIs, secrets, DBs, or the host runtime directly.

## Responses Workflow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ use http::Request;
use http_body_util::BodyExt;
use ironclaw_host_api::{AgentId, ProjectId, TenantId, ThreadId, UserId};
use ironclaw_product_adapters::{
AuthRequirement, FakeProductWorkflow, ProductCommandResultPayload, ProductInboundAck,
ProductInboundEnvelope, ProductInboundPayload, ProductProjectionReadInput,
AuthRequirement, FakeProductWorkflow, ProductAdapterError, ProductCommandResultPayload,
ProductInboundAck, ProductInboundEnvelope, ProductInboundPayload, ProductProjectionReadInput,
ProductProjectionSubject, ProductRejection, ProductRejectionKind, ProductWorkflow,
ProjectionReadRequest, ProtocolAuthEvidence, ProtocolAuthFailure,
ProjectionReadRequest, ProtocolAuthEvidence, ProtocolAuthFailure, RedactedString,
};
use ironclaw_reborn_openai_compat::{
InMemoryOpenAiCompatRefStore, OpenAiChatCompletionProjection,
Expand Down Expand Up @@ -561,6 +561,38 @@ async fn chat_completion_projection_reader_error_is_propagated_as_response() {
assert_eq!(workflow.read_count(), 1);
}

#[tokio::test]
async fn chat_completion_product_workflow_error_redacts_request_and_backend_details() {
let workflow = Arc::new(ErrorWorkflow::new(ProductAdapterError::Internal {
detail: RedactedString::new(
"provider stack /host/path /Users/alice SECRET_SENTINEL sk-live runtime trace",
),
}));
let router = test_router_with_workflow(
workflow,
Arc::new(StaticChatProjectionReader::text("unused")),
);

let response = router
.oneshot(chat_request(
json!({
"model": "gpt-reborn",
"messages": [{
"role": "user",
"content": "RAW_TOOL_INPUT_SENTINEL secret-token /host/path"
}]
}),
None,
))
.await
.expect("response");

assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
let rendered = json_body(response).await.to_string();
assert!(rendered.contains("internal_error"), "{rendered}");
assert_error_body_excludes_redaction_sentinels(&rendered);
}

#[test]
fn authenticated_caller_rejects_missing_claim() {
let result = OpenAiCompatAuthenticatedCaller::new(
Expand Down Expand Up @@ -1131,6 +1163,26 @@ impl ProductWorkflow for FixedAckWorkflow {
}
}

struct ErrorWorkflow {
error: ProductAdapterError,
}

impl ErrorWorkflow {
fn new(error: ProductAdapterError) -> Self {
Self { error }
}
}

#[async_trait]
impl ProductWorkflow for ErrorWorkflow {
async fn submit_inbound(
&self,
_envelope: ProductInboundEnvelope,
) -> Result<ProductInboundAck, ProductAdapterError> {
Err(self.error.clone())
}
}

fn accepted_ack() -> ProductInboundAck {
ProductInboundAck::Accepted {
accepted_message_ref: AcceptedMessageRef::new("msg:test").expect("accepted ref"),
Expand Down Expand Up @@ -1253,3 +1305,21 @@ impl OpenAiChatCompletionProjectionReader for RecordingChatProjectionReader {
Ok(self.projection.clone())
}
}

fn assert_error_body_excludes_redaction_sentinels(rendered: &str) {
for forbidden in [
"RAW_TOOL_INPUT_SENTINEL",
"provider stack",
"/host/path",
"/Users/alice",
"SECRET_SENTINEL",
"secret-token",
"sk-live",
"runtime trace",
] {
assert!(
!rendered.contains(forbidden),
"error body leaked forbidden detail {forbidden:?}: {rendered}"
);
}
}
Loading
Loading