Skip to content

Commit 41bec64

Browse files
SmithSmith
authored andcommitted
feat: Enhance drift detection and validation processes
- Added validation for canonical source matrix contract and project context registry mirror in `evals/validate.mjs`. - Updated `plans/project-context.md` to reflect changes in governance and project context metadata. - Introduced new entries in `session-outcomes.md` for tracking plan outcomes and lessons learned. - Removed obsolete `report.json`, `spike-search.js`, and `spike-search2.js` files. - Deleted outdated test output file `test-output.txt`. - Created `governance/canonical-source-matrix.json` to define canonical ownership mapping. - Established `governance/project-context-registry.json` as a source of truth for project context metadata.
1 parent f4ccb45 commit 41bec64

15 files changed

Lines changed: 934 additions & 572 deletions

.tmp-run.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

NOTES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
Repo-persistent active-objective state only. See `docs/agent-engineering/MEMORY-ARCHITECTURE.md` for the three-layer memory model; task-episodic history lives under `plans/artifacts/<task-slug>/`.
44

5-
- Active objective: plan editing policy reform complete; awaiting user review.
5+
- Active objective: capability-matrix registry follow-up cleanup is complete; runtime uses registry metadata and strict file-level anchor-map coverage.
66
- Blockers: none.
7-
- Pending: user review before any commit.
7+
- Pending: optional follow-up to investigate pre-existing capability-matrix live-tree drift flags surfaced by the smoke test.

evals/capability-matrix.mjs

Lines changed: 23 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Reconciles:
77
* - governance/tool-grants.json (meta keys filtered via key.startsWith('_'))
88
* - Each *.agent.md file's frontmatter (model_role, tools)
9-
* - plans/project-context.md executor / review-pipeline / Agent Role Matrix tables
9+
* - governance/project-context-registry.json executor / review-pipeline / Agent Role Matrix
1010
*
1111
* Exported helpers are covered by evals/tests/capability-matrix.test.mjs.
1212
*
@@ -59,77 +59,38 @@ export function parseAgentFrontmatter(content) {
5959
return { modelRole, tools };
6060
}
6161

62-
// ── parseProjectContextRoster ─────────────────────────────────────────────────
62+
// ── loadRosterFromRegistry ──────────────────────────────────────────────────────
6363

6464
/**
65-
* Parse plans/project-context.md to extract executor agents, review-pipeline
66-
* agents, and the Agent Role Matrix table.
65+
* Load executor/review roster and role matrix metadata from the machine-readable
66+
* governance registry (governance/project-context-registry.json).
6767
*
68-
* Recognised section headers (exact markdown `##` headings):
69-
* - "Phase Executor Agents"
70-
* - "Review Pipeline Agents"
71-
* - "Agent Role Matrix"
68+
* Maps registry snake_case fields to the camelCase roleMatrix shape used by
69+
* buildCapabilityMatrix:
70+
* schema_output → schemaOutput
71+
* tools_profile → toolsProfile
72+
* delegation_source → delegationSource
7273
*
73-
* Agent names are stored without the `.agent.md` suffix (matching table values).
74-
*
75-
* @param {string} content - Raw file content of project-context.md.
74+
* @param {string} registryPath - Absolute path to project-context-registry.json.
7675
* @returns {{
7776
* executors: string[],
7877
* reviewPipeline: string[],
7978
* roleMatrix: Map<string, { schemaOutput: string, toolsProfile: string, delegationSource: string }>
8079
* }}
8180
*/
82-
export function parseProjectContextRoster(content) {
83-
const lines = content.split('\n');
84-
85-
const executors = [];
86-
const reviewPipeline = [];
87-
const roleMatrix = new Map();
88-
89-
let section = null;
81+
export function loadRosterFromRegistry(registryPath) {
82+
const registry = JSON.parse(readFileSync(registryPath, 'utf8'));
9083

91-
for (const line of lines) {
92-
// Detect targeted section headers first (continue to skip generic ## check)
93-
if (/^##\s+Phase Executor Agents/.test(line)) {
94-
section = 'executors';
95-
continue;
96-
}
97-
if (/^##\s+Review Pipeline Agents/.test(line)) {
98-
section = 'reviewPipeline';
99-
continue;
100-
}
101-
if (/^##\s+Agent Role Matrix/.test(line)) {
102-
section = 'roleMatrix';
103-
continue;
104-
}
105-
// Any other ## heading exits the tracked sections
106-
if (/^##/.test(line)) {
107-
section = null;
108-
continue;
109-
}
84+
const executors = (registry.phase_executor_agents || []).map((e) => e.agent);
85+
const reviewPipeline = (registry.review_pipeline_agents || []).map((r) => r.agent);
11086

111-
if (!section || !line.includes('|')) continue;
112-
113-
// Skip markdown separator rows (only -, |, :, space)
114-
if (/^\s*\|[\s|:-]+\|\s*$/.test(line)) continue;
115-
116-
// Split pipe-bordered row; meaningful cells start at index 1
117-
const cells = line.split('|').map((c) => c.trim());
118-
if (cells.length < 3) continue;
119-
120-
const agentName = cells[1];
121-
if (!agentName || agentName === 'Agent') continue; // skip header row
122-
123-
if (section === 'executors') {
124-
executors.push(agentName);
125-
} else if (section === 'reviewPipeline') {
126-
reviewPipeline.push(agentName);
127-
} else if (section === 'roleMatrix') {
128-
const schemaOutput = cells[2] ? cells[2].trim() : '';
129-
const toolsProfile = cells[3] ? cells[3].trim() : '';
130-
const delegationSource = cells[4] ? cells[4].trim() : '';
131-
roleMatrix.set(agentName, { schemaOutput, toolsProfile, delegationSource });
132-
}
87+
const roleMatrix = new Map();
88+
for (const entry of registry.agent_role_matrix || []) {
89+
roleMatrix.set(entry.agent, {
90+
schemaOutput: entry.schema_output || '',
91+
toolsProfile: entry.tools_profile || '',
92+
delegationSource: entry.delegation_source || '',
93+
});
13394
}
13495

13596
return { executors, reviewPipeline, roleMatrix };
@@ -268,10 +229,10 @@ const isMain = process.argv[1] && resolve(process.argv[1]) === resolve(__filenam
268229

269230
if (isMain) {
270231
const grantsPath = join(ROOT, 'governance', 'tool-grants.json');
271-
const projectContextPath = join(ROOT, 'plans', 'project-context.md');
232+
const registryPath = join(ROOT, 'governance', 'project-context-registry.json');
272233

273234
const grants = JSON.parse(readFileSync(grantsPath, 'utf8'));
274-
const roster = parseProjectContextRoster(readFileSync(projectContextPath, 'utf8'));
235+
const roster = loadRosterFromRegistry(registryPath);
275236

276237
const agents = new Map();
277238
for (const agentFile of Object.keys(grants)) {

0 commit comments

Comments
 (0)