From 8747c4f4cfce036f71b85b8f84b39a9b9a222458 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sun, 28 Jun 2026 14:14:10 +0100 Subject: [PATCH] implements PLN-0127 async io lane boundaries --- crates/console/prometeu-system/src/lib.rs | 3 +- .../src/services/async_work.rs | 158 +++++++++++++++++- .../prometeu-system/src/services/fs/mod.rs | 2 +- .../src/services/fs/virtual_fs.rs | 38 +++++ .../prometeu-system/src/services/memcard.rs | 50 ++++++ discussion/index.ndjson | 2 +- ...p-home-filesystem-surface-and-semantics.md | 10 ++ ...e-memcard-and-fs-integration-boundaries.md | 2 +- 8 files changed, 252 insertions(+), 13 deletions(-) diff --git a/crates/console/prometeu-system/src/lib.rs b/crates/console/prometeu-system/src/lib.rs index 77e44959..c0c052f0 100644 --- a/crates/console/prometeu-system/src/lib.rs +++ b/crates/console/prometeu-system/src/lib.rs @@ -10,9 +10,10 @@ pub use prometeu_hal::{RenderWorkerBackend, RenderWorkerFrameSink}; pub use services::async_work::{ AsyncWorkActiveJob, AsyncWorkCancelToken, AsyncWorkJobContext, AsyncWorkJobId, AsyncWorkJobKind, AsyncWorkJobOutcome, AsyncWorkLane, AsyncWorkLaneConfig, - AsyncWorkLaneController, AsyncWorkLaneError, AsyncWorkLaneTelemetry, + AsyncWorkLaneController, AsyncWorkLaneError, AsyncWorkLaneTelemetry, AsyncWorkPriority, }; pub use services::fs; +pub use services::memcard::MemcardAsyncLaneOperation; pub use services::process; pub use services::task; pub use services::vm_runtime::{ diff --git a/crates/console/prometeu-system/src/services/async_work.rs b/crates/console/prometeu-system/src/services/async_work.rs index 002926b3..475ef27e 100644 --- a/crates/console/prometeu-system/src/services/async_work.rs +++ b/crates/console/prometeu-system/src/services/async_work.rs @@ -29,6 +29,36 @@ pub enum AsyncWorkJobKind { Test, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AsyncWorkPriority { + MemcardCommitWrite, + FsWriteConfig, + AssetLoad, + NonCriticalReadList, +} + +impl AsyncWorkPriority { + const fn rank(self) -> u8 { + match self { + Self::MemcardCommitWrite => 0, + Self::FsWriteConfig => 1, + Self::AssetLoad => 2, + Self::NonCriticalReadList => 3, + } + } +} + +impl AsyncWorkJobKind { + pub const fn default_priority(self) -> AsyncWorkPriority { + match self { + Self::Memcard => AsyncWorkPriority::MemcardCommitWrite, + Self::Fs => AsyncWorkPriority::FsWriteConfig, + Self::Asset => AsyncWorkPriority::AssetLoad, + Self::Test => AsyncWorkPriority::NonCriticalReadList, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AsyncWorkJobOutcome { Completed, @@ -59,6 +89,7 @@ impl Default for AsyncWorkLaneConfig { pub struct AsyncWorkActiveJob { pub id: AsyncWorkJobId, pub kind: AsyncWorkJobKind, + pub priority: AsyncWorkPriority, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] @@ -131,6 +162,7 @@ impl AsyncWorkCancelToken { pub struct AsyncWorkJobContext { pub job_id: AsyncWorkJobId, pub kind: AsyncWorkJobKind, + pub priority: AsyncWorkPriority, cancel_token: AsyncWorkCancelToken, } @@ -145,6 +177,7 @@ type AsyncWorkFn = Box AsyncWorkJobOutcome + struct PendingAsyncWorkJob { id: AsyncWorkJobId, kind: AsyncWorkJobKind, + priority: AsyncWorkPriority, cancel_token: AsyncWorkCancelToken, work: AsyncWorkFn, } @@ -152,6 +185,7 @@ struct PendingAsyncWorkJob { struct RunningAsyncWorkJob { id: AsyncWorkJobId, kind: AsyncWorkJobKind, + priority: AsyncWorkPriority, cancel_token: AsyncWorkCancelToken, work: AsyncWorkFn, } @@ -177,6 +211,15 @@ impl AsyncWorkLane { &self, kind: AsyncWorkJobKind, work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static, + ) -> Result { + self.submit_with_priority(kind, kind.default_priority(), work) + } + + pub fn submit_with_priority( + &self, + kind: AsyncWorkJobKind, + priority: AsyncWorkPriority, + work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static, ) -> Result { let mut state = self.state.lock().map_err(|_| AsyncWorkLaneError::InternalFailure)?; if state.shutdown_requested { @@ -186,18 +229,52 @@ impl AsyncWorkLane { state.next_job_id += 1; let id = AsyncWorkJobId::new(state.next_job_id); let cancel_token = AsyncWorkCancelToken::new(); - state.pending.push_back(PendingAsyncWorkJob { - id, - kind, - cancel_token, - work: Box::new(work), - }); + let job = PendingAsyncWorkJob { id, kind, priority, cancel_token, work: Box::new(work) }; + let insert_at = state + .pending + .iter() + .position(|pending| priority.rank() < pending.priority.rank()) + .unwrap_or(state.pending.len()); + state.pending.insert(insert_at, job); let pending_depth = state.pending.len(); state.telemetry.record_submitted(id, pending_depth); self.ready.notify_one(); Ok(id) } + pub fn submit_memcard_commit_write( + &self, + work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static, + ) -> Result { + self.submit_with_priority( + AsyncWorkJobKind::Memcard, + AsyncWorkPriority::MemcardCommitWrite, + work, + ) + } + + pub fn submit_fs_write_config( + &self, + work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static, + ) -> Result { + self.submit_with_priority(AsyncWorkJobKind::Fs, AsyncWorkPriority::FsWriteConfig, work) + } + + pub fn submit_asset_load( + &self, + work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static, + ) -> Result { + self.submit_with_priority(AsyncWorkJobKind::Asset, AsyncWorkPriority::AssetLoad, work) + } + + pub fn submit_non_critical_read_list( + &self, + kind: AsyncWorkJobKind, + work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static, + ) -> Result { + self.submit_with_priority(kind, AsyncWorkPriority::NonCriticalReadList, work) + } + pub fn request_cancel(&self, job_id: AsyncWorkJobId) -> bool { let state = self.state.lock().unwrap(); if state.active.is_some_and(|active| active.id == job_id) { @@ -253,7 +330,11 @@ impl AsyncWorkLane { } if let Some(pending) = state.pending.pop_front() { - let active = AsyncWorkActiveJob { id: pending.id, kind: pending.kind }; + let active = AsyncWorkActiveJob { + id: pending.id, + kind: pending.kind, + priority: pending.priority, + }; state.active = Some(active); state.active_cancel_token = Some(pending.cancel_token.clone()); let pending_depth = state.pending.len(); @@ -261,6 +342,7 @@ impl AsyncWorkLane { return Some(RunningAsyncWorkJob { id: pending.id, kind: pending.kind, + priority: pending.priority, cancel_token: pending.cancel_token, work: pending.work, }); @@ -332,8 +414,12 @@ impl Drop for AsyncWorkLaneController { fn run_async_work_loop(lane: Arc) { while let Some(job) = lane.wait_take() { - let context = - AsyncWorkJobContext { job_id: job.id, kind: job.kind, cancel_token: job.cancel_token }; + let context = AsyncWorkJobContext { + job_id: job.id, + kind: job.kind, + priority: job.priority, + cancel_token: job.cancel_token, + }; let result = catch_unwind(AssertUnwindSafe(|| (job.work)(context))) .unwrap_or(AsyncWorkJobOutcome::Failed); lane.record_finished(job.id, result); @@ -379,6 +465,60 @@ mod tests { assert_eq!(telemetry.completed_jobs, 2); } + #[test] + fn async_work_lane_runs_higher_priority_io_before_assets() { + let lane = Arc::new(AsyncWorkLane::default()); + let (tx, rx) = mpsc::channel(); + + lane.submit_asset_load({ + let tx = tx.clone(); + move |ctx| { + tx.send((ctx.kind, ctx.priority)).unwrap(); + AsyncWorkJobOutcome::Completed + } + }) + .unwrap(); + lane.submit_non_critical_read_list(AsyncWorkJobKind::Fs, { + let tx = tx.clone(); + move |ctx| { + tx.send((ctx.kind, ctx.priority)).unwrap(); + AsyncWorkJobOutcome::Completed + } + }) + .unwrap(); + lane.submit_memcard_commit_write({ + let tx = tx.clone(); + move |ctx| { + tx.send((ctx.kind, ctx.priority)).unwrap(); + AsyncWorkJobOutcome::Completed + } + }) + .unwrap(); + lane.submit_fs_write_config({ + let tx = tx.clone(); + move |ctx| { + tx.send((ctx.kind, ctx.priority)).unwrap(); + AsyncWorkJobOutcome::Completed + } + }) + .unwrap(); + + let mut controller = + AsyncWorkLaneController::start(AsyncWorkLaneConfig::default(), Arc::clone(&lane)); + + assert_eq!( + rx.recv().unwrap(), + (AsyncWorkJobKind::Memcard, AsyncWorkPriority::MemcardCommitWrite) + ); + assert_eq!(rx.recv().unwrap(), (AsyncWorkJobKind::Fs, AsyncWorkPriority::FsWriteConfig)); + assert_eq!(rx.recv().unwrap(), (AsyncWorkJobKind::Asset, AsyncWorkPriority::AssetLoad)); + assert_eq!( + rx.recv().unwrap(), + (AsyncWorkJobKind::Fs, AsyncWorkPriority::NonCriticalReadList) + ); + controller.stop().unwrap(); + } + #[test] fn async_work_lane_has_one_active_job() { let lane = Arc::new(AsyncWorkLane::default()); diff --git a/crates/console/prometeu-system/src/services/fs/mod.rs b/crates/console/prometeu-system/src/services/fs/mod.rs index dcb16257..bed0b115 100644 --- a/crates/console/prometeu-system/src/services/fs/mod.rs +++ b/crates/console/prometeu-system/src/services/fs/mod.rs @@ -8,4 +8,4 @@ pub use fs_backend::FsBackend; pub use fs_entry::FsEntry; pub use fs_error::FsError; pub use fs_state::FsState; -pub use virtual_fs::VirtualFS; +pub use virtual_fs::{FsAsyncLaneOperation, VirtualFS}; diff --git a/crates/console/prometeu-system/src/services/fs/virtual_fs.rs b/crates/console/prometeu-system/src/services/fs/virtual_fs.rs index dc09d3f8..fdc6b6f6 100644 --- a/crates/console/prometeu-system/src/services/fs/virtual_fs.rs +++ b/crates/console/prometeu-system/src/services/fs/virtual_fs.rs @@ -1,4 +1,5 @@ use crate::fs::{FsBackend, FsEntry, FsError}; +use crate::services::async_work::{AsyncWorkJobKind, AsyncWorkPriority}; /// Virtual Filesystem (VFS) interface for Prometeu. /// @@ -13,6 +14,29 @@ pub struct VirtualFS { backend: Option>, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FsAsyncLaneOperation { + Write, + Delete, + Config, + Read, + ListDir, + Exists, +} + +impl FsAsyncLaneOperation { + pub const fn job_kind(self) -> AsyncWorkJobKind { + AsyncWorkJobKind::Fs + } + + pub const fn priority(self) -> AsyncWorkPriority { + match self { + Self::Write | Self::Delete | Self::Config => AsyncWorkPriority::FsWriteConfig, + Self::Read | Self::ListDir | Self::Exists => AsyncWorkPriority::NonCriticalReadList, + } + } +} + impl Default for VirtualFS { fn default() -> Self { Self::new() @@ -242,4 +266,18 @@ mod tests { assert!(matches!(vfs.delete("/"), Err(FsError::PermissionDenied))); assert_eq!(calls.delete.load(Ordering::Relaxed), 0); } + + #[test] + fn fs_async_lane_operations_classify_priority_without_public_api_changes() { + assert_eq!(FsAsyncLaneOperation::Write.job_kind(), AsyncWorkJobKind::Fs); + assert_eq!(FsAsyncLaneOperation::Write.priority(), AsyncWorkPriority::FsWriteConfig); + assert_eq!(FsAsyncLaneOperation::Delete.priority(), AsyncWorkPriority::FsWriteConfig); + assert_eq!(FsAsyncLaneOperation::Config.priority(), AsyncWorkPriority::FsWriteConfig); + assert_eq!(FsAsyncLaneOperation::Read.priority(), AsyncWorkPriority::NonCriticalReadList); + assert_eq!( + FsAsyncLaneOperation::ListDir.priority(), + AsyncWorkPriority::NonCriticalReadList + ); + assert_eq!(FsAsyncLaneOperation::Exists.priority(), AsyncWorkPriority::NonCriticalReadList); + } } diff --git a/crates/console/prometeu-system/src/services/memcard.rs b/crates/console/prometeu-system/src/services/memcard.rs index a53b5436..5ade70eb 100644 --- a/crates/console/prometeu-system/src/services/memcard.rs +++ b/crates/console/prometeu-system/src/services/memcard.rs @@ -1,4 +1,5 @@ use crate::fs::{FsError, VirtualFS}; +use crate::services::async_work::{AsyncWorkJobKind, AsyncWorkPriority}; use std::collections::HashMap; pub const MEMCARD_SLOT_COUNT: usize = 32; @@ -50,6 +51,30 @@ pub struct MemcardWriteResult { pub bytes_written: u32, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MemcardAsyncLaneOperation { + SlotWrite, + SlotCommit, + SlotClear, + SlotRead, + SlotStat, +} + +impl MemcardAsyncLaneOperation { + pub const fn job_kind(self) -> AsyncWorkJobKind { + AsyncWorkJobKind::Memcard + } + + pub const fn priority(self) -> AsyncWorkPriority { + match self { + Self::SlotWrite | Self::SlotCommit | Self::SlotClear => { + AsyncWorkPriority::MemcardCommitWrite + } + Self::SlotRead | Self::SlotStat => AsyncWorkPriority::NonCriticalReadList, + } + } +} + #[derive(Debug, Clone)] struct SlotImage { payload: Vec, @@ -428,4 +453,29 @@ mod tests { let stat = mem.slot_stat(&fs, 7, 2); assert_eq!(stat.state, MemcardSlotState::Corrupt); } + + #[test] + fn memcard_async_lane_operations_classify_priority_without_changing_public_api() { + assert_eq!(MemcardAsyncLaneOperation::SlotWrite.job_kind(), AsyncWorkJobKind::Memcard); + assert_eq!( + MemcardAsyncLaneOperation::SlotWrite.priority(), + AsyncWorkPriority::MemcardCommitWrite + ); + assert_eq!( + MemcardAsyncLaneOperation::SlotCommit.priority(), + AsyncWorkPriority::MemcardCommitWrite + ); + assert_eq!( + MemcardAsyncLaneOperation::SlotClear.priority(), + AsyncWorkPriority::MemcardCommitWrite + ); + assert_eq!( + MemcardAsyncLaneOperation::SlotRead.priority(), + AsyncWorkPriority::NonCriticalReadList + ); + assert_eq!( + MemcardAsyncLaneOperation::SlotStat.priority(), + AsyncWorkPriority::NonCriticalReadList + ); + } } diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 5cb9d955..23b25bdc 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -18,7 +18,7 @@ {"type":"discussion","id":"DSC-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"AGD-0005-vm-owned-random-service.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"AGD-0006-app-home-filesystem-surface-and-semantics.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0008","status":"done","ticket":"perf-runtime-telemetry-hot-path","title":"Agenda - [PERF] Runtime Telemetry Hot Path","created_at":"2026-03-27","updated_at":"2026-06-04","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0026","file":"discussion/lessons/DSC-0008-perf-runtime-telemetry-hot-path/LSN-0026-push-based-telemetry-model.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} -{"type":"discussion","id":"DSC-0009","status":"in_progress","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-06-28","tags":["perf","asset","fs","async","scheduler","runtime"],"agendas":[{"id":"AGD-0008","file":"AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-06-28"}],"decisions":[{"id":"DEC-0034","file":"DEC-0034-async-work-lane-and-asset-backlog-contract.md","status":"accepted","created_at":"2026-06-28","updated_at":"2026-06-28","ref_agenda":"AGD-0008"}],"plans":[{"id":"PLN-0123","file":"PLN-0123-async-work-lane-specification-propagation.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0124","file":"PLN-0124-async-work-lane-runtime-infrastructure.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0125","file":"PLN-0125-asset-backlog-and-stable-slot-handles.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0126","file":"PLN-0126-asset-backlog-public-api-and-status-surface.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0127","file":"PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0128","file":"PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0009","status":"in_progress","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-06-28","tags":["perf","asset","fs","async","scheduler","runtime"],"agendas":[{"id":"AGD-0008","file":"AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-06-28"}],"decisions":[{"id":"DEC-0034","file":"DEC-0034-async-work-lane-and-asset-backlog-contract.md","status":"accepted","created_at":"2026-06-28","updated_at":"2026-06-28","ref_agenda":"AGD-0008"}],"plans":[{"id":"PLN-0123","file":"PLN-0123-async-work-lane-specification-propagation.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0124","file":"PLN-0124-async-work-lane-runtime-infrastructure.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0125","file":"PLN-0125-asset-backlog-and-stable-slot-handles.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0126","file":"PLN-0126-asset-backlog-public-api-and-status-surface.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0127","file":"PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0128","file":"PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]}],"lessons":[]} {"type":"discussion","id":"DSC-0010","status":"done","ticket":"perf-host-desktop-frame-pacing-and-presentation","title":"Agenda - [PERF] Host Desktop Frame Pacing and Presentation","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0036","file":"discussion/lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]} {"type":"discussion","id":"DSC-0011","status":"abandoned","ticket":"perf-gfx-render-pipeline-and-dirty-regions","title":"Agenda - [PERF] GFX Render Pipeline and Dirty Regions","created_at":"2026-03-27","updated_at":"2026-06-28","tags":[],"agendas":[{"id":"AGD-0010","file":"AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md","status":"abandoned","created_at":"2026-03-27","updated_at":"2026-06-28","_override_reason":"User explicitly chose to remove this agenda because it no longer applies to the current render scenario; future optimizations can be discussed when needed."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to abandon this discussion because it no longer applies to the current render scenario; future optimizations can be discussed when needed."} {"type":"discussion","id":"DSC-0012","status":"done","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-04-19","tags":["perf","runtime","syscall","telemetry","debug","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0034","file":"discussion/lessons/DSC-0012-perf-runtime-introspection-syscalls/LSN-0034-host-owned-debug-boundaries.md","status":"done","created_at":"2026-04-19","updated_at":"2026-04-19"}]} diff --git a/discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md b/discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md index 01bf40cc..588a197c 100644 --- a/discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md +++ b/discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md @@ -26,6 +26,16 @@ Sem um contrato claro de `home` por app, a API tende a crescer com semantica inc agenda deve decidir a API publica de FS considerando essa lane, mas a existencia da lane ja esta fechada por `DEC-0034`. +## Fronteira com a Async IO Lane + +`DEC-0034` fecha a existencia de uma lane serial para trabalho async de IO. FS +pode consumir essa lane internamente para escrita, configuracao, leitura ou +listagem, conforme prioridade operacional definida pelo runtime. + +Esta agenda continua dona da decisao sobre API publica de FS para `app home`. +Portanto, a existencia da lane nao implica criar agora request handles, polling +publico ou novas syscalls de FS. Esses shapes permanecem em aberto aqui. + ## Alvo da Discussao Fechar a superficie e semantica funcional de `fs` para perfil `app`, com sandbox por `app_id` e transporte de bytes via decisao `003`. diff --git a/discussion/workflow/plans/PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md b/discussion/workflow/plans/PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md index d04bae32..4dd01f7b 100644 --- a/discussion/workflow/plans/PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md +++ b/discussion/workflow/plans/PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md @@ -2,7 +2,7 @@ id: PLN-0127 ticket: perf-async-background-work-lanes-for-assets-and-fs title: Async Lane Memcard and FS Integration Boundaries -status: open +status: done created: 2026-06-28 completed: ref_decisions: [DEC-0034]