Repository navigation
Settings ownership P59: visible help configuration rejection (E2992 v23) #3153
Description
Part of #2992. This phase implements the user-required strict rejection of duplicate JSON members in consumed AgentsCommander configuration. Return an error with the file intact; never choose first/last, repair, default or partially consume duplicates. Other errors preserve their existing policy.
Approved Full R17 specification: D:/0_repos/AgentsCommander_iac/.ac/plans/2992-settings-owner/plan-v23-settings-owner.md
Plan-SHA256: FA45A1A72BD0A8DF30F3AFD0BAE6DDA3159FA5DCD65D5F7B4EC5CDFF4F6B34F3
Phase: P59; owner ac-dev-webpage-ui-v5; class design-bearing; depends on P58 (#3152), which must be merged before starting.
Phase file: D:/0_repos/AgentsCommander_iac/.ac/plans/2992-settings-owner/P59-v1-help-ui-rejection.md
Phase-SHA256: 86610CB48D22AE1FA94D6896A00B76ACD11155BCE0684F4B78897C20CC165CF2
Consensus: D:/0_repos/AgentsCommander_iac/.ac/project-shared/2992-settings-owner/minimum-path-20261016/full-r17-consensus-approved-v1.json
Source is gated by the exact mapping/standing-authorization receipt and a successful cold implementation dispatch. Branch is created only at phase start from current synchronized main. Product checks are executed exclusively by shipper; no intermediate app build. One final requested no-bundle build after all phases merge.
The following immutable phase specification contains the situation, decided change, exact allowed files, acceptance and verification commands. It is the sole implementation specification; the issue/mapping supplies administrative identity only.
E2992 P59 v1 — help-ui-rejection
Status: READY_FOR_IMPLEMENTATION
Versión:1; inicial R16, sin archivo histórico creado en R15. Readiness sólo AUTORÍA; consenso/ejecución pendientes.
Owner: ac-dev-webpage-ui-v5. Class: design-bearing. Changed contract: UI consumo de error. Depends-on: P58.
Epic2992; workflow Full continuación R16, gate frío full-r16-planning-cold-gate-approved-v1.json. Sólo autoría autorizada; fuente bloqueada.
Participantes exactos: ac-architect-v5, ac-dev-rust-v5, ac-dev-webpage-ui-v5, ac-dev-rust-grinch-v5, ac-architect-grinch-v5. Mismo master+veinte digests, revisión pendiente; lead despacha, votos previos no se transfieren.
Repo D:/0_repos/AgentsCommander_iac/.ac/room-09-ac-dev-team-v5/repo-AgentsCommander; diseño9ce147f8454af7ad9d4bd27c1a358f93fd229850. Rutas producto relativas al repo, planes desde .ac.
Fuente pedido: autorización rechazo p45-user-reject-duplicate-json-paths-authorization-v1.json/decisión restore v2/propuesta v3/reconciliación R15-B1, no permiso de fuente.
Riesgo rutinario funcional transversal: selección/conservación/concurrencia cooperante, sin host attestation/signing/PATH hostil.
Archivos permitidos
Sólo MODIFIED/ADDED0/REMOVED0; 4 paths máximo10, un dueño, secuencial tras dependencia merged. No reabrir seis entregadas.
| Archivo relativo al repo | Impacto |
|---|---|
| src/shared/agent-help.ts | MODIFIED |
| src/shared/agent-help.overlay.test.ts | MODIFIED |
| src/shared/agent-help.test.ts | MODIFIED |
| src/sidebar/components/SettingsModal.tsx | MODIFIED |
Objetivo y corte verde
Ayuda UI distingue duplicateJsonMember de error opcional, muestra causa y bloquea ayuda/params/docs derivados. Dueño exclusivo ac-dev-webpage-ui-v5; backend P58 disponible, DTO exitoso intacto.
Decisiones por archivo
- src/shared/agent-help.ts: catch :105 ya no devuelve EMPTY_AGENT_HELP_OVERLAY para error con prefijo interno duplicateJsonMember:. Usar localError existente con prefijo duplicateJsonMember: como estado interno de fallo y local/remote nulos, sin campo wire nuevo; no convertirlo en overlay válido. Clasificar sólo negativa backend conocida (String/Error transport existente), no buscar claves/payload ni inferir permisos.
- resolveEntry:57 y resolveGeneral:74, resolvers de params/docs y sus helpers consultan estado de rechazo y no devuelven embedded, genérico, capa parcial ni valor previo como ayuda vigente: entry/general/docs retornan null y paramsExampleFor retorna "" conservando su firma string. Otros errores opcionales conservan fallback histórico; éxito/ausencia válidos conservan precedencia y embedded actual.
- src/sidebar/components/SettingsModal.tsx: mostrar mensaje de lectura existente en superficie de ayuda, deshabilitar controles dependientes (incluido fallback params genérico) mientras error de duplicado está presente. No bloquear settings ajenos ni introducir vista/flujo de reparación/export automático. Usuario corrige archivo y retry normal vuelve a cargar; éxito posterior reemplaza estado de error, sin store/backend nuevo.
- src/shared/agent-help.overlay.test.ts y agent-help.test.ts: extender fake transport/helpers existentes, no test por detalle de implementación. Rechazo duplicate de invoke mantiene mensaje y resolvers sin embedded/local/remote/params/docs parciales; error opcional no duplicate conserva fallback; válido después de corrección recupera precedencia. Verificar estado visible que usa SettingsModal sin screenshot/mouse/keyboard.
Sin cambio IPC/version/DTO de éxito ni parser de JSON frontend. Promesa rechazada existente es frontera; backend no impone UI response nueva.
Fuentes de datos/permiso/caps/locks y settings-owner quedan en Rust, no importar transporte a leaf ni leer archivos en UI.
Evidencia y aceptación propia
Reutilización confirmada estática en agent-help.ts :57/74/105: catch EMPTY y embedded son fallback actual; invoke Promise soporta Err sin nuevo protocolo. UI votará estos bytes y suficiencia SettingsModal/helpers. Ningún voto backend sustituye ese voto.
Verificación futura shipper con cwd repo y Node22.14.0/npm11.6.2: npm test -- src/shared/agent-help.overlay.test.ts src/shared/agent-help.test.ts; npm run typecheck. Suites deben ejecutar casos, fallo/selección vacía no PASS. Ningún build intermedio ni GUI.
Handoff P45: ocho nuevos cortes backend/UI completos, todavía faltan cuatro fuentes core/binding/activación. No afirmar cierre global ni reparar bytes desde UI.
Contrato común de admisión y preservación
Único dueño settings/RAM, sin nueva fuente productiva. Configuración JSON AC realmente consumida incluye core, menús/ayuda, catálogos, project/team/agent/replica/co-managed y config.state mixto por selección/perfiles. Primera lectura necesaria para boot/restore automático rechaza startup; lectura/reemplazo posterior rechaza uso dependiente. Sin discovery de no usados, defaults/partial/Off ante duplicate, reparación automática ni atomicidad multiarchivo.
Igualdad de claves decodificadas por objeto recursivo (x/\u0078 iguales); padre repetido invalida, padres/índices diferentes y literal a.b versus a/b válidos; overrides entre documentos válidos. No casefold/normalización Unicode/identidad filesystem de keys. No confundir IDs repetidos con duplicate JSON.
Bytes admitidos son los mismos que se parsean; antes Value/typed/filter/merge/shadowing/refresh/naming. Caps/locks/autoridad/identidad/revocation/rechecks actuales preceden y acompañan lectura; preparado no acredita actualidad. Ausencia verificada conserva ausencia; otros errores mantienen política, sintaxis siempre pasa por parser nativo.
Err duplicateJsonMember: con archivo/causa sin keys/values/env/token; conservar razón tipada donde existe. IPC/WS Err envelope actual, CLI mensaje/exit no cero, boot StartupError/present_fatal; workers sin Result cortan acción dependiente y ERROR sanitizado al sink/error_log_event existente. No log sólo WARN y fallback. Pre-auth remoto nunca revela path/payload; respuesta fija sensible intacta, diagnóstico local autorizado.
No tocar sessions/clocks/TASK/protocolos/journals/caches puramente operativos, capture/state ni loops TOML; config.state sí se valida entero por uso mixto. No parser/deps/framework/registry/transporte/esquema nuevo.
Preservar dueño/availability/guard por expresión/PTY permit hasta write, principal/caps/privacidad, nueve sessions/F2/FinalRetention, retención activa+archivada y QuitGate. P51 current_read fallible/factories/catch/guards/barriers/tail y cadencia/cancelación exactos; sin reabrir seis entregadas. Fallo parcial real nunca se llama rollback/no-write ni adopta defaults.
Capas y evidencia estática
Base9ce147f8454af7ad9d4bd27c1a358f93fd229850. Índice raíz room09 ready44413/262454 sin SHA; metadata_changed obliga fuente directa focal, no prueba universal de callers. Blueprint v3 y reconciliación R15-B1 respaldan receta, no sustituyen decisiones de esta spec ni son spec alternativa.
Delta duplicate previsto: cero parejas de módulos nuevas/retiradas; adaptador en path_identity y forwarding por relaciones existentes. Callbacks puros hacia hojas local_overlay/co_managed/capture_catalog/container_repos/session_context evitan imports al scanner/host. Nada lower->AppHandle/Tauri/CLI/WS. PRE histórico c61cfbd/SCC89 y module-arcs DDBC8A498AF2FBFCFA8275001F0A623A77BD4615E1AF601AECBA3231A47A16FC no son POST actual.
Shipper debe comprobar PRE/POST integrado exacto, SCC member sets idénticos, cero nuevas parejas externas, arcos regenerados byte-idénticos y guards verdes; manual métodos/macros/guards complementa detector. No compile/Send/lifetime/runtime/test/graph/CI/GUI PASS durante autoría. Hallazgo no previsto detiene corte/revisión acotada, no ampliar baseline.
Ejecución futura y entrega (no ejecutada durante autoría)
No mutar producto antes de cinco votos exactos, revisión humana y despacho del lead. Un solo corte en vuelo; dueño ac-dev-webpage-ui-v5. Revisor Rust Grinch, arquitectura Grinch y dueño UI revisan el mismo conjunto. Child/branch/bases/PR/recibos van en registro externo MAP provisto por lead externo, nunca editar Markdown congelado. Un child y un PR por fase; epic2992 permanece abierto hasta integración/build final.
Todas las compilaciones/tests/fmt/grafos/npm son ejecución FUTURA EXCLUSIVA ac-shipper-v5; Rust/UI sólo editan fuente autorizada. Los comandos focales anteriores se ejecutan DESPUÉS de inicializar R/EVIDENCE/toolchain/containment abajo. Shipper retiene stdout/stderr/exit/UTC con timeout del runner; filtro vacío/fallo/timeout bloquea. Verificar Node22.14.0/npm11.6.2/Rust1.98.1 y locks intactos; Cargo sólo proceso, sin config/jobs/profile/caches globales. Antes de integration tests repetir --no-run --message-format=json con sus tres --test y el mismo chequeo de executable containment. No build intermedio; único build de producto final tras todas las fases.
Git Bash en Windows; cwd raíz repo room09. Ejecutar con timeout/log/stdout/stderr/exit del runner existente, stdin no interactivo; guardar evidencia en target/2992-evidence/<PHASE_ID>. Fallo/timeout/filtro vacío no es PASS. No reset/clean global ni restauración que pise terceros.
set -euo pipefail
PHASE_ID="P59"
EXPECTED_DEPS="P58"
R="D:/0_repos/AgentsCommander_iac/.ac/room-09-ac-dev-team-v5/repo-AgentsCommander"
cd "$R"
test "$(git rev-parse --show-toplevel)" = "$R"
test -z "$(git status --porcelain=v1 --untracked-files=all)"
test -z "$(git diff --cached --name-only)"
: "${MAP:?lead debe proveer ruta exacta del mapping validado de este conjunto}"
export MAP PHASE_ID EXPECTED_DEPS
# CHILD/BRANCH provistos por lead desde mapping validado, no inventarlos.
export CHILD BRANCH
node --input-type=module - <<'NODE'
import fs from 'node:fs'; import assert from 'node:assert/strict';
const m=JSON.parse(fs.readFileSync(process.env.MAP,'utf8'));
const p=m.phases[process.env.PHASE_ID]; assert(p);
assert.equal(String(p.child),process.env.CHILD); assert.equal(p.branch,process.env.BRANCH);
assert.deepEqual(p.dependencies,process.env.EXPECTED_DEPS.split(',').filter(Boolean));
assert(new RegExp('^refactor/'+p.child+'-[a-z0-9-]+$').test(p.branch));
NODE
test "$(gh issue view "$CHILD" --repo mblua/AgentsCommander --json state --jq .state)" = OPEN
gh api --paginate repos/mblua/AgentsCommander/issues/2992/sub_issues --jq '.[].number' | grep -Fx "$CHILD"
git fetch origin main
git diff --name-status 9ce147f8454af7ad9d4bd27c1a358f93fd229850 origin/main
# Clasificar drift por paths/formatter/toolchain/CI. Refrescar sólo evidencia relevante.
EVIDENCE="$R/target/2992-evidence/$PHASE_ID"
mkdir -p "$EVIDENCE"
node --input-type=module - <<'NODE' > "$EVIDENCE/dependencies.tsv"
import fs from 'node:fs';import assert from 'node:assert/strict';
const m=JSON.parse(fs.readFileSync(process.env.MAP,'utf8'));
for(const id of m.phases[process.env.PHASE_ID].dependencies){
const p=m.phases[id]; assert(p.pr&&p.mergeSha); console.log(p.pr+'\t'+p.mergeSha);
}
NODE
while IFS=$'\t' read -r dep_pr merge_sha; do
test "$(gh pr view "$dep_pr" --repo mblua/AgentsCommander --json state --jq .state)" = MERGED
test "$(gh pr view "$dep_pr" --repo mblua/AgentsCommander --json baseRefName --jq .baseRefName)" = main
test "$(gh pr view "$dep_pr" --repo mblua/AgentsCommander --json mergeCommit --jq .mergeCommit.oid)" = "$merge_sha"
git merge-base --is-ancestor "$merge_sha" origin/main
done < "$EVIDENCE/dependencies.tsv"
git switch -c "$BRANCH" origin/main
BASE="$(git rev-parse HEAD)"
printf '%s\n' "$BASE" > "$EVIDENCE/base.sha"
git branch --show-current
git status --porcelain=v1 --untracked-files=all
node --version # esperado22.14.0
npm --version # esperado11.6.2
cd "$R"
npm ci
npm test -- src/shared/agent-help.overlay.test.ts src/shared/agent-help.test.ts
npm run typecheckConservar Node22.14.0/npm11.6.2/Rust1.98.1 de CI y locks existentes. Sin cambios de versiones/deps/formatter ajenos. Al escribir: revalidar branch/base/index/paths, registrar preimagen y output propio por path. Recuperar sólo bytes propios aún intactos; preservar cambios ajenos y reportar conflicto. Comparar full BASE..HEAD (jamás HEAD~1), staged y ordinary-untracked; todo hunk propio dentro de la tabla. Cero es estado válido, no ausencia de evidencia.
Antes de push/PR integrar origin/main localmente y resolver sólo conflictos autorizados. Registrar PRE actual (origin/main) e integrated HEAD limpios. Si merge incorpora cambios ajenos, mantener full diff desde BASE y documentar por separado ese delta byte-idéntico a upstream; comprobar también PRE..HEAD para aislar trabajo propio, sin ocultar cambios ni reiniciar diseño por drift ajeno. No ampliar union propia para silenciar gates.
cd "$R"
git fetch origin main
PRE="$(git rev-parse origin/main)"
git merge --no-edit origin/main
# Resolver/commit si corresponde; después debe estar limpio.
test -z "$(git status --porcelain=v1 --untracked-files=all)"
mkdir -p "$EVIDENCE/pre-source"
git archive "$PRE" | tar -x -C "$EVIDENCE/pre-source"
set +e
node scripts/01-rust_module-dependency-cycles.mjs "$EVIDENCE/pre-source/src-tauri" --emit-graph "$EVIDENCE/pre.json" --quiet > "$EVIDENCE/pre-detector.log" 2>&1
pre_exit=$?
node scripts/01-rust_module-dependency-cycles.mjs src-tauri --emit-graph "$EVIDENCE/post.json" --quiet > "$EVIDENCE/post-detector.log" 2>&1
post_exit=$?
set -e
test "$pre_exit" -le 1
test "$post_exit" -le 1
node scripts/02-module-arc-record.mjs --graph "$EVIDENCE/post.json" --out "$EVIDENCE/post-arcs.txt"
export EVIDENCE
node --input-type=module - <<'NODE'
import fs from 'node:fs';import assert from 'node:assert/strict';
const root=process.env.EVIDENCE,pre=JSON.parse(fs.readFileSync(root+'/pre.json')),post=JSON.parse(fs.readFileSync(root+'/post.json'));
function scc(g){
const a=new Map(g.modules.map(n=>[n.id,[]]));for(const e of g.edges)if(e.from!==e.to)a.get(e.from).push(e.to);
let i=0,idx=new Map,low=new Map,stack=[],on=new Set,sets=[];
function walk(v){idx.set(v,i);low.set(v,i++);stack.push(v);on.add(v);
for(const w of a.get(v)){if(!idx.has(w)){walk(w);low.set(v,Math.min(low.get(v),low.get(w)))}else if(on.has(w))low.set(v,Math.min(low.get(v),idx.get(w)))}
if(low.get(v)===idx.get(v)){let z=[],w;do{w=stack.pop();on.delete(w);z.push(w)}while(w!==v);sets.push(z.sort())}}
for(const v of a.keys())if(!idx.has(v))walk(v);
const cyclic=sets.filter(z=>z.length>1).sort((a,b)=>a[0].localeCompare(b[0]));
assert.equal(cyclic.length,g.summary.moduleCycles);return {sets,cyclic,map:new Map(sets.flatMap((z,i)=>z.map(v=>[v,i])))};
}
const a=scc(pre),b=scc(post);assert.deepEqual(b.cyclic,a.cyclic);
const arcs=g=>new Set(g.edges.filter(e=>e.from!==e.to).map(e=>e.from+' -> '+e.to)),old=arcs(pre),now=arcs(post);
const internal=(f,t)=>a.map.has(f)&&a.map.get(f)===a.map.get(t)&&a.sets[a.map.get(f)].length>1;
assert(internal(a.cyclic[0][0],a.cyclic[0][1])); // control positivo sobre SCC real
const outside=a.sets.find(z=>z.length===1);assert(outside);assert(!internal(a.cyclic[0][0],outside[0]));
const added=[...now].filter(x=>!old.has(x));for(const x of added){const [f,t]=x.split(' -> ');assert(internal(f,t),'new cross-boundary arc '+x)}
console.log(JSON.stringify({cyclicSccs:b.cyclic.length,memberSetsIdentical:true,newArcs:added,newCrossBoundaryPairs:0}));
NODE
cmp "$EVIDENCE/post-arcs.txt" src-tauri/module-arcs.txt
npm run check:arcs
npm run check:cycles
git diff --check "$BASE" HEAD
git diff --name-status "$BASE" HEAD
git diff --name-status "$PRE" HEAD
git diff --cached --exit-code
git status --porcelain=v1 --untracked-files=allReviewer compara grafos por SCC con algoritmo del instrumento: coverage.graphShape.cyclicSccs debe ser igual PRE/POST, TODOS los conjuntos completos de miembros idénticos, cero NUEVAS parejas from->to entre SCC PRE distintos; referencias internas sólo en SCC preexistente. No basta igual número/SCC count. Graph exit1 con archivo válido es ciclos existentes; exit2/3/análisis ausente bloquea. Instrumento1.1.1 ignora métodos/macros y algunos paths: revisar manualmente referencias reales/guards y el permiso PTY; no afirmar cero universal. Registro regenerado byte-idéntico y guards verdes antes de publicación, no POST imaginario.
Todos los jobs disparados y configured-required verdes en exact PR-head. CI actual: ci-tier/test-debt/rust-regression Windows/clippy-linux/rust-regression-linux/rust-linux-release-parity/rust-regression-macos/rust-fmt/terminal-snapshot-portable/windows-release-cli-smoke/issue-1850-windows-profile/frontend-regression según tier. Lead consulta reglas required reales y aplica ci:full para evidencia OS nueva; skips inexplicados/otro SHA bloquean. P41 incorpora filtros e2992_ en Linux/macOS existentes; Windows ya corre --lib --bins --tests. No nuevo runner.
git rev-parse HEAD
# Tras autorización de publicar: PR base main, head BRANCH/SHA exactos, cierre sólo CHILD.
gh pr view "$PR" --repo mblua/AgentsCommander --json baseRefName,headRefName,headRefOid,closingIssuesReferences,statusCheckRollup
gh pr checks "$PR" --repo mblua/AgentsCommander --watch
gh pr checks "$PR" --repo mblua/AgentsCommander --requiredLead verifica conjunto de cierres exactamente {CHILD}, tested-head, mergeSHA y ancestry en main; guarda recibos externos. No cierre de epic por keyword de fase. Build histórico sólo tras todos los children merged: shipper ejecuta npm run build:prod:no-bundle en main limpio/upstream origin/main/igualdad targetSHA; copia no-clobber de agentscommander_testeable.exe al target/release/agentscommander_standalone_room-09__.exe (UTC del build), entrega path/hash/bytes/UTC/SHAfuente; lead verifica y recién entonces cierra epic. No instalación/release/version bump.
Activity
mblua commentedon Oct 28, 2026
Postponed by user decision (2026-10-28): the settings-ownership effort went out of scope and its re-plan #3146 was closed as not planned. Kept open with
postponedso it can be resumed later; not assigned to any room meanwhile.