# How to Use IronQR with ChatGPT Codex
QR code backlogs often arrive as disorganized folders containing scanned asset tags, on-site phone photos, fax copies, zero-byte files from scanner errors, and cover sheets without codes. A naive loop that processes one file at a time halts at the first corrupted file and does not track which images failed or the reasons for failure.
ChatGPT Codex is OpenAI's coding agent. It operates in terminals, IDEs, the ChatGPT desktop app, and the cloud, all under a single ChatGPT sign-in. IronQR is Iron Software's .NET QR code library.
Each prompt instructs Codex to perform the task and generate a results file holding one row per image. Since batch jobs may process files that yield no results, all counts referenced in this guide are derived from these results files, as indicated in the prompts.
*as-heading:2(Quickstart)*
!!!--LIBRARY_NUGET_INSTALL_BLOCK--!!!
Start Codex within a .NET console project containing your image folder, and specify the batch processing requirements.
```text
$ironqr Add the IronQR package to this console project, then make Program.cs
decode every file in Backlog/sample/ with QrReader.ReadAsync. Write
out/quickstart.csv with one row per file (name, decoded value or empty, QR
type, exception type if the file threw) and print the decoded, empty, and
threw counts by reading that CSV back. Load the key out of the
IRONQR_LICENSE_KEY variable. Call Dispose() on each QrImageInput yourself
instead of a using block, which throws in IronQR 2026.10.1. Run dotnet build
and dotnet run and fix whatever fails.
```
The `$ironqr` mention loads IronQR's skill before Codex writes code.
<div class="hsg-featured-snippet">
<h3>Minimal Workflow (5 steps)</h3>
<ol>
<li><a class="js-modal-open" data-modal-id="trial-license-after-download" href="https://www.nuget.org/packages/IronQR/">Download the C# QR code library from NuGet</a></li>
<li>Save IronQR's skill as <code>.agents/skills/ironqr/SKILL.md</code> so <code>$ironqr</code> resolves</li>
<li>Commit an <code>AGENTS.md</code> with the repository's QR decoding rules</li>
<li>Ask for one mode at a time (sequential, <code>Parallel.ForEach</code>, then <code>SemaphoreSlim</code>) and compare each results file with the last</li>
<li>Look up each member Codex used in the <a href="https://ironsoftware.com/qr/csharp/object-reference/api/">API reference</a> and check the final results file against your ground truth</li>
</ol>
</div>
<br class="clear" />
---
## What is ChatGPT Codex, and how does it fit into .NET QR code work?
Codex works inside the permissions you grant it. It writes the decoder, runs it over the images, reads the output, and fixes the code. Install the CLI with `npm install -g @openai/codex` from the [Codex CLI repository](https://github.com/openai/codex), and see the [Codex CLI documentation](https://learn.chatgpt.com/docs/codex/cli) for terminal use.
A backlog with ground truth makes review short. An invented member fails the build, and a wrong decode shows up as a row that disagrees with `Backlog/manifest.csv`.
The agent may structure each batch differently, but the decoder has to return the same value for a given image at any thread count. That consistency belongs to the library, and IronQR covers the parts a backlog needs.
- **Photos and scans in one call:** the `QrScanMode.Auto` mode runs the detection model and, when the model finds nothing, retries with a basic scan. The [scan modes guide](https://ironsoftware.com/qr/csharp/how-to/read-qr-codes-with-scan-modes/) compares it with the two single-pass modes.
- **The symbol type on every result:** `QrResult.QrType` separates standard QR (`QrEncoding.QRCode`) from Micro QR and rMQR, so a validator can reject labels that are not asset tags.
## How do you write an IronQR prompt and set up Codex for it?
An effective IronQR batch prompt states six things. For this backlog, the input is every PNG and JPEG in `Backlog/images/`, a `SemaphoreSlim` sized by a `--concurrency` option sets the concurrency, and the output is `out/pipeline.csv` with one row per file. A file that throws gets a row while the batch continues. The proof is a row-for-row match with `out/sequential.csv`, and the constraints are no new packages and Auto as the default.
OpenAI's [research on hallucination](https://openai.com/index/why-language-models-hallucinate/) finds that training and evaluation reward guessing, and in a batch job the guess is usually a total in the closing summary.
[[t:(Before you send a batch prompt, find the output file it names.)]]
Codex discovers skills in the repository's `.agents/skills` folder, and each skill is a folder holding a `SKILL.md` with a name and a description in its frontmatter. Save IronQR's [`skill.md`](https://ironsoftware.com/qr/csharp/skill.md) as `.agents/skills/ironqr/SKILL.md` and the `$ironqr` mention resolves. Codex can also pick the skill without the mention when a task matches its description.
`AGENTS.md` carries the rules every session starts with. Codex reads at most one such file from each directory between the repository root and the folder it was started in, and a deeper file overrides the guidance above it. The `/init` command drafts one. The prompt below also names IronQR's [`llms.txt`](https://ironsoftware.com/qr/csharp/llms.txt) index as an API reference.
```text
Create the repository's AGENTS.md with a section titled QR decoding. It
should say that IronQR (package IronQR, namespace IronQr) is the only QR
library here, that the ironqr skill in .agents/skills/ironqr/ and IronQR's
llms.txt documentation index are the API references, that the key is read
from IRONQR_LICENSE_KEY and never printed or written to a file, that
QrImageInput is disposed with an explicit Dispose() call and never a using
block, that Backlog/manifest.csv is ground truth and read-only, and that any
count, rate, or timing you report must come from a per-image results file
written in the same turn, named in your reply.
```
## How do you ask Codex to add IronQR and decode a first folder?
```text
Show me the IronQR PackageReference in AssetBacklog.csproj and the installed
version. Then run dotnet list package --include-transitive and quote the lines
for IronQR.Slim and Microsoft.ML.OnnxRuntime, since the detection model
depends on them. Change no package references.
```
The `IronQR` package brings in `IronQR.Slim` for the managed API, `Microsoft.ML.OnnxRuntime` for detection, and the Windows native readers. A project on `IronQR.Slim` alone, which on Windows also needs `IronSoftware.ReaderInternals.Windows`, still reads with the basic scan but cannot run the detection model. The package is `IronQR` with a capital R while the namespace is `IronQr`, so a `using IronQR` directive fails to compile. Check the [IronQR changelog](https://ironsoftware.com/qr/csharp/product-updates/changelog/) before you pin a release.
IronQR adds no trial watermark to reads. An unlicensed first read throws LicensingException with the message "Production License Required", and with `ReadAsync` the exception surfaces when the task is awaited. In a batch, that becomes one identical failure row per image.
```text
Move the license setup into a Startup.ApplyIronQrKey method called first in
Program.cs. If IRONQR_LICENSE_KEY is unset or License.IsLicensed is false,
exit with code 2 and one clear line on stderr before any image is opened,
because IronQR throws on the first read instead of degrading. Run the program
once as is and once with the variable removed for that single command, and
report both exit codes and what IsLicensed returned.
```
The [license keys page](https://ironsoftware.com/qr/csharp/get-started/license-keys/) covers trial keys and the configuration files IronQR also reads. The exception type is internal to the IronQr assembly, so catching `LicensingException` by type fails to compile with error CS0122.
A sequential decoder comes next, and its results file becomes the reference for every faster mode.
```text
Create Services/BacklogDecoder.cs with DecodeAsync(string path) returning an
ImageOutcome record: file, status (Decoded, Empty, Unreadable), values, QR
types, exception type and message, and milliseconds. Open the file with
AnyBitmap.FromFile, read with QrScanMode.Auto, and never let one file stop
the batch. Add a --sequential mode that runs it over Backlog/images/ one file
at a time, writes out/sequential.csv, and starts out/runs.csv with the mode
and wall time. Report the status counts and the wall time from those files.
```
## How do you decode a folder in parallel with IronQR?
The [async and multithreading guide](https://ironsoftware.com/qr/csharp/how-to/async-multithreading/) calls a separate reader per iteration the safe default, because IronQR makes no explicit thread-safety guarantee for a shared instance. The next prompt has Codex's built-in explorer agent, a read-heavy agent for codebase exploration, check that against the documentation before anything goes parallel.
```text
Ask the explorer agent to answer this without editing anything. Before the
decoder goes parallel, list every QrReader, QrImageInput, and AnyBitmap that
BacklogDecoder creates or keeps, say which could be shared across threads,
and back each answer with a quote from
https://ironsoftware.com/qr/csharp/how-to/async-multithreading/ or the
ironqr skill. Flag anything the documentation leaves unstated.
```
In calibration, sharing one reader across 32 threads produced the same results as a reader per file. A later release may differ, and a reader per file costs little.
The input object needs care too. In version 2026.10.1, a using block around it throws NotImplementedException on exit, even after a successful read, so a batch would record every good image as a failure. Review each diff for one.
For the parallel version, the prompt names the built-in worker agent, which runs the edit in its own thread.
```text
Have the worker agent add a --parallel mode with Parallel.ForEach,
MaxDegreeOfParallelism from a --threads option (default
Environment.ProcessorCount), and a new QrReader per file. Use the synchronous
Read inside the loop body, since Parallel.ForEach does not await an async
lambda. Write out/parallel.csv sorted by file name, then compare its status
and value columns with out/sequential.csv and fix any row that differs
before you finish. Append mode, thread count, and wall time to out/runs.csv.
```
An async lambda passed to `Parallel.ForEach` compiles as async void, so the loop returns before the reads finish and the results file comes out short. The comparison with the sequential file catches it.
## How do you limit concurrent reads with ReadAsync?
A parallel loop blocks a thread per read. `ReadAsync` behind a `SemaphoreSlim` keeps a fixed number of reads in flight and leaves the calling thread free, the pattern the async guide gives for high-volume batches. It takes an `IQrInput` and has no overload for a file path, so each task still opens its own bitmap.
```text
Add a --pipeline mode: Task.WhenAll over every file, each task waiting on a
SemaphoreSlim sized by --concurrency and awaiting ReadAsync. Run it at
concurrency 2, 8, and Environment.ProcessorCount, writing
out/pipeline-c<N>.csv for each and appending to out/runs.csv. Confirm each
file matches out/sequential.csv row for row, then tell me which setting was
fastest according to runs.csv.
```
The detection model already spreads one read across several cores, so a higher limit need not run faster. Let `runs.csv` pick the limit for your hardware, and keep the semaphore for what it does reliably, which is capping memory use.
## Which scan mode should a batch use?
The detection model reads photos the basic pass misses, and it also accounts for most of the time each read takes.
```text
Measure what the machine learning detector costs on this backlog. Run the
pipeline at the fastest concurrency from runs.csv, first under
QrScanMode.Auto and then under QrScanMode.OnlyBasicScan, writing
out/mode-auto.csv and out/mode-basic.csv with per-image milliseconds. Join
both with the category column of Backlog/manifest.csv and report decoded
count and median milliseconds per category for each mode. If either mode
reads files the other misses, keep Auto as the default and build a single
OnlyBasicScan retry of its Empty files into the decoder for every later run.
Rerun to out/mode-fallback.csv and count the recoveries.
```
Both modes read every clean scan and fax copy, and each missed a different handful of phone photos. On the photos only the basic pass read, the detection model located the code and could not decode it, so Auto never fell back. Retrying the empty files with `QrScanMode.OnlyBasicScan` recovered every photo.
## How do you validate decoded values and report failures?
A batch meets three kinds of failure, and each one looks different in code.
| Input | Result | Status |
|---|---|---|
| Empty, truncated, or non-image file | The image load throws before IronQR runs | Unreadable |
| No code, or damage past error correction | An empty collection, never null | Empty |
| Any code, read in the detection-only mode | Corner points, an empty `Value`, a `QrType` of `QrEncoding.None` | Empty, not Decoded |
The third row is the dangerous one. Our first sequential decoder would have counted it as Decoded, because it only checked whether any result came back.
```text
Backlog/README.md defines a valid asset tag. Add Services/TagValidator.cs and
apply it after each read: a decode only counts when the symbol is a standard
QR code (QrEncoding.QRCode), the value matches the documented pattern, and
the Luhn check digit holds. Give the others the statuses WrongSymbol,
InvalidFormat, or BadCheckDigit. Rerun the pipeline to out/validated.csv and
list every file whose status disagrees with expected_status in the manifest.
```
A check digit is part of your data, so IronQR cannot validate it. Reed-Solomon correction inside the read, which the [checksum and fault tolerance guide](https://ironsoftware.com/qr/csharp/how-to/checksum-and-fault-tolerance/) describes, only proves the symbol decoded, and a result carries no confidence property. A tag with a wrong check digit scans cleanly and is still invalid.
IronQR's [error messages guide](https://ironsoftware.com/qr/csharp/how-to/detailed-error-messages/) shows the per-file logging pattern the next prompt applies to the whole backlog.
```text
Every file that is not Decoded needs a recorded reason. Write one JSON line
per file to out/decode-log.jsonl (file, status, exception type, message,
milliseconds), send IronQR's own log to out/ironqr-debug.log through
IronQr.Logging.Logger, and generate out/failures.md from the JSONL only: one
section per status other than Decoded, a count, and every file name. Then
check the section counts against out/validated.csv.
```
In version 2026.10.1, the `IronQr.Logging.Logger` log records the scan path each read took, such as a detection scan with the machine learning model. Because the report is built only from the JSONL, a failure the decoder never logged shows up as a count that disagrees with the results file.
## How do you diagnose an image that will not decode?
Codex can look at a picture. Attach the file with `codex --image` when you start the session, or paste it into the composer.
```text
I attached IMG_0018.png from Backlog/images/ with codex --image. The pipeline
marks it Empty. Look at the picture and tell me whether it is a damaged code,
a page with no code, or a capture problem a rescan would fix. Then test your
answer: read it with all three QrScanMode values and write
out/retry-IMG_0018.csv. Do not change the pipeline.
```
In our run, all three modes came back empty for this file, and in calibration the detection model found nothing to locate. The code itself is damaged, so the loss is past what error correction recovers, and neither another read nor a rescan of the same label will change that. The fix is a replacement label and, if you print the labels yourself, a higher [error correction level](https://ironsoftware.com/qr/csharp/how-to/error-correction-qr-code/).
## How do you debug incorrect batch output end to end?
```text
Send the explorer agent into Jobs/NightlyImport.cs and Logs/nightly-2026-10-06/
before anything is edited. That run reported six unreadable files and no other
failure, yet Backlog/archive-index-gaps.csv lists assets the archive never
received. Find what the missing assets have in common, explain why the job
counted them as successes, and propose the smallest fix together with the
command that would prove it. Put the findings in out/nightly-diagnosis.md and
change no code.
```
Approve the diagnosis before any fix. A good one quotes the line that decides success. Check the proposed fix against `out/mode-fallback.csv` too.
## How do you turn one-off code into reusable, tested components?
```text
Put the worker agent on folding the modes into a BacklogJob class. Add the
Microsoft.Extensions.DependencyInjection package, register the job with it,
and configure it with a BacklogOptions record (input folder, concurrency,
scan mode, output folder). Keep the validator, the basic-scan retry, and the
logging in it, stay under 160 lines, and add XML doc comments. Before
finishing, run it over Backlog/images/ into out/final.csv and confirm every
row agrees with the manifest's expected_status and expected_value.
```
A refactor can drop the validator, the basic-scan retry, or the per-file reader without a build error. Only a full rerun against ground truth catches that.
```text
Add an xUnit project, AssetBacklog.Tests, and cover BacklogJob with tests
that read real files from Backlog/images/: a clean scan decodes to its
manifest value, the zero-byte file comes back Unreadable without throwing, a
damaged image is Empty, a Micro QR label is WrongSymbol, a bad check digit is
BadCheckDigit, and a parallel run over 40 files matches a sequential one.
Assert on status and value. Run dotnet test and report the totals it prints.
```
The manifest fixes status and value, while timings change from run to run.
Keep recurring IronQR questions in the repository skill. OpenAI's [custom prompts](https://learn.chatgpt.com/docs/custom-prompts) page marks the older `~/.codex/prompts` folder deprecated in favor of skills.
```text
$ironqr Using the IronQR API reference, list every member of QrResult and say
whether IronQR reports a confidence or checksum score for a read. Then point
to the line in BacklogJob that decides a file is Decoded.
```
In our run, Codex listed the four members of a result and traced Decoded from the job through the decoder to the validator line that returns it.
## How do you reuse these prompts on another image folder?
Change the paths, and every prompt above runs as written. The [AI coding assistants guide](https://ironsoftware.com/qr/csharp/ai-agents/ai/) runs a single acceptance prompt end to end and publishes the result.
## What did the prompts produce in a real run?
We sent every prompt above to Codex in order, in one thread, against a sample repository. It holds a console application and a backlog of 301 images (clean scans, phone photos, fax copies, damaged labels, blank cover sheets, Micro QR shelf labels, invalid tags, and six files that are not images at all), with a manifest of the expected status and value of each. After the session we rebuilt the project in Release and ran the tests. We also decoded every image independently to check the results below.
- The basic-scan retry raised Decoded from 256 to 264. Before it, each mode decoded 256 of 301, and 16 files differed between the two.
- A clean scan took a median 360 ms under Auto and 35 ms under the basic scan, which ran the whole backlog in 7 s at limit 2.
- The sequential decoder recorded 256 Decoded, 39 Empty and 6 Unreadable in 71 s.
- Parallel mode wrote the same 301 rows in 60 s at 32 threads, and the pipeline wrote them in 55, 66 and 68 s at limits 2, 8 and 32.
- In the quickstart, 9 of 12 rows decoded, 2 came back empty and 1 zero-byte file threw.
- Validation matched the manifest with 240 Decoded, 31 Empty, 8 WrongSymbol, 8 InvalidFormat, 8 BadCheckDigit and 6 Unreadable.
- The failure report held 61 records, one per failed file.
- After the refactor, the 64-line job matched the manifest on 301 of 301 rows, and 6 of 6 tests passed, again in Release.
- All 16 prompts ran with every build passing and no manual code edits. Prompts 13 to 16 hit the free-plan usage limit and resumed in the same thread after Codex was signed in again.
- Before the investigation prompt we added a nightly job with a planted fault, its logs and a gaps list. Codex found the detection-only mode on photo JPEGs and a success check that accepts any result.
Codex's version, model, timing and test machine for this run are in the shared [run record](/qr/csharp/ai-agents/ai/#the-run-record).
---
## Troubleshooting
- [Async and Multithreading](https://ironsoftware.com/qr/csharp/how-to/async-multithreading/) if a parallel run writes fewer rows than there are images
- [Apply a License Key in IronQR](https://ironsoftware.com/qr/csharp/troubleshooting/apply-a-license-key-in-ironqr/) if every row in the results file shows the same licensing failure
- [Read QR Codes with Scan Modes](https://ironsoftware.com/qr/csharp/how-to/read-qr-codes-with-scan-modes/) if results come back with corner points and no values
- [Runtimes Copy Exception](https://ironsoftware.com/qr/csharp/troubleshooting/runtimes-copy-exception/) if the detection model fails to load after a publish
---
## Questions?
If you have any questions, reach out to [support@ironsoftware.com](mailto:support@ironsoftware.com)
QR code backlogs often arrive as disorganized folders containing scanned asset tags, on-site phone photos, fax copies, zero-byte files from scanner errors, and cover sheets without codes. A naive loop that processes one file at a time halts at the first corrupted file and does not track which images failed or the reasons for failure.
ChatGPT Codex is OpenAI's coding agent. It operates in terminals, IDEs, the ChatGPT desktop app, and the cloud, all under a single ChatGPT sign-in. IronQR is Iron Software's .NET QR code library.
Each prompt instructs Codex to perform the task and generate a results file holding one row per image. Since batch jobs may process files that yield no results, all counts referenced in this guide are derived from these results files, as indicated in the prompts.
Quickstart
Install with NuGet
PM > Install-Package IronQR
Install-Package IronQR
Install IronQR by running the command above in the NuGet Package Manager Console, or search for the package in the NuGet Package Manager.
Start Codex within a .NET console project containing your image folder, and specify the batch processing requirements.
$ironqr Add the IronQR package to this console project, then make Program.csdecode every file in Backlog/sample/ with QrReader.ReadAsync. Writeout/quickstart.csv with one row per file (name, decoded value or empty, QRtype, exception type if the file threw) and print the decoded, empty, andthrew counts by reading that CSV back. Load the key out of theIRONQR_LICENSE_KEY variable. Call Dispose() on each QrImageInput yourselfinstead of a using block, which throws in IronQR 2026.10.1. Run dotnet buildand dotnet run and fix whatever fails.
$ironqr Add the IronQR package to this console project, then make Program.cs
decode every file in Backlog/sample/ with QrReader.ReadAsync. Write
out/quickstart.csv with one row per file (name, decoded value or empty, QR
type, exception type if the file threw) and print the decoded, empty, and
threw counts by reading that CSV back. Load the key out of the
IRONQR_LICENSE_KEY variable. Call Dispose() on each QrImageInput yourself
instead of a using block, which throws in IronQR 2026.10.1. Run dotnet build
and dotnet run and fix whatever fails.
Text
The $ironqr mention loads IronQR's skill before Codex writes code.
Save IronQR's skill as .agents/skills/ironqr/SKILL.md so $ironqr resolves
Commit an AGENTS.md with the repository's QR decoding rules
Ask for one mode at a time (sequential, Parallel.ForEach, then SemaphoreSlim) and compare each results file with the last
Look up each member Codex used in the API reference and check the final results file against your ground truth
What is ChatGPT Codex, and how does it fit into .NET QR code work?
Codex works inside the permissions you grant it. It writes the decoder, runs it over the images, reads the output, and fixes the code. Install the CLI with npm install -g @openai/codex from the Codex CLI repository, and see the Codex CLI documentation for terminal use.
A backlog with ground truth makes review short. An invented member fails the build, and a wrong decode shows up as a row that disagrees with Backlog/manifest.csv.
The agent may structure each batch differently, but the decoder has to return the same value for a given image at any thread count. That consistency belongs to the library, and IronQR covers the parts a backlog needs.
Photos and scans in one call: the QrScanMode.Auto mode runs the detection model and, when the model finds nothing, retries with a basic scan. The scan modes guide compares it with the two single-pass modes.
The symbol type on every result:QrResult.QrType separates standard QR (QrEncoding.QRCode) from Micro QR and rMQR, so a validator can reject labels that are not asset tags.
How do you write an IronQR prompt and set up Codex for it?
An effective IronQR batch prompt states six things. For this backlog, the input is every PNG and JPEG in Backlog/images/, a SemaphoreSlim sized by a --concurrency option sets the concurrency, and the output is out/pipeline.csv with one row per file. A file that throws gets a row while the batch continues. The proof is a row-for-row match with out/sequential.csv, and the constraints are no new packages and Auto as the default.
OpenAI's research on hallucination finds that training and evaluation reward guessing, and in a batch job the guess is usually a total in the closing summary.
Tips: Before you send a batch prompt, find the output file it names.
Codex discovers skills in the repository's .agents/skills folder, and each skill is a folder holding a SKILL.md with a name and a description in its frontmatter. Save IronQR's skill.md as .agents/skills/ironqr/SKILL.md and the $ironqr mention resolves. Codex can also pick the skill without the mention when a task matches its description.
AGENTS.md carries the rules every session starts with. Codex reads at most one such file from each directory between the repository root and the folder it was started in, and a deeper file overrides the guidance above it. The /init command drafts one. The prompt below also names IronQR's llms.txt index as an API reference.
Create the repository's AGENTS.md with a section titled QR decoding. Itshould say that IronQR (package IronQR, namespace IronQr) is the only QRlibrary here, that the ironqr skill in .agents/skills/ironqr/ and IronQR'sllms.txt documentation index are the API references, that the key is readfrom IRONQR_LICENSE_KEY and never printed or written to a file, thatQrImageInput is disposed with an explicit Dispose() call and never a usingblock, that Backlog/manifest.csv is ground truth and read-only, and that anycount, rate, or timing you report must come from a per-image results filewritten in the same turn, named in your reply.
Create the repository's AGENTS.md with a section titled QR decoding. It
should say that IronQR (package IronQR, namespace IronQr) is the only QR
library here, that the ironqr skill in .agents/skills/ironqr/ and IronQR's
llms.txt documentation index are the API references, that the key is read
from IRONQR_LICENSE_KEY and never printed or written to a file, that
QrImageInput is disposed with an explicit Dispose() call and never a using
block, that Backlog/manifest.csv is ground truth and read-only, and that any
count, rate, or timing you report must come from a per-image results file
written in the same turn, named in your reply.
Text
How do you ask Codex to add IronQR and decode a first folder?
Show me the IronQR PackageReference in AssetBacklog.csproj and the installedversion. Then run dotnet list package --include-transitive and quote the linesfor IronQR.Slim and Microsoft.ML.OnnxRuntime, since the detection modeldepends on them. Change no package references.
Show me the IronQR PackageReference in AssetBacklog.csproj and the installed
version. Then run dotnet list package --include-transitive and quote the lines
for IronQR.Slim and Microsoft.ML.OnnxRuntime, since the detection model
depends on them. Change no package references.
Text
The IronQR package brings in IronQR.Slim for the managed API, Microsoft.ML.OnnxRuntime for detection, and the Windows native readers. A project on IronQR.Slim alone, which on Windows also needs IronSoftware.ReaderInternals.Windows, still reads with the basic scan but cannot run the detection model. The package is IronQR with a capital R while the namespace is IronQr, so a using IronQR directive fails to compile. Check the IronQR changelog before you pin a release.
IronQR adds no trial watermark to reads. An unlicensed first read throws LicensingException with the message "Production License Required", and with ReadAsync the exception surfaces when the task is awaited. In a batch, that becomes one identical failure row per image.
Move the license setup into a Startup.ApplyIronQrKey method called first inProgram.cs. If IRONQR_LICENSE_KEY is unset or License.IsLicensed is false,exit with code 2 and one clear line on stderr before any image is opened,because IronQR throws on the first read instead of degrading. Run the programonce as is and once with the variable removed for that single command, andreport both exit codes and what IsLicensed returned.
Move the license setup into a Startup.ApplyIronQrKey method called first in
Program.cs. If IRONQR_LICENSE_KEY is unset or License.IsLicensed is false,
exit with code 2 and one clear line on stderr before any image is opened,
because IronQR throws on the first read instead of degrading. Run the program
once as is and once with the variable removed for that single command, and
report both exit codes and what IsLicensed returned.
Text
The license keys page covers trial keys and the configuration files IronQR also reads. The exception type is internal to the IronQr assembly, so catching LicensingException by type fails to compile with error CS0122.
A sequential decoder comes next, and its results file becomes the reference for every faster mode.
Create Services/BacklogDecoder.cs with DecodeAsync(string path) returning anImageOutcome record: file, status (Decoded, Empty, Unreadable), values, QRtypes, exception type and message, and milliseconds. Open the file withAnyBitmap.FromFile, read with QrScanMode.Auto, and never let one file stopthe batch. Add a --sequential mode that runs it over Backlog/images/ one fileat a time, writes out/sequential.csv, and starts out/runs.csv with the modeand wall time. Report the status counts and the wall time from those files.
Create Services/BacklogDecoder.cs with DecodeAsync(string path) returning an
ImageOutcome record: file, status (Decoded, Empty, Unreadable), values, QR
types, exception type and message, and milliseconds. Open the file with
AnyBitmap.FromFile, read with QrScanMode.Auto, and never let one file stop
the batch. Add a --sequential mode that runs it over Backlog/images/ one file
at a time, writes out/sequential.csv, and starts out/runs.csv with the mode
and wall time. Report the status counts and the wall time from those files.
Text
How do you decode a folder in parallel with IronQR?
The async and multithreading guide calls a separate reader per iteration the safe default, because IronQR makes no explicit thread-safety guarantee for a shared instance. The next prompt has Codex's built-in explorer agent, a read-heavy agent for codebase exploration, check that against the documentation before anything goes parallel.
Ask the explorer agent to answer this without editing anything. Before thedecoder goes parallel, list every QrReader, QrImageInput, and AnyBitmap thatBacklogDecoder creates or keeps, say which could be shared across threads,and back each answer with a quote fromhttps://ironsoftware.com/qr/csharp/how-to/async-multithreading/ or theironqr skill. Flag anything the documentation leaves unstated.
Ask the explorer agent to answer this without editing anything. Before the
decoder goes parallel, list every QrReader, QrImageInput, and AnyBitmap that
BacklogDecoder creates or keeps, say which could be shared across threads,
and back each answer with a quote from
https://ironsoftware.com/qr/csharp/how-to/async-multithreading/ or the
ironqr skill. Flag anything the documentation leaves unstated.
Text
In calibration, sharing one reader across 32 threads produced the same results as a reader per file. A later release may differ, and a reader per file costs little.
The input object needs care too. In version 2026.10.1, a using block around it throws NotImplementedException on exit, even after a successful read, so a batch would record every good image as a failure. Review each diff for one.
For the parallel version, the prompt names the built-in worker agent, which runs the edit in its own thread.
Have the worker agent add a --parallel mode with Parallel.ForEach,MaxDegreeOfParallelism from a --threads option (defaultEnvironment.ProcessorCount), and a new QrReader per file. Use the synchronousRead inside the loop body, since Parallel.ForEach does not await an asynclambda. Write out/parallel.csv sorted by file name, then compare its statusand value columns with out/sequential.csv and fix any row that differsbefore you finish. Append mode, thread count, and wall time to out/runs.csv.
Have the worker agent add a --parallel mode with Parallel.ForEach,
MaxDegreeOfParallelism from a --threads option (default
Environment.ProcessorCount), and a new QrReader per file. Use the synchronous
Read inside the loop body, since Parallel.ForEach does not await an async
lambda. Write out/parallel.csv sorted by file name, then compare its status
and value columns with out/sequential.csv and fix any row that differs
before you finish. Append mode, thread count, and wall time to out/runs.csv.
Text
An async lambda passed to Parallel.ForEach compiles as async void, so the loop returns before the reads finish and the results file comes out short. The comparison with the sequential file catches it.
How do you limit concurrent reads with ReadAsync?
A parallel loop blocks a thread per read. ReadAsync behind a SemaphoreSlim keeps a fixed number of reads in flight and leaves the calling thread free, the pattern the async guide gives for high-volume batches. It takes an IQrInput and has no overload for a file path, so each task still opens its own bitmap.
Add a --pipeline mode: Task.WhenAll over every file, each task waiting on aSemaphoreSlim sized by --concurrency and awaiting ReadAsync. Run it atconcurrency 2, 8, and Environment.ProcessorCount, writingout/pipeline-c<N>.csv for each and appending to out/runs.csv. Confirm eachfile matches out/sequential.csv row for row, then tell me which setting wasfastest according to runs.csv.
Add a --pipeline mode: Task.WhenAll over every file, each task waiting on a
SemaphoreSlim sized by --concurrency and awaiting ReadAsync. Run it at
concurrency 2, 8, and Environment.ProcessorCount, writing
out/pipeline-c<N>.csv for each and appending to out/runs.csv. Confirm each
file matches out/sequential.csv row for row, then tell me which setting was
fastest according to runs.csv.
Text
The detection model already spreads one read across several cores, so a higher limit need not run faster. Let runs.csv pick the limit for your hardware, and keep the semaphore for what it does reliably, which is capping memory use.
Which scan mode should a batch use?
The detection model reads photos the basic pass misses, and it also accounts for most of the time each read takes.
Measure what the machine learning detector costs on this backlog. Run thepipeline at the fastest concurrency from runs.csv, first underQrScanMode.Auto and then under QrScanMode.OnlyBasicScan, writingout/mode-auto.csv and out/mode-basic.csv with per-image milliseconds. Joinboth with the category column of Backlog/manifest.csv and report decodedcount and median milliseconds per category for each mode. If either modereads files the other misses, keep Auto as the default and build a singleOnlyBasicScan retry of its Empty files into the decoder for every later run.Rerun to out/mode-fallback.csv and count the recoveries.
Measure what the machine learning detector costs on this backlog. Run the
pipeline at the fastest concurrency from runs.csv, first under
QrScanMode.Auto and then under QrScanMode.OnlyBasicScan, writing
out/mode-auto.csv and out/mode-basic.csv with per-image milliseconds. Join
both with the category column of Backlog/manifest.csv and report decoded
count and median milliseconds per category for each mode. If either mode
reads files the other misses, keep Auto as the default and build a single
OnlyBasicScan retry of its Empty files into the decoder for every later run.
Rerun to out/mode-fallback.csv and count the recoveries.
Text
Both modes read every clean scan and fax copy, and each missed a different handful of phone photos. On the photos only the basic pass read, the detection model located the code and could not decode it, so Auto never fell back. Retrying the empty files with QrScanMode.OnlyBasicScan recovered every photo.
How do you validate decoded values and report failures?
A batch meets three kinds of failure, and each one looks different in code.
Input
Result
Status
Empty, truncated, or non-image file
The image load throws before IronQR runs
Unreadable
No code, or damage past error correction
An empty collection, never null
Empty
Any code, read in the detection-only mode
Corner points, an empty Value, a QrType of QrEncoding.None
Empty, not Decoded
The third row is the dangerous one. Our first sequential decoder would have counted it as Decoded, because it only checked whether any result came back.
Backlog/README.md defines a valid asset tag. Add Services/TagValidator.cs andapply it after each read: a decode only counts when the symbol is a standardQR code (QrEncoding.QRCode), the value matches the documented pattern, andthe Luhn check digit holds. Give the others the statuses WrongSymbol,InvalidFormat, or BadCheckDigit. Rerun the pipeline to out/validated.csv andlist every file whose status disagrees with expected_status in the manifest.
Backlog/README.md defines a valid asset tag. Add Services/TagValidator.cs and
apply it after each read: a decode only counts when the symbol is a standard
QR code (QrEncoding.QRCode), the value matches the documented pattern, and
the Luhn check digit holds. Give the others the statuses WrongSymbol,
InvalidFormat, or BadCheckDigit. Rerun the pipeline to out/validated.csv and
list every file whose status disagrees with expected_status in the manifest.
Text
A check digit is part of your data, so IronQR cannot validate it. Reed-Solomon correction inside the read, which the checksum and fault tolerance guide describes, only proves the symbol decoded, and a result carries no confidence property. A tag with a wrong check digit scans cleanly and is still invalid.
IronQR's error messages guide shows the per-file logging pattern the next prompt applies to the whole backlog.
Every file that is not Decoded needs a recorded reason. Write one JSON lineper file to out/decode-log.jsonl (file, status, exception type, message,milliseconds), send IronQR's own log to out/ironqr-debug.log throughIronQr.Logging.Logger, and generate out/failures.md from the JSONL only: onesection per status other than Decoded, a count, and every file name. Thencheck the section counts against out/validated.csv.
Every file that is not Decoded needs a recorded reason. Write one JSON line
per file to out/decode-log.jsonl (file, status, exception type, message,
milliseconds), send IronQR's own log to out/ironqr-debug.log through
IronQr.Logging.Logger, and generate out/failures.md from the JSONL only: one
section per status other than Decoded, a count, and every file name. Then
check the section counts against out/validated.csv.
Text
In version 2026.10.1, the IronQr.Logging.Logger log records the scan path each read took, such as a detection scan with the machine learning model. Because the report is built only from the JSONL, a failure the decoder never logged shows up as a count that disagrees with the results file.
How do you diagnose an image that will not decode?
Codex can look at a picture. Attach the file with codex --image when you start the session, or paste it into the composer.
I attached IMG_0018.png from Backlog/images/ with codex --image. The pipelinemarks it Empty. Look at the picture and tell me whether it is a damaged code,a page with no code, or a capture problem a rescan would fix. Then test youranswer: read it with all three QrScanMode values and writeout/retry-IMG_0018.csv. Do not change the pipeline.
I attached IMG_0018.png from Backlog/images/ with codex --image. The pipeline
marks it Empty. Look at the picture and tell me whether it is a damaged code,
a page with no code, or a capture problem a rescan would fix. Then test your
answer: read it with all three QrScanMode values and write
out/retry-IMG_0018.csv. Do not change the pipeline.
Text
In our run, all three modes came back empty for this file, and in calibration the detection model found nothing to locate. The code itself is damaged, so the loss is past what error correction recovers, and neither another read nor a rescan of the same label will change that. The fix is a replacement label and, if you print the labels yourself, a higher error correction level.
How do you debug incorrect batch output end to end?
Send the explorer agent into Jobs/NightlyImport.cs and Logs/nightly-2026-10-06/before anything is edited. That run reported six unreadable files and no otherfailure, yet Backlog/archive-index-gaps.csv lists assets the archive neverreceived. Find what the missing assets have in common, explain why the jobcounted them as successes, and propose the smallest fix together with thecommand that would prove it. Put the findings in out/nightly-diagnosis.md andchange no code.
Send the explorer agent into Jobs/NightlyImport.cs and Logs/nightly-2026-10-06/
before anything is edited. That run reported six unreadable files and no other
failure, yet Backlog/archive-index-gaps.csv lists assets the archive never
received. Find what the missing assets have in common, explain why the job
counted them as successes, and propose the smallest fix together with the
command that would prove it. Put the findings in out/nightly-diagnosis.md and
change no code.
Text
Approve the diagnosis before any fix. A good one quotes the line that decides success. Check the proposed fix against out/mode-fallback.csv too.
How do you turn one-off code into reusable, tested components?
Put the worker agent on folding the modes into a BacklogJob class. Add theMicrosoft.Extensions.DependencyInjection package, register the job with it,and configure it with a BacklogOptions record (input folder, concurrency,scan mode, output folder). Keep the validator, the basic-scan retry, and thelogging in it, stay under 160 lines, and add XML doc comments. Beforefinishing, run it over Backlog/images/ into out/final.csv and confirm everyrow agrees with the manifest's expected_status and expected_value.
Put the worker agent on folding the modes into a BacklogJob class. Add the
Microsoft.Extensions.DependencyInjection package, register the job with it,
and configure it with a BacklogOptions record (input folder, concurrency,
scan mode, output folder). Keep the validator, the basic-scan retry, and the
logging in it, stay under 160 lines, and add XML doc comments. Before
finishing, run it over Backlog/images/ into out/final.csv and confirm every
row agrees with the manifest's expected_status and expected_value.
Text
A refactor can drop the validator, the basic-scan retry, or the per-file reader without a build error. Only a full rerun against ground truth catches that.
Add an xUnit project, AssetBacklog.Tests, and cover BacklogJob with teststhat read real files from Backlog/images/: a clean scan decodes to itsmanifest value, the zero-byte file comes back Unreadable without throwing, adamaged image is Empty, a Micro QR label is WrongSymbol, a bad check digit isBadCheckDigit, and a parallel run over 40 files matches a sequential one.Assert on status and value. Run dotnet test and report the totals it prints.
Add an xUnit project, AssetBacklog.Tests, and cover BacklogJob with tests
that read real files from Backlog/images/: a clean scan decodes to its
manifest value, the zero-byte file comes back Unreadable without throwing, a
damaged image is Empty, a Micro QR label is WrongSymbol, a bad check digit is
BadCheckDigit, and a parallel run over 40 files matches a sequential one.
Assert on status and value. Run dotnet test and report the totals it prints.
Text
The manifest fixes status and value, while timings change from run to run.
Keep recurring IronQR questions in the repository skill. OpenAI's custom prompts page marks the older ~/.codex/prompts folder deprecated in favor of skills.
$ironqr Using the IronQR API reference, list every member of QrResult and saywhether IronQR reports a confidence or checksum score for a read. Then pointto the line in BacklogJob that decides a file is Decoded.
$ironqr Using the IronQR API reference, list every member of QrResult and say
whether IronQR reports a confidence or checksum score for a read. Then point
to the line in BacklogJob that decides a file is Decoded.
Text
In our run, Codex listed the four members of a result and traced Decoded from the job through the decoder to the validator line that returns it.
How do you reuse these prompts on another image folder?
Change the paths, and every prompt above runs as written. The AI coding assistants guide runs a single acceptance prompt end to end and publishes the result.
What did the prompts produce in a real run?
We sent every prompt above to Codex in order, in one thread, against a sample repository. It holds a console application and a backlog of 301 images (clean scans, phone photos, fax copies, damaged labels, blank cover sheets, Micro QR shelf labels, invalid tags, and six files that are not images at all), with a manifest of the expected status and value of each. After the session we rebuilt the project in Release and ran the tests. We also decoded every image independently to check the results below.
The basic-scan retry raised Decoded from 256 to 264. Before it, each mode decoded 256 of 301, and 16 files differed between the two.
A clean scan took a median 360 ms under Auto and 35 ms under the basic scan, which ran the whole backlog in 7 s at limit 2.
The sequential decoder recorded 256 Decoded, 39 Empty and 6 Unreadable in 71 s.
Parallel mode wrote the same 301 rows in 60 s at 32 threads, and the pipeline wrote them in 55, 66 and 68 s at limits 2, 8 and 32.
In the quickstart, 9 of 12 rows decoded, 2 came back empty and 1 zero-byte file threw.
Validation matched the manifest with 240 Decoded, 31 Empty, 8 WrongSymbol, 8 InvalidFormat, 8 BadCheckDigit and 6 Unreadable.
The failure report held 61 records, one per failed file.
After the refactor, the 64-line job matched the manifest on 301 of 301 rows, and 6 of 6 tests passed, again in Release.
All 16 prompts ran with every build passing and no manual code edits. Prompts 13 to 16 hit the free-plan usage limit and resumed in the same thread after Codex was signed in again.
Before the investigation prompt we added a nightly job with a planted fault, its logs and a gaps list. Codex found the detection-only mode on photo JPEGs and a success check that accepts any result.
Codex's version, model, timing and test machine for this run are in the shared run record.
Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.