🚨 AI協調開発の危機回避事例を論文化(paper-09)

「ん?大丈夫?」の一言がPython特化ハードコーディングを防いだ事例を記録。
Everything is Box哲学 vs 技術的正しさの綱渡りからの生還を分析。

- docs/research/paper-09-ai-collaboration-pitfall/ を新規作成
  - incident-analysis.md: Lowerer特殊化危機の詳細分析
  - ai-collaboration-lessons.md: AI協調開発の教訓
  - intuition-in-engineering.md: エンジニアの直感の価値
  - summary.md: 綱渡りからの生還まとめ
- 研究論文の1論文1フォルダ原則に従い整理
- Python統合関連の実装修正とビルド成功確認

🛡️ Generated with Claude Code
This commit is contained in:
Moe Charm
2025-08-30 08:54:15 +09:00
parent db265d7f29
commit 7a0f9bd432
103 changed files with 8697 additions and 909 deletions

View File

@ -200,11 +200,31 @@ mod enabled {
}
}
impl PluginBoxV2 {
pub fn instance_id(&self) -> u32 { self.inner.instance_id }
pub fn finalize_now(&self) { self.inner.finalize_now() }
pub fn is_finalized(&self) -> bool { self.inner.finalized.load(std::sync::atomic::Ordering::SeqCst) }
}
impl PluginBoxV2 {
pub fn instance_id(&self) -> u32 { self.inner.instance_id }
pub fn finalize_now(&self) { self.inner.finalize_now() }
pub fn is_finalized(&self) -> bool { self.inner.finalized.load(std::sync::atomic::Ordering::SeqCst) }
}
/// Public helper to construct a PluginBoxV2 from raw parts (for VM/JIT integration)
pub fn construct_plugin_box(
box_type: String,
type_id: u32,
invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
instance_id: u32,
fini_method_id: Option<u32>,
) -> PluginBoxV2 {
PluginBoxV2 {
box_type,
inner: std::sync::Arc::new(PluginHandleInner {
type_id,
invoke_fn,
instance_id,
fini_method_id,
finalized: std::sync::atomic::AtomicBool::new(false),
}),
}
}
/// Plugin loader v2
pub struct PluginLoaderV2 {
@ -222,7 +242,7 @@ impl PluginBoxV2 {
box_specs: RwLock<HashMap<(String,String), LoadedBoxSpec>>,
}
impl PluginLoaderV2 {
impl PluginLoaderV2 {
fn find_box_by_type_id<'a>(&'a self, config: &'a NyashConfigV2, toml_value: &'a toml::Value, type_id: u32) -> Option<(&'a str, &'a str)> {
for (lib_name, lib_def) in &config.libraries {
for box_name in &lib_def.boxes {
@ -472,6 +492,26 @@ impl PluginBoxV2 {
Ok(method.method_id)
}
/// Determine whether a method returns a Result (Ok/Err) wrapper.
pub fn method_returns_result(&self, box_type: &str, method_name: &str) -> bool {
let config = match &self.config { Some(c) => c, None => return false };
let lib_name = match self.find_lib_name_for_box(box_type) { Some(n) => n, None => return false };
// Prefer spec if present
if let Some(spec) = self.box_specs.read().unwrap().get(&(lib_name.clone(), box_type.to_string())) {
if let Some(m) = spec.methods.get(method_name) { return m.returns_result; }
}
// Fallback to central nyash.toml
let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml");
if let Ok(toml_content) = std::fs::read_to_string(cfg_path) {
if let Ok(toml_value) = toml::from_str::<toml::Value>(&toml_content) {
if let Some(bc) = config.get_box_config(&lib_name, box_type, &toml_value) {
return bc.methods.get(method_name).map(|m| m.returns_result).unwrap_or(false);
}
}
}
false
}
/// Invoke an instance method on a plugin box by name (minimal TLV encoding)
pub fn invoke_instance_method(
&self,
@ -490,10 +530,20 @@ impl PluginBoxV2 {
let cfg_path = self.config_path.as_ref().map(|s| s.as_str()).unwrap_or("nyash.toml");
let toml_content = std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?;
let toml_value: toml::Value = toml::from_str(&toml_content).map_err(|_| BidError::PluginError)?;
// Prefer spec-loaded type_id/method returns_result
// Prefer spec values; but if method isn't listed in spec, fallback to central nyash.toml for returns_result
let (type_id, returns_result) = if let Some(spec) = self.box_specs.read().unwrap().get(&(lib_name.clone(), box_type.to_string())) {
let tid = spec.type_id.unwrap_or_else(|| config.box_types.get(box_type).copied().unwrap_or(0));
let rr = spec.methods.get(method_name).map(|m| m.returns_result).unwrap_or(false);
let tid = spec
.type_id
.or_else(|| config.box_types.get(box_type).copied())
.ok_or(BidError::InvalidType)?;
let rr = if let Some(m) = spec.methods.get(method_name) {
m.returns_result
} else {
// Fallback to central config for method flags
if let Some(box_conf) = config.get_box_config(&lib_name, box_type, &toml_value) {
box_conf.methods.get(method_name).map(|m| m.returns_result).unwrap_or(false)
} else { false }
};
(tid, rr)
} else {
let box_conf = config.get_box_config(&lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?;
@ -537,8 +587,21 @@ impl PluginBoxV2 {
if let Ok(val_opt) = self.invoke_instance_method("IntegerBox", "get", p.inner.instance_id, &[]) {
if let Some(val_box) = val_opt { enc_owned = Some(val_box); enc_ref = enc_owned.as_ref().unwrap(); }
}
} else if p.box_type == "StringBox" {
// Prefer TLV string for expected string params
let need_string = if let Some(exp) = expected_args.as_ref() {
matches!(exp.get(idx), Some(crate::config::nyash_toml_v2::ArgDecl::Typed { kind, .. }) if kind == "string")
} else {
// Name fallback: allow coercion to string
true
};
if need_string {
if let Ok(val_opt) = self.invoke_instance_method("StringBox", "toUtf8", p.inner.instance_id, &[]) {
if let Some(val_box) = val_opt { enc_owned = Some(val_box); enc_ref = enc_owned.as_ref().unwrap(); }
}
}
}
// Future: StringBox/BoolBox/F64 plugin-to-primitive coercions
// Future: BoolBox/F64 coercions
}
// If schema exists, validate per expected kind
if let Some(exp) = expected_args.as_ref() {
@ -557,8 +620,16 @@ impl PluginBoxV2 {
}
"string" => {
if enc_ref.as_any().downcast_ref::<StringBox>().is_none() {
return Err(BidError::InvalidArgs);
// Attempt late coercion for plugin StringBox
if let Some(p) = enc_ref.as_any().downcast_ref::<PluginBoxV2>() {
if p.box_type == "StringBox" {
if let Ok(val_opt) = self.invoke_instance_method("StringBox", "toUtf8", p.inner.instance_id, &[]) {
if let Some(val_box) = val_opt { enc_owned = Some(val_box); enc_ref = enc_owned.as_ref().unwrap(); }
}
}
}
}
if enc_ref.as_any().downcast_ref::<StringBox>().is_none() { return Err(BidError::InvalidArgs); }
}
"int" | "i32" => {
if enc_ref.as_any().downcast_ref::<IntegerBox>().is_none() {
@ -692,6 +763,12 @@ impl PluginBoxV2 {
}
}
if let Some((tag, size, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(data) {
if dbg_on() {
let preview_len = std::cmp::min(payload.len(), 16);
let preview: Vec<String> = payload[..preview_len].iter().map(|b| format!("{:02X}", b)).collect();
eprintln!("[Plugin→VM] tag={} size={} preview={} returns_result={} for {}.{}",
tag, size, preview.join(" "), returns_result, box_type, method_name);
}
match tag {
1 if size == 1 => { // Bool
let b = crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false);
@ -748,13 +825,9 @@ impl PluginBoxV2 {
6 | 7 => { // String/Bytes
let s = crate::runtime::plugin_ffi_common::decode::string(payload);
if dbg_on() { eprintln!("[Plugin→VM] return str/bytes len={} (returns_result={})", size, returns_result); }
if returns_result {
// Heuristic: for Result-returning methods, string payload represents an error message
let err = crate::exception_box::ErrorBox::new(&s);
Some(Box::new(crate::boxes::result::NyashResultBox::new_err(Box::new(err))) as Box<dyn NyashBox>)
} else {
Some(Box::new(StringBox::new(s)) as Box<dyn NyashBox>)
}
// Treat as Ok payload; Err is indicated by non-zero rc earlier
let val: Box<dyn NyashBox> = Box::new(StringBox::new(s));
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(val)) as Box<dyn NyashBox>) } else { Some(val) }
}
3 if size == 8 => { // I64
// Try decoding as i64 directly; also support legacy i32 payload size 4 when mis-encoded
@ -986,8 +1059,46 @@ impl PluginBoxV2 {
let mut output_buffer = vec![0u8; 1024]; // 1KB buffer for output
let mut output_len = output_buffer.len();
// Create TLV-encoded empty arguments (version=1, argc=0)
let tlv_args = crate::runtime::plugin_ffi_common::encode_empty_args();
// Encode constructor arguments (best-effort). Birth schemas are optional; we support common primitives.
let tlv_args = {
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(_args.len() as u16);
for (idx, a) in _args.iter().enumerate() {
// Coerce plugin integer/string primitive boxes to builtins first
let mut enc_ref: &Box<dyn NyashBox> = a;
let mut enc_owned: Option<Box<dyn NyashBox>> = None;
if let Some(p) = a.as_any().downcast_ref::<PluginBoxV2>() {
if p.box_type == "IntegerBox" {
if let Ok(val_opt) = self.invoke_instance_method("IntegerBox", "get", p.inner.instance_id, &[]) {
if let Some(val_box) = val_opt { enc_owned = Some(val_box); enc_ref = enc_owned.as_ref().unwrap(); }
}
} else if p.box_type == "StringBox" {
if let Ok(val_opt) = self.invoke_instance_method("StringBox", "toUtf8", p.inner.instance_id, &[]) {
if let Some(val_box) = val_opt { enc_owned = Some(val_box); enc_ref = enc_owned.as_ref().unwrap(); }
}
}
}
if let Some(i) = enc_ref.as_any().downcast_ref::<IntegerBox>() {
crate::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value);
continue;
}
if let Some(s) = enc_ref.as_any().downcast_ref::<StringBox>() {
crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value);
continue;
}
if let Some(b) = enc_ref.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
crate::runtime::plugin_ffi_common::encode::bool(&mut buf, b.value);
continue;
}
if let Some(f) = enc_ref.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() {
crate::runtime::plugin_ffi_common::encode::f64(&mut buf, f.value);
continue;
}
// Fallback: stringify
let sv = enc_ref.to_string_box().value;
crate::runtime::plugin_ffi_common::encode::string(&mut buf, &sv);
}
buf
};
eprintln!("🔍 Output buffer allocated, about to call plugin invoke_fn...");
let birth_result = unsafe {