feat: assemble autofocus study evidence

This commit is contained in:
fy59 2026-09-03 14:46:15 +02:00
parent 1a96bcce1f
commit 6a52ef4957
9 changed files with 459 additions and 3 deletions

View file

@ -188,3 +188,38 @@ calibration evidence.
The bridge performs no DB access, metadata interpretation, physical AF decision,
thresholding, interpolation or extrapolation.
## Materialized study assembly v1
**Status: PASS / FROZEN.**
`CALIBRATION_AF_STUDY_ASSEMBLY_V1` composes the frozen Workflow bridge and
`L3DAFST1` producer so a physical AF study does not need caller-written sample
arrays.
The caller supplies:
```text
study_context_sha256
2..64 entries {
materialized Calibration Workflow evidence
exact opaque focus token
FIT or HOLDOUT role
}
```
The assembly:
1. converts every entry through the frozen Workflow bridge;
2. requires one exact oriented width/height for the whole study;
3. rejects a repeated `calibration_evidence_sha256` even when the caller changes
focus token or FIT/HOLDOUT role;
4. passes the resulting bounded samples to the frozen AF-study producer;
5. returns deterministic `L3DAFST1`, artifact SHA-256 and summary.
`study_context_sha256` remains caller-retained and explicit. This layer does not
derive body/lens/focal/non-focus state from solver metadata and does not decide
whether different focus observations belong to one physically valid domain.
No Project DB access, solver execution, metadata inference, thresholding,
interpolation or extrapolation is introduced.

View file

@ -952,5 +952,6 @@ numbered `prompt/` execution contract. The current dependency is physical autofo
applicability validation for real equipment, followed by a dedicated calibrated real campaign.
Generic autofocus applicability machinery is available. AF-study evidence can now be produced from
already-materialized Calibration Workflow results without a second solver parser, while focus tokens
remain explicit study annotations. The real A6000 + E PZ 16-50 autofocus applicability remains
blocked until physical evidence validates it.
remain explicit study annotations. Multiple independent materialized samples can now be assembled into
one deterministic L3DAFST1 artifact without caller-written sample arrays. The real
A6000 + E PZ 16-50 autofocus applicability remains blocked until physical evidence validates it.

View file

@ -0,0 +1,61 @@
#ifndef LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_H
#define LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_H
#include <lardon3d/calibration_af_study.h>
#include <lardon3d/calibration_af_study_workflow.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_OK = 0,
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT,
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE,
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CAPACITY,
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CRYPTO_ERROR,
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_ENCODING_ERROR,
} Lardon3DCalibrationAfStudyAssemblyResult;
typedef struct {
const Lardon3DCalibrationWorkflowExternalEvidence *external;
Lardon3DCalibrationAfStudySampleRole role;
const char *focus_token;
} Lardon3DCalibrationAfStudyAssemblyEntry;
typedef struct {
/* Exact caller-retained identity of the common body/lens/focal/non-focus
* geometric study context. The assembly does not derive or reinterpret it. */
unsigned char study_context_sha256[
LARDON3D_CALIBRATION_AF_STUDY_SHA256_SIZE];
const Lardon3DCalibrationAfStudyAssemblyEntry *entries;
size_t entry_count;
} Lardon3DCalibrationAfStudyAssemblyInput;
/* Assemble 2..MAX_SAMPLES already-materialized Calibration Workflow results
* into one deterministic L3DAFST1 artifact.
*
* Each entry is converted through the frozen Workflow bridge; no solver file
* is parsed here. All samples must share exact oriented dimensions because one
* L3DAFST1 study has one image geometry. The common optical/non-focus identity
* remains the explicit study_context_sha256 supplied by the caller.
*
* Duplicate calibration_evidence_sha256 values are rejected regardless of
* focus token or FIT/HOLDOUT role. A single physical calibration result cannot
* therefore be relabelled to masquerade as independent AF evidence.
*
* This boundary performs no Project DB access, no metadata interpretation, no
* physical applicability decision and no acceptance thresholding. */
Lardon3DCalibrationAfStudyAssemblyResult
lardon3d_calibration_af_study_assemble_materialized(
const Lardon3DCalibrationAfStudyAssemblyInput *input,
unsigned char *artifact, size_t artifact_capacity, size_t *written,
unsigned char artifact_sha256[
LARDON3D_CALIBRATION_AF_STUDY_SHA256_SIZE],
Lardon3DCalibrationAfStudySummary *summary);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -185,6 +185,7 @@ lardon3d_app = executable(
'src/calibration_tooling_v2.c',
'src/calibration_af_study.c',
'src/calibration_af_study_workflow.c',
'src/calibration_af_study_assembly.c',
'src/calibration_workflow.cpp',
'src/calibration_workflow_materialize.cpp',
'src/calibration_workflow_bind.cpp',
@ -947,6 +948,24 @@ test(
timeout: 30,
)
calibration_af_study_assembly_test = executable(
'test-calibration-af-study-assembly',
sources: [
'tests/test_calibration_af_study_assembly.c',
'src/calibration_af_study_assembly.c',
'src/calibration_af_study_workflow.c',
'src/calibration_af_study.c',
],
include_directories: include_directories('include'),
dependencies: [openssl, cc.find_library('m')],
)
test(
'calibration-af-study-assembly',
calibration_af_study_assembly_test,
timeout: 30,
)
calibration_workflow_v2_test = executable(
'test-calibration-workflow-v2',
sources: [

View file

@ -43,6 +43,7 @@ Adaptive capture settings semantics PASS/FROZEN
Autofocus v2 foundation PASS/FROZEN
Calibration AF study evidence v1 PASS/FROZEN
Calibration AF study Workflow bridge v1 PASS/FROZEN
Calibration AF study assembly v1 PASS/FROZEN
Calibration Tooling planarity alignment PASS/FROZEN
```

View file

@ -25,6 +25,7 @@ ADAPTIVE_CAPTURE_SETTINGS_CONTRACT=PASS/FROZEN
AUTOFOCUS_V2_FOUNDATION=PASS/FROZEN
CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN
CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1=PASS/FROZEN
CALIBRATION_AF_STUDY_ASSEMBLY_V1=PASS/FROZEN
CURRENT_CALIBRATION_NEXT=PHYSICAL_AUTOFOCUS_OPTICAL_APPLICABILITY_VALIDATION
```

View file

@ -26,7 +26,7 @@ Default dependency order:
4. Heterogeneous calibration publication / Tooling / Bootstrap evolution — PASS/FROZEN;
5. Heterogeneous Workflow v2 truthful READY proof — PASS/FROZEN;
6. Adaptive capture settings / generic autofocus foundation — PASS/FROZEN;
7. physical autofocus/optical applicability validation and dedicated calibrated real campaign — CURRENT; `CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN` supplies deterministic measurement evidence and `CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1=PASS/FROZEN` converts already-materialized Calibration Workflow evidence into AF-study samples without reparsing solver artifacts; neither makes a physical applicability decision;
7. physical autofocus/optical applicability validation and dedicated calibrated real campaign — CURRENT; `CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN` supplies deterministic measurement evidence, `CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1=PASS/FROZEN` converts already-materialized Calibration Workflow evidence into AF-study samples without reparsing solver artifacts, and `CALIBRATION_AF_STUDY_ASSEMBLY_V1=PASS/FROZEN` assembles multiple independent materialized samples into one bounded L3DAFST1 artifact; none makes a physical applicability decision;
8. real Sparse SfM proof;
9. durable Dense/OpenMVS orchestration;
10. mesh / refinement / texturing / export;

View file

@ -0,0 +1,112 @@
#include <lardon3d/calibration_af_study_assembly.h>
#include <stdbool.h>
#include <string.h>
static bool nonzero_digest(const unsigned char value[32]) {
unsigned char any = 0;
for (size_t index = 0; index < 32; ++index) any |= value[index];
return any != 0;
}
static Lardon3DCalibrationAfStudyAssemblyResult map_bridge_result(
Lardon3DCalibrationAfStudyWorkflowResult result) {
switch (result) {
case LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT:
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT;
case LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE:
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
case LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_CRYPTO_ERROR:
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CRYPTO_ERROR;
case LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK:
break;
}
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
}
static Lardon3DCalibrationAfStudyAssemblyResult map_study_result(
Lardon3DCalibrationAfStudyResult result) {
switch (result) {
case LARDON3D_CALIBRATION_AF_STUDY_INVALID_ARGUMENT:
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT;
case LARDON3D_CALIBRATION_AF_STUDY_CAPACITY:
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CAPACITY;
case LARDON3D_CALIBRATION_AF_STUDY_INVALID_EVIDENCE:
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
case LARDON3D_CALIBRATION_AF_STUDY_ENCODING_ERROR:
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_ENCODING_ERROR;
case LARDON3D_CALIBRATION_AF_STUDY_OK:
break;
}
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
}
Lardon3DCalibrationAfStudyAssemblyResult
lardon3d_calibration_af_study_assemble_materialized(
const Lardon3DCalibrationAfStudyAssemblyInput *input,
unsigned char *artifact, size_t artifact_capacity, size_t *written,
unsigned char artifact_sha256[32],
Lardon3DCalibrationAfStudySummary *summary) {
if (written) *written = 0;
if (artifact_sha256) memset(artifact_sha256, 0, 32);
if (summary) memset(summary, 0, sizeof(*summary));
if (!input || !artifact || !written || !artifact_sha256 || !summary ||
!input->entries || input->entry_count < 2 ||
input->entry_count > LARDON3D_CALIBRATION_AF_STUDY_MAX_SAMPLES ||
!nonzero_digest(input->study_context_sha256))
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT;
Lardon3DCalibrationAfStudySample
samples[LARDON3D_CALIBRATION_AF_STUDY_MAX_SAMPLES];
memset(samples, 0, sizeof(samples));
uint32_t width = 0;
uint32_t height = 0;
for (size_t index = 0; index < input->entry_count; ++index) {
const Lardon3DCalibrationAfStudyAssemblyEntry *entry =
&input->entries[index];
if (!entry->external || !entry->focus_token)
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT;
Lardon3DCalibrationAfStudyWorkflowResult bridge =
lardon3d_calibration_af_study_sample_from_materialized_evidence(
entry->external, entry->role, entry->focus_token, &samples[index]);
if (bridge != LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK)
return map_bridge_result(bridge);
if (index == 0) {
width = entry->external->oriented_width;
height = entry->external->oriented_height;
} else if (entry->external->oriented_width != width ||
entry->external->oriented_height != height) {
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
}
/* Stronger than the raw L3DAFST1 producer's (token,digest) duplicate rule:
* an already-materialized calibration result is independent evidence only
* once, regardless of how the caller labels focus or study role. */
for (size_t previous = 0; previous < index; ++previous) {
if (memcmp(samples[previous].calibration_evidence_sha256,
samples[index].calibration_evidence_sha256, 32) == 0)
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
}
}
Lardon3DCalibrationAfStudyInput study = {0};
memcpy(study.study_context_sha256, input->study_context_sha256, 32);
study.width = width;
study.height = height;
study.samples = samples;
study.sample_count = input->entry_count;
Lardon3DCalibrationAfStudyResult result =
lardon3d_calibration_af_study_produce(
&study, artifact, artifact_capacity, written, artifact_sha256,
summary);
if (result != LARDON3D_CALIBRATION_AF_STUDY_OK)
return map_study_result(result);
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_OK;
}

View file

@ -0,0 +1,226 @@
#include <lardon3d/calibration_af_study_assembly.h>
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#define CHECK(expression) \
do { \
if (!(expression)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #expression); \
return 1; \
} \
} while (0)
static void fill_digest(unsigned char output[32], unsigned char value) {
memset(output, value, 32);
}
static int validation_sha(
const Lardon3DCalibrationWorkflowInputBoundary *boundary,
unsigned char output[32]) {
static const char domain[] = "L3DCAL_WORKFLOW_VALIDATION_V1\n";
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
if (!ctx) return 0;
int ok = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) == 1 &&
EVP_DigestUpdate(ctx, domain, sizeof(domain) - 1) == 1 &&
EVP_DigestUpdate(ctx, boundary->detection_sha256, 32) == 1 &&
EVP_DigestUpdate(ctx, boundary->solve_sha256, 32) == 1 &&
EVP_DigestUpdate(ctx, boundary->evidence_sha256, 32) == 1 &&
EVP_DigestUpdate(ctx, boundary->producer_sha256, 32) == 1;
unsigned int length = 0;
ok = ok && EVP_DigestFinal_ex(ctx, output, &length) == 1 && length == 32;
EVP_MD_CTX_free(ctx);
return ok;
}
static Lardon3DCalibrationWorkflowExternalEvidence fixture(
unsigned char identity, double focal_offset) {
Lardon3DCalibrationWorkflowExternalEvidence value = {0};
fill_digest(value.boundary.session_sha256, identity);
fill_digest(value.boundary.detection_sha256, (unsigned char)(identity + 1));
fill_digest(value.boundary.solve_sha256, (unsigned char)(identity + 2));
fill_digest(value.boundary.evidence_sha256, (unsigned char)(identity + 3));
fill_digest(value.boundary.producer_sha256, 0x55);
fill_digest(value.boundary.campaign_state_sha256, 0x66);
fill_digest(value.boundary.optical_state_sha256,
(unsigned char)(identity + 4));
fill_digest(value.boundary.solver_executable_sha256, 0x88);
fill_digest(value.boundary.solver_configuration_sha256, 0x99);
fill_digest(value.target_sha256, 0xA1);
memcpy(value.optical_state_sha256, value.boundary.optical_state_sha256, 32);
memcpy(value.solver_executable_sha256,
value.boundary.solver_executable_sha256, 32);
memcpy(value.solver_configuration_sha256,
value.boundary.solver_configuration_sha256, 32);
memcpy(value.initialization_evidence_sha256,
value.boundary.session_sha256, 32);
(void)validation_sha(&value.boundary, value.validation_evidence_sha256);
value.oriented_width = 6000;
value.oriented_height = 4000;
const double params[8] = {
4000.0 + focal_offset, 4002.0 + focal_offset,
3000.0, 2000.0, -0.1, 0.01, 0.001, -0.001,
};
for (size_t run = 0; run < 3; ++run)
memcpy(value.repeated_parameters[run], params, sizeof(params));
return value;
}
static Lardon3DCalibrationAfStudyAssemblyInput make_input(
Lardon3DCalibrationAfStudyAssemblyEntry *entries, size_t count) {
Lardon3DCalibrationAfStudyAssemblyInput input = {0};
fill_digest(input.study_context_sha256, 0xD1);
input.entries = entries;
input.entry_count = count;
return input;
}
static int test_happy_path_and_order_independence(void) {
Lardon3DCalibrationWorkflowExternalEvidence first = fixture(0x11, 0.0);
Lardon3DCalibrationWorkflowExternalEvidence second = fixture(0x21, 8.0);
Lardon3DCalibrationWorkflowExternalEvidence third = fixture(0x31, 14.0);
Lardon3DCalibrationAfStudyAssemblyEntry entries[3] = {
{&first, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:137"},
{&second, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:151"},
{&third, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT, "focus:165"},
};
Lardon3DCalibrationAfStudyAssemblyInput input = make_input(entries, 3);
unsigned char artifact_a[LARDON3D_CALIBRATION_AF_STUDY_MAX_ARTIFACT_BYTES];
unsigned char artifact_b[LARDON3D_CALIBRATION_AF_STUDY_MAX_ARTIFACT_BYTES];
unsigned char sha_a[32];
unsigned char sha_b[32];
size_t written_a = 0;
size_t written_b = 0;
Lardon3DCalibrationAfStudySummary summary_a;
Lardon3DCalibrationAfStudySummary summary_b;
CHECK(lardon3d_calibration_af_study_assemble_materialized(
&input, artifact_a, sizeof(artifact_a), &written_a, sha_a,
&summary_a) == LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_OK);
CHECK(written_a > 0);
CHECK(memcmp(artifact_a, "L3DAFST1", 8) == 0);
CHECK(summary_a.sample_count == 3);
CHECK(summary_a.fit_count == 2);
CHECK(summary_a.holdout_count == 1);
CHECK(summary_a.pair_count == 3);
Lardon3DCalibrationAfStudyAssemblyEntry reordered[3] = {
entries[2], entries[0], entries[1],
};
input.entries = reordered;
CHECK(lardon3d_calibration_af_study_assemble_materialized(
&input, artifact_b, sizeof(artifact_b), &written_b, sha_b,
&summary_b) == LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_OK);
CHECK(written_a == written_b);
CHECK(memcmp(artifact_a, artifact_b, written_a) == 0);
CHECK(memcmp(sha_a, sha_b, 32) == 0);
CHECK(memcmp(&summary_a, &summary_b, sizeof(summary_a)) == 0);
return 0;
}
static int test_relabelled_duplicate_calibration_rejected(void) {
Lardon3DCalibrationWorkflowExternalEvidence same = fixture(0x11, 0.0);
Lardon3DCalibrationAfStudyAssemblyEntry entries[2] = {
{&same, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:137"},
{&same, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT, "focus:165"},
};
Lardon3DCalibrationAfStudyAssemblyInput input = make_input(entries, 2);
unsigned char artifact[LARDON3D_CALIBRATION_AF_STUDY_MAX_ARTIFACT_BYTES];
unsigned char sha[32];
size_t written = 999;
Lardon3DCalibrationAfStudySummary summary;
memset(sha, 0xA5, sizeof(sha));
memset(&summary, 0xA5, sizeof(summary));
CHECK(lardon3d_calibration_af_study_assemble_materialized(
&input, artifact, sizeof(artifact), &written, sha, &summary) ==
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE);
CHECK(written == 0);
unsigned char zero_sha[32] = {0};
CHECK(memcmp(sha, zero_sha, 32) == 0);
Lardon3DCalibrationAfStudySummary zero_summary = {0};
CHECK(memcmp(&summary, &zero_summary, sizeof(summary)) == 0);
return 0;
}
static int test_dimension_mismatch_rejected(void) {
Lardon3DCalibrationWorkflowExternalEvidence first = fixture(0x11, 0.0);
Lardon3DCalibrationWorkflowExternalEvidence second = fixture(0x21, 8.0);
second.oriented_width = 5999;
Lardon3DCalibrationAfStudyAssemblyEntry entries[2] = {
{&first, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:137"},
{&second, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT, "focus:165"},
};
Lardon3DCalibrationAfStudyAssemblyInput input = make_input(entries, 2);
unsigned char artifact[LARDON3D_CALIBRATION_AF_STUDY_MAX_ARTIFACT_BYTES];
unsigned char sha[32];
size_t written = 0;
Lardon3DCalibrationAfStudySummary summary;
CHECK(lardon3d_calibration_af_study_assemble_materialized(
&input, artifact, sizeof(artifact), &written, sha, &summary) ==
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE);
return 0;
}
static int test_invalid_bridge_evidence_rejected(void) {
Lardon3DCalibrationWorkflowExternalEvidence first = fixture(0x11, 0.0);
Lardon3DCalibrationWorkflowExternalEvidence second = fixture(0x21, 8.0);
second.validation_evidence_sha256[0] ^= 1;
Lardon3DCalibrationAfStudyAssemblyEntry entries[2] = {
{&first, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:137"},
{&second, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT, "focus:165"},
};
Lardon3DCalibrationAfStudyAssemblyInput input = make_input(entries, 2);
unsigned char artifact[LARDON3D_CALIBRATION_AF_STUDY_MAX_ARTIFACT_BYTES];
unsigned char sha[32];
size_t written = 0;
Lardon3DCalibrationAfStudySummary summary;
CHECK(lardon3d_calibration_af_study_assemble_materialized(
&input, artifact, sizeof(artifact), &written, sha, &summary) ==
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE);
return 0;
}
static int test_invalid_arguments_and_capacity(void) {
Lardon3DCalibrationWorkflowExternalEvidence first = fixture(0x11, 0.0);
Lardon3DCalibrationWorkflowExternalEvidence second = fixture(0x21, 8.0);
Lardon3DCalibrationAfStudyAssemblyEntry entries[2] = {
{&first, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:137"},
{&second, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT, "focus:165"},
};
Lardon3DCalibrationAfStudyAssemblyInput input = make_input(entries, 1);
unsigned char artifact[LARDON3D_CALIBRATION_AF_STUDY_MAX_ARTIFACT_BYTES];
unsigned char sha[32];
size_t written = 0;
Lardon3DCalibrationAfStudySummary summary;
CHECK(lardon3d_calibration_af_study_assemble_materialized(
&input, artifact, sizeof(artifact), &written, sha, &summary) ==
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT);
input.entry_count = 2;
CHECK(lardon3d_calibration_af_study_assemble_materialized(
&input, artifact, 8, &written, sha, &summary) ==
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CAPACITY);
return 0;
}
int main(void) {
CHECK(test_happy_path_and_order_independence() == 0);
CHECK(test_relabelled_duplicate_calibration_rejected() == 0);
CHECK(test_dimension_mismatch_rejected() == 0);
CHECK(test_invalid_bridge_evidence_rejected() == 0);
CHECK(test_invalid_arguments_and_capacity() == 0);
puts("CALIBRATION_AF_STUDY_ASSEMBLY_V1=PASS");
return 0;
}