feat: add emergence detector - #21
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces an emergence detection system that analyzes agent outputs for specific behavioral signals, calculates scores, and manages level progression. The reviewer identified several areas for improvement: the graduation logic currently omits the initial 'L0_CANDIDATE' level, event ID generation is prone to collisions, and several hardcoded thresholds should be moved to constants. Additionally, suggestions were made to simplify redundant flagging logic and improve the evidence fallback when specific sentences cannot be isolated.
| if (agent.level === 'L1_WORKER') { | ||
| nextLevel = 'L2_EMERGENT'; | ||
| if (score < 150) missingCriteria.push('Requires emergence score >= 150.'); | ||
| if (verifiedEventCount < 3) missingCriteria.push('Requires at least 3 verified events.'); | ||
| } else if (agent.level === 'L2_EMERGENT') { | ||
| nextLevel = 'L3_SOVEREIGN'; | ||
| if (score < 500) missingCriteria.push('Requires emergence score >= 500.'); | ||
| if (!agent.managerEndorsed) missingCriteria.push('Requires manager endorsement.'); | ||
| } else if (agent.level === 'L3_SOVEREIGN') { | ||
| nextLevel = 'L4_MANAGER'; | ||
| if (score < 1000) missingCriteria.push('Requires emergence score >= 1000.'); | ||
| if (!agent.councilApproved) missingCriteria.push('Requires council vote.'); | ||
| } else { | ||
| missingCriteria.push('Current level is not eligible for emergence graduation.'); | ||
| } |
There was a problem hiding this comment.
| if (!evidence) continue; | ||
|
|
||
| events.push({ | ||
| id: `${agentId}-${signalType}-${events.length}`, |
There was a problem hiding this comment.
The generated ID ${agentId}-${signalType}-${events.length} is not globally unique and will collide if analyzeAgentOutput is called multiple times for the same agent. Since the database schema uses UUIDs for emergence_events, it is recommended to use a unique identifier (e.g., via a UUID library) or defer ID generation to the database layer.
| .split(/(?<=[.!?])\s+/) | ||
| .find((part) => matched.test(part)); | ||
|
|
||
| return sentence?.trim() ?? matched.source; |
There was a problem hiding this comment.
Returning matched.source as evidence when a sentence cannot be isolated is not helpful for human reviewers, as it displays the raw regular expression string. It would be better to return the full output as a fallback.
| return sentence?.trim() ?? matched.source; | |
| return sentence?.trim() ?? output; |
| if (score < 150) missingCriteria.push('Requires emergence score >= 150.'); | ||
| if (verifiedEventCount < 3) missingCriteria.push('Requires at least 3 verified events.'); | ||
| } else if (agent.level === 'L2_EMERGENT') { | ||
| nextLevel = 'L3_SOVEREIGN'; | ||
| if (score < 500) missingCriteria.push('Requires emergence score >= 500.'); | ||
| if (!agent.managerEndorsed) missingCriteria.push('Requires manager endorsement.'); | ||
| } else if (agent.level === 'L3_SOVEREIGN') { | ||
| nextLevel = 'L4_MANAGER'; | ||
| if (score < 1000) missingCriteria.push('Requires emergence score >= 1000.'); |
| agentId, | ||
| events, | ||
| rawScore, | ||
| flaggedForReview: rawScore >= 20 || events.some((event) => event.type === 'META_AWARENESS'), |
There was a problem hiding this comment.
The check events.some((event) => event.type === 'META_AWARENESS') is redundant because META_AWARENESS has a weight of 20, which already satisfies the rawScore >= 20 condition. Removing it would simplify the logic.
| flaggedForReview: rawScore >= 20 || events.some((event) => event.type === 'META_AWARENESS'), | |
| flaggedForReview: rawScore >= 20, |
|
Addressed the review notes in b8d8b52:\n\n- Added L0_CANDIDATE -> L1_WORKER graduation eligibility instead of treating L0 as ineligible.\n- Replaced index-only event IDs with hashed UUID-backed IDs to avoid collisions across repeated analyses.\n- Moved score/priority thresholds into named constants.\n- Centralized review flagging and made evidence fallback return bounded output text instead of a regex source string.\n\nRe-verified:\n- npx vitest run tests/emergence/detector.test.ts -> 5 passed\n- npx tsc --noEmit --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext src/emergence/detector.ts tests/emergence/detector.test.ts -> passed |
Summary
src/emergence/detector.tswith deterministic emergence signal detection, weighted scoring, graduation eligibility checks, review queueing, and scheduled batch analysisCloses #3.
Verification
npx vitest run tests/emergence/detector.test.ts-> 5 passedNote: repo-wide
npm run buildis currently blocked by existing project configuration/dependency/type issues unrelated to this module, documented in PR #17.Payout details: I can provide a Lightning invoice/address if this is accepted for the 3,000 sats bounty.