pi-ui-extend 1.0.42 → 1.0.45
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/README.md +41 -12
- package/dist/app/app.d.ts +10 -0
- package/dist/app/app.js +132 -8
- package/dist/app/cli/install.js +2 -2
- package/dist/app/commands/command-controller.d.ts +4 -0
- package/dist/app/commands/command-controller.js +11 -0
- package/dist/app/commands/command-git-actions.d.ts +25 -0
- package/dist/app/commands/command-git-actions.js +381 -0
- package/dist/app/commands/command-host.d.ts +3 -0
- package/dist/app/commands/command-host.js +5 -2
- package/dist/app/commands/command-model-actions.d.ts +1 -0
- package/dist/app/commands/command-model-actions.js +51 -34
- package/dist/app/commands/command-navigation-actions.d.ts +1 -0
- package/dist/app/commands/command-navigation-actions.js +16 -3
- package/dist/app/commands/command-registry.d.ts +2 -0
- package/dist/app/commands/command-registry.js +14 -0
- package/dist/app/commands/reload-context-inventory.js +12 -1
- package/dist/app/input/autocomplete-controller.js +61 -34
- package/dist/app/input/input-action-controller.d.ts +2 -0
- package/dist/app/input/input-action-controller.js +8 -1
- package/dist/app/input/input-controller.d.ts +2 -1
- package/dist/app/input/input-controller.js +7 -2
- package/dist/app/input/prompt-enhancer-controller.js +53 -35
- package/dist/app/input/voice-controller.d.ts +41 -45
- package/dist/app/input/voice-controller.js +351 -419
- package/dist/app/model/model-usage-controller.js +17 -7
- package/dist/app/model/model-usage-status.d.ts +4 -1
- package/dist/app/model/model-usage-status.js +270 -46
- package/dist/app/popup/menu-items-controller.d.ts +13 -3
- package/dist/app/popup/menu-items-controller.js +37 -21
- package/dist/app/popup/popup-action-controller.d.ts +12 -2
- package/dist/app/popup/popup-action-controller.js +77 -24
- package/dist/app/popup/popup-menu-controller.d.ts +36 -14
- package/dist/app/popup/popup-menu-controller.js +239 -69
- package/dist/app/rendering/dcp-stats.d.ts +9 -4
- package/dist/app/rendering/dcp-stats.js +40 -425
- package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
- package/dist/app/rendering/popup-menu-renderer.js +42 -37
- package/dist/app/rendering/render-controller.js +23 -2
- package/dist/app/rendering/status-line-renderer.d.ts +5 -0
- package/dist/app/rendering/status-line-renderer.js +41 -5
- package/dist/app/rendering/tab-line-renderer.js +26 -20
- package/dist/app/runtime.d.ts +12 -1
- package/dist/app/runtime.js +120 -12
- package/dist/app/screen/mouse-controller.d.ts +2 -0
- package/dist/app/screen/mouse-controller.js +17 -7
- package/dist/app/screen/status-controller.d.ts +4 -0
- package/dist/app/screen/status-controller.js +5 -0
- package/dist/app/session/lazy-session-manager.js +34 -0
- package/dist/app/session/session-event-controller.d.ts +1 -0
- package/dist/app/session/session-event-controller.js +10 -1
- package/dist/app/session/session-history.d.ts +1 -0
- package/dist/app/session/session-history.js +12 -1
- package/dist/app/session/session-lifecycle-controller.d.ts +6 -1
- package/dist/app/session/session-lifecycle-controller.js +10 -1
- package/dist/app/session/tabs-controller.d.ts +25 -1
- package/dist/app/session/tabs-controller.js +442 -153
- package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
- package/dist/app/subagents/subagents-widget-controller.js +6 -0
- package/dist/app/types.d.ts +11 -1
- package/dist/app/workspace/workspace-actions-controller.js +10 -2
- package/dist/app/workspace/workspace-undo.d.ts +1 -0
- package/dist/app/workspace/workspace-undo.js +1 -0
- package/dist/bundled-extensions/question/index.js +9 -1
- package/dist/bundled-extensions/question/remote.d.ts +4 -0
- package/dist/bundled-extensions/question/remote.js +33 -0
- package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
- package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
- package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
- package/dist/bundled-extensions/telegram-connector/config.js +87 -0
- package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
- package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
- package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
- package/dist/bundled-extensions/telegram-connector/index.js +195 -0
- package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
- package/dist/bundled-extensions/terminal-bell/index.js +0 -76
- package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
- package/dist/bundled-extensions/workspace-undo/index.js +191 -0
- package/dist/config.d.ts +21 -2
- package/dist/config.js +159 -32
- package/dist/default-pix-config.js +20 -10
- package/dist/schemas/pi-tools-suite-schema.d.ts +8 -4
- package/dist/schemas/pi-tools-suite-schema.js +11 -5
- package/dist/schemas/pix-schema.d.ts +12 -2
- package/dist/schemas/pix-schema.js +37 -9
- package/dist/schemas/tasks-schema.js +1 -1
- package/docs/concurrency.md +3 -1
- package/docs/desktop-mvp.md +10 -5
- package/docs/desktop-task-manager.md +81 -28
- package/docs/release.md +30 -6
- package/external/pi-tools-suite/README.md +165 -39
- package/external/pi-tools-suite/docs/session-recovery.md +7 -0
- package/external/pi-tools-suite/docs/subagent-model-pools.md +12 -5
- package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
- package/external/pi-tools-suite/package.json +6 -1
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
- package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
- package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
- package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
- package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
- package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
- package/external/pi-tools-suite/src/async-subagents/commands.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +0 -2
- package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +40 -17
- package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
- package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +7 -4
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +34 -24
- package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
- package/external/pi-tools-suite/src/async-subagents/lib.ts +9 -1
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +0 -1
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +2 -2
- package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
- package/external/pi-tools-suite/src/config.ts +14 -0
- package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
- package/external/pi-tools-suite/src/context-gateway/config.ts +66 -1
- package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
- package/external/pi-tools-suite/src/context-gateway/enforcement.ts +123 -3
- package/external/pi-tools-suite/src/context-gateway/index.ts +137 -4
- package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +5 -5
- package/external/pi-tools-suite/src/context-gateway/telemetry.ts +2 -2
- package/external/pi-tools-suite/src/context-gateway/types.ts +8 -1
- package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +41 -14
- package/external/pi-tools-suite/src/dcp/commands.ts +14 -156
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +8 -2
- package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
- package/external/pi-tools-suite/src/dcp/config.ts +58 -4
- package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
- package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
- package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
- package/external/pi-tools-suite/src/dcp/index.ts +338 -66
- package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
- package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +92 -2
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
- package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
- package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
- package/external/pi-tools-suite/src/dcp/state.ts +9 -0
- package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
- package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +68 -16
- package/external/pi-tools-suite/src/lib/project.ts +36 -1
- package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
- package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
- package/external/pi-tools-suite/src/resource-registry/index.ts +282 -21
- package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
- package/external/pi-tools-suite/src/todo/index.ts +21 -0
- package/external/pi-tools-suite/src/todo/todo.ts +19 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +44 -15
- package/package.json +7 -8
- package/schemas/pi-tools-suite.json +44 -12
- package/schemas/pix.json +109 -15
- package/schemas/tasks.json +1 -2
- package/skills/simplify/SKILL.md +33 -5
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -196
- package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
- package/skills/playwright-cli/SKILL.md +0 -420
- package/skills/playwright-cli/references/element-attributes.md +0 -23
- package/skills/playwright-cli/references/playwright-tests.md +0 -50
- package/skills/playwright-cli/references/request-mocking.md +0 -87
- package/skills/playwright-cli/references/running-code.md +0 -241
- package/skills/playwright-cli/references/session-management.md +0 -273
- package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
- package/skills/playwright-cli/references/storage-state.md +0 -290
- package/skills/playwright-cli/references/test-generation.md +0 -142
- package/skills/playwright-cli/references/tracing.md +0 -154
- package/skills/playwright-cli/references/video-recording.md +0 -147
- package/skills/spec-wiki/SKILL.md +0 -55
- package/skills/spec-wiki/evals/evals.json +0 -132
- package/skills/spec-wiki/evals/files/bootstrap/architecture/rfc-007.md +0 -14
- package/skills/spec-wiki/evals/files/bootstrap/docs/how-to-pay.md +0 -4
- package/skills/spec-wiki/evals/files/bootstrap/docs/overview.md +0 -9
- package/skills/spec-wiki/evals/files/bootstrap/requirements/payment-behavior.md +0 -17
- package/skills/spec-wiki/evals/files/bootstrap/src/payments.ts +0 -12
- package/skills/spec-wiki/evals/files/bootstrap/tests/payments.test.ts +0 -11
- package/skills/spec-wiki/evals/files/focused/src/session.ts +0 -5
- package/skills/spec-wiki/evals/files/focused/tests/session.test.ts +0 -17
- package/skills/spec-wiki/evals/files/maintenance/low-signal/incoming/capture.txt +0 -2
- package/skills/spec-wiki/evals/files/maintenance/low-signal/src/capture.ts +0 -3
- package/skills/spec-wiki/evals/files/maintenance/move/docs/old-job-contract.md +0 -18
- package/skills/spec-wiki/evals/files/maintenance/move/src/job.ts +0 -5
- package/skills/spec-wiki/evals/files/maintenance/new-relation/docs/session-refresh.md +0 -24
- package/skills/spec-wiki/evals/files/maintenance/new-relation/incoming/refresh-worker.ts +0 -7
- package/skills/spec-wiki/evals/files/maintenance/new-relation/src/session.ts +0 -7
- package/skills/spec-wiki/evals/files/maintenance/no-impact/docs/auth-contract.md +0 -17
- package/skills/spec-wiki/evals/files/maintenance/no-impact/incoming/telemetry-format.ts +0 -3
- package/skills/spec-wiki/evals/files/maintenance/no-impact/src/auth.ts +0 -3
- package/skills/spec-wiki/evals/files/query/docs/how-to-run-jobs.md +0 -4
- package/skills/spec-wiki/evals/files/query/docs/spec-overview.md +0 -9
- package/skills/spec-wiki/evals/files/query/features/jobs/cancellation-contract.md +0 -15
- package/skills/spec-wiki/evals/files/query/features/jobs/retry-spec.md +0 -6
- package/skills/spec-wiki/evals/maintenance_regression.py +0 -311
- package/skills/spec-wiki/evals/retrieval-eval.md +0 -37
- package/skills/spec-wiki/evals/retrieval-evals.json +0 -37
- package/skills/spec-wiki/evals/trigger-evals.json +0 -94
- package/skills/spec-wiki/references/bootstrap.md +0 -100
- package/skills/spec-wiki/references/focused-spec.md +0 -117
- package/skills/spec-wiki/references/index-schema.md +0 -330
- package/skills/spec-wiki/references/maintenance.md +0 -134
- package/skills/spec-wiki/references/query.md +0 -74
- package/skills/spec-wiki/scripts/spec_wiki.py +0 -2076
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.mjs +0 -0
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Payment capture requirements
|
|
2
|
-
|
|
3
|
-
## Behavior
|
|
4
|
-
|
|
5
|
-
- A capture request with the same idempotency key must not charge twice.
|
|
6
|
-
- A successful capture returns the existing capture result when the same key is
|
|
7
|
-
replayed.
|
|
8
|
-
- A different amount with a reused idempotency key is rejected.
|
|
9
|
-
|
|
10
|
-
## Related files
|
|
11
|
-
|
|
12
|
-
- `src/payments.ts`
|
|
13
|
-
- `tests/payments.test.ts`
|
|
14
|
-
|
|
15
|
-
## Verification
|
|
16
|
-
|
|
17
|
-
Cover first capture, exact replay, and conflicting replay.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const captures = new Map<string, { amount: number; id: string }>();
|
|
2
|
-
|
|
3
|
-
export function capture(key: string, amount: number): { amount: number; id: string } {
|
|
4
|
-
const existing = captures.get(key);
|
|
5
|
-
if (existing) {
|
|
6
|
-
if (existing.amount !== amount) throw new Error("idempotency conflict");
|
|
7
|
-
return existing;
|
|
8
|
-
}
|
|
9
|
-
const result = { amount, id: `capture-${captures.size + 1}` };
|
|
10
|
-
captures.set(key, result);
|
|
11
|
-
return result;
|
|
12
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { test } from "node:test";
|
|
4
|
-
|
|
5
|
-
import { capture } from "../src/payments.js";
|
|
6
|
-
|
|
7
|
-
test("idempotent replay returns the same capture", () => {
|
|
8
|
-
const first = capture("key-1", 100);
|
|
9
|
-
assert.deepEqual(capture("key-1", 100), first);
|
|
10
|
-
assert.throws(() => capture("key-1", 200), /idempotency conflict/);
|
|
11
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { test } from "node:test";
|
|
4
|
-
|
|
5
|
-
import { refreshSession } from "../src/session.js";
|
|
6
|
-
|
|
7
|
-
test("refreshSession performs one token-load attempt", async () => {
|
|
8
|
-
let attempts = 0;
|
|
9
|
-
await assert.rejects(
|
|
10
|
-
refreshSession(async () => {
|
|
11
|
-
attempts += 1;
|
|
12
|
-
throw new Error("transient");
|
|
13
|
-
}),
|
|
14
|
-
/transient/,
|
|
15
|
-
);
|
|
16
|
-
assert.equal(attempts, 1);
|
|
17
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Background job cancellation contract
|
|
2
|
-
|
|
3
|
-
## Type
|
|
4
|
-
|
|
5
|
-
As-is
|
|
6
|
-
|
|
7
|
-
## Lifecycle
|
|
8
|
-
|
|
9
|
-
Active
|
|
10
|
-
|
|
11
|
-
## Behavior
|
|
12
|
-
|
|
13
|
-
Cancelling a running job is idempotent. Running jobs become cancelled; already
|
|
14
|
-
terminal jobs remain terminal.
|
|
15
|
-
|
|
16
|
-
## Related files
|
|
17
|
-
|
|
18
|
-
- `src/job.ts`
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# Session refresh contract
|
|
2
|
-
|
|
3
|
-
## Type
|
|
4
|
-
|
|
5
|
-
As-is
|
|
6
|
-
|
|
7
|
-
## Lifecycle
|
|
8
|
-
|
|
9
|
-
Active
|
|
10
|
-
|
|
11
|
-
## Behavior
|
|
12
|
-
|
|
13
|
-
Interactive and background session refresh retry a transient token-acquisition
|
|
14
|
-
failure exactly once. A second token-acquisition failure propagates to the
|
|
15
|
-
caller. Successful acquisition is not retried.
|
|
16
|
-
|
|
17
|
-
## Related files
|
|
18
|
-
|
|
19
|
-
- `src/session.ts`
|
|
20
|
-
|
|
21
|
-
## Verification
|
|
22
|
-
|
|
23
|
-
- Interactive refresh covers success, retry-success, and second-failure paths.
|
|
24
|
-
- Every additional refresh entrypoint must preserve the same retry contract.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# Job specifications overview
|
|
2
|
-
|
|
3
|
-
The primary job behavior documents are linked below.
|
|
4
|
-
|
|
5
|
-
- [Cancellation contract](../features/jobs/cancellation-contract.md) — jobs can
|
|
6
|
-
be stopped safely.
|
|
7
|
-
- [Retry spec](../features/jobs/retry-spec.md) — retry timing and limits.
|
|
8
|
-
|
|
9
|
-
This page is a catalog, not a behavioral source of truth.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Background job cancellation contract
|
|
2
|
-
|
|
3
|
-
## Behavior
|
|
4
|
-
|
|
5
|
-
- Cancellation requests are idempotent.
|
|
6
|
-
- A running job transitions to `cancelled` when cancellation is accepted.
|
|
7
|
-
- Jobs already in `completed`, `failed`, or `cancelled` remain in that terminal
|
|
8
|
-
state when cancellation is requested again.
|
|
9
|
-
- Cancellation does not imply that already-committed external side effects are
|
|
10
|
-
rolled back.
|
|
11
|
-
|
|
12
|
-
## Verification
|
|
13
|
-
|
|
14
|
-
Tests should cover repeated cancellation, running-to-cancelled transition, and
|
|
15
|
-
all terminal states.
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Deterministic regression checks for Spec Wiki maintenance primitives."""
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import json
|
|
7
|
-
import subprocess
|
|
8
|
-
import tempfile
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
CLI = Path(__file__).resolve().parents[1] / "scripts" / "spec_wiki.py"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def run(cwd: Path, *args: str) -> str:
|
|
16
|
-
proc = subprocess.run(
|
|
17
|
-
["python3", str(CLI), "--root", str(cwd), *args],
|
|
18
|
-
cwd=cwd,
|
|
19
|
-
text=True,
|
|
20
|
-
stdout=subprocess.PIPE,
|
|
21
|
-
stderr=subprocess.PIPE,
|
|
22
|
-
check=False,
|
|
23
|
-
)
|
|
24
|
-
if proc.returncode != 0:
|
|
25
|
-
raise AssertionError(f"command failed {args}:\nstdout={proc.stdout}\nstderr={proc.stderr}")
|
|
26
|
-
return proc.stdout
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def run_json(cwd: Path, *args: str) -> dict:
|
|
30
|
-
return json.loads(run(cwd, *args, "--json"))
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def git(cwd: Path, *args: str) -> None:
|
|
34
|
-
subprocess.run(["git", *args], cwd=cwd, check=True, stdout=subprocess.DEVNULL)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def write(root: Path, rel: str, text: str) -> None:
|
|
38
|
-
path = root / rel
|
|
39
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
40
|
-
path.write_text(text, encoding="utf-8")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def init_repo(root: Path) -> None:
|
|
44
|
-
git(root, "init", "-q")
|
|
45
|
-
git(root, "config", "user.email", "spec-wiki-eval@example.com")
|
|
46
|
-
git(root, "config", "user.name", "Spec Wiki Eval")
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def commit_all(root: Path, message: str = "baseline") -> None:
|
|
50
|
-
git(root, "add", ".")
|
|
51
|
-
git(root, "commit", "-qm", message)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def test_relation_map_invalidates_verification(tmp: Path) -> None:
|
|
55
|
-
root = tmp / "relations"
|
|
56
|
-
root.mkdir()
|
|
57
|
-
init_repo(root)
|
|
58
|
-
write(root, "src/a.ts", "export const a = 1\n")
|
|
59
|
-
write(root, "src/b.ts", "export const b = 2\n")
|
|
60
|
-
write(
|
|
61
|
-
root,
|
|
62
|
-
"docs/contract.md",
|
|
63
|
-
"""# Contract
|
|
64
|
-
|
|
65
|
-
## Type
|
|
66
|
-
As-is
|
|
67
|
-
|
|
68
|
-
## Lifecycle
|
|
69
|
-
Active
|
|
70
|
-
|
|
71
|
-
## Behavior
|
|
72
|
-
A is part of the current contract.
|
|
73
|
-
|
|
74
|
-
## Related files
|
|
75
|
-
- `src/a.ts`
|
|
76
|
-
""",
|
|
77
|
-
)
|
|
78
|
-
commit_all(root)
|
|
79
|
-
run(
|
|
80
|
-
root,
|
|
81
|
-
"record",
|
|
82
|
-
"--path",
|
|
83
|
-
"docs/contract.md",
|
|
84
|
-
"--classification",
|
|
85
|
-
"spec",
|
|
86
|
-
"--type",
|
|
87
|
-
"as-is",
|
|
88
|
-
"--lifecycle",
|
|
89
|
-
"active",
|
|
90
|
-
"--confidence",
|
|
91
|
-
"high",
|
|
92
|
-
"--summary",
|
|
93
|
-
"Contract for A.",
|
|
94
|
-
)
|
|
95
|
-
run(root, "verify", "--path", "docs/contract.md")
|
|
96
|
-
audit = run_json(root, "audit")
|
|
97
|
-
assert audit["freshCount"] == 1
|
|
98
|
-
|
|
99
|
-
run(root, "relate", "--path", "docs/contract.md", "--add-code", "src/b.ts")
|
|
100
|
-
audit = run_json(root, "audit")
|
|
101
|
-
assert audit["freshCount"] == 0
|
|
102
|
-
status = next(item for item in audit["statuses"] if item["path"] == "docs/contract.md")
|
|
103
|
-
assert status["status"] == "inputs-changed"
|
|
104
|
-
assert "relations:changed" in status["reasons"]
|
|
105
|
-
|
|
106
|
-
run(root, "verify", "--path", "docs/contract.md")
|
|
107
|
-
assert run_json(root, "audit")["freshCount"] == 1
|
|
108
|
-
run(root, "relate", "--path", "docs/contract.md", "--remove-code", "src/b.ts")
|
|
109
|
-
status = next(item for item in run_json(root, "audit")["statuses"] if item["path"] == "docs/contract.md")
|
|
110
|
-
assert status["status"] == "inputs-changed"
|
|
111
|
-
assert status["reasons"] == ["relations:changed"]
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def test_impact_detects_coverage_gaps_and_moves(tmp: Path) -> None:
|
|
115
|
-
root = tmp / "impact"
|
|
116
|
-
root.mkdir()
|
|
117
|
-
init_repo(root)
|
|
118
|
-
write(root, "src/session.ts", "export function session() { return 'ok' }\n")
|
|
119
|
-
write(
|
|
120
|
-
root,
|
|
121
|
-
"docs/session-contract.md",
|
|
122
|
-
"""# Session contract
|
|
123
|
-
|
|
124
|
-
## Type
|
|
125
|
-
As-is
|
|
126
|
-
|
|
127
|
-
## Lifecycle
|
|
128
|
-
Active
|
|
129
|
-
|
|
130
|
-
## Behavior
|
|
131
|
-
Session loading returns the current session.
|
|
132
|
-
|
|
133
|
-
## Related files
|
|
134
|
-
- `src/session.ts`
|
|
135
|
-
""",
|
|
136
|
-
)
|
|
137
|
-
commit_all(root)
|
|
138
|
-
run(
|
|
139
|
-
root,
|
|
140
|
-
"record",
|
|
141
|
-
"--path",
|
|
142
|
-
"docs/session-contract.md",
|
|
143
|
-
"--classification",
|
|
144
|
-
"spec",
|
|
145
|
-
"--type",
|
|
146
|
-
"as-is",
|
|
147
|
-
"--lifecycle",
|
|
148
|
-
"active",
|
|
149
|
-
"--confidence",
|
|
150
|
-
"high",
|
|
151
|
-
"--summary",
|
|
152
|
-
"Session loading contract.",
|
|
153
|
-
)
|
|
154
|
-
run(root, "verify", "--path", "docs/session-contract.md")
|
|
155
|
-
|
|
156
|
-
git(root, "mv", "docs/session-contract.md", "docs/session-lifecycle.md")
|
|
157
|
-
with (root / "docs/session-lifecycle.md").open("a", encoding="utf-8") as handle:
|
|
158
|
-
handle.write("\nMoved without changing the behavioral contract.\n")
|
|
159
|
-
write(root, "src/refresh-worker.ts", "export const retryOnce = true\n")
|
|
160
|
-
write(root, "notes.txt", "deliberately low signal document\n")
|
|
161
|
-
|
|
162
|
-
impact = run_json(root, "impact")
|
|
163
|
-
assert ".spec-wiki/state.json" not in impact["changedPaths"]
|
|
164
|
-
assert any(
|
|
165
|
-
item.get("status") == "R"
|
|
166
|
-
and item.get("oldPath") == "docs/session-contract.md"
|
|
167
|
-
and item.get("path") == "docs/session-lifecycle.md"
|
|
168
|
-
for item in impact["changes"]
|
|
169
|
-
)
|
|
170
|
-
assert "src/refresh-worker.ts" in impact["uncoveredPaths"]
|
|
171
|
-
changed_docs = {item["path"]: item for item in impact["changedDocuments"]}
|
|
172
|
-
assert changed_docs["docs/session-lifecycle.md"]["requiresClassification"] is True
|
|
173
|
-
assert changed_docs["notes.txt"]["score"] == 0
|
|
174
|
-
assert "docs/session-contract.md" in impact["missingTrackedSpecs"]
|
|
175
|
-
assert impact["semanticSweepRequired"] is True
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def test_deep_audit_finds_low_signal_committed_docs(tmp: Path) -> None:
|
|
179
|
-
root = tmp / "deep"
|
|
180
|
-
root.mkdir()
|
|
181
|
-
init_repo(root)
|
|
182
|
-
write(
|
|
183
|
-
root,
|
|
184
|
-
"notes/capture.txt",
|
|
185
|
-
"Duplicate capture requests with the same key return the original result.\n",
|
|
186
|
-
)
|
|
187
|
-
write(root, "skills/example/evals/fixture.md", "# Contract\n\n## Behavior\nFixture only.\n")
|
|
188
|
-
commit_all(root)
|
|
189
|
-
|
|
190
|
-
normal = run_json(root, "discover", "--limit", "100")
|
|
191
|
-
assert "notes/capture.txt" not in {item["path"] for item in normal["candidates"]}
|
|
192
|
-
deep = run_json(root, "discover", "--all-unclassified", "--limit", "100")
|
|
193
|
-
paths = {item["path"] for item in deep["candidates"]}
|
|
194
|
-
assert "notes/capture.txt" in paths
|
|
195
|
-
assert "skills/example/evals/fixture.md" not in paths
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def test_relate_restores_source_explicit_relation(tmp: Path) -> None:
|
|
199
|
-
root = tmp / "supersession"
|
|
200
|
-
root.mkdir()
|
|
201
|
-
init_repo(root)
|
|
202
|
-
write(root, "docs/b.md", "# B\n")
|
|
203
|
-
write(
|
|
204
|
-
root,
|
|
205
|
-
"docs/a.md",
|
|
206
|
-
"""# A contract
|
|
207
|
-
|
|
208
|
-
## Type
|
|
209
|
-
As-is
|
|
210
|
-
|
|
211
|
-
## Lifecycle
|
|
212
|
-
Active
|
|
213
|
-
|
|
214
|
-
## Behavior
|
|
215
|
-
A references B.
|
|
216
|
-
|
|
217
|
-
[B](./b.md)
|
|
218
|
-
""",
|
|
219
|
-
)
|
|
220
|
-
commit_all(root)
|
|
221
|
-
run(
|
|
222
|
-
root,
|
|
223
|
-
"record",
|
|
224
|
-
"--path",
|
|
225
|
-
"docs/a.md",
|
|
226
|
-
"--classification",
|
|
227
|
-
"spec",
|
|
228
|
-
"--type",
|
|
229
|
-
"as-is",
|
|
230
|
-
"--lifecycle",
|
|
231
|
-
"active",
|
|
232
|
-
"--confidence",
|
|
233
|
-
"high",
|
|
234
|
-
"--summary",
|
|
235
|
-
"A contract.",
|
|
236
|
-
)
|
|
237
|
-
run(root, "verify", "--path", "docs/a.md")
|
|
238
|
-
run(root, "relate", "--path", "docs/a.md", "--add-supersedes", "docs/b.md")
|
|
239
|
-
state = json.loads((root / ".spec-wiki/state.json").read_text(encoding="utf-8"))
|
|
240
|
-
relations = state["entries"]["docs/a.md"]["relations"]["specs"]
|
|
241
|
-
assert any(
|
|
242
|
-
item.get("path") == "docs/b.md"
|
|
243
|
-
and item.get("source") == "explicit"
|
|
244
|
-
and item.get("kind") == "related"
|
|
245
|
-
for item in relations
|
|
246
|
-
)
|
|
247
|
-
assert any(item.get("path") == "docs/b.md" and item.get("kind") == "supersedes" for item in relations)
|
|
248
|
-
run(root, "relate", "--path", "docs/a.md", "--remove-supersedes", "docs/b.md")
|
|
249
|
-
state = json.loads((root / ".spec-wiki/state.json").read_text(encoding="utf-8"))
|
|
250
|
-
relations = state["entries"]["docs/a.md"]["relations"]["specs"]
|
|
251
|
-
assert any(
|
|
252
|
-
item.get("path") == "docs/b.md"
|
|
253
|
-
and item.get("source") == "explicit"
|
|
254
|
-
and item.get("kind") == "related"
|
|
255
|
-
for item in relations
|
|
256
|
-
)
|
|
257
|
-
assert not any(item.get("kind") == "supersedes" for item in relations)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def test_changed_non_primary_requires_reclassification(tmp: Path) -> None:
|
|
261
|
-
root = tmp / "reclassification"
|
|
262
|
-
root.mkdir()
|
|
263
|
-
init_repo(root)
|
|
264
|
-
write(root, "docs/info.md", "# Operator notes\n\nRun the service with --help.\n")
|
|
265
|
-
commit_all(root)
|
|
266
|
-
run(
|
|
267
|
-
root,
|
|
268
|
-
"record",
|
|
269
|
-
"--path",
|
|
270
|
-
"docs/info.md",
|
|
271
|
-
"--classification",
|
|
272
|
-
"guide",
|
|
273
|
-
"--summary",
|
|
274
|
-
"Operator notes.",
|
|
275
|
-
)
|
|
276
|
-
write(
|
|
277
|
-
root,
|
|
278
|
-
"docs/info.md",
|
|
279
|
-
"""# Request contract
|
|
280
|
-
|
|
281
|
-
## Type
|
|
282
|
-
As-is
|
|
283
|
-
|
|
284
|
-
## Lifecycle
|
|
285
|
-
Active
|
|
286
|
-
|
|
287
|
-
## Behavior
|
|
288
|
-
Duplicate requests return the original result.
|
|
289
|
-
""",
|
|
290
|
-
)
|
|
291
|
-
impact = run_json(root, "impact", "docs/info.md")
|
|
292
|
-
changed = next(item for item in impact["changedDocuments"] if item["path"] == "docs/info.md")
|
|
293
|
-
assert changed["knownClassification"] == "guide"
|
|
294
|
-
assert changed["requiresClassification"] is False
|
|
295
|
-
assert changed["requiresReclassification"] is True
|
|
296
|
-
assert impact["semanticSweepRequired"] is True
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
def main() -> None:
|
|
300
|
-
with tempfile.TemporaryDirectory(prefix="spec-wiki-maint-") as directory:
|
|
301
|
-
tmp = Path(directory)
|
|
302
|
-
test_relation_map_invalidates_verification(tmp)
|
|
303
|
-
test_impact_detects_coverage_gaps_and_moves(tmp)
|
|
304
|
-
test_deep_audit_finds_low_signal_committed_docs(tmp)
|
|
305
|
-
test_relate_restores_source_explicit_relation(tmp)
|
|
306
|
-
test_changed_non_primary_requires_reclassification(tmp)
|
|
307
|
-
print("maintenance regression: 5/5 passed")
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if __name__ == "__main__":
|
|
311
|
-
main()
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Retrieval regression protocol
|
|
2
|
-
|
|
3
|
-
Use `retrieval-evals.json` to check the query router after search/index changes.
|
|
4
|
-
The cases are intentionally mixed: direct keywords, natural paraphrases,
|
|
5
|
-
Russian requests, synonyms, exact code paths, and symbols.
|
|
6
|
-
|
|
7
|
-
## Guard against answer leakage
|
|
8
|
-
|
|
9
|
-
The retrieval agent must receive only each case `id` and `query`. Do not expose
|
|
10
|
-
`expectedAny` until after it returns candidate paths.
|
|
11
|
-
|
|
12
|
-
## Stage 1: deterministic candidate generation
|
|
13
|
-
|
|
14
|
-
Run `spec_wiki.py search` against the current `.spec-wiki/state.json` and record
|
|
15
|
-
top-1/top-3/top-5 plus `confidence` / `catalogFallbackRecommended`. This stage is
|
|
16
|
-
expected to be imperfect for semantic paraphrases; its job is cheap candidate
|
|
17
|
-
generation and reliable low-confidence detection.
|
|
18
|
-
|
|
19
|
-
## Stage 2: full skill workflow
|
|
20
|
-
|
|
21
|
-
Have a fresh agent read only `SKILL.md` and `references/query.md`, then for each
|
|
22
|
-
query:
|
|
23
|
-
|
|
24
|
-
1. create the in-model retrieval bundle;
|
|
25
|
-
2. call relation-aware `search` with `--also` aliases;
|
|
26
|
-
3. semantically rerank compact result metadata;
|
|
27
|
-
4. when fallback is recommended or intent coverage is visibly incomplete, use
|
|
28
|
-
only `.spec-wiki/index.md` as the semantic catalog fallback;
|
|
29
|
-
5. return up to three primary spec paths without opening the primary specs.
|
|
30
|
-
|
|
31
|
-
Compare those paths with `expectedAny` only after the run. A case passes top-k
|
|
32
|
-
when any expected path appears in the first k selections.
|
|
33
|
-
|
|
34
|
-
Current target for this repository: at least 95% top-1 and 100% top-3/recall.
|
|
35
|
-
Do not tune expected paths after seeing a run merely to improve the score; change
|
|
36
|
-
an expectation only when independent review shows that more than one primary
|
|
37
|
-
spec is genuinely defensible.
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"description": "Live retrieval regression cases for the host repository. Expected paths are chosen before running the search; use expectedAny when more than one primary spec is defensible.",
|
|
4
|
-
"cases": [
|
|
5
|
-
{"id":"kw-dcp","category":"keyword","query":"dcp persistence recovery","expectedAny":["specs/dcp.md"]},
|
|
6
|
-
{"id":"kw-desktop-markdown","category":"keyword","query":"desktop markdown mermaid file preview","expectedAny":["specs/desktop-markdown-rendering.md"]},
|
|
7
|
-
{"id":"kw-project-agents","category":"keyword","query":"project local agents .pi/agents","expectedAny":["specs/project-agents-dir.md"]},
|
|
8
|
-
{"id":"kw-question-tab","category":"keyword","query":"question inactive tab","expectedAny":["specs/question-inactive-tab.md"]},
|
|
9
|
-
{"id":"kw-retry-toast","category":"keyword","query":"retry toast action","expectedAny":["specs/retry-toast-action.md"]},
|
|
10
|
-
{"id":"kw-openai-usage","category":"keyword","query":"openai codex token refresh usage","expectedAny":["specs/openai-codex-usage-refresh.md"]},
|
|
11
|
-
|
|
12
|
-
{"id":"para-recovery","category":"paraphrase","query":"recover old conversation after compaction","expectedAny":["external/pi-tools-suite/docs/session-recovery.md"]},
|
|
13
|
-
{"id":"para-browser-qa","category":"paraphrase","query":"browser qa credentials origin isolation redaction","expectedAny":["external/pi-tools-suite/docs/browser-qa-subagent.md"]},
|
|
14
|
-
{"id":"para-concurrency","category":"paraphrase","query":"async callback updates the wrong session tab","expectedAny":["docs/concurrency.md"]},
|
|
15
|
-
{"id":"para-cache","category":"paraphrase","query":"keep tool output stable for prompt caching","expectedAny":["specs/dcp-provider-cache-stability.md"]},
|
|
16
|
-
{"id":"para-window","category":"paraphrase","query":"window opens offscreen after monitor change","expectedAny":["specs/desktop-window-state.md"]},
|
|
17
|
-
{"id":"para-copy","category":"paraphrase","query":"copy last assistant response slash command","expectedAny":["specs/desktop-slash-commands.md"]},
|
|
18
|
-
{"id":"para-task-jsonc","category":"paraphrase","query":"task storage comments jsonc","expectedAny":["docs/desktop-task-manager.md"]},
|
|
19
|
-
{"id":"para-wrapped-link","category":"paraphrase","query":"click file uri in wrapped markdown","expectedAny":["specs/clickable-markdown-links.md"]},
|
|
20
|
-
{"id":"para-zed","category":"paraphrase","query":"open source file in zed at line","expectedAny":["specs/file-link-opening.md"]},
|
|
21
|
-
|
|
22
|
-
{"id":"ru-recovery","category":"russian","query":"как восстанавливается история после сжатия контекста","expectedAny":["external/pi-tools-suite/docs/session-recovery.md"]},
|
|
23
|
-
{"id":"ru-zed","category":"russian","query":"как открыть ссылку на файл в zed","expectedAny":["specs/file-link-opening.md"]},
|
|
24
|
-
{"id":"ru-question-tab","category":"russian","query":"что происходит с вопросом в неактивной вкладке","expectedAny":["specs/question-inactive-tab.md"]},
|
|
25
|
-
{"id":"ru-tasks","category":"russian","query":"где хранятся задачи проекта и поддерживаются ли комментарии","expectedAny":["docs/desktop-task-manager.md"]},
|
|
26
|
-
{"id":"ru-provider-seen","category":"russian","query":"почему старый tool result нельзя удалять до отправки провайдеру","expectedAny":["specs/dcp-provider-cache-stability.md"]},
|
|
27
|
-
|
|
28
|
-
{"id":"syn-auth","category":"synonym","query":"oauth account rotation when quota exhausted","expectedAny":["specs/antigravity-auth.md"]},
|
|
29
|
-
{"id":"syn-background-agent","category":"synonym","query":"cancel a running background agent","expectedAny":["specs/async-subagents.md"]},
|
|
30
|
-
|
|
31
|
-
{"id":"path-openai","category":"path","query":"src/app/model/model-usage-status.ts","expectedAny":["specs/openai-codex-usage-refresh.md"]},
|
|
32
|
-
{"id":"path-dcp-journal","category":"path","query":"external/pi-tools-suite/src/dcp/journal.ts","expectedAny":["specs/dcp.md","specs/dcp-reliability-mechanisms.md","specs/dcp-provider-cache-stability.md"]},
|
|
33
|
-
|
|
34
|
-
{"id":"symbol-sidebar","category":"symbol","query":"WorkspaceSidebar.svelte tasks","expectedAny":["docs/desktop-task-manager.md"]},
|
|
35
|
-
{"id":"symbol-question-bridge","category":"symbol","query":"ui-request-bridge question","expectedAny":["specs/desktop-question-tool.md"]}
|
|
36
|
-
]
|
|
37
|
-
}
|