lunheng-article-pipeline 18.78.2 → 18.80.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.
Files changed (99) hide show
  1. package/README-es.md +4 -4
  2. package/README-hi.md +4 -4
  3. package/README-pt.md +4 -4
  4. package/README-zh.md +4 -4
  5. package/README.md +5 -5
  6. package/SECURITY.md +3 -3
  7. package/cordis.patch.yml +1 -1
  8. package/docs/installation.md +1 -1
  9. package/docs/introduction.md +7 -7
  10. package/docs/troubleshooting.md +3 -3
  11. package/examples/preset/README.md +1 -1
  12. package/lib/ethics-sanitize.js +31 -3
  13. package/lib/index.js +15 -3
  14. package/lib/tools.js +5 -2
  15. package/package.json +1 -1
  16. package/skills/lunheng-article-pipeline/AGENTS.md +3 -2
  17. package/skills/lunheng-article-pipeline/QUICKSTART.md +2 -2
  18. package/skills/lunheng-article-pipeline/README.md +2 -2
  19. package/skills/lunheng-article-pipeline/SKILL.md +5 -4
  20. package/skills/lunheng-article-pipeline/references/_shared/DSH-/351/233/206/346/210/220/346/226/271/346/241/210.md +1 -1
  21. package/skills/lunheng-article-pipeline/references/_shared/M-Gate-Algorithm-appendix.md +1 -1
  22. package/skills/lunheng-article-pipeline/references/_shared/M-Gate-Algorithm.md +17 -1
  23. package/skills/lunheng-article-pipeline/references/_shared/audit-checklist-quickref.md +1 -1
  24. package/skills/lunheng-article-pipeline/references/_shared/auto_cite-integration.md +1 -1
  25. package/skills/lunheng-article-pipeline/references/_shared/degraded-scenarios.md +1 -1
  26. package/skills/lunheng-article-pipeline/references/_shared/failure-modes.md +1 -1
  27. package/skills/lunheng-article-pipeline/references/_shared/format-export.md +1 -1
  28. package/skills/lunheng-article-pipeline/references/_shared//344/270/255/346/226/207/346/225/260/346/215/256/346/272/220/351/233/206/346/210/220.md +1 -1
  29. package/skills/lunheng-article-pipeline/references/_shared//345/244/226/351/203/250/346/243/200/347/264/242/346/272/220/346/216/245/345/205/245/351/235/242.md +1 -1
  30. package/skills/lunheng-article-pipeline/references/_shared//345/255/227/346/225/260/345/210/244/345/256/232/350/241/250.md +13 -1
  31. package/skills/lunheng-article-pipeline/references/_shared//345/274/225/347/224/250/346/240/274/345/274/217.md +1 -1
  32. package/skills/lunheng-article-pipeline/references/_shared//346/226/207/347/261/273/346/241/243/346/241/210.md +24 -1
  33. package/skills/lunheng-article-pipeline/references/_shared//346/234/237/345/210/212/345/214/271/351/205/215/347/256/227/346/263/225.md +1 -1
  34. package/skills/lunheng-article-pipeline/references/_shared//346/234/237/345/210/212/346/225/260/346/215/256/345/272/223.md +1 -1
  35. package/skills/lunheng-article-pipeline/references/_shared//346/234/272/346/243/200/347/241/254/346/240/274/345/274/217.md +1 -1
  36. package/skills/lunheng-article-pipeline/references/_shared//346/235/220/346/226/231/346/221/204/345/217/226.md +1 -1
  37. package/skills/lunheng-article-pipeline/references/_shared//346/243/200/347/264/242/346/263/250/345/205/245/351/230/262/345/276/241.md +1 -1
  38. package/skills/lunheng-article-pipeline/references/_shared//346/250/241/345/236/213/350/267/257/347/224/261.md +63 -3
  39. package/skills/lunheng-article-pipeline/references/_shared//350/247/204/350/214/203-/346/234/272/346/242/260/351/227/250/345/257/271/347/205/247/350/241/250.md +7 -1
  40. package/skills/lunheng-article-pipeline/references/_shared//350/264/237/347/237/245/350/257/206/350/264/246/346/234/254.md +1 -1
  41. package/skills/lunheng-article-pipeline/references/agents/00-/344/270/273/346/216/247-coordinator.md +1 -1
  42. package/skills/lunheng-article-pipeline/references/agents/00-/344/270/273/346/216/247-/346/211/251/345/261/225/350/201/214/350/264/243.md +37 -1
  43. package/skills/lunheng-article-pipeline/references/agents/01-/346/226/207/347/214/256/346/243/200/347/264/242-literature-scout.md +1 -1
  44. package/skills/lunheng-article-pipeline/references/agents/02-/346/225/260/346/215/256/346/243/200/347/264/242-data-scout.md +1 -1
  45. package/skills/lunheng-article-pipeline/references/agents/03-/346/241/210/344/276/213/346/243/200/347/264/242-case-scout.md +12 -1
  46. package/skills/lunheng-article-pipeline/references/agents/04-/345/210/206/346/236/220-analyst.md +1 -1
  47. package/skills/lunheng-article-pipeline/references/agents/05-/345/206/231/344/275/234-writer.md +7 -2
  48. package/skills/lunheng-article-pipeline/references/agents/06-/346/211/271/345/210/244-critical-companion.md +1 -1
  49. package/skills/lunheng-article-pipeline/references/agents/07-/345/256/241/350/256/241-auditor.md +34 -1
  50. package/skills/lunheng-article-pipeline/references/agents/08-/347/273/210/346/243/200-finalizer.md +4 -2
  51. package/skills/lunheng-article-pipeline/references/agents/09-/345/256/241/347/250/277-peer-reviewer.md +1 -1
  52. package/skills/lunheng-article-pipeline/references/case-studies.md +1 -1
  53. package/skills/lunheng-article-pipeline/references/checkers//344/270/255/346/226/207AI/347/227/225/350/277/271-checker.md +1 -1
  54. package/skills/lunheng-article-pipeline/references/checkers//345/217/257/350/257/273/346/200/247-checker.md +1 -1
  55. package/skills/lunheng-article-pipeline/references/deliverables.md +1 -1
  56. package/skills/lunheng-article-pipeline/references/dispatch-cards.md +2 -2
  57. package/skills/lunheng-article-pipeline/references/errors.md +21 -1
  58. package/skills/lunheng-article-pipeline/references/gates/14-/344/270/255/346/226/207AI/347/227/225/350/277/271-gate.md +1 -1
  59. package/skills/lunheng-article-pipeline/references/gates//345/217/257/350/257/273/346/200/247/345/211/226/351/235/242-gate.md +1 -1
  60. package/skills/lunheng-article-pipeline/references/glossary.md +1 -1
  61. package/skills/lunheng-article-pipeline/references/maintainers.md +1 -1
  62. package/skills/lunheng-article-pipeline/references/memory/lessons.md +1 -1
  63. package/skills/lunheng-article-pipeline/references/operations.md +1 -1
  64. package/skills/lunheng-article-pipeline/references/pipeline-readme.md +2 -1
  65. package/skills/lunheng-article-pipeline/references/templates/AI-/344/275/277/347/224/250/345/243/260/346/230/216-template.md +1 -1
  66. package/skills/lunheng-article-pipeline/references/templates/COI-/345/210/251/347/233/212/345/206/262/347/252/201/345/243/260/346/230/216.md +1 -1
  67. package/skills/lunheng-article-pipeline/references/templates/CRediT-/344/275/234/350/200/205/350/264/241/347/214/256/345/243/260/346/230/216.md +1 -1
  68. package/skills/lunheng-article-pipeline/references/templates/G14/346/243/200/346/265/213/346/212/245/345/221/212-template.md +1 -1
  69. package/skills/lunheng-article-pipeline/references/templates/auto_cite-/350/241/245/345/205/205-template.md +1 -1
  70. package/skills/lunheng-article-pipeline/references/templates/status-template.md +1 -1
  71. package/skills/lunheng-article-pipeline/references/templates/style-baseline-template.md +1 -1
  72. package/skills/lunheng-article-pipeline/references/templates//344/270/273/344/272/272/346/212/225/345/226/202/346/270/205/345/215/225-template.md +1 -1
  73. package/skills/lunheng-article-pipeline/references/templates//344/270/273/344/272/272/347/241/256/350/256/244-template.md +2 -1
  74. package/skills/lunheng-article-pipeline/references/templates//344/272/244/344/273/230/350/257/264/346/230/216-template.md +1 -1
  75. package/skills/lunheng-article-pipeline/references/templates//344/273/273/345/212/241/347/256/200/346/212/245-template.md +4 -1
  76. package/skills/lunheng-article-pipeline/references/templates//344/274/246/347/220/206/345/256/241/346/211/271/345/243/260/346/230/216.md +1 -1
  77. package/skills/lunheng-article-pipeline/references/templates//344/277/256/350/256/242/350/257/264/346/230/216-template-full.md +1 -1
  78. package/skills/lunheng-article-pipeline/references/templates//345/261/200/351/231/220/346/200/247-template.md +1 -1
  79. package/skills/lunheng-article-pipeline/references/templates//346/212/225/347/250/277/345/260/261/347/273/252/346/243/200/346/237/245/350/241/250-template.md +1 -1
  80. package/skills/lunheng-article-pipeline/references/templates//346/225/260/346/215/256/345/217/257/347/224/250/346/200/247/345/243/260/346/230/216.md +1 -1
  81. package/skills/lunheng-article-pipeline/references/templates//346/250/241/345/236/213/350/267/257/347/224/261/350/241/250-template.md +1 -1
  82. package/skills/lunheng-article-pipeline/references/templates//347/264/240/346/235/220/345/212/240/350/275/275/346/270/205/345/215/225-template.md +1 -1
  83. package/skills/lunheng-article-pipeline/references/templates//350/277/233/345/261/225-/344/270/273/344/272/272/347/211/210-template.md +1 -1
  84. package/skills/lunheng-article-pipeline/references/templates//351/227/270/351/227/250/350/256/260/345/275/225-template.md +1 -1
  85. package/skills/lunheng-article-pipeline/scripts/_lib/cc-rules/repo-surface-rules.mjs +78 -1
  86. package/skills/lunheng-article-pipeline/scripts/_lib/cc-rules/rule-registry.mjs +1 -0
  87. package/skills/lunheng-article-pipeline/scripts/_lib/cc-rules/script-rules.mjs +21 -3
  88. package/skills/lunheng-article-pipeline/scripts/_lib/mgate-gates/mexist-gates.mjs +84 -8
  89. package/skills/lunheng-article-pipeline/scripts/_lib/mgate-gates/mform-gates.mjs +8 -1
  90. package/skills/lunheng-article-pipeline/scripts/_lib/source-mask.mjs +174 -0
  91. package/skills/lunheng-article-pipeline/scripts/build-evidence-bundle.mjs +16 -2
  92. package/skills/lunheng-article-pipeline/scripts/g-audit-check.mjs +10 -1
  93. package/skills/lunheng-article-pipeline/scripts/m-gate-check.mjs +7 -0
  94. package/skills/lunheng-article-pipeline/scripts/methodology-check.mjs +40 -4
  95. package/skills/lunheng-article-pipeline/scripts/model-routing.mjs +80 -10
  96. package/skills/lunheng-article-pipeline/scripts/token-cost.mjs +38 -6
  97. package/skills/lunheng-commands/SKILL.md +2 -2
  98. package/skills/lunheng-commands/scripts/history-cli.mjs +1 -1
  99. package/skills/lunheng-commands/scripts/stats-cli.mjs +1 -1
package/README-es.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lunheng (lunheng-article-pipeline) — pipeline multiagente para textos largos
2
2
 
3
- > 版本:v18.78.2(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
3
+ > 版本:v18.80.0(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
4
4
 
5
5
  > 🌐 [English](README.md) | [中文](README-zh.md) | **Español**(este archivo)| [Português](README-pt.md) | [हिन्दी](README-hi.md)
6
6
 
@@ -41,7 +41,7 @@ Regla práctica: pregunte si la evidencia ya está **publicada**. Si lo está, L
41
41
  | Borradores | Versiones sucesivas con limpieza de huellas de IA, cada una de un redactor independiente |
42
42
  | Informes | Informe crítico (C1–C7), auditoría (G0–G14), revisión por pares (6 dimensiones + revistas) |
43
43
  | Entregables finales | `final/定稿.md`, figuras, paquete probatorio, notas de entrega, informe de la puerta M |
44
- | Integración con DSH (instalación como bundle) | Cuatro herramientas **de solo lectura**: `lunheng_m_gate` (precomprobación mecánica de la puerta M), `lunheng_char_count` (recuento de caracteres chinos), `lunheng_handoff_check` (verificación de forma / versión / pares / agents-log del informe de traspaso) y `lunheng_ethics_sanitize` (desidentificación ética: enmascara DNI / teléfonos / correos / tarjetas bancarias / nombres de personas / topónimos en transcripciones de entrevistas y notas de campo; devuelve el texto enmascarado para que quien lo invoca decida dónde escribirlo; v18.60.1); si su sesión no las expone, invoque los mismos scripts con `pwsh` como antes (misma fuente de verdad). Comandos humanos `/lunheng-status` (lee `run/<proyecto>/status.md`) y `/lunheng-stats` (panel de telemetría entre proyectos; lanza `scripts/lunheng-stats.mjs` con `--json` como único argumento en lista blanca); ninguno genera mensajes de modelo. **Protección de escritura de los archivos de mecanismo**: un guard global rechaza las llamadas de herramientas tipo `write`/`edit` dirigidas al paquete de la skill, de modo que una sesión no puede reescribir en silencio las reglas de la propia pipeline. Límite declarado sin adornos: el guard solo ve **llamadas de herramientas**; `pwsh` y cualquier subproceso **no pasan por esta puerta**; la excepción del propietario es `LUNHENG_ALLOW_MECH_EDIT=1` (o `config: { allowMechanismEdit: true }`). El **Config** del plugin (interruptor de despliegue, en la fila de este plugin de su perfil) cubre `quiet`, `allowMechanismEdit`, `scriptTimeoutMs`, `scriptMaxOutputBytes`, `handoffLevel`, `hookRewriteContent` y `hookMaxBlockChars` — las dos primeras son equivalentes a las variables `LUNHENG_QUIET` / `LUNHENG_ALLOW_MECH_EDIT`, mientras que `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` son **solo Config (sin variable de entorno)**; las siete son revisables con el perfil; una **config inválida falla ruidosamente en la carga** en lugar de volver en silencio a los valores por defecto. |
44
+ | Integración con DSH (instalación como bundle) | Cuatro herramientas **de solo lectura**: `lunheng_m_gate` (precomprobación mecánica de la puerta M), `lunheng_char_count` (recuento de caracteres chinos), `lunheng_handoff_check` (verificación de forma / versión / pares / agents-log del informe de traspaso) y `lunheng_ethics_sanitize` (desidentificación ética: enmascara DNI / teléfonos / correos / tarjetas bancarias / nombres de personas / topónimos en transcripciones de entrevistas y notas de campo; devuelve el texto enmascarado para que quien lo invoca decida dónde escribirlo; v18.60.1); si su sesión no las expone, invoque los mismos scripts con `pwsh` como antes (misma fuente de verdad). Comandos humanos `/lunheng-status` (lee `run/<proyecto>/status.md`) y `/lunheng-stats` (panel de telemetría entre proyectos; lanza `scripts/lunheng-stats.mjs` con `--json` como único argumento en lista blanca); ninguno genera mensajes de modelo. **Protección de escritura de los archivos de mecanismo**: un guard global rechaza las llamadas de herramientas tipo `write`/`edit` dirigidas al paquete de la skill, de modo que una sesión no puede reescribir en silencio las reglas de la propia pipeline. Límite declarado sin adornos: el guard solo ve **llamadas de herramientas**; `pwsh` y cualquier subproceso **no pasan por esta puerta**; la excepción del propietario es `LUNHENG_ALLOW_MECH_EDIT=1` (o `config: { allowMechanismEdit: true }`). El **Config** del plugin (interruptor de despliegue, en la fila de este plugin de su perfil) cubre `quiet`, `allowMechanismEdit`, `scriptTimeoutMs`, `scriptMaxOutputBytes`, `handoffLevel`, `hookRewriteContent` y `hookMaxBlockChars` y `ethicsHook` (v18.80.0: interruptor maestro del listener H2 de desidentificación ética, **activado** por defecto; desactívelo en flujos puramente de código/documentación, donde las coincidencias de nombres de persona son medibles como todas de baja confianza y solo añaden ruido) — las dos primeras son equivalentes a las variables `LUNHENG_QUIET` / `LUNHENG_ALLOW_MECH_EDIT`, mientras que `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` / `ethicsHook` son **solo Config (sin variable de entorno)**; las ocho son revisables con el perfil; una **config inválida falla ruidosamente en la carga** en lugar de volver en silencio a los valores por defecto. |
45
45
 
46
46
  ## Pipeline overview
47
47
 
@@ -117,7 +117,7 @@ La capa patch hace dos cosas: **inserta una fila para este paquete** (`- id: lun
117
117
  Las versiones se publican **solo por tag**; `npm publish` local está prohibido (evita las puertas de CI y la procedencia OIDC, y una versión npm nunca se puede sobrescribir).
118
118
 
119
119
  ```sh
120
- git tag v18.78.2 && git push origin v18.78.2 # un tag por push (GitHub: >3 tags en un push no dispara workflow)
120
+ git tag v18.80.0 && git push origin v18.80.0 # un tag por push (GitHub: >3 tags en un push no dispara workflow)
121
121
  # publish.yml ejecuta: puerta 1 consistencia → puerta 2 empaquetado → puerta 3 higiene → puerta 4 humo del paquete → tests
122
122
  # → tag/versión iguales → guarda de idempotencia → OIDC publish --provenance --tag dsh → auditoría posterior
123
123
  ```
@@ -198,7 +198,7 @@ El coordinador debe divulgar estos envíos y obtener consentimiento explícito e
198
198
 
199
199
  > **Cómo leer esta tabla (dos criterios que se confunden con facilidad)**: ① «rondas de revisión» cuenta **todas las pasadas del redactor en esa ejecución** (fase 3.5 → v2, correcciones de la crítica T6, rondas G14, bucle de auditoría), mientras que el límite propio de la pipeline (**≤2 rondas**) se aplica **solo al bucle de auditoría de la fase 4.2**: los dos números no miden lo mismo; ② los resultados son **valores históricos registrados en el momento de cada ejecución, con la versión de scripts de entonces** — no son reproducibles con los scripts actuales. Volver a ejecutar los scripts del paquete sobre los proyectos archivados da hoy, por ejemplo, el artículo de la col china con formaldehído en `exit 2` con 5 P0 (M-Form-6/10, M-Exist-7/9, M-Integrity-1): tres de esas puertas se añadieron **después** de aquella ejecución. Lea la tabla como «lo que la pipeline produjo entonces», no como «el conjunto de puertas actual aprueba estos proyectos».
200
200
 
201
- > **Referencias cruzadas (lote de auditoría documental v18.78.0 §7.2)**: análisis detallado de los 6 casos representativos de esta tabla en [`docs/introduction.md` §实战验证](../lunheng-article-pipeline-dsh/docs/introduction.md) (caso del repollo chino con formaldehído ~4200 caracteres v2.3.7-dsh.8 + 6 casos del período v18.x); campos completos y rutas de productos para casos v2.x en [`references/case-studies.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/case-studies.md) §instantánea histórica. **Tarjeta rápida para recién llegados (lote v18.78.0 §7.2)**: números duros (versión / roles / fases / puertas / scripts / comandos / familia de códigos de salida) fuente única = [`docs/quick-facts.md`](../lunheng-article-pipeline-dsh/docs/quick-facts.md).
201
+ > **Referencias cruzadas (lote de auditoría documental v18.78.0 §7.2)**: análisis detallado de los 6 casos representativos de esta tabla en [`docs/introduction.md` §实战验证](docs/introduction.md) (caso del repollo chino con formaldehído ~4200 caracteres v2.3.7-dsh.8 + 6 casos del período v18.x); campos completos y rutas de productos para casos v2.x en [`references/case-studies.md`](skills/lunheng-article-pipeline/references/case-studies.md) §instantánea histórica. **Tarjeta rápida para recién llegados (lote v18.78.0 §7.2)**: números duros (versión / roles / fases / puertas / scripts / comandos / familia de códigos de salida) fuente única = [`docs/quick-facts.md`](docs/quick-facts.md).
202
202
 
203
203
  Puertas locales (**solo fuentes del repositorio** — el paquete npm **no incluye** `scripts/` ni `tests/`, por lo que los comandos siguientes no se pueden ejecutar en una instalación desde el paquete; dentro del paquete puede ejecutar el conjunto equivalente sobre el tarball recién descargado con `pack-smoke`): `node skills/lunheng-article-pipeline/scripts/consistency-check.mjs`, `node scripts/plugin-surface-check.mjs`, `node scripts/repo-hygiene-check.mjs`, `node scripts/pack-smoke.mjs`, `node --test "tests/**/*.test.mjs"`.
204
204
 
package/README-hi.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # लुन्हेंग (lunheng-article-pipeline) — बहु-एजेंट दीर्घ-लेख पाइपलाइन
2
2
 
3
- > 版本:v18.78.2(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
3
+ > 版本:v18.80.0(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
4
4
 
5
5
  > 🌐 [English](README.md) | [中文](README-zh.md) | [Español](README-es.md) | [Português](README-pt.md) | **हिन्दी**(यह फ़ाइल)
6
6
 
@@ -41,7 +41,7 @@
41
41
  | प्रारूप | क्रमिक संस्करण, AI-चिह्न सफ़ाई सहित, प्रत्येक स्वतंत्र लेखक से |
42
42
  | रिपोर्ट | समालोचना (C1–C7), लेखा-परीक्षा (G0–G14), सहकर्मी समीक्षा (6 आयाम + पत्रिकाएँ) |
43
43
  | अंतिम उपज | `final/定稿.md`, चित्र, प्रमाण-पुंज, वितरण टिप्पणियाँ, M-द्वार रिपोर्ट |
44
- | DSH एकीकरण (bundle स्थापना पर) | चार **केवल-पठन** उपकरण — `lunheng_m_gate` (M-द्वार यांत्रिक पूर्व-जाँच), `lunheng_char_count` (शुद्ध चीनी अक्षर गणना), `lunheng_handoff_check` (हैंडऑफ रिपोर्ट के आकार / संस्करण / युग्मों / agents-log की जाँच), और `lunheng_ethics_sanitize` (नैतिक अनामीकरण: साक्षात्कार प्रतिलेखों व क्षेत्र-टिप्पणियों में पहचान-पत्र / फ़ोन / ई-मेल / बैंक कार्ड / व्यक्ति-नाम / स्थान-नाम छिपाता है; छिपाया हुआ पाठ लौटाता है ताकि कॉलर तय करे कि कहाँ लिखना है; v18.60.1); सूची में न हों तो पहले की तरह `pwsh` से वही स्क्रिप्ट चलाएँ (एक ही स्रोत)। मानव आदेश `/lunheng-status` (`run/<परियोजना>/status.md` पढ़ता है) तथा `/lunheng-stats` (परियोजनाओं के बीच टेलीमेट्री पैनल; होस्ट-प्रक्रिया में `scripts/lunheng-stats.mjs` को केवल `--json` श्वेतसूची के साथ चलाता है); कोई भी मॉडल संदेश नहीं बनाता। **तंत्र-फ़ाइल लेखन सुरक्षा**: वैश्विक guard स्किल पैकेज को लक्षित `write`/`edit` प्रकार की उपकरण-कॉल अस्वीकार करता है, अतः सत्र चुपचाप पाइपलाइन के अपने नियम नहीं बदल सकता। **सीमा स्पष्ट रूप से**: guard केवल **उपकरण-कॉल** देखता है — `pwsh`/उप-प्रक्रियाएँ **इस द्वार से नहीं गुज़रतीं**; स्वामी की छूट `LUNHENG_ALLOW_MECH_EDIT=1` (या `config: { allowMechanismEdit: true }`) है। प्लगइन **Config** (परिनियोजन स्विच, आपकी profile में इस प्लगइन की पंक्ति पर) `quiet`, `allowMechanismEdit`, `scriptTimeoutMs`, `scriptMaxOutputBytes`, `handoffLevel`, `hookRewriteContent`, और `hookMaxBlockChars` — कुल **सात** — कवर करता है; पहले दो LUNHENG_QUIET / LUNHENG_ALLOW_MECH_EDIT env के समान हैं, जबकि scriptTimeoutMs / scriptMaxOutputBytes / handoffLevel / hookRewriteContent / hookMaxBlockChars **केवल Config (कोई env पथ नहीं)**, पर profile के साथ संस्करणित और समीक्षायोग्य; **अवैध config लोड के समय ज़ोरदार विफल होता है**, चुपचाप डिफ़ॉल्ट पर नहीं लौटता। |
44
+ | DSH एकीकरण (bundle स्थापना पर) | चार **केवल-पठन** उपकरण — `lunheng_m_gate` (M-द्वार यांत्रिक पूर्व-जाँच), `lunheng_char_count` (शुद्ध चीनी अक्षर गणना), `lunheng_handoff_check` (हैंडऑफ रिपोर्ट के आकार / संस्करण / युग्मों / agents-log की जाँच), और `lunheng_ethics_sanitize` (नैतिक अनामीकरण: साक्षात्कार प्रतिलेखों व क्षेत्र-टिप्पणियों में पहचान-पत्र / फ़ोन / ई-मेल / बैंक कार्ड / व्यक्ति-नाम / स्थान-नाम छिपाता है; छिपाया हुआ पाठ लौटाता है ताकि कॉलर तय करे कि कहाँ लिखना है; v18.60.1); सूची में न हों तो पहले की तरह `pwsh` से वही स्क्रिप्ट चलाएँ (एक ही स्रोत)। मानव आदेश `/lunheng-status` (`run/<परियोजना>/status.md` पढ़ता है) तथा `/lunheng-stats` (परियोजनाओं के बीच टेलीमेट्री पैनल; होस्ट-प्रक्रिया में `scripts/lunheng-stats.mjs` को केवल `--json` श्वेतसूची के साथ चलाता है); कोई भी मॉडल संदेश नहीं बनाता। **तंत्र-फ़ाइल लेखन सुरक्षा**: वैश्विक guard स्किल पैकेज को लक्षित `write`/`edit` प्रकार की उपकरण-कॉल अस्वीकार करता है, अतः सत्र चुपचाप पाइपलाइन के अपने नियम नहीं बदल सकता। **सीमा स्पष्ट रूप से**: guard केवल **उपकरण-कॉल** देखता है — `pwsh`/उप-प्रक्रियाएँ **इस द्वार से नहीं गुज़रतीं**; स्वामी की छूट `LUNHENG_ALLOW_MECH_EDIT=1` (या `config: { allowMechanismEdit: true }`) है। प्लगइन **Config** (परिनियोजन स्विच, आपकी profile में इस प्लगइन की पंक्ति पर) `quiet`, `allowMechanismEdit`, `scriptTimeoutMs`, `scriptMaxOutputBytes`, `handoffLevel`, `hookRewriteContent`, और `hookMaxBlockChars` तथा `ethicsHook` (v18.80.0: नैतिक अनामीकरण H2 श्रोता का मुख्य स्विच, डिफ़ॉल्ट रूप से **सक्रिय**; शुद्ध कोड/दस्तावेज़ कार्यप्रवाह में इसे बंद करें, जहाँ व्यक्ति-नाम की सभी मिलानें मापनीय रूप से निम्न-विश्वास हैं और केवल शोर जोड़ती हैं) — कुल **आठ** — कवर करता है; पहले दो LUNHENG_QUIET / LUNHENG_ALLOW_MECH_EDIT env के समान हैं, जबकि scriptTimeoutMs / scriptMaxOutputBytes / handoffLevel / hookRewriteContent / hookMaxBlockChars / ethicsHook **केवल Config (कोई env पथ नहीं)**, पर profile के साथ संस्करणित और समीक्षायोग्य; **अवैध config लोड के समय ज़ोरदार विफल होता है**, चुपचाप डिफ़ॉल्ट पर नहीं लौटता। |
45
45
 
46
46
  ## Pipeline overview
47
47
 
@@ -117,7 +117,7 @@ patch परत दो काम करती है: **इस पैकेज
117
117
  संस्करण **केवल tag से** प्रकाशित होते हैं; स्थानीय `npm publish` वर्जित है (यह CI द्वारों और OIDC provenance को दरकिनार करता है, और npm संस्करण कभी अधिलेखित नहीं हो सकता)।
118
118
 
119
119
  ```sh
120
- git tag v18.78.2 && git push origin v18.78.2 # एक बार में एक ही tag (GitHub: >3 tag एक push में कोई workflow नहीं चलाता)
120
+ git tag v18.80.0 && git push origin v18.80.0 # एक बार में एक ही tag (GitHub: >3 tag एक push में कोई workflow नहीं चलाता)
121
121
  # publish.yml क्रम: द्वार 1 सुसंगति → द्वार 2 पैकेजिंग → द्वार 3 स्वच्छता → द्वार 4 पैकेज स्मोक → स्क्रिप्ट परीक्षण
122
122
  # → tag/संस्करण समानता → idempotency गार्ड → OIDC publish --provenance --tag dsh → प्रकाशन-पश्चात लेखा-परीक्षा
123
123
  ```
@@ -198,7 +198,7 @@ DSH में मॉडल मार्ग `settings.yaml` तय करता
198
198
 
199
199
  > **यह तालिका कैसे पढ़ें (दो मानदंड जो आसानी से मिल जाते हैं)**: ① «संशोधन चक्र» उस **पूरे चक्र के सभी लेखक-पास** गिनता है (चरण 3.5 → v2, T6 समालोचना-सुधार, G14 चक्र, लेखा-परीक्षा लूप), जबकि पाइपलाइन की अपनी सीमा (**≤2 चक्र**) केवल **चरण 4.2 के लेखा-परीक्षा लूप** पर लागू होती है — दोनों संख्याएँ एक ही बात नहीं नापतीं; ② परिणाम **प्रत्येक चक्र के समय दर्ज ऐतिहासिक मान हैं, उस समय के स्क्रिप्ट संस्करण के साथ** — वर्तमान स्क्रिप्ट से पुनरुत्पाद्य नहीं। संग्रहीत परियोजनाओं पर आज पैकेज की स्क्रिप्ट चलाने पर, उदाहरणार्थ, फ़ॉर्मेल्डिहाइड बंदगोभी लेख `exit 2` तथा 5 P0 (M-Form-6/10, M-Exist-7/9, M-Integrity-1) देता है: उनमें से तीन द्वार उस चक्र के **बाद** जोड़े गए थे। इस तालिका को «तब पाइपलाइन ने क्या उत्पादित किया» के रूप में पढ़ें, «वर्तमान द्वार-समूह इन परियोजनाओं को उत्तीर्ण करता है» के रूप में नहीं।
200
200
 
201
- > **पारस्परिक संदर्भ (व18.78.0 दस्तावेज़ ऑडिट बैच §7.2)**: इस तालिका के 6 प्रतिनिधि मामलों का गहन विश्लेषण [`docs/introduction.md` §实战验证](../lunheng-article-pipeline-dsh/docs/introduction.md) में (फ़ॉर्मेल्डिहाइड बंदगोभी मामला ~4200 अक्षर v2.3.7-dsh.8 + v18.x अवधि के 6 मामले); v2.x मामलों के लिए पूर्ण फ़ील्ड और उत्पाद पथ [`references/case-studies.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/case-studies.md) §ऐतिहासिक स्नैपशॉट में। **नवागंतुकों के लिए त्वरित कार्ड (व18.78.0 बैच §7.2)**: कठोर संख्याएँ (संस्करण / भूमिकाएँ / चरण / द्वार / स्क्रिप्ट / आदेश / निकास कोड परिवार) एकल स्रोत = [`docs/quick-facts.md`](../lunheng-article-pipeline-dsh/docs/quick-facts.md).
201
+ > **पारस्परिक संदर्भ (व18.78.0 दस्तावेज़ ऑडिट बैच §7.2)**: इस तालिका के 6 प्रतिनिधि मामलों का गहन विश्लेषण [`docs/introduction.md` §实战验证](docs/introduction.md) में (फ़ॉर्मेल्डिहाइड बंदगोभी मामला ~4200 अक्षर v2.3.7-dsh.8 + v18.x अवधि के 6 मामले); v2.x मामलों के लिए पूर्ण फ़ील्ड और उत्पाद पथ [`references/case-studies.md`](skills/lunheng-article-pipeline/references/case-studies.md) §ऐतिहासिक स्नैपशॉट में। **नवागंतुकों के लिए त्वरित कार्ड (व18.78.0 बैच §7.2)**: कठोर संख्याएँ (संस्करण / भूमिकाएँ / चरण / द्वार / स्क्रिप्ट / आदेश / निकास कोड परिवार) एकल स्रोत = [`docs/quick-facts.md`](docs/quick-facts.md).
202
202
 
203
203
  स्थानीय द्वार (**केवल स्रोत रिपॉज़िटरी** — npm पैकेज में `scripts/` या `tests/` **शामिल नहीं** हैं, इसलिए नीचे दिए आदेश पैकेज-इंस्टॉल रूप में नहीं चलेंगे; पैकेज के भीतर आप `pack-smoke` से नए डाउनलोड किए tarball पर समतुल्य जाँच चला सकते हैं): `node skills/lunheng-article-pipeline/scripts/consistency-check.mjs`, `node scripts/plugin-surface-check.mjs`, `node scripts/repo-hygiene-check.mjs`, `node scripts/pack-smoke.mjs`, `node --test "tests/**/*.test.mjs"`.
204
204
 
package/README-pt.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lunheng (lunheng-article-pipeline) — pipeline multiagente para textos longos
2
2
 
3
- > 版本:v18.78.2(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
3
+ > 版本:v18.80.0(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
4
4
 
5
5
  > 🌐 [English](README.md) | [中文](README-zh.md) | [Español](README-es.md) | **Português**(este arquivo)| [हिन्दी](README-hi.md)
6
6
 
@@ -41,7 +41,7 @@ Regra prática: pergunte se a evidência já está **publicada**. Se estiver, Lu
41
41
  | Rascunhos | Versões sucessivas com limpeza de vestígios de IA, cada uma de um redator independente |
42
42
  | Relatórios | Relatório crítico (C1–C7), auditoria (G0–G14), revisão por pares (6 dimensões + periódicos) |
43
43
  | Entregáveis finais | `final/定稿.md`, figuras, pacote probatório, notas de entrega, relatório do portão M |
44
- | Integração com o DSH (instalação como bundle) | Quatro ferramentas **somente leitura**: `lunheng_m_gate` (pré-checagem mecânica do portão M), `lunheng_char_count` (contagem de caracteres chineses), `lunheng_handoff_check` (verificação de forma / versão / pares / agents-log do relatório de handoff) e `lunheng_ethics_sanitize` (desidentificação ética: mascara CPF/documentos, telefones, e-mails, cartões bancários, nomes de pessoas e topônimos em transcrições de entrevistas e notas de campo; devolve o texto mascarado para que quem chama decida onde gravá-lo; v18.60.1); se a sua sessão não as expõe, chame os mesmos scripts com `pwsh` como antes (mesma fonte de verdade). Comandos humanos `/lunheng-status` (lê `run/<projeto>/status.md`) e `/lunheng-stats` (painel de telemetria entre projetos; lança `scripts/lunheng-stats.mjs` com `--json` como único argumento em lista branca); nenhum gera mensagens de modelo. **Proteção de escrita dos arquivos de mecanismo**: um guard global recusa chamadas de ferramentas do tipo `write`/`edit` dirigidas ao pacote da skill, de modo que uma sessão não pode reescrever em silêncio as regras da própria pipeline. Limite declarado sem rodeios: o guard só vê **chamadas de ferramentas**; `pwsh` e qualquer subprocesso **não passam por este portão**; a exceção do proprietário é `LUNHENG_ALLOW_MECH_EDIT=1` (ou `config: { allowMechanismEdit: true }`). O **Config** do plugin (chave de implantação, na linha deste plugin do seu perfil) cobre `quiet`, `allowMechanismEdit`, `scriptTimeoutMs`, `scriptMaxOutputBytes`, `handoffLevel`, `hookRewriteContent` e `hookMaxBlockChars` — os dois primeiros equivalem às variáveis `LUNHENG_QUIET` / `LUNHENG_ALLOW_MECH_EDIT`, enquanto `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` são **somente Config (sem variável de ambiente)**; os sete são revisáveis com o perfil; uma **config inválida falha ruidosamente no carregamento** em vez de voltar em silêncio aos padrões. |
44
+ | Integração com o DSH (instalação como bundle) | Quatro ferramentas **somente leitura**: `lunheng_m_gate` (pré-checagem mecânica do portão M), `lunheng_char_count` (contagem de caracteres chineses), `lunheng_handoff_check` (verificação de forma / versão / pares / agents-log do relatório de handoff) e `lunheng_ethics_sanitize` (desidentificação ética: mascara CPF/documentos, telefones, e-mails, cartões bancários, nomes de pessoas e topônimos em transcrições de entrevistas e notas de campo; devolve o texto mascarado para que quem chama decida onde gravá-lo; v18.60.1); se a sua sessão não as expõe, chame os mesmos scripts com `pwsh` como antes (mesma fonte de verdade). Comandos humanos `/lunheng-status` (lê `run/<projeto>/status.md`) e `/lunheng-stats` (painel de telemetria entre projetos; lança `scripts/lunheng-stats.mjs` com `--json` como único argumento em lista branca); nenhum gera mensagens de modelo. **Proteção de escrita dos arquivos de mecanismo**: um guard global recusa chamadas de ferramentas do tipo `write`/`edit` dirigidas ao pacote da skill, de modo que uma sessão não pode reescrever em silêncio as regras da própria pipeline. Limite declarado sem rodeios: o guard só vê **chamadas de ferramentas**; `pwsh` e qualquer subprocesso **não passam por este portão**; a exceção do proprietário é `LUNHENG_ALLOW_MECH_EDIT=1` (ou `config: { allowMechanismEdit: true }`). O **Config** do plugin (chave de implantação, na linha deste plugin do seu perfil) cobre `quiet`, `allowMechanismEdit`, `scriptTimeoutMs`, `scriptMaxOutputBytes`, `handoffLevel`, `hookRewriteContent` e `hookMaxBlockChars` e `ethicsHook` (v18.80.0: interruptor mestre do listener H2 de desidentificação ética, **ativado** por padrão; desative em fluxos puramente de código/documentação, onde as correspondências de nomes de pessoa são mensuráveis como todas de baixa confiança e só acrescentam ruído) — os dois primeiros equivalem às variáveis `LUNHENG_QUIET` / `LUNHENG_ALLOW_MECH_EDIT`, enquanto `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` / `ethicsHook` são **somente Config (sem variável de ambiente)**; os oito são revisáveis com o perfil; uma **config inválida falha ruidosamente no carregamento** em vez de voltar em silêncio aos padrões. |
45
45
 
46
46
  ## Pipeline overview
47
47
 
@@ -117,7 +117,7 @@ A camada patch faz duas coisas: **insere uma linha para este pacote** (`- id: lu
117
117
  As versões são publicadas **apenas por tag**; `npm publish` local é proibido (contorna os portões de CI e a proveniência OIDC, e uma versão npm nunca pode ser sobrescrita).
118
118
 
119
119
  ```sh
120
- git tag v18.78.2 && git push origin v18.78.2 # uma tag por push (GitHub: >3 tags em um push não dispara workflow)
120
+ git tag v18.80.0 && git push origin v18.80.0 # uma tag por push (GitHub: >3 tags em um push não dispara workflow)
121
121
  # publish.yml executa: portão 1 consistência → portão 2 empacotamento → portão 3 higiene → portão 4 teste de fumaça do pacote → testes
122
122
  # → tag/versão iguais → guarda de idempotência → OIDC publish --provenance --tag dsh → auditoria posterior
123
123
  ```
@@ -198,7 +198,7 @@ O coordenador deve divulgar esses envios e obter consentimento explícito na fas
198
198
 
199
199
  > **Como ler esta tabela (dois critérios que se confundem facilmente)**: ① «rodadas de revisão» conta **todas as passagens do redator naquela execução** (fase 3.5 → v2, correções da crítica T6, rodadas G14, laço de auditoria), enquanto o limite da própria pipeline (**≤2 rodadas**) vale **apenas para o laço de auditoria da fase 4.2** — os dois números não medem a mesma coisa; ② os resultados são **valores históricos registrados no momento de cada execução, com a versão de scripts daquela época** — não são reproduzíveis com os scripts atuais. Reexecutar hoje os scripts do pacote sobre os projetos arquivados devolve, por exemplo, o artigo do repolho com formaldeído em `exit 2` com 5 P0 (M-Form-6/10, M-Exist-7/9, M-Integrity-1): três desses portões foram acrescentados **depois** daquela execução. Leia a tabela como «o que a pipeline produziu então», não como «o conjunto de portões atual aprova estes projetos».
200
200
 
201
- > **Referências cruzadas (lote de auditoria documental v18.78.0 §7.2)**: análise aprofundada dos 6 casos representativos desta tabela em [`docs/introduction.md` §实战验证](../lunheng-article-pipeline-dsh/docs/introduction.md) (caso do repolho chinês com formaldeído ~4200 caracteres v2.3.7-dsh.8 + 6 casos do período v18.x); campos completos e caminhos de produtos para casos v2.x em [`references/case-studies.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/case-studies.md) §instantâneo histórico. **Cartão rápido para novatos (lote v18.78.0 §7.2)**: números concretos (versão / papéis / fases / portas / scripts / comandos / família de códigos de saída) fonte única = [`docs/quick-facts.md`](../lunheng-article-pipeline-dsh/docs/quick-facts.md).
201
+ > **Referências cruzadas (lote de auditoria documental v18.78.0 §7.2)**: análise aprofundada dos 6 casos representativos desta tabela em [`docs/introduction.md` §实战验证](docs/introduction.md) (caso do repolho chinês com formaldeído ~4200 caracteres v2.3.7-dsh.8 + 6 casos do período v18.x); campos completos e caminhos de produtos para casos v2.x em [`references/case-studies.md`](skills/lunheng-article-pipeline/references/case-studies.md) §instantâneo histórico. **Cartão rápido para novatos (lote v18.78.0 §7.2)**: números concretos (versão / papéis / fases / portas / scripts / comandos / família de códigos de saída) fonte única = [`docs/quick-facts.md`](docs/quick-facts.md).
202
202
 
203
203
  Portões locais (**somente fontes do repositório** — o pacote npm **não inclui** `scripts/` nem `tests/`, portanto os comandos abaixo não rodam numa instalação a partir do pacote; dentro do pacote você pode rodar o conjunto equivalente no tarball recém-baixado via `pack-smoke`): `node skills/lunheng-article-pipeline/scripts/consistency-check.mjs`, `node scripts/plugin-surface-check.mjs`, `node scripts/repo-hygiene-check.mjs`, `node scripts/pack-smoke.mjs`, `node --test "tests/**/*.test.mjs"`.
204
204
 
package/README-zh.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 🌐 [English](README.md) | **中文**(本文件)| [Español](README-es.md) | [Português](README-pt.md) | [हिन्दी](README-hi.md)
4
4
 
5
- > 版本:v18.78.2(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
5
+ > 版本:v18.80.0(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
6
6
 
7
7
  > 一个 DeepSeek Harness(DSH)bundle 插件,注册**两个按需加载的 agent 技能**:`lunheng-article-pipeline`(9 角色主流水线)与 `lunheng-commands`(薄壳包装 11 个 `/lunheng-*` 斜杠命令:draft / resume / cite / audit / journal / ppt / history / rollback / status / stats / help;**不引入新角色、不引入新 M 门**,详见 `skills/lunheng-commands/SKILL.md`)。主技能把深度长文的生产——学术论文、行业分析、商业评论、公众号深文——变成**带人在环节点的 9 角色流水线**。
8
8
 
@@ -41,7 +41,7 @@
41
41
  | 初稿 | 逐版递进,含中文 AI 痕迹清理,每轮由独立写手执行 |
42
42
  | 审阅报告 | 批判报告(C1–C7)、审计报告(G0–G14)、审稿报告(6 维度 + 期刊匹配)、AI 痕迹报告 |
43
43
  | 终交付 | `final/定稿.md`、图件、证据包、交付说明、M 门报告 |
44
- | DSH 集成(bundle 安装时) | 四个**只读**工具 `lunheng_m_gate`(M 门机械预检)、`lunheng_char_count`(纯汉字数)、`lunheng_handoff_check`(交接报告形态/版本/成对/agents-log 校验)与 `lunheng_ethics_sanitize`(伦理脱敏:把访谈逐字稿/田野笔记里的身份证/手机/邮箱/银行卡/人名/地名换成占位符;**返回脱敏文本、由调用方决定落盘**;v18.60.1)——清单里没有就照旧用 `pwsh` 调同名脚本(同源)。人类命令 `/lunheng-status` 与 `/lunheng-stats`(读 `run/<项目>/status.md`,**不产生模型消息**)。**机制文件写保护**:全局 guard 否决指向技能包内的 `write`/`edit` 类工具调用,会话无法悄悄改写流水线自己的规则。**边界如实声明**:guard 只看**工具调用**——`pwsh`/子进程**不经此门**;主人授权例外走 `LUNHENG_ALLOW_MECH_EDIT=1`(或本插件行 `config: { allowMechanismEdit: true }`)。插件 **Config**(部署开关,写在你 profile 的本插件行上)覆盖 `quiet` / `allowMechanismEdit` / `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel`(交接门灰度) / `hookRewriteContent`(H2 监听器改写 `result.content` 的 opt-in 开关,默认 **false** = 只标记不改写,避免误伤时间戳/订单 ID 等被硬模式误判的合法文本;**不建议**改写) / `hookMaxBlockChars`(H2 监听器单 text 块脱敏上限,默认 **262144** = 256 KB;超此大小的块**跳过改写**,避免在不可控体积上同步占用「原文 + 脱敏副本」两份内存——超限内容用 `lunheng_ethics_sanitize` 工具分页处理)——其中 `quiet` / `allowMechanismEdit` 与 `LUNHENG_QUIET` / `LUNHENG_ALLOW_MECH_EDIT` env 同义,而 `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` 五者**仅 Config(无 env 路径)**;七者均随 profile 走且可 review;**非法配置在加载期响亮失败**,不静默回落默认值。 |
44
+ | DSH 集成(bundle 安装时) | 四个**只读**工具 `lunheng_m_gate`(M 门机械预检)、`lunheng_char_count`(纯汉字数)、`lunheng_handoff_check`(交接报告形态/版本/成对/agents-log 校验)与 `lunheng_ethics_sanitize`(伦理脱敏:把访谈逐字稿/田野笔记里的身份证/手机/邮箱/银行卡/人名/地名换成占位符;**返回脱敏文本、由调用方决定落盘**;v18.60.1)——清单里没有就照旧用 `pwsh` 调同名脚本(同源)。人类命令 `/lunheng-status` 与 `/lunheng-stats`(读 `run/<项目>/status.md`,**不产生模型消息**)。**机制文件写保护**:全局 guard 否决指向技能包内的 `write`/`edit` 类工具调用,会话无法悄悄改写流水线自己的规则。**边界如实声明**:guard 只看**工具调用**——`pwsh`/子进程**不经此门**;主人授权例外走 `LUNHENG_ALLOW_MECH_EDIT=1`(或本插件行 `config: { allowMechanismEdit: true }`)。插件 **Config**(部署开关,写在你 profile 的本插件行上)覆盖 `quiet` / `allowMechanismEdit` / `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel`(交接门灰度) / `hookRewriteContent`(H2 监听器改写 `result.content` 的 opt-in 开关,默认 **false** = 只标记不改写,避免误伤时间戳/订单 ID 等被硬模式误判的合法文本;**不建议**改写) / `hookMaxBlockChars`(H2 监听器单 text 块脱敏上限,默认 **262144** = 256 KB;超此大小的块**跳过改写**,避免在不可控体积上同步占用「原文 + 脱敏副本」两份内存——超限内容用 `lunheng_ethics_sanitize` 工具分页处理) / `ethicsHook`(**v18.80.0**:H2 伦理脱敏监听器**总开关**,默认 **true**;纯代码/文档工作流可置 false 关掉噪声——实测该监听器对技术文档的人名命中**全部是低置信候选、无一是真人姓名**)——其中 `quiet` / `allowMechanismEdit` 与 `LUNHENG_QUIET` / `LUNHENG_ALLOW_MECH_EDIT` env 同义,而 `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` / `ethicsHook` 六者**仅 Config(无 env 路径)**;八者均随 profile 走且可 review;**非法配置在加载期响亮失败**,不静默回落默认值。 |
45
45
 
46
46
  ## Pipeline overview
47
47
 
@@ -66,7 +66,7 @@ Phase 5 终检 T8 终检(主控执行)→ 定稿、证据包、交付
66
66
 
67
67
  **三角证据底座**(**同义「三角验证」**;与英文 README L70 "Triangular evidence base" 同义;单一术语真源 = `skills/lunheng-article-pipeline/references/glossary.md` §十二.1)(`[L]` + `[D]` + `[C]`)——每条论断都要能映射到文献、数据与(事件类论断)案例证据。**独立审计**——审计员不改稿,只报告。**四道人在环节点**——Phase 0 / 2.5 / 3.5 / 5。
68
68
 
69
- > **实战案例互引(v18.78.0 §7.2 双文档互引落地)**:本仓库实战案例的事实表(**字数 / 资源 / 审计结果 / 关键结论**)见英文 [`README.md` 顶部 Verification status 表](../lunheng-article-pipeline-dsh/README.md#verification-status)(**单一真源**);v2.x 与 v18.x 期间的深入剖析见 [`docs/introduction.md` §实战验证](../lunheng-article-pipeline-dsh/docs/introduction.md) 与 [`skills/lunheng-article-pipeline/references/case-studies.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/case-studies.md)。**新主人口径**(v18.78.0 §7.2 立项):硬数字(版本 / 角色 / 阶段 / 闸门 / 脚本 / 命令 / 退出码族)单一真源 = [`docs/quick-facts.md`](../lunheng-article-pipeline-dsh/docs/quick-facts.md)。
69
+ > **实战案例互引(v18.78.0 §7.2 双文档互引落地)**:本仓库实战案例的事实表(**字数 / 资源 / 审计结果 / 关键结论**)见英文 [`README.md` 顶部 Verification status 表](README.md#verification-status)(**单一真源**);v2.x 与 v18.x 期间的深入剖析见 [`docs/introduction.md` §实战验证](docs/introduction.md) 与 [`skills/lunheng-article-pipeline/references/case-studies.md`](skills/lunheng-article-pipeline/references/case-studies.md)。**新主人口径**(v18.78.0 §7.2 立项):硬数字(版本 / 角色 / 阶段 / 闸门 / 脚本 / 命令 / 退出码族)单一真源 = [`docs/quick-facts.md`](docs/quick-facts.md)。
70
70
 
71
71
  ## Repository layout
72
72
 
@@ -119,7 +119,7 @@ patch 层做两件事:**插入一行本包自注册行**(`- id: lunheng-arti
119
119
  **只推 tag 发布,禁止本地 `npm publish`**(本地直发会绕过 CI 三道门与 OIDC 来源证明,且 npm 版本不可覆盖)。
120
120
 
121
121
  ```sh
122
- git tag v18.78.2 && git push origin v18.78.2 # 一次只推 1 个 tag(GitHub:单次 push >3 个 tag 不触发任何 workflow)
122
+ git tag v18.80.0 && git push origin v18.80.0 # 一次只推 1 个 tag(GitHub:单次 push >3 个 tag 不触发任何 workflow)
123
123
  # publish.yml 依次跑:门 1 一致性 → 门 2 打包面 → 门 3 机械卫生 → 门 4 打包产物冒烟 → 脚本回归测试
124
124
  # → tag/版本一致校验 → 幂等守卫 → OIDC 发布 --provenance --tag dsh → 发布后审计
125
125
  ```
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 🌐 **English** (this file) | [中文](README-zh.md) | [Español](README-es.md) | [Português](README-pt.md) | [हिन्दी](README-hi.md)
4
4
 
5
- > 版本:v18.78.2(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
5
+ > 版本:v18.80.0(DSH bundle:package.json + cordis.patch.yml + lib/index.js)
6
6
 
7
7
  > A DeepSeek Harness (DSH) bundle that registers **two on-demand agent skills**:
8
8
  > - `lunheng-article-pipeline` — the main 9-role pipeline (T1–T9, see below).
@@ -46,7 +46,7 @@ Rule of thumb: ask whether the evidence is already **published**. If yes, Lunhen
46
46
  | Drafts | Successive versions with AI-trace cleanup, each independent writer run |
47
47
  | Review reports | Critical report (C1–C7), audit report (G0–G14), peer-review report (6 dimensions + journal matching), AI-trace report |
48
48
  | Final deliverables | `final/定稿.md`, figures, evidence bundle, delivery notes, M-gate report |
49
- | DSH integration (bundle install) | Four **read-only** tools — `lunheng_m_gate` (M-gate mechanical pre-check), `lunheng_char_count` (pure Chinese-character count), `lunheng_handoff_check` (handoff-report shape / version / pairs / agents-log verification at the role-handoff boundary), and `lunheng_ethics_sanitize` (ethics de-identification: masks IDs / phones / e-mails / bank cards / person names / place names in interview transcripts and field notes; returns the masked text so the caller decides where to write it; v18.60.1); if your session does not expose them, call the same scripts with `pwsh` as before (same source of truth). Human commands `/lunheng-status` (reads `run/<project>/status.md`; produces no model message) and `/lunheng-stats` (cross-project telemetry dashboard; spawns the host-process `scripts/lunheng-stats.mjs` with `--json` whitelisted only; output truncated to 8000 chars; produces no model message). **Mechanism-file write protection**: a global guard rejects `write`/`edit`-style tool calls that target the skill package, so a session cannot quietly rewrite the pipeline's own rules. Boundary, stated plainly: the guard only sees **tool calls** — `pwsh` and any subprocess are **not** behind this gate; the owner's escape hatch is `LUNHENG_ALLOW_MECH_EDIT=1` (or `config: { allowMechanismEdit: true }`). Plugin **Config** (deployment switch, this plugin's row in your profile) covers `quiet`, `allowMechanismEdit`, `scriptTimeoutMs`, `scriptMaxOutputBytes`, `handoffLevel`, `hookRewriteContent`, and `hookMaxBlockChars` — `handoffLevel` toggles the handoff-check strictness (`basic` = A1/A2/B1 only; `strict` = + structure/version/pairs/agents-log); `hookRewriteContent` defaults to **false** (H2 listener only injects metadata without altering `result.content` — recommended); `hookMaxBlockChars` defaults to **262144** (256 KB) and caps single-text-block sanitization to keep the listener off the hot path for huge payloads (use the `lunheng_ethics_sanitize` tool for paging); the first two are mirrored by the `LUNHENG_QUIET` / `LUNHENG_ALLOW_MECH_EDIT` env vars, while `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` are **Config-only (no env var path)** — all seven are reviewable in the profile; an **invalid config fails loudly at load time** instead of silently falling back to defaults. |
49
+ | DSH integration (bundle install) | Four **read-only** tools — `lunheng_m_gate` (M-gate mechanical pre-check), `lunheng_char_count` (pure Chinese-character count), `lunheng_handoff_check` (handoff-report shape / version / pairs / agents-log verification at the role-handoff boundary), and `lunheng_ethics_sanitize` (ethics de-identification: masks IDs / phones / e-mails / bank cards / person names / place names in interview transcripts and field notes; returns the masked text so the caller decides where to write it; v18.60.1); if your session does not expose them, call the same scripts with `pwsh` as before (same source of truth). Human commands `/lunheng-status` (reads `run/<project>/status.md`; produces no model message) and `/lunheng-stats` (cross-project telemetry dashboard; spawns the host-process `scripts/lunheng-stats.mjs` with `--json` whitelisted only; output truncated to 8000 chars; produces no model message). **Mechanism-file write protection**: a global guard rejects `write`/`edit`-style tool calls that target the skill package, so a session cannot quietly rewrite the pipeline's own rules. Boundary, stated plainly: the guard only sees **tool calls** — `pwsh` and any subprocess are **not** behind this gate; the owner's escape hatch is `LUNHENG_ALLOW_MECH_EDIT=1` (or `config: { allowMechanismEdit: true }`). Plugin **Config** (deployment switch, this plugin's row in your profile) covers `quiet`, `allowMechanismEdit`, `scriptTimeoutMs`, `scriptMaxOutputBytes`, `handoffLevel`, `hookRewriteContent`, `hookMaxBlockChars`, and `ethicsHook` — `handoffLevel` toggles the handoff-check strictness (`basic` = A1/A2/B1 only; `strict` = + structure/version/pairs/agents-log); `hookRewriteContent` defaults to **false** (H2 listener only injects metadata without altering `result.content` — recommended); `hookMaxBlockChars` defaults to **262144** (256 KB) and caps single-text-block sanitization to keep the listener off the hot path for huge payloads (use the `lunheng_ethics_sanitize` tool for paging); the first two are mirrored by the `LUNHENG_QUIET` / `LUNHENG_ALLOW_MECH_EDIT` env vars, while `scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` / `ethicsHook` are **Config-only (no env var path)** — all eight are reviewable in the profile; an **invalid config fails loudly at load time** instead of silently falling back to defaults. |
50
50
 
51
51
  ## Pipeline overview
52
52
 
@@ -122,7 +122,7 @@ The patch layer does two things: it **inserts one row for this package** (`- id:
122
122
  Releases are **tag-only**; a local `npm publish` is forbidden (it would bypass the CI gates and OIDC provenance, and a published npm version can never be overwritten).
123
123
 
124
124
  ```sh
125
- git tag v18.78.2 && git push origin v18.78.2 # push one tag at a time (GitHub: >3 tags in one push triggers no workflow)
125
+ git tag v18.80.0 && git push origin v18.80.0 # push one tag at a time (GitHub: >3 tags in one push triggers no workflow)
126
126
  # publish.yml then runs gate 1 consistency → gate 2 packaging surface → gate 3 hygiene → gate 4 pack smoke → script tests
127
127
  # → tag/version equality → idempotency guard → OIDC publish --provenance --tag dsh → post-publish audit
128
128
  ```
@@ -201,9 +201,9 @@ The coordinator must disclose these and obtain explicit consent at Phase 0. For
201
201
  | Formaldehyde cabbage article (2026-08) | ~4200 chars, 12 sources + 29 data + 4 cases | M-gate exit 0; 6 back-feed rules merged |
202
202
  | Notion vs. idea philosophy paper (2026-09) | ~6280 chars, 18 sources + 15 data, 0 cases | 2 audit rounds, 23/30 minor revision, M-gate true P0 = 0 |
203
203
 
204
- > **本表互引**:本表 6 案例的代表性深入剖析见 [`docs/introduction.md` §实战验证](../lunheng-article-pipeline-dsh/docs/introduction.md)(**甲醛白菜事件 ~4200 字 v2.3.7-dsh.8** 全流程详解 + v18.x 期间 6 案例一览);v2.x 完整字段与产物路径见 [`references/case-studies.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/case-studies.md) §历史快照。
204
+ > **本表互引**:本表 6 案例的代表性深入剖析见 [`docs/introduction.md` §实战验证](docs/introduction.md)(**甲醛白菜事件 ~4200 字 v2.3.7-dsh.8** 全流程详解 + v18.x 期间 6 案例一览);v2.x 完整字段与产物路径见 [`references/case-studies.md`](skills/lunheng-article-pipeline/references/case-studies.md) §历史快照。
205
205
  >
206
- > **新主人口径(v18.78.0 §7.2 立项)**:硬数字(版本 / 角色 / 阶段 / 闸门 / 脚本 / 命令 / 退出码族)单一真源 = [`docs/quick-facts.md`](../lunheng-article-pipeline-dsh/docs/quick-facts.md),本表不再复述。
206
+ > **新主人口径(v18.78.0 §7.2 立项)**:硬数字(版本 / 角色 / 阶段 / 闸门 / 脚本 / 命令 / 退出码族)单一真源 = [`docs/quick-facts.md`](docs/quick-facts.md),本表不再复述。
207
207
 
208
208
  > **How to read this table (two calibers that are easy to mix up)**: ① "revision rounds" counts **all writer passes in that run** (Phase 3.5 → v2, critique/T6 fixes, G14 rounds, audit loop) — the pipeline's own cap of **≤2 rounds** applies to the **Phase 4.2 audit loop alone**, so the two numbers measure different things; ② the outcomes are **historical values recorded at the time of each run, with that run's script version** — they are not reproducible with the current scripts. Re-running the packaged scripts on the archived projects today yields e.g. the formaldehyde cabbage article at `exit 2` with 5 P0 (M-Form-6/10, M-Exist-7/9, M-Integrity-1): three of those gates were added **after** that run. Read this table as "what the pipeline produced then", not as "the current gate set passes these projects".
209
209
 
package/SECURITY.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 安全策略(SECURITY)
2
2
 
3
- > 版本:v18.78.2(DSH 原生插件)
3
+ > 版本:v18.80.0(DSH 原生插件)
4
4
 
5
5
  ## 上报漏洞
6
6
 
@@ -22,9 +22,9 @@
22
22
  | 入口代码 · 原生工具(`lib/tools.js`) | 注册四个**只读**工具:`lunheng_m_gate`(M 门机械预检)、`lunheng_char_count`(纯汉字数)、`lunheng_handoff_check`(交接报告形态校验)、`lunheng_ethics_sanitize`(伦理脱敏,v18.60.1)。**前三个**每次调用**派生一个子进程**跑随包脚本——`spawn(process.execPath, [<skillRoot>/scripts/<门脚本>.mjs, …])`,真实调用集中在前三个 `disposers.push(tools.register(defineTool({…})))` 块(v18.6.0 起统一入口;行号随改动漂移故**按符号引用**:寻找 `name: 'lunheng_*'` 的 `spawn(...)` 调用即可,**不写绝对行号**)。收紧点:脚本路径由入口拼死、**不经 shell**(无 `shell:true`)、参数走数组 → 工具入参**不能**注入额外命令;子进程用的是**同一个 Node 二进制**;v18.2.6 起有超时与输出上限(`Config.scriptTimeoutMs` / `scriptMaxOutputBytes`)。**第四个工具不派生任何子进程**(v18.60.1 如实声明,纠「每次工具调用都派生一个子进程」这一旧断言)——`lunheng_ethics_sanitize` 是 `lib/ethics-sanitize.js` 的**纯函数**:只 `readFileSync` 调用方指定的那一个文本文件 + 读随包三个词表(`references/dicts/*.txt`),**不写任何文件、不联网、不起子进程**;脱敏结果**随返回值给出**,落盘由主控用自身 `write` 决定,故本工具不构成新的写面。**原文回吐面(v18.76.0 · v18.75.1 全量架构审计 R4 修复)**:`render` 输出(= **唯一被会话日志持久化**的那一面,见 `lib/tools.js` 头注释)此前会把 `reviewFlags[].text`(**原文人名**)印出来,与同一返回值里的占位符一一对应、整篇匿名化稿可被还原;现只打印**项数 + 类别**。**残留(如实登记)**:结构化 canonical value 仍含 `replacements[].from` / `reviewFlags[].text`(output schema 明文声明「原名 → 占位符」)——它**不进会话日志**,但 Code Mode / 结构化消费方读得到;需要映射时请自行评估是否把这些字段转交下游。**是否注册取决于宿主**:宿主无 `tools` 服务或 `@deepseek-ai/dsh-tools` 不可解析时,**只降级并打印一行提示,不报错**(安装函数 try/catch 包裹),技能注册不受影响。 |
23
23
  | 入口代码 · 机制写保护(`lib/guard.js`) | 经官方 `ctx.tools.guard()` 安装**全局**工具守卫(**按符号引用**:`lib/guard.js` 的 `installMechanismGuard()` 内的 `tools.guard(...)` 调用;profile 级 ctx → 官方语义「A plain-context guard applies globally」——**不写绝对行号**。v18.18.9 更正:本处原先引的那个行号指向 `const cwd = process.cwd()`,与守卫安装无关,是**行号漂移**造成的假引用;真实安装点是 `installMechanismGuard()` 内的 `tools.guard(...)`)。**作用域是该 profile 下的全部会话**(不止论衡自己的会话):命中受保护根的 `write` / `edit` 类调用**在分发前即被否决**(返回理由 = 否决,后续监听器无法改回允许)。受保护根 = **当前生效的技能副本**(v18.2.6 起另含同名技能的 rank 100 / 200 / 400 / 500 落点)+ `lib/` + 仓库级 `scripts/` + `cordis.patch.yml`;**不含** `docs/`、README、CHANGELOG、`tests/`。**边界(如实,不夸大)**:① 只看**工具调用**——`pwsh` 与任何子进程写盘**不经此门**(官方对子进程的围栏是部署级沙箱,插件改不了别人的 profile);② 只匹配常见写工具名(`write` / `edit` / `apply_patch` / `str_replace*` 等集合 + 参数键名与补丁文本里的路径,**宁松勿误伤**);③ 相对路径按**会话工作区** `execution.agent.session.header.cwd`(与 fs 工具同源)与进程 cwd **两个基准同时判**(v18.2.6 修掉的基准错位绕过);④ **主人授权例外**:宿主环境设 `LUNHENG_ALLOW_MECH_EDIT=1`,或在本插件行 `config` 写 `allowMechanismEdit: true`——授权是主人的动作,agent 不得自行声明;⑤ 纯技能目录部署(不经入口)下**不生效**——那里仍只有文档纪律。故本保护是「**比 prompt 强、比机制强制弱**」的部分强制。 |
24
24
  | 入口代码 · 人类命令(`lib/commands.js`) | 注册两条人类命令(`ctx.commands.register()`;官方语义:命令 dispatch **不产生模型消息**):<br>① **`/lunheng-status`** —— handler **只读工作区文件**:`<cwd>/run/<项目>/status.md` 与 `进展-主人版.md`,输出截断到 4000 字符。**不写文件、不联网、不派生子进程**。未给项目名时 `readdirSync(<cwd>/run)` 取 `status.md` 最近修改的项目,并把最多 12 个项目名回显到命令输出。<br>② **`/lunheng-stats`** —— handler **派生宿主进程** `spawnSync(process.execPath, [<skillRoot>/scripts/lunheng-stats.mjs, '--run-dir', <run>, ...args])`(与 `tools.js` 的异步沙箱内 spawn 不同:本命令跑在宿主进程、有完整 spawn 权限)。**参数白名单化(v18.16.0;v18.62.4 扩面)**:接受 `--json` 与**受围栏的** `--run-dir <path>` 两个旗标;其它 token 一律拒绝(`/lunheng-stats --something-else` → 错误返回,不派生子进程)。`--run-dir` 的值**必须**落在 `<会话工作区>/run/` 之内(逐个 realpath 后判包含关系,与 `/lunheng-status` 的 `pickProject` 同一套三层收口:词法 → 结构 → 物理),用户无法借此把 spawn 指向工作区之外。`lunheng-stats.mjs` 本身只读 `run/<项目>`,**不写文件、不联网**。stdout 截断到 8000 字符。 |
25
- | 入口代码 · DSH 新能力接缝监听器(v18.60.1 反哺 v3,主人显式授权;按符号引用:`lib/index.js` 的 `ctx.on(...)` 块,位于 §执行能力边界 末项之后;H5 已按 v18.62.1 全量审计 P1-1 移除) | **H2 伦理脱敏监听器**(`ctx.on('tools/post-execute', listener)`,**3 参 `(exec, result, next)`**):监听**材料类工具**(`read` / `web_*` / `subagent*`)的 result,自动调 `lib/ethics-sanitize.js` 的 `sanitize()`(basic 模式,`maxChars: MAX_SAFE_INTEGER`)。**载体(v18.76.0 · v18.75.1 全量架构审计 R1 修复)**:命中摘要写成**一行标记**、经 `decision.additionalContexts` 送达下一轮上下文(宿主 `tools/post-execute` **唯一采纳**的插件副通道);`reviewFlags` / `ethicsSanitized` 只挂在**返回值**上、供更晚注册的同进程监听器读取——**不写 `result`**(真宿主交给监听器的 result 是深冻对象,结果随后还要过字段白名单投影,写 `result.*` **必然静默失效**;证据见 `audits/核实报告-v18.75.1-全量架构审计-2026-10-04.md` §2)。**默认不改写工具结果正文**(v18.62.5 定默认值)——避免误伤「时间戳」「订单 ID」「银行卡模式数字」等被硬模式误判的合法文本;**改写为 opt-in**(`Config.hookRewriteContent: true`,走 `decision.content`,默认 `false`);v18.62.4 及更早版本是默认改写,已被审计为 P0-2(见 v18.62.4 全量审计-性能与安全报告 §P0-2)。标记文本**只含计数与类别**,不含原文(避免把 PII 写进会话日志)。**读面**:监听器读 result.content 块数组、读三个随包词表 (`references/dicts/*.txt`);**子进程派生**:**零**(纯函数);**写盘**:**零**(守住 v18.60.0 ethics_sanitize「只读」承诺)。失败只降级 warn,不影响技能。**H5 refresh-gates HMR 联动(已移除)**:原 `ctx.on('file-watcher:change', listener)` 已在 v18.62.1 全量审计 P1-1 删除(宿主无该事件派发方,死监听器);refresh-gates 仍是幂等脚本、可手动跑。 |
25
+ | 入口代码 · DSH 新能力接缝监听器(v18.60.1 反哺 v3,主人显式授权;按符号引用:`lib/index.js` 的 `ctx.on(...)` 块,位于 §执行能力边界 末项之后;H5 已按 v18.62.1 全量审计 P1-1 移除) | **H2 伦理脱敏监听器**(`ctx.on('tools/post-execute', listener)`,**3 参 `(exec, result, next)`**):监听**材料类工具**(`read` / `web_*` / `subagent*`)的 result,自动调 `lib/ethics-sanitize.js` 的 `sanitize()`(basic 模式,`maxChars: MAX_SAFE_INTEGER`)。**载体(v18.76.0 · v18.75.1 全量架构审计 R1 修复)**:命中摘要写成**一行标记**、经 `decision.additionalContexts` 送达下一轮上下文(宿主 `tools/post-execute` **唯一采纳**的插件副通道);`reviewFlags` / `ethicsSanitized` 只挂在**返回值**上、供更晚注册的同进程监听器读取——**不写 `result`**(真宿主交给监听器的 result 是深冻对象,结果随后还要过字段白名单投影,写 `result.*` **必然静默失效**;证据见 `audits/核实报告-v18.75.1-全量架构审计-2026-10-04.md` §2)。**默认不改写工具结果正文**(v18.62.5 定默认值)——避免误伤「时间戳」「订单 ID」「银行卡模式数字」等被硬模式误判的合法文本;**改写为 opt-in**(`Config.hookRewriteContent: true`,走 `decision.content`,默认 false);v18.62.4 及更早版本是默认改写,已被审计为 P0-2(见 v18.62.4 全量审计-性能与安全报告 §P0-2)。标记文本**只含计数与类别**,不含原文(避免把 PII 写进会话日志)。**读面**:监听器读 result.content 块数组、读三个随包词表 (`references/dicts/*.txt`);**子进程派生**:**零**(纯函数);**写盘**:**零**(守住 v18.60.0 ethics_sanitize「只读」承诺)。失败只降级 warn,不影响技能。**H5 refresh-gates HMR 联动(已移除)**:原 `ctx.on('file-watcher:change', listener)` 已在 v18.62.1 全量审计 P1-1 删除(宿主无该事件派发方,死监听器);refresh-gates 仍是幂等脚本、可手动跑。 |
26
26
  | 入口代码 · v18.61.0 反哺 v4 监听器(主人显式授权;原三个,**H3 与 H7 均已移除**——H3 按 v18.62.1 全量审计 P1-1、H7 按 v18.75.1 全量架构审计 R2;按符号引用:`lib/index.js` §执行能力边界末项之后的 `ctx.on(...)` 块) | **H3 G14 启发式预筛监听器(已移除)**:原 `ctx.on('assistant/chunk', listener)` 已在 v18.62.1 全量审计 P1-1 删除(`assistant/chunk` 是 session 日志事件、无 ctx 派发方,监听器永不触发);G14 中文 AI 痕迹检测由 G14 终闸(v2.4.0 + v18.2.8 三层防御第 3 层 LLM 推理判定)完整覆盖。**H4 system-prompt/assemble 钩子**(`ctx.on('system-prompt/assemble', listener)`,**3 参 `(assembly, context, next)`**):监听 prompt 装配 → `await next()` 后向 `sections` **只追加不覆盖** dsh 默认 prompt(附加「论衡技能加载中」提示段 + **每次装配现读**的版本号,v18.76.0 起不再用 apply 期快照——原地升级也能看到真实版本)。**读面**:listener 读装配结果对象 + 向下游追加 sections 项;**子进程派生**:**零**;**写盘**:**零**(仅修改返回的装配结果对象)。失败只降级为返回下游结果原样。**H7 agent/request waterfall 模型路由(已移除 —— v18.76.0 · v18.75.1 全量架构审计 R2)**:原 `ctx.on('agent/request', listener)` 读 `request.toolName` 判三档,但该事件 payload **只有 `{turn, step, signal}`**(宿主 `dsh-agent-loop` 的派发点)→ 判别键恒 `undefined` → **每次请求空转**,属「派发方存在但判别键缺失」的死监听器,且挂在每个模型请求的 waterfall 上。分档路由改由 `examples/preset/agent-tiered/cordis.yml` 的 `agentOptions` 单独承载(**加载期**求值,改 env 后**需重载插件行**);「改 env 不重启即切模型」的旧承诺已撤下。**删除后本包不再注册 `agent/request` 监听器。**
27
- | 可调参数(`export const Config`,v18.2.6 起) | 入口导出 standard-schema 形态的 `Config`(**零宿主依赖**——刻意不 import `@deepseek-ai/schemastery`,见 `lib/index.js` 头注释)。**七个开关**:`allowMechanismEdit`(等价 `LUNHENG_ALLOW_MECH_EDIT=1`)、`quiet`(等价 `LUNHENG_QUIET=1`,只静音 info 级状态行,warn **永不**静音)、`scriptTimeoutMs`、`scriptMaxOutputBytes`、`handoffLevel`(枚举 `basic` / `strict`,默认 `basic`;交接门灰度——`basic` 只验存在/非空/回报六要素,`strict` 加结构/版本/成对/agents-log)、`hookRewriteContent`(v18.62.5 全量审计-性能与安全 P0-2 定默认值;v18.76.0 · R1 改**载体**:默认关闭 = 只产出一行标记经 `decision.additionalContexts` 送达,**不建议**改写——改写会误伤时间戳/订单 ID 等被硬模式误判的合法文本,且改写走 `decision.content` 替换工具结果正文)、`hookMaxBlockChars`(v18.62.5 P2-2 修复:H2 监听器对单个 text 块的脱敏上限,默认 `262144` = 256 KB;超此大小的块**跳过改写**仅注入 `too-large-for-hook` 标记,避免在不可控体积上同步占用「原文 + 脱敏副本」两份内存——超限内容用 `lunheng_ethics_sanitize` 工具分页处理)。**只有前两键有 env 等价物**:`scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` 五者**仅 Config,无 env 路径**(v18.18.1 更正:本行长期写「四个开关」漏 `handoffLevel`;同批对外文档另有一处把 `handoffLevel` 误称为存在 `LUNHENG_HANDOFF_LEVEL` env 镜像,一并更正——**`lib/` 入口面(插件运行期)的 env 读取点共四处**:`LUNHENG_QUIET` 与 `LUNHENG_ALLOW_MECH_EDIT`(均在 `lib/index.js`),以及 `DSH_HOME` 与 `AGENTS_HOME`(`lib/guard.js` 的 `mirrorRoots()`)。**后两者是安全相关面**:它们被用来把「同名技能的用户级落点」并入受保护根(rank 400 / 500),即 `$DSH_HOME/skills/<技能名>` 与 `$AGENTS_HOME/skills/<技能名>`——设了它,guard 的**受保护根集合会变大**;未设则按「不登记」处理(环境变量为空时该分支整体不执行,该落点不会被加进受保护根)。**口径边界(v18.78.1 全量审计 · 批 5 更正)**:本句原写「全库 env 读取点只有 …两处」,按实测(`Select-String -Path lib/*.js -Pattern 'process\.env\.'`)**漏列了 guard 的两处**,且「全库」一词过宽——`cordis.patch.yml` 另有 3 处 `disabled:` 行级门控读 `LUNHENG_TIERING` 与三档 `*_PROVIDER` / `*_MODEL`(**加载期**求值,见下方「加载期执行」行),随包与仓库脚本另读 `GITHUB_ACTIONS` / `DSH_HOME` / `RUN_DIR` 等(不属「插件入口面」这一口径)。**非法配置在加载期响亮失败**(Cordis 抛 ValidationError),不会静默回落到默认值;`--dump-config` 可见。env 是**操作者开关**,Config 是**部署开关**(profile 补丁)。 |
27
+ | 可调参数(`export const Config`,v18.2.6 起) | 入口导出 standard-schema 形态的 `Config`(**零宿主依赖**——刻意不 import `@deepseek-ai/schemastery`,见 `lib/index.js` 头注释)。**八个开关**:`allowMechanismEdit`(等价 `LUNHENG_ALLOW_MECH_EDIT=1`)、`quiet`(等价 `LUNHENG_QUIET=1`,只静音 info 级状态行,warn **永不**静音)、`scriptTimeoutMs`、`scriptMaxOutputBytes`、`handoffLevel`(枚举 `basic` / `strict`,默认 `basic`;交接门灰度——`basic` 只验存在/非空/回报六要素,`strict` 加结构/版本/成对/agents-log)、`hookRewriteContent`(v18.62.5 全量审计-性能与安全 P0-2 定默认值;v18.76.0 · R1 改**载体**:默认关闭 = 只产出一行标记经 `decision.additionalContexts` 送达,**不建议**改写——改写会误伤时间戳/订单 ID 等被硬模式误判的合法文本,且改写走 `decision.content` 替换工具结果正文)、`hookMaxBlockChars`(v18.62.5 P2-2 修复:H2 监听器对单个 text 块的脱敏上限,默认 `262144` = 256 KB;超此大小的块**跳过改写**仅注入 `too-large-for-hook` 标记,避免在不可控体积上同步占用「原文 + 脱敏副本」两份内存——超限内容用 `lunheng_ethics_sanitize` 工具分页处理)、`ethicsHook`(**v18.80.0 · 全量审计-v18.79.1 P1-2 新增**:H2 伦理脱敏监听器**总开关**,默认 true;置 false 则本会话完全不扫、不注入伦理标记——纯代码/文档工作流用它关掉常驻噪声,实测依据见 CHANGELOG 本版段)。**只有前两键有 env 等价物**:`scriptTimeoutMs` / `scriptMaxOutputBytes` / `handoffLevel` / `hookRewriteContent` / `hookMaxBlockChars` / `ethicsHook` 六者**仅 Config,无 env 路径**(v18.18.1 更正:本行长期写「四个开关」漏 `handoffLevel`;同批对外文档另有一处把 `handoffLevel` 误称为存在 `LUNHENG_HANDOFF_LEVEL` env 镜像,一并更正——**`lib/` 入口面(插件运行期)的 env 读取点共四处**:`LUNHENG_QUIET` 与 `LUNHENG_ALLOW_MECH_EDIT`(均在 `lib/index.js`),以及 `DSH_HOME` 与 `AGENTS_HOME`(`lib/guard.js` 的 `mirrorRoots()`)。**后两者是安全相关面**:它们被用来把「同名技能的用户级落点」并入受保护根(rank 400 / 500),即 `$DSH_HOME/skills/<技能名>` 与 `$AGENTS_HOME/skills/<技能名>`——设了它,guard 的**受保护根集合会变大**;未设则按「不登记」处理(环境变量为空时该分支整体不执行,该落点不会被加进受保护根)。**口径边界(v18.78.1 全量审计 · 批 5 更正)**:本句原写「全库 env 读取点只有 …两处」,按实测(`Select-String -Path lib/*.js -Pattern 'process\.env\.'`)**漏列了 guard 的两处**,且「全库」一词过宽——`cordis.patch.yml` 另有 3 处 `disabled:` 行级门控读 `LUNHENG_TIERING` 与三档 `*_PROVIDER` / `*_MODEL`(**加载期**求值,见下方「加载期执行」行),随包与仓库脚本另读 `GITHUB_ACTIONS` / `DSH_HOME` / `RUN_DIR` 等(不属「插件入口面」这一口径)。**非法配置在加载期响亮失败**(Cordis 抛 ValidationError),不会静默回落到默认值;`--dump-config` 可见。env 是**操作者开关**,Config 是**部署开关**(profile 补丁)。 |
28
28
  | 加载期执行 | `cordis.patch.yml` 含 **6 处 `!!js` 表达式** = **3 处 `disabled:`(三段分档工具行的行级门控,读 `LUNHENG_TIERING` 与三档 `*_PROVIDER` / `*_MODEL`)** + **3 处 `agentOptions:`(各自读本档 `LUNHENG_<档>_PROVIDER` / `_MODEL`,未设则返回 `undefined`)**。计数口径:`^\s*[A-Za-z]+:\s*!!js` 的 YAML 值行(`Select-String cordis.patch.yml -Pattern '^\s*[A-Za-z]+:\s*!!js'` → 6 行)。它们由 DSH 宿主进程在**加载期以完整 Node 权限**求值(`disabled` 由 loader 的 `disabledOf()` 求值,`@deepseek-ai/cordis-plugin-loader/src/config/entry.ts:104-107`),**发生在 agent 沙箱与审批关卡之前**——安装本包即等于允许这些表达式在每次启动时执行。(v18.2.6 更正:旧版本节与 patch 注释只写「3 处」,**漏算 3 处 `disabled`**,等于把加载期执行面**少报一半**。)另:v18.2.6 起三段分档行改为「**默认不装载**」——未设任何 `LUNHENG_*` 时三行不挂载(不再每会话白付 3 份与内置 `subagent` 同义的工具 schema)。 |
29
29
  | 允许的表达式内容 | 仅 `process.env.*` 与全局 `Object.assign`。(v18.0.0 起技能目录不再由 patch 求值挂载,故 `baseUrl` / `URL` / `decodeURIComponent` / `process.platform` 已不再需要——表达式面比 v17.0.0 **更窄**。) |
30
30
  | 禁止的表达式内容(CI 红线,`consistency-check.mjs` 规则 ⑭) | `getBuiltinModule` / `child_process` / `require(` / `import(` / `eval(` / `new Function` / `node:` / `fs.` |
package/cordis.patch.yml CHANGED
@@ -1,4 +1,4 @@
1
- # lunheng-article-pipeline bundle patch(DSH 原生插件 v18.78.2)
1
+ # lunheng-article-pipeline bundle patch(DSH 原生插件 v18.80.0)
2
2
  #
3
3
  # 本文件用 DSH cordis-plugin-include 的 `- insert:` 列表语法在 standard 组合树之上叠加 4 行:
4
4
  # 1. **本包入口(自注册行)**:`name: lunheng-article-pipeline` → loader 按包名解析到
@@ -9,7 +9,7 @@
9
9
  # ⚠️ **务必钉版本**:不写 `@<版本>` 时,装到哪个版本取决于本机包管理器**当时**的解析状态。
10
10
  # 实测过一次:同一条命令装到 **18.15.0**,而 registry 上的 `latest` 已是 18.20.4 —— 差了五个小版本,
11
11
  # 且按下面流程走下去**没有任何一步能让人察觉**。版本真源只有一处:`npm view lunheng-article-pipeline version`。
12
- dsh plugin --profile <profile> add lunheng-article-pipeline@18.78.2
12
+ dsh plugin --profile <profile> add lunheng-article-pipeline@18.80.0
13
13
 
14
14
  # 1b) **核对装到的版本**(钉了版本也值得跑一次——它读的是 profile 里**实际落盘**的 package.json)
15
15
  node -e "console.log(require('<DSH_HOME>/profiles/<profile>/node_modules/lunheng-article-pipeline/package.json').version)"
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **论衡(lunheng-article-pipeline)** 是一个多 Agent 深度长文生产流水线,DeepSeek Harness(dsh)bundle 插件。它不是让一个 AI 直接写文章,而是让一支 **9 个 AI 角色组成的"论文生产小队"** 按既定协议协作:定题 → 三线并行检索 → 分析 → 写作 → 批判 → 审计 → 审稿 → 终检。每一步都有明确产出物、交接报告与质量闸门,最终交付**有证据底座、有反方论证、有独立审计、有人工核验节点**的文章。
4
4
 
5
- > 适用:公众号深度长文、研究报告、学术论文、商业评论、行业分析——任何"要站得住脚"的长内容。当前版本 **v18.78.2**(DSH 独立版本)。
5
+ > 适用:公众号深度长文、研究报告、学术论文、商业评论、行业分析——任何"要站得住脚"的长内容。当前版本 **v18.80.0**(DSH 独立版本)。
6
6
 
7
7
  ---
8
8
 
@@ -206,7 +206,7 @@ dsh plugin --profile web add lunheng-article-pipeline
206
206
  ## 获取方式
207
207
 
208
208
  - **GitHub(DSH bundle)**:https://github.com/zuoyunlai/lunheng-article-pipeline-dsh
209
- - **npm**:`lunheng-article-pipeline@dsh`(dist-tag `dsh` = 最新 DSH 迭代版);锁定具体版本可写 `@18.78.2`
209
+ - **npm**:`lunheng-article-pipeline@dsh`(dist-tag `dsh` = 最新 DSH 迭代版);锁定具体版本可写 `@18.80.0`
210
210
  - **历史版本线(独立化前)**:https://github.com/zuoyunlai/lunheng-article-pipeline
211
211
 
212
212
  ---
@@ -220,12 +220,12 @@ dsh plugin --profile web add lunheng-article-pipeline
220
220
  | 项 | 单一真源 | 本档章节指针 |
221
221
  |---|---|---|
222
222
  | 硬数字(版本 / 角色 / 阶段 / 闸门 / 脚本 / 命令 / 退出码族) | [`docs/quick-facts.md`](quick-facts.md) | §实战验证段 / §核心设计 / §M 门段 |
223
- | 概念(9 角色 / 三环制 / G 清单 / F 模式 / 三角验证 等) | [`skills/lunheng-article-pipeline/references/glossary.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/glossary.md) | §修订回环段 / §独立审计闭环 / §能力演进段 |
224
- | 流水线协议(Phase 0-5 + 派发话术) | [`skills/lunheng-article-pipeline/references/pipeline-readme.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/pipeline-readme.md) | §核心设计段 / §修订回环段 |
225
- | M 门算法(25 项总 / 真源 / 复算) | [`skills/lunheng-article-pipeline/references/_shared/M-Gate-Algorithm.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/_shared/M-Gate-Algorithm.md) | §独立审计闭环 / §M 门段 |
226
- | T9 启用决策树(文类档案) | [`skills/lunheng-article-pipeline/references/_shared/文类档案.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/_shared/文类档案.md) | §能力演进段 |
223
+ | 概念(9 角色 / 三环制 / G 清单 / F 模式 / 三角验证 等) | [`skills/lunheng-article-pipeline/references/glossary.md`](../skills/lunheng-article-pipeline/references/glossary.md) | §修订回环段 / §独立审计闭环 / §能力演进段 |
224
+ | 流水线协议(Phase 0-5 + 派发话术) | [`skills/lunheng-article-pipeline/references/pipeline-readme.md`](../skills/lunheng-article-pipeline/references/pipeline-readme.md) | §核心设计段 / §修订回环段 |
225
+ | M 门算法(25 项总 / 真源 / 复算) | [`skills/lunheng-article-pipeline/references/_shared/M-Gate-Algorithm.md`](../skills/lunheng-article-pipeline/references/_shared/M-Gate-Algorithm.md) | §独立审计闭环 / §M 门段 |
226
+ | T9 启用决策树(文类档案) | [`skills/lunheng-article-pipeline/references/_shared/文类档案.md`](../skills/lunheng-article-pipeline/references/_shared/文类档案.md) | §能力演进段 |
227
227
  | 排错(exit code / 闸门报错) | [`docs/troubleshooting.md`](troubleshooting.md) | §M 门段 / §独立审计闭环 |
228
- | 实战案例(v18.x + 历史 v2.x) | [`README.md` 顶部 Verification status 表](../lunheng-article-pipeline-dsh/README.md) + [`skills/lunheng-article-pipeline/references/case-studies.md`](../lunheng-article-pipeline-dsh/skills/lunheng-article-pipeline/references/case-studies.md) | §实战验证段 |
228
+ | 实战案例(v18.x + 历史 v2.x) | [`README.md` 顶部 Verification status 表](../README.md) + [`skills/lunheng-article-pipeline/references/case-studies.md`](../skills/lunheng-article-pipeline/references/case-studies.md) | §实战验证段 |
229
229
  | 五语 README(en/zh/es/pt/hi) | `README.md` / `README-zh.md` / `README-es.md` / `README-pt.md` / `README-hi.md`(顶部均含 🌐 语言切换器行) | §获取方式 + §实战验证段互引 |
230
230
 
231
231
  > **本档变更机制**:按 AGENTS.md §机制文件写保护,本档属「**机制文件**」——agent 默认禁写;改进动议只写 `audits/反哺报告-vN.md`,由主人在 host shell 手工 apply。**v18.78.0 §7.1 立规则 ㉜** 已把「CHANGELOG 新段位 ↔ §实战验证段对账」机械化——后续主人 bump 章节时 ㉜ 会主动提示。
@@ -1,6 +1,6 @@
1
1
  # 故障排查(troubleshooting)
2
2
 
3
- > 版本:v18.78.2(DSH 原生插件;发布日期见 CHANGELOG 对应版本段——**此处刻意不写死日期**。v18.62.4 更正:旧文写「发布于 2026-09-25」而该版本段实际为 2026-10-01,且日期点位无任何机检覆盖)
3
+ > 版本:v18.80.0(DSH 原生插件;发布日期见 CHANGELOG 对应版本段——**此处刻意不写死日期**。v18.62.4 更正:旧文写「发布于 2026-09-25」而该版本段实际为 2026-10-01,且日期点位无任何机检覆盖)
4
4
 
5
5
  安装/验证失败时按「症状 → 原因 → 处置」对照。
6
6
 
@@ -111,14 +111,14 @@ node --test "tests/**/*.test.mjs" # 随包脚
111
111
  > **不共用本语义的工具**(以各自头注释为准,均非流水线闸门):`token-budget` / `token-cost`(**v18.12.0 起**:0 成功 / 10 参数或路径错(含用法错) / 70 内部错误——旧版用法错用 1、`token-budget` 更早的 2 均与 M 门撞义,已收口)、`md2html`(**v18.12.0 起**:0 成功 / 10 参数或路径错 / **40 导出被拒·`--strict` 校验失败** / 70 内部错误)、`pdfcheck`(1 结构异常 / 10 参数或路径错)、`normalize-trust-level`(1 = 有未决条目,其自有语义;全部输入路径都不存在 → 10)、`apply-diff`(**v18.2.6 补登**:0 = 全部条目应用成功 / 1 = 有跳过或未解析条目、或清单解析出 0 条(需人工处理)/ 10 = 参数或路径错 / 70 = 内部错误(口径以该脚本头注释为准),其余走 `exit-guard` 兜底)、`consistency-check` 与仓库两道门(0/1,CI 独立命名空间)。
112
112
  > **命名空间配额(v18.12.1 登记,防下一个人随便挑数字)**:M 门族 `0/1/2/3` + 参数错 `10` + 内部错 `70`;**非 M 门语义一律另给码,且不得复用 0-3/10/70** —— 已有 `4`(model-routing 需人工决定)、`20/21/22`(交接门)、`30`(裁定被拒)、`40`(md2html 导出被拒)。下次新增非闸门退出码时从这里往后取,并在**同一次提交**里登记进 `EXIT_CONTRACT` + 本表。
113
113
  >
114
- > 退出码契约由**源码仓库的** `scripts/repo-hygiene-check.mjs` 的**退出码表**机械核验(v18.0.2 新增;**v18.0.5 大修**——旧版只 grep `process.exit(字面量)`、且「表内数字在文件里出现过」近乎恒真,等于没核)。现规则:解析 `process.exit(<字面量|本文件 const|guard 导出常量>)` 的实际取值 → 必须是声明集子集;每个声明码必须能被解析或(动态 exit 时)有字面量;每个读盘脚本**必须 import `_lib/exit-guard.mjs`**,否则判失败。**边界(如实)**:动态计算的退出码静态不可判定,那部分由 `tests/scripts.test.mjs` 的异常路径用例覆盖。
114
+ > 退出码契约由**源码仓库的** `scripts/repo-hygiene-check.mjs` 的**退出码表**机械核验(v18.0.2 新增;**v18.0.5 大修**——旧版只 grep `process.exit(字面量)`、且「表内数字在文件里出现过」近乎恒真,等于没核)。现规则:解析 `process.exit(<字面量|本文件 const|guard 导出常量>)` 的实际取值 → 必须是声明集子集;每个声明码必须能被解析或(动态 exit 时)有字面量;每个读盘脚本**必须 import `_lib/exit-guard.mjs`**,否则判失败。**边界(如实)**:动态计算的退出码静态不可判定,那部分由 `tests/exit-namespace.test.mjs` 与 `tests/exit-resolution.test.mjs` 覆盖(退出码族的对抗用例)。
115
115
  >
116
116
  > **上段的「同一次提交一起改」现已有门**(v18.18.5,审计 C-11 机械化):规则 **⑧b** 双向对账「`EXIT_CONTRACT` 里实际用到的码集合」与「本段声明的码集合」——**加了码却没登记到本段**会红,**本段声明了但代码里已无人用**也会红。至此本段与 `EXIT_CONTRACT` 不会静默漂移。
117
117
 
118
118
  ## 9. CI 绿灯但内容有问题
119
119
 
120
120
  先确认四道门都跑了:`ci.yml` 的 `drift-check` / `plugin-surface` / `hygiene` / `pack-smoke` / `script-tests`(后者含 **windows / macos** 矩阵)。
121
- 若某类漂移仍漏检,请按 `consistency-check.mjs` 的既有规则样式补规则 + **对抗测试**(注入假漂移确认能抓到,再还原),见 `tests/scripts/.mjs`。
121
+ 若某类漂移仍漏检,请按 `consistency-check.mjs` 的既有规则样式补规则 + **对抗测试**(注入假漂移确认能抓到,再还原),见 `tests/scripts/consistency-check.test.mjs`(一致性规则族的 per-script 用例)与 `tests/exit-namespace.test.mjs`(退出码族对抗用例)。
122
122
 
123
123
  > **「安装→启动→卸载」这一段曾经没有门**(v18.0.5 补,第三方审计 P1-7):CI 从来没有 `verify` job,本机 `dsh-plugin-dev verify` 又被 DSH Desktop 的 `dsh` shim(硬编码 `DSH_HOME`,见 §7)与 pnpm 原生依赖策略挡住。现由 **`pack-smoke` job + 源码仓库的 `scripts/pack-smoke.mjs`** 覆盖「发布物可装载」:`npm pack` → 解包 → 断言自注册行恰一行 / patch 行依赖已声明 / 真跑解包入口的 `apply`(注册名、正文非空、frontmatter 已剥离、`resourceBase` 指向解包目录)/ `tests` 不随包。**它仍不等于官方 verify**(不起真实 profile)——那一段在本机不可达,如实标注。
124
124
 
@@ -17,7 +17,7 @@
17
17
  ```sh
18
18
  # 1. 在 profile 里声明 bundle 依赖(DSH 读 dsh.bundle.patch 找到 cordis.patch.yml 加载)
19
19
  dsh plugin --profile web add lunheng-article-pipeline@dsh # 推荐:跟随最新 DSH 迭代版
20
- # 锁定具体版本:dsh plugin --profile web add lunheng-article-pipeline@18.78.2
20
+ # 锁定具体版本:dsh plugin --profile web add lunheng-article-pipeline@18.80.0
21
21
 
22
22
  # 2. (可选,但要让三档工具真的挂上就必做)设三档 subagent 工具的 provider/model 环境变量
23
23
  # v18.2.6 起:不设任何变量 = 三档行**根本不装载**(不再是「装载但全继承」)
@@ -191,11 +191,22 @@ export function sanitize(text, opts = {}) {
191
191
  const maxList = Number.isFinite(opts.maxList) && opts.maxList > 0 ? opts.maxList : 200
192
192
 
193
193
  const src = typeof text === 'string' ? text : ''
194
- const counts = { idcard: 0, bankcard: 0, phone: 0, landline: 0, email: 0, person: 0, place: 0 }
194
+ // v18.80.0(全量审计-v18.79.1 **P1-2**):`person` 拆成 **高/低置信两个计数**。
195
+ // 病灶(审计实测):对真实技术文本 `SKILL.md` 报 `person=462`、`CHANGELOG.md` 报 `person=1658`,
196
+ // 而抽样**无一是真人姓名**(`生多 / 文流水 / 明细 / 成因 / 包目录 / 权限 / 开关`…)——
197
+ // 人名轮的正则是 `(${ROLE_WORDS})?(${surnames})([汉字]{1,2})`,「姓氏字 + 任意 1-2 汉字」的
198
+ // 笛卡尔积覆盖海量普通词,排除表(476 词)挡不住。后果有两层:
199
+ // ① 汇总用「**命中替换** N 处」这一措辞报全部命中 → 消费方以为「真的替换了 N 处 PII」;
200
+ // ② 本工具是交付给论文作者用的,照 `docs` 的用法(把返回 text 交给下游角色)会把一份正常
201
+ // 中文稿替换成数百处 `受访者A/B/C` —— 属**静默损坏正文**。
202
+ // 本版三处收敛:① 计数分档;② **低置信不再改写正文**(只登记待复核);③ 分档进 `summarize`。
203
+ const counts = { idcard: 0, bankcard: 0, phone: 0, landline: 0, email: 0, person: 0, personHigh: 0, personLow: 0, place: 0 }
195
204
  const replacements = []
196
205
  const reviewFlags = []
197
206
  const stats = { persons: dicts.surnames.size, places: dicts.places.size }
198
207
  const distinct = new Set()
208
+ /** 低置信候选是否也改写正文。**默认 false**(见上);工具侧经 `rewriteLowConfidence` 开启。 */
209
+ const rewriteLow = opts.rewriteLowConfidence === true
199
210
 
200
211
  if (mode === 'none') {
201
212
  return {
@@ -271,9 +282,22 @@ export function sanitize(text, opts = {}) {
271
282
  if (prev >= '\u4e00' && prev <= '\u9fff' && dicts.excludes.has(prev + surname)) return m
272
283
  }
273
284
  const full = surname + g
285
+ // 计数**先于**一切上限判定:`counts.personLow` 是「本轮共发现多少低置信候选」的**完整**事实
286
+ // (与 maxList 无关);`reviewFlags` 只是它的**前 200 条明细**。二者不得混为一谈——
287
+ // 旧实现在 reviewFlags 打满后静默丢弃,使「候选数」被上限伪装成事实。
274
288
  counts.person++
275
- distinct.add(full)
289
+ if (role) counts.personHigh++
290
+ else counts.personLow++
276
291
  const to = nextPerson(full)
292
+ // v18.80.0 P1-2:**低置信默认不改写正文**——只登记候选(改由调用方决定是否送审)。
293
+ // 高置信(带角色词前缀,如「受访者张三」)仍照旧替换:那才是正文真的在写「某人在说话」。
294
+ if (!role && !rewriteLow) {
295
+ if (reviewFlags.length < maxList) {
296
+ reviewFlags.push({ kind: 'person-low-confidence', text: full, detail: '姓氏命中但上下文无角色词——**未改写正文**,请人工确认是否真人姓名(实测此类绝大多数是把普通词切成人名)' })
297
+ }
298
+ return m
299
+ }
300
+ distinct.add(full)
277
301
  if (replacements.length < maxList) replacements.push({ type: 'person', label: '人名', from: full, to, ...(role ? {} : { lowConfidence: true }) })
278
302
  if (!role && reviewFlags.length < maxList) {
279
303
  reviewFlags.push({ kind: 'person-low-confidence', text: full, detail: '姓氏命中但上下文无角色词——请人工确认是否真人姓名(可能把普通词切成人名)' })
@@ -322,9 +346,13 @@ export function sanitize(text, opts = {}) {
322
346
  /** 人类可读摘要(供工具 render 与主控汇报复用;**不含脱敏文本本身**)。 */
323
347
  export function summarize(result) {
324
348
  const c = result.counts
349
+ const low = c.personLow || 0
350
+ const high = c.personHigh !== undefined ? c.personHigh : (c.person || 0)
325
351
  const parts = [
326
352
  `身份证 ${c.idcard}`, `手机 ${c.phone}`, `固话 ${c.landline}`, `邮箱 ${c.email}`, `银行卡 ${c.bankcard}`,
327
- `人名 ${c.person}(${result.distinctPersons} 个不同)`, `地名 ${c.place}`,
353
+ // v18.80.0 P1-2:人名**分档报**——旧版把「高置信替换」与「低置信候选」合并成「人名 N」,
354
+ // 使一个 `CHANGELOG.md` 也能报出 `人名 1658`。分档后消费方一眼能分辨「真的换了几个」。
355
+ `人名·高置信 ${high}`, `人名·低置信候选 ${low}(${result.distinctPersons} 个不同已被替换)`, `地名 ${c.place}`,
328
356
  ]
329
357
  return `模式=${result.mode}|替换:${parts.join(' / ')}|待复核 ${result.reviewFlags.length} 条` +
330
358
  (result.truncated ? '|⚠️ 输出已截断(原文超上限,请分段处理)' : '') +
package/lib/index.js CHANGED
@@ -57,6 +57,7 @@ const CONFIG_SPEC = Object.freeze({
57
57
  handoffLevel: { kind: 'enum', def: 'basic', values: ['basic', 'strict'], why: '交接门灰度开关(v18.6.0):basic 只验 A1/A2/B1(存在/非空/回报六要素),strict 加结构/版本/成对/agents-log' },
58
58
  hookRewriteContent: { kind: 'boolean', def: false, why: 'H2 伦理脱敏监听器(v18.62.5 全量审计-性能与安全 P0-2 定默认值;本批修复改**载体**):默认 false = 「只标记不改写」——标记经 `decision.additionalContexts`(宿主 `tools/post-execute` **唯一采纳**的插件副通道)作为一行摘要进下一轮上下文;true = 用脱敏后正文替换工具结果的 content(走 `decision.content`;v18.62.4 旧默认行为,会误伤时间戳/订单 ID 等普通文本,**不建议**)。**键名与语义保持不变,只改载体**——旧载体(写 `result.*`)在真宿主上必失效(result 深冻 + 白名单投影,见 lib/index.js 的 H2 监听器注释)。**版本细节见 CHANGELOG 与 `audits/核实报告-v18.75.1-全量架构审计-2026-10-04.md`。**' },
59
59
  hookMaxBlockChars: { kind: 'positive', def: 262144, why: 'H2 监听器对单个 text 块的脱敏上限(v18.62.5 全量审计-性能与安全 P2-2 修复):默认 262144 = 256 KB。超此大小的块**跳过脱敏改写**(仅注入 skipped 元数据),避免在不可控体积上同步占用「原文 + 脱敏副本」两份内存。' },
60
+ ethicsHook: { kind: 'boolean', def: true, why: 'H2 伦理脱敏监听器**总开关**(v18.80.0 新增)。默认 true。置 false = 本会话完全不扫、不注入伦理标记。**为什么必须给开关**:该监听器对材料类工具的**每个** text 块无条件跑 basic 模式,而实测它对技术文档的命中率是「**person 全部为低置信、无一是真人姓名**」(见 CHANGELOG 本版段的实测数字)——纯代码审计与文档工作流里它只产出噪声。默认 true 是因为伦理脱敏是**合规能力**,不能因噎废食;但操作者必须有办法关掉它。' },
60
61
  })
61
62
 
62
63
  /** 默认值(文档与测试引用此处;改这里即改默认)。 */
@@ -366,6 +367,12 @@ export function apply(ctx, config) {
366
367
  // ——该消息会被 host 落进会话日志,原样回吐 PII 会重演 R4。
367
368
  const decided = (await next()) ?? { kind: 'accept' }
368
369
  try {
370
+ // v18.80.0(全量审计 P1-2):**总开关**。默认 true(伦理脱敏是合规能力),
371
+ // 置 false 则本会话完全不扫、不注入标记——供纯代码与文档工作流关掉噪声。
372
+ // 为什么给开关:该监听器对材料类工具的每个 text 块无条件跑 basic 模式,
373
+ // 而实测其对技术文档的命中「person 全部为低置信、无一是真人姓名」(数字见 CHANGELOG 本版段)
374
+ // ——在不需要伦理脱敏的场景里,它只产出常驻上下文噪声。
375
+ if (cfg.ethicsHook === false) return decided
369
376
  // v18.78.1 全量审计 · 批 5(P3 · H2 工具名大小写):与同包 `lib/guard.js` 的写工具名判定同口径(那里已
370
377
  // `.toLowerCase()`)。不加 lower 时 `Read` / `READ` / `WEB_SEARCH` / `Subagent` 全部
371
378
  // 被判成「非材料类工具」→ **静默漏脱敏**(探针实测:四者 ethicsSanitized 全为 no)。
@@ -391,7 +398,7 @@ export function apply(ctx, config) {
391
398
  // 改写正文改为**显式 opt-in**(`Config.hookRewriteContent: true`,默认 `false`),走 `decision.content`。
392
399
  const hookRewrite = !!cfg.hookRewriteContent
393
400
  const hookMaxBlockChars = Number.isFinite(cfg.hookMaxBlockChars) && cfg.hookMaxBlockChars > 0 ? cfg.hookMaxBlockChars : 262144
394
- const aggCounts = { idcard: 0, bankcard: 0, phone: 0, landline: 0, email: 0, person: 0, place: 0 }
401
+ const aggCounts = { idcard: 0, bankcard: 0, phone: 0, landline: 0, email: 0, person: 0, personHigh: 0, personLow: 0, place: 0 }
395
402
  let aggReplacements = 0
396
403
  let aggTruncated = false
397
404
  const distinctPersons = new Set()
@@ -479,13 +486,18 @@ export function apply(ctx, config) {
479
486
  out.content = sanitized
480
487
  }
481
488
  // ② 标记走 `additionalContexts`——宿主**唯一**会把「插件附加信息」送达消费方的通道。
482
- const actionable = aggReplacements > 0 || skippedTooLarge > 0 || deduped.length > 0 || !!dicts?.missing?.length
489
+ // v18.80.0(P1-2):`actionable` 补 `lowCandidates > 0` —— 低置信候选**不再改写正文**,
490
+ // 但仍须让操作者知道「扫到了多少候选」(否则从「噪声」变成「沉默」,更糟)。
491
+ const lowCandidates = aggCounts.personLow || 0
492
+ const actionable = aggReplacements > 0 || skippedTooLarge > 0 || deduped.length > 0 || !!dicts?.missing?.length || lowCandidates > 0
483
493
  if (actionable) {
484
- const byType = Object.entries(aggCounts).filter(([, v]) => v > 0).map(([k, v]) => `${k}=${v}`)
494
+ // byType 只列**真实发生**的计数:`personLow` 是候选数不是替换数,单列,避免混进「替换」列表。
495
+ const byType = Object.entries(aggCounts).filter(([k, v]) => v > 0 && k !== 'personLow').map(([k, v]) => `${k}=${v}`)
485
496
  const kinds = [...new Set(deduped.map((r) => String(r?.kind || 'unknown')))]
486
497
  const marker =
487
498
  `〔论衡 H2 伦理脱敏〕basic 模式:命中替换 ${aggReplacements} 处` +
488
499
  (byType.length ? `(${byType.join(' / ')})` : '') +
500
+ (lowCandidates > 0 ? `;另有**低置信候选 ${lowCandidates} 个未改写**(姓氏命中但无角色词,实测绝大多数是普通词,须人工确认)` : '') +
489
501
  `;扫描 text 块 ${scannedBlocks} 个、跳过超限块 ${skippedTooLarge} 个;改写正文=${hookRewrite ? '是' : '否'}` +
490
502
  (kinds.length ? `;待复核类别:${kinds.join(' / ')}(明细见 lunheng_ethics_sanitize 工具返回值,本标记不含原文)` : '') +
491
503
  (dicts?.missing?.length ? `;⚠ 词表缺失:${dicts.missing.join(' / ')}(该维度未生效,不得声称已完整脱敏)` : '') +
package/lib/tools.js CHANGED
@@ -364,6 +364,8 @@ export async function installLunhengTools(ctx, { skillRoot, defineToolFactory, r
364
364
  '**只读**:不写盘、不联网、不起子进程;脱敏后的文本**随返回值给出**,落盘由主控用自身 write 工具决定。' +
365
365
  '覆盖:身份证 / 手机 / 固话 / 邮箱 / 银行卡(正则判定)+ 人名 / 地名(词表判定)。' +
366
366
  'mode:none(显式声明不脱敏)/ basic(默认)/ strict(地名泛化到省级,保留地域分析价值)。' +
367
+ '**低置信人名默认不改写正文**(v18.80.0 P1-2):姓氏命中但上下文无角色词的人名只登记进 reviewFlags,' +
368
+ '要连它们一起替换须显式传 rewriteLowConfidence=true(**会误伤**「张三补充」→「受访者C充」这类普通词,故不默认)。' +
367
369
  '**能力边界(务必如实转述,不得读成「已彻底匿名化」)**:人名与地名靠词表匹配,' +
368
370
  '召回率与精度都不可能 100%;拿不准的进 reviewFlags 待人工复核。**本工具不是双盲评审的充分条件**。' +
369
371
  '大文件用 offset + maxChars 分页读(返回 nextOffset)。',
@@ -372,6 +374,7 @@ export async function installLunhengTools(ctx, { skillRoot, defineToolFactory, r
372
374
  mode: { type: 'string', enum: ['none', 'basic', 'strict'], description: '脱敏模式;省略 = basic' },
373
375
  offset: { type: 'number', description: '返回文本的起始字符偏移(分页用);省略 = 0' },
374
376
  maxChars: { type: 'number', description: '本次返回的字符上限;省略 = 60000' },
377
+ rewriteLowConfidence: { type: 'boolean', description: 'true = 低置信人名也一并替换(默认 false:只登记待复核,不改写正文)' },
375
378
  },
376
379
  output: {
377
380
  schema: {
@@ -384,7 +387,7 @@ export async function installLunhengTools(ctx, { skillRoot, defineToolFactory, r
384
387
  textTotalChars: { type: 'number', description: '脱敏后全文总字符数' },
385
388
  nextOffset: { type: 'number', description: '下一页的 offset;仅当还有未返回内容时出现' },
386
389
  truncated: { type: 'boolean', description: '本片段被 maxChars 截断(还有后续内容)' },
387
- counts: { type: 'json', description: '各类实体替换计数(idcard/bankcard/phone/landline/email/person/place)' },
390
+ counts: { type: 'json', description: '各类实体替换计数(idcard/bankcard/phone/landline/email/person/personHigh/personLow/place;personLow 是**候选数非替换数**)' },
388
391
  distinctPersons: { type: 'number', description: '涉及的不同人名个数' },
389
392
  replacements: { type: 'json', description: '替换明细(原名 → 占位符),受条数上限约束' },
390
393
  reviewFlags: { type: 'json', description: '**待人工复核项**(含低置信度人名与缺词表降级说明)' },
@@ -431,7 +434,7 @@ export async function installLunhengTools(ctx, { skillRoot, defineToolFactory, r
431
434
  }
432
435
  const raw = readFileSync(args.file, 'utf8')
433
436
  const dicts = loadDicts(skillRoot)
434
- const full = sanitize(raw, { mode: args.mode, dicts, maxChars: Number.MAX_SAFE_INTEGER })
437
+ const full = sanitize(raw, { mode: args.mode, dicts, maxChars: Number.MAX_SAFE_INTEGER, rewriteLowConfidence: args.rewriteLowConfidence === true })
435
438
  const offset = Number.isFinite(args.offset) && args.offset > 0 ? Math.floor(args.offset) : 0
436
439
  const limit = Number.isFinite(args.maxChars) && args.maxChars > 0 ? Math.floor(args.maxChars) : 60000
437
440
  const total = full.text.length