fix(invariants): harden runtime contracts and gates
This commit is contained in:
@@ -40,7 +40,7 @@ The current executable companions protect these relationships:
|
||||
| `dsh-fs`, `dsh-subagent`, `dsh-workflow` | Filesystem event identity, provider/child pairing, and workflow/agent lifecycle identity. |
|
||||
| `dsh-permission`, `dsh-user-approval` | Active-preset references and approval asked/decided audit pairing. |
|
||||
| `dsh-tasks`, `dsh-tool-todo` | Task snapshot lifecycle/ownership fields and durable whole-list todo structure. |
|
||||
| `dsh-time-context` | Durable clock readings agree with the session's open turn and next pre-step position, elapsed baseline, and event timestamp. |
|
||||
| `dsh-time-context` | Durable clock readings agree with the session's open turn and next pre-step position and elapsed baseline; rendered time parses and does not postdate its event. |
|
||||
|
||||
The root entrypoint of each owner remains independent of diagnostics. Loading the service alone installs no product checks, and loading a companion without the service waits on its declared `invariants` injection.
|
||||
|
||||
@@ -77,6 +77,6 @@ None; invariant checks do not assemble or send provider requests.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- Request reconstruction covers frozen loop-built requests with a live session id; direct one-shot LLM calls remain outside that marker contract.
|
||||
- Request reconstruction covers requests explicitly marked by the loop before freezing; direct one-shot LLM calls remain outside that marker contract even when callers freeze them or attach a session id.
|
||||
- Live-only lifecycle companions cannot reconstruct operations that began before their own reload. Standard and test compositions mount them before the corresponding operations begin.
|
||||
- Regular-expression filters are fixed for the service lifetime; changing them requires ordinary Cordis plugin reload.
|
||||
|
||||
@@ -162,27 +162,28 @@ export class InvariantService extends Service {
|
||||
throw new InvariantError(packageName, message)
|
||||
})
|
||||
)
|
||||
const child = ctx.plugin(installer.inject === undefined
|
||||
? installInvariant
|
||||
: Object.assign(installInvariant, { inject: installer.inject }))
|
||||
|
||||
try {
|
||||
await child
|
||||
} catch (error) {
|
||||
try {
|
||||
await child.dispose()
|
||||
} finally {
|
||||
registrations.delete(packageName)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
const child = ctx.plugin(installer.inject === undefined
|
||||
? installInvariant
|
||||
: Object.assign(installInvariant, { inject: installer.inject }))
|
||||
|
||||
return async () => {
|
||||
try {
|
||||
await child
|
||||
} catch (error) {
|
||||
await child.dispose()
|
||||
} finally {
|
||||
registrations.delete(packageName)
|
||||
throw error
|
||||
}
|
||||
|
||||
return async () => {
|
||||
try {
|
||||
await child.dispose()
|
||||
} finally {
|
||||
registrations.delete(packageName)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
registrations.delete(packageName)
|
||||
throw error
|
||||
}
|
||||
}, `invariants.register(${JSON.stringify(packageName)})`)
|
||||
} catch (error) {
|
||||
|
||||
@@ -260,6 +260,28 @@ describe('InvariantService lifecycle', () => {
|
||||
expect(retry).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('rolls back publication effects and ownership when child-fiber publication fails', async () => {
|
||||
const { ctx } = await setup()
|
||||
const leaked = vi.fn()
|
||||
let rejectPublication = true
|
||||
const stopRejecting = ctx.on('internal/plugin', (fiber) => {
|
||||
if (!rejectPublication || fiber.uid === null) return
|
||||
rejectPublication = false
|
||||
fiber.ctx.on('invariants-test/ping', leaked, { global: true })
|
||||
throw new Error('publication failed')
|
||||
})
|
||||
|
||||
const failed = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-publication-probe', () => {}))
|
||||
await expect(Promise.resolve(failed)).rejects.toThrow('publication failed')
|
||||
ctx.emit('invariants-test/ping')
|
||||
expect(leaked).not.toHaveBeenCalled()
|
||||
stopRejecting()
|
||||
|
||||
const retry = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-publication-probe', () => {}))
|
||||
await retry
|
||||
await retry()
|
||||
})
|
||||
|
||||
it('joins asynchronous checks and rolls back their effects on failure', async () => {
|
||||
const { ctx } = await setup()
|
||||
const leaked = vi.fn()
|
||||
|
||||
Reference in New Issue
Block a user