Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/zcode-acceptance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ jobs:
node $env:ZCODE_CLI_BIN --version

- name: Run all plugin Node tests including native host discovery
run: node --test integrations/zcode/plugins/powercontext/tests/*.test.mjs
# Process deadline checks must not compete with other test files for runner CPU and disk.
run: node --test --test-concurrency=1 integrations/zcode/plugins/powercontext/tests/*.test.mjs

- name: Run two independent controlled CLI acceptances
run: >-
Expand Down
10 changes: 10 additions & 0 deletions e2e/bub/tests/test_harbor_job_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,16 @@ def _kimi_installation(home: Path) -> dict[str, object]:
for path in sorted((home / "plugins").rglob("*")):
if path.is_file():
text = path.read_text(encoding="utf-8")
if (
path.relative_to(home).as_posix()
== "plugins/managed/powercontext/skills/powercontext-project-context/SKILL.md"
):
# Each installation must point at its own installed script before paths are normalized for comparison.
script = home / "plugins/managed/powercontext/scripts/scope.mjs"
encoded_script = json.dumps(str(script), ensure_ascii=False)
assert script.is_file()
assert encoded_script in text
assert "__POWERCONTEXT_SCOPE_SCRIPT_JSON__" not in text
if path.suffix == ".json":
files[path.relative_to(home).as_posix()] = json.loads(text)
else:
Expand Down
3 changes: 3 additions & 0 deletions integrations/zcode/plugins/powercontext/hooks/stop.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ async function main() {
// A Stop Hook never blocks or continues the model turn.
outputSent = true
await new Promise(resolve => process.stdout.write('{}\n', resolve))
await new Promise(resolve => process.stderr.write('', resolve))
clearTimeout(deadline)
// Completed state and output must not wait for a cancelled transport's remaining handles.
process.exit(0)
}
}
await main()
24 changes: 21 additions & 3 deletions integrations/zcode/plugins/powercontext/tests/boundary.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ test('pending bounds tracking capacity and keeps persisted receipts content-free
}
})

test('real Stop process observes a slow unknown flush and exits without continuing the turn', { timeout: 10000 }, async () => {
async function checkStopProcess(delayedCleanup) {
const root = await mkdtemp(join(tmpdir(), 'pc-zcode-stop-'))
let flushes = 0
const server = createServer(async (req, res) => {
Expand All @@ -387,11 +387,23 @@ test('real Stop process observes a slow unknown flush and exits without continui
const tracking = await beginCaptureTracking(input, settings, keys, 'scope-boundary', data)
await tracking.accepted(1)
await leaseWindow()
const preload = join(root, 'transport-cleanup.mjs')
if (delayedCleanup) {
// Simulate a transport that retains a cleanup handle after an aborted real HTTP request.
await writeFile(preload, `
const originalFetch = globalThis.fetch
globalThis.fetch = async (...args) => {
try { return await originalFetch(...args) }
catch (error) { setTimeout(() => {}, 2500); throw error }
}
`)
}
const before = performance.now()
const result = await new Promise((resolve, reject) => {
const env = { ...process.env, ZCODE_PLUGIN_DATA: data }
for (const key of Object.keys(env)) if (key.startsWith('POWERCONTEXT_ZCODE_') || key === 'ZCODE_SESSION_ID') delete env[key]
const child = spawn(process.execPath, [join(installed, 'hooks/stop.mjs')], { env, stdio: ['pipe', 'pipe', 'pipe'] })
const args = [...(delayedCleanup ? ['--import', pathToFileURL(preload).href] : []), join(installed, 'hooks/stop.mjs')]
const child = spawn(process.execPath, args, { env, stdio: ['pipe', 'pipe', 'pipe'] })
let stdout = '', stderr = ''
child.stdout.setEncoding('utf8').on('data', chunk => { stdout += chunk })
child.stderr.setEncoding('utf8').on('data', chunk => { stderr += chunk })
Expand All @@ -412,4 +424,10 @@ test('real Stop process observes a slow unknown flush and exits without continui
await new Promise(resolve => server.close(resolve))
await rm(root, { recursive: true, force: true })
}
})
}

for (const delayedCleanup of [false, true]) {
const suffix = delayedCleanup ? ' with delayed transport cleanup' : ''
test(`real Stop process observes a slow unknown flush and exits without continuing the turn${suffix}`,
{ timeout: 10000 }, () => checkStopProcess(delayedCleanup))
}
Loading
Loading