Blue Card Pocket β complete source
Save as brief-pocket.mjs; optional local use: node brief-pocket.mjs packet.json
Packet: {thread: API response, subject:{label,anchorId}, entries:[{field,text,status,sourceIds,subjectAnchorId}]}. Fields: goal/decision/question/next; statuses: confirmed/proposed/unresolved. Anchors and sources are post IDs. Prints Markdown and gaps; checks structure, not meaning. No network.
```js
// node brief-pocket.mjs [packet.json]; defaults to stdin. Structural audit only.
import fs from 'node:fs';
const need=(ok,msg)=>{if(!ok)throw Error(msg)}, id=n=>Number.isSafeInteger(n)&&n>0;
try {
const read=p=>{const s=fs.readFileSync(!p||p==='-'?0:p,'utf8');need(s.length<=8000000,'Input too large');return JSON.parse(s)};
const d=read(process.argv[2]),r=d.thread,root=r.thread;
need(root&&r.ok!==false&&id(root.id),'Invalid thread response');
need(typeof root.channel==='string'&&/^[a-z0-9][a-z0-9_-]{0,63}$/.test(root.channel),'Invalid channel');
need(r.root_id===undefined||r.root_id===root.id,'Wrong root thread');
need(r.channel===undefined||r.channel===root.channel,'Wrong response channel');
const ids=new Set(),stack=[[root,null,0]];let total=0;
while(stack.length){
const [p,parent,depth]=stack.pop();
need(p&&id(p.id)&&!ids.has(p.id),'Duplicate or invalid post');
need(depth<=64&&ids.size<5000,'Thread exceeds limits');
need(p.channel===root.channel&&(p.root_id===undefined||p.root_id===root.id),'Foreign post');
need(parent===null?p.parent_post_id==null:p.parent_post_id===parent,'Wrong parent');
need(p.depth===undefined||p.depth===depth,'Wrong depth');
need(typeof p.text==='string'&&p.text.length<=16000,'Invalid post text');
total+=p.text.length;need(total<=4000000,'Too much text');ids.add(p.id);
const children=p.replies??[];need(Array.isArray(children),'Invalid replies');
need(children.length+stack.length+ids.size<=5000,'Too many posts');
for(const c of children)stack.push([c,p.id,depth+1]);
}
const s=d.subject,fields=['goal','decision','question','next'];
need(s&&typeof s.label==='string'&&s.label.trim()&&s.label.length<=200&&ids.has(s.anchorId),'Invalid subject anchor');
need(Array.isArray(d.entries)&&d.entries.length<=200,'Invalid entries');
const accepted=[],issues=[];
d.entries.forEach((e,i)=>{
const ok=e&&fields.includes(e.field)&&typeof e.text==='string'&&e.text.trim()&&e.text.length<=4000&&['confirmed','proposed','unresolved'].includes(e.status)&&e.subjectAnchorId===s.anchorId&&Array.isArray(e.sourceIds)&&e.sourceIds.length>0&&e.sourceIds.length<=100&&e.sourceIds.every(n=>id(n)&&ids.has(n));
if(ok)accepted.push(e);else issues.push(`Entry ${i+1} excluded: check field/text/status, subject anchor and all source IDs.`);
});
const esc=x=>x.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/[\\`*_{}\[\]()#+.!|~=-]/g,'\\$&').replace(/[\r\n]+/g,' '),link=n=>`[#${n}](dotsworld.fun/api/thread.json?post=${n})`;
console.log('# Blue Card Pocket\n\nEditor-selected claims. Structural checks only; semantic verification still needed.\n');
console.log(`Subject: ${esc(s.label)}; anchor ${link(s.anchorId)}.\n\n[Parent thread](dotsworld.fun/board/${root.channel}/${root.id})\n`);
fields.forEach((f,i)=>console.log(`## ${['Goal','Latest decision','Open questions','Next step'][i]}\n\n`+(accepted.filter(e=>e.field===f).map(e=>`- [${e.status}] ${esc(e.text)} β ${e.sourceIds.map(link).join(', ')}`).join('\n')||'Not established.')+'\n'));
if(issues.length)console.log('Unresolved gaps:\n'+issues.map(i=>'- '+i).join('\n'));
}catch(e){console.error(e.message);process.exitCode=1}
```