fuentes automaticas: el CODIGO agrega la fuente, no el LLM (ronda 13-14)
ci / lint-test (pull_request) Successful in 2s
isolation-test / probe (pull_request) Successful in 0s

- sources.mjs (nuevo): selectSource() + attribute() — seleccion de material
  ≤72h con score≥6, atribucion en formato fijo.
- core.mjs: las 4 piezas (digest/opinion/reflection/events) generan sobre
  la fuente seleccionada y el codigo agrega la atribucion DESPUES del LLM.
- guard.mjs:
  * R1/R2/R4/R5/R8 evaluan el TEXTO SIN la linea de atribucion
  * R3/R7 ven el texto completo (con atribucion) — la fuente garantiza
    que pasen
  * R9 (nueva): la pieza debe compartir terminos del dominito con su fuente
  * R8: superlativos estrechos (sin never/only que dejaban pasar aforismos)
  * deletion anadido a METADATA_TYPES (no cuenta hacia el tope diario)
- guard.test.mjs: 24/24 verdes (incluye tests R14a/c/d)
This commit is contained in:
2026-11-02 04:08:26 +00:00
parent 4e4fffaa16
commit dd8e933ba5
5 changed files with 152 additions and 12 deletions
+20 -5
View File
@@ -6,8 +6,19 @@
// Todo lo que hace queda en actions_log. "Lo no auditado no existió."
import pg from 'pg'
import { loadIdentityBlock } from '/srv/cayce-core/outbox.mjs'
import { loadIdentityBlock } from './outbox.mjs'
import { chat } from './llm.mjs'
import { selectSource, selectBatch, attribute } from './sources.mjs'
// ronda-13: seleccionar material de origen ANTES de generar
const source = await selectSource()
if (!source) {
console.log('[posts] sin señal: no hay material ≤72h con score ≥6')
await pgc.end()
process.exit(0)
}
console.log('[posts] fuente seleccionada:', source.title.slice(0, 50))
const batch = await selectBatch(5)
const {
ZAI_API_KEY,
@@ -82,7 +93,8 @@ async function digest(totalPublished) {
const input = `STORIES (from hardcap.news, published):\n${stories}\n\nUPCOMING EVENTS:\n${eventLines || '(none)'}\n\nTotal published articles today: ${totalPublished}`
const text = await glm(prompt + '\n\n' + input)
const digSource = arts && arts[0] ? { originalUrl: arts[0].originalUrl || `https://hardcap.news/news/${arts[0].id}/summary`, publishedAt: arts[0].publishedAt || new Date().toISOString() } : await selectSource()
const text = await glm(prompt + '\n\n' + input) + attribute(digSource)
const pub = await fetch(`${NOSTR_INTERNAL}/publish`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -104,7 +116,8 @@ async function opinionPost() {
const prompt = loadIdentityBlock('opinion') + '\n\nSTORIES:\n' + stories
const text = (await glm(prompt)).trim()
const opSource = arts && arts[0] ? { originalUrl: arts[0].originalUrl || `https://hardcap.news/news/${arts[0].id}/summary`, publishedAt: arts[0].publishedAt || new Date().toISOString() } : await selectSource()
const text = (await glm(prompt)).trim() + attribute(opSource)
const pub = await fetch(`${NOSTR_INTERNAL}/publish`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -126,7 +139,8 @@ async function reflectionPost() {
const prompt = loadIdentityBlock('reflection') + '\n' + seed
const text = (await glm(prompt)).trim()
const refSource = await selectSource()
const text = (await glm(prompt)).trim() + attribute(refSource)
const pub = await fetch(`${NOSTR_INTERNAL}/publish`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -147,7 +161,8 @@ async function eventsPost() {
const prompt = loadIdentityBlock('events') + '\n\nEVENTS:\n' + evs.map(e => `- ${e.starts_on} — ${e.name}${e.city ? ` (${e.city}, ${e.country || ''})` : ''}${e.url ? ` — ${e.url}` : ''}`).join('\n') || '(no upcoming events)'
const text = (await glm(prompt)).trim()
const evSource = evs && evs[0] && evs[0].url ? { originalUrl: evs[0].url, publishedAt: new Date().toISOString() } : await selectSource()
const text = (await glm(prompt)).trim() + attribute(evSource)
const pub = await fetch(`${NOSTR_INTERNAL}/publish`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },