Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b1771dc
feat(core): implement Error Governance (Principle VII) and stabilize …
EmiyaKiritsugu3 Apr 3, 2026
46cc5fe
feat(core): implement Error Governance (Principle VII) and stabilize …
EmiyaKiritsugu3 Apr 3, 2026
4686df6
fix(core): resolve logical errors in ErrorRegistry (concurrency, corr…
EmiyaKiritsugu3 Apr 3, 2026
7e01b30
chore: align test expectations and satisfy constitution rule 97
EmiyaKiritsugu3 Apr 3, 2026
a61f6f4
feat(core): migrate legacy console calls to ErrorRegistry (framework …
EmiyaKiritsugu3 Apr 4, 2026
8df2725
chore(internal): update registry manifest after hook
EmiyaKiritsugu3 Apr 4, 2026
2c30fe8
test: align validate-publish expectations with ErrorRegistry dependency
EmiyaKiritsugu3 Apr 4, 2026
34f686b
merge: synchronize with upstream main and resolve package-lock conflicts
EmiyaKiritsugu3 Apr 4, 2026
f3408b3
docs(stories): include finalized stories for PR reference
EmiyaKiritsugu3 Apr 4, 2026
6b83e96
feat(monitor): implement Write Queue pattern in ErrorRegistry
EmiyaKiritsugu3 Apr 4, 2026
e4a7632
fix(infra): strengthen error governance in installer and PR reviewer
EmiyaKiritsugu3 Apr 4, 2026
48415a7
fix(core): implement Principle VII error governance in learning modules
EmiyaKiritsugu3 Apr 4, 2026
484b91f
refactor(synapse): isolate pipeline metrics and extract execution logic
EmiyaKiritsugu3 Apr 4, 2026
fec800c
feat(synapse): implement session normalization for consistent interna…
EmiyaKiritsugu3 Apr 4, 2026
3b04fdf
chore: synchronize manifest and apply minor quality adjustments
EmiyaKiritsugu3 Apr 4, 2026
41d98d4
refactor(monitor): align ErrorRegistry with architectural sequence di…
EmiyaKiritsugu3 Apr 4, 2026
61e4fef
fix(infra): correct ErrorRegistry require paths in infrastructure scr…
EmiyaKiritsugu3 Apr 4, 2026
27ac723
fix(monitor): prevent AIOXError mutation and handle unawaited logs
EmiyaKiritsugu3 Apr 4, 2026
472cb33
fix(suggestion-engine): await ErrorRegistry.log in buildContext
EmiyaKiritsugu3 Apr 4, 2026
1c59c2a
fix(monitor): preserve non-enumerable properties during AIOXError clo…
EmiyaKiritsugu3 Apr 4, 2026
f0db2fc
fix(infra): use fire-and-forget logging in config-loader to avoid mas…
EmiyaKiritsugu3 Apr 4, 2026
2a58511
refactor(learning): migrate PatternStore to async and fix suggestion-…
EmiyaKiritsugu3 Apr 5, 2026
bb85cab
fix(monitor): address CodeRabbit review feedback for ErrorRegistry
EmiyaKiritsugu3 Apr 5, 2026
fd9fca3
fix(config): align config resolution with Principle VII (Error Govern…
EmiyaKiritsugu3 Apr 5, 2026
f1cf703
fix(synapse): shield engine with ErrorRegistry governance
EmiyaKiritsugu3 Apr 5, 2026
44fa745
fix(learning): align workflow intelligence with Principle VII
EmiyaKiritsugu3 Apr 5, 2026
9a3c8f5
fix(core): final audit and shielding of MasterOrchestrator and CLI bo…
EmiyaKiritsugu3 Apr 5, 2026
f5ae2ac
docs: improve docstring coverage to satisfy quality gate
EmiyaKiritsugu3 Apr 5, 2026
5261dba
fix(core): normalize infrastructure paths and import patterns in exec…
EmiyaKiritsugu3 Apr 5, 2026
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
37 changes: 28 additions & 9 deletions .aiox-core/constitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,42 @@ Qualidade nΓ£o Γ© negociΓ‘vel. Todo cΓ³digo passa por mΓΊltiplos gates antes de

### VI. Absolute Imports (SHOULD)

Imports relativos criam acoplamento e dificultam refatoraΓ§Γ£o.
Relative imports create coupling and make refactoring difficult.

**Regras:**
- SHOULD: Sempre usar imports absolutos com alias `@/`
- SHOULD NOT: Usar imports relativos (`../../../`)
- EXCEPTION: Imports dentro do mesmo mΓ³dulo/feature podem ser relativos
**Rules:**
- SHOULD: Always use absolute imports with the `@/` alias.
- SHOULD NOT: Use relative imports (`../../../`).
- EXCEPTION: Imports within the same module/feature may be relative.

**Exemplo:**
**Example:**
```typescript
// CORRETO
// CORRECT
import { useStore } from '@/stores/feature/store'

// INCORRETO
// INCORRECT
import { useStore } from '../../../stores/feature/store'
```

**Gate:** ESLint rule (jΓ‘ implementado)
**Gate:** ESLint rule (already implemented)

---

### VII. Error Governance (MUST)

All errors must be traceable, persistent, and categorized to ensure framework observability.

**Rules:**
- MUST: All errors must be registered and persisted via `ErrorRegistry`.
- MUST NOT: Empty `catch` blocks are strictly prohibited; errors must be at least logged.
- MUST: Categorize errors as `OPERATIONAL` (validation/user-info) or `SYSTEM` (framework bugs/agent failures).
- MUST: Include contextual metadata (Agent ID, Action, and Timestamp) in every log.

**Hierarchy:**
```
System Errors (Critical/Red) β†’ Operational Errors (Audit/Yellow) β†’ Info (Blue)
```
Comment on lines +139 to +141
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Add a language tag to the fenced hierarchy block (markdownlint MD040).

The code fence under Hierarchy has no language identifier, which triggers the configured markdownlint warning.

Suggested patch
-```
+```text
 System Errors (Critical/Red) β†’ Operational Errors (Audit/Yellow) β†’ Info (Blue)
</details>

<!-- suggestion_start -->

<details>
<summary>πŸ“ Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
πŸͺ› markdownlint-cli2 (0.22.0)

[warning] 139-139: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/constitution.md around lines 139 - 141, The fenced code block
under the "Hierarchy" section lacks a language tag which triggers markdownlint
MD040; fix it by adding a language identifier (e.g., text) to the
triple-backtick fence so the block becomes ```text ... ```, updating the fenced
block in the "Hierarchy" content to include that tag.


**Gate:** `scripts/quality/pr-review-ai.js` - BLOCK if raw `console.error` or empty `catch` detected.

Comment on lines +128 to 144
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Update constitution metadata after introducing Principle VII.

This section adds a new constitutional principle, but the document header still shows Version: 1.0.0 and Last Amended: 2025-01-30. Please bump version/amendment metadata to keep governance history accurate.

🧰 Tools
πŸͺ› LanguageTool

[grammar] ~135-~135: PossΓ­vel erro de concordΓ’ncia de nΓΊmero.
Context: ...AL(validation/user-info) orSYSTEM` (framework bugs/agent failures). - MUST: Include contex...

(GENERAL_NUMBER_AGREEMENT_ERRORS)

πŸͺ› markdownlint-cli2 (0.22.0)

[warning] 139-139: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/constitution.md around lines 128 - 144, Update the document
header to reflect the addition of Principle VII by bumping the version (e.g.,
change Version from 1.0.0 to 1.1.0) and updating the Last Amended date to the
current amendment date (replace 2025-01-30 with 2026-04-04), and ensure any
header metadata references to Principle VII, ErrorRegistry, or the gate script
scripts/quality/pr-review-ai.js remain consistent.

---

Expand Down
37 changes: 35 additions & 2 deletions .aiox-core/core/config/config-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ const performanceMetrics = {

/**
* Checks if cache is still valid
*
* @returns {boolean} True if cache is valid
* @private
*/
function isCacheValid() {
if (!configCache.lastLoad) return false;
Expand All @@ -90,6 +93,9 @@ function isCacheValid() {

/**
* Loads full config file (used for initial load or cache refresh)
*
* @returns {Promise<Object>} The full configuration object
* @private
*/
async function loadFullConfig() {
const configPath = path.join('.aiox-core', 'core-config.yaml');
Expand Down Expand Up @@ -123,6 +129,7 @@ async function loadFullConfig() {
*
* @param {string[]} sections - Array of section names to load
* @returns {Promise<Object>} Config object with requested sections
* @public
*/
async function loadConfigSections(sections) {
const startTime = Date.now();
Expand Down Expand Up @@ -164,15 +171,14 @@ async function loadConfigSections(sections) {
*
* @param {string} agentId - Agent ID (e.g., 'dev', 'qa', 'po')
* @returns {Promise<Object>} Config object with sections needed by agent
* @public
*/
async function loadAgentConfig(agentId) {
const startTime = Date.now();

// Get required sections for this agent
const requiredSections = agentRequirements[agentId] || ALWAYS_LOADED;

console.log(`πŸ“¦ Loading config for @${agentId} (${requiredSections.length} sections)...`);

const config = await loadConfigSections(requiredSections);

const loadTime = Date.now() - startTime;
Expand All @@ -189,13 +195,17 @@ async function loadAgentConfig(agentId) {
* Loads always-loaded sections (minimal config)
*
* @returns {Promise<Object>} Minimal config with always-loaded sections
* @public
*/
async function loadMinimalConfig() {
return await loadConfigSections(ALWAYS_LOADED);
}

/**
* Preloads config into cache (useful for startup optimization)
*
* @returns {Promise<void>}
* @public
*/
async function preloadConfig() {
console.log('πŸ”„ Preloading config into cache...');
Expand All @@ -205,6 +215,9 @@ async function preloadConfig() {

/**
* Clears config cache (useful for testing or forcing reload)
*
* @returns {void}
* @public
*/
function clearCache() {
configCache.full = null;
Expand All @@ -217,6 +230,7 @@ function clearCache() {
* Gets performance metrics
*
* @returns {Object} Performance statistics
* @public
*/
function getPerformanceMetrics() {
return {
Expand All @@ -233,6 +247,7 @@ function getPerformanceMetrics() {
*
* @param {string} agentId - Agent ID to validate
* @returns {Promise<Object>} Validation result
* @public
*/
async function validateAgentConfig(agentId) {
const requiredSections = agentRequirements[agentId] || ALWAYS_LOADED;
Expand All @@ -257,6 +272,7 @@ async function validateAgentConfig(agentId) {
*
* @param {string} sectionName - Section to load
* @returns {Promise<any>} Section content
* @public
*/
async function getConfigSection(sectionName) {
const config = await loadConfigSections([sectionName]);
Expand All @@ -277,3 +293,20 @@ module.exports = {
agentRequirements,
ALWAYS_LOADED,
};
ents,
ALWAYS_LOADED,
};
getConfigSection,
agentRequirements,
ALWAYS_LOADED,
};
onfig,
loadFullConfig,
preloadConfig,
clearCache,
getPerformanceMetrics,
validateAgentConfig,
getConfigSection,
agentRequirements,
ALWAYS_LOADED,
};
66 changes: 47 additions & 19 deletions .aiox-core/core/config/config-resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const yaml = require('js-yaml');
const { deepMerge } = require('./merge-utils');
const { interpolateEnvVars, lintEnvPatterns } = require('./env-interpolator');
const { globalConfigCache } = require('./config-cache');
const AIOXError = require('aiox-core/utils/aiox-error');
const ErrorRegistry = require('aiox-core/monitor/error-registry');

// ---------------------------------------------------------------------------
// JSON Schema validation (Story 12.2)
Expand All @@ -52,6 +54,7 @@ const SCHEMA_FILES = {
* Get or create the shared Ajv instance (lazy-loaded).
*
* @returns {Object} Ajv instance
* @private
*/
function getAjvInstance() {
if (!_ajvInstance) {
Expand All @@ -68,6 +71,7 @@ function getAjvInstance() {
*
* @param {string} schemaFileName - Schema file name
* @returns {Object|null} Parsed schema or null if not found
* @private
*/
function loadSchema(schemaFileName) {
if (_schemaCache[schemaFileName]) {
Expand Down Expand Up @@ -98,6 +102,7 @@ function loadSchema(schemaFileName) {
* @param {Object} data - Config data to validate
* @param {string} filePath - Source file path (for error messages)
* @returns {string[]} Validation warnings (empty if valid)
* @private
*/
function validateConfig(level, data, filePath) {
const warnings = [];
Expand Down Expand Up @@ -132,6 +137,9 @@ function validateConfig(level, data, filePath) {

/**
* Clear the schema cache (useful for testing).
*
* @returns {void}
* @private
*/
function clearSchemaCache() {
_schemaCache = {};
Expand Down Expand Up @@ -173,6 +181,7 @@ const LEVELS = {
* @param {string} projectRoot - Project root directory
* @param {string} relativePath - Path relative to projectRoot
* @returns {{ data: Object|null, path: string }} Parsed YAML or null
* @private
*/
function loadYaml(projectRoot, relativePath) {
const fullPath = path.join(projectRoot, relativePath);
Expand All @@ -186,7 +195,10 @@ function loadYaml(projectRoot, relativePath) {
const data = yaml.load(content) || {};
return { data, path: fullPath };
} catch (error) {
throw new Error(`Failed to parse YAML at ${fullPath}: ${error.message}`);
throw new AIOXError(`Failed to parse YAML at ${fullPath}: ${error.message}`, {
category: 'SYSTEM',
metadata: { path: fullPath, originalError: error.message },
});
}
}

Expand All @@ -196,6 +208,7 @@ function loadYaml(projectRoot, relativePath) {
*
* @param {string} absolutePath - Absolute file path
* @returns {{ data: Object|null, path: string }} Parsed YAML or null
* @private
*/
function loadYamlAbsolute(absolutePath) {
try {
Expand Down Expand Up @@ -224,6 +237,7 @@ function loadYamlAbsolute(absolutePath) {
*
* @param {string} projectRoot - Project root directory
* @returns {boolean} True if legacy mode
* @public
*/
function isLegacyMode(projectRoot) {
const hasLegacy = fs.existsSync(path.join(projectRoot, CONFIG_FILES.legacy));
Expand All @@ -245,10 +259,8 @@ function isLegacyMode(projectRoot) {
* @param {Object} options - Load options
* @param {string} [options.appDir] - App directory for L3 config
* @param {boolean} [options.debug] - Collect source-tracking metadata
* @returns {Object} result
* @returns {Object} result.config - Merged configuration
* @returns {Object} [result.sources] - Per-key source tracking (when debug=true)
* @returns {string[]} result.warnings - Lint/interpolation warnings
* @returns {Object} Result object containing config, sources, and warnings
* @public
*/
function loadLayeredConfig(projectRoot, options = {}) {
const warnings = [];
Expand Down Expand Up @@ -350,16 +362,18 @@ function loadLayeredConfig(projectRoot, options = {}) {
* Load configuration in legacy mode (monolithic core-config.yaml).
*
* @param {string} projectRoot - Project root directory
* @returns {Object} result
* @returns {Object} result.config - Parsed configuration
* @returns {string[]} result.warnings - Deprecation warnings
* @returns {Object} Result object containing config, sources, and warnings
* @public
*/
function loadLegacyConfig(projectRoot) {
const warnings = [];
const legacy = loadYaml(projectRoot, CONFIG_FILES.legacy);

if (!legacy.data) {
throw new Error(`Legacy config file not found: ${CONFIG_FILES.legacy}`);
throw new AIOXError(`Legacy config file not found: ${CONFIG_FILES.legacy}`, {
category: 'SYSTEM',
metadata: { projectRoot, path: CONFIG_FILES.legacy },
});
}

const suppressDeprecation = process.env.AIOX_SUPPRESS_DEPRECATION === 'true'
Expand Down Expand Up @@ -389,6 +403,8 @@ function loadLegacyConfig(projectRoot) {
* @param {string} level - Level label (L1, L2, Pro, L3, L4)
* @param {string} file - Source file path
* @param {string} [prefix] - Key prefix for nested tracking
* @returns {void}
* @private
*/
function trackSources(sources, data, level, file, prefix = '') {
if (!sources || !data) return;
Expand Down Expand Up @@ -422,11 +438,8 @@ function trackSources(sources, data, level, file, prefix = '') {
* @param {string} [options.appDir] - App directory for L3 (monorepo)
* @param {boolean} [options.debug] - Enable source tracking
* @param {boolean} [options.skipCache] - Bypass cache
* @returns {Object} result
* @returns {Object} result.config - Final resolved config
* @returns {Object} [result.sources] - Source tracking (debug only)
* @returns {string[]} result.warnings - All warnings
* @returns {boolean} result.legacy - Whether legacy mode was used
* @returns {Object} Result object containing config, sources, warnings, and legacy flag
* @public
*/
function resolveConfig(projectRoot, options = {}) {
const cacheKey = `resolved:${projectRoot}:${options.appDir || 'root'}:${options.debug ? 'debug' : 'std'}`;
Expand Down Expand Up @@ -470,6 +483,7 @@ function resolveConfig(projectRoot, options = {}) {
* @param {Object} [options] - Options
* @param {string} [options.appDir] - App directory for level 'app'
* @returns {Object|null} Raw config or null if file doesn't exist
* @public
*/
function getConfigAtLevel(projectRoot, level, options = {}) {
let relativePath;
Expand Down Expand Up @@ -499,7 +513,10 @@ function getConfigAtLevel(projectRoot, level, options = {}) {
relativePath = CONFIG_FILES.legacy;
break;
default:
throw new Error(`Unknown config level: ${level}`);
throw new AIOXError(`Unknown config level: ${level}`, {
category: 'SYSTEM',
metadata: { level, projectRoot },
});
}

const { data } = loadYaml(projectRoot, relativePath);
Expand All @@ -519,6 +536,7 @@ const VALID_USER_PROFILES = ['bob', 'advanced'];
* Ensure the ~/.aiox/ directory exists with secure permissions.
*
* @returns {string} Path to ~/.aiox/ directory
* @private
*/
function ensureUserConfigDir() {
const dir = path.dirname(CONFIG_FILES.user);
Expand All @@ -536,6 +554,7 @@ function ensureUserConfigDir() {
* @param {string} key - Config key to set
* @param {*} value - Value to set
* @returns {Object} Updated user config
* @public
*/
function setUserConfigValue(key, value) {
ensureUserConfigDir();
Expand All @@ -552,19 +571,28 @@ function setUserConfigValue(key, value) {

config[key] = value;

const yamlContent = yaml.dump(config, { lineWidth: -1 });
fs.writeFileSync(CONFIG_FILES.user, yamlContent, 'utf8');
try {
const yamlContent = yaml.dump(config, { lineWidth: -1 });
fs.writeFileSync(CONFIG_FILES.user, yamlContent, 'utf8');

globalConfigCache.clear();
globalConfigCache.clear();

return config;
return config;
} catch (error) {
ErrorRegistry.log(error, {
category: 'SYSTEM',
metadata: { key, value, path: CONFIG_FILES.user },
}).catch(() => {});
throw error;
}
}

/**
* Toggle user_profile between 'bob' and 'advanced'.
* Reads current value, flips it, writes back, and invalidates cache.
*
* @returns {{ previous: string, current: string }} Previous and new profile values
* @public
*/
function toggleUserProfile() {
let config = {};
Expand Down
4 changes: 2 additions & 2 deletions .aiox-core/core/orchestration/executors/epic-4-executor.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Epic4Executor extends EpicExecutor {
_getPlanTracker() {
if (!this._planTracker) {
try {
const PlanTracker = require('../../infrastructure/scripts/plan-tracker');
const { PlanTracker } = require('../../../infrastructure/scripts/plan-tracker');
this._planTracker = PlanTracker;
} catch (error) {
this._log(`PlanTracker not available: ${error.message}`, 'warn');
Expand All @@ -51,7 +51,7 @@ class Epic4Executor extends EpicExecutor {
_getSubtaskVerifier() {
if (!this._subtaskVerifier) {
try {
const SubtaskVerifier = require('../../infrastructure/scripts/subtask-verifier');
const { SubtaskVerifier } = require('../../../infrastructure/scripts/subtask-verifier');
this._subtaskVerifier = SubtaskVerifier;
} catch (error) {
this._log(`SubtaskVerifier not available: ${error.message}`, 'warn');
Expand Down
Loading
Loading