asset preload alignment
This commit is contained in:
parent
d7c970a3e2
commit
d576f7ddbd
@ -2,7 +2,7 @@
|
|||||||
use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller};
|
use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller};
|
||||||
use prometeu_hal::AssetBridge;
|
use prometeu_hal::AssetBridge;
|
||||||
use prometeu_hal::asset::{
|
use prometeu_hal::asset::{
|
||||||
AssetEntry, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, LoadStatus,
|
AssetEntry, AssetId, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, LoadStatus,
|
||||||
PreloadEntry, SlotRef, SlotStats,
|
PreloadEntry, SlotRef, SlotStats,
|
||||||
};
|
};
|
||||||
use prometeu_hal::cartridge::AssetsPayloadSource;
|
use prometeu_hal::cartridge::AssetsPayloadSource;
|
||||||
@ -48,7 +48,7 @@ impl<T> ResidentEntry<T> {
|
|||||||
/// This is internal to the AssetManager and not visible to peripherals.
|
/// This is internal to the AssetManager and not visible to peripherals.
|
||||||
pub struct BankPolicy<T> {
|
pub struct BankPolicy<T> {
|
||||||
/// Dedup table: asset_id -> resident entry (value + telemetry).
|
/// Dedup table: asset_id -> resident entry (value + telemetry).
|
||||||
resident: Arc<RwLock<HashMap<u32, ResidentEntry<T>>>>,
|
resident: Arc<RwLock<HashMap<AssetId, ResidentEntry<T>>>>,
|
||||||
|
|
||||||
/// Staging area: handle -> value ready to commit.
|
/// Staging area: handle -> value ready to commit.
|
||||||
staging: Arc<RwLock<HashMap<HandleId, Arc<T>>>>,
|
staging: Arc<RwLock<HashMap<HandleId, Arc<T>>>>,
|
||||||
@ -63,7 +63,7 @@ impl<T> BankPolicy<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try get a resident value by asset_id (dedupe path).
|
/// Try get a resident value by asset_id (dedupe path).
|
||||||
pub fn get_resident(&self, asset_id: u32) -> Option<Arc<T>> {
|
pub fn get_resident(&self, asset_id: AssetId) -> Option<Arc<T>> {
|
||||||
let mut map = self.resident.write().unwrap();
|
let mut map = self.resident.write().unwrap();
|
||||||
let entry = map.get_mut(&asset_id)?;
|
let entry = map.get_mut(&asset_id)?;
|
||||||
entry.last_used = Instant::now();
|
entry.last_used = Instant::now();
|
||||||
@ -71,7 +71,7 @@ impl<T> BankPolicy<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert or reuse a resident entry. Returns the resident Arc<T>.
|
/// Insert or reuse a resident entry. Returns the resident Arc<T>.
|
||||||
pub fn put_resident(&self, asset_id: u32, value: Arc<T>, bytes: usize) -> Arc<T> {
|
pub fn put_resident(&self, asset_id: AssetId, value: Arc<T>, bytes: usize) -> Arc<T> {
|
||||||
let mut map = self.resident.write().unwrap();
|
let mut map = self.resident.write().unwrap();
|
||||||
match map.get_mut(&asset_id) {
|
match map.get_mut(&asset_id) {
|
||||||
Some(existing) => {
|
Some(existing) => {
|
||||||
@ -104,8 +104,8 @@ impl<T> BankPolicy<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AssetManager {
|
pub struct AssetManager {
|
||||||
assets: Arc<RwLock<HashMap<u32, AssetEntry>>>,
|
assets: Arc<RwLock<HashMap<AssetId, AssetEntry>>>,
|
||||||
name_to_id: Arc<RwLock<HashMap<String, u32>>>,
|
name_to_id: Arc<RwLock<HashMap<String, AssetId>>>,
|
||||||
handles: Arc<RwLock<HashMap<HandleId, LoadHandleInfo>>>,
|
handles: Arc<RwLock<HashMap<HandleId, LoadHandleInfo>>>,
|
||||||
next_handle_id: Mutex<HandleId>,
|
next_handle_id: Mutex<HandleId>,
|
||||||
assets_data: Arc<RwLock<AssetsPayloadSource>>,
|
assets_data: Arc<RwLock<AssetsPayloadSource>>,
|
||||||
@ -115,8 +115,8 @@ pub struct AssetManager {
|
|||||||
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
||||||
|
|
||||||
/// Track what is installed in each hardware slot (for stats/info).
|
/// Track what is installed in each hardware slot (for stats/info).
|
||||||
gfx_slots: Arc<RwLock<[Option<u32>; 16]>>,
|
gfx_slots: Arc<RwLock<[Option<AssetId>; 16]>>,
|
||||||
sound_slots: Arc<RwLock<[Option<u32>; 16]>>,
|
sound_slots: Arc<RwLock<[Option<AssetId>; 16]>>,
|
||||||
|
|
||||||
/// Residency policy for GFX tile banks.
|
/// Residency policy for GFX tile banks.
|
||||||
gfx_policy: BankPolicy<TileBank>,
|
gfx_policy: BankPolicy<TileBank>,
|
||||||
@ -128,7 +128,7 @@ pub struct AssetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct LoadHandleInfo {
|
struct LoadHandleInfo {
|
||||||
_asset_id: u32,
|
_asset_id: AssetId,
|
||||||
slot: SlotRef,
|
slot: SlotRef,
|
||||||
status: LoadStatus,
|
status: LoadStatus,
|
||||||
}
|
}
|
||||||
@ -238,8 +238,7 @@ impl AssetManager {
|
|||||||
for item in preload {
|
for item in preload {
|
||||||
let entry_opt = {
|
let entry_opt = {
|
||||||
let assets = self.assets.read().unwrap();
|
let assets = self.assets.read().unwrap();
|
||||||
let name_to_id = self.name_to_id.read().unwrap();
|
assets.get(&item.asset_id).cloned()
|
||||||
name_to_id.get(&item.asset_name).and_then(|id| assets.get(id)).cloned()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(entry) = entry_opt {
|
if let Some(entry) = entry_opt {
|
||||||
@ -302,10 +301,7 @@ impl AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else {
|
// else {
|
||||||
// eprintln!(
|
// eprintln!("[AssetManager] Preload failed: asset id '{}' not found in table", item.asset_id);
|
||||||
// "[AssetManager] Preload failed: asset '{}' not found in table",
|
|
||||||
// item.asset_name
|
|
||||||
// );
|
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1047,7 +1043,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let preload = vec![PreloadEntry { asset_name: "preload_sound".to_string(), slot: 5 }];
|
let preload = vec![PreloadEntry { asset_id: 2, slot: 5 }];
|
||||||
|
|
||||||
let am = AssetManager::new(
|
let am = AssetManager::new(
|
||||||
vec![],
|
vec![],
|
||||||
@ -1076,7 +1072,7 @@ mod tests {
|
|||||||
let mut asset_entry = test_tile_asset_entry("my_tiles", data.len());
|
let mut asset_entry = test_tile_asset_entry("my_tiles", data.len());
|
||||||
asset_entry.asset_id = 10;
|
asset_entry.asset_id = 10;
|
||||||
|
|
||||||
let preload = vec![PreloadEntry { asset_name: "my_tiles".to_string(), slot: 3 }];
|
let preload = vec![PreloadEntry { asset_id: 10, slot: 3 }];
|
||||||
|
|
||||||
let am = AssetManager::new(
|
let am = AssetManager::new(
|
||||||
vec![],
|
vec![],
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub type HandleId = u32;
|
pub type HandleId = u32;
|
||||||
|
pub type AssetId = i32;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@ -13,7 +14,7 @@ pub enum BankType {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct AssetEntry {
|
pub struct AssetEntry {
|
||||||
pub asset_id: u32,
|
pub asset_id: AssetId,
|
||||||
pub asset_name: String,
|
pub asset_name: String,
|
||||||
pub bank_type: BankType,
|
pub bank_type: BankType,
|
||||||
pub offset: u64,
|
pub offset: u64,
|
||||||
@ -25,7 +26,7 @@ pub struct AssetEntry {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct PreloadEntry {
|
pub struct PreloadEntry {
|
||||||
pub asset_name: String,
|
pub asset_id: AssetId,
|
||||||
pub slot: usize,
|
pub slot: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ pub struct BankStats {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct SlotStats {
|
pub struct SlotStats {
|
||||||
pub asset_id: Option<u32>,
|
pub asset_id: Option<AssetId>,
|
||||||
pub asset_name: Option<String>,
|
pub asset_name: Option<String>,
|
||||||
pub generation: u32,
|
pub generation: u32,
|
||||||
pub resident_bytes: usize,
|
pub resident_bytes: usize,
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use crate::asset::{AssetEntry, BankType};
|
||||||
use crate::cartridge::{
|
use crate::cartridge::{
|
||||||
ASSETS_PA_MAGIC, ASSETS_PA_PRELUDE_SIZE, ASSETS_PA_SCHEMA_VERSION, AssetsPackHeader,
|
ASSETS_PA_MAGIC, ASSETS_PA_PRELUDE_SIZE, ASSETS_PA_SCHEMA_VERSION, AssetsPackHeader,
|
||||||
AssetsPackPrelude, AssetsPayloadSource, Capability, Cartridge, CartridgeDTO, CartridgeError,
|
AssetsPackPrelude, AssetsPayloadSource, Capability, Cartridge, CartridgeDTO, CartridgeError,
|
||||||
@ -168,11 +169,29 @@ fn parse_assets_pack(path: &Path) -> Result<ParsedAssetsPack, CartridgeError> {
|
|||||||
file.read_exact(&mut header_bytes).map_err(|_| CartridgeError::InvalidFormat)?;
|
file.read_exact(&mut header_bytes).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||||
let header: AssetsPackHeader =
|
let header: AssetsPackHeader =
|
||||||
serde_json::from_slice(&header_bytes).map_err(|_| CartridgeError::InvalidFormat)?;
|
serde_json::from_slice(&header_bytes).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||||
|
validate_preload(&header.asset_table, &header.preload)?;
|
||||||
let payload_len = u64::try_from(file_len - payload_offset).map_err(|_| CartridgeError::InvalidFormat)?;
|
let payload_len = u64::try_from(file_len - payload_offset).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||||
|
|
||||||
Ok(ParsedAssetsPack { header, payload_offset: prelude.payload_offset, payload_len })
|
Ok(ParsedAssetsPack { header, payload_offset: prelude.payload_offset, payload_len })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_preload(asset_table: &[AssetEntry], preload: &[crate::asset::PreloadEntry]) -> Result<(), CartridgeError> {
|
||||||
|
let mut claimed_slots = HashSet::<(BankType, usize)>::new();
|
||||||
|
|
||||||
|
for item in preload {
|
||||||
|
let entry = asset_table
|
||||||
|
.iter()
|
||||||
|
.find(|entry| entry.asset_id == item.asset_id)
|
||||||
|
.ok_or(CartridgeError::InvalidFormat)?;
|
||||||
|
|
||||||
|
if !claimed_slots.insert((entry.bank_type, item.slot)) {
|
||||||
|
return Err(CartridgeError::InvalidFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -380,7 +399,7 @@ mod tests {
|
|||||||
let payload = vec![1_u8, 2, 3, 4];
|
let payload = vec![1_u8, 2, 3, 4];
|
||||||
dir.write_assets_pa(
|
dir.write_assets_pa(
|
||||||
vec![test_asset_entry(0, payload.len() as u64)],
|
vec![test_asset_entry(0, payload.len() as u64)],
|
||||||
vec![PreloadEntry { asset_name: "tiles".to_string(), slot: 2 }],
|
vec![PreloadEntry { asset_id: 7, slot: 2 }],
|
||||||
&payload,
|
&payload,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -396,9 +415,52 @@ mod tests {
|
|||||||
assert_eq!(cartridge.asset_table.len(), 1);
|
assert_eq!(cartridge.asset_table.len(), 1);
|
||||||
assert_eq!(cartridge.asset_table[0].asset_name, "tiles");
|
assert_eq!(cartridge.asset_table[0].asset_name, "tiles");
|
||||||
assert_eq!(cartridge.preload.len(), 1);
|
assert_eq!(cartridge.preload.len(), 1);
|
||||||
|
assert_eq!(cartridge.preload[0].asset_id, 7);
|
||||||
assert_eq!(cartridge.preload[0].slot, 2);
|
assert_eq!(cartridge.preload[0].slot, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_rejects_preload_with_missing_asset_id() {
|
||||||
|
let dir = TestCartridgeDir::new(manifest_with_capabilities(Some(vec!["asset"])));
|
||||||
|
dir.write_assets_pa(
|
||||||
|
vec![test_asset_entry(0, 4)],
|
||||||
|
vec![PreloadEntry { asset_id: 999, slot: 2 }],
|
||||||
|
&[1_u8, 2, 3, 4],
|
||||||
|
);
|
||||||
|
|
||||||
|
let error = DirectoryCartridgeLoader::load(dir.path()).unwrap_err();
|
||||||
|
|
||||||
|
assert!(matches!(error, CartridgeError::InvalidFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_rejects_preload_slot_clash_per_bank_type() {
|
||||||
|
let dir = TestCartridgeDir::new(manifest_with_capabilities(Some(vec!["asset"])));
|
||||||
|
let asset_table = vec![
|
||||||
|
test_asset_entry(0, 4),
|
||||||
|
AssetEntry {
|
||||||
|
asset_id: 8,
|
||||||
|
asset_name: "other_tiles".to_string(),
|
||||||
|
bank_type: BankType::TILES,
|
||||||
|
offset: 4,
|
||||||
|
size: 4,
|
||||||
|
decoded_size: 4,
|
||||||
|
codec: "RAW".to_string(),
|
||||||
|
metadata: json!({
|
||||||
|
"tile_size": 16,
|
||||||
|
"width": 16,
|
||||||
|
"height": 16
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let preload = vec![PreloadEntry { asset_id: 7, slot: 2 }, PreloadEntry { asset_id: 8, slot: 2 }];
|
||||||
|
dir.write_assets_pa(asset_table, preload, &[1_u8, 2, 3, 4, 5, 6, 7, 8]);
|
||||||
|
|
||||||
|
let error = DirectoryCartridgeLoader::load(dir.path()).unwrap_err();
|
||||||
|
|
||||||
|
assert!(matches!(error, CartridgeError::InvalidFormat));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_rejects_invalid_assets_pa_prelude() {
|
fn load_rejects_invalid_assets_pa_prelude() {
|
||||||
let dir = TestCartridgeDir::new(manifest_with_capabilities(Some(vec!["asset"])));
|
let dir = TestCartridgeDir::new(manifest_with_capabilities(Some(vec!["asset"])));
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
# Agenda 024 - `assets.pa` Preload and Asset Table ID-based Contract (Fechada)
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Fechada pela decisao:
|
||||||
|
|
||||||
|
- `../decisions/012-assets-preload-asset-id-contract.md`
|
||||||
|
|
||||||
|
## O Que Foi Fechado
|
||||||
|
|
||||||
|
1. `PreloadEntry` deixa de usar `asset_name` e passa a ser normativamente `{ asset_id, slot }`.
|
||||||
|
2. `asset_id` passa a ser o identificador operacional de preload com semantica de `java int`.
|
||||||
|
3. `asset_name` permanece apenas como metadata descritiva/debug e nao participa do preload.
|
||||||
|
4. `preload` deve ser validado contra a `asset_table` do proprio `assets.pa` durante o bootstrap.
|
||||||
|
5. `asset_id` ausente da `asset_table` e clash de slot por tipo caracterizam erro estrutural de formacao do cart.
|
||||||
|
6. Nao existe compatibilidade normativa para preload legado por `asset_name`.
|
||||||
|
|
||||||
|
## Efeito Pratico
|
||||||
|
|
||||||
|
- `spec 15` passa a precisar explicitar o shape normativo de `PreloadEntry` e a validacao estrutural de preload.
|
||||||
|
- loader, parser de `assets.pa` e `AssetManager` passam a consumir preload por `asset_id`.
|
||||||
|
- a modelagem atual em codigo baseada em `u32` para `asset_id` precisa ser revisitada onde o contrato exposto ainda assume unsigned.
|
||||||
|
|
||||||
|
## Follow-up
|
||||||
|
|
||||||
|
- propagar a decisao em `specs 13/15`;
|
||||||
|
- criar suporte de parse/validacao estrutural para preload por `asset_id`;
|
||||||
|
- remover o preload por `asset_name` do runtime e dos testes;
|
||||||
|
- manter `asset.load(name, kind, slot)` inalterado ate decisao futura sobre a superficie VM-facing.
|
||||||
@ -24,7 +24,6 @@ As agendas atuais são:
|
|||||||
- `020-perf-host-debug-overlay-isolation.md`
|
- `020-perf-host-debug-overlay-isolation.md`
|
||||||
- `021-perf-vm-allocation-and-copy-pressure.md`
|
- `021-perf-vm-allocation-and-copy-pressure.md`
|
||||||
- `022-perf-cartridge-boot-and-program-ownership.md`
|
- `022-perf-cartridge-boot-and-program-ownership.md`
|
||||||
|
|
||||||
## Sequenciamento Recomendado [PERF]
|
## Sequenciamento Recomendado [PERF]
|
||||||
|
|
||||||
Ordem sugerida para discussao das agendas de performance:
|
Ordem sugerida para discussao das agendas de performance:
|
||||||
|
|||||||
157
docs/runtime/decisions/012-assets-preload-asset-id-contract.md
Normal file
157
docs/runtime/decisions/012-assets-preload-asset-id-contract.md
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
# Decision Record - `assets.pa` Preload Uses `asset_id`
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Contexto
|
||||||
|
|
||||||
|
A `decision 011` fechou o envelope runtime-facing de `assets.pa`:
|
||||||
|
|
||||||
|
- `assets.pa` e o contrato primario de assets do runtime;
|
||||||
|
- `asset_table` e `preload` vivem no header interno do artefato;
|
||||||
|
- `preload` e consumido apenas no bootstrap.
|
||||||
|
|
||||||
|
Essa decisao, no entanto, nao definiu a semantica de identidade de `PreloadEntry`.
|
||||||
|
|
||||||
|
O estado atual do runtime ainda mistura dois modelos:
|
||||||
|
|
||||||
|
- `AssetEntry` ja trata `asset_id` como identidade operacional do asset;
|
||||||
|
- `PreloadEntry` ainda usa `asset_name` como chave pratica de resolucao no boot.
|
||||||
|
|
||||||
|
Isso preserva uma dependencia estrutural de campo mutavel justamente na fase em que o cart deveria ser mais deterministicamente validavel.
|
||||||
|
|
||||||
|
## Decisao
|
||||||
|
|
||||||
|
### 1. `PreloadEntry` passa a ser `{ asset_id, slot }`
|
||||||
|
|
||||||
|
O shape normativo de preload em `assets.pa` passa a ser:
|
||||||
|
|
||||||
|
```text
|
||||||
|
PreloadEntry {
|
||||||
|
asset_id,
|
||||||
|
slot
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`asset_name` deixa de fazer parte do contrato operacional de preload.
|
||||||
|
|
||||||
|
### 2. `asset_id` tem semantica de `java int`
|
||||||
|
|
||||||
|
`asset_id` no contrato runtime-facing de assets passa a ser um inteiro de 32 bits com semantica de `java int`.
|
||||||
|
|
||||||
|
Qualquer representacao interna do runtime deve respeitar essa semantica e nao introduzir contrato unsigned por acidente.
|
||||||
|
|
||||||
|
### 3. `asset_name` permanece apenas como metadata descritiva
|
||||||
|
|
||||||
|
`asset_name` continua permitido em `AssetEntry` para:
|
||||||
|
|
||||||
|
- debug;
|
||||||
|
- observabilidade;
|
||||||
|
- tooling editorial;
|
||||||
|
- UX de inspecao.
|
||||||
|
|
||||||
|
Mas `asset_name` nao participa:
|
||||||
|
|
||||||
|
- da identidade normativa do asset;
|
||||||
|
- da validacao de preload;
|
||||||
|
- da resolucao operacional de preload.
|
||||||
|
|
||||||
|
### 4. `preload` deve ser validado contra a `asset_table`
|
||||||
|
|
||||||
|
Quando `assets.pa` existir, o runtime deve validar `preload` durante o bootstrap usando a `asset_table` carregada do mesmo artefato.
|
||||||
|
|
||||||
|
Validacoes minimas obrigatorias:
|
||||||
|
|
||||||
|
- todo `preload.asset_id` deve existir na `asset_table`;
|
||||||
|
- nao pode haver clash de slot por tipo.
|
||||||
|
|
||||||
|
### 5. Clash de slot por tipo e proibido
|
||||||
|
|
||||||
|
O namespace de slot de preload e derivado de `bank_type` da `asset_table`.
|
||||||
|
|
||||||
|
Portanto, depois de resolver cada `preload.asset_id` para sua respectiva `AssetEntry`, o cart e invalido se houver mais de uma entry de preload para o mesmo par:
|
||||||
|
|
||||||
|
```text
|
||||||
|
(bank_type, slot)
|
||||||
|
```
|
||||||
|
|
||||||
|
Isso vale mesmo que as entries apontem para o mesmo asset.
|
||||||
|
|
||||||
|
### 6. Nao ha compatibilidade normativa com preload legado por `asset_name`
|
||||||
|
|
||||||
|
O contrato do runtime nao oferece modo de compatibilidade para preload legado baseado em `asset_name`.
|
||||||
|
|
||||||
|
Artefatos que nao seguirem o shape novo devem ser considerados invalidos para o contrato vigente.
|
||||||
|
|
||||||
|
### 7. Falhas de preload invalido sao estruturais de cart
|
||||||
|
|
||||||
|
Os seguintes casos sao erros estruturais de formacao do cart:
|
||||||
|
|
||||||
|
- `preload.asset_id` ausente da `asset_table`;
|
||||||
|
- clash de slot por tipo dentro de `preload`.
|
||||||
|
|
||||||
|
Esses casos devem falhar no bootstrap do cart, antes da execucao normal, e nao pertencem ao espaco de `status` operacional de `asset.load`.
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
Esta decisao foi adotada porque:
|
||||||
|
|
||||||
|
- alinha preload com a identidade estavel do dominio de assets;
|
||||||
|
- remove dependencia de metadata mutavel no bootstrap;
|
||||||
|
- preserva `asset_table` como fonte de verdade unica para identidade e tipo;
|
||||||
|
- torna a validacao do cart mais deterministica;
|
||||||
|
- impede que compatibilidade legado vire contrato permanente por inercia.
|
||||||
|
|
||||||
|
O uso de `java int` foi escolhido para manter alinhamento de semantica de ID com a fronteira de linguagem/plataforma desejada, em vez de cristalizar um detalhe unsigned especifico da implementacao atual em Rust.
|
||||||
|
|
||||||
|
## Invariantes / Contrato
|
||||||
|
|
||||||
|
- `PreloadEntry` e normativamente `{ asset_id, slot }`.
|
||||||
|
- `asset_id` segue semantica de `java int`.
|
||||||
|
- `asset_name` nao tem autoridade operacional sobre preload.
|
||||||
|
- `preload` so pode referenciar assets existentes na `asset_table` do mesmo `assets.pa`.
|
||||||
|
- `preload` nao pode conter mais de uma ocupacao para o mesmo par `(bank_type, slot)`.
|
||||||
|
- falhas de validacao de preload sao erros estruturais de cart e devem abortar o bootstrap.
|
||||||
|
- `asset.load(name, kind, slot)` permanece fora do escopo desta decisao.
|
||||||
|
|
||||||
|
## Impactos
|
||||||
|
|
||||||
|
### Specs
|
||||||
|
|
||||||
|
- `15-asset-management.md` deve explicitar o shape normativo de `PreloadEntry`, a semantica de `asset_id` e as validacoes estruturais de preload.
|
||||||
|
- `13-cartridge.md` deve registrar que `assets.pa` invalido inclui preload com IDs ausentes ou clash de slot por tipo.
|
||||||
|
|
||||||
|
### Runtime
|
||||||
|
|
||||||
|
- `prometeu-hal/src/asset.rs` deve trocar `PreloadEntry.asset_name` por `asset_id`.
|
||||||
|
- tipos de `asset_id` expostos no contrato runtime-facing devem ser revisados para semantica de `java int`.
|
||||||
|
- o loader de `assets.pa` deve rejeitar preload invalido antes de iniciar preload.
|
||||||
|
- o `AssetManager` deve resolver preload diretamente por `asset_id`, sem ponte nome -> id.
|
||||||
|
|
||||||
|
### Tooling / Packer
|
||||||
|
|
||||||
|
- o produtor de `assets.pa` deve emitir preload apenas com `asset_id`.
|
||||||
|
- carts com preload legado por `asset_name` deixam de ser validos no contrato atual.
|
||||||
|
|
||||||
|
## Referencias
|
||||||
|
|
||||||
|
- `../agendas/024-assets-pa-preload-and-asset-table-id-based-contract.md`
|
||||||
|
- `../decisions/011-assets-pa-autocontained-runtime-contract.md`
|
||||||
|
- `../specs/13-cartridge.md`
|
||||||
|
- `../specs/15-asset-management.md`
|
||||||
|
- `../../crates/console/prometeu-hal/src/asset.rs`
|
||||||
|
- `../../crates/console/prometeu-hal/src/cartridge_loader.rs`
|
||||||
|
- `../../crates/console/prometeu-drivers/src/asset.rs`
|
||||||
|
|
||||||
|
## Propagacao Necessaria
|
||||||
|
|
||||||
|
- criar PR/plan para atualizar `spec 13` e `spec 15`;
|
||||||
|
- criar PR/plan de codigo para:
|
||||||
|
- trocar o shape de `PreloadEntry` para `asset_id`;
|
||||||
|
- validar preload durante parse/bootstrap de `assets.pa`;
|
||||||
|
- rejeitar `asset_id` ausente da `asset_table`;
|
||||||
|
- rejeitar clash de slot por tipo;
|
||||||
|
- revisar a tipagem de `asset_id` para semantica de `java int`;
|
||||||
|
- atualizar testes de loader e `AssetManager`;
|
||||||
|
- remover a `agenda 024` da lista de agendas ativas.
|
||||||
@ -20,6 +20,7 @@ Decisoes ativas:
|
|||||||
- `006-vm-owned-stateful-core-contract.md`
|
- `006-vm-owned-stateful-core-contract.md`
|
||||||
- `007-filesystem-fault-core-policy.md`
|
- `007-filesystem-fault-core-policy.md`
|
||||||
- `011-assets-pa-autocontained-runtime-contract.md`
|
- `011-assets-pa-autocontained-runtime-contract.md`
|
||||||
|
- `012-assets-preload-asset-id-contract.md`
|
||||||
|
|
||||||
Decisoes implementadas e aposentadas (migradas para `learn/`):
|
Decisoes implementadas e aposentadas (migradas para `learn/`):
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
# PR 012 - Propagacao da Decision 012 para Preload por `asset_id`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Esta PR executa a `decision 012`, que fecha a identidade normativa de preload em `assets.pa`.
|
||||||
|
|
||||||
|
O objetivo e remover preload por `asset_name`, adotar `asset_id` com semantica de `java int`, e validar preload como parte estrutural da formacao do cart.
|
||||||
|
|
||||||
|
## Decisions de Origem
|
||||||
|
|
||||||
|
- `../decisions/011-assets-pa-autocontained-runtime-contract.md`
|
||||||
|
- `../decisions/012-assets-preload-asset-id-contract.md`
|
||||||
|
|
||||||
|
## Alvo
|
||||||
|
|
||||||
|
Propagar a decisao em specs e codigo do runtime para que:
|
||||||
|
|
||||||
|
- `PreloadEntry` seja `{ asset_id, slot }`;
|
||||||
|
- preload invalido falhe no bootstrap;
|
||||||
|
- o runtime nao dependa de nome para resolver preload.
|
||||||
|
|
||||||
|
## Escopo
|
||||||
|
|
||||||
|
- atualizar `spec 13` e `spec 15`;
|
||||||
|
- atualizar structs e parse do header de `assets.pa`;
|
||||||
|
- validar preload contra `asset_table`;
|
||||||
|
- detectar clash de slot por tipo;
|
||||||
|
- ajustar `AssetManager` e testes para preload por `asset_id`;
|
||||||
|
- revisar tipos expostos de `asset_id` para semantica de `java int`.
|
||||||
|
|
||||||
|
## Fora de Escopo
|
||||||
|
|
||||||
|
- mudar a superficie VM-facing de `asset.load(name, kind, slot)`;
|
||||||
|
- redesenhar `asset_table` alem do necessario para suportar a decisao;
|
||||||
|
- introduzir compatibilidade legado por `asset_name`;
|
||||||
|
- alterar politica de bancos/slots fora da validacao de preload.
|
||||||
|
|
||||||
|
## Plano de Execucao
|
||||||
|
|
||||||
|
1. Atualizar specs `13` e `15` com o shape normativo novo e as regras de validacao estrutural.
|
||||||
|
2. Trocar `PreloadEntry` em `prometeu-hal` para `asset_id`.
|
||||||
|
3. Revisar os pontos runtime-facing onde `asset_id` ainda esta fixado como unsigned.
|
||||||
|
4. Validar, no parse/bootstrap de `assets.pa`, que:
|
||||||
|
- todo preload referencia `asset_id` existente;
|
||||||
|
- nao existe duplicidade de `(bank_type, slot)`.
|
||||||
|
5. Remover a resolucao nome -> id do caminho de preload no `AssetManager`.
|
||||||
|
6. Atualizar testes de loader, parse e preload em memoria.
|
||||||
|
|
||||||
|
## Criterios de Aceite
|
||||||
|
|
||||||
|
- `assets.pa` com preload valido por `asset_id` carrega normalmente.
|
||||||
|
- `assets.pa` com `asset_id` ausente da `asset_table` falha no bootstrap.
|
||||||
|
- `assets.pa` com clash de slot por tipo falha no bootstrap.
|
||||||
|
- preload deixa de depender de `asset_name`.
|
||||||
|
- nenhuma spec vigente continua descrevendo preload por nome.
|
||||||
|
|
||||||
|
## Tests / Validacao
|
||||||
|
|
||||||
|
- teste de parse do header com `PreloadEntry { asset_id, slot }`;
|
||||||
|
- teste de rejeicao para `preload.asset_id` inexistente;
|
||||||
|
- teste de rejeicao para duplicidade de `(bank_type, slot)`;
|
||||||
|
- teste de inicializacao do `AssetManager` preloading por `asset_id`;
|
||||||
|
- revisao textual das specs `13` e `15` contra a `decision 012`.
|
||||||
|
|
||||||
|
## Riscos
|
||||||
|
|
||||||
|
- a troca de semantica de `asset_id` para `java int` pode expor assumptos unsigned espalhados no runtime;
|
||||||
|
- validacao estrutural no loader pode exigir ajuste em fixtures de teste existentes;
|
||||||
|
- se houver produtor externo ainda emitindo preload por `asset_name`, ele quebrara imediatamente, como mandado pela decisao.
|
||||||
@ -38,3 +38,4 @@ Uma PR deste diretório deve:
|
|||||||
|
|
||||||
PRs propostas para execucao da rodada atual:
|
PRs propostas para execucao da rodada atual:
|
||||||
|
|
||||||
|
- `012-assets-preload-asset-id-propagation.md`
|
||||||
|
|||||||
@ -109,6 +109,13 @@ The JSON header carries:
|
|||||||
|
|
||||||
`asset_table` and `preload` are therefore no longer the primary runtime contract of `manifest.json`.
|
`asset_table` and `preload` are therefore no longer the primary runtime contract of `manifest.json`.
|
||||||
|
|
||||||
|
`preload` is structurally validated as part of `assets.pa` bootstrap:
|
||||||
|
|
||||||
|
- each preload entry is `{ asset_id, slot }`;
|
||||||
|
- `asset_id` uses 32-bit signed integer semantics;
|
||||||
|
- each `preload.asset_id` must resolve to an entry in the same `asset_table`;
|
||||||
|
- no two preload entries may claim the same `(bank_type, slot)` pair once `bank_type` is resolved from `asset_table`.
|
||||||
|
|
||||||
Runtime lifecycle:
|
Runtime lifecycle:
|
||||||
|
|
||||||
- `asset_table` is loaded from `assets.pa` and remains live while the cartridge is running;
|
- `asset_table` is loaded from `assets.pa` and remains live while the cartridge is running;
|
||||||
@ -119,6 +126,7 @@ Runtime loading policy:
|
|||||||
|
|
||||||
- cartridges without `assets.pa` remain valid when `Asset` capability is not declared;
|
- cartridges without `assets.pa` remain valid when `Asset` capability is not declared;
|
||||||
- cartridges that declare `Asset` capability must provide valid `assets.pa`;
|
- cartridges that declare `Asset` capability must provide valid `assets.pa`;
|
||||||
|
- `assets.pa` with invalid preload references or preload slot clashes is structurally invalid;
|
||||||
- failure must occur as early as cartridge bootstrap, before preload execution.
|
- failure must occur as early as cartridge bootstrap, before preload execution.
|
||||||
|
|
||||||
## 7 Runtime Forms
|
## 7 Runtime Forms
|
||||||
|
|||||||
@ -96,6 +96,8 @@ AssetEntry {
|
|||||||
|
|
||||||
This table describes content identity and storage layout, not live residency.
|
This table describes content identity and storage layout, not live residency.
|
||||||
|
|
||||||
|
`asset_id` is the stable runtime-facing asset identity and uses 32-bit signed integer semantics compatible with Java `int`.
|
||||||
|
|
||||||
`offset` is relative to the start of the payload region inside `assets.pa`.
|
`offset` is relative to the start of the payload region inside `assets.pa`.
|
||||||
|
|
||||||
## 5 Banks and Slots
|
## 5 Banks and Slots
|
||||||
@ -180,13 +182,33 @@ These metrics support debugging, telemetry, and certification-oriented inspectio
|
|||||||
|
|
||||||
`preload` is stored in the JSON header of `assets.pa`.
|
`preload` is stored in the JSON header of `assets.pa`.
|
||||||
|
|
||||||
|
The normative preload shape is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
PreloadEntry {
|
||||||
|
asset_id
|
||||||
|
slot
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
These preload entries are consumed during cartridge initialization so the asset manager can establish initial residency before normal execution flow.
|
These preload entries are consumed during cartridge initialization so the asset manager can establish initial residency before normal execution flow.
|
||||||
|
|
||||||
|
Validation rules:
|
||||||
|
|
||||||
|
- `preload` is resolved by `asset_id`, not by `asset_name`;
|
||||||
|
- every `preload.asset_id` must exist in the same `asset_table`;
|
||||||
|
- no two preload entries may resolve to the same `(bank_type, slot)` pair;
|
||||||
|
- legacy preload keyed by `asset_name` is invalid for the current contract.
|
||||||
|
|
||||||
Lifecycle rule:
|
Lifecycle rule:
|
||||||
|
|
||||||
- `preload` is boot-time input only;
|
- `preload` is boot-time input only;
|
||||||
- it does not need to remain live after initialization completes.
|
- it does not need to remain live after initialization completes.
|
||||||
|
|
||||||
|
Bootstrap rule:
|
||||||
|
|
||||||
|
- invalid preload is a structural cartridge error and must fail cartridge bootstrap before normal execution begins.
|
||||||
|
|
||||||
## 10 Relationship to Other Specs
|
## 10 Relationship to Other Specs
|
||||||
|
|
||||||
- [`13-cartridge.md`](13-cartridge.md) defines the cartridge package and the requirement that `assets.pa` carries its own asset header.
|
- [`13-cartridge.md`](13-cartridge.md) defines the cartridge package and the requirement that `assets.pa` carries its own asset header.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user