Cel
Zaimplementować i w pełni przetestować WriteCoordinator oraz prawdziwie dwuetapową ścieżkę prepare/confirm/commit dla pojedynczego zapisu, pozostawiając funkcję nieosiągalną z produkcyjnego composition root do ukończenia #22 i #23 .
Granica etapu
Porty i capability
WriteCoordinator zależy od:
ReadBusPort;
WriteBusPort;
AuditPort;
ProfileTrustPort;
ClockPort;
SessionControlPort.
WriteBusPort::execute przyjmuje wyłącznie PreparedBusWrite:
typ jest publiczny dla implementacji adaptera;
pola są prywatne;
nie ma publicznego konstruktora;
jedyny konstruktor znajduje się w prywatnym kernelu WriteCoordinator;
TUI, backup, logger i composition root nie mogą utworzyć tej wartości.
Autorytatywność profilu
WriteIntent jest tylko żądaniem operatora.
Przed utworzeniem planu Coordinator pobiera aktywny ValidatedDeviceProfile po profile_hash.
Access, guards, target raw, write function i read-back policy są pobierane ponownie z profilu.
preview_raw z intentu nie jest autorytatywne.
Dwie fazy użytkowe
1. prepare_write(intent) -> PreparedWritePlan
Wykonuje:
process gate;
Connected + Armed + audit Healthy + operation Idle;
zgodność SessionId/fingerprint/profile hash;
trust bieżącego profilu;
odrzucenie ReadOnly/Dangerous/motion/fault reset/raw write;
pobranie świeżej wartości i guards;
rekalkulację engineering → raw;
sprawdzenie zakresu, kroku i reprezentowalności.
Wynikiem jest niezmienny, single-use PreparedWritePlan zawierający:
PlanId;
session/fingerprint/profile/parameter;
context hash;
old raw/engineering;
requested engineering i recomputed target raw;
model potwierdzenia;
challenge;
expires_at, maksymalnie 15 sekund.
Faza prepare nie wysyła write i nie tworzy PreparedToken audytu urządzenia.
2. confirm_write(plan_id, confirmation)
Atomowo konsumuje plan; ponowne użycie jest niemożliwe.
Ponownie sprawdza session, fingerprint, profile hash, trust, authorization, audit health i operation Idle.
Ponownie pobiera finalną świeżą old value i wszystkie guards po potwierdzeniu operatora .
Jakakolwiek zmiana daje PreconditionChanged, zero write i próbę trwałego zapisu rekordu decyzji.
Ustawia operation = SingleWrite dopiero przed urządzeniową fazą audytu.
Wywołuje AuditPort::prepare_device_write i czeka na trwały sukces.
Błąd prepare resetuje operation do Idle, rozbraja i ustawia Degraded.
Dopiero po sukcesie tworzy PreparedBusWrite i wywołuje dokładnie jeden write.
Wykonuje bounded read-back.
Wywołuje finalize_device_write.
Kończy operation i aktualizuje session state.
AuditPort — rozdzielone kontrakty
Decyzje przed fizycznym write
record_decision(DecisionAuditRecord) obsługuje:
Expired;
Cancelled;
RejectedByPolicy;
ProfileNotTrusted;
PreconditionChanged.
Nie wymaga tokenu urządzenia.
Jeżeli samo record_decision nie może zostać trwale wykonane:
Coordinator zwraca NotExecuted(AuditUnavailable);
nie wysyła write;
rozbraja sesję i ustawia audit health Degraded;
emituje wyłącznie best-effort diagnostykę o pierwotnej decyzji i błędzie storage;
nie próbuje rekurencyjnie zapisać AuditUnavailable i nie udaje istnienia trwałego rekordu.
Fizyczny write
prepare_device_write(DeviceWritePreparation) -> PreparedToken — trwały rekord przed write.
finalize_device_write(PreparedToken, DeviceWriteOutcome, ReadBackEvidence) — wyłącznie wyniki po rozpoczęciu write.
PreparedToken jest single-use i związany z PlanId/RequestId/context hash.
Outcomes
WriteOutcome =
NotExecuted(DecisionOutcome)
Executed(DeviceWriteOutcome)
DecisionOutcome:
Expired;
Cancelled;
RejectedByPolicy;
ProfileNotTrusted;
PreconditionChanged;
AuditUnavailable — brak trwałego rekordu, nie dowód jego istnienia.
DeviceWriteOutcome:
Verified;
DeviceRejected;
ReadBackMismatch;
OutcomeUnknown;
TransportLost;
AuditDegraded.
Prywatny kernel i rozszerzalna granica operacji
Prywatny execute_once przyjmuje sealed ExecutionAuthority:
OperationStepAuthority:
#17 później dodaje jedyną produkcyjną metodę WriteCoordinator::authorize_restore_step(&mut RestoreOperationPermit, index), która po pełnej weryfikacji permitu tworzy OperationStepAuthority wewnątrz tego samego crate’a i natychmiast przekazuje je do kernela. Nie wymaga to zmiany publicznego API transportu ani kryteriów zamknięcia #16 .
Wykonanie i read-back
Dokładnie jeden write.
Brak retry write.
TimeoutBeforeSend = pewne zero write.
Timeout po rozpoczęciu = OutcomeUnknown i disarm.
Delayed apply może wykonać 1–3 dodatkowe read-back po settle delay; nigdy kolejny write.
Integer/fixed/BCD/enum/bitfield używają exact raw albo accepted raw set z [PLAN 04] Zaprojektować wersjonowany format profilu i pełną walidację #4 .
Float używa polityki profilu.
UI aktualizuje wartość dopiero po pipeline/read-back.
Zakres 1.0 manual write
Dozwolone manualnie:
pojedynczy WritableWhenStopped;
pojedynczy Commissioning po dodatkowym potwierdzeniu kodu i wartości.
Niedozwolone:
Dangerous;
raw address/PDU;
broadcast;
motion;
fault reset;
arbitralny bulk write;
rollback.
Testy
Każda bramka i decyzja NotExecuted.
Plan expiry, single-use i context hash mismatch.
Zmiana old value/guard po prepare, przed confirm.
Intent preview/read-back policy nie może zmienić profilu.
Brak AuditPort/ProfileTrustPort = zero write.
Awaria record_decision zwraca AuditUnavailable, degraduje i nie wykonuje rekurencyjnego audytu.
Prepare device audit failure = zero write + operation Idle + Degraded.
Success, exception, TimeoutBeforeSend, OutcomeUnknown, disconnect, mismatch.
Delayed apply: dodatkowe read, dokładnie jeden write.
Finalize failure = AuditDegraded.
Compile fixture: PreparedBusWrite i OperationStepAuthority niekonstruowalne poza dozwolonym modułem.
Testowy wariant OperationStep przechodzi przez ten sam kernel i respektuje exact target/context, bez implementacji restore.
Composition root po [PLAN 16] Zbudować dwuetapowy core prepare/confirm zapisu #16 nadal nie udostępnia write.
Kryteria akceptacji
Zależności
Cel
Zaimplementować i w pełni przetestować
WriteCoordinatororaz prawdziwie dwuetapową ścieżkę prepare/confirm/commit dla pojedynczego zapisu, pozostawiając funkcję nieosiągalną z produkcyjnego composition root do ukończenia #22 i #23.Granica etapu
Porty i capability
WriteCoordinatorzależy od:ReadBusPort;WriteBusPort;AuditPort;ProfileTrustPort;ClockPort;SessionControlPort.WriteBusPort::executeprzyjmuje wyłączniePreparedBusWrite:WriteCoordinator;Autorytatywność profilu
WriteIntentjest tylko żądaniem operatora.ValidatedDeviceProfilepoprofile_hash.preview_rawz intentu nie jest autorytatywne.Dwie fazy użytkowe
1.
prepare_write(intent) -> PreparedWritePlanWykonuje:
Connected + Armed + audit Healthy + operation Idle;Wynikiem jest niezmienny, single-use
PreparedWritePlanzawierający:PlanId;expires_at, maksymalnie 15 sekund.Faza prepare nie wysyła write i nie tworzy
PreparedTokenaudytu urządzenia.2.
confirm_write(plan_id, confirmation)PreconditionChanged, zero write i próbę trwałego zapisu rekordu decyzji.operation = SingleWritedopiero przed urządzeniową fazą audytu.AuditPort::prepare_device_writei czeka na trwały sukces.PreparedBusWritei wywołuje dokładnie jeden write.finalize_device_write.AuditPort — rozdzielone kontrakty
Decyzje przed fizycznym write
record_decision(DecisionAuditRecord)obsługuje:Nie wymaga tokenu urządzenia.
Jeżeli samo
record_decisionnie może zostać trwale wykonane:NotExecuted(AuditUnavailable);Degraded;AuditUnavailablei nie udaje istnienia trwałego rekordu.Fizyczny write
prepare_device_write(DeviceWritePreparation) -> PreparedToken— trwały rekord przed write.finalize_device_write(PreparedToken, DeviceWriteOutcome, ReadBackEvidence)— wyłącznie wyniki po rozpoczęciu write.PreparedTokenjest single-use i związany z PlanId/RequestId/context hash.Outcomes
DecisionOutcome:DeviceWriteOutcome:Prywatny kernel i rozszerzalna granica operacji
Prywatny
execute_onceprzyjmuje sealedExecutionAuthority:Manual(ConsumedPreparedWritePlan)— jedyny wariant używany produkcyjnie w [PLAN 16] Zbudować dwuetapowy core prepare/confirm zapisu #16;OperationStep(OperationStepAuthority)— ogólna capability dla przyszłej, wcześniej zatwierdzonej operacji wielokrokowej.OperationStepAuthority:RestoreOperationPermit;#17 później dodaje jedyną produkcyjną metodę
WriteCoordinator::authorize_restore_step(&mut RestoreOperationPermit, index), która po pełnej weryfikacji permitu tworzyOperationStepAuthoritywewnątrz tego samego crate’a i natychmiast przekazuje je do kernela. Nie wymaga to zmiany publicznego API transportu ani kryteriów zamknięcia #16.Wykonanie i read-back
Zakres 1.0 manual write
Dozwolone manualnie:
WritableWhenStopped;Commissioningpo dodatkowym potwierdzeniu kodu i wartości.Niedozwolone:
Testy
record_decisionzwraca AuditUnavailable, degraduje i nie wykonuje rekurencyjnego audytu.Kryteria akceptacji
prepare_write → PreparedWritePlan → confirm_write.Zależności