diff --git a/docs/badges/future-agi/future-agi.svg b/docs/badges/future-agi/future-agi.svg new file mode 100644 index 0000000..19f1c16 --- /dev/null +++ b/docs/badges/future-agi/future-agi.svg @@ -0,0 +1,13 @@ + +OIA: L2·L3·L4·L5·L6·L7·L9 · auto + + + + + + + + +OIA +L2·L3·L4·L5·L6·L7·L9 · auto + \ No newline at end of file diff --git a/docs/data/oia-matrix.json b/docs/data/oia-matrix.json index cb54951..748c2d1 100644 --- a/docs/data/oia-matrix.json +++ b/docs/data/oia-matrix.json @@ -1132,6 +1132,67 @@ "status": "pending", "submitted": null, "issue": 53 + }, + { + "name": "future-agi/future-agi", + "desc": "Open-source, end-to-end platform for evaluating, observing, and improving LLM and AI agent applications. Tracing · Evals", + "layers": [ + 0, + 0, + 2, + 2, + 2, + 2, + 2, + 2, + 0, + 2 + ], + "spans": [ + { + "label": "security", + "badge": "sec" + }, + { + "label": "sovereignty", + "badge": "sov" + }, + { + "label": "auditability", + "badge": "aud" + }, + { + "label": "provenance", + "badge": "prov" + }, + { + "label": "identity", + "badge": "idn" + } + ], + "notes": { + "2": "signal-detected centre of gravity (Sovereign Infrastructure)", + "3": "signal-detected centre of gravity (Agent Data Substrate)", + "4": "signal-detected centre of gravity (Model Training & Adaptation)", + "5": "signal-detected centre of gravity (Inference & Retrieval)", + "6": "signal-detected centre of gravity (Context & Knowledge)", + "7": "signal-detected centre of gravity (Orchestration & Workflow)", + "9": "signal-detected centre of gravity (Human & Browser Interface)" + }, + "narrative": "Auto-classified from a submission. Heuristic file-tree scan of 6000 of 12397 project files (58 scaffolding paths excluded; 6397 beyond the 6000-file scan cap, not read); language Python, pushed 2026-09-25. Centre of gravity L2, L3, L4, L5, L6, L7, L9; presence none. This is a structural signal pass, NOT a comprehension read, and awaits committee review before promotion.", + "category": "app", + "evidence": { + "t": "auto", + "n": "Heuristic file-tree scan of 6000 of 12397 project files on 2026-09-24 (58 scaffolding paths excluded; 6397 beyond the 6000-file scan cap, not read): structural signals (real files, not just names), not comprehension. Qualification gate: issues ✓, docs ✓, ADRs ✗, PRD ✗, infographic ✓. Promote to code ✓ with a reader-agent audit and committee vote." + }, + "gaps": [ + "Committee: verify these auto-detected placements against the actual source (auto → code ✓).", + "Surface issue tracker + documentation + PRD/ADRs in the repo to meet the qualification gate.", + "Name the OIA layers/spans the project targets in its README." + ], + "status": "pending", + "submitted": "2026-09-24", + "issue": null } ] } diff --git a/docs/oia-matrix.html b/docs/oia-matrix.html index a8060ba..a520ca5 100644 --- a/docs/oia-matrix.html +++ b/docs/oia-matrix.html @@ -217,7 +217,14 @@

Sources

const hdr=layers.map(l=>`${l[0]}`).join(""); const mini=vals.map((v,i)=>`${v==2?'●':v==1?'·':'–'}`).join(""); const gk=Object.entries(gate).map(([k,v])=>`${k} ${v?'✓':'✗'}`).join(" · "); - s.innerHTML=`Auto-scan of ${corpus.kept} project files${corpus.dropped?` (${corpus.dropped} scaffolding paths excluded)`:''}${tj.truncated?' (tree truncated by GitHub)':''} · pushed ${(meta.pushed_at||'').slice(0,10)}
${hdr}
${mini}
Δ vs v0: ${diff.length?diff.join(", "):"no change"}
Spans detected: ${spans.join(", ")||"none"}
Qualification gate: ${gk}
Evidence: auto ⟳ heuristic file-tree scan — structural signals (checks real files, not just names), not comprehension. Promote to code ✓ with a reader-agent audit.`; + // #64: report scaffolding exclusion and scan-cap truncation separately. + const scanScope=corpus.truncated?`${corpus.kept} of ${corpus.kept+corpus.truncated}`:`${corpus.kept}`; + const scanParts=[]; + if(corpus.dropped) scanParts.push(`${corpus.dropped} scaffolding path${corpus.dropped===1?'':'s'} excluded`); + if(corpus.truncated) scanParts.push(`${corpus.truncated} beyond the ${OIA.SCAN_CAP}-file scan cap, not read`); + if(tj.truncated) scanParts.push('tree truncated by GitHub'); + const scanNote=scanParts.length?` (${scanParts.join('; ')})`:''; + s.innerHTML=`Auto-scan of ${scanScope} project files${scanNote} · pushed ${(meta.pushed_at||'').slice(0,10)}
${hdr}
${mini}
Δ vs v0: ${diff.length?diff.join(", "):"no change"}
Spans detected: ${spans.join(", ")||"none"}
Qualification gate: ${gk}
Evidence: auto ⟳ heuristic file-tree scan — structural signals (checks real files, not just names), not comprehension. Promote to code ✓ with a reader-agent audit.`; const bdg=document.querySelector('#r'+apps.indexOf(a)+' .badge'); if(bdg){bdg.className='badge auto';bdg.textContent='auto ⟳';} }catch(e){s.textContent="Scan failed (offline, CORS, or rate limit): "+e;} } diff --git a/docs/oia-signals.mjs b/docs/oia-signals.mjs index 386632e..80ed41b 100644 --- a/docs/oia-signals.mjs +++ b/docs/oia-signals.mjs @@ -79,16 +79,31 @@ export function denoisePaths(paths){ * on the matrix row for display only. Adding it would be a scope change that * promotes rows rather than de-noising them; propose it separately if wanted. */ +/** Upper bound on file paths read into the tree corpus. */ +export const SCAN_CAP = 6000; + export function buildCorpus({paths=[], readme='', topics=[], language=''} = {}){ - const lower = paths.map(p => String(p).toLowerCase()); - const kept = denoisePaths(lower).slice(0, 6000); - const tree = kept.join(' '); + const lower = paths.map(p => String(p).toLowerCase()); + const denoised = denoisePaths(lower); + const kept = denoised.slice(0, SCAN_CAP); + const tree = kept.join(' '); const doc = [ String(readme||'').toLowerCase().slice(0, 40000), (topics||[]).join(' ').toLowerCase(), String(language||'').toLowerCase(), ].join(' '); - return {tree, doc, all: tree + ' ' + doc, kept: kept.length, dropped: lower.length - kept.length}; + // `dropped` and `truncated` are deliberately separate (#64). They used to be + // summed into one counter computed against the pre-cap length, which the + // narrative then reported wholly as scaffolding. On a repo larger than + // SCAN_CAP that published a scaffolding count off by two orders of magnitude + // and hid the fact that most of the tree was never read at all. + // kept + dropped + truncated partitions the input. + return { + tree, doc, all: tree + ' ' + doc, + kept: kept.length, + dropped: lower.length - denoised.length, + truncated: denoised.length - kept.length, + }; } /** How many regexes in a group match the corpus (presence per regex, not total hits). */ diff --git a/scripts/classify-oia.mjs b/scripts/classify-oia.mjs index 18be6b9..f9855ec 100644 --- a/scripts/classify-oia.mjs +++ b/scripts/classify-oia.mjs @@ -6,7 +6,7 @@ // comprehension), never "code ✓". Usage: node scripts/classify-oia.mjs owner/name [issueNumber] [YYYY-MM-DD] import { pathToFileURL } from 'node:url'; -import { LAYER_NAMES, buildCorpus, classifySignals } from '../docs/oia-signals.mjs'; +import { LAYER_NAMES, SCAN_CAP, buildCorpus, classifySignals } from '../docs/oia-signals.mjs'; const NAME_RE=/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/; // GitHub owner/repo charset // Untrusted text (repo description) is later rendered via innerHTML on the page. @@ -40,15 +40,26 @@ export async function classify(name,issue,submitted){ const gate={issues:!!meta.has_issues,docs:paths.some(x=>/^docs?\//.test(x))||readme.length>800,ADRs:paths.some(x=>/adrs?\/|adr-\d/.test(x)),PRD:paths.some(x=>/prd/.test(x)),infographic:paths.some(x=>/infographic|\.svg$/.test(x))}; const gateStr=Object.entries(gate).map(([k,v])=>`${k} ${v?'✓':'✗'}`).join(', '); + // #64: scaffolding exclusion and scan-cap truncation are reported separately. + // Summing them and calling the total "scaffolding" overstated the count by two + // orders of magnitude on large repos and hid how much of the tree went unread. + const denoisedTotal=corpus.kept+corpus.truncated; + const scanScope=corpus.truncated?`${corpus.kept} of ${denoisedTotal}`:`${corpus.kept}`; + const scanParts=[]; + if(corpus.dropped) scanParts.push(`${corpus.dropped} scaffolding path${corpus.dropped===1?'':'s'} excluded`); + if(corpus.truncated) scanParts.push(`${corpus.truncated} beyond the ${SCAN_CAP}-file scan cap, not read`); + if(t.json.truncated) scanParts.push('tree truncated by GitHub'); + const scanNote=scanParts.length?` (${scanParts.join('; ')})`:''; + return { name, desc:clean(meta.description||name,120), layers, spans, notes, - narrative:`Auto-classified from a submission. Heuristic file-tree scan of ${corpus.kept} files${t.json.truncated?' (tree truncated)':''}${corpus.dropped?` (${corpus.dropped} scaffolding paths excluded)`:''}; language ${meta.language||'n/a'}, pushed ${(meta.pushed_at||'').slice(0,10)}. Centre of gravity ${cog.join(', ')||'none detected'}; presence ${pres.join(', ')||'none'}. This is a structural signal pass, NOT a comprehension read, and awaits committee review before promotion.`, + narrative:`Auto-classified from a submission. Heuristic file-tree scan of ${scanScope} project files${scanNote}; language ${meta.language||'n/a'}, pushed ${(meta.pushed_at||'').slice(0,10)}. Centre of gravity ${cog.join(', ')||'none detected'}; presence ${pres.join(', ')||'none'}. This is a structural signal pass, NOT a comprehension read, and awaits committee review before promotion.`, category:'app', - evidence:{t:'auto',n:`Heuristic file-tree scan of ${corpus.kept} project files on ${submitted||'submission'} (${corpus.dropped} scaffolding paths excluded): structural signals (real files, not just names), not comprehension. Qualification gate: ${gateStr}. Promote to code ✓ with a reader-agent audit and committee vote.`}, + evidence:{t:'auto',n:`Heuristic file-tree scan of ${scanScope} project files on ${submitted||'submission'}${scanNote}: structural signals (real files, not just names), not comprehension. Qualification gate: ${gateStr}. Promote to code ✓ with a reader-agent audit and committee vote.`}, gaps:['Committee: verify these auto-detected placements against the actual source (auto → code ✓).','Surface issue tracker + documentation + PRD/ADRs in the repo to meet the qualification gate.','Name the OIA layers/spans the project targets in its README.'], status:'pending', submitted:submitted||null, diff --git a/test/oia-signals.test.js b/test/oia-signals.test.js index 7e5ff96..bc53b20 100644 --- a/test/oia-signals.test.js +++ b/test/oia-signals.test.js @@ -13,7 +13,7 @@ import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, join } from 'node:path'; import { - LSIG, SSIG, BOILERPLATE, denoisePaths, buildCorpus, classifySignals, cnt, + LSIG, SSIG, BOILERPLATE, SCAN_CAP, denoisePaths, buildCorpus, classifySignals, cnt, } from '../docs/oia-signals.mjs'; const HERE = dirname(fileURLToPath(import.meta.url)); @@ -162,3 +162,43 @@ test('#50: the server classifier defines no signal maps of its own', () => { assert.doesNotMatch(src, /const\s+LSIG\s*=/, 'classify-oia.mjs must not redeclare LSIG'); assert.doesNotMatch(src, /const\s+SSIG\s*=/, 'classify-oia.mjs must not redeclare SSIG'); }); + +// --------------------------------------------------------------------------- +// Issue #64 defect 1 — scaffolding and scan-cap truncation are different things +// and must be counted separately. +// +// buildCorpus used to compute `dropped` against the pre-cap length, so the two +// were summed and the narrative attributed all of it to scaffolding. On +// future-agi/future-agi that published "6455 scaffolding paths excluded" when +// 58 were scaffolding and 6397 were simply beyond the scan cap: an overstatement +// of roughly 110x, and it concealed that only 48% of the repo was read. +// +// It stayed latent because every earlier submission was smaller than the cap, +// so `dropped` was 0 and the parenthetical never rendered. +// --------------------------------------------------------------------------- +test('#64: dropped counts scaffolding only, truncated counts the scan-cap overflow', () => { + const scaffolding = ['.github/workflows/ci.yml', 'package-lock.json', 'license']; + const overflow = 500; + const real = Array.from({ length: SCAN_CAP + overflow }, (_, i) => `src/mod${i}.ts`); + const c = buildCorpus({ paths: [...scaffolding, ...real] }); + + assert.equal(c.dropped, scaffolding.length, + 'dropped must count de-noised scaffolding only, never the scan-cap overflow'); + assert.equal(c.truncated, overflow, + 'truncated must count the paths discarded by the scan cap'); + assert.equal(c.kept, SCAN_CAP, 'kept is capped at SCAN_CAP'); +}); + +test('#64: a repo under the scan cap reports no truncation', () => { + const c = buildCorpus({ paths: ['.github/workflows/ci.yml', 'src/a.ts', 'src/b.ts'] }); + assert.equal(c.dropped, 1, 'the workflow file is scaffolding'); + assert.equal(c.truncated, 0, 'nothing was beyond the cap'); + assert.equal(c.kept, 2); +}); + +test('#64: kept + dropped + truncated accounts for every input path', () => { + const paths = ['license', ...Array.from({ length: SCAN_CAP + 7 }, (_, i) => `pkg/f${i}.py`)]; + const c = buildCorpus({ paths }); + assert.equal(c.kept + c.dropped + c.truncated, paths.length, + 'the three counters must partition the input, with nothing unaccounted for'); +});