gentle-pi 3.7.0 → 4.0.0
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 +37 -6
- package/assets/agents/gentle-ai-explore.md +4 -4
- package/assets/agents/gentle-ai-verify.md +6 -4
- package/assets/agents/gentle-ai-worker.md +7 -9
- package/assets/orchestrator-delegation.md +40 -41
- package/assets/orchestrator-memory.md +1 -22
- package/assets/orchestrator-skills.md +1 -1
- package/assets/orchestrator.md +12 -24
- package/assets/support/strict-tdd-verify.md +4 -266
- package/assets/support/strict-tdd.md +8 -360
- package/bin/gentle-shell.mjs +254 -29
- package/docs/delegated-verification.md +26 -1
- package/docs/gentle-agents-activity.md +24 -0
- package/docs/gentle-shell.md +92 -28
- package/docs/native-authority-architecture.md +2 -2
- package/docs/prompt-history.md +280 -0
- package/docs/readme-reference.md +112 -206
- package/docs/telemetry.md +1 -1
- package/docs/yolo-mode.md +86 -0
- package/extensions/child-context.ts +26 -0
- package/extensions/child-safety.ts +23 -0
- package/extensions/gentle-agents.ts +429 -262
- package/extensions/gentle-ai.ts +781 -683
- package/extensions/gentle-shell.ts +1375 -88
- package/extensions/gentle-stats.ts +101 -0
- package/extensions/gentle-todo.ts +18 -7
- package/extensions/history/atomic-write.ts +38 -0
- package/extensions/history/hide-prompts.ts +183 -0
- package/extensions/history/index.ts +1419 -0
- package/extensions/history/load-shared-history.ts +39 -0
- package/extensions/history/selector-helpers.ts +538 -0
- package/extensions/history/session-scan.ts +233 -0
- package/extensions/history/store.ts +1119 -0
- package/extensions/nan-provider.ts +6 -0
- package/extensions/quiet-tools.ts +179 -87
- package/extensions/resume-hint.ts +60 -0
- package/extensions/skill-registry.ts +16 -12
- package/extensions/startup-banner.ts +60 -31
- package/lib/agent-assets.ts +604 -0
- package/lib/agent-profile-pin.ts +12 -0
- package/lib/agents-message-delivery.ts +181 -0
- package/lib/agents-protocol.ts +60 -0
- package/lib/agents-runner.ts +96 -98
- package/lib/agents-view.ts +26 -3
- package/lib/agents-widget.ts +16 -9
- package/lib/append-system-prompt.ts +21 -0
- package/lib/bounded-writer-admission.ts +147 -0
- package/lib/card-style-policy.ts +60 -0
- package/lib/child-context-files.ts +166 -0
- package/lib/codemode-renderer.ts +185 -0
- package/lib/command-palette-catalog.ts +3 -9
- package/lib/command-palette.ts +25 -14
- package/lib/destructive-command-guard.ts +144 -0
- package/lib/gentle-ai-elapsed-store.ts +87 -0
- package/lib/gentle-ai-renderer.ts +196 -39
- package/lib/gentle-shell-launcher.ts +24 -13
- package/lib/gentle-shell-resume-hint.ts +176 -0
- package/lib/history-capture-policy.ts +95 -0
- package/lib/model-routing-authority.ts +5 -1
- package/lib/nan-provider.ts +227 -0
- package/lib/native-review-cli.ts +49 -108
- package/lib/odd-phase-inference.ts +231 -0
- package/lib/odd-phase.ts +141 -0
- package/lib/overlay-repaint.ts +26 -0
- package/lib/pi-tui-keys.ts +53 -0
- package/lib/review-candidate-view-owner.ts +67 -17
- package/lib/review-candidate-view.ts +112 -25
- package/lib/review-reminder-receipt.ts +48 -8
- package/lib/review-risk-assessment.ts +156 -11
- package/lib/review-sidebar-state.ts +223 -0
- package/lib/selection-engine.ts +515 -0
- package/lib/session-messaging-grants.ts +135 -0
- package/lib/session-worktree-registry.ts +14 -2
- package/lib/shell-bar.ts +179 -24
- package/lib/shell-card.ts +287 -18
- package/lib/shell-changes-view.ts +2 -1
- package/lib/shell-prompt.ts +102 -6
- package/lib/shell-sidebar-layout.ts +78 -14
- package/lib/shell-sidebar.ts +15 -1
- package/lib/shell-todo.ts +23 -13
- package/lib/shell-usage-view.ts +9 -4
- package/lib/shell-usage.ts +66 -10
- package/lib/stats-collector.ts +381 -0
- package/lib/stats-view.ts +431 -0
- package/lib/theme-customization.ts +52 -0
- package/lib/vim-editor-adapter.ts +379 -0
- package/lib/vim-normal-engine.ts +154 -0
- package/lib/vim-operator-engine.ts +416 -0
- package/lib/vim-policy.ts +49 -0
- package/lib/vim-visual-engine.ts +107 -0
- package/lib/visual-customization-policy.ts +108 -0
- package/lib/visual-customize-view.ts +330 -0
- package/lib/visual-profiles.ts +228 -0
- package/lib/yolo-session-policy.ts +240 -0
- package/package.json +20 -8
- package/runtime/gentle-shell-launcher.mjs +23 -12
- package/runtime/gentle-shell-resume-hint.mjs +177 -0
- package/runtime/native-review-cli.mjs +49 -108
- package/runtime/review-risk-assessment.mjs +154 -9
- package/scripts/build-runtime-modules.mjs +1 -0
- package/scripts/gentle-ai-installer.mjs +14 -13
- package/scripts/mirror-odd-routing.mjs +2 -2
- package/scripts/run-test-suite.mjs +76 -0
- package/scripts/test-packed-runner.mjs +31 -14
- package/scripts/verify-package-files.mjs +11 -22
- package/skills/branch-pr/SKILL.md +24 -52
- package/skills/chained-pr/SKILL.md +31 -15
- package/skills/chained-pr/references/chaining-details.md +31 -20
- package/skills/gentle-ai/SKILL.md +9 -15
- package/skills/issue-creation/SKILL.md +8 -2
- package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
- package/skills/work-unit-commits/SKILL.md +4 -3
- package/tests/agent-profiles.test.ts +18 -0
- package/tests/agents-fake-child.ts +2 -2
- package/tests/agents-message-delivery.test.ts +106 -0
- package/tests/agents-protocol.test.ts +40 -0
- package/tests/agents-runner.test.ts +378 -89
- package/tests/agents-view-thread-identity.test.ts +169 -0
- package/tests/agents-view.test.ts +8 -2
- package/tests/agents-widget.test.ts +154 -15
- package/tests/append-system-prompt-route.test.ts +160 -0
- package/tests/append-system-prompt.test.ts +46 -0
- package/tests/artifact-language.test.ts +19 -213
- package/tests/ask-user-question.test.ts +44 -1
- package/tests/asset-installation-runtime.test.ts +5 -16
- package/tests/autonomous-guard.test.ts +69 -1
- package/tests/bounded-writer-admission.test.ts +95 -0
- package/tests/branch-pr-skill.test.ts +43 -0
- package/tests/card-style-policy.test.ts +55 -0
- package/tests/chained-pr-skill.test.ts +124 -0
- package/tests/child-context-files.test.ts +255 -0
- package/tests/child-safety.test.ts +82 -0
- package/tests/codemode-rendering.test.ts +491 -0
- package/tests/command-palette.test.ts +39 -3
- package/tests/delegated-key-learnings-contract.test.ts +0 -76
- package/tests/destructive-command-guard.test.ts +84 -0
- package/tests/devbinary/native-review-parity.devtest.ts +170 -2
- package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
- package/tests/generic-agent-tools.test.ts +54 -0
- package/tests/gentle-agents.test.ts +1047 -252
- package/tests/gentle-ai-binary.test.ts +3 -3
- package/tests/gentle-ai-elapsed-store.test.ts +68 -0
- package/tests/gentle-ai-installer.test.ts +68 -54
- package/tests/gentle-ai-renderer.test.ts +487 -8
- package/tests/gentle-ai.test.ts +288 -65
- package/tests/gentle-card-text.ts +2 -1
- package/tests/gentle-shell-bin.test.ts +651 -114
- package/tests/gentle-shell-launcher.test.ts +99 -52
- package/tests/gentle-shell-resume-hint.test.ts +270 -0
- package/tests/gentle-shell.test.ts +3839 -187
- package/tests/gentle-stats.test.ts +152 -0
- package/tests/gentle-theme.test.ts +4 -1
- package/tests/gentle-todo.test.ts +80 -8
- package/tests/history-atomic-write.test.ts +57 -0
- package/tests/history-capture-policy.test.ts +102 -0
- package/tests/history-command-registration.test.ts +164 -0
- package/tests/history-dedupe-entries.test.ts +123 -0
- package/tests/history-delete-backfill.test.ts +190 -0
- package/tests/history-delete-confirm.test.ts +460 -0
- package/tests/history-dispatch.test.ts +180 -0
- package/tests/history-drain-hidden.test.ts +110 -0
- package/tests/history-drain-order.test.ts +98 -0
- package/tests/history-expanded-globals.test.ts +62 -0
- package/tests/history-gc.test.ts +832 -0
- package/tests/history-header-layout.test.ts +265 -0
- package/tests/history-hide-prompts.test.ts +275 -0
- package/tests/history-lazy-windowing.test.ts +508 -0
- package/tests/history-legacy-migrate-v2.test.ts +297 -0
- package/tests/history-load-shared-history.test.ts +53 -0
- package/tests/history-max-results-cap.test.ts +76 -0
- package/tests/history-multi-reader.test.ts +203 -0
- package/tests/history-off-path.test.ts +170 -0
- package/tests/history-openflow-integration.test.ts +173 -0
- package/tests/history-overlay-margin.test.ts +326 -0
- package/tests/history-preview-layout.test.ts +93 -0
- package/tests/history-registry.test.ts +143 -0
- package/tests/history-scope-delete.test.ts +411 -0
- package/tests/history-search-caret-keys.test.ts +142 -0
- package/tests/history-seed-bootstrap.test.ts +170 -0
- package/tests/history-seed-regen.test.ts +129 -0
- package/tests/history-selector-windowing.test.ts +94 -0
- package/tests/history-session-scan-directory.test.ts +87 -0
- package/tests/history-session-scan-extract.test.ts +583 -0
- package/tests/history-session-writer.test.ts +351 -0
- package/tests/history-store-paths.test.ts +79 -0
- package/tests/history-tombstone-exact.test.ts +139 -0
- package/tests/history-wheel-mouse.test.ts +242 -0
- package/tests/inprocess-reviewer.test.ts +29 -19
- package/tests/issue-creation-skill.test.ts +61 -0
- package/tests/model-routing-authority.test.ts +16 -0
- package/tests/nan-provider.test.ts +471 -0
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-cli.test.ts +6 -120
- package/tests/native-review-parity-runtime.test.ts +100 -3
- package/tests/odd-integration.test.ts +67 -0
- package/tests/odd-phase-inference.test.ts +213 -0
- package/tests/odd-phase-loader.test.ts +253 -0
- package/tests/odd-phase.test.ts +307 -0
- package/tests/odd-routing-canonical-ratchet.test.ts +11 -6
- package/tests/odd-routing-contract.test.ts +86 -35
- package/tests/orchestrator-budget.test.ts +14 -39
- package/tests/orchestrator-rdd-ownership.test.ts +3 -3
- package/tests/overlay-repaint.test.ts +74 -0
- package/tests/package-manifest.test.ts +252 -115
- package/tests/packed-runner-owned-path.test.ts +46 -0
- package/tests/persona-single-channel.test.ts +6 -6
- package/tests/provider-defect-handoff.test.ts +3 -11
- package/tests/quiet-bash-runtime.test.ts +76 -0
- package/tests/quiet-tool-rendering.test.ts +409 -184
- package/tests/rdd-aware-verification-contract.test.ts +76 -1
- package/tests/rdd-status-line.test.ts +9 -4
- package/tests/resume-hint-extension.test.ts +122 -0
- package/tests/review-agent-end-preflight.test.ts +176 -12
- package/tests/review-candidate-owner-retry.test.ts +22 -1
- package/tests/review-candidate-view.test.ts +298 -0
- package/tests/review-contract-prompt.test.ts +108 -43
- package/tests/review-controller-lock-status.test.ts +0 -1
- package/tests/review-controller-native-routing.test.ts +611 -5
- package/tests/review-controller-workspace-root.test.ts +163 -4
- package/tests/review-host-relay-routing.test.ts +338 -2
- package/tests/review-integration-v2-forward.test.ts +200 -0
- package/tests/review-ledger-contract.test.ts +10 -34
- package/tests/review-reminder-receipt.test.ts +47 -1
- package/tests/review-risk-assessment.test.ts +498 -6
- package/tests/review-sidebar-state.test.ts +402 -0
- package/tests/run-test-suite.test.ts +124 -0
- package/tests/runtime-harness.mjs +145 -786
- package/tests/runtime-metrics-children.test.ts +16 -23
- package/tests/selection-engine.test.ts +421 -0
- package/tests/session-messaging-grants.test.ts +255 -0
- package/tests/session-worktree-registry.test.ts +77 -0
- package/tests/shell-bar.test.ts +382 -1
- package/tests/shell-card.test.ts +353 -1
- package/tests/shell-changes-view.test.ts +52 -0
- package/tests/shell-prompt.test.ts +94 -2
- package/tests/shell-sidebar-layout.test.ts +325 -21
- package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
- package/tests/shell-todo.test.ts +87 -1
- package/tests/shell-usage-view.test.ts +27 -0
- package/tests/shell-usage.test.ts +73 -0
- package/tests/skill-registry.test.ts +50 -1
- package/tests/startup-banner.test.ts +130 -2
- package/tests/stats-collector.test.ts +195 -0
- package/tests/stats-view.test.ts +202 -0
- package/tests/telemetry-trigger.test.ts +81 -20
- package/tests/theme-customization.test.ts +72 -0
- package/tests/vim-editor-adapter-host-resolution.test.ts +37 -0
- package/tests/vim-editor-adapter.test.ts +804 -0
- package/tests/vim-normal-engine.test.ts +101 -0
- package/tests/vim-operator-engine.test.ts +215 -0
- package/tests/vim-policy.test.ts +19 -0
- package/tests/vim-visual-engine.test.ts +52 -0
- package/tests/visual-customization-policy.test.ts +110 -0
- package/tests/visual-customize-view.test.ts +418 -0
- package/tests/visual-profiles.test.ts +87 -0
- package/tests/yolo-customize.test.ts +256 -0
- package/tests/yolo-mode-runtime.test.ts +161 -0
- package/tests/yolo-mode.test.ts +261 -0
- package/tests/yolo-session-policy.test.ts +59 -0
- package/themes/Gentle.json +2 -1
- package/themes/Gentleman-Cute.json +2 -1
- package/themes/Gentleman-Sexy.json +2 -1
- package/assets/agents/sdd-apply.md +0 -159
- package/assets/agents/sdd-archive.md +0 -228
- package/assets/agents/sdd-design.md +0 -49
- package/assets/agents/sdd-explore.md +0 -48
- package/assets/agents/sdd-init.md +0 -56
- package/assets/agents/sdd-onboard.md +0 -52
- package/assets/agents/sdd-proposal.md +0 -64
- package/assets/agents/sdd-remediate.md +0 -37
- package/assets/agents/sdd-research.md +0 -49
- package/assets/agents/sdd-spec.md +0 -192
- package/assets/agents/sdd-status.md +0 -54
- package/assets/agents/sdd-tasks.md +0 -108
- package/assets/agents/sdd-verify.md +0 -124
- package/assets/chains/sdd-full.chain.md +0 -83
- package/assets/chains/sdd-plan.chain.md +0 -56
- package/assets/chains/sdd-verify.chain.md +0 -43
- package/assets/sdd-orchestrator-workflow.md +0 -319
- package/assets/support/sdd-status-contract.md +0 -77
- package/docs/assets/diagrams/sdd-cycle.svg +0 -14
- package/extensions/sdd-init.ts +0 -816
- package/lib/openspec-deltas.ts +0 -156
- package/lib/sdd-preflight.ts +0 -1066
- package/lib/sdd-research-capabilities.ts +0 -94
- package/lib/sdd-status.ts +0 -26
- package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
- package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
- package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
- package/tests/openspec-deltas.test.ts +0 -209
- package/tests/sdd-agent-tools.test.ts +0 -156
- package/tests/sdd-archive-replay.test.ts +0 -82
- package/tests/sdd-classical-continuation.test.ts +0 -74
- package/tests/sdd-execution-routing-contract.test.ts +0 -44
- package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
- package/tests/sdd-native-managed-uptake.test.ts +0 -243
- package/tests/sdd-no-attempts-contract.test.ts +0 -15
- package/tests/sdd-odd-integration.test.ts +0 -33
- package/tests/sdd-optional-research.test.ts +0 -124
- package/tests/sdd-planning-routing-contract.test.ts +0 -45
- package/tests/sdd-preflight-rpc-input.test.ts +0 -125
- package/tests/sdd-preflight.test.ts +0 -541
- package/tests/sdd-research-capabilities.test.ts +0 -114
- package/tests/sdd-research-live.test.ts +0 -241
- package/tests/sdd-selection-transport.test.ts +0 -653
- package/tests/sdd-status.test.ts +0 -9
- package/tests/sdd-task-truth.test.ts +0 -43
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
decodeReviewStartV4,
|
|
18
18
|
decodeReviewStatusV3,
|
|
19
19
|
} from "../lib/review-integration-v2.ts";
|
|
20
|
+
import { NativeReviewCliV216, type ExecFileAdapter } from "../lib/native-review-cli.ts";
|
|
20
21
|
|
|
21
22
|
const DEV_FIXTURES = join(process.cwd(), "tests", "fixtures", "devbinary");
|
|
22
23
|
const V2_FIXTURES = join(process.cwd(), "contracts", "review-integration", "v2", "fixtures");
|
|
@@ -556,6 +557,205 @@ test("status/v6 decodes and enforces the intended-untracked selection submission
|
|
|
556
557
|
}
|
|
557
558
|
});
|
|
558
559
|
|
|
560
|
+
function queuedTargetStatusAdapter(): { adapter: ExecFileAdapter; calls: string[][] } {
|
|
561
|
+
const calls: string[][] = [];
|
|
562
|
+
return {
|
|
563
|
+
calls,
|
|
564
|
+
adapter: async (request) => {
|
|
565
|
+
calls.push([...request.arguments]);
|
|
566
|
+
return { stdout: JSON.stringify(currentStatusFixture("status-v5.captured.json")), stderr: "", exitCode: 0, signal: null, timedOut: false, outputLimitExceeded: false };
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function targetStatusClient(adapter: ExecFileAdapter): NativeReviewCliV216 {
|
|
572
|
+
return new NativeReviewCliV216(adapter, "/package/.gentle-ai/gentle-ai", 30_000, 1024 * 1024);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const SUBMITTED_PROVIDER_TOKENS = ["--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", "--intended-untracked-selection={{value}}"];
|
|
576
|
+
|
|
577
|
+
test("targetStatus appends the forwarded base-ref/committed-only pair after intended-untracked submission tokens", async () => {
|
|
578
|
+
const queue = queuedTargetStatusAdapter();
|
|
579
|
+
await targetStatusClient(queue.adapter).targetStatus({
|
|
580
|
+
cwd: "/repo",
|
|
581
|
+
baseRef: "deadbeef",
|
|
582
|
+
committedOnly: true,
|
|
583
|
+
intendedUntrackedSelection: { argumentTokens: SUBMITTED_PROVIDER_TOKENS, value: '{"selection":true}' },
|
|
584
|
+
});
|
|
585
|
+
assert.deepEqual(queue.calls[0], [
|
|
586
|
+
"review", "status", "--cwd", "/repo",
|
|
587
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
588
|
+
"--base-ref", "deadbeef", "--committed-only",
|
|
589
|
+
]);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
test("targetStatus leaves the intended-untracked submission argv unchanged when no baseRef is forwarded", async () => {
|
|
593
|
+
const queue = queuedTargetStatusAdapter();
|
|
594
|
+
await targetStatusClient(queue.adapter).targetStatus({
|
|
595
|
+
cwd: "/repo",
|
|
596
|
+
intendedUntrackedSelection: { argumentTokens: SUBMITTED_PROVIDER_TOKENS, value: '{"selection":true}' },
|
|
597
|
+
});
|
|
598
|
+
assert.deepEqual(queue.calls[0], [
|
|
599
|
+
"review", "status", "--cwd", "/repo",
|
|
600
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
601
|
+
]);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
test("targetStatus rejects a forwarded baseRef that conflicts with a base-ref the submission already carries", async () => {
|
|
605
|
+
const queue = queuedTargetStatusAdapter();
|
|
606
|
+
const tokensWithBaseRef = [...SUBMITTED_PROVIDER_TOKENS, "--base-ref=cafebabe"];
|
|
607
|
+
await assert.rejects(
|
|
608
|
+
() => targetStatusClient(queue.adapter).targetStatus({
|
|
609
|
+
cwd: "/repo", baseRef: "deadbeef", committedOnly: true,
|
|
610
|
+
intendedUntrackedSelection: { argumentTokens: tokensWithBaseRef, value: '{"selection":true}' },
|
|
611
|
+
}),
|
|
612
|
+
TypeError,
|
|
613
|
+
);
|
|
614
|
+
assert.equal(queue.calls.length, 0);
|
|
615
|
+
|
|
616
|
+
// Same value on both sides is not a conflict, but the submission still lacks
|
|
617
|
+
// --committed-only: T3 appends it so the selector stays complete.
|
|
618
|
+
const sameValueQueue = queuedTargetStatusAdapter();
|
|
619
|
+
await targetStatusClient(sameValueQueue.adapter).targetStatus({
|
|
620
|
+
cwd: "/repo", baseRef: "cafebabe", committedOnly: true,
|
|
621
|
+
intendedUntrackedSelection: { argumentTokens: tokensWithBaseRef, value: '{"selection":true}' },
|
|
622
|
+
});
|
|
623
|
+
assert.deepEqual(sameValueQueue.calls[0], [
|
|
624
|
+
"review", "status", "--cwd", "/repo",
|
|
625
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
626
|
+
"--base-ref=cafebabe",
|
|
627
|
+
"--committed-only",
|
|
628
|
+
]);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test("targetStatus appends --committed-only when the submission's same base-ref is missing it, and skips it when already present", async () => {
|
|
632
|
+
const missingQueue = queuedTargetStatusAdapter();
|
|
633
|
+
const tokensWithBaseRefOnly = [...SUBMITTED_PROVIDER_TOKENS, "--base-ref", "cafebabe"];
|
|
634
|
+
await targetStatusClient(missingQueue.adapter).targetStatus({
|
|
635
|
+
cwd: "/repo", baseRef: "cafebabe", committedOnly: true,
|
|
636
|
+
intendedUntrackedSelection: { argumentTokens: tokensWithBaseRefOnly, value: '{"selection":true}' },
|
|
637
|
+
});
|
|
638
|
+
assert.deepEqual(missingQueue.calls[0], [
|
|
639
|
+
"review", "status", "--cwd", "/repo",
|
|
640
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
641
|
+
"--base-ref", "cafebabe",
|
|
642
|
+
"--committed-only",
|
|
643
|
+
]);
|
|
644
|
+
|
|
645
|
+
const presentQueue = queuedTargetStatusAdapter();
|
|
646
|
+
const tokensWithBoth = [...SUBMITTED_PROVIDER_TOKENS, "--base-ref=cafebabe", "--committed-only=true"];
|
|
647
|
+
await targetStatusClient(presentQueue.adapter).targetStatus({
|
|
648
|
+
cwd: "/repo", baseRef: "cafebabe", committedOnly: true,
|
|
649
|
+
intendedUntrackedSelection: { argumentTokens: tokensWithBoth, value: '{"selection":true}' },
|
|
650
|
+
});
|
|
651
|
+
assert.deepEqual(presentQueue.calls[0], [
|
|
652
|
+
"review", "status", "--cwd", "/repo",
|
|
653
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
654
|
+
"--base-ref=cafebabe", "--committed-only=true",
|
|
655
|
+
]);
|
|
656
|
+
|
|
657
|
+
const committedOnlyQueue = queuedTargetStatusAdapter();
|
|
658
|
+
const tokensWithCommittedOnly = [...SUBMITTED_PROVIDER_TOKENS, "--committed-only"];
|
|
659
|
+
await targetStatusClient(committedOnlyQueue.adapter).targetStatus({
|
|
660
|
+
cwd: "/repo", baseRef: "cafebabe", committedOnly: true,
|
|
661
|
+
intendedUntrackedSelection: { argumentTokens: tokensWithCommittedOnly, value: '{"selection":true}' },
|
|
662
|
+
});
|
|
663
|
+
assert.deepEqual(committedOnlyQueue.calls[0], [
|
|
664
|
+
"review", "status", "--cwd", "/repo",
|
|
665
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
666
|
+
"--committed-only",
|
|
667
|
+
"--base-ref", "cafebabe",
|
|
668
|
+
]);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
test("targetStatus forwards --lineage on the submitted branch, skips a matching value, and rejects a conflicting one", async () => {
|
|
672
|
+
const forwardedQueue = queuedTargetStatusAdapter();
|
|
673
|
+
await targetStatusClient(forwardedQueue.adapter).targetStatus({
|
|
674
|
+
cwd: "/repo", lineageId: "review-abc123",
|
|
675
|
+
intendedUntrackedSelection: { argumentTokens: SUBMITTED_PROVIDER_TOKENS, value: '{"selection":true}' },
|
|
676
|
+
});
|
|
677
|
+
assert.deepEqual(forwardedQueue.calls[0], [
|
|
678
|
+
"review", "status", "--cwd", "/repo",
|
|
679
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
680
|
+
"--lineage", "review-abc123",
|
|
681
|
+
]);
|
|
682
|
+
|
|
683
|
+
const sameValueQueue = queuedTargetStatusAdapter();
|
|
684
|
+
const tokensWithLineage = [...SUBMITTED_PROVIDER_TOKENS, "--lineage=review-abc123"];
|
|
685
|
+
await targetStatusClient(sameValueQueue.adapter).targetStatus({
|
|
686
|
+
cwd: "/repo", lineageId: "review-abc123",
|
|
687
|
+
intendedUntrackedSelection: { argumentTokens: tokensWithLineage, value: '{"selection":true}' },
|
|
688
|
+
});
|
|
689
|
+
assert.deepEqual(sameValueQueue.calls[0], [
|
|
690
|
+
"review", "status", "--cwd", "/repo",
|
|
691
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
692
|
+
"--lineage=review-abc123",
|
|
693
|
+
]);
|
|
694
|
+
|
|
695
|
+
const conflictQueue = queuedTargetStatusAdapter();
|
|
696
|
+
await assert.rejects(
|
|
697
|
+
() => targetStatusClient(conflictQueue.adapter).targetStatus({
|
|
698
|
+
cwd: "/repo", lineageId: "review-other",
|
|
699
|
+
intendedUntrackedSelection: { argumentTokens: tokensWithLineage, value: '{"selection":true}' },
|
|
700
|
+
}),
|
|
701
|
+
TypeError,
|
|
702
|
+
);
|
|
703
|
+
assert.equal(conflictQueue.calls.length, 0);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
test("targetStatus forwards base-ref and lineage together on the submitted branch, base-ref first", async () => {
|
|
707
|
+
const queue = queuedTargetStatusAdapter();
|
|
708
|
+
await targetStatusClient(queue.adapter).targetStatus({
|
|
709
|
+
cwd: "/repo", baseRef: "deadbeef", committedOnly: true, lineageId: "review-abc123",
|
|
710
|
+
intendedUntrackedSelection: { argumentTokens: SUBMITTED_PROVIDER_TOKENS, value: '{"selection":true}' },
|
|
711
|
+
});
|
|
712
|
+
assert.deepEqual(queue.calls[0], [
|
|
713
|
+
"review", "status", "--cwd", "/repo",
|
|
714
|
+
"--contract=gentle-ai.review-integration/v2", "--next-transition=true", "--agent=pi", "--projection=workspace", '--intended-untracked-selection={"selection":true}',
|
|
715
|
+
"--base-ref", "deadbeef", "--committed-only",
|
|
716
|
+
"--lineage", "review-abc123",
|
|
717
|
+
]);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
test("targetStatus rejects a dangling or empty --base-ref/--lineage in the submitted tokens", async () => {
|
|
721
|
+
for (const tokens of [
|
|
722
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--base-ref"],
|
|
723
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--base-ref="],
|
|
724
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--lineage"],
|
|
725
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--lineage="],
|
|
726
|
+
]) {
|
|
727
|
+
const queue = queuedTargetStatusAdapter();
|
|
728
|
+
await assert.rejects(
|
|
729
|
+
() => targetStatusClient(queue.adapter).targetStatus({
|
|
730
|
+
cwd: "/repo",
|
|
731
|
+
intendedUntrackedSelection: { argumentTokens: tokens, value: '{"selection":true}' },
|
|
732
|
+
}),
|
|
733
|
+
TypeError,
|
|
734
|
+
JSON.stringify(tokens),
|
|
735
|
+
);
|
|
736
|
+
assert.equal(queue.calls.length, 0, JSON.stringify(tokens));
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
test("targetStatus rejects ambiguous or malformed provider selectors before executing STATUS", async () => {
|
|
741
|
+
for (const tokens of [
|
|
742
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--committed-only=false"],
|
|
743
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--committed-only=garbage"],
|
|
744
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--committed-only=true", "--committed-only=true"],
|
|
745
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--committed-only", "--committed-only=true"],
|
|
746
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--base-ref=deadbeef", "--base-ref", "deadbeef"],
|
|
747
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--lineage=review-a", "--lineage", "review-a"],
|
|
748
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--base-ref", "--lineage=review-a"],
|
|
749
|
+
[...SUBMITTED_PROVIDER_TOKENS, "--lineage", "--base-ref=deadbeef"],
|
|
750
|
+
]) {
|
|
751
|
+
const queue = queuedTargetStatusAdapter();
|
|
752
|
+
await assert.rejects(() => targetStatusClient(queue.adapter).targetStatus({
|
|
753
|
+
cwd: "/repo", intendedUntrackedSelection: { argumentTokens: tokens, value: '{"selection":true}' },
|
|
754
|
+
}), TypeError, JSON.stringify(tokens));
|
|
755
|
+
assert.equal(queue.calls.length, 0, JSON.stringify(tokens));
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
559
759
|
test("START/v4 accepts only its reviewing status continuation and preserves v3 strictness", () => {
|
|
560
760
|
for (const action of ["created", "replayed"] as const) {
|
|
561
761
|
const start = reviewingStartV4(action);
|
|
@@ -21,16 +21,7 @@ const GENTLE_SKILL = "skills/gentle-ai/SKILL.md";
|
|
|
21
21
|
const README = "README.md";
|
|
22
22
|
const TECHNICAL_REFERENCE = "docs/readme-reference.md";
|
|
23
23
|
const CHAIN = "assets/chains/4r-review.chain.md";
|
|
24
|
-
const SDD_WORKFLOW = "assets/sdd-orchestrator-workflow.md";
|
|
25
24
|
const WORKER = "assets/agents/gentle-ai-worker.md";
|
|
26
|
-
const CANONICAL_LIFECYCLE_SPECS = [
|
|
27
|
-
"openspec/specs/review-orchestration/spec.md",
|
|
28
|
-
"openspec/specs/review-transaction/spec.md",
|
|
29
|
-
] as const;
|
|
30
|
-
const HISTORICAL_LIFECYCLE_SPECS = [
|
|
31
|
-
"openspec/changes/complete-native-review-lifecycle/specs/review-orchestration/spec.md",
|
|
32
|
-
"openspec/changes/complete-native-review-lifecycle/specs/review-transaction/spec.md",
|
|
33
|
-
] as const;
|
|
34
25
|
|
|
35
26
|
function read(path: string): string {
|
|
36
27
|
return readFileSync(join(ROOT, path), "utf8");
|
|
@@ -85,7 +76,6 @@ const JUDGMENT_DAY_SEMANTIC_SURFACES = [
|
|
|
85
76
|
...JUDGES,
|
|
86
77
|
FIX_AGENT,
|
|
87
78
|
"assets/orchestrator-delegation.md",
|
|
88
|
-
SDD_WORKFLOW,
|
|
89
79
|
] as const;
|
|
90
80
|
|
|
91
81
|
const JUDGMENT_DAY_REJUDGMENT_PATTERNS = [
|
|
@@ -240,25 +230,14 @@ test("ordinary lens prompts contain the literal compact-v2 native result envelop
|
|
|
240
230
|
}
|
|
241
231
|
});
|
|
242
232
|
|
|
243
|
-
test("
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
assert.match(content, /forecast/i, path);
|
|
252
|
-
assert.doesNotMatch(content, /up to three failed targeted attempts|third failed attempt/i, path);
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
test("historical lifecycle change specs preserve their completed one-attempt design context", () => {
|
|
257
|
-
for (const path of HISTORICAL_LIFECYCLE_SPECS) {
|
|
258
|
-
const content = read(path);
|
|
259
|
-
assert.match(content, /at most one correction|one correction batch|After the one correction|GIVEN one exact ordinary correction|one validator and one final verification/i, path);
|
|
260
|
-
assert.doesNotMatch(content, /up to three failed targeted attempts/i, path);
|
|
261
|
-
}
|
|
233
|
+
test("retained provider review contract preserves bounded correction without declaring approval", () => {
|
|
234
|
+
const content = read(CANONICAL);
|
|
235
|
+
assert.match(content, /one correction transaction/i);
|
|
236
|
+
assert.match(content, /original budget/i);
|
|
237
|
+
assert.match(content, /`correction_required`/);
|
|
238
|
+
assert.doesNotMatch(content, /up to three failed targeted attempts|third failed attempt/i);
|
|
239
|
+
assert.match(content, /`reviewing`, `correction_required`, `validating`, `approved`, and `escalated`/);
|
|
240
|
+
assert.match(content, /Only severe `introduced`, `behavior-activated`, or `worsened` findings with valid proof can enter `correction_ids`/);
|
|
262
241
|
});
|
|
263
242
|
|
|
264
243
|
test("risk lens distinguishes trusted orchestration from concrete boundary bypasses", () => {
|
|
@@ -355,15 +334,13 @@ test("Judgment Day fix routing has one canonical shape and never falls back to g
|
|
|
355
334
|
[JD_SKILL, read(JD_SKILL)],
|
|
356
335
|
[JD_PROMPTS, fencedBlock(JD_PROMPTS, "## Fix Agent Prompt")],
|
|
357
336
|
["assets/orchestrator-delegation.md", read("assets/orchestrator-delegation.md")],
|
|
358
|
-
[SDD_WORKFLOW, read(SDD_WORKFLOW)],
|
|
359
337
|
] as const) {
|
|
360
338
|
assert.ok(content.includes(canonicalShape), `${path} must carry the canonical Judgment Day fix shape`);
|
|
361
339
|
assert.match(content, /requires no graph-v1 or native review lineage/i);
|
|
362
340
|
}
|
|
363
|
-
const routing =
|
|
341
|
+
const routing = read("assets/orchestrator-delegation.md");
|
|
364
342
|
assert.match(routing, /Judgment Day phase roles are never generic fallbacks\./);
|
|
365
343
|
assert.match(routing, /If the generic writer chain is unavailable, use the documented native generic fallback or stop\./);
|
|
366
|
-
assert.match(read(SDD_WORKFLOW), /\| default\s+\| balanced\s+\| SDD phase fallback; never a Judgment Day role\s+\|/);
|
|
367
344
|
});
|
|
368
345
|
|
|
369
346
|
test("orchestrator, injected skill, and technical reference defer RDD lifecycle ownership to Gentle AI", () => {
|
|
@@ -399,13 +376,12 @@ test("technical reference documents the dynamic runtime authority boundary witho
|
|
|
399
376
|
});
|
|
400
377
|
|
|
401
378
|
test("managed contracts retain no fresh lifecycle review directive", () => {
|
|
402
|
-
const managed = union([...ORCHESTRATOR,
|
|
379
|
+
const managed = union([...ORCHESTRATOR, WORKER, GENTLE_SKILL, README]);
|
|
403
380
|
for (const obsolete of [
|
|
404
381
|
"A fresh review still follows delegated implementation.",
|
|
405
382
|
"run a fresh-context review lens unless",
|
|
406
383
|
"Run a fresh review before pushing a code release",
|
|
407
384
|
]) assert.ok(!managed.includes(obsolete), `managed contracts retain ${obsolete}`);
|
|
408
|
-
assert.match(read(SDD_WORKFLOW), /SDD phase validation does not start ordinary review or Judgment Day/);
|
|
409
385
|
});
|
|
410
386
|
|
|
411
387
|
test("static 4R chain runs each selected lens once and owns no orchestration", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
|
-
import { consumeReviewMutation, pendingReviewMutation, recordReviewMutation, REVIEW_REMINDER_RECEIPT } from "../lib/review-reminder-receipt.ts";
|
|
3
|
+
import { consumeReviewMutation, pendingReviewMutation, pendingReviewMutationProfiles, recordReviewMutation, REVIEW_REMINDER_RECEIPT } from "../lib/review-reminder-receipt.ts";
|
|
4
4
|
|
|
5
5
|
function fixture() {
|
|
6
6
|
let sessionId = "parent";
|
|
@@ -60,3 +60,49 @@ test("malformed, unknown-version, and unmatched consumption entries do not estab
|
|
|
60
60
|
consumeReviewMutation(f.host, f.session, "/repo", "unknown", "acknowledged", "target");
|
|
61
61
|
assert.equal(pendingReviewMutation(f.session, "/repo"), captured);
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
// gentle-pi#1175 (T2): mutation receipts optionally carry the writer profile
|
|
65
|
+
// the runtime resolved, so ASSESS never has to trust a model re-declaration.
|
|
66
|
+
test("mutation receipts validate with or without runtime writer profile fields, never with empty ones", () => {
|
|
67
|
+
const f = fixture();
|
|
68
|
+
recordReviewMutation(f.host, f.session, "/repo", { source: "direct", toolName: "write", toolCallId: "plain" });
|
|
69
|
+
recordReviewMutation(f.host, f.session, "/repo", { source: "direct", toolName: "edit", toolCallId: "profiled", writerModelId: "anthropic/claude-sonnet-5", writerEffort: "high" });
|
|
70
|
+
recordReviewMutation(f.host, f.session, "/repo", { source: "subagent", taskId: "task-1", toolName: "write", toolCallId: "model-only", writerModelId: "openai/gpt-5-mini" });
|
|
71
|
+
assert.equal(f.entries.length, 3);
|
|
72
|
+
assert.deepEqual(pendingReviewMutationProfiles(f.session, "/repo"), [
|
|
73
|
+
{},
|
|
74
|
+
{ writerModelId: "anthropic/claude-sonnet-5", writerEffort: "high" },
|
|
75
|
+
{ writerModelId: "openai/gpt-5-mini" },
|
|
76
|
+
]);
|
|
77
|
+
const good = f.entries[1];
|
|
78
|
+
const data = good.data as Record<string, unknown>;
|
|
79
|
+
f.entries.length = 0;
|
|
80
|
+
for (const patch of [{ writerModelId: "" }, { writerEffort: " " }, { writerModelId: 1 }, { writerEffort: null }, { writerModel: "x" }]) {
|
|
81
|
+
f.entries.push({ ...good, data: { ...data, ...patch } });
|
|
82
|
+
}
|
|
83
|
+
assert.equal(pendingReviewMutation(f.session, "/repo"), undefined, "a present-but-empty or malformed profile field invalidates the receipt");
|
|
84
|
+
assert.deepEqual(pendingReviewMutationProfiles(f.session, "/repo"), []);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("recording drops unknown or blank profile fields instead of dropping the mutation evidence", () => {
|
|
88
|
+
const f = fixture();
|
|
89
|
+
recordReviewMutation(f.host, f.session, "/repo", { source: "direct", toolName: "write", toolCallId: "a", writerModelId: "", writerEffort: undefined });
|
|
90
|
+
assert.equal(f.entries.length, 1);
|
|
91
|
+
assert.deepEqual(Object.keys(f.entries[0].data as object).sort(), ["id", "kind", "root", "sessionId", "source", "toolCallId", "toolName"]);
|
|
92
|
+
assert.ok(pendingReviewMutation(f.session, "/repo"));
|
|
93
|
+
assert.deepEqual(pendingReviewMutationProfiles(f.session, "/repo"), [{}]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("pending writer profiles follow the same consumption watermark as pendingReviewMutation", () => {
|
|
97
|
+
const f = fixture();
|
|
98
|
+
recordReviewMutation(f.host, f.session, "/repo", { source: "direct", toolName: "write", toolCallId: "a", writerModelId: "gpt-5-mini" });
|
|
99
|
+
const captured = pendingReviewMutation(f.session, "/repo");
|
|
100
|
+
recordReviewMutation(f.host, f.session, "/repo", { source: "direct", toolName: "write", toolCallId: "b", writerModelId: "claude-sonnet-5", writerEffort: "high" });
|
|
101
|
+
consumeReviewMutation(f.host, f.session, "/repo", captured, "acknowledged", "target");
|
|
102
|
+
assert.deepEqual(pendingReviewMutationProfiles(f.session, "/repo"), [{ writerModelId: "claude-sonnet-5", writerEffort: "high" }]);
|
|
103
|
+
assert.deepEqual(pendingReviewMutationProfiles(f.session, "/sibling"), []);
|
|
104
|
+
consumeReviewMutation(f.host, f.session, "/repo", pendingReviewMutation(f.session, "/repo"), "nudged", "target");
|
|
105
|
+
assert.deepEqual(pendingReviewMutationProfiles(f.session, "/repo"), []);
|
|
106
|
+
f.switchSession("other");
|
|
107
|
+
assert.deepEqual(pendingReviewMutationProfiles(f.session, "/repo"), []);
|
|
108
|
+
});
|