@ingeniomaps/cauce 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -5,6 +5,9 @@ name: Agent learning proposals
|
|
|
5
5
|
|
|
6
6
|
on:
|
|
7
7
|
schedule:
|
|
8
|
+
# Semanal la investigación, mensual la consolidación. La propuesta sólo vale si antes hubo
|
|
9
|
+
# informes: consolidar sin ellos produce un andamiaje vacío que nadie puede aprobar.
|
|
10
|
+
- cron: "17 13 * * 1"
|
|
8
11
|
- cron: "17 13 1 * *"
|
|
9
12
|
workflow_dispatch:
|
|
10
13
|
inputs:
|
|
@@ -61,7 +64,97 @@ jobs:
|
|
|
61
64
|
fs.appendFileSync(process.env.GITHUB_OUTPUT, `agents=${JSON.stringify(list)}\n`)
|
|
62
65
|
' "$all"
|
|
63
66
|
|
|
67
|
+
# La investigación necesita un modelo, así que este job sólo corre si el repositorio declara una
|
|
68
|
+
# credencial. Sin ella se saltea entero en vez de abrir un PR por cargo con un informe vacío, que
|
|
69
|
+
# es ruido con forma de trabajo. `learn` solo arma el andamiaje; llenarlo es lo que hace un agente.
|
|
70
|
+
research:
|
|
71
|
+
if: github.event.schedule != '17 13 1 * *'
|
|
72
|
+
needs: discover
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
strategy:
|
|
75
|
+
fail-fast: false
|
|
76
|
+
max-parallel: 3
|
|
77
|
+
matrix:
|
|
78
|
+
agent: ${{ fromJSON(needs.discover.outputs.agents) }}
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v4
|
|
81
|
+
- uses: actions/setup-node@v4
|
|
82
|
+
with:
|
|
83
|
+
node-version: "24"
|
|
84
|
+
- name: Check credential
|
|
85
|
+
id: creds
|
|
86
|
+
env:
|
|
87
|
+
KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
88
|
+
run: |
|
|
89
|
+
if [ -n "$KEY" ]; then
|
|
90
|
+
echo "ready=true" >> "$GITHUB_OUTPUT"
|
|
91
|
+
else
|
|
92
|
+
echo "ready=false" >> "$GITHUB_OUTPUT"
|
|
93
|
+
echo "Sin ANTHROPIC_API_KEY: la investigación semanal queda para correr a mano." >&2
|
|
94
|
+
fi
|
|
95
|
+
- name: Resolve CLI
|
|
96
|
+
if: steps.creds.outputs.ready == 'true'
|
|
97
|
+
id: cli
|
|
98
|
+
run: |
|
|
99
|
+
for candidate in tools/ops.js engine/cli/ops.js; do
|
|
100
|
+
if [ -f "$candidate" ]; then
|
|
101
|
+
echo "path=$candidate" >> "$GITHUB_OUTPUT"
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
done
|
|
105
|
+
echo "No se encontró el CLI de Cauce." >&2
|
|
106
|
+
exit 1
|
|
107
|
+
- name: Prepare report
|
|
108
|
+
if: steps.creds.outputs.ready == 'true'
|
|
109
|
+
env:
|
|
110
|
+
AGENT: ${{ matrix.agent }}
|
|
111
|
+
OPS: ${{ steps.cli.outputs.path }}
|
|
112
|
+
run: node "$OPS" learn "$AGENT"
|
|
113
|
+
# El prompt no lo escribe este archivo: lo declara el propio cargo en su
|
|
114
|
+
# `learning/CODEX_AUTOMATION.md`. Así un cargo nuevo trae su investigación sin tocar el cron.
|
|
115
|
+
- name: Research
|
|
116
|
+
if: steps.creds.outputs.ready == 'true'
|
|
117
|
+
env:
|
|
118
|
+
AGENT: ${{ matrix.agent }}
|
|
119
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
120
|
+
run: |
|
|
121
|
+
npm install -g @anthropic-ai/claude-code
|
|
122
|
+
claude -p "Leé el learning/CODEX_AUTOMATION.md del cargo $AGENT y ejecutá exactamente lo que declara. El informe de esta semana ya está creado y vacío: completalo. Cada afirmación necesita enlace, fecha y versión; lo que no puedas corroborar va a Preguntas abiertas en vez de afirmarse. El único archivo que escribís es ese informe."
|
|
123
|
+
- name: Detect changes
|
|
124
|
+
if: steps.creds.outputs.ready == 'true'
|
|
125
|
+
id: changes
|
|
126
|
+
run: |
|
|
127
|
+
if git diff --quiet; then
|
|
128
|
+
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
129
|
+
else
|
|
130
|
+
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
131
|
+
fi
|
|
132
|
+
- name: Open research pull request
|
|
133
|
+
if: steps.creds.outputs.ready == 'true' && steps.changes.outputs.changed == 'true'
|
|
134
|
+
env:
|
|
135
|
+
AGENT: ${{ matrix.agent }}
|
|
136
|
+
GH_TOKEN: ${{ github.token }}
|
|
137
|
+
run: |
|
|
138
|
+
stamp="$(date -u +%Y-%m-%d)"
|
|
139
|
+
branch="automation/$AGENT-research-$stamp"
|
|
140
|
+
report="$(git diff --name-only -- "agents/*/$AGENT/learning/reports/$stamp.md")"
|
|
141
|
+
if [ -z "$report" ]; then
|
|
142
|
+
echo "El agente cambió archivos fuera de su informe; no se abre PR." >&2
|
|
143
|
+
git diff --name-only >&2
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
146
|
+
git config user.name "github-actions[bot]"
|
|
147
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
148
|
+
git switch -c "$branch"
|
|
149
|
+
git add "$report"
|
|
150
|
+
git commit -m "docs($AGENT): weekly research $stamp"
|
|
151
|
+
git push origin "$branch"
|
|
152
|
+
gh pr create --base "$GITHUB_REF_NAME" --head "$branch" \
|
|
153
|
+
--title "$AGENT weekly research — $stamp" \
|
|
154
|
+
--body "Investigación semanal de la profesión. No modifica SKILL.md ni propone cambios: alimenta la propuesta mensual."
|
|
155
|
+
|
|
64
156
|
propose:
|
|
157
|
+
if: github.event.schedule == '17 13 1 * *' || github.event_name == 'workflow_dispatch'
|
|
65
158
|
needs: discover
|
|
66
159
|
runs-on: ubuntu-latest
|
|
67
160
|
strategy:
|
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,32 @@ esa operación sea confiable en vez de sólo cómoda: acá se lee qué cambió a
|
|
|
8
8
|
un cambio en el protocolo, en las reglas del sistema o en un guard es visible para el usuario y sube
|
|
9
9
|
minor aunque no toque una sola línea de código.
|
|
10
10
|
|
|
11
|
+
## [0.13.0] - 2026-08-16
|
|
12
|
+
|
|
13
|
+
### Añadido
|
|
14
|
+
|
|
15
|
+
- **El ciclo de aprendizaje se cierra solo hasta la firma, y sigue solo después de ella.** Faltaban
|
|
16
|
+
las dos mitades entre «recomendación» y «contrato actualizado`»:
|
|
17
|
+
- `/agent-propose <cargo>` escribe el cambio concreto —el texto exacto, archivo por archivo— y lo
|
|
18
|
+
contrasta contra los casos vigentes. Antes la propuesta llegaba con «Cambio propuesto: por
|
|
19
|
+
definir», y nadie firma una intención.
|
|
20
|
+
- `/agent-promote <cargo>` aplica una propuesta **ya firmada**, registra en `HISTORY.md` y manda a
|
|
21
|
+
correr los casos. Dos candados: se niega si «Aprobación humana» no tiene responsable —un agente no
|
|
22
|
+
se autoriza a sí mismo— y exige verificar, porque aplicar sin correr los casos deja un contrato
|
|
23
|
+
cambiado sin saber si se sostiene.
|
|
24
|
+
- Aplica **prosa, no un parche**, a propósito: un parche envejece si alguien toca el archivo mientras
|
|
25
|
+
la propuesta espera firma. El costo es que aplicar exige criterio, y por eso toda desviación se
|
|
26
|
+
escribe en la propia propuesta: quien firmó tiene derecho a saber qué se aplicó de lo que firmó.
|
|
27
|
+
- Investigación **semanal** en el cron, además de la consolidación mensual. Corre sólo si el
|
|
28
|
+
repositorio declara `ANTHROPIC_API_KEY`; sin ella se saltea entero en vez de abrir un PR por cargo
|
|
29
|
+
con un informe vacío. El prompt no vive en el cron: lo declara cada cargo en su
|
|
30
|
+
`learning/CODEX_AUTOMATION.md`, así que un cargo nuevo trae su investigación sin tocar el workflow.
|
|
31
|
+
|
|
32
|
+
### Nota
|
|
33
|
+
|
|
34
|
+
El cron es de Cauce, no de las instancias: una empresa no lo recibe. Su ciclo de aprendizaje es por
|
|
35
|
+
comando, y activarlo en su propio repositorio es decisión suya.
|
|
36
|
+
|
|
11
37
|
## [0.12.0] - 2026-08-16
|
|
12
38
|
|
|
13
39
|
### Cambiado
|
|
@@ -32,6 +32,14 @@
|
|
|
32
32
|
{
|
|
33
33
|
"source": "../../workflows/agent-eval.js",
|
|
34
34
|
"target": ".claude/workflows/agent-eval.js"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"source": "../../workflows/agent-propose.js",
|
|
38
|
+
"target": ".claude/workflows/agent-propose.js"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"source": "../../workflows/agent-promote.js",
|
|
42
|
+
"target": ".claude/workflows/agent-promote.js"
|
|
35
43
|
}
|
|
36
44
|
],
|
|
37
45
|
"capabilities": {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Aplica una propuesta ya firmada y comprueba que el cargo siga aguantando sus casos.
|
|
2
|
+
//
|
|
3
|
+
// Es el único recorrido que modifica un cargo, y por eso tiene dos candados que no se pueden saltar:
|
|
4
|
+
//
|
|
5
|
+
// 1. Se niega si «Aprobación humana» no está firmada con responsable. Un agente no se autoriza a sí
|
|
6
|
+
// mismo, y `automatic_apply: false` sigue siendo cierto: esto no corre solo, lo corre alguien
|
|
7
|
+
// después de firmar.
|
|
8
|
+
// 2. Corre los casos adversariales al terminar. Aplicar sin verificar deja un contrato cambiado y
|
|
9
|
+
// nadie sabiendo si todavía se sostiene, que es peor que no haber aplicado.
|
|
10
|
+
//
|
|
11
|
+
// Aplica **prosa**, no un parche, y es a propósito: la propuesta dice «agregar dos viñetas después de
|
|
12
|
+
// la última existente». Un parche envejece si alguien toca el archivo mientras la propuesta espera
|
|
13
|
+
// firma; la prosa sobrevive a eso, a cambio de exigir criterio al aplicar. Ese criterio queda visible:
|
|
14
|
+
// toda desviación se escribe en la propia propuesta.
|
|
15
|
+
export const meta = {
|
|
16
|
+
name: 'agent-promote',
|
|
17
|
+
description: 'Aplica una propuesta firmada, registra el cambio y vuelve a correr los casos',
|
|
18
|
+
whenToUse: 'Después de firmar «Aprobación humana» en una propuesta mensual.',
|
|
19
|
+
phases: [
|
|
20
|
+
{ title: 'Firma', detail: 'Sin aprobación humana no se toca nada' },
|
|
21
|
+
{ title: 'Aplicar', detail: 'El cambio, archivo por archivo' },
|
|
22
|
+
{ title: 'Registrar', detail: 'Historial del cargo' },
|
|
23
|
+
{ title: 'Verificar', detail: 'Los casos adversariales contra el contrato nuevo' },
|
|
24
|
+
],
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ROOT = '{{OPS_DIR}}'.replace(/\/+$/, '') || '.'
|
|
28
|
+
const AGENT = String((typeof args === 'string' ? args : (args || {}).agent) || '').trim()
|
|
29
|
+
const PERIOD = String((args || {}).period || '').trim()
|
|
30
|
+
|
|
31
|
+
const FIRMA = {
|
|
32
|
+
type: 'object', additionalProperties: false, required: ['dir', 'proposal', 'approved'],
|
|
33
|
+
properties: {
|
|
34
|
+
dir: { type: 'string' },
|
|
35
|
+
proposal: { type: 'string' },
|
|
36
|
+
approved: { type: 'boolean' },
|
|
37
|
+
signedBy: { type: 'string' },
|
|
38
|
+
state: { type: 'string' },
|
|
39
|
+
hasChange: { type: 'boolean' },
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const APLICADO = {
|
|
44
|
+
type: 'object', additionalProperties: false, required: ['applied', 'files'],
|
|
45
|
+
properties: {
|
|
46
|
+
applied: { type: 'boolean' },
|
|
47
|
+
files: { type: 'array', items: { type: 'string' } },
|
|
48
|
+
deviations: { type: 'string' },
|
|
49
|
+
newCase: { type: 'string' },
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function finish(result) {
|
|
54
|
+
log(`Fin: ${JSON.stringify(result)}`)
|
|
55
|
+
return result
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const stop = (reason, detail = '') => {
|
|
59
|
+
log(`Checkpoint: ${reason}${detail ? ` — ${detail}` : ''}`)
|
|
60
|
+
return finish({ stopped: true, reason, detail })
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!AGENT) return stop('sin-cargo', 'pasá el slug del cargo')
|
|
64
|
+
|
|
65
|
+
phase('Firma')
|
|
66
|
+
|
|
67
|
+
const firma = await agent(
|
|
68
|
+
`From ${ROOT}, run "node tools/ops.js agents list --json" and take the path it printed for ${AGENT}. ` +
|
|
69
|
+
`Set dir to "${ROOT}/<path>": that command prints paths relative to ${ROOT}.\n\n` +
|
|
70
|
+
`Find the newest <dir>/learning/proposals/AAAA-MM.md` +
|
|
71
|
+
`${PERIOD ? `, preferring ${PERIOD}.md` : ''} and set proposal to its full path. Read **only** its ` +
|
|
72
|
+
`"Aprobación humana" and "Cambio propuesto" sections and report, without interpreting in anyone's ` +
|
|
73
|
+
`favour:\n` +
|
|
74
|
+
`- approved: true only if the state says it is approved AND a named person is recorded. "pendiente", ` +
|
|
75
|
+
`"por definir" or an empty responsible means false.\n` +
|
|
76
|
+
`- signedBy: the person recorded, or empty.\n` +
|
|
77
|
+
`- state: the literal state line.\n` +
|
|
78
|
+
`- hasChange: true only if "Cambio propuesto" carries a concrete change; "por definir" means false.`,
|
|
79
|
+
{ schema: FIRMA, label: 'firma' },
|
|
80
|
+
)
|
|
81
|
+
if (!firma) return stop('sin-propuesta', `no se pudo leer una propuesta de ${AGENT}`)
|
|
82
|
+
if (!firma.hasChange) {
|
|
83
|
+
return stop('propuesta-vacia', `${firma.proposal} no tiene cambio concreto: corré /agent-propose primero`)
|
|
84
|
+
}
|
|
85
|
+
if (!firma.approved) {
|
|
86
|
+
return stop('sin-firma', `${firma.proposal} no está aprobada (${firma.state || 'sin estado'}). ` +
|
|
87
|
+
'Firmá «Aprobación humana» con un responsable y repetí: nadie se autoriza a sí mismo')
|
|
88
|
+
}
|
|
89
|
+
log(`Aprobada por ${firma.signedBy}`)
|
|
90
|
+
|
|
91
|
+
phase('Aplicar')
|
|
92
|
+
|
|
93
|
+
const aplicado = await agent(
|
|
94
|
+
`La propuesta ${firma.proposal} está aprobada por ${firma.signedBy}. Aplicá su sección «Cambio ` +
|
|
95
|
+
`propuesto» al cargo ${AGENT} en ${firma.dir}, archivo por archivo, tal como la propuesta lo ` +
|
|
96
|
+
`describe.\n\n` +
|
|
97
|
+
`Reglas al aplicar:\n` +
|
|
98
|
+
`- Preferí agregar sobre reescribir. Si la propuesta dice «agregar después de X», agregá; no ` +
|
|
99
|
+
`reordenes ni reformules lo que ya estaba.\n` +
|
|
100
|
+
`- Si la propuesta pide crear un caso adversarial nuevo, crealo con el enunciado que da y con ` +
|
|
101
|
+
`**cuatro** comportamientos esperados, como todos los del catálogo. Si el enunciado trae más, ` +
|
|
102
|
+
`fusioná los que se solapen sin perder ninguna idea.\n` +
|
|
103
|
+
`- Si algo de la propuesta ya no aplica —el archivo cambió, la sección no existe— **no lo fuerces**: ` +
|
|
104
|
+
`dejalo sin aplicar y reportalo como desviación.\n` +
|
|
105
|
+
`- Toda desviación, por chica que sea, se escribe al final de «Aprobación humana» en la propia ` +
|
|
106
|
+
`propuesta, explicando qué se hizo distinto y por qué. Quien firmó tiene derecho a saber qué se ` +
|
|
107
|
+
`aplicó de lo que firmó.\n\n` +
|
|
108
|
+
`No toques otros cargos. No hagas commit ni push. Devolvé qué archivos modificaste, el caso nuevo si ` +
|
|
109
|
+
`lo creaste, y las desviaciones —o cadena vacía si no hubo—.`,
|
|
110
|
+
{ schema: APLICADO, label: `aplica:${AGENT}` },
|
|
111
|
+
)
|
|
112
|
+
if (!aplicado || !aplicado.applied) {
|
|
113
|
+
return stop('no-aplicada', 'el cambio no se pudo aplicar; la propuesta queda como estaba')
|
|
114
|
+
}
|
|
115
|
+
log(`Aplicado en: ${(aplicado.files || []).join(', ')}`)
|
|
116
|
+
|
|
117
|
+
phase('Registrar')
|
|
118
|
+
|
|
119
|
+
await agent(
|
|
120
|
+
`Agregá una fila a ${firma.dir}/learning/HISTORY.md con la fecha de hoy —obtenela con "date +%F"—, ` +
|
|
121
|
+
`la propuesta ${firma.proposal}, decisión "Aprobada", quién firmó (${firma.signedBy}) y qué se ` +
|
|
122
|
+
`aplicó: ${(aplicado.files || []).join(', ')}` +
|
|
123
|
+
`${aplicado.newCase ? ` más el caso ${aplicado.newCase}` : ''}` +
|
|
124
|
+
`${aplicado.deviations ? `. Desviaciones: ${aplicado.deviations}` : ''}.\n\n` +
|
|
125
|
+
`Respetá el formato de tabla que ya tiene el archivo. No toques ninguna otra cosa, no hagas commit.`,
|
|
126
|
+
{ label: 'historial' },
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
phase('Verificar')
|
|
130
|
+
|
|
131
|
+
log('El contrato cambió: los casos valen sólo si se vuelven a correr contra la versión nueva.')
|
|
132
|
+
return finish({
|
|
133
|
+
agent: AGENT,
|
|
134
|
+
proposal: firma.proposal,
|
|
135
|
+
signedBy: firma.signedBy,
|
|
136
|
+
files: aplicado.files || [],
|
|
137
|
+
newCase: aplicado.newCase || '',
|
|
138
|
+
deviations: aplicado.deviations || '',
|
|
139
|
+
next: `corré /agent-eval ${AGENT}: el registro anterior quedó viejo y no vale para el contrato nuevo`,
|
|
140
|
+
})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Convierte la recomendación de los informes en el cambio concreto que una persona puede aprobar.
|
|
2
|
+
//
|
|
3
|
+
// `learn --proposal` consolida qué recomendaron los informes de la semana, pero deja «Cambio
|
|
4
|
+
// propuesto: por definir». Eso no es aprobable: nadie firma una intención. Este recorrido escribe el
|
|
5
|
+
// texto exacto que habría que agregar, archivo por archivo, y lo contrasta contra los casos
|
|
6
|
+
// adversariales vigentes antes de proponerlo.
|
|
7
|
+
//
|
|
8
|
+
// Nunca aplica nada, y no es una formalidad: la sección «Aprobación humana» la firma una persona, y
|
|
9
|
+
// hasta que esté firmada `/agent-promote` se niega a tocar el cargo.
|
|
10
|
+
export const meta = {
|
|
11
|
+
name: 'agent-propose',
|
|
12
|
+
description: 'Escribe el cambio concreto de una propuesta mensual y lo contrasta con los casos',
|
|
13
|
+
whenToUse: 'Después de que el ciclo consolide una propuesta, para dejarla en estado aprobable.',
|
|
14
|
+
phases: [
|
|
15
|
+
{ title: 'Contexto', detail: 'Contrato, fuentes, conductas y casos vigentes' },
|
|
16
|
+
{ title: 'Proponer', detail: 'El cambio exacto, sus riesgos y su evaluación' },
|
|
17
|
+
],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// El prefijo lo completa `automation install`. El runtime de workflows no expone `process`.
|
|
21
|
+
const ROOT = '{{OPS_DIR}}'.replace(/\/+$/, '') || '.'
|
|
22
|
+
const AGENT = String((typeof args === 'string' ? args : (args || {}).agent) || '').trim()
|
|
23
|
+
const PERIOD = String((args || {}).period || '').trim()
|
|
24
|
+
|
|
25
|
+
const CONTEXT = {
|
|
26
|
+
type: 'object', additionalProperties: false, required: ['dir', 'proposal', 'cases'],
|
|
27
|
+
properties: {
|
|
28
|
+
dir: { type: 'string' },
|
|
29
|
+
proposal: { type: 'string' },
|
|
30
|
+
cases: { type: 'integer' },
|
|
31
|
+
hasChange: { type: 'boolean' },
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const RESULT = {
|
|
36
|
+
type: 'object', additionalProperties: false, required: ['completed', 'files'],
|
|
37
|
+
properties: {
|
|
38
|
+
completed: { type: 'boolean' },
|
|
39
|
+
files: { type: 'array', items: { type: 'string' } },
|
|
40
|
+
newCase: { type: 'string' },
|
|
41
|
+
risks: { type: 'integer' },
|
|
42
|
+
summary: { type: 'string' },
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function finish(result) {
|
|
47
|
+
log(`Fin: ${JSON.stringify(result)}`)
|
|
48
|
+
return result
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const stop = (reason, detail = '') => {
|
|
52
|
+
log(`Checkpoint: ${reason}${detail ? ` — ${detail}` : ''}`)
|
|
53
|
+
return finish({ stopped: true, reason, detail })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!AGENT) return stop('sin-cargo', 'pasá el slug del cargo')
|
|
57
|
+
|
|
58
|
+
phase('Contexto')
|
|
59
|
+
|
|
60
|
+
const contexto = await agent(
|
|
61
|
+
`From ${ROOT}, run "node tools/ops.js agents list --json" and take the path it printed for ${AGENT}. ` +
|
|
62
|
+
`Set dir to "${ROOT}/<path>": that command prints paths relative to ${ROOT} and the next agent runs ` +
|
|
63
|
+
`from elsewhere, so the prefix is not optional.\n\n` +
|
|
64
|
+
`Then find the newest file under <dir>/learning/proposals/ named AAAA-MM.md` +
|
|
65
|
+
`${PERIOD ? `, preferring ${PERIOD}.md` : ''}. Set proposal to its full path. Read it and set ` +
|
|
66
|
+
`hasChange to true only if its "Cambio propuesto" section already carries a concrete change —if it ` +
|
|
67
|
+
`still says "por definir", it is false.\n\n` +
|
|
68
|
+
`Finally run "node tools/ops.js evaluate ${AGENT} --cases" and set cases to how many it listed. ` +
|
|
69
|
+
`Report only what the commands printed.`,
|
|
70
|
+
{ schema: CONTEXT, label: 'contexto' },
|
|
71
|
+
)
|
|
72
|
+
if (!contexto) return stop('sin-contexto', `no se pudo ubicar el cargo ${AGENT}`)
|
|
73
|
+
if (contexto.hasChange) {
|
|
74
|
+
return stop('ya-propuesta', `${contexto.proposal} ya tiene un cambio concreto; revisalo o borralo antes`)
|
|
75
|
+
}
|
|
76
|
+
log(`${contexto.proposal} · ${contexto.cases} caso(s) vigentes`)
|
|
77
|
+
|
|
78
|
+
phase('Proponer')
|
|
79
|
+
|
|
80
|
+
const propuesta = await agent(
|
|
81
|
+
`Sos quien mantiene el cargo ${AGENT}. La propuesta ${contexto.proposal} consolidó lo que ` +
|
|
82
|
+
`recomendaron los informes semanales, pero le falta lo único que una persona puede aprobar: el ` +
|
|
83
|
+
`cambio concreto.\n\n` +
|
|
84
|
+
`Leé los informes citados en su sección «Hallazgos», el SKILL.md del cargo, ` +
|
|
85
|
+
`${contexto.dir}/learning/sources.yaml, ${contexto.dir}/evaluations/expected-behaviors.yaml y sus ` +
|
|
86
|
+
`casos en ${contexto.dir}/evaluations/cases/. Después completá **sólo** estas tres secciones de la ` +
|
|
87
|
+
`propuesta, sin tocar ninguna otra ni el frontmatter:\n\n` +
|
|
88
|
+
`**Cambio propuesto**: el texto exacto a agregar o reemplazar, archivo por archivo, citando la ` +
|
|
89
|
+
`sección o la línea que se toca. Que se pueda aplicar leyéndolo, sin volver al informe. Preferí ` +
|
|
90
|
+
`agregar sobre reescribir: el núcleo del contrato no está en discusión. Si una fuente no se pudo ` +
|
|
91
|
+
`corroborar, dejalo escrito en el propio texto en vez de afirmarla.\n\n` +
|
|
92
|
+
`**Riesgos y regresiones**: qué caso adversarial existente podría empezar a fallar y qué parte del ` +
|
|
93
|
+
`contrato podría contradecirse. Si dos reglas quedan en tensión aparente, resolvela en el texto ` +
|
|
94
|
+
`propuesto, no la dejes para quien aplique.\n\n` +
|
|
95
|
+
`**Evaluación**: contrastá el cambio contra los ${contexto.cases} casos vigentes, uno por uno, y ` +
|
|
96
|
+
`decí si cada uno sigue pasando. Si una conducta prohibida nueva no tiene caso que la distinga de ` +
|
|
97
|
+
`las que ya están, escribí el enunciado del caso que haría falta —con **cuatro** comportamientos ` +
|
|
98
|
+
`esperados, como todos los del catálogo— y **no crees el archivo**: cambiar el denominador de la ` +
|
|
99
|
+
`evaluación es parte de lo que se aprueba.\n\n` +
|
|
100
|
+
`No modifiques SKILL.md, sources.yaml, expected-behaviors.yaml ni los casos: esto es una propuesta, ` +
|
|
101
|
+
`no su aplicación. No toques «Aprobación humana»: esa la firma una persona. No hagas commit ni push. ` +
|
|
102
|
+
`El único archivo que escribís es la propuesta.\n\n` +
|
|
103
|
+
`Devolvé qué archivos tocaría el cambio, el enunciado del caso nuevo si lo hay, cuántos riesgos ` +
|
|
104
|
+
`identificaste y un resumen en una frase.`,
|
|
105
|
+
{ schema: RESULT, label: `propone:${AGENT}` },
|
|
106
|
+
)
|
|
107
|
+
if (!propuesta) return stop('sin-propuesta', 'el agente no devolvió resultado')
|
|
108
|
+
|
|
109
|
+
log(`Tocaría: ${(propuesta.files || []).join(', ')}`)
|
|
110
|
+
return finish({
|
|
111
|
+
agent: AGENT,
|
|
112
|
+
proposal: contexto.proposal,
|
|
113
|
+
files: propuesta.files || [],
|
|
114
|
+
risks: propuesta.risks || 0,
|
|
115
|
+
newCase: propuesta.newCase || '',
|
|
116
|
+
next: 'firmá «Aprobación humana» y corré /agent-promote',
|
|
117
|
+
})
|