@codyswann/lisa 3.11.2 → 3.11.4
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/dist/core/upstream-evidence-manifest.js +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/scripts/automation-run-record.mjs +37 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/automation-run-record.mjs +37 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/scripts/automation-run-record.mjs +37 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/scripts/automation-run-record.mjs +37 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/scripts/automation-run-record.mjs +37 -0
|
@@ -451,7 +451,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
451
451
|
"plugins/src/base/rules/reference/verification.md": "9ae8db87b37c7700687fabd1b8632b3cddd0f1f1e4b04384c604f88f160056ed",
|
|
452
452
|
"plugins/src/base/rules/reference/wiki-knowledge-source.md": "725a7415a0606f85164722bd4b4975bce1f496cc43c97e45b224810b1a685189",
|
|
453
453
|
"plugins/src/base/rules/reference/work-item-definition-of-ready.md": "ebcfe89f5a33a8008c4a54c1c2f4a092952df3263adc8f537a4c0b6ad8dbe18d",
|
|
454
|
-
"plugins/src/base/scripts/automation-run-record.mjs": "
|
|
454
|
+
"plugins/src/base/scripts/automation-run-record.mjs": "a499794a20f82a1e311ef4cb5188dd890f25b970e19e788d6945f2d5898dc9d2",
|
|
455
455
|
"plugins/src/base/scripts/automation-status-claude-adapter.mjs": "b1054acf4a5cbbe5354bd62a22b96533bcbc69b6cb9245976ceb8af638a04494",
|
|
456
456
|
"plugins/src/base/scripts/automation-status-codex-adapter.mjs": "ba1d8891f3369865d307e00699d5817172f64947d5b0c4e569e2f8e5bb46a9f5",
|
|
457
457
|
"plugins/src/base/scripts/automation-status-contract-drift.mjs": "5f3f327fddedd6870017a86f2702d9cbb17352384fb225ab353eed54241182ef",
|
package/package.json
CHANGED
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
}
|
|
123
123
|
},
|
|
124
124
|
"name": "@codyswann/lisa",
|
|
125
|
-
"version": "3.11.
|
|
125
|
+
"version": "3.11.4",
|
|
126
126
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
127
127
|
"main": "dist/index.js",
|
|
128
128
|
"exports": {
|
|
@@ -41,6 +41,7 @@ export const DEFAULT_AUTOMATION_RUN_HISTORY_MAX_ENTRIES = 50;
|
|
|
41
41
|
* readonly runId?: string
|
|
42
42
|
* readonly ts?: string | Date
|
|
43
43
|
* readonly maxEntries?: number
|
|
44
|
+
* readonly extras?: Readonly<Record<string, unknown>>
|
|
44
45
|
* }} RecordAutomationRunInput
|
|
45
46
|
*/
|
|
46
47
|
|
|
@@ -188,7 +189,26 @@ function buildAutomationRunRecord(input) {
|
|
|
188
189
|
throw new Error("Automation run_id is required.");
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
// Unknown keys are preserved rather than dropped. `recordAutomationRun`
|
|
193
|
+
// rewrites the WHOLE history file on every append, and stored records are
|
|
194
|
+
// re-validated through this function on read — so dropping an unrecognised
|
|
195
|
+
// key here does not merely omit it from the new row, it erases it from every
|
|
196
|
+
// row that already had it. The loss is invisible: the write succeeds, the
|
|
197
|
+
// file stays valid JSONL, and the reader returns records that look complete.
|
|
198
|
+
// Consumers get a false all-clear instead of an error (#2524).
|
|
199
|
+
//
|
|
200
|
+
// Validated fields are spread LAST so a corrupt or hostile stored row cannot
|
|
201
|
+
// override `outcome`, `run_id`, or any other checked value with an extra key
|
|
202
|
+
// of the same name.
|
|
203
|
+
const extras =
|
|
204
|
+
input.extras &&
|
|
205
|
+
typeof input.extras === "object" &&
|
|
206
|
+
!Array.isArray(input.extras)
|
|
207
|
+
? input.extras
|
|
208
|
+
: {};
|
|
209
|
+
|
|
191
210
|
return {
|
|
211
|
+
...extras,
|
|
192
212
|
ts,
|
|
193
213
|
loop_id: loopId,
|
|
194
214
|
outcome: input.outcome,
|
|
@@ -199,6 +219,17 @@ function buildAutomationRunRecord(input) {
|
|
|
199
219
|
};
|
|
200
220
|
}
|
|
201
221
|
|
|
222
|
+
/** Keys this module validates explicitly; everything else is passed through. */
|
|
223
|
+
const KNOWN_RECORD_KEYS = new Set([
|
|
224
|
+
"ts",
|
|
225
|
+
"loop_id",
|
|
226
|
+
"outcome",
|
|
227
|
+
"summary",
|
|
228
|
+
"runbook",
|
|
229
|
+
"refs",
|
|
230
|
+
"run_id",
|
|
231
|
+
]);
|
|
232
|
+
|
|
202
233
|
/**
|
|
203
234
|
* @param {unknown} value
|
|
204
235
|
* @returns {AutomationRunRecord}
|
|
@@ -215,6 +246,12 @@ function validateStoredRecord(value) {
|
|
|
215
246
|
runbook: String(value.runbook ?? ""),
|
|
216
247
|
refs: Array.isArray(value.refs) ? value.refs.map(ref => String(ref)) : [],
|
|
217
248
|
runId: String(value.run_id ?? ""),
|
|
249
|
+
// Carry every unrecognised key forward. Without this, re-validating a
|
|
250
|
+
// stored record on read silently strips it, and the next append writes the
|
|
251
|
+
// stripped history back over the file (#2524).
|
|
252
|
+
extras: Object.fromEntries(
|
|
253
|
+
Object.entries(value).filter(([key]) => !KNOWN_RECORD_KEYS.has(key))
|
|
254
|
+
),
|
|
218
255
|
});
|
|
219
256
|
}
|
|
220
257
|
|
|
@@ -41,6 +41,7 @@ export const DEFAULT_AUTOMATION_RUN_HISTORY_MAX_ENTRIES = 50;
|
|
|
41
41
|
* readonly runId?: string
|
|
42
42
|
* readonly ts?: string | Date
|
|
43
43
|
* readonly maxEntries?: number
|
|
44
|
+
* readonly extras?: Readonly<Record<string, unknown>>
|
|
44
45
|
* }} RecordAutomationRunInput
|
|
45
46
|
*/
|
|
46
47
|
|
|
@@ -188,7 +189,26 @@ function buildAutomationRunRecord(input) {
|
|
|
188
189
|
throw new Error("Automation run_id is required.");
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
// Unknown keys are preserved rather than dropped. `recordAutomationRun`
|
|
193
|
+
// rewrites the WHOLE history file on every append, and stored records are
|
|
194
|
+
// re-validated through this function on read — so dropping an unrecognised
|
|
195
|
+
// key here does not merely omit it from the new row, it erases it from every
|
|
196
|
+
// row that already had it. The loss is invisible: the write succeeds, the
|
|
197
|
+
// file stays valid JSONL, and the reader returns records that look complete.
|
|
198
|
+
// Consumers get a false all-clear instead of an error (#2524).
|
|
199
|
+
//
|
|
200
|
+
// Validated fields are spread LAST so a corrupt or hostile stored row cannot
|
|
201
|
+
// override `outcome`, `run_id`, or any other checked value with an extra key
|
|
202
|
+
// of the same name.
|
|
203
|
+
const extras =
|
|
204
|
+
input.extras &&
|
|
205
|
+
typeof input.extras === "object" &&
|
|
206
|
+
!Array.isArray(input.extras)
|
|
207
|
+
? input.extras
|
|
208
|
+
: {};
|
|
209
|
+
|
|
191
210
|
return {
|
|
211
|
+
...extras,
|
|
192
212
|
ts,
|
|
193
213
|
loop_id: loopId,
|
|
194
214
|
outcome: input.outcome,
|
|
@@ -199,6 +219,17 @@ function buildAutomationRunRecord(input) {
|
|
|
199
219
|
};
|
|
200
220
|
}
|
|
201
221
|
|
|
222
|
+
/** Keys this module validates explicitly; everything else is passed through. */
|
|
223
|
+
const KNOWN_RECORD_KEYS = new Set([
|
|
224
|
+
"ts",
|
|
225
|
+
"loop_id",
|
|
226
|
+
"outcome",
|
|
227
|
+
"summary",
|
|
228
|
+
"runbook",
|
|
229
|
+
"refs",
|
|
230
|
+
"run_id",
|
|
231
|
+
]);
|
|
232
|
+
|
|
202
233
|
/**
|
|
203
234
|
* @param {unknown} value
|
|
204
235
|
* @returns {AutomationRunRecord}
|
|
@@ -215,6 +246,12 @@ function validateStoredRecord(value) {
|
|
|
215
246
|
runbook: String(value.runbook ?? ""),
|
|
216
247
|
refs: Array.isArray(value.refs) ? value.refs.map(ref => String(ref)) : [],
|
|
217
248
|
runId: String(value.run_id ?? ""),
|
|
249
|
+
// Carry every unrecognised key forward. Without this, re-validating a
|
|
250
|
+
// stored record on read silently strips it, and the next append writes the
|
|
251
|
+
// stripped history back over the file (#2524).
|
|
252
|
+
extras: Object.fromEntries(
|
|
253
|
+
Object.entries(value).filter(([key]) => !KNOWN_RECORD_KEYS.has(key))
|
|
254
|
+
),
|
|
218
255
|
});
|
|
219
256
|
}
|
|
220
257
|
|
|
@@ -41,6 +41,7 @@ export const DEFAULT_AUTOMATION_RUN_HISTORY_MAX_ENTRIES = 50;
|
|
|
41
41
|
* readonly runId?: string
|
|
42
42
|
* readonly ts?: string | Date
|
|
43
43
|
* readonly maxEntries?: number
|
|
44
|
+
* readonly extras?: Readonly<Record<string, unknown>>
|
|
44
45
|
* }} RecordAutomationRunInput
|
|
45
46
|
*/
|
|
46
47
|
|
|
@@ -188,7 +189,26 @@ function buildAutomationRunRecord(input) {
|
|
|
188
189
|
throw new Error("Automation run_id is required.");
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
// Unknown keys are preserved rather than dropped. `recordAutomationRun`
|
|
193
|
+
// rewrites the WHOLE history file on every append, and stored records are
|
|
194
|
+
// re-validated through this function on read — so dropping an unrecognised
|
|
195
|
+
// key here does not merely omit it from the new row, it erases it from every
|
|
196
|
+
// row that already had it. The loss is invisible: the write succeeds, the
|
|
197
|
+
// file stays valid JSONL, and the reader returns records that look complete.
|
|
198
|
+
// Consumers get a false all-clear instead of an error (#2524).
|
|
199
|
+
//
|
|
200
|
+
// Validated fields are spread LAST so a corrupt or hostile stored row cannot
|
|
201
|
+
// override `outcome`, `run_id`, or any other checked value with an extra key
|
|
202
|
+
// of the same name.
|
|
203
|
+
const extras =
|
|
204
|
+
input.extras &&
|
|
205
|
+
typeof input.extras === "object" &&
|
|
206
|
+
!Array.isArray(input.extras)
|
|
207
|
+
? input.extras
|
|
208
|
+
: {};
|
|
209
|
+
|
|
191
210
|
return {
|
|
211
|
+
...extras,
|
|
192
212
|
ts,
|
|
193
213
|
loop_id: loopId,
|
|
194
214
|
outcome: input.outcome,
|
|
@@ -199,6 +219,17 @@ function buildAutomationRunRecord(input) {
|
|
|
199
219
|
};
|
|
200
220
|
}
|
|
201
221
|
|
|
222
|
+
/** Keys this module validates explicitly; everything else is passed through. */
|
|
223
|
+
const KNOWN_RECORD_KEYS = new Set([
|
|
224
|
+
"ts",
|
|
225
|
+
"loop_id",
|
|
226
|
+
"outcome",
|
|
227
|
+
"summary",
|
|
228
|
+
"runbook",
|
|
229
|
+
"refs",
|
|
230
|
+
"run_id",
|
|
231
|
+
]);
|
|
232
|
+
|
|
202
233
|
/**
|
|
203
234
|
* @param {unknown} value
|
|
204
235
|
* @returns {AutomationRunRecord}
|
|
@@ -215,6 +246,12 @@ function validateStoredRecord(value) {
|
|
|
215
246
|
runbook: String(value.runbook ?? ""),
|
|
216
247
|
refs: Array.isArray(value.refs) ? value.refs.map(ref => String(ref)) : [],
|
|
217
248
|
runId: String(value.run_id ?? ""),
|
|
249
|
+
// Carry every unrecognised key forward. Without this, re-validating a
|
|
250
|
+
// stored record on read silently strips it, and the next append writes the
|
|
251
|
+
// stripped history back over the file (#2524).
|
|
252
|
+
extras: Object.fromEntries(
|
|
253
|
+
Object.entries(value).filter(([key]) => !KNOWN_RECORD_KEYS.has(key))
|
|
254
|
+
),
|
|
218
255
|
});
|
|
219
256
|
}
|
|
220
257
|
|
|
@@ -41,6 +41,7 @@ export const DEFAULT_AUTOMATION_RUN_HISTORY_MAX_ENTRIES = 50;
|
|
|
41
41
|
* readonly runId?: string
|
|
42
42
|
* readonly ts?: string | Date
|
|
43
43
|
* readonly maxEntries?: number
|
|
44
|
+
* readonly extras?: Readonly<Record<string, unknown>>
|
|
44
45
|
* }} RecordAutomationRunInput
|
|
45
46
|
*/
|
|
46
47
|
|
|
@@ -188,7 +189,26 @@ function buildAutomationRunRecord(input) {
|
|
|
188
189
|
throw new Error("Automation run_id is required.");
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
// Unknown keys are preserved rather than dropped. `recordAutomationRun`
|
|
193
|
+
// rewrites the WHOLE history file on every append, and stored records are
|
|
194
|
+
// re-validated through this function on read — so dropping an unrecognised
|
|
195
|
+
// key here does not merely omit it from the new row, it erases it from every
|
|
196
|
+
// row that already had it. The loss is invisible: the write succeeds, the
|
|
197
|
+
// file stays valid JSONL, and the reader returns records that look complete.
|
|
198
|
+
// Consumers get a false all-clear instead of an error (#2524).
|
|
199
|
+
//
|
|
200
|
+
// Validated fields are spread LAST so a corrupt or hostile stored row cannot
|
|
201
|
+
// override `outcome`, `run_id`, or any other checked value with an extra key
|
|
202
|
+
// of the same name.
|
|
203
|
+
const extras =
|
|
204
|
+
input.extras &&
|
|
205
|
+
typeof input.extras === "object" &&
|
|
206
|
+
!Array.isArray(input.extras)
|
|
207
|
+
? input.extras
|
|
208
|
+
: {};
|
|
209
|
+
|
|
191
210
|
return {
|
|
211
|
+
...extras,
|
|
192
212
|
ts,
|
|
193
213
|
loop_id: loopId,
|
|
194
214
|
outcome: input.outcome,
|
|
@@ -199,6 +219,17 @@ function buildAutomationRunRecord(input) {
|
|
|
199
219
|
};
|
|
200
220
|
}
|
|
201
221
|
|
|
222
|
+
/** Keys this module validates explicitly; everything else is passed through. */
|
|
223
|
+
const KNOWN_RECORD_KEYS = new Set([
|
|
224
|
+
"ts",
|
|
225
|
+
"loop_id",
|
|
226
|
+
"outcome",
|
|
227
|
+
"summary",
|
|
228
|
+
"runbook",
|
|
229
|
+
"refs",
|
|
230
|
+
"run_id",
|
|
231
|
+
]);
|
|
232
|
+
|
|
202
233
|
/**
|
|
203
234
|
* @param {unknown} value
|
|
204
235
|
* @returns {AutomationRunRecord}
|
|
@@ -215,6 +246,12 @@ function validateStoredRecord(value) {
|
|
|
215
246
|
runbook: String(value.runbook ?? ""),
|
|
216
247
|
refs: Array.isArray(value.refs) ? value.refs.map(ref => String(ref)) : [],
|
|
217
248
|
runId: String(value.run_id ?? ""),
|
|
249
|
+
// Carry every unrecognised key forward. Without this, re-validating a
|
|
250
|
+
// stored record on read silently strips it, and the next append writes the
|
|
251
|
+
// stripped history back over the file (#2524).
|
|
252
|
+
extras: Object.fromEntries(
|
|
253
|
+
Object.entries(value).filter(([key]) => !KNOWN_RECORD_KEYS.has(key))
|
|
254
|
+
),
|
|
218
255
|
});
|
|
219
256
|
}
|
|
220
257
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -41,6 +41,7 @@ export const DEFAULT_AUTOMATION_RUN_HISTORY_MAX_ENTRIES = 50;
|
|
|
41
41
|
* readonly runId?: string
|
|
42
42
|
* readonly ts?: string | Date
|
|
43
43
|
* readonly maxEntries?: number
|
|
44
|
+
* readonly extras?: Readonly<Record<string, unknown>>
|
|
44
45
|
* }} RecordAutomationRunInput
|
|
45
46
|
*/
|
|
46
47
|
|
|
@@ -188,7 +189,26 @@ function buildAutomationRunRecord(input) {
|
|
|
188
189
|
throw new Error("Automation run_id is required.");
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
// Unknown keys are preserved rather than dropped. `recordAutomationRun`
|
|
193
|
+
// rewrites the WHOLE history file on every append, and stored records are
|
|
194
|
+
// re-validated through this function on read — so dropping an unrecognised
|
|
195
|
+
// key here does not merely omit it from the new row, it erases it from every
|
|
196
|
+
// row that already had it. The loss is invisible: the write succeeds, the
|
|
197
|
+
// file stays valid JSONL, and the reader returns records that look complete.
|
|
198
|
+
// Consumers get a false all-clear instead of an error (#2524).
|
|
199
|
+
//
|
|
200
|
+
// Validated fields are spread LAST so a corrupt or hostile stored row cannot
|
|
201
|
+
// override `outcome`, `run_id`, or any other checked value with an extra key
|
|
202
|
+
// of the same name.
|
|
203
|
+
const extras =
|
|
204
|
+
input.extras &&
|
|
205
|
+
typeof input.extras === "object" &&
|
|
206
|
+
!Array.isArray(input.extras)
|
|
207
|
+
? input.extras
|
|
208
|
+
: {};
|
|
209
|
+
|
|
191
210
|
return {
|
|
211
|
+
...extras,
|
|
192
212
|
ts,
|
|
193
213
|
loop_id: loopId,
|
|
194
214
|
outcome: input.outcome,
|
|
@@ -199,6 +219,17 @@ function buildAutomationRunRecord(input) {
|
|
|
199
219
|
};
|
|
200
220
|
}
|
|
201
221
|
|
|
222
|
+
/** Keys this module validates explicitly; everything else is passed through. */
|
|
223
|
+
const KNOWN_RECORD_KEYS = new Set([
|
|
224
|
+
"ts",
|
|
225
|
+
"loop_id",
|
|
226
|
+
"outcome",
|
|
227
|
+
"summary",
|
|
228
|
+
"runbook",
|
|
229
|
+
"refs",
|
|
230
|
+
"run_id",
|
|
231
|
+
]);
|
|
232
|
+
|
|
202
233
|
/**
|
|
203
234
|
* @param {unknown} value
|
|
204
235
|
* @returns {AutomationRunRecord}
|
|
@@ -215,6 +246,12 @@ function validateStoredRecord(value) {
|
|
|
215
246
|
runbook: String(value.runbook ?? ""),
|
|
216
247
|
refs: Array.isArray(value.refs) ? value.refs.map(ref => String(ref)) : [],
|
|
217
248
|
runId: String(value.run_id ?? ""),
|
|
249
|
+
// Carry every unrecognised key forward. Without this, re-validating a
|
|
250
|
+
// stored record on read silently strips it, and the next append writes the
|
|
251
|
+
// stripped history back over the file (#2524).
|
|
252
|
+
extras: Object.fromEntries(
|
|
253
|
+
Object.entries(value).filter(([key]) => !KNOWN_RECORD_KEYS.has(key))
|
|
254
|
+
),
|
|
218
255
|
});
|
|
219
256
|
}
|
|
220
257
|
|