session-orchestrator 3.20.0 → 3.22.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/000-session-orchestrator.mdc +3 -2
- package/.cursor/rules/030-wave-execution.mdc +10 -8
- package/.cursor/rules/040-discovery.mdc +6 -6
- package/.cursor/rules/050-plan.mdc +8 -8
- package/CHANGELOG.md +515 -0
- package/README.md +16 -11
- package/agents/analyst.md +1 -1
- package/agents/architect-reviewer.md +1 -1
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +1 -1
- package/agents/dialectic-deriver.md +1 -1
- package/agents/docs-writer.md +1 -1
- package/agents/memory-proposal-collector.md +7 -5
- package/agents/qa-strategist.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/session-reviewer.md +42 -1
- package/agents/skill-applied-judge.md +1 -1
- package/agents/test-writer.md +1 -1
- package/agents/ui-developer.md +1 -1
- package/agents/ux-evaluator.md +1 -1
- package/commands/eli5.md +33 -0
- package/commands/release.md +62 -0
- package/commands/test.md +2 -2
- package/docs/components.md +6 -5
- package/docs/migration-v3.md +9 -6
- package/docs/persona-panel.md +3 -1
- package/docs/scope-collision-guard.md +167 -0
- package/docs/session-config-reference.md +31 -8
- package/hooks/_lib/lock-bootstrap.mjs +19 -13
- package/hooks/enforce-scope.mjs +103 -3
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +21 -1
- package/hooks/on-session-end.mjs +76 -97
- package/hooks/on-session-start.mjs +195 -104
- package/hooks/on-stop.mjs +127 -12
- package/hooks/post-bash-write-verify.mjs +8 -32
- package/hooks/pre-auq-clarity.mjs +787 -0
- package/hooks/pre-bash-issue-budget.mjs +17 -18
- package/hooks/pre-task-scope-disjoint.mjs +1042 -0
- package/package.json +3 -1
- package/pi/prompts/eli5.md +12 -0
- package/pi/prompts/release.md +12 -0
- package/scripts/auq-audit.mjs +825 -0
- package/scripts/autopilot.mjs +10 -9
- package/scripts/emit-session.mjs +42 -0
- package/scripts/export-hw-learnings.mjs +61 -2
- package/scripts/lib/auq/clarity.mjs +1314 -0
- package/scripts/lib/auq/parse.mjs +1006 -0
- package/scripts/lib/auq/schema.mjs +1457 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/backlog-scan.mjs +106 -15
- package/scripts/lib/build-live-signals.mjs +7 -3
- package/scripts/lib/ci-status-banner.mjs +267 -77
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +32 -9
- package/scripts/lib/config/vault-integration.mjs +12 -1
- package/scripts/lib/dispatcher/rank.mjs +4 -7
- package/scripts/lib/gates/gate-full.mjs +3 -3
- package/scripts/lib/gates/gate-helpers.mjs +17 -6
- package/scripts/lib/git-config-drift.mjs +471 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +65 -12
- package/scripts/lib/io.mjs +432 -7
- package/scripts/lib/issue-budget.mjs +63 -9
- package/scripts/lib/learnings/select.mjs +157 -3
- package/scripts/lib/memory-cleanup-stamp.mjs +132 -8
- package/scripts/lib/mirror-issues-banner.mjs +266 -0
- package/scripts/lib/named-vault-resolver.mjs +105 -16
- package/scripts/lib/owner-interview.mjs +78 -32
- package/scripts/lib/peer-cards/schema.mjs +6 -2
- package/scripts/lib/peer-discovery.mjs +73 -22
- package/scripts/lib/project-hygiene.mjs +64 -4
- package/scripts/lib/reconcile/renderer.mjs +17 -4
- package/scripts/lib/reconcile/writer.mjs +69 -30
- package/scripts/lib/redact-spans.mjs +89 -0
- package/scripts/lib/resource-probe/evaluate.mjs +330 -149
- package/scripts/lib/resource-probe/probe-platform.mjs +35 -0
- package/scripts/lib/resource-probe.mjs +18 -2
- package/scripts/lib/scope-baseline.mjs +77 -17
- package/scripts/lib/scope-gate.mjs +658 -0
- package/scripts/lib/secret-masker.mjs +262 -0
- package/scripts/lib/session-lock.mjs +34 -10
- package/scripts/lib/session-registry.mjs +9 -1
- package/scripts/lib/spiral-carryover.mjs +23 -2
- package/scripts/lib/state-md/mission-status.mjs +164 -58
- package/scripts/lib/tmux-layout/vcs-detector.mjs +108 -4
- package/scripts/lib/validate/check-agents.mjs +77 -5
- package/scripts/lib/validate/check-auq-clarity.mjs +274 -0
- package/scripts/lib/validate/check-commands.mjs +2 -20
- package/scripts/lib/validate/check-doc-cli-commands.mjs +514 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +48 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +185 -17
- package/scripts/lib/validate/check-rules.mjs +153 -9
- package/scripts/lib/validate/check-skills.mjs +191 -0
- package/scripts/lib/validate/check-test-git-config-target.mjs +665 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +22 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +925 -0
- package/scripts/lib/validate/check-unwired-features.mjs +219 -11
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +965 -0
- package/scripts/lib/validate/frontmatter-block.mjs +61 -0
- package/scripts/lib/validate/tier-inference.mjs +46 -8
- package/scripts/lib/vault-backfill/glab.mjs +91 -58
- package/scripts/lib/vault-backfill/manifest.mjs +28 -8
- package/scripts/lib/vault-mirror/namespace.mjs +146 -1
- package/scripts/lib/vault-mirror/process.mjs +264 -31
- package/scripts/lib/vault-mirror/render-sessions.mjs +115 -4
- package/scripts/lib/vault-status/board-writer.mjs +300 -56
- package/scripts/lib/vault-status/narrative-mirror.mjs +119 -5
- package/scripts/lib/vcs-repo-spec.mjs +680 -30
- package/scripts/lib/wave-resource-gate.mjs +67 -73
- package/scripts/materialize-wave-scope.mjs +281 -0
- package/scripts/print-learnings-index.mjs +30 -3
- package/scripts/release.mjs +983 -107
- package/scripts/run-quality-gate.mjs +14 -0
- package/scripts/site-numbers.mjs +1049 -0
- package/scripts/validate-plugin.mjs +64 -0
- package/scripts/validate-wave-scope.mjs +286 -12
- package/scripts/vault-backfill.mjs +32 -5
- package/scripts/vault-mirror.mjs +26 -1
- package/skills/_shared/monitor-patterns.md +24 -4
- package/skills/_shared/parallel-aware-auq.md +30 -24
- package/skills/_shared/parallel-aware-preamble.md +31 -2
- package/skills/_shared/state-ownership.md +49 -6
- package/skills/bootstrap/SKILL.md +2 -1
- package/skills/brainstorm/SKILL.md +18 -18
- package/skills/brainstorm/soul.md +12 -0
- package/skills/claude-md-drift-check/SKILL.md +9 -1
- package/skills/debug/SKILL.md +4 -1
- package/skills/discovery/SKILL.md +28 -24
- package/skills/discovery/issue-templates.md +4 -4
- package/skills/discovery/probes-code.md +2 -2
- package/skills/discovery/probes-feature.md +6 -6
- package/skills/discovery/probes-infra.md +2 -2
- package/skills/discovery/probes-session.md +5 -5
- package/skills/dispatcher/SKILL.md +10 -1
- package/skills/eli5/SKILL.md +43 -0
- package/skills/evolve/SKILL.md +8 -9
- package/skills/frontmatter-guard/SKILL.md +9 -1
- package/skills/gitlab-ops/SKILL.md +73 -59
- package/skills/gitlab-portfolio/SKILL.md +10 -1
- package/skills/grill/SKILL.md +6 -6
- package/skills/grill/soul.md +16 -0
- package/skills/memory-cleanup/SKILL.md +20 -7
- package/skills/npm-publish/SKILL.md +23 -51
- package/skills/peekaboo-driver/SKILL.md +3 -3
- package/skills/persona-panel/SKILL.md +3 -1
- package/skills/plan/SKILL.md +18 -16
- package/skills/plan/mode-feature.md +1 -1
- package/skills/plan/mode-new.md +42 -12
- package/skills/plan/soul.md +12 -0
- package/skills/reconcile/SKILL.md +3 -3
- package/skills/repo-audit/SKILL.md +10 -1
- package/skills/session-end/SKILL.md +97 -22
- package/skills/session-end/metrics-collection.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +37 -2
- package/skills/session-end/session-metrics-write.md +4 -10
- package/skills/session-plan/SKILL.md +2 -2
- package/skills/session-plan/wave-template.md +1 -1
- package/skills/session-start/SKILL.md +82 -36
- package/skills/session-start/phase-2-5-docs-planning.md +8 -8
- package/skills/session-start/phase-4-5-resource-health.md +82 -19
- package/skills/session-start/soul.md +110 -0
- package/skills/spinout/SKILL.md +5 -1
- package/skills/sunset-review/SKILL.md +11 -1
- package/skills/test-runner/SKILL.md +2 -2
- package/skills/tmux-layout/SKILL.md +7 -2
- package/skills/using-orchestrator/SKILL.md +1 -1
- package/skills/vault-mirror/SKILL.md +10 -1
- package/skills/vault-sync/SKILL.md +10 -1
- package/skills/vault-sync/validator.mjs +55 -6
- package/skills/wave-executor/wave-loop.md +64 -12
- package/skills/write-executable-plan/SKILL.md +6 -6
- package/scripts/lib/mission-status-schema.mjs +0 -114
- package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +0 -8
- package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +0 -8
- package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +0 -8
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +0 -8
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +0 -10
- package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/README.md +0 -3
- package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +0 -11
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +0 -9
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +0 -8
- package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +0 -7
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +0 -9
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +0 -11
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +0 -3
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +0 -3
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +0 -11
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +0 -11
- package/skills/vault-sync/tests/schema-drift.test.mjs +0 -133
package/scripts/autopilot.mjs
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
import { buildLiveSignals } from './lib/build-live-signals.mjs';
|
|
45
45
|
import { surfaceTopN, decayOptsFromConfig } from './lib/learnings/surface.mjs';
|
|
46
46
|
import { selectMode } from './lib/mode-selector.mjs';
|
|
47
|
-
import { probe, evaluate } from './lib/resource-probe.mjs';
|
|
47
|
+
import { probe, evaluate, DEFAULT_RESOURCE_THRESHOLDS } from './lib/resource-probe.mjs';
|
|
48
48
|
import { detectPeers } from './lib/session-registry.mjs';
|
|
49
49
|
import { normalizeSession } from './lib/session-schema.mjs';
|
|
50
50
|
|
|
@@ -112,13 +112,12 @@ const config = loadConfig();
|
|
|
112
112
|
* @returns {object} thresholds object consumable by evaluate()
|
|
113
113
|
*/
|
|
114
114
|
function loadResourceThresholds() {
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
};
|
|
115
|
+
// #1089: this block used to hold its OWN four numbers (2.0 / 0.5 / 85 / 3)
|
|
116
|
+
// under a comment claiming it "mirrors resource-probe hard-coded values" —
|
|
117
|
+
// which it did not, and had not for some time. Autopilot therefore judged
|
|
118
|
+
// every host against a threshold set no other consumer used, and the comment
|
|
119
|
+
// is what made that invisible. Now sourced from the one canonical definition.
|
|
120
|
+
const DEFAULTS = { ...DEFAULT_RESOURCE_THRESHOLDS };
|
|
122
121
|
|
|
123
122
|
const rt = config && config['resource-thresholds'];
|
|
124
123
|
if (rt && typeof rt === 'object') {
|
|
@@ -228,9 +227,11 @@ let cachedProbeSnapshot = null;
|
|
|
228
227
|
* @returns {Promise<{mode: string, confidence: number, rationale?: string, alternatives?: object[]}>}
|
|
229
228
|
*/
|
|
230
229
|
async function modeSelector() {
|
|
230
|
+
// `backlogLimit` is intentionally NOT passed: the window default lives once,
|
|
231
|
+
// in backlog-scan.mjs (`DEFAULT_BACKLOG_LIMIT`). The hand-written 50 that
|
|
232
|
+
// stood here under-read this repo's own backlog and hid its critical issues.
|
|
231
233
|
const signals = await buildLiveSignals({
|
|
232
234
|
learnings: surfacedLearnings,
|
|
233
|
-
backlogLimit: 50,
|
|
234
235
|
});
|
|
235
236
|
return selectMode(signals);
|
|
236
237
|
}
|
package/scripts/emit-session.mjs
CHANGED
|
@@ -25,8 +25,14 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import { readFileSync } from 'node:fs';
|
|
28
|
+
import { dirname, join } from 'node:path';
|
|
28
29
|
import { fileURLToPath } from 'node:url';
|
|
29
30
|
import { appendJsonl } from './lib/common.mjs';
|
|
31
|
+
import {
|
|
32
|
+
MEMORY_CLEANUP_EVENT,
|
|
33
|
+
deriveMemoryCleanupSignal,
|
|
34
|
+
stampMemoryCleanup,
|
|
35
|
+
} from './lib/memory-cleanup-stamp.mjs';
|
|
30
36
|
import { serializeSessionLineChecked } from './lib/session-schema/serializer.mjs';
|
|
31
37
|
import {
|
|
32
38
|
validateSession,
|
|
@@ -111,6 +117,42 @@ async function main() {
|
|
|
111
117
|
);
|
|
112
118
|
}
|
|
113
119
|
|
|
120
|
+
// `memory_cleanup_at` derivation (#699 follow-up — Disziplin statt Mechanik).
|
|
121
|
+
// The flag used to be a boolean the coordinator-LLM remembered to pass at
|
|
122
|
+
// session-end; it was forgotten on 2026-08-14 and the cadence marker stalled
|
|
123
|
+
// 29 days behind the operator's own notes. It is now READ from the session's
|
|
124
|
+
// own `orchestrator.memory.cleanup_completed` events, sitting in the sibling
|
|
125
|
+
// events.jsonl of the target sessions.jsonl (same `.orchestrator/metrics/`
|
|
126
|
+
// directory in production, same tmp dir under test — no env plumbing).
|
|
127
|
+
//
|
|
128
|
+
// Precedence: an EXPLICIT `memory_cleanup_at` on the incoming record WINS and
|
|
129
|
+
// is never overwritten — an explicit stamp is a caller's positive assertion,
|
|
130
|
+
// while derivation only fills the gap left by silence.
|
|
131
|
+
const alreadyStamped =
|
|
132
|
+
typeof repaired.memory_cleanup_at === 'string' && repaired.memory_cleanup_at.length > 0;
|
|
133
|
+
if (!alreadyStamped) {
|
|
134
|
+
const eventsFile = join(dirname(args.file), 'events.jsonl');
|
|
135
|
+
const signal = deriveMemoryCleanupSignal({
|
|
136
|
+
eventsFile,
|
|
137
|
+
sessionId: repaired.session_id,
|
|
138
|
+
startedAt: repaired.started_at,
|
|
139
|
+
completedAt: repaired.completed_at,
|
|
140
|
+
});
|
|
141
|
+
if (signal.ranCleanup) {
|
|
142
|
+
const before = repaired;
|
|
143
|
+
repaired = stampMemoryCleanup(repaired, {
|
|
144
|
+
ranCleanup: true,
|
|
145
|
+
completedAt: repaired.completed_at,
|
|
146
|
+
});
|
|
147
|
+
if (repaired !== before) {
|
|
148
|
+
process.stderr.write(
|
|
149
|
+
`emit-session: derived memory_cleanup_at=${repaired.memory_cleanup_at} from ` +
|
|
150
|
+
`${signal.matches} ${MEMORY_CLEANUP_EVENT} event(s) (latest ${signal.at})\n`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
114
156
|
let validated;
|
|
115
157
|
try {
|
|
116
158
|
validated = validateSession(repaired);
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
*
|
|
23
23
|
* ## Anonymization pipeline (enforced; no opt-out)
|
|
24
24
|
*
|
|
25
|
+
* - Mask env-derived secret VALUES first (#1025, value-based — orthogonal to the
|
|
26
|
+
* form-based rules below; see the note above `maskSecretValues`)
|
|
25
27
|
* - Strip all absolute paths (macOS, Linux system paths, Windows)
|
|
26
28
|
* - Redact IPv4 addresses
|
|
27
29
|
* - Redact GitHub/GitLab URLs containing org/repo paths
|
|
@@ -55,6 +57,7 @@ import {
|
|
|
55
57
|
} from './lib/learnings.mjs';
|
|
56
58
|
import { findProjectRoot, resolveInstructionFile, expandTilde } from './lib/common.mjs';
|
|
57
59
|
import { parseSessionConfig } from './lib/config.mjs';
|
|
60
|
+
import { createSecretValueMasker } from './lib/secret-masker.mjs';
|
|
58
61
|
|
|
59
62
|
// Vault-relative default write target (Epic #774 — docs Public-Split removed
|
|
60
63
|
// the prior in-repo generated telemetry doc in favor of the private Meta-Vault).
|
|
@@ -135,6 +138,58 @@ const SIGNED_OFF_RE = /Signed-off-by:[^\n]+/g;
|
|
|
135
138
|
// those never appear with a .local/.lan/.home suffix.
|
|
136
139
|
const HOSTNAME_RE = /\b[A-Za-z0-9]+(?:[-.][A-Za-z0-9]+)*\.(?:local|lan|home|internal|corp)\b/g;
|
|
137
140
|
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Value-based secret masking (#1025) — the SECOND half of the pipeline
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
//
|
|
145
|
+
// `anonymizeString` below is FORM-based: eight regexes over the SHAPE of a
|
|
146
|
+
// string. `createSecretValueMasker` is VALUE-based: it looks for the literal
|
|
147
|
+
// values of secret-NAMED env vars. The two are orthogonal and BOTH are needed —
|
|
148
|
+
// measured on four probe secrets:
|
|
149
|
+
//
|
|
150
|
+
// AWS secret key (with slashes) FORM: secret survives VALUE: [REDACTED]
|
|
151
|
+
// DB password (short, symbols) FORM: secret survives VALUE: [REDACTED]
|
|
152
|
+
// GitLab PAT (token shape) FORM: <redacted-token> VALUE: [REDACTED]
|
|
153
|
+
// all-letter passphrase FORM: secret survives VALUE: [REDACTED]
|
|
154
|
+
//
|
|
155
|
+
// The AWS key is the sharpest case: `/` is outside TOKEN_RE's character class,
|
|
156
|
+
// so the key breaks into runs of 13/7/18 characters — every one of them BELOW
|
|
157
|
+
// the 20-character floor, hence not a single match. Conversely the form catches
|
|
158
|
+
// tokens of FOREIGN hosts that were never in `process.env`, which the value
|
|
159
|
+
// masker cannot see. Neither subsumes the other.
|
|
160
|
+
//
|
|
161
|
+
// ORDER IS LOAD-BEARING: mask FIRST, anonymize SECOND. `[REDACTED]` is 8
|
|
162
|
+
// characters with no digit, so it passes through `anonymizeString` untouched
|
|
163
|
+
// (TOKEN_RE requires ≥20 chars AND a digit). The reverse order blinds the
|
|
164
|
+
// masker: a value that `anonymizeString` has already rewritten to
|
|
165
|
+
// `<redacted-token>` is no longer findable as a literal, so a secret that only
|
|
166
|
+
// the VALUE filter would catch — because the form leaves a residue rather than
|
|
167
|
+
// the whole span — can no longer be matched.
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Lazily-built, process-wide masker. `createSecretValueMasker` scans the whole
|
|
171
|
+
* env and compiles one RegExp per needle, so it is built ONCE per process and
|
|
172
|
+
* reused for every entry — never rebuilt per record.
|
|
173
|
+
*
|
|
174
|
+
* @type {{ mask: (text: string) => string, needleCount: number } | null}
|
|
175
|
+
*/
|
|
176
|
+
let _secretMasker = null;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Mask env-derived secret VALUES in a free-form string.
|
|
180
|
+
*
|
|
181
|
+
* Fail-soft: non-strings pass through by reference, and a zero-needle env makes
|
|
182
|
+
* `mask` the identity — the masker must never be the reason an export dies.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} s
|
|
185
|
+
* @returns {string}
|
|
186
|
+
*/
|
|
187
|
+
function maskSecretValues(s) {
|
|
188
|
+
if (typeof s !== 'string') return s;
|
|
189
|
+
if (_secretMasker === null) _secretMasker = createSecretValueMasker(process.env);
|
|
190
|
+
return _secretMasker.mask(s);
|
|
191
|
+
}
|
|
192
|
+
|
|
138
193
|
/**
|
|
139
194
|
* Scrub a free-form string of PII / host-identifying content.
|
|
140
195
|
* Order matters: author/signoff patterns first (they contain emails), then
|
|
@@ -185,8 +240,12 @@ export function anonymizeLearning(entry) {
|
|
|
185
240
|
const e = normalizeLearning(entry);
|
|
186
241
|
const out = {
|
|
187
242
|
...e,
|
|
188
|
-
|
|
189
|
-
|
|
243
|
+
// #1025: value-mask BEFORE form-anonymize — see the ORDER IS LOAD-BEARING
|
|
244
|
+
// note above `maskSecretValues`. This is the choke-point for BOTH write
|
|
245
|
+
// paths (promoteHwLearnings and exportHwLearnings both route through here);
|
|
246
|
+
// wiring at exportHwLearnings alone would leave the promote path unhardened.
|
|
247
|
+
insight: anonymizeString(maskSecretValues(e.insight)),
|
|
248
|
+
evidence: anonymizeString(maskSecretValues(e.evidence)),
|
|
190
249
|
// Stamp after redaction so callers that write back through validateLearning
|
|
191
250
|
// do not hit the scope=public contract check.
|
|
192
251
|
anonymized: true,
|