Skip to content

Commit b8ee2a3

Browse files
committed
Correct native outbox and canonical queue composition fixtures
1 parent f403bf6 commit b8ee2a3

6 files changed

Lines changed: 251 additions & 10 deletions

File tree

‎docs/ADR/ADR-067-composable-agent-database.md‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,27 @@ canonical. No old-store conversion or journal removal. Agent ownership/start,
125125
tests, error flows and join conditions are frozen in this implementation contract
126126
and the durable feature acceptance below. The local root plan tracks execution.
127127
Remain Accepted until all required source/migration/tests/GitHub evidence exist.
128+
129+
TASK-COMP-007 / AC-COMP-004/007/008 preserves the existing AC-DSTORE-005
130+
outbox-image regression after the native format change. Its independently
131+
constructed expected object graph shares Resource/Collection strings and creates
132+
separate RowAccess values, while actual canonical decode and document `with`
133+
clones have different reference sharing. Orleans encodes those relationships;
134+
value-equal graphs need not have identical bytes. The pre-composition writer
135+
reproduces this failure. Test-only repair compares every decoded entry/receipt,
136+
polymorphic mutation and before/after image value, then requires byte-exact native
137+
reserialization of the actual decoded graph. Preserve the real store, sequential
138+
same-ID mutations, tombstone/revision/outbox counts and failure/CAS assertions.
139+
Worker owns only DocumentMutationImageTests.cs; root owns this contract and source
140+
join. No serializer, production image path or durability behavior changes. Build
141+
and focused native outbox/composition tests precede scoped delivery; complete
142+
Linux CI remains required. This corrects a fixture reference-identity assumption,
143+
not the exact user-content or frozen canonical digest contracts.
144+
145+
RF3 AC-COMP-007 fixture refinement: existing Enqueue canonicalizes the outer
146+
payload JSON before projection. Compare the complete decoded QueueGraphLink
147+
against the submitted link, including canonical endpoints, label and exact
148+
AttributesJson string; preserve source Ready/Attempts and graph assertions.
149+
Original property ordering is not a queue contract. Do not change queue storage
150+
or invoke a second projection to bypass the failure. Root owns this RF3 test
151+
join and must rerun the genuine SDK/MCP workflow before qualification.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
{
2+
"schema": "keyload.database-composition.fixture-stage.v1",
3+
"status": "verified_development_snapshot_genuine_RF3_rerun_required",
4+
"decision": "docs/ADR/ADR-067-composable-agent-database.md",
5+
"task": "TASK-COMP-007",
6+
"acceptance": [
7+
"AC-COMP-004",
8+
"AC-COMP-007",
9+
"AC-COMP-008",
10+
"AC-DSTORE-005"
11+
],
12+
"sourceBaseRevision": "f403bf61e730919a7b6e4b811539b870b8c8433d",
13+
"sourceFiles": [
14+
{
15+
"path": "docs/ADR/ADR-067-composable-agent-database.md",
16+
"sha256": "4149ed64816036e41a79770bd0ecc10a986d0a6baea113db92494bc504aed76b"
17+
},
18+
{
19+
"path": "tests/KeyLoad.UnitTests/Features/DocumentStorage/DocumentMutationImageTests.cs",
20+
"sha256": "fb4ca4916fe73e43943b225a92da5a868ff9bc3afbfd0b636d72e01137366f72"
21+
},
22+
{
23+
"path": "tests/KeyLoad.IntegrationTests/Features/DatabaseComposition/DatabaseCompositionRf3Tests.cs",
24+
"sha256": "a3238835d27941268e3d74ef653b4fe43119e543f6463245c3eab6d1bbd33a50"
25+
}
26+
],
27+
"changes": [
28+
"Compare every persisted outbox value and actual decoded native graph byte roundtrip; keep original document and failure assertions",
29+
"Compare complete decoded QueueGraphLink after existing queue JSON canonicalization; retain forward reverse and stable retry RF3 assertions"
30+
],
31+
"productionBehaviorChanged": false,
32+
"localDevelopment": {
33+
"fixture": "/private/tmp/keyload-composition-fixture-delivery-check",
34+
"os": "macOS/Darwin 27",
35+
"architecture": "arm64",
36+
"dotnetSdk": "10.0.401",
37+
"githubQualification": false,
38+
"build": {
39+
"exitCode": 0,
40+
"warnings": 0,
41+
"errors": 0,
42+
"log": {
43+
"path": "/private/tmp/keyload-composition-fixture-delivery-build.log",
44+
"sha256": "298458bee44a6811b85db902de30a401253f22f4e4b734903b7374fc29ac3958"
45+
}
46+
},
47+
"format": {
48+
"exitCode": 0,
49+
"log": {
50+
"path": "/private/tmp/keyload-composition-fixture-delivery-format.log",
51+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
52+
}
53+
},
54+
"governance": {
55+
"exitCode": 0,
56+
"projects": 26,
57+
"modules": 4,
58+
"log": {
59+
"path": "/private/tmp/keyload-composition-fixture-delivery-governance.log",
60+
"sha256": "76cb9ab8d9569308e691c3b773158a6447ff5fb4be35c6528a3497948ddd31ae"
61+
}
62+
},
63+
"compositionNormal": {
64+
"path": "/private/tmp/keyload-composition-fixture-normal-results/KeyLoad.UnitTests_net10.0_arm64.trx",
65+
"sha256": "e7b32d6803496b5b4a33936ee3431de22c542c574e2bb872b7e9218882ffde67",
66+
"total": 32,
67+
"passed": 32,
68+
"failed": 0,
69+
"notExecuted": 0
70+
},
71+
"compositionScalar": {
72+
"path": "/private/tmp/keyload-composition-fixture-scalar-results/KeyLoad.UnitTests_net10.0_arm64.trx",
73+
"sha256": "612f16b9bce76de129fbf2b7a36b642dc68b876bfa48526550051e307b0f4cea",
74+
"total": 32,
75+
"passed": 32,
76+
"failed": 0,
77+
"notExecuted": 0
78+
},
79+
"outboxNormal": {
80+
"path": "/private/tmp/keyload-composition-fixture-outbox-results/KeyLoad.UnitTests_net10.0_arm64.trx",
81+
"sha256": "7f5dc168097ba4eac0914617ce1ee5113f92e60e6bca341844f049d0f015e314",
82+
"total": 2,
83+
"passed": 2,
84+
"failed": 0,
85+
"notExecuted": 0
86+
},
87+
"outboxScalar": {
88+
"path": "/private/tmp/keyload-composition-fixture-outbox-scalar-results/KeyLoad.UnitTests_net10.0_arm64.trx",
89+
"sha256": "80740a052681963075fab3c04761d9aa36123ddc196987d902b86c760f03d60e",
90+
"total": 2,
91+
"passed": 2,
92+
"failed": 0,
93+
"notExecuted": 0
94+
},
95+
"scalarSetting": "DOTNET_EnableHWIntrinsic=0"
96+
},
97+
"originalIntegratedActionsBeforeFixtureRepair": {
98+
"sourceRevision": "ca22b1baa919198c81749d86ffab56df24ec9317",
99+
"runId": 37124217640,
100+
"url": "https://github.com/managedcode/KeyLoad/actions/runs/37124217640",
101+
"conclusion": "failure",
102+
"linuxOnly": true,
103+
"buildFormatGovernanceAnalyzers": "passed",
104+
"normal": {
105+
"total": 2402,
106+
"passed": 2357,
107+
"failed": 45,
108+
"skipped": 0,
109+
"compositionPassed": 32,
110+
"reportSha256": "37dff3dbcb1801dac6d4b229b09dd7622473596e2da313c8ee4fd9c8295f771e"
111+
},
112+
"scalar": "skipped_after_normal_failure",
113+
"recovery": {
114+
"total": 190,
115+
"passed": 189,
116+
"failed": 1,
117+
"skipped": 0,
118+
"compositionPassed": 7,
119+
"reportSha256": "062794104f2db5d106a578c8eb92f7441fb49660ae54c9f72d4bc527d8fddbf9"
120+
},
121+
"rf3": {
122+
"total": 67,
123+
"passed": 66,
124+
"failed": 1,
125+
"skipped": 0,
126+
"compositionPassed": 1,
127+
"compositionTotal": 2,
128+
"reportSha256": "0a29cc8c3c5d29db4f1135277856af42c23a584a83a232b0358c8636b74f1ad1"
129+
},
130+
"artifacts": [
131+
{
132+
"id": 11275655112,
133+
"name": "test-results-ubuntu-latest",
134+
"sha256": "e76ea89faa5733cfbeb2d85268007caa1d65d25116dba5146e7d068da628196a"
135+
},
136+
{
137+
"id": 11274444675,
138+
"name": "docker-rf3-qualification",
139+
"sha256": "fe3ff64da75430d353963c5ec8e55d73d47624f6e9052df89deb74d0af2d7238"
140+
}
141+
],
142+
"rf3Failure": "AC-COMP-007 assertion compares existing canonical queue JSON with original property order; reverse portion not reached",
143+
"recoveryFailure": "ReplicaTermMetadataMutationTests.MalformedNestedOperationCannotBeSkippedOnTheStrictTermMiss missing ReplicaOperationInspectionCodec DI registration",
144+
"fullGatePassed": false
145+
},
146+
"review": "Separate read-only review found no fixture blocker; root inspected every owned diff",
147+
"delivery": {
148+
"status": "scoped_commit_and_original_Actions_rerun_pending",
149+
"runtimeQualified": false
150+
},
151+
"remainingGates": [
152+
"Delivered-source complete normal/scalar/process recovery CI",
153+
"Both genuine Docker Aspire RF3 SQL SDK official MCP composition cases",
154+
"Complete Benchmarks/site publication inputs"
155+
],
156+
"fullDeclarativeSqlComplete": false,
157+
"allModelCompositionQualified": false,
158+
"websitePublished": false
159+
}

‎docs/implementation/database-composition-source-stage-001.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,8 @@
333333
"latestIntegratedSourceRevision": "ca22b1baa919198c81749d86ffab56df24ec9317",
334334
"latestIntegratedCiRunId": 37124217640,
335335
"latestIntegratedCiUrl": "https://github.com/managedcode/KeyLoad/actions/runs/37124217640",
336-
"latestIntegratedCiStatus": "queued"
336+
"latestIntegratedCiStatus": "failure_original_Linux_reports_verified",
337+
"latestIntegratedEvidence": "docs/implementation/database-composition-fixture-stage-002.json"
337338
},
338339
"remainingGates": [
339340
"Delivered-source full build/format/governance and TUnit normal/scalar/process recovery",

‎docs/implementation/status.json‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,25 @@
2222
"rf3": "cancelled_unqualified",
2323
"latestIntegratedSourceRevision": "ca22b1baa919198c81749d86ffab56df24ec9317",
2424
"latestIntegratedCiRunId": 37124217640,
25-
"latestIntegratedCiStatus": "queued",
26-
"runtimeQualified": false
25+
"latestIntegratedCiStatus": "failure_original_Linux_reports_verified",
26+
"runtimeQualified": false,
27+
"latestIntegratedNormalCompositionPassed": 32,
28+
"latestIntegratedRecoveryCompositionPassed": 7,
29+
"latestIntegratedRf3CompositionPassed": 1,
30+
"latestIntegratedRf3CompositionTotal": 2,
31+
"latestIntegratedScalar": "skipped_after_normal_failure"
2732
},
2833
"fullDeclarativeCompositionComplete": false,
2934
"crossPartitionCompositionImplemented": false,
3035
"newCasesQualified": false,
3136
"remaining": [
32-
"Full unit gate retains 140 failures also present at base 8071148; local composition checks pass",
37+
"Latest integrated Linux CI has 45 unit and one recovery failures; own 32 unit and seven recovery cases pass, RF3 fixture repair requires rerun",
3338
"Exact-source normal scalar process recovery and RF3 SQL SDK official MCP Actions",
3439
"Full declarative model sources joins DML under ADR-065",
3540
"All-model and distributed composition correctness fault and comparable performance",
3641
"Complete Benchmarks and site qualification before publication"
37-
]
42+
],
43+
"fixtureReceipt": "docs/implementation/database-composition-fixture-stage-002.json"
3844
},
3945
"SQL-CLIENT-001": {
4046
"title": "Повний SQL, клієнтський протокол та перевірка текстового індексу",

‎tests/KeyLoad.IntegrationTests/Features/DatabaseComposition/DatabaseCompositionRf3Tests.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public async Task AC_COMP_007_SqlReadsQueuedLinksAndWritesGraphThenOfficialMcpEn
4040
await SqlRf3Protocol.EqualAsync(receipt, await SqlRf3Protocol.McpAsync<CommitReceipt>(mcp, sql, deadline.Token));
4141
await SqlRf3Protocol.EqualAsync(receipt, await McpCallerAssertions.SdkSuccessAsync(
4242
await sdk.CommitAsync(command, deadline.Token)));
43-
await VerifyForwardAsync(sdk, mcp, scenario, payload, deadline.Token);
43+
await VerifyForwardAsync(sdk, mcp, scenario, link, deadline.Token);
4444
await VerifyReverseAsync(sdk, mcp, scenario, link, deadline.Token);
4545
}
4646

@@ -73,7 +73,7 @@ await Assert.That(await McpCallerAssertions.SdkSuccessAsync(await sdk.InspectAsy
7373
}
7474

7575
private static async Task VerifyForwardAsync(KeyLoadClient sdk, McpOfficialClient mcp,
76-
RelationalSqlRf3Scenario scenario, string payload, CancellationToken cancellationToken)
76+
RelationalSqlRf3Scenario scenario, QueueGraphLink link, CancellationToken cancellationToken)
7777
{
7878
var traversal = new TraverseRequest(scenario.Partition, RelationalSqlRf3Tokens.Graph, scenario.First);
7979
var graph = await McpCallerAssertions.SdkSuccessAsync(await sdk.TraverseAsync(traversal, cancellationToken));
@@ -86,7 +86,7 @@ await SqlRf3Protocol.EqualAsync(graph, await SqlRf3Protocol.McpAsync<GraphTraver
8686
var source = await McpCallerAssertions.SdkSuccessAsync(await sdk.InspectAsync(scenario.Inspect(Message), cancellationToken));
8787
await Assert.That(source!.Metadata.State).IsEqualTo(MessageState.Ready);
8888
await Assert.That(source.Metadata.Attempts).IsEqualTo(RelationalSqlRf3Tokens.NoResults);
89-
await Assert.That(source.PayloadJson).IsEqualTo(payload);
89+
await Assert.That(JsonSerializer.Deserialize<QueueGraphLink>(source.PayloadJson!, JsonDefaults.Options)).IsEqualTo(link);
9090
}
9191

9292
private static async Task VerifyReverseAsync(KeyLoadClient sdk, McpOfficialClient mcp,

‎tests/KeyLoad.UnitTests/Features/DocumentStorage/DocumentMutationImageTests.cs‎

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,17 @@ public async Task AcDstore005SequentialSameIdMutationsPersistExactOutboxImagesAn
4242
mutations[ordinal], receipt.Mutations[ordinal], ordinal == 0 ? null : images[ordinal - 1], images[ordinal]);
4343
var bytes = db.Store.Read(view => view.ReadOwnedValue(
4444
KeySpace.Partition(OutboxSpace, db.Partition, ordinal + FirstSequence))!);
45-
await Assert.That(bytes.SequenceEqual(NativeSerialization.Serialize(expected))).IsTrue();
46-
await Assert.That(NativeSerialization.Deserialize<OutboxEntry>(bytes).After).IsEqualTo(images[ordinal]);
45+
var actual = NativeSerialization.Deserialize<OutboxEntry>(bytes);
46+
47+
await Assert.That(actual.Sequence).IsEqualTo(expected.Sequence);
48+
await Assert.That(actual.Ordinal).IsEqualTo(expected.Ordinal);
49+
await Assert.That(actual.CommittedAt).IsEqualTo(expected.CommittedAt);
50+
await AssertCommitTokenEqual(actual.Commit, expected.Commit);
51+
await AssertMutationEqual(actual.Mutation, expected.Mutation);
52+
await AssertMutationReceiptEqual(actual.Receipt, expected.Receipt);
53+
await Assert.That(actual.Before).IsEqualTo(expected.Before);
54+
await Assert.That(actual.After).IsEqualTo(expected.After);
55+
await Assert.That(bytes.SequenceEqual(NativeSerialization.Serialize(actual))).IsTrue();
4756
}
4857

4958
await Assert.That(db.Database.GetDocument("root", reference)).IsNull();
@@ -75,4 +84,46 @@ private static OperationResult Submit(TestDatabase db, Mutation mutation)
7584
var id = Guid.NewGuid();
7685
return db.Submit(OperationKind.Batch, new CommandRequest(id, db.Partition, [mutation]), id: id);
7786
}
87+
88+
private static async Task AssertCommitTokenEqual(CommitToken actual, CommitToken expected)
89+
{
90+
await Assert.That(actual.Incarnation).IsEqualTo(expected.Incarnation);
91+
await Assert.That(actual.AtomicPartitionId).IsEqualTo(expected.AtomicPartitionId);
92+
await Assert.That(actual.Position).IsEqualTo(expected.Position);
93+
await Assert.That(actual.OwnershipEpoch).IsEqualTo(expected.OwnershipEpoch);
94+
}
95+
96+
private static async Task AssertMutationReceiptEqual(MutationReceipt actual, MutationReceipt expected)
97+
{
98+
await Assert.That(actual.Kind).IsEqualTo(expected.Kind);
99+
await Assert.That(actual.Resource).IsEqualTo(expected.Resource);
100+
await Assert.That(actual.Id).IsEqualTo(expected.Id);
101+
await Assert.That(actual.Revision).IsEqualTo(expected.Revision);
102+
await Assert.That(actual.CompositionReferences.IsEmpty).IsTrue();
103+
}
104+
105+
private static async Task AssertMutationEqual(Mutation actual, Mutation expected)
106+
{
107+
await Assert.That(actual.GetType()).IsEqualTo(expected.GetType());
108+
await Assert.That(actual.Resource).IsEqualTo(expected.Resource);
109+
switch (actual)
110+
{
111+
case PutDocument put:
112+
await Assert.That(put).IsEqualTo((PutDocument)expected);
113+
break;
114+
case PatchDocument patch:
115+
var expectedPatch = (PatchDocument)expected;
116+
await Assert.That(patch.Collection).IsEqualTo(expectedPatch.Collection);
117+
await Assert.That(patch.Id).IsEqualTo(expectedPatch.Id);
118+
await Assert.That(patch.ExpectedRevision).IsEqualTo(expectedPatch.ExpectedRevision);
119+
await Assert.That(patch.Patches.SequenceEqual(expectedPatch.Patches)).IsTrue();
120+
break;
121+
case DeleteDocument delete:
122+
await Assert.That(delete).IsEqualTo((DeleteDocument)expected);
123+
break;
124+
default:
125+
throw new InvalidOperationException("The expected document mutation is unsupported.");
126+
}
127+
}
128+
78129
}

0 commit comments

Comments
 (0)