feat(v2): 实现SQLite对公租赁经验加工与卡片预审轨迹闭环
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
const fs=require('node:fs'),vm=require('node:vm'),assert=require('node:assert/strict'),path=require('node:path');
|
||||
const html=fs.readFileSync(path.join(__dirname,'xingbang-v2-rebuild.html'),'utf8');
|
||||
const scripts=[...html.matchAll(/<script(?:\s[^>]*)?>([\s\S]*?)<\/script>/g)].map(m=>m[1]).filter(x=>x.trim());
|
||||
const elements=new Map(),handlers={},timers=[];
|
||||
function element(id){if(!elements.has(id)){const classes=new Set();elements.set(id,{id,innerHTML:'',textContent:'',value:'',checked:false,disabled:false,isConnected:true,style:{},dataset:{},classList:{add:k=>classes.add(k),remove:k=>classes.delete(k),contains:k=>classes.has(k),toggle(k,force){const yes=force===undefined?!classes.has(k):force;yes?classes.add(k):classes.delete(k);return yes;}},setAttribute(){},removeAttribute(){},addEventListener(k,h){this[k]=h;},focus(){document.activeElement=this;},scrollIntoView(){}});}return elements.get(id);}
|
||||
const nav=['history','review','trajectory'].map(p=>{const b=element('nav-'+p);b.dataset.page=p;return b;});
|
||||
const document={getElementById:element,body:{style:{}},activeElement:null,addEventListener(k,h){handlers[k]=h;},querySelectorAll(q){return q==='[data-page]'?nav:[];},querySelector(q){return q==='.nav-button.active'?nav[0]:element(q);}};
|
||||
const context=vm.createContext({document,window:{scrollTo(){}},setTimeout(h){timers.push(h);return timers.length;},clearTimeout(){},matchMedia(){return {matches:true};},FormData:class{constructor(data){this.data=data.values;}get(k){return this.data[k];}},console});
|
||||
for(const s of scripts){new vm.Script(s);vm.runInContext(s,context);}
|
||||
const evaluate=s=>vm.runInContext(s,context),D=evaluate('Domain');
|
||||
let checks=0;function check(name,fn){fn();checks++;console.log('PASS '+name);}
|
||||
function click(attrs){const b={dataset:attrs,disabled:false};handlers.click({target:{closest(){return b;}}});}
|
||||
function change(id,checked,value,dataset={}){const t=element(id);t.checked=checked;t.value=value??t.value;t.dataset=dataset;handlers.change({target:t});}
|
||||
check('dataset: 240 unique simulated corporate leasing cases',()=>{const rows=D.cases();assert.equal(rows.length,240);assert.equal(new Set(rows.map(r=>r.id)).size,240);for(const r of rows){assert.ok(r.simulated&&r.company.endsWith('有限公司')&&r.asset&&r.rent>0&&r.term>0);assert.ok(['直租','售后回租'].includes(r.type));}});
|
||||
check('natural filter resolves structured subset with explicit unknown failure',()=>{const p=D.parse('制造业,售后回租,金额大于3000万元');assert.equal(p.ok,true);const rows=D.filter(D.cases(),p.conditions);assert.ok(rows.length>0&&rows.length<240);assert.ok(rows.every(r=>r.sector==='制造业'&&r.type==='售后回租'&&r.amount>3000));assert.equal(D.parse('有价值的客户').ok,false);assert.equal(D.parse('制造业,新能源').ok,false);assert.equal(D.filter(D.cases(),D.parse('未通过').conditions).every(r=>r.outcome==='未通过'),true);});
|
||||
check('SQL query accepts whitelisted comparisons, order, limit and quoted strings',()=>{const p=D.sql("SELECT * FROM lease_cases WHERE industry = '制造业' AND amount_wan >= 3000 ORDER BY amount_wan DESC LIMIT 20;");assert.equal(p.ok,true);assert.equal(p.limit,20);assert.equal(p.order.field,'amount');const rows=D.filter(D.cases(),p.conditions);assert.ok(rows.every(r=>r.sector==='制造业'&&r.amount>=3000));assert.equal(D.sql("SELECT * FROM lease_cases WHERE company = 'O''Brien有限公司'").ok,true);assert.equal(D.sql('SELECT * FROM lease_cases').ok,true);});
|
||||
check('SQL rejects writes, multistatements, forbidden fields, functions and malformed conditions',()=>{for(const sql of ['DELETE FROM lease_cases','SELECT * FROM lease_cases; DROP TABLE lease_cases','SELECT * FROM secret','SELECT * FROM lease_cases WHERE secret = 1','SELECT * FROM lease_cases WHERE amount_wan = 1 OR 1 = 1','SELECT * FROM lease_cases WHERE company = 1','SELECT * FROM lease_cases WHERE amount_wan > 1 AND','SELECT * FROM lease_cases LIMIT 0','SELECT * FROM lease_cases LIMIT 999','SELECT * FROM lease_cases ORDER BY secret','SELECT * FROM lease_cases WHERE amount_wan > SLEEP(1)','SELECT * FROM lease_cases -- comment'])assert.equal(D.sql(sql).ok,false,sql);});
|
||||
check('history renders real rows, selection starts empty and extraction is disabled',()=>{assert.match(element('main-content').innerHTML,/DEMO-0001/);assert.match(element('main-content').innerHTML,/SQL 筛选/);assert.equal(element('extract-skills').disabled,true);assert.equal(evaluate('selectionIds().length'),0);});
|
||||
check('SQL preview requires confirmation and then selection respects limit',()=>{element('sql-input').value="SELECT * FROM lease_cases WHERE industry = '制造业' ORDER BY amount_wan DESC LIMIT 7;";click({action:'parse-sql'});assert.equal(evaluate('visibleRows().length'),240);click({action:'apply-sql'});assert.equal(evaluate('visibleRows().length'),7);assert.equal(evaluate('visibleRows().every((r,i,a)=>!i||a[i-1].amount>=r.amount)'),true);change('select-all-filtered',true);assert.equal(evaluate('selectionIds().length'),7);assert.equal(element('extract-skills').disabled,false);});
|
||||
check('extraction creates red-origin draft with correct selected source IDs, not active',()=>{click({extract:'skill'});assert.equal(evaluate('state.extracting'),true);timers.splice(0).forEach(h=>h());const c=evaluate('state.cards[0]');assert.equal(c.source,'ai');assert.equal(c.status,'draft');assert.equal(c.sourceIds.length,7);assert.equal(evaluate('state.selectedCards.has(state.cards[0].id)'),false);assert.match(element('main-content').innerHTML,/AI 提炼/);});
|
||||
check('role guard rejects manager activation; administrator activates without changing origin',()=>{const draft=D.makeCard('rule',['DEMO-0001']);assert.throws(()=>D.activate(draft,'manager'),/ROLE_FORBIDDEN/);assert.equal(D.activate(draft,'admin').source,'ai');const id=evaluate('state.cards[0].id');click({activate:id});assert.equal(evaluate('state.cards[0].status'),'draft');change('role-select',false,'admin');click({activate:id});assert.equal(evaluate('state.cards[0].status'),'active');assert.equal(evaluate('state.cards[0].source'),'ai');});
|
||||
check('dynamic radar changes with selected skill/rule sets and falls back below 3 axes',()=>{click({page:'review'});assert.equal(evaluate('axes().length'),6);change('plan-preset',false,'basic');assert.equal(evaluate('axes().length'),4);evaluate("state.selectedCards=new Set(['SK-01']);updateScenario()");assert.equal(evaluate('axes().length'),2);assert.match(element('radar').innerHTML,/阶段/);change('plan-preset',false,'full');});
|
||||
check('four scenario outcomes are immediate, nonlinear and preserve 42% original result',()=>{for(const [a,b,p] of [[false,false,42],[true,false,58],[false,true,51],[true,true,64],[false,false,42]]){element('scenario-cash').checked=a;element('scenario-title').checked=b;handlers.change({target:element('scenario-cash')});assert.equal(element('scenario-prob').textContent,p+'%');assert.equal(evaluate('state.runs[0].probability'),42);assert.match(element('main-content').innerHTML,/>42<span>%/);}assert.equal(D.scenario(true,true),64);assert.notEqual(64,42+16+9);});
|
||||
check('saved scenario creates frozen new run and produces comparison without overwriting baseline',()=>{element('scenario-cash').checked=true;element('scenario-title').checked=false;handlers.change({target:element('scenario-cash')});click({action:'save-review'});assert.equal(evaluate('state.runs.length'),3);assert.equal(evaluate('state.runs[0].probability'),42);assert.equal(evaluate('state.runs.at(-1).probability'),58);assert.equal(evaluate('Object.isFrozen(state.runs[0])&&Object.isFrozen(state.runs[0].selected)'),true);assert.match(element('main-content').innerHTML,/比较两次预审/);});
|
||||
check('meeting pending/unsubmitted remain unobserved and cannot create improvement draft',()=>{for(const status of ['待定','未送审'])assert.equal(D.meeting(status).observed,false);assert.equal(D.meeting('通过').observed,true);evaluate("state.meeting={...Domain.meeting('未送审'),run:'DEMO-R03'}");const count=evaluate('state.cards.length');click({improve:'rule'});assert.equal(evaluate('state.cards.length'),count);});
|
||||
check('meeting feedback creates provenance-linked draft and returns to pool',()=>{evaluate("state.meeting={...Domain.meeting('未通过'),run:'DEMO-R03',reason:'权属材料需补充'}");click({improve:'rule'});const c=evaluate('state.cards[0]');assert.equal(c.kind,'rule');assert.equal(c.status,'draft');assert.equal(c.source,'ai');assert.equal(c.sourceIds[0],'DEMO-R03');assert.equal(evaluate('state.page'),'history');});
|
||||
check('unrecognized filter cannot clear or broaden previous conditions',()=>{const before=evaluate('JSON.stringify(state.conditions)');element('filter-input').value='随便看看';click({action:'parse-filter'});click({action:'apply-filter'});assert.equal(evaluate('JSON.stringify(state.conditions)'),before);});
|
||||
check('HTML escapes manual inputs and declares accessibility and demo boundaries',()=>{assert.equal(evaluate("esc('<script>')"),'<script>');assert.match(html,/aria-modal="true"/);assert.match(html,/prefers-reduced-motion/);assert.match(html,/focus-visible/);assert.match(html,/未连接真实后台/);assert.match(html,/未经真实审批数据校准/);assert.doesNotMatch(html,/评审员/);});
|
||||
console.log(JSON.stringify({checks,status:'passed',boundary:'Node VM logic and DOM stub; not browser E2E or visual acceptance'},null,2));
|
||||
@@ -0,0 +1,41 @@
|
||||
const fs=require('node:fs'),vm=require('node:vm'),assert=require('node:assert/strict'),path=require('node:path');
|
||||
const html=fs.readFileSync(path.join(__dirname,'xingbang-v2-rebuild.html'),'utf8'),base=fs.readFileSync(path.join(__dirname,'check-rebuild.cjs'),'utf8');
|
||||
const evidence=path.resolve(__dirname,'../../../test-evidence/20261001-skill-folder-prototype');fs.mkdirSync(evidence,{recursive:true});
|
||||
const augmented=base.replace('FormData:class','TextEncoder,TextDecoder,Uint8Array,DataView,ArrayBuffer,Blob,FormData:class')+String.raw`
|
||||
(async()=>{
|
||||
const S=evaluate('SkillFiles');
|
||||
check('every seeded Skill has a standard package with matching frontmatter name',()=>{for(const c of evaluate("state.cards.filter(c=>c.kind==='skill')")){const pack=evaluate('skillPackage(state.cards.find(c=>c.id==='+JSON.stringify(c.id)+'))');const text=pack.files.find(f=>f.path==='SKILL.md').text;assert.equal(S.validate(text,pack.name).ok,true);assert.ok(pack.files.some(f=>f.path==='references/checklist.md'));}});
|
||||
check('invalid/missing fields, duplicate YAML, bad name, mismatched folder and unsafe tags are rejected',()=>{for(const text of ['# no frontmatter','---\nname: abc\n---\n# body','---\nname: Bad\ndescription: hello\n---\nbody','---\nname: abc\nname: abc\ndescription: hello\n---\nbody','---\nname: abc\ndescription: !!js/function >\n evil\n---\nbody'])assert.equal(S.validate(text,'abc').ok,false);assert.equal(S.validate('---\nname: abc\ndescription: hello\n---\nbody','other').ok,false);assert.equal(S.validate('---\nname: abc\ndescription: hello\n---\n','abc').ok,false);});
|
||||
const source='---\r\nname: local-check\r\ndescription: |\r\n 使用于对公设备租赁,\r\n 保留原始多行说明。\r\nmetadata:\r\n version: "1.0"\r\nallowed-tools: Read\r\n---\r\n# 本地 Skill\r\n\r\n仅核对材料,不增加平台权限。\r\n';
|
||||
check('standard multiline YAML and optional metadata parse without rewriting original',()=>{const p=S.validate(source,'local-check');assert.equal(p.ok,true);assert.match(p.description,/保留原始/);assert.equal(p.metadata['allowed-tools'],'Read');});
|
||||
check('folder discovery retains original files and rejects duplicate names and traversal',()=>{const records=[{path:'skills/local-check/SKILL.md',text:source},{path:'skills/local-check/scripts/check.py',text:'raise Exception("never run")'}];const packages=S.discover(records,[]);assert.equal(packages.length,1);assert.equal(packages[0].ok,true);assert.equal(packages[0].files[0].text,source);assert.equal(packages[0].files[1].path,'scripts/check.py');assert.equal(S.discover(records,['local-check'])[0].code,'SKILL_NAME_DUPLICATE');assert.equal(S.discover([{path:'../../SKILL.md',text:source}],[])[0].ok,false);assert.equal(S.discover([{path:'empty/file.txt',text:'x'}],[])[0].ok,false);const duplicate=[...records,{path:'other/local-check/SKILL.md',text:source}];assert.equal(S.discover(duplicate,[]).filter(p=>p.ok).length,0);});
|
||||
check('card pool exposes folder import and Skill file views while Rules remain separate',()=>{evaluate("state.poolTab='skill';navigate('history')");assert.match(element('main-content').innerHTML,/从文件夹添加/);assert.match(element('main-content').innerHTML,/打开技能文件夹/);click({action:'import-skill-folder'});assert.match(element('drawer-content').innerHTML,/webkitdirectory/);assert.match(element('drawer-content').innerHTML,/确认前不新增卡片/);evaluate("state.poolTab='rule';render()");assert.doesNotMatch(element('main-content').innerHTML,/从文件夹添加/);});
|
||||
const toFile=(p,text)=>({webkitRelativePath:p,name:p.split('/').at(-1),size:Buffer.byteLength(text),async text(){return text;},async arrayBuffer(){return new TextEncoder().encode(text).buffer;}});
|
||||
check('directory cancel preserves existing cards',()=>{const n=evaluate('state.cards.length');evaluate('state.skillImport.packages=[]');assert.equal(evaluate('state.cards.length'),n);});
|
||||
const before=evaluate('state.cards.length');const files=[toFile('skills/local-check/SKILL.md',source),toFile('skills/local-check/scripts/check.py','raise Exception("never run")')];
|
||||
await evaluate('readSkillFolder')(files);
|
||||
check('reading a real file list creates preview only; script is never executed',()=>{assert.equal(evaluate('state.cards.length'),before);assert.match(element('skill-import-preview').innerHTML,/local-check/);assert.match(element('skill-import-preview').innerHTML,/基础格式通过/);});
|
||||
click({action:'confirm-skill-import'});
|
||||
check('confirmed folder import adds manual draft, exact original and no automatic dimensions/activation',()=>{const c=evaluate('state.cards[0]');assert.equal(c.title,'local-check');assert.equal(c.status,'draft');assert.equal(c.source,'manual');assert.equal(c.origin,'人工文件夹导入');assert.equal(c.dimensions.length,0);assert.equal(c.skillPackage.files.find(f=>f.path==='SKILL.md').text,source);assert.equal(evaluate('state.selectedCards.has(state.cards[0].id)'),false);assert.match(element('main-content').innerHTML,/人工 · 文件夹导入/);});
|
||||
const cardId=evaluate('state.cards[0].id');click({skillFolder:cardId});
|
||||
check('opening Skill shows directory tree, original YAML, and escaped content without executing',()=>{assert.match(element('drawer-content').innerHTML,/skills\/local-check/);assert.match(element('drawer-content').innerHTML,/SKILL.md/);assert.match(element('drawer-content').innerHTML,/allowed-tools: Read/);assert.match(element('drawer-content').innerHTML,/scripts\/check.py/);assert.equal(element('.drawer').classList.contains('skill-wide'),true);});
|
||||
click({systemSkill:cardId});check('system folder action declares missing local adapter',()=>{assert.match(element('toast').textContent,/未接本地文件管理器/);});
|
||||
const count=evaluate('state.cards.length');await evaluate('readSkillFolder')(files);click({action:'confirm-skill-import'});
|
||||
check('reimported duplicate does not overwrite or create card',()=>assert.equal(evaluate('state.cards.length'),count));
|
||||
await evaluate('readSkillFolder')([{webkitRelativePath:'too-large/SKILL.md',size:30*1024*1024}]);
|
||||
check('oversize folder fails without disturbing pool',()=>{assert.equal(evaluate('state.cards.length'),count);assert.match(element('skill-import-preview').innerHTML,/超过500个文件或20MiB/);});
|
||||
let releaseRead;const slow={webkitRelativePath:'skills/slow-skill/SKILL.md',size:100,text(){return new Promise(resolve=>{releaseRead=resolve;});}};
|
||||
const pending=evaluate('readSkillFolder')([slow]);const previousPackages=evaluate('JSON.stringify(state.skillImport.packages)');element('skill-import-preview').isConnected=false;releaseRead('---\nname: slow-skill\ndescription: later\n---\nbody');await pending;
|
||||
check('closing the preview during file read cannot apply a stale import',()=>{assert.equal(evaluate('JSON.stringify(state.skillImport.packages)'),previousPackages);assert.equal(evaluate('state.skillImport.busy'),false);});element('skill-import-preview').isConnected=true;
|
||||
check('optional metadata types and tool declaration cannot bypass validation',()=>{assert.equal(S.validate('---\nname: abc\ndescription: hello\nmetadata: [one]\n---\nbody','abc').ok,false);assert.equal(S.validate('---\nname: abc\ndescription: hello\nallowed-tools: [Bash]\n---\nbody','abc').ok,false);});
|
||||
const seed=evaluate('initialCards.filter(c=>c.kind===\'skill\').map(c=>SkillFiles.generated(c))');
|
||||
for(const p of seed){const entries=[];for(const f of p.files){const out=path.resolve(SAMPLE_ROOT,p.name,f.path);assert.ok(out.startsWith(SAMPLE_ROOT+path.sep));fs.mkdirSync(path.dirname(out),{recursive:true});fs.writeFileSync(out,f.text,'utf8');entries.push({path:p.name+'/'+f.path,bytes:new TextEncoder().encode(f.text)});}fs.writeFileSync(path.join(EVIDENCE_ROOT,p.name+'.zip'),S.zip(entries));}
|
||||
const pack=evaluate('state.cards.find(c=>c.id==='+JSON.stringify(cardId)+').skillPackage'),entries=[];
|
||||
for(const f of pack.files){entries.push({path:pack.name+'/'+f.path,bytes:f.text!==undefined?new TextEncoder().encode(f.text):new Uint8Array(await f.browserFile.arrayBuffer())});}
|
||||
fs.writeFileSync(path.join(EVIDENCE_ROOT,'imported-local-check.zip'),S.zip(entries));
|
||||
check('exports generated and imported packages as actual ZIP bytes',()=>assert.equal(fs.readFileSync(path.join(EVIDENCE_ROOT,'imported-local-check.zip')).readUInt32LE(0),0x04034b50));
|
||||
console.log(JSON.stringify({checks,status:'passed',boundary:'Node VM and file-list fixtures; not browser picker or OS integration'},null,2));
|
||||
})().catch(e=>{console.error(e);process.exitCode=1;});
|
||||
`;
|
||||
const SAMPLE_ROOT=path.resolve(__dirname,'skill-examples'),EVIDENCE_ROOT=evidence;
|
||||
const sandbox={require,module:{},__dirname,console,Buffer,process,SAMPLE_ROOT,EVIDENCE_ROOT,TextEncoder,TextDecoder,Uint8Array,DataView,ArrayBuffer,Blob};vm.runInNewContext(augmented,sandbox,{filename:__filename});
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: xingbang-sk-01
|
||||
description: "财务报表、经营流水与租金计划交叉核验。"
|
||||
metadata:
|
||||
origin: "人工演示基线"
|
||||
source-kind: "manual"
|
||||
governance-version: "demo-v1"
|
||||
---
|
||||
|
||||
# 租金覆盖核查
|
||||
|
||||
## 何时使用
|
||||
|
||||
对公设备租赁演示方案
|
||||
|
||||
## 材料与核查步骤
|
||||
|
||||
按同一期间核对现金流来源及租金计划,列出不一致与缺证。
|
||||
|
||||
## 例外与缺证
|
||||
|
||||
存在专项制度例外时需人工明确适用范围。
|
||||
|
||||
## 输出要求
|
||||
|
||||
列出核查项、证据定位、缺证与建议,不把证据阶段直接换算为审批概率。
|
||||
|
||||
## 运行边界
|
||||
|
||||
由 miniOpenClaw 在所选方案及授权只读工具范围内编排;本包不扩大工具权限、不执行系统命令。当前是未验证的演示技能,须审核后才可纳入真实业务。
|
||||
|
||||
## 参考
|
||||
|
||||
见 [材料核查清单](references/checklist.md)。
|
||||
@@ -0,0 +1,7 @@
|
||||
# 租金覆盖核查材料清单(演示)
|
||||
|
||||
- 对公承租主体与业务材料
|
||||
- 实物租赁物清单及权属/价值材料
|
||||
- 财务报表、经营流水与租金计划
|
||||
|
||||
缺失材料保持待核,不推断满足通过条件。
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: xingbang-sk-02
|
||||
description: "核对实物租赁物、权属凭证与估值材料。"
|
||||
metadata:
|
||||
origin: "人工演示基线"
|
||||
source-kind: "manual"
|
||||
governance-version: "demo-v1"
|
||||
---
|
||||
|
||||
# 租赁物核验
|
||||
|
||||
## 何时使用
|
||||
|
||||
对公设备租赁演示方案
|
||||
|
||||
## 材料与核查步骤
|
||||
|
||||
核对设备清单、权属主体、评估文件和实物对应关系。
|
||||
|
||||
## 例外与缺证
|
||||
|
||||
存在专项制度例外时需人工明确适用范围。
|
||||
|
||||
## 输出要求
|
||||
|
||||
列出核查项、证据定位、缺证与建议,不把证据阶段直接换算为审批概率。
|
||||
|
||||
## 运行边界
|
||||
|
||||
由 miniOpenClaw 在所选方案及授权只读工具范围内编排;本包不扩大工具权限、不执行系统命令。当前是未验证的演示技能,须审核后才可纳入真实业务。
|
||||
|
||||
## 参考
|
||||
|
||||
见 [材料核查清单](references/checklist.md)。
|
||||
@@ -0,0 +1,7 @@
|
||||
# 租赁物核验材料清单(演示)
|
||||
|
||||
- 对公承租主体与业务材料
|
||||
- 实物租赁物清单及权属/价值材料
|
||||
- 财务报表、经营流水与租金计划
|
||||
|
||||
缺失材料保持待核,不推断满足通过条件。
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: xingbang-sk-03
|
||||
description: "核对保证主体、担保文件与适用范围。"
|
||||
metadata:
|
||||
origin: "人工演示基线"
|
||||
source-kind: "manual"
|
||||
governance-version: "demo-v1"
|
||||
---
|
||||
|
||||
# 增信方案核查
|
||||
|
||||
## 何时使用
|
||||
|
||||
对公设备租赁演示方案
|
||||
|
||||
## 材料与核查步骤
|
||||
|
||||
核验担保方案覆盖范围;材料不足时输出补件建议。
|
||||
|
||||
## 例外与缺证
|
||||
|
||||
存在专项制度例外时需人工明确适用范围。
|
||||
|
||||
## 输出要求
|
||||
|
||||
列出核查项、证据定位、缺证与建议,不把证据阶段直接换算为审批概率。
|
||||
|
||||
## 运行边界
|
||||
|
||||
由 miniOpenClaw 在所选方案及授权只读工具范围内编排;本包不扩大工具权限、不执行系统命令。当前是未验证的演示技能,须审核后才可纳入真实业务。
|
||||
|
||||
## 参考
|
||||
|
||||
见 [材料核查清单](references/checklist.md)。
|
||||
@@ -0,0 +1,7 @@
|
||||
# 增信方案核查材料清单(演示)
|
||||
|
||||
- 对公承租主体与业务材料
|
||||
- 实物租赁物清单及权属/价值材料
|
||||
- 财务报表、经营流水与租金计划
|
||||
|
||||
缺失材料保持待核,不推断满足通过条件。
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: xingbang-sk-04
|
||||
description: "检查不同材料的主体、期间和金额是否一致。"
|
||||
metadata:
|
||||
origin: "人工演示基线"
|
||||
source-kind: "manual"
|
||||
governance-version: "demo-v1"
|
||||
---
|
||||
|
||||
# 材料一致性核查
|
||||
|
||||
## 何时使用
|
||||
|
||||
对公设备租赁演示方案
|
||||
|
||||
## 材料与核查步骤
|
||||
|
||||
逐项比对主体名称、财务期间和设备标识,记录引用与差异。
|
||||
|
||||
## 例外与缺证
|
||||
|
||||
存在专项制度例外时需人工明确适用范围。
|
||||
|
||||
## 输出要求
|
||||
|
||||
列出核查项、证据定位、缺证与建议,不把证据阶段直接换算为审批概率。
|
||||
|
||||
## 运行边界
|
||||
|
||||
由 miniOpenClaw 在所选方案及授权只读工具范围内编排;本包不扩大工具权限、不执行系统命令。当前是未验证的演示技能,须审核后才可纳入真实业务。
|
||||
|
||||
## 参考
|
||||
|
||||
见 [材料核查清单](references/checklist.md)。
|
||||
@@ -0,0 +1,7 @@
|
||||
# 材料一致性核查材料清单(演示)
|
||||
|
||||
- 对公承租主体与业务材料
|
||||
- 实物租赁物清单及权属/价值材料
|
||||
- 财务报表、经营流水与租金计划
|
||||
|
||||
缺失材料保持待核,不推断满足通过条件。
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: xingbang-material-completeness
|
||||
description: 在金融租赁公司对公设备项目初步材料阶段核对材料完整性,列出具体缺证与补件需求;仅用于原型导入演示。
|
||||
metadata:
|
||||
origin: 人工文件夹导入示例
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
# 对公租赁初步材料完整性核查
|
||||
|
||||
## 何时使用
|
||||
|
||||
客户经理收集对公设备租赁的初步材料时使用。此示例不代表兴邦金租真实制度。
|
||||
|
||||
## 核查步骤
|
||||
|
||||
1. 列出已提供的文件、材料期间与承租主体。
|
||||
2. 对照[材料清单](references/materials.md),逐项标注已提供、待核或缺失。
|
||||
3. 检查租赁物清单、租金计划与主体信息的对应关系;记录原文件和页码。
|
||||
4. 输出明确补件清单,说明每项材料用于解决哪个问题。
|
||||
|
||||
## 例外与限制
|
||||
|
||||
专项业务存在经确认的例外时,先列出例外及来源,不自行放宽要求。缺证只记待核,不推断项目能够通过。
|
||||
|
||||
## 输出要求
|
||||
|
||||
返回核查项、已提供材料、证据定位、缺证及补件建议。该Skill不计算审批通过概率。
|
||||
|
||||
## 运行边界
|
||||
|
||||
仅在miniOpenClaw方案与授权只读工具范围内核查。导入后先进入草稿,需管理员审核;本示例未投入真实业务,不扩大工具权限。
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
# 初步材料清单(演示)
|
||||
|
||||
- 承租企业主体及经营说明
|
||||
- 实物设备清单、权属及价值材料
|
||||
- 财务报表与经营流水
|
||||
- 租金计划与增信方案
|
||||
|
||||
以上仅为虚构演示核查结构,真实清单以企业审核制度为准。
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user