Skip to content

Settings ownership P59: visible help configuration rejection (E2992 v23) #3153

Open

Description

@mblua

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 typecheck

Conservar 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=all

Reviewer 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 --required

Lead 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

  1. mblua commented on Oct 28, 2026

    @mblua
    OwnerAuthor

    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 postponed so it can be resumed later; not assigned to any room meanwhile.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpostponedOn hold by decision; resume laterrefactor

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions