Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .aiox-core/cli/commands/validate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function createValidateCommand() {
.option('-d, --dry-run', 'Preview repairs without applying (use with --repair)')
.option('--detailed', 'Show detailed file list')
.option('--no-hash', 'Skip hash verification (faster)')
.option('--no-signature', 'Skip manifest signature verification (insecure; recovery only)')
.option('--extras', 'Detect extra files not in manifest')
.option('-v, --verbose', 'Enable verbose output')
.option('--json', 'Output results as JSON')
Expand Down Expand Up @@ -213,6 +214,7 @@ async function runValidation(options) {
// Create validator instance
const validator = new PostInstallValidator(projectRoot, sourceDir, {
verifyHashes: options.hash !== false,
requireSignature: options.signature !== false,
detectExtras: options.extras === true,
verbose: options.verbose === true,
onProgress: options.json
Expand Down
33 changes: 29 additions & 4 deletions .aiox-core/infrastructure/scripts/generate-settings-json.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,36 @@ function writeSettingsJson(projectRoot, permissions) {
}

const updated = { ...existing };

if (permissions.deny.length > 0 || permissions.allow.length > 0) {
updated.permissions = permissions;
const existingPermissions =
existing && existing.permissions && typeof existing.permissions === 'object'
? existing.permissions
: {};
const generatedPermissionsEmpty =
permissions.allow.length === 0 && permissions.deny.length === 0;

if (generatedPermissionsEmpty) {
if (Object.keys(existingPermissions).length > 0) {
updated.permissions = existingPermissions;
} else {
delete updated.permissions;
}
} else {
delete updated.permissions;
const mergedAllow = Array.from(
new Set([...(Array.isArray(existingPermissions.allow) ? existingPermissions.allow : []), ...permissions.allow]),
);
const mergedDeny = Array.from(
new Set([...(Array.isArray(existingPermissions.deny) ? existingPermissions.deny : []), ...permissions.deny]),
);

if (mergedDeny.length > 0 || mergedAllow.length > 0) {
updated.permissions = {
...existingPermissions,
allow: mergedAllow,
deny: mergedDeny,
};
} else {
delete updated.permissions;
}
}

const newContent = JSON.stringify(updated, null, 2) + '\n';
Expand Down
82 changes: 43 additions & 39 deletions .aiox-core/install-manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
# - File types for categorization
#
version: 5.0.3
generated_at: "2026-03-11T15:04:09.395Z"
generated_at: "2026-04-18T21:06:06.371Z"
generator: scripts/generate-install-manifest.js
file_count: 1090
file_count: 1091
files:
- path: cli/commands/config/index.js
hash: sha256:25c4b9bf4e0241abf7754b55153f49f1a214f1fb5fe904a576675634cb7b3da9
Expand Down Expand Up @@ -117,9 +117,9 @@ files:
type: cli
size: 5320
- path: cli/commands/validate/index.js
hash: sha256:b49dcd35424f753f4fedc96c8e1070f716a1f3cb3357baa5b7377d60b5f75cad
hash: sha256:dcf19f1b4a46e562812c5c718783e88f9ac8ef0bf51f27eb1a6d31c7081fafdf
type: cli
size: 12893
size: 13040
- path: cli/commands/workers/formatters/info-formatter.js
hash: sha256:11c17e16be0b7d09ba8949497e0887bb20996966d266454aa2bb5dfc9d9d91b8
type: cli
Expand Down Expand Up @@ -193,21 +193,21 @@ files:
type: core
size: 5637
- path: core/code-intel/helpers/dev-helper.js
hash: sha256:2418a5f541003c73cc284e88a6b0cb666896a47ffd5ed4c08648269d281efc4c
hash: sha256:7e7f9bb92725ca1d85b0a7151668bc5bcdd6fc9b73fed5b2b2c28217d14535ab
type: core
size: 5770
size: 5751
- path: core/code-intel/helpers/devops-helper.js
hash: sha256:c40cfa9ac2f554a707ff68c7709ae436349041bf00ad2f42811ccbe8ba842462
hash: sha256:e72f95de2f3737b6e12094526eabfb4974a8339ce6d25f2e323f734fe567c155
type: core
size: 5115
size: 5043
- path: core/code-intel/helpers/planning-helper.js
hash: sha256:2edcf275122125205a9e737035c8b25efdc4af13e7349ffc10c3ebe8ebe7654d
hash: sha256:9ca5b57b74b5729685369662659e15a91e35ec3a33691973be000ecd85974f3d
type: core
size: 6863
size: 6844
- path: core/code-intel/helpers/qa-helper.js
hash: sha256:ca069dad294224dd5c3369826fb39d5c24287d49d74360049f8bbc55f190eeda
hash: sha256:9dbb84c1c4ed1aa57385ad2a6c74520f2020e6f8883012dd57c51486172ee528
type: core
size: 5184
size: 5146
- path: core/code-intel/helpers/story-helper.js
hash: sha256:778466253ac66103ebc3b1caf71f44b06a0d5fb3d39fe8d3d473dd4bc73fefc6
type: core
Expand Down Expand Up @@ -277,9 +277,9 @@ files:
type: core
size: 936
- path: core/config/template-overrides.js
hash: sha256:1708dc8764e7f88dfefd7684240afcd5f13657170ac104aed99145e2bb8ae82c
hash: sha256:202d141a292bc5a8dd0697e044d7627b260839ae8b7119fd40ae486b3a1b0825
type: core
size: 2223
size: 2224
- path: core/config/templates/user-config.yaml
hash: sha256:3505471b0adff9bfcea08f46cca3aeeda46a283bbe7ee711dd566e5974c3257f
type: template
Expand Down Expand Up @@ -337,9 +337,9 @@ files:
type: core
size: 2265
- path: core/doctor/checks/rules-files.js
hash: sha256:3996e6343a224021fa684d7930dc99b66469c59cb15d416b0c024a770d722ab6
hash: sha256:ec58342215cede634f50c5b3164155c4f27fd8070af176ec0e02e6deec6fb218
type: core
size: 1426
size: 1368
- path: core/doctor/checks/settings-json.js
hash: sha256:bd26841b966fcfa003eca6f85416d4f877b9dcfea0e4017df9f2a97c14c33fbb
type: core
Expand Down Expand Up @@ -441,13 +441,13 @@ files:
type: core
size: 11060
- path: core/graph-dashboard/cli.js
hash: sha256:1f2fd6c6b5ace42f3bddc89695fe32d01949321d96057bbf50e2e48892f2c8f5
hash: sha256:29f273a06fecc77eb3e39162ba1aaf28e1cbadb2a000158f009817021a30b4d1
type: core
size: 10251
size: 10205
- path: core/graph-dashboard/data-sources/code-intel-source.js
hash: sha256:e508d6cbadcd2358fa7756dcaceefbaa510bd89155e036e2cbd386585408ff8f
hash: sha256:2b0534f57a8f6ca2ff5942e42faf147f1be84773b3af33c9e506ee8f318b558c
type: core
size: 6799
size: 6800
- path: core/graph-dashboard/data-sources/metrics-source.js
hash: sha256:b1e4027f82350760b67ea8f58e04a5e739f87f010838487043e29dab7301ae9e
type: core
Expand Down Expand Up @@ -725,7 +725,7 @@ files:
type: core
size: 3624
- path: core/ids/layer-classifier.js
hash: sha256:4ae1e7d341076a13d08b8b5baf7a687ad2c7df673d50fc3554d522fe79debcdc
hash: sha256:2a240b70ac3507e50a64b96d580c4d933bf2116125fb52c8237db2ed9ebf27b7
type: core
size: 2382
- path: core/ids/README.md
Expand Down Expand Up @@ -1145,9 +1145,9 @@ files:
type: core
size: 4672
- path: core/synapse/layers/layer-processor.js
hash: sha256:73cb0e5b4bada80d8e256009004679e483792077fac4358c6466cd77136f79fa
hash: sha256:15f9e4c1525d3fa2186170705a26191ad87d94ffd7fa7d61f373b07b6fb3d874
type: core
size: 2881
size: 2882
- path: core/synapse/memory/memory-bridge.js
hash: sha256:820875f97ceea80fc6402c0dab1706cfe58de527897b22dea68db40b0d6ec368
type: core
Expand Down Expand Up @@ -1217,13 +1217,13 @@ files:
type: data
size: 34235
- path: data/capability-detection.js
hash: sha256:5176849c01d90e5867f18962e03ff10a10628f40c30fe5c8cb65209f833c0884
hash: sha256:317d1b51b5cda2e35ac6d468e33e05c0948e6d7f05f51d750d7ce6ff5a58535a
type: data
size: 9575
size: 9590
- path: data/entity-registry.yaml
hash: sha256:cc1bf74d3ef4e90b7a396d5b77259e540b2f9bd4a5b4b1da4977fe49ae83525d
hash: sha256:881b9fa781b5a969d121a3a7c20c898e669d43477a3d714cafeaf856b9a4ffa0
type: data
size: 521869
size: 523573
- path: data/learned-patterns.yaml
hash: sha256:24ac0b160615583a0ff783d3da8af80b7f94191575d6db2054ec8e10a3f945dc
type: data
Expand Down Expand Up @@ -1273,17 +1273,17 @@ files:
type: data
size: 7026
- path: data/tok3-token-comparison.js
hash: sha256:1f484f8054bec7a7e8055acbc9fddd7863a769948c30c6db42c5c8694410da8f
hash: sha256:da4e3cb3a09684d6fc6d0bb2b285d1837054b9fedc65b58e7fdc8d7a99b1d8a1
type: data
size: 4622
size: 4486
- path: data/tool-registry.yaml
hash: sha256:64e867d0eb36c7f7ac86f4f73f1b2ff89f43f37f28a6de34389be74b9346860c
type: data
size: 15178
- path: data/tool-search-validation.js
hash: sha256:8757bf087692f002d67115dbe1c8244bbd869600e4f52c49b0d9b07cb9fbb783
hash: sha256:d83f0680d38996be4615c1f3425d35e0b20c5c93c09da470d2c6a42dcfc3583c
type: data
size: 5754
size: 5755
- path: data/workflow-chains.yaml
hash: sha256:1fbf1625e267eedc315cf1e08e5827c250ddc6785fb2cb139e7702def9b66268
type: data
Expand Down Expand Up @@ -2957,13 +2957,13 @@ files:
type: script
size: 40724
- path: infrastructure/scripts/codex-skills-sync/index.js
hash: sha256:a7a3c97374c34a900acad13498f61f8a40517574480354218e349d1e1d3931a4
hash: sha256:e587b49a997e7fab5a12441a63663fb882b9daced606d274cf365dfb04aaf10d
type: script
size: 5246
size: 5474
- path: infrastructure/scripts/codex-skills-sync/validate.js
hash: sha256:0fbc1baff25f20e3a37d3e4be51d146a75254d5ed638b3438d9f1bf0e587c997
hash: sha256:591ce1dc3d4a7de883d05677eb29fedf0b8b46bfe977390248731de38043e32c
type: script
size: 4572
size: 5111
- path: infrastructure/scripts/collect-tool-usage.js
hash: sha256:8a739b79182dc41e28b7e02aeb9ec1dde5ec49f3ca534399acc59711b3b92bbf
type: script
Expand Down Expand Up @@ -3057,9 +3057,9 @@ files:
type: script
size: 18899
- path: infrastructure/scripts/generate-settings-json.js
hash: sha256:bb4c6f664eb06622fd78eb455c0a74ee29ecee5fe47b4a7fcb2de8a89119ff5a
hash: sha256:dc5b8803825ed749080925d7c17ece39b33a7faf3b02d08a445e8cc7408048a1
type: script
size: 8292
size: 9159
- path: infrastructure/scripts/git-config-detector.js
hash: sha256:52ed96d98fc6f9e83671d7d27f78dcff4f2475f3b8e339dc31922f6b2814ad78
type: script
Expand All @@ -3085,9 +3085,9 @@ files:
type: script
size: 5534
- path: infrastructure/scripts/ide-sync/index.js
hash: sha256:2f48896307b1fc3839f13169cab554c0a9a34f9d0e3961f1ccc07a2bbfaebdb2
hash: sha256:34377b67d0099aa68611ecff15b5fbe7d0073b86acf16edea0fa4a87ff51d6e4
type: script
size: 14906
size: 14929
- path: infrastructure/scripts/ide-sync/README.md
hash: sha256:c18c2563b2ca64580a4814edd3c20a79c96f33fa8b953ee02206ef0faad53d35
type: script
Expand Down Expand Up @@ -3248,6 +3248,10 @@ files:
hash: sha256:118d4cdbc64cf3238065f2fb98958305ae81e1384bc68f5a6c7b768f1232cd1e
type: script
size: 34686
- path: infrastructure/scripts/repair-agent-references.js
hash: sha256:63b5f580b783d5098c3ab3d8da11af9871306b3a1fc0f986f25c813eb4c4dd75
type: script
size: 7200
- path: infrastructure/scripts/repository-detector.js
hash: sha256:10ffca7f57d24d3729c71a9104a154500a3c72328d67884e26e38d22199af332
type: script
Expand Down
6 changes: 4 additions & 2 deletions bin/utils/framework-guard.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ const FALLBACK_EXCEPTIONS = [
* @returns {RegExp}
*/
function globToRegex(glob) {
const placeholder = '\u0000';

// 1. Replace ** with placeholder before processing
let pattern = glob.replace(/\*\*/g, '\u0000');
let pattern = glob.replace(/\*\*/g, placeholder);

// 2. Escape all regex-special chars (dots, plus, etc.) — but NOT * or placeholder
pattern = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
Expand All @@ -54,7 +56,7 @@ function globToRegex(glob) {
pattern = pattern.replace(/\*/g, '[^/]+');

// 4. Restore ** placeholder to any-depth matcher
pattern = pattern.replace(/\u0000/g, '.+');
pattern = pattern.split(placeholder).join('.+');

// If pattern ends with .+ (was **), match prefix
if (glob.endsWith('**')) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Story 123.5: Recovery do update e segurança brownfield no installer

## Status

- [x] Rascunho
- [x] Em revisão
- [x] Concluída

## Contexto

O fluxo público de atualização do AIOX está quebrado em dois caminhos críticos:

- `aiox update` instala o pacote novo via npm, mas não sincroniza o `.aiox-core` do projecto antes da validação, causando rollback com manifest antigo.
- `aiox install --force` continua perigoso em brownfield: `--dry-run` é ignorado e ficheiros mutáveis de projecto, como `core-config.yaml`, `MEMORY.md` e regras custom de `.claude/settings.json`, podem ser sobrescritos.

Isto bloqueia upgrades públicos e viola as garantias de segurança/configuração do framework em projectos existentes.

## Objetivo

Restabelecer um caminho seguro e reproduzível para instalar e atualizar o framework em projectos brownfield, sem rollback falso-positivo nem overwrite destrutivo de configuração local.

## Acceptance Criteria

- [x] AC1. `aiox update` sincroniza o `.aiox-core` do pacote recém-instalado para o projecto antes da validação e deixa de falhar por carregar manifest antigo.
- [x] AC2. O updater usa metadata suficiente para distinguir ficheiros de framework alterados pelo release de ficheiros customizados pelo utilizador, preservando customizações locais durante o sync.
- [x] AC3. `aiox install --dry-run` não escreve ficheiros nem executa bootstrap/sync, mesmo com `--quiet` e `--force`.
- [x] AC4. O install brownfield deixa de sobrescrever cegamente `.claude/settings.json`, `.aiox-core/core-config.yaml`, `.env.example` e `development/agents/*/MEMORY.md`.
- [x] AC5. `aiox validate` expõe um modo explícito para saltar verificação de assinatura em cenários de recovery/documented break-glass.
- [x] AC6. Há testes automatizados cobrindo os regressions de update, dry-run e preservação brownfield.

## Tasks

- [x] Corrigir o sync pós-`npm install` no updater e reaproveitar manifest/source package na validação
- [x] Endurecer persistência de manifest/version metadata para brownfield upgrades
- [x] Implementar guard global de `dryRun` no wizard modular
- [x] Preservar e/ou fazer merge dos ficheiros mutáveis de brownfield reportados no issue
- [x] Adicionar testes de regressão para updater, wizard/install e settings merge

## Execution

- Validar localmente com `npm run lint`, `npm run typecheck`, `npm test`
- Smoke test manual em projeto brownfield publicado `5.0.4 -> 5.0.7`, incluindo preservação de `MEMORY.md` customizado
- Regressions adicionais corrigidos durante smoke test final:
- preservação de `permissions.allow/deny` em `.claude/settings.json` mesmo quando o gerador não produz regras novas
- seleção do manifest instalado mais completo a partir do package anterior para evitar overwrite falso de ficheiros mutáveis
- `npm run lint` ✓
- `npm run typecheck` ✓
- `npm test` ✓

## File List

- [docs/stories/epic-123/STORY-123.5-installer-update-recovery-and-brownfield-safety.md](./STORY-123.5-installer-update-recovery-and-brownfield-safety.md)
- [.aiox-core/cli/commands/validate/index.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/.aiox-core/cli/commands/validate/index.js)
- [.aiox-core/infrastructure/scripts/generate-settings-json.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/.aiox-core/infrastructure/scripts/generate-settings-json.js)
- [packages/installer/src/config/configure-environment.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/packages/installer/src/config/configure-environment.js)
- [packages/installer/src/installer/aiox-core-installer.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/packages/installer/src/installer/aiox-core-installer.js)
- [packages/installer/src/installer/brownfield-upgrader.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/packages/installer/src/installer/brownfield-upgrader.js)
- [packages/installer/src/updater/index.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/packages/installer/src/updater/index.js)
- [packages/installer/src/wizard/ide-config-generator.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/packages/installer/src/wizard/ide-config-generator.js)
- [packages/installer/src/wizard/index.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/packages/installer/src/wizard/index.js)
- [tests/installer/aiox-core-installer.test.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/tests/installer/aiox-core-installer.test.js)
- [tests/installer/brownfield-upgrader.test.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/tests/installer/brownfield-upgrader.test.js)
- [tests/installer/configure-environment-brownfield.test.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/tests/installer/configure-environment-brownfield.test.js)
- [tests/installer/generate-settings-json.test.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/tests/installer/generate-settings-json.test.js)
- [packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js)
- [tests/updater/aiox-updater.test.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/tests/updater/aiox-updater.test.js)
- [tests/wizard/integration.test.js](/Users/rafaelcosta/Projects/AIOX/aiox-core/tests/wizard/integration.test.js)
Loading
Loading