dev/runtime-owned-variable-glyph-bank-palette-protocol #37
@ -191,10 +191,10 @@ impl GlyphAssetSlotIndex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const GLYPH_BANK_PALETTE_COUNT_V1: usize = 64;
|
const GLYPH_BANK_MAX_PALETTE_COUNT_V1: usize = 64;
|
||||||
const GLYPH_BANK_COLORS_PER_PALETTE: usize = 16;
|
const GLYPH_BANK_COLORS_PER_PALETTE: usize = 16;
|
||||||
const GLYPH_BANK_PALETTE_BYTES_V1: usize =
|
const GLYPH_BANK_PALETTE_BYTES_PER_PALETTE: usize =
|
||||||
GLYPH_BANK_PALETTE_COUNT_V1 * GLYPH_BANK_COLORS_PER_PALETTE * size_of::<u32>();
|
GLYPH_BANK_COLORS_PER_PALETTE * size_of::<u32>();
|
||||||
|
|
||||||
/// Resident metadata for a decoded/materialized asset inside a BankPolicy.
|
/// Resident metadata for a decoded/materialized asset inside a BankPolicy.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -599,7 +599,7 @@ impl AssetBridge for AssetManager {
|
|||||||
impl AssetManager {
|
impl AssetManager {
|
||||||
fn decode_glyph_bank_layout(
|
fn decode_glyph_bank_layout(
|
||||||
entry: &AssetEntry,
|
entry: &AssetEntry,
|
||||||
) -> Result<(TileSize, usize, usize, usize), String> {
|
) -> Result<(TileSize, usize, usize, usize, usize, usize), String> {
|
||||||
let meta = entry.metadata_as_glyph_bank()?;
|
let meta = entry.metadata_as_glyph_bank()?;
|
||||||
|
|
||||||
let tile_size = match meta.tile_size {
|
let tile_size = match meta.tile_size {
|
||||||
@ -609,7 +609,8 @@ impl AssetManager {
|
|||||||
_ => return Err(format!("Invalid tile_size: {}", meta.tile_size)),
|
_ => return Err(format!("Invalid tile_size: {}", meta.tile_size)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if meta.palette_count as usize != GLYPH_BANK_PALETTE_COUNT_V1 {
|
let palette_count = meta.palette_count as usize;
|
||||||
|
if !(1..=GLYPH_BANK_MAX_PALETTE_COUNT_V1).contains(&palette_count) {
|
||||||
return Err(format!("Invalid palette_count: {}", meta.palette_count));
|
return Err(format!("Invalid palette_count: {}", meta.palette_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -618,11 +619,14 @@ impl AssetManager {
|
|||||||
|
|
||||||
let logical_pixels = width.checked_mul(height).ok_or("GlyphBank dimensions overflow")?;
|
let logical_pixels = width.checked_mul(height).ok_or("GlyphBank dimensions overflow")?;
|
||||||
let serialized_pixel_bytes = logical_pixels.div_ceil(2);
|
let serialized_pixel_bytes = logical_pixels.div_ceil(2);
|
||||||
|
let palette_bytes = palette_count
|
||||||
|
.checked_mul(GLYPH_BANK_PALETTE_BYTES_PER_PALETTE)
|
||||||
|
.ok_or("GlyphBank palette size overflow")?;
|
||||||
let serialized_size = serialized_pixel_bytes
|
let serialized_size = serialized_pixel_bytes
|
||||||
.checked_add(GLYPH_BANK_PALETTE_BYTES_V1)
|
.checked_add(palette_bytes)
|
||||||
.ok_or("GlyphBank serialized size overflow")?;
|
.ok_or("GlyphBank serialized size overflow")?;
|
||||||
let decoded_size = logical_pixels
|
let decoded_size = logical_pixels
|
||||||
.checked_add(GLYPH_BANK_PALETTE_BYTES_V1)
|
.checked_add(palette_bytes)
|
||||||
.ok_or("GlyphBank decoded size overflow")?;
|
.ok_or("GlyphBank decoded size overflow")?;
|
||||||
|
|
||||||
if entry.size != serialized_size as u64 {
|
if entry.size != serialized_size as u64 {
|
||||||
@ -639,7 +643,7 @@ impl AssetManager {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((tile_size, width, height, serialized_pixel_bytes))
|
Ok((tile_size, width, height, serialized_pixel_bytes, palette_count, palette_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unpack_glyph_bank_pixels(packed_pixels: &[u8], logical_pixels: usize) -> Vec<u8> {
|
fn unpack_glyph_bank_pixels(packed_pixels: &[u8], logical_pixels: usize) -> Vec<u8> {
|
||||||
@ -1200,8 +1204,9 @@ impl AssetManager {
|
|||||||
entry: &AssetEntry,
|
entry: &AssetEntry,
|
||||||
buffer: &[u8],
|
buffer: &[u8],
|
||||||
) -> Result<GlyphBank, String> {
|
) -> Result<GlyphBank, String> {
|
||||||
let (tile_size, width, height, packed_pixel_bytes) = Self::decode_glyph_bank_layout(entry)?;
|
let (tile_size, width, height, packed_pixel_bytes, palette_count, palette_bytes) =
|
||||||
if buffer.len() < packed_pixel_bytes + GLYPH_BANK_PALETTE_BYTES_V1 {
|
Self::decode_glyph_bank_layout(entry)?;
|
||||||
|
if buffer.len() < packed_pixel_bytes + palette_bytes {
|
||||||
return Err("Buffer too small for GLYPHBANK".to_string());
|
return Err("Buffer too small for GLYPHBANK".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1209,10 +1214,10 @@ impl AssetManager {
|
|||||||
let packed_pixels = &buffer[0..packed_pixel_bytes];
|
let packed_pixels = &buffer[0..packed_pixel_bytes];
|
||||||
let pixel_indices = Self::unpack_glyph_bank_pixels(packed_pixels, logical_pixels);
|
let pixel_indices = Self::unpack_glyph_bank_pixels(packed_pixels, logical_pixels);
|
||||||
let palette_data =
|
let palette_data =
|
||||||
&buffer[packed_pixel_bytes..packed_pixel_bytes + GLYPH_BANK_PALETTE_BYTES_V1];
|
&buffer[packed_pixel_bytes..packed_pixel_bytes + palette_bytes];
|
||||||
|
|
||||||
let mut palettes =
|
let mut palettes =
|
||||||
vec![[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1];
|
vec![[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; palette_count];
|
||||||
for (p, pal) in palettes.iter_mut().enumerate() {
|
for (p, pal) in palettes.iter_mut().enumerate() {
|
||||||
for (c, slot) in pal.iter_mut().enumerate() {
|
for (c, slot) in pal.iter_mut().enumerate() {
|
||||||
let offset = (p * 16 + c) * 4;
|
let offset = (p * 16 + c) * 4;
|
||||||
@ -1232,7 +1237,8 @@ impl AssetManager {
|
|||||||
entry: &AssetEntry,
|
entry: &AssetEntry,
|
||||||
reader: &mut impl Read,
|
reader: &mut impl Read,
|
||||||
) -> Result<GlyphBank, String> {
|
) -> Result<GlyphBank, String> {
|
||||||
let (tile_size, width, height, packed_pixel_bytes) = Self::decode_glyph_bank_layout(entry)?;
|
let (tile_size, width, height, packed_pixel_bytes, palette_count, palette_bytes) =
|
||||||
|
Self::decode_glyph_bank_layout(entry)?;
|
||||||
let logical_pixels = width * height;
|
let logical_pixels = width * height;
|
||||||
let mut packed_pixels = vec![0_u8; packed_pixel_bytes];
|
let mut packed_pixels = vec![0_u8; packed_pixel_bytes];
|
||||||
reader
|
reader
|
||||||
@ -1241,13 +1247,13 @@ impl AssetManager {
|
|||||||
|
|
||||||
let pixel_indices = Self::unpack_glyph_bank_pixels(&packed_pixels, logical_pixels);
|
let pixel_indices = Self::unpack_glyph_bank_pixels(&packed_pixels, logical_pixels);
|
||||||
|
|
||||||
let mut palette_data = [0_u8; GLYPH_BANK_PALETTE_BYTES_V1];
|
let mut palette_data = vec![0_u8; palette_bytes];
|
||||||
reader
|
reader
|
||||||
.read_exact(&mut palette_data)
|
.read_exact(&mut palette_data)
|
||||||
.map_err(|_| "Buffer too small for GLYPHBANK".to_string())?;
|
.map_err(|_| "Buffer too small for GLYPHBANK".to_string())?;
|
||||||
|
|
||||||
let mut palettes =
|
let mut palettes =
|
||||||
vec![[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1];
|
vec![[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; palette_count];
|
||||||
for (p, pal) in palettes.iter_mut().enumerate() {
|
for (p, pal) in palettes.iter_mut().enumerate() {
|
||||||
for (c, slot) in pal.iter_mut().enumerate() {
|
for (c, slot) in pal.iter_mut().enumerate() {
|
||||||
let offset = (p * 16 + c) * 4;
|
let offset = (p * 16 + c) * 4;
|
||||||
@ -1883,35 +1889,56 @@ mod tests {
|
|||||||
use prometeu_hal::tile::Tile;
|
use prometeu_hal::tile::Tile;
|
||||||
use prometeu_hal::tilemap::TileMap;
|
use prometeu_hal::tilemap::TileMap;
|
||||||
|
|
||||||
fn expected_glyph_payload_size(width: usize, height: usize) -> usize {
|
fn expected_glyph_payload_size(width: usize, height: usize, palette_count: usize) -> usize {
|
||||||
(width * height).div_ceil(2) + GLYPH_BANK_PALETTE_BYTES_V1
|
(width * height).div_ceil(2) + (palette_count * GLYPH_BANK_PALETTE_BYTES_PER_PALETTE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expected_glyph_decoded_size(width: usize, height: usize) -> usize {
|
fn expected_glyph_decoded_size(width: usize, height: usize, palette_count: usize) -> usize {
|
||||||
width * height + GLYPH_BANK_PALETTE_BYTES_V1
|
width * height + (palette_count * GLYPH_BANK_PALETTE_BYTES_PER_PALETTE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_glyph_asset_data() -> Vec<u8> {
|
fn test_glyph_asset_data() -> Vec<u8> {
|
||||||
|
test_glyph_asset_data_with_palette_count(GLYPH_BANK_MAX_PALETTE_COUNT_V1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_glyph_asset_data_with_palette_count(palette_count: usize) -> Vec<u8> {
|
||||||
let mut data = vec![0x11u8; 128];
|
let mut data = vec![0x11u8; 128];
|
||||||
data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_BYTES_V1]);
|
data.extend_from_slice(&vec![
|
||||||
|
0u8;
|
||||||
|
palette_count * GLYPH_BANK_PALETTE_BYTES_PER_PALETTE
|
||||||
|
]);
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_glyph_asset_entry(asset_name: &str, width: usize, height: usize) -> AssetEntry {
|
fn test_glyph_asset_entry(asset_name: &str, width: usize, height: usize) -> AssetEntry {
|
||||||
|
test_glyph_asset_entry_with_palette_count(
|
||||||
|
asset_name,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
GLYPH_BANK_MAX_PALETTE_COUNT_V1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_glyph_asset_entry_with_palette_count(
|
||||||
|
asset_name: &str,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
palette_count: usize,
|
||||||
|
) -> AssetEntry {
|
||||||
AssetEntry {
|
AssetEntry {
|
||||||
asset_id: 0,
|
asset_id: 0,
|
||||||
asset_name: asset_name.to_string(),
|
asset_name: asset_name.to_string(),
|
||||||
bank_type: BankType::GLYPH,
|
bank_type: BankType::GLYPH,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: expected_glyph_payload_size(width, height) as u64,
|
size: expected_glyph_payload_size(width, height, palette_count) as u64,
|
||||||
decoded_size: expected_glyph_decoded_size(width, height) as u64,
|
decoded_size: expected_glyph_decoded_size(width, height, palette_count) as u64,
|
||||||
codec: AssetCodec::None,
|
codec: AssetCodec::None,
|
||||||
metadata: serde_json::json!({
|
metadata: serde_json::json!({
|
||||||
"tile_size": 16,
|
"tile_size": 16,
|
||||||
"width": width,
|
"width": width,
|
||||||
"height": height,
|
"height": height,
|
||||||
"palette_count": GLYPH_BANK_PALETTE_COUNT_V1,
|
"palette_count": palette_count,
|
||||||
"palette_authored": GLYPH_BANK_PALETTE_COUNT_V1
|
"palette_authored": palette_count
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2034,22 +2061,69 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decode_glyph_bank_unpacks_packed_pixels_and_reads_palette_colors() {
|
fn test_decode_glyph_bank_unpacks_packed_pixels_and_reads_palette_colors() {
|
||||||
let entry = test_glyph_asset_entry("glyphs", 2, 2);
|
let entry = test_glyph_asset_entry_with_palette_count("glyphs", 2, 2, 1);
|
||||||
let mut data = vec![0x10, 0x23];
|
let mut data = vec![0x10, 0x23];
|
||||||
data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_BYTES_V1]);
|
data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_BYTES_PER_PALETTE]);
|
||||||
data[2..6].copy_from_slice(&[0x12, 0x34, 0x56, 0x78]);
|
data[2..6].copy_from_slice(&[0x12, 0x34, 0x56, 0x78]);
|
||||||
|
|
||||||
let bank =
|
let bank =
|
||||||
AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("glyph decode");
|
AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("glyph decode");
|
||||||
|
|
||||||
assert_eq!(bank.pixel_indices, vec![1, 0, 2, 3]);
|
assert_eq!(bank.pixel_indices, vec![1, 0, 2, 3]);
|
||||||
|
assert_eq!(bank.palette_count(), 1);
|
||||||
assert_eq!(bank.palettes[0][0], Color::from_raw(0x12345678));
|
assert_eq!(bank.palettes[0][0], Color::from_raw(0x12345678));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_glyph_bank_accepts_intermediate_palette_count() {
|
||||||
|
let entry = test_glyph_asset_entry_with_palette_count("glyphs", 16, 16, 7);
|
||||||
|
let data = test_glyph_asset_data_with_palette_count(7);
|
||||||
|
|
||||||
|
let bank =
|
||||||
|
AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("glyph decode");
|
||||||
|
|
||||||
|
assert_eq!(bank.palette_count(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_glyph_bank_accepts_max_palette_count() {
|
||||||
|
let entry = test_glyph_asset_entry_with_palette_count(
|
||||||
|
"glyphs",
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
GLYPH_BANK_MAX_PALETTE_COUNT_V1,
|
||||||
|
);
|
||||||
|
let data = test_glyph_asset_data_with_palette_count(GLYPH_BANK_MAX_PALETTE_COUNT_V1);
|
||||||
|
|
||||||
|
let bank =
|
||||||
|
AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("glyph decode");
|
||||||
|
|
||||||
|
assert_eq!(bank.palette_count(), GLYPH_BANK_MAX_PALETTE_COUNT_V1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_glyph_bank_reader_matches_buffer_for_variable_palette_count() {
|
||||||
|
let entry = test_glyph_asset_entry_with_palette_count("glyphs", 16, 16, 3);
|
||||||
|
let data = test_glyph_asset_data_with_palette_count(3);
|
||||||
|
|
||||||
|
let from_buffer =
|
||||||
|
AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("buffer decode");
|
||||||
|
let mut reader = std::io::Cursor::new(data);
|
||||||
|
let from_reader =
|
||||||
|
AssetManager::decode_glyph_bank_from_reader(&entry, &mut reader).expect("reader decode");
|
||||||
|
|
||||||
|
assert_eq!(from_buffer.palette_count(), from_reader.palette_count());
|
||||||
|
assert_eq!(from_buffer.pixel_indices, from_reader.pixel_indices);
|
||||||
|
assert_eq!(from_buffer.palettes, from_reader.palettes);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decode_glyph_bank_rejects_short_packed_buffer() {
|
fn test_decode_glyph_bank_rejects_short_packed_buffer() {
|
||||||
let entry = test_glyph_asset_entry("glyphs", 16, 16);
|
let entry = test_glyph_asset_entry("glyphs", 16, 16);
|
||||||
let data = vec![0u8; expected_glyph_payload_size(16, 16) - 1];
|
let data = vec![
|
||||||
|
0u8;
|
||||||
|
expected_glyph_payload_size(16, 16, GLYPH_BANK_MAX_PALETTE_COUNT_V1) - 1
|
||||||
|
];
|
||||||
|
|
||||||
let err = match AssetManager::decode_glyph_bank_from_buffer(&entry, &data) {
|
let err = match AssetManager::decode_glyph_bank_from_buffer(&entry, &data) {
|
||||||
Ok(_) => panic!("glyph decode should reject short buffer"),
|
Ok(_) => panic!("glyph decode should reject short buffer"),
|
||||||
@ -2060,9 +2134,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decode_glyph_bank_requires_palette_count_64() {
|
fn test_decode_glyph_bank_rejects_palette_count_zero() {
|
||||||
let mut entry = test_glyph_asset_entry("glyphs", 16, 16);
|
let mut entry = test_glyph_asset_entry("glyphs", 16, 16);
|
||||||
entry.metadata["palette_count"] = serde_json::json!(32);
|
entry.metadata["palette_count"] = serde_json::json!(0);
|
||||||
|
|
||||||
let err =
|
let err =
|
||||||
match AssetManager::decode_glyph_bank_from_buffer(&entry, &test_glyph_asset_data()) {
|
match AssetManager::decode_glyph_bank_from_buffer(&entry, &test_glyph_asset_data()) {
|
||||||
@ -2070,7 +2144,44 @@ mod tests {
|
|||||||
Err(err) => err,
|
Err(err) => err,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(err, "Invalid palette_count: 32");
|
assert_eq!(err, "Invalid palette_count: 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_glyph_bank_rejects_palette_count_above_v1_max() {
|
||||||
|
let mut entry = test_glyph_asset_entry("glyphs", 16, 16);
|
||||||
|
entry.metadata["palette_count"] = serde_json::json!(65);
|
||||||
|
|
||||||
|
let err =
|
||||||
|
match AssetManager::decode_glyph_bank_from_buffer(&entry, &test_glyph_asset_data()) {
|
||||||
|
Ok(_) => panic!("glyph decode should reject invalid palette_count"),
|
||||||
|
Err(err) => err,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(err, "Invalid palette_count: 65");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_glyph_bank_rejects_mismatched_variable_decoded_size() {
|
||||||
|
let mut entry = test_glyph_asset_entry_with_palette_count("glyphs", 16, 16, 3);
|
||||||
|
entry.decoded_size += 1;
|
||||||
|
|
||||||
|
let err = match AssetManager::decode_glyph_bank_from_buffer(
|
||||||
|
&entry,
|
||||||
|
&test_glyph_asset_data_with_palette_count(3),
|
||||||
|
) {
|
||||||
|
Ok(_) => panic!("glyph decode should reject invalid decoded_size"),
|
||||||
|
Err(err) => err,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
err,
|
||||||
|
format!(
|
||||||
|
"Invalid GLYPHBANK decoded_size: expected {}, got {}",
|
||||||
|
expected_glyph_decoded_size(16, 16, 3),
|
||||||
|
expected_glyph_decoded_size(16, 16, 3) + 1
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -44,4 +44,4 @@
|
|||||||
{"type":"discussion","id":"DSC-0033","status":"done","ticket":"system-os-service-ownership-and-module-layout","title":"Agenda - SystemOS Service Ownership and Module Layout","created_at":"2026-05-14","updated_at":"2026-05-15","tags":["runtime","os","services","module-layout","vm","window-manager","logging"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0033-system-os-service-ownership-and-module-layout/LSN-0042-systemos-service-ownership-boundary.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]}
|
{"type":"discussion","id":"DSC-0033","status":"done","ticket":"system-os-service-ownership-and-module-layout","title":"Agenda - SystemOS Service Ownership and Module Layout","created_at":"2026-05-14","updated_at":"2026-05-15","tags":["runtime","os","services","module-layout","vm","window-manager","logging"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0033-system-os-service-ownership-and-module-layout/LSN-0042-systemos-service-ownership-boundary.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]}
|
||||||
{"type":"discussion","id":"DSC-0036","status":"done","ticket":"prometeu-hub-ui-direction","title":"Agenda - Prometeu Hub UI Direction","created_at":"2026-05-15","updated_at":"2026-05-22","tags":["hub","ui","shell","system-apps","lifecycle","design-system"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0045","file":"discussion/lessons/DSC-0036-prometeu-hub-ui-direction/LSN-0045-hub-ui-slices-should-prove-os-boundaries.md","status":"done","created_at":"2026-05-22","updated_at":"2026-05-22"}]}
|
{"type":"discussion","id":"DSC-0036","status":"done","ticket":"prometeu-hub-ui-direction","title":"Agenda - Prometeu Hub UI Direction","created_at":"2026-05-15","updated_at":"2026-05-22","tags":["hub","ui","shell","system-apps","lifecycle","design-system"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0045","file":"discussion/lessons/DSC-0036-prometeu-hub-ui-direction/LSN-0045-hub-ui-slices-should-prove-os-boundaries.md","status":"done","created_at":"2026-05-22","updated_at":"2026-05-22"}]}
|
||||||
{"type":"discussion","id":"DSC-0037","status":"done","ticket":"rgba8888-framebuffer-and-pixel-format-direction","title":"Agenda - RGBA8888 Framebuffer and Pixel Format Direction","created_at":"2026-05-22","updated_at":"2026-05-23","tags":["gfx","framebuffer","rgb565","rgba8888","renderer","assets","host","backend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0046","file":"discussion/lessons/DSC-0037-rgba8888-framebuffer-and-pixel-format-direction/LSN-0046-pixel-format-contracts-must-move-as-one-surface.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23"}]}
|
{"type":"discussion","id":"DSC-0037","status":"done","ticket":"rgba8888-framebuffer-and-pixel-format-direction","title":"Agenda - RGBA8888 Framebuffer and Pixel Format Direction","created_at":"2026-05-22","updated_at":"2026-05-23","tags":["gfx","framebuffer","rgb565","rgba8888","renderer","assets","host","backend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0046","file":"discussion/lessons/DSC-0037-rgba8888-framebuffer-and-pixel-format-direction/LSN-0046-pixel-format-contracts-must-move-as-one-surface.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23"}]}
|
||||||
{"type":"discussion","id":"DSC-0046","status":"in_progress","ticket":"runtime-owned-variable-glyph-bank-palette-protocol","title":"Runtime-Owned Variable Glyph Bank Palette Protocol","created_at":"2026-07-14","updated_at":"2026-07-14","tags":["runtime","gfx","assets","glyph-bank","palette-serialization","protocol"],"agendas":[{"id":"AGD-0049","file":"AGD-0049-runtime-owned-variable-glyph-bank-palette-protocol.md","status":"accepted","created_at":"2026-07-14","updated_at":"2026-07-14"}],"decisions":[{"id":"DEC-0041","file":"DEC-0041-variable-glyph-bank-palette-protocol.md","status":"accepted","created_at":"2026-07-14","updated_at":"2026-07-14","ref_agenda":"AGD-0049"}],"plans":[{"id":"PLN-0167","file":"PLN-0167-spec-contract-update-for-variable-glyph-palettes.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0168","file":"PLN-0168-glyphbank-variable-palette-resident-model.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0169","file":"PLN-0169-asset-decode-validation-for-variable-glyph-palettes.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0170","file":"PLN-0170-composer-palette-reference-failure-semantics.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0171","file":"PLN-0171-variable-glyph-palette-tests-fixtures-and-residue-scan.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0172","file":"PLN-0172-runtime-spec-handoff-to-packer-and-studio.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]}],"lessons":[]}
|
{"type":"discussion","id":"DSC-0046","status":"in_progress","ticket":"runtime-owned-variable-glyph-bank-palette-protocol","title":"Runtime-Owned Variable Glyph Bank Palette Protocol","created_at":"2026-07-14","updated_at":"2026-07-14","tags":["runtime","gfx","assets","glyph-bank","palette-serialization","protocol"],"agendas":[{"id":"AGD-0049","file":"AGD-0049-runtime-owned-variable-glyph-bank-palette-protocol.md","status":"accepted","created_at":"2026-07-14","updated_at":"2026-07-14"}],"decisions":[{"id":"DEC-0041","file":"DEC-0041-variable-glyph-bank-palette-protocol.md","status":"accepted","created_at":"2026-07-14","updated_at":"2026-07-14","ref_agenda":"AGD-0049"}],"plans":[{"id":"PLN-0167","file":"PLN-0167-spec-contract-update-for-variable-glyph-palettes.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0168","file":"PLN-0168-glyphbank-variable-palette-resident-model.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0169","file":"PLN-0169-asset-decode-validation-for-variable-glyph-palettes.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0170","file":"PLN-0170-composer-palette-reference-failure-semantics.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0171","file":"PLN-0171-variable-glyph-palette-tests-fixtures-and-residue-scan.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]},{"id":"PLN-0172","file":"PLN-0172-runtime-spec-handoff-to-packer-and-studio.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0041"]}],"lessons":[]}
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
id: PLN-0169
|
id: PLN-0169
|
||||||
ticket: runtime-owned-variable-glyph-bank-palette-protocol
|
ticket: runtime-owned-variable-glyph-bank-palette-protocol
|
||||||
title: Asset Decode Validation for Variable Glyph Palettes
|
title: Asset Decode Validation for Variable Glyph Palettes
|
||||||
status: open
|
status: done
|
||||||
|
completed: 2026-07-14
|
||||||
created: 2026-07-14
|
created: 2026-07-14
|
||||||
ref_decisions: [DEC-0041]
|
ref_decisions: [DEC-0041]
|
||||||
tags: [runtime, assets, glyph-bank, decode, validation]
|
tags: [runtime, assets, glyph-bank, decode, validation]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user