Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/badges/future-agi/future-agi.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions docs/data/oia-matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
}
9 changes: 8 additions & 1 deletion docs/oia-matrix.html
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,14 @@ <h3>Sources</h3>
const hdr=layers.map(l=>`<span style="display:inline-block;width:28px;text-align:center;font-size:10px;color:var(--mut)">${l[0]}</span>`).join("");
const mini=vals.map((v,i)=>`<span title="${layers[i][1]}" style="display:inline-block;width:28px;text-align:center;color:${v==2?'var(--o)':v==1?'var(--tx)':'var(--line)'}">${v==2?'●':v==1?'·':'–'}</span>`).join("");
const gk=Object.entries(gate).map(([k,v])=>`${k} ${v?'✓':'✗'}`).join(" · ");
s.innerHTML=`<b>Auto-scan</b> 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)}<div style="margin:6px 0;font-family:ui-monospace,Menlo,monospace">${hdr}<br>${mini}</div>Δ vs v0: <b>${diff.length?diff.join(", "):"no change"}</b><br>Spans detected: ${spans.join(", ")||"none"}<br>Qualification gate: ${gk}<br><span style="color:var(--mut)">Evidence: <span class="badge auto">auto ⟳</span> heuristic file-tree scan — structural signals (checks real files, not just names), not comprehension. Promote to <b>code ✓</b> with a reader-agent audit.</span>`;
// #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=`<b>Auto-scan</b> of ${scanScope} project files${scanNote} · pushed ${(meta.pushed_at||'').slice(0,10)}<div style="margin:6px 0;font-family:ui-monospace,Menlo,monospace">${hdr}<br>${mini}</div>Δ vs v0: <b>${diff.length?diff.join(", "):"no change"}</b><br>Spans detected: ${spans.join(", ")||"none"}<br>Qualification gate: ${gk}<br><span style="color:var(--mut)">Evidence: <span class="badge auto">auto ⟳</span> heuristic file-tree scan — structural signals (checks real files, not just names), not comprehension. Promote to <b>code ✓</b> with a reader-agent audit.</span>`;
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;}
}
Expand Down
23 changes: 19 additions & 4 deletions docs/oia-signals.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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). */
Expand Down
17 changes: 14 additions & 3 deletions scripts/classify-oia.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
42 changes: 41 additions & 1 deletion test/oia-signals.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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');
});
Loading