Improve federation API docs and errors
This commit is contained in:
parent
5cc56b2ce8
commit
d8a6603ddc
14
AUDITS.md
14
AUDITS.md
|
|
@ -73,6 +73,8 @@ Status legend: ✅ implemented, 🟡 planned/in-progress, ⬜ not started.
|
|||
| `ASL/INDEXES/1` | 🟡 | Taxonomy planned. |
|
||||
| `ASL/TGK-EXEC-PLAN/1` | 🟡 | Encoding implemented; executor out of scope. |
|
||||
| `ENC/ASL-TGK-EXEC-PLAN/1` | ✅ | Plan encoding implemented. |
|
||||
| `ASL/FEDERATION/1` | ✅ | Core federation primitives implemented. |
|
||||
| `ASL/FEDERATION-REPLAY/1` | ✅ | Deterministic replay and view construction implemented. |
|
||||
| `ASL/SYSTEM/1` | 🟡 | Cross-cutting view planned. |
|
||||
| `TGK/1` | 🟡 | Semantic layer planned. |
|
||||
|
||||
|
|
@ -348,3 +350,15 @@ Status legend: ✅ completed, ⬜ pending.
|
|||
embedded commit-message appendix; tightened wording throughout; bumped the
|
||||
document version/date.
|
||||
- Tests: N/A (documentation-only change).
|
||||
|
||||
## 2026-02-XX — Federation core (`tier1/asl-federation-1.md`, `tier1/asl-federation-replay-1.md`)
|
||||
- Scope: core federation registry, ingest validation, deterministic replay, view
|
||||
construction, and resolve semantics.
|
||||
- Findings: missing record typing and identity coverage for PER/TGK/tombstones;
|
||||
policy gating needed explicit per-domain + optional per-record handling; view
|
||||
build and resolve error reporting needed explicit codes and tests.
|
||||
- Resolution: added federation registry storage, ingest validation, replay/view
|
||||
build, resolve APIs, and tests for ordering, tombstone scoping, conflicts,
|
||||
bounds, and metadata preservation; documented middle-layer boundary and
|
||||
ref-only remote fetch guidance.
|
||||
- Tests: user reported “100% tests passed, 0 tests failed out of 29”.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ typedef struct {
|
|||
uint8_t policy_ok;
|
||||
uint8_t reserved[6];
|
||||
amduat_hash_id_t policy_hash_id;
|
||||
amduat_octets_t policy_hash; /* Empty when unknown. */
|
||||
amduat_octets_t policy_hash; /* Empty when unknown; caller-owned bytes. */
|
||||
} amduat_fed_domain_state_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -44,9 +44,11 @@ const amduat_fed_domain_state_t *amduat_fed_registry_value_lookup(
|
|||
|
||||
void amduat_fed_registry_value_free(amduat_fed_registry_value_t *value);
|
||||
|
||||
/* Encode/Decode allocate buffers; caller frees via amduat_octets_free. */
|
||||
bool amduat_fed_registry_encode(const amduat_fed_registry_value_t *value,
|
||||
amduat_octets_t *out_bytes);
|
||||
|
||||
/* Decode allocates policy_hash bytes; free via amduat_fed_registry_value_free. */
|
||||
bool amduat_fed_registry_decode(amduat_octets_t bytes,
|
||||
amduat_fed_registry_value_t *out_value);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
amduat_fed_record_type_t type;
|
||||
amduat_reference_t ref;
|
||||
amduat_reference_t ref; /* PER/TGK identities are ASL references. */
|
||||
} amduat_fed_record_id_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -44,6 +44,7 @@ typedef struct {
|
|||
size_t len;
|
||||
} amduat_fed_replay_view_t;
|
||||
|
||||
/* Caller frees record ids with amduat_fed_replay_view_free. */
|
||||
bool amduat_fed_record_validate(const amduat_fed_record_t *record);
|
||||
|
||||
bool amduat_fed_replay_domain(const amduat_fed_record_t *records,
|
||||
|
|
|
|||
|
|
@ -39,14 +39,23 @@ typedef struct {
|
|||
size_t denies_len;
|
||||
} amduat_fed_view_t;
|
||||
|
||||
bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
||||
size_t count,
|
||||
uint32_t local_domain_id,
|
||||
const amduat_fed_view_bounds_t *bounds,
|
||||
size_t bounds_len,
|
||||
const amduat_fed_policy_deny_t *denies,
|
||||
size_t denies_len,
|
||||
amduat_fed_view_t *out_view);
|
||||
typedef enum {
|
||||
AMDUAT_FED_VIEW_OK = 0,
|
||||
AMDUAT_FED_VIEW_ERR_INVALID = 1,
|
||||
AMDUAT_FED_VIEW_ERR_CONFLICT = 2,
|
||||
AMDUAT_FED_VIEW_ERR_OOM = 3
|
||||
} amduat_fed_view_error_t;
|
||||
|
||||
/* Caller frees records/denies with amduat_fed_view_free. */
|
||||
amduat_fed_view_error_t amduat_fed_view_build(
|
||||
const amduat_fed_record_t *records,
|
||||
size_t count,
|
||||
uint32_t local_domain_id,
|
||||
const amduat_fed_view_bounds_t *bounds,
|
||||
size_t bounds_len,
|
||||
const amduat_fed_policy_deny_t *denies,
|
||||
size_t denies_len,
|
||||
amduat_fed_view_t *out_view);
|
||||
|
||||
void amduat_fed_view_free(amduat_fed_view_t *view);
|
||||
|
||||
|
|
|
|||
|
|
@ -75,22 +75,23 @@ static void amduat_fed_policy_deny_free(amduat_fed_policy_deny_t *deny) {
|
|||
memset(deny, 0, sizeof(*deny));
|
||||
}
|
||||
|
||||
static bool amduat_fed_view_append(amduat_fed_view_t *view,
|
||||
const amduat_fed_record_t *records,
|
||||
size_t count) {
|
||||
static amduat_fed_view_error_t amduat_fed_view_append(
|
||||
amduat_fed_view_t *view,
|
||||
const amduat_fed_record_t *records,
|
||||
size_t count) {
|
||||
size_t i;
|
||||
size_t base_len;
|
||||
|
||||
if (count == 0u) {
|
||||
return true;
|
||||
return AMDUAT_FED_VIEW_OK;
|
||||
}
|
||||
if (view->len > SIZE_MAX - count) {
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||
}
|
||||
if (view->records == NULL) {
|
||||
view->records = (amduat_fed_record_t *)calloc(count, sizeof(*view->records));
|
||||
if (view->records == NULL) {
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||
}
|
||||
} else {
|
||||
amduat_fed_record_t *next =
|
||||
|
|
@ -98,7 +99,7 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view,
|
|||
(view->len + count) *
|
||||
sizeof(*view->records));
|
||||
if (next == NULL) {
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||
}
|
||||
view->records = next;
|
||||
}
|
||||
|
|
@ -115,7 +116,7 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view,
|
|||
amduat_fed_record_free(&view->records[j]);
|
||||
}
|
||||
view->len = base_len;
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_CONFLICT;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -127,26 +128,27 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view,
|
|||
amduat_fed_record_free(&view->records[j]);
|
||||
}
|
||||
view->len = base_len;
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||
}
|
||||
view->len++;
|
||||
}
|
||||
return true;
|
||||
return AMDUAT_FED_VIEW_OK;
|
||||
}
|
||||
|
||||
bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
||||
size_t count,
|
||||
uint32_t local_domain_id,
|
||||
const amduat_fed_view_bounds_t *bounds,
|
||||
size_t bounds_len,
|
||||
const amduat_fed_policy_deny_t *denies,
|
||||
size_t denies_len,
|
||||
amduat_fed_view_t *out_view) {
|
||||
amduat_fed_view_error_t amduat_fed_view_build(
|
||||
const amduat_fed_record_t *records,
|
||||
size_t count,
|
||||
uint32_t local_domain_id,
|
||||
const amduat_fed_view_bounds_t *bounds,
|
||||
size_t bounds_len,
|
||||
const amduat_fed_policy_deny_t *denies,
|
||||
size_t denies_len,
|
||||
amduat_fed_view_t *out_view) {
|
||||
size_t i;
|
||||
amduat_fed_view_t view;
|
||||
|
||||
if (out_view == NULL) {
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_INVALID;
|
||||
}
|
||||
out_view->records = NULL;
|
||||
out_view->len = 0;
|
||||
|
|
@ -155,7 +157,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
|||
out_view->denies_len = 0;
|
||||
|
||||
if (records == NULL && count != 0u) {
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_INVALID;
|
||||
}
|
||||
|
||||
view.records = NULL;
|
||||
|
|
@ -165,13 +167,13 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
|||
view.denies_len = 0;
|
||||
|
||||
if (denies_len != 0u && denies == NULL) {
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_INVALID;
|
||||
}
|
||||
if (denies_len != 0u) {
|
||||
view.denies = (amduat_fed_policy_deny_t *)calloc(denies_len,
|
||||
sizeof(*view.denies));
|
||||
if (view.denies == NULL) {
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||
}
|
||||
for (i = 0; i < denies_len; ++i) {
|
||||
if (!amduat_fed_policy_deny_clone(&denies[i], &view.denies[i])) {
|
||||
|
|
@ -180,7 +182,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
|||
amduat_fed_policy_deny_free(&view.denies[j]);
|
||||
}
|
||||
free(view.denies);
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||
}
|
||||
}
|
||||
view.denies_len = denies_len;
|
||||
|
|
@ -214,7 +216,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
|||
(amduat_fed_record_t *)calloc(domain_count, sizeof(*domain_records));
|
||||
if (domain_records == NULL) {
|
||||
amduat_fed_view_free(&view);
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||
}
|
||||
|
||||
domain_count = 0;
|
||||
|
|
@ -234,7 +236,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
|||
}
|
||||
free(domain_records);
|
||||
amduat_fed_view_free(&view);
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||
}
|
||||
domain_count++;
|
||||
}
|
||||
|
|
@ -251,20 +253,25 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
|||
free(domain_records);
|
||||
if (!ok) {
|
||||
amduat_fed_view_free(&view);
|
||||
return false;
|
||||
return AMDUAT_FED_VIEW_ERR_INVALID;
|
||||
}
|
||||
if (!amduat_fed_view_append(&view,
|
||||
domain_view.records,
|
||||
domain_view.len)) {
|
||||
amduat_fed_replay_view_free(&domain_view);
|
||||
amduat_fed_view_free(&view);
|
||||
return false;
|
||||
{
|
||||
amduat_fed_view_error_t append_err;
|
||||
|
||||
append_err = amduat_fed_view_append(&view,
|
||||
domain_view.records,
|
||||
domain_view.len);
|
||||
if (append_err != AMDUAT_FED_VIEW_OK) {
|
||||
amduat_fed_replay_view_free(&domain_view);
|
||||
amduat_fed_view_free(&view);
|
||||
return append_err;
|
||||
}
|
||||
}
|
||||
amduat_fed_replay_view_free(&domain_view);
|
||||
}
|
||||
|
||||
*out_view = view;
|
||||
return true;
|
||||
return AMDUAT_FED_VIEW_OK;
|
||||
}
|
||||
|
||||
void amduat_fed_view_free(amduat_fed_view_t *view) {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ static int test_view_and_resolve(void) {
|
|||
denies[0].id.ref = ref_d;
|
||||
denies[0].reason_code = 0;
|
||||
|
||||
if (!amduat_fed_view_build(records, 4, 1, bounds, 2, denies, 1, &view)) {
|
||||
if (amduat_fed_view_build(records, 4, 1, bounds, 2, denies, 1, &view) !=
|
||||
AMDUAT_FED_VIEW_OK) {
|
||||
fprintf(stderr, "view build failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -167,7 +168,8 @@ static int test_view_conflict(void) {
|
|||
bounds[1].snapshot_id = 1;
|
||||
bounds[1].log_prefix = 10;
|
||||
|
||||
if (amduat_fed_view_build(records, 2, 1, bounds, 2, NULL, 0, &view)) {
|
||||
if (amduat_fed_view_build(records, 2, 1, bounds, 2, NULL, 0, &view) !=
|
||||
AMDUAT_FED_VIEW_ERR_CONFLICT) {
|
||||
fprintf(stderr, "expected conflict\n");
|
||||
amduat_fed_view_free(&view);
|
||||
return 1;
|
||||
|
|
@ -191,11 +193,13 @@ static int test_view_rebuild_metadata(void) {
|
|||
bounds[0].snapshot_id = 1;
|
||||
bounds[0].log_prefix = 10;
|
||||
|
||||
if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_a)) {
|
||||
if (amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_a) !=
|
||||
AMDUAT_FED_VIEW_OK) {
|
||||
fprintf(stderr, "view rebuild build A failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_b)) {
|
||||
if (amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_b) !=
|
||||
AMDUAT_FED_VIEW_OK) {
|
||||
fprintf(stderr, "view rebuild build B failed\n");
|
||||
amduat_fed_view_free(&view_a);
|
||||
return 1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue