Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions loopx/control_plane/capabilities/native_child_admission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ export interface NativeChildReportAdmission extends JsonObject {
}

/** Consume committed admission facts, not a whitelist of current status labels.
* Identity and phase must first be verified by the settlement readback owner. */
* Identity, phase and any semantic replan obligation must first be verified by
* the settlement readback owner. Reporting does not grant ordinary delivery. */
export function nativeChildReportAdmission(
guardDetails: JsonObject,
guardStatus: unknown,
effectId: string,
phase: ReceiptBoundReplayPhase,
closeoutStarted: boolean,
semanticReplanObligationId: string | null = null,
): NativeChildReportAdmission {
const projectionAbsent = ["must_attempt_work", "delivery_allowed", "quiet_noop_allowed"]
.every((field) => !Object.hasOwn(guardDetails, field));
Expand All @@ -29,9 +31,12 @@ export function nativeChildReportAdmission(
(guardStatus === "normal_run" || guardStatus === "turn_run_once") &&
(guardDetails.ok === undefined || guardDetails.ok === true) &&
(guardDetails.should_run === undefined || guardDetails.should_run === true);
const workRouteAdmitted = guardDetails.delivery_allowed === true ||
(guardDetails.delivery_allowed === false && guardDetails.interaction_mode === "autonomous_replan" &&
semanticReplanObligationId !== null);
const admitted = legacyAdmission || (
guardDetails.ok === true && guardDetails.should_run === true &&
guardDetails.must_attempt_work === true && guardDetails.delivery_allowed === true &&
guardDetails.must_attempt_work === true && workRouteAdmitted &&
guardDetails.quiet_noop_allowed === false
);
const open = phase === "open" && !closeoutStarted;
Expand Down
10 changes: 5 additions & 5 deletions loopx/control_plane/quota/heartbeat_receipt_identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,21 +207,21 @@ export function heartbeatWorkRequalification(
if (identity.todo_id !== null && currentDetails.selected_todo_id !== identity.todo_id) return replay;
// No legacy/status-label compatibility for new qualifications: all modern
// work facts must be explicit. This reuses the report-admission predicate.
if (nativeChildReportAdmission(currentDetails, null, identity.effect_id, phase, false)
.report_permission !== "new_operation") return replay;
const oldDetails = heartbeatReceiptDetails(original);
if (nativeChildReportAdmission(oldDetails, original.status, identity.effect_id, phase, false)
.report_permission === "new_operation") return replay;
const originalReplan = normalizeHeartbeatReplanObligationId(oldDetails.semantic_replan_obligation_id);
const currentReplan = normalizeHeartbeatReplanObligationId(currentDetails.semantic_replan_obligation_id);
if (nativeChildReportAdmission(currentDetails, null, identity.effect_id, phase, false, currentReplan)
.report_permission !== "new_operation") return replay;
if (nativeChildReportAdmission(oldDetails, original.status, identity.effect_id, phase, false, originalReplan)
.report_permission === "new_operation") return replay;
if (currentReplan !== null && currentReplan !== originalReplan) {
throw receiptIdentityConflict("work requalification cannot replace the original semantic replan guard");
}
const qualified = {...oldDetails};
// The first positive work admission owns these facts. A repair-only
// receipt's earlier selection cannot describe the newly admitted work.
for (const field of ["ok", "should_run", "must_attempt_work", "delivery_allowed", "quiet_noop_allowed",
"admission_resume_when", "admission_resume_ready"]) {
"interaction_mode", "admission_resume_when", "admission_resume_ready"]) {
if (currentDetails[field] !== undefined) qualified[field] = currentDetails[field]!;
else delete qualified[field];
}
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/quota/settlement_readback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1342,7 +1342,7 @@ function readQuotaSettlementFromRequest(
receiptDetails.should_run === true && receiptDetails.delivery_allowed === false,
native_child_admission: nativeChildReportAdmission(
receiptDetails, heartbeatReceipt.status, identity.effect_id, replayPhase,
closeoutStarted,
closeoutStarted, normalizeReplanObligationId(semanticReplanGuard.selected_obligation_id),
),
...(request.heartbeat_reentry_guard === null ? {} : {
heartbeat_reentry_qualification: heartbeatWorkRequalification(heartbeatReceipt,
Expand Down
4 changes: 3 additions & 1 deletion tests/control_plane/test_native_child_replan_guard_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ def test_legal_replan_reports_native_child_without_settling_parent(
identity = guard["heartbeat_receipt"]["settlement_identity"]
assert identity.get("todo_id") == (TODO if todo_bound else None)
assert bool(identity.get("replan_obligation_id")) is not todo_bound
assert guard["interaction_contract"]["agent_channel"]["delivery_allowed"] is True
assert guard["interaction_contract"]["agent_channel"]["delivery_allowed"] is False
assert guard["interaction_contract"]["agent_channel"]["must_attempt"] is True
assert guard["heartbeat_receipt"]["semantic_replan_obligation_id"]
original_index = index.read_bytes()
base = ("native-child", "--goal-id", GOAL, "--agent-id", AGENT,
"--turn-instance-id", TURN)
Expand Down
42 changes: 42 additions & 0 deletions tests/control_plane_ts/native_child_admission.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,31 @@ test("late facts are allowed but no new operation once closeout has begun", () =
.report_permission, "existing_operation_only");
});

test("an admitted semantic replan can report without ordinary delivery authority", () => {
const replan = {...proof, interaction_mode: "autonomous_replan", delivery_allowed: false};
const obligation = "replan-0000000000000001";
for (const status of ["autonomous_replan_required", "future_work_label"]) {
assert.equal(nativeChildReportAdmission(replan, status, "original", "open", false, obligation)
.report_permission, "new_operation");
assert.equal(nativeChildReportAdmission(replan, status, "original", "open", true, obligation)
.report_permission, "existing_operation_only");
for (const phase of ["settlement_pending", "settled"] as const) {
assert.equal(nativeChildReportAdmission(replan, status, "original", phase, false, obligation)
.report_permission, "existing_operation_only");
}
}
for (const denied of [
{...replan, ok: false}, {...replan, should_run: false}, {...replan, must_attempt_work: false},
{...replan, quiet_noop_allowed: true}, {...replan, delivery_allowed: "false"},
{...replan, interaction_mode: "successor_replan_required"},
]) {
assert.equal(nativeChildReportAdmission(denied, "autonomous_replan_required", "original", "open", false, obligation)
.report_permission, "not_admitted");
}
assert.equal(nativeChildReportAdmission(replan, "autonomous_replan_required", "original", "open", false)
.report_permission, "not_admitted");
});

test("legacy ordinary compatibility never overrides partial or negative facts", async (t) => {
for (const status of ["normal_run", "turn_run_once"]) {
assert.equal(nativeChildReportAdmission({}, status, "original", "open", false)
Expand Down Expand Up @@ -103,6 +128,23 @@ test("exact-Turn original binding is read without selecting or inferring new wor
}
});

test("readback validates a semantic replan obligation before report admission", async () => {
for (const replan of [false, true]) {
const {root, request, identity, event, log} = await fixture(replan);
try {
const details = {...event.details, delivery_allowed: false, interaction_mode: "autonomous_replan",
semantic_replan_obligation_id: "replan-0000000000000001"};
await writeFile(log, JSON.stringify({...event, details}) + "\n");
const result = await readQuotaSettlement(request);
assert.deepEqual(result.identity.result.value, settlementIdentityPayload(identity));
assert.equal(result.native_child_admission.report_permission, "new_operation");
assert.equal(result.heartbeat_receipt.details.delivery_allowed, false);
await writeFile(log, JSON.stringify({...event, details: {...details, semantic_replan_obligation_id: "malformed"}}) + "\n");
await assert.rejects(readQuotaSettlement(request), /semantic replan guard is malformed/);
} finally {await rm(root, {recursive: true, force: true});}
}
});

test("exact binding resolution preserves explicit denials and conflicts", async (t) => {
for (const [label, mutate] of [
["other actor", {agent_id: "other-coordinator"}],
Expand Down
Loading