Skip to content

Commit 70077a4

Browse files
committed
fix(dsh): the gate now says when it judged a capped payload
Both gate decisions carry truncated and the egress facts, and this file forwarded only reason and feedback -- so a gate that had judged a fragment presented its decision exactly like one that had judged all of it. That is the defect the core's renderResult had, in the one place where the audience is a person deciding whether to approve something rather than a model deciding what to do next.
1 parent bb52aff commit 70077a4

2 files changed

Lines changed: 57 additions & 3 deletions

File tree

‎packages/dsh/src/index.ts‎

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,32 @@ export const CONFIG_DOC = {
108108
type Level = 'silent' | 'warn' | 'info' | 'debug'
109109
const ORDER: Record<Level, number> = { silent: 0, warn: 1, info: 2, debug: 3 }
110110

111+
/**
112+
* Add what a human needs when the gate judged a fragment rather than the whole.
113+
*
114+
* Both gate decisions carry `truncated` and the egress facts, and this file
115+
* forwarded only `reason` — so a gate that had judged a capped payload presented
116+
* its decision exactly like one that had judged all of it. That is the defect the
117+
* core's `renderResult` had, in the one place where the audience is a person
118+
* deciding whether to approve something rather than a model deciding what to do
119+
* next.
120+
*
121+
* Only an explicit `true` adds the sentence. `undefined` means the payload was
122+
* judged whole, which is the ordinary case, and a caveat attached to every
123+
* decision is a caveat nobody reads.
124+
*/
125+
export const withTruncationCaveat = (
126+
text: string | undefined,
127+
decision: { readonly truncated?: boolean },
128+
): string | undefined => {
129+
if (decision.truncated !== true) return text
130+
const base = text ?? 'the jevcore gate did not explain its decision'
131+
return (
132+
`${base} (What was judged had been capped first, so this is a judgment about the part that ` +
133+
'survived the cap rather than about all of it.)'
134+
)
135+
}
136+
111137
interface Logger {
112138
warn: (message: string) => void
113139
info: (message: string) => void
@@ -421,10 +447,11 @@ export function apply(ctx: Context, input?: JevConfigInput): void {
421447
...(call.signal === undefined ? {} : { signal: call.signal }),
422448
})
423449
if (decision.kind === 'allow') return (next as () => Promise<unknown>)()
450+
const reason = withTruncationCaveat(decision.reason, decision)
424451
if (decision.kind === 'deny') {
425-
return { kind: 'deny', reason: decision.reason ?? 'denied by the jevcore safety gate' }
452+
return { kind: 'deny', reason: reason ?? 'denied by the jevcore safety gate' }
426453
}
427-
return { kind: 'ask', ...(decision.reason === undefined ? {} : { reason: decision.reason }) }
454+
return { kind: 'ask', ...(reason === undefined ? {} : { reason }) }
428455
}),
429456
)
430457
}
@@ -450,7 +477,12 @@ export function apply(ctx: Context, input?: JevConfigInput): void {
450477
if (!decision.block) return (next as () => Promise<unknown>)()
451478
return {
452479
kind: 'block',
453-
feedback: [{ type: 'text', text: decision.feedback ?? 'result withheld by jevcore' }],
480+
feedback: [
481+
{
482+
type: 'text',
483+
text: withTruncationCaveat(decision.feedback, decision) ?? 'result withheld by jevcore',
484+
},
485+
],
454486
}
455487
}),
456488
)

‎packages/dsh/test/plugin.test.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,3 +536,25 @@ describe('the recent() window', () => {
536536
expect(at).toEqual([...at].sort((left, right) => left - right))
537537
})
538538
})
539+
540+
describe('the truncation caveat', () => {
541+
// `GateDecision` carries `truncated`, and the plugin forwarded only `reason`.
542+
// So a gate that had judged a capped payload presented its decision exactly
543+
// like one that had judged all of it — the same defect the core's
544+
// `renderResult` had, in the one place where the audience is a person deciding
545+
// whether to approve something.
546+
it('is added only when the payload really was capped', () => {
547+
expect(plugin.withTruncationCaveat('flagged x', {})).toBe('flagged x')
548+
expect(plugin.withTruncationCaveat('flagged x', { truncated: false })).toBe('flagged x')
549+
expect(plugin.withTruncationCaveat('flagged x', { truncated: true })).toContain('capped')
550+
})
551+
552+
it('says something even when the gate gave no reason of its own', () => {
553+
// A caveat that silently vanishes when the text it would attach to is missing
554+
// is the failure this is meant to prevent, so absence gets a sentence too.
555+
const text = plugin.withTruncationCaveat(undefined, { truncated: true })
556+
expect(text).toBeDefined()
557+
expect(text).toContain('capped')
558+
expect(plugin.withTruncationCaveat(undefined, {})).toBeUndefined()
559+
})
560+
})

0 commit comments

Comments
 (0)