Skip to content

Commit c85d49e

Browse files
committed
wip
1 parent b1433e8 commit c85d49e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+2048
-1064
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ jobs:
2626
dotnet-version: ${{ env.DOTNET_VERSION }}
2727

2828
- name: Restore
29-
run: dotnet restore CodexSharp.slnx
29+
run: dotnet restore ManagedCode.CodexSharpSDK.slnx
3030

3131
- name: Build
32-
run: dotnet build CodexSharp.slnx -c Release -warnaserror --no-restore
32+
run: dotnet build ManagedCode.CodexSharpSDK.slnx -c Release -warnaserror --no-restore
3333

3434
- name: Test
35-
run: dotnet test --solution CodexSharp.slnx -c Release --no-build
35+
run: dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release --no-build
3636

3737
aot-smoke:
3838
name: NativeAOT Smoke
@@ -49,7 +49,7 @@ jobs:
4949

5050
- name: Publish AOT smoke app
5151
run: |
52-
dotnet publish samples/CodexSharp.AotSmoke/CodexSharp.AotSmoke.csproj \
52+
dotnet publish tests/AotSmoke/ManagedCode.CodexSharpSDK.AotSmoke.csproj \
5353
-c Release \
5454
-r linux-x64 \
5555
-p:PublishAot=true \

.github/workflows/codeql.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
dotnet-version: '10.0.x'
3838

3939
- name: Build
40-
run: dotnet build CodexSharp.slnx -c Release -warnaserror
40+
run: dotnet build ManagedCode.CodexSharpSDK.slnx -c Release -warnaserror
4141

4242
- name: Perform CodeQL Analysis
4343
uses: github/codeql-action/analyze@v3
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Real Integration
2+
3+
on:
4+
schedule:
5+
- cron: '0 2 * * *'
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
11+
env:
12+
DOTNET_VERSION: '10.0.x'
13+
NODE_VERSION: '22'
14+
15+
jobs:
16+
codex-real-integration:
17+
name: Real Codex Integration (${{ matrix.os }})
18+
if: ${{ secrets.OPENAI_API_KEY != '' }}
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
os: [ubuntu-latest, macos-latest, windows-latest]
23+
runs-on: ${{ matrix.os }}
24+
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v5
28+
with:
29+
submodules: recursive
30+
31+
- name: Setup Node.js
32+
uses: actions/setup-node@v4
33+
with:
34+
node-version: ${{ env.NODE_VERSION }}
35+
36+
- name: Setup .NET
37+
uses: actions/setup-dotnet@v4
38+
with:
39+
dotnet-version: ${{ env.DOTNET_VERSION }}
40+
41+
- name: Install Codex CLI
42+
run: npm install --no-save @openai/codex
43+
44+
- name: Restore
45+
run: dotnet restore ManagedCode.CodexSharpSDK.slnx
46+
47+
- name: Run real integration tests
48+
run: dotnet test --project tests/CodexSharpSDK.Tests.csproj -c Release -- --treenode-filter "/*/*/RealCodexIntegrationTests/*"
49+
env:
50+
CODEX_REAL_INTEGRATION: "1"
51+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
52+
CODEX_TEST_MODEL: gpt-5.3-codex

.github/workflows/release.yml

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,35 @@ jobs:
3030
- name: Extract version
3131
id: version
3232
run: |
33-
VERSION=$(grep -oPm1 "(?<=<Version>)[^<]+" Directory.Build.props)
33+
VERSION=$(dotnet msbuild src/CodexSharpSDK.csproj -nologo -getProperty:Version)
34+
if [ -z "$VERSION" ]; then
35+
echo "Failed to resolve package version from project file."
36+
exit 1
37+
fi
3438
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
3539
echo "Version: $VERSION"
3640
41+
- name: Validate semantic version
42+
run: |
43+
VERSION="${{ steps.version.outputs.version }}"
44+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
45+
echo "Version '$VERSION' is not a valid semantic version."
46+
exit 1
47+
fi
48+
3749
- name: Restore
38-
run: dotnet restore CodexSharp.slnx
50+
run: dotnet restore ManagedCode.CodexSharpSDK.slnx
3951

4052
- name: Build
41-
run: dotnet build CodexSharp.slnx -c Release -warnaserror --no-restore
53+
run: dotnet build ManagedCode.CodexSharpSDK.slnx -c Release -warnaserror --no-restore
4254

4355
- name: Test
44-
run: dotnet test --solution CodexSharp.slnx -c Release --no-build
56+
run: dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release --no-build
4557

4658
- name: Pack
4759
run: |
4860
mkdir -p artifacts
49-
dotnet pack src/CodexSharp/CodexSharp.csproj \
61+
dotnet pack src/CodexSharpSDK.csproj \
5062
-c Release \
5163
--no-build \
5264
-o artifacts
@@ -145,30 +157,12 @@ jobs:
145157
git push origin "$TAG"
146158
fi
147159
148-
- name: Generate release notes
149-
run: |
150-
VERSION="${{ needs.publish.outputs.version }}"
151-
{
152-
echo "# ManagedCode.CodexSharp $VERSION"
153-
echo
154-
echo "Released on $(date +'%Y-%m-%d')"
155-
echo
156-
echo "## Packages"
157-
for package in artifacts/*.nupkg; do
158-
if [[ "$package" == *.snupkg ]]; then
159-
continue
160-
fi
161-
name=$(basename "$package")
162-
echo "- $name"
163-
done
164-
} > release_notes.md
165-
166160
- name: Create GitHub release
167161
uses: softprops/action-gh-release@v2
168162
with:
169163
tag_name: v${{ needs.publish.outputs.version }}
170164
name: v${{ needs.publish.outputs.version }}
171-
body_path: release_notes.md
165+
generate_release_notes: true
172166
files: artifacts/*.*nupkg
173167
draft: false
174168
prerelease: false

AGENTS.md

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AGENTS.md
22

3-
Project: ManagedCode.CodexSharp
3+
Project: ManagedCode.CodexSharpSDK
44
Stack: .NET 10, C# 14, TUnit, GitHub Actions, NativeAOT, NuGet, Codex CLI integration
55

66
Follows [MCAF](https://mcaf.managed-code.com/)
@@ -64,12 +64,12 @@ If no new rule is detected -> do not update the file.
6464

6565
### Commands
6666

67-
- build: `dotnet build CodexSharp.slnx -c Release -warnaserror`
68-
- test: `dotnet test --solution CodexSharp.slnx -c Release`
69-
- format: `dotnet format CodexSharp.slnx`
70-
- analyze: `dotnet build CodexSharp.slnx -c Release -warnaserror /p:TreatWarningsAsErrors=true`
71-
- coverage: `dotnet test --solution CodexSharp.slnx -c Release -- --coverage --coverage-output-format cobertura --coverage-output coverage.cobertura.xml`
72-
- aot-smoke: `dotnet publish samples/CodexSharp.AotSmoke/CodexSharp.AotSmoke.csproj -c Release -r osx-arm64 /p:PublishAot=true`
67+
- build: `dotnet build ManagedCode.CodexSharpSDK.slnx -c Release -warnaserror`
68+
- test: `dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release`
69+
- format: `dotnet format ManagedCode.CodexSharpSDK.slnx`
70+
- analyze: `dotnet build ManagedCode.CodexSharpSDK.slnx -c Release -warnaserror /p:TreatWarningsAsErrors=true`
71+
- coverage: `dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release -- --coverage --coverage-output-format cobertura --coverage-output coverage.cobertura.xml`
72+
- aot-smoke: `dotnet publish tests/AotSmoke/ManagedCode.CodexSharpSDK.AotSmoke.csproj -c Release -r osx-arm64 /p:PublishAot=true`
7373

7474
### Task Delivery (ALL TASKS)
7575

@@ -86,6 +86,8 @@ If no new rule is detected -> do not update the file.
8686
- `docs/ADR/*` for design/architecture decisions
8787
- `docs/Architecture/Overview.md` when module boundaries or interactions change
8888
- Implement code and tests together.
89+
- When asked to fix review findings, close every confirmed finding in the same pass; do not leave partial fixes.
90+
- Do not keep or add public sample projects; repository focus is SDK + tests (including AOT tests) only.
8991
- Run verification in this order:
9092
- focused tests for changed behavior
9193
- full solution tests
@@ -108,11 +110,20 @@ If no new rule is detected -> do not update the file.
108110

109111
### Testing (ALL TASKS)
110112

111-
- Testing framework is TUnit (`tests/CodexSharp.Tests`).
113+
- Testing framework is TUnit (`tests`).
114+
- TUnit/MTP does not support `--filter`; for focused runs use `dotnet test ... -- --treenode-filter "<pattern>"`.
115+
- Always invoke runner options after `--` (for example `dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release -- --treenode-filter "<pattern>"`); if a focused filter runs zero tests, treat it as an invalid filter and correct it before reporting results.
116+
- In this repository, method-level `treenode-filter` patterns resolve at depth 3 (`/*/*/*/<TestMethodName>`). For integration subset runs use `dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release -- --treenode-filter "/*/*/*/RunAsync_*_EndToEnd"` (matches current `CodexExecIntegrationTests`).
117+
- For reliable discovery when selecting focused tests, use the built test app directly: `tests/bin/Release/net10.0/ManagedCode.CodexSharpSDK.Tests --list-tests` (the `dotnet test ... -- --list-tests` wrapper can report zero tests in this setup).
112118
- Every behavior change must include or update tests.
113119
- Prefer behavior-level tests over trivial implementation tests.
114-
- For CLI process interactions, use `FakeCodexProcessRunner` test doubles rather than invoking external binaries.
120+
- Integration test sandboxes must be created under the repository `tests` tree (for example `tests/.sandbox/*`) for deterministic, inspectable paths; do not use `Path.GetTempPath()` for test sandbox directories.
121+
- For CLI process interaction tests, use the real installed `codex` CLI (no `FakeCodexProcessRunner` test doubles).
122+
- Treat `codex` CLI as a test prerequisite: ensure local/CI test setup installs `codex` before running CLI interaction tests; do not replace this with fakes.
123+
- Real Codex integration tests must work with existing Codex CLI login/session by default; `OPENAI_API_KEY` is optional and must not be a hard requirement.
124+
- Do not bypass integration tests on Windows with unconditional early returns; keep tests cross-platform for supported Codex CLI environments.
115125
- Parser changes require tests in `ThreadEventParserTests` for supported and invalid payloads.
126+
- Parser performance tests must use representative mixed payloads across supported event/item types and assert parsed output shape; avoid single-payload stopwatch loops that do not validate branch coverage.
116127
- Client/thread concurrency changes require explicit concurrent tests.
117128
- Never delete/skip tests to get green CI.
118129

@@ -126,12 +137,29 @@ If no new rule is detected -> do not update the file.
126137

127138
- Follow `.editorconfig` and analyzer rules.
128139
- Always build with `-warnaserror` so warnings fail the build.
140+
- Prefer idiomatic, readable C# with clear naming and straightforward control flow so code can be understood quickly during review and maintenance.
141+
- Use synchronization primitives only when there is a proven shared-state invariant; prefer simpler designs over ad-hoc locking for maintainable production code.
129142
- No magic literals: extract constants/enums/config values.
130143
- Protocol and CLI string tokens are mandatory constants: never inline literals in parsing, mapping, or switch branches.
131144
- In SDK model records, never inline protocol type literals in constructors (`ThreadItem(..., "...")`, `ThreadEvent("...")`); always reference protocol constants.
132145
- Do not expose a public SDK type named `Thread`; use `CodexThread` to avoid .NET type-name conflicts.
133-
- Keep public API and naming aligned with package/namespace `ManagedCode.CodexSharp`.
146+
- Keep public API and naming aligned with package/namespace `ManagedCode.CodexSharpSDK`.
147+
- Solution/workspace file naming must use `ManagedCode.CodexSharpSDK` prefix for consistency with package identity.
148+
- Keep package/version metadata centralized in `Directory.Build.props`; avoid duplicating version structure or release metadata blocks in individual `.csproj` files unless a project-specific override is required.
149+
- Never hardcode guessed Codex/OpenAI model names in tests, docs, or defaults; verify supported models and active default via Codex CLI first.
150+
- Before setting or changing any `Model` value, read available models and current default from the local `codex` CLI in the same environment/account and only then update code/tests/docs.
151+
- Model identifiers in code/tests must come from centralized constants or a shared resolver helper; do not inline model string literals repeatedly.
152+
- Image input API must support passing local image data as file path, `FileInfo`, and `Stream`.
153+
- Use `Microsoft.Extensions.Logging.ILogger` for SDK logging extension points; do not introduce custom logger interfaces or custom log-level enums.
154+
- In tests, prefer `Microsoft.Extensions.Logging.Abstractions.NullLogger` instead of custom fake logger implementations when log capture is not required.
134155
- Default to AOT/trimming-safe patterns (explicit JSON handling, avoid reflection-heavy designs).
156+
- Avoid ambiguous option names like `*Override` for primary settings; prefer explicit names (for example executable path / working directory) and keep compatibility aliases only when necessary.
157+
- README first examples must be beginner-friendly: avoid advanced/optional knobs (for example `CodexPathOverride`) in the very first snippet.
158+
- When a README snippet shows model tuning, include `ModelReasoningEffort` together with `Model`.
159+
- Public examples should build output schemas with typed `StructuredOutputSchema` models and map responses to typed DTOs for readability and maintainability.
160+
- Do not keep or add `JsonSchema` helper abstractions in SDK API/tests; use typed request/response DTO models instead of schema-builder utilities.
161+
- Do not handcraft JSON with `JsonValue`/`JsonNode` literals for structured outputs in API/tests/examples; define typed DTO models and map schema/fields from those models.
162+
- Keep consumer usage examples in `README.md`; do not introduce standalone sample projects.
135163

136164
### Critical (NEVER violate)
137165

@@ -165,9 +193,17 @@ If no new rule is detected -> do not update the file.
165193
- Explicit, deterministic behavior
166194
- Full test coverage for new logic
167195
- Clear docs with diagrams and direct code links
196+
- Simple onboarding examples first, advanced configuration later.
168197

169198
### Dislikes
170199

171200
- Magic strings in protocol parsing and CLI mappings
172201
- Hidden assumptions in CI/release pipelines
202+
- Unreadable, non-idiomatic C# that looks chaotic and hard to reason about
203+
- Unjustified `lock`/thread-synchronization complexity that obscures intent and increases maintenance risk
173204
- Template placeholders left in production repository docs
205+
- Raw nested `JsonObject`/`JsonArray` schema literals in user-facing examples.
206+
- Public sample projects in this repository; prefer tests (including AOT tests) instead.
207+
- Custom logging abstractions when `ILogger` already solves the integration use case.
208+
- Performance tests that exercise only one parser payload and do not cover supported parsing branches.
209+
- Example code scattered across standalone sample projects instead of `README.md`.

CodexSharp.slnx

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/CodexSharp.Tests/CodexSharp.Tests.csproj renamed to CodexSharpSDK.Tests/CodexSharpSDK.Tests.csproj

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22
<PropertyGroup>
33
<OutputType>Exe</OutputType>
44
<IsPackable>false</IsPackable>
5-
<RootNamespace>ManagedCode.CodexSharp.Tests</RootNamespace>
6-
<AssemblyName>ManagedCode.CodexSharp.Tests</AssemblyName>
5+
<RootNamespace>ManagedCode.CodexSharpSDK.Tests</RootNamespace>
6+
<AssemblyName>ManagedCode.CodexSharpSDK.Tests</AssemblyName>
77
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
88
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
99
<NoWarn>$(NoWarn);CA1707</NoWarn>
1010
</PropertyGroup>
1111

12+
<ItemGroup>
13+
<Compile Remove="AotSmoke/**/*.cs" />
14+
<EmbeddedResource Remove="AotSmoke/**" />
15+
<None Remove="AotSmoke/**" />
16+
</ItemGroup>
17+
1218
<ItemGroup>
1319
<PackageReference Include="TUnit" />
1420
<PackageReference Include="coverlet.collector">
@@ -18,6 +24,6 @@
1824
</ItemGroup>
1925

2026
<ItemGroup>
21-
<ProjectReference Include="../../src/CodexSharp/CodexSharp.csproj" />
27+
<ProjectReference Include="../src/CodexSharpSDK.csproj" />
2228
</ItemGroup>
2329
</Project>

tests/CodexSharp.Tests/CodexExecIntegrationTests.cs renamed to CodexSharpSDK.Tests/Integration/CodexExecIntegrationTests.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
namespace ManagedCode.CodexSharp.Tests;
1+
namespace ManagedCode.CodexSharpSDK.Tests;
22

33
public class CodexExecIntegrationTests
44
{
5+
private const string SandboxRootDirectoryName = ".sandbox";
6+
private const string SandboxDirectoryPrefix = "codexsharp-integration-";
7+
58
[Test]
69
public async Task RunAsync_UsesDefaultProcessRunner_EndToEnd()
710
{
@@ -26,7 +29,7 @@ public async Task RunAsync_UsesDefaultProcessRunner_EndToEnd()
2629

2730
var thread = client.StartThread(new ThreadOptions
2831
{
29-
Model = "gpt-5",
32+
Model = "gpt-5.3-codex",
3033
SandboxMode = SandboxMode.WorkspaceWrite,
3134
});
3235

@@ -40,7 +43,7 @@ public async Task RunAsync_UsesDefaultProcessRunner_EndToEnd()
4043
await Assert.That(args).Contains("exec");
4144
await Assert.That(args).Contains("--experimental-json");
4245
await Assert.That(args).Contains("--model");
43-
await Assert.That(args).Contains("gpt-5");
46+
await Assert.That(args).Contains("gpt-5.3-codex");
4447
await Assert.That(args).Contains("--sandbox");
4548
await Assert.That(args).Contains("workspace-write");
4649

@@ -126,7 +129,11 @@ public async Task RunAsync_PropagatesNonZeroExitCode_EndToEnd()
126129

127130
private static string CreateSandboxDirectory()
128131
{
129-
var directory = Path.Combine(Path.GetTempPath(), $"codexsharp-integration-{Guid.NewGuid():N}");
132+
var testsDirectory = Environment.CurrentDirectory;
133+
var sandboxRootDirectory = Path.Combine(testsDirectory, SandboxRootDirectoryName);
134+
Directory.CreateDirectory(sandboxRootDirectory);
135+
136+
var directory = Path.Combine(sandboxRootDirectory, $"{SandboxDirectoryPrefix}{Guid.NewGuid():N}");
130137
Directory.CreateDirectory(directory);
131138
return directory;
132139
}

0 commit comments

Comments
 (0)