gentle-pi 4.2.0 → 4.3.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 +336 -137
- package/assets/agents/gentle-ai-explore.md +1 -0
- package/assets/agents/gentle-ai-verify.md +6 -4
- package/assets/agents/gentle-ai-worker.md +7 -1
- package/assets/orchestrator-delegation-reference.md +163 -0
- package/assets/orchestrator-delegation.md +25 -176
- package/assets/orchestrator-tracking.md +1 -1
- package/assets/orchestrator-verification.md +14 -8
- package/assets/orchestrator-writer.md +5 -1
- package/assets/orchestrator.md +9 -9
- package/bin/gentle-shell.mjs +170 -22
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/README.md +7 -0
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/manifest.json +11 -2
- package/contracts/review-provider-contract-mirror/v1.4.0/bundle/routing/pi.md +92 -0
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-capabilities.baseline.json +8 -1
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-roles.baseline.json +1 -1
- package/docs/assets/diagrams/agent-orchestration.svg +54 -15
- package/docs/assets/diagrams/gentleman-workflow.svg +69 -11
- package/docs/assets/diagrams/measured-routing.svg +49 -0
- package/docs/assets/diagrams/native-review.svg +52 -12
- package/docs/assets/diagrams/odd-workflow.svg +128 -66
- package/docs/assets/diagrams/permission-modes.svg +51 -0
- package/docs/assets/features/audio-notifications.png +0 -0
- package/docs/assets/features/command-palette.png +0 -0
- package/docs/bundled-install.md +33 -3
- package/docs/delegated-verification.md +10 -8
- package/docs/gentle-agents-activity.md +11 -5
- package/docs/gentle-shell-uninstall.md +8 -1
- package/docs/gentle-shell.md +5 -4
- package/docs/install-wizard.md +8 -1
- package/docs/permission-modes.md +1 -1
- package/docs/readme-reference.md +107 -19
- package/docs/yolo-mode.md +21 -8
- package/extensions/codegraph-tools.ts +68 -6
- package/extensions/gentle-agents.ts +99 -12
- package/extensions/gentle-ai.ts +284 -52
- package/extensions/gentle-herdr-activity.ts +2 -3
- package/extensions/gentle-shell.ts +27 -12
- package/extensions/gentle-todo.ts +43 -9
- package/extensions/quiet-tools.ts +1 -1
- package/extensions/session-scratch.ts +365 -0
- package/extensions/startup-banner.ts +2 -3
- package/lib/agent-home.ts +8 -3
- package/lib/agents-protocol.ts +3 -0
- package/lib/agents-runner.ts +55 -6
- package/lib/agents-view.ts +3 -1
- package/lib/bounded-writer-admission.ts +51 -0
- package/lib/command-palette-catalog.ts +0 -4
- package/lib/commit-slice-signal.ts +100 -0
- package/lib/context-backstop.ts +84 -0
- package/lib/delivered-test-lint.ts +80 -0
- package/lib/destructive-command-guard.ts +120 -9
- package/lib/gauge-theme.ts +146 -0
- package/lib/gentle-ai-binary.ts +28 -7
- package/lib/gentle-shell-bundled.ts +39 -3
- package/lib/gentle-shell-import-pi.ts +233 -0
- package/lib/gentle-shell-launcher.ts +145 -11
- package/lib/gentle-shell-uninstall.ts +18 -1
- package/lib/herdr-activity.ts +24 -0
- package/lib/native-review-cli.ts +9 -1
- package/lib/orchestrator-discovery.ts +5 -1
- package/lib/orchestrator-presence.ts +9 -0
- package/lib/orchestrator-scope.ts +21 -3
- package/lib/provider-contract-bundle.ts +99 -53
- package/lib/review-candidate-view-owner.ts +29 -19
- package/lib/review-candidate-view.ts +36 -2
- package/lib/review-risk-assessment.ts +43 -38
- package/lib/review-session-standing-permission.ts +3 -1
- package/lib/session-profile-freeze.ts +30 -1
- package/lib/session-scratch.ts +474 -0
- package/lib/shell-bar.ts +25 -25
- package/lib/shell-changes-view.ts +80 -13
- package/lib/shell-gauge.ts +49 -6
- package/lib/shell-todo.ts +23 -4
- package/lib/shell-usage-view.ts +5 -1
- package/lib/shell-usage.ts +7 -7
- package/lib/verifier-admission.ts +29 -0
- package/lib/yolo-temp-scratch.ts +69 -0
- package/package.json +1 -2
- package/runtime/gentle-ai-binary.mjs +28 -7
- package/runtime/gentle-shell-bundled.mjs +38 -2
- package/runtime/gentle-shell-import-pi.mjs +234 -0
- package/runtime/gentle-shell-launcher.mjs +143 -9
- package/runtime/gentle-shell-uninstall.mjs +18 -1
- package/runtime/native-review-cli.mjs +8 -0
- package/runtime/review-risk-assessment.mjs +43 -38
- package/scripts/build-runtime-modules.mjs +1 -0
- package/scripts/bundled-engram-migration.mjs +125 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/installer-server.mjs +14 -4
- package/scripts/main-channel.mjs +3 -2
- package/scripts/run-test-suite.mjs +5 -0
- package/scripts/verify-package-files.mjs +19 -13
- package/skills/gentle-ai/SKILL.md +3 -3
- package/skills/work-unit-commits/SKILL.md +5 -3
- package/tests/agent-risk-escalation.test.ts +37 -10
- package/tests/agents-runner.test.ts +260 -3
- package/tests/autonomous-guard.test.ts +122 -0
- package/tests/bounded-writer-admission.test.ts +11 -3
- package/tests/card-content-policy.test.ts +19 -9
- package/tests/child-safety.test.ts +39 -0
- package/tests/codegraph-tools.test.ts +123 -2
- package/tests/command-palette.test.ts +14 -4
- package/tests/commit-slice-signal.test.ts +162 -0
- package/tests/context-backstop.test.ts +159 -0
- package/tests/delegate-for-reason-contract.test.ts +91 -24
- package/tests/delivered-test-lint.test.ts +105 -0
- package/tests/destructive-command-guard.test.ts +85 -1
- package/tests/engram-safety.test.ts +1 -1
- package/tests/fixtures/primary-session-prompt/gentleman.golden.md +143 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/README.md +29 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/manifest.json +83 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/orchestration/pi.md +70 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/routing/pi.md +92 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/lens.schema.json +16 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/refuter.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/targeted-validator.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/lens.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/refuter.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/targeted-validator.json +1 -0
- package/tests/gauge-theme.test.ts +120 -0
- package/tests/gentle-agents.test.ts +282 -3
- package/tests/gentle-ai-binary.test.ts +1 -1
- package/tests/gentle-ai-child-guards.test.ts +61 -0
- package/tests/gentle-ai-doctor-herdr.test.ts +94 -0
- package/tests/gentle-ai-installer.test.ts +51 -51
- package/tests/gentle-shell-bin.test.ts +163 -0
- package/tests/gentle-shell-bundled.test.ts +169 -6
- package/tests/gentle-shell-import-pi.test.ts +262 -0
- package/tests/gentle-shell-launcher.test.ts +79 -0
- package/tests/gentle-shell-uninstall.test.ts +30 -0
- package/tests/gentle-shell.test.ts +42 -9
- package/tests/gentle-todo.test.ts +179 -0
- package/tests/headless-delivery-question.test.ts +58 -0
- package/tests/herdr-activity.test.ts +34 -1
- package/tests/installer-server.test.ts +10 -2
- package/tests/installer-windows-bootstrap.test.ts +33 -4
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-parity.test.ts +22 -1
- package/tests/no-self-pause-contract.test.ts +5 -2
- package/tests/odd-integration.test.ts +5 -1
- package/tests/odd-routing-canonical-ratchet.test.ts +127 -162
- package/tests/odd-routing-contract.test.ts +115 -30
- package/tests/orchestrator-budget.test.ts +19 -8
- package/tests/orchestrator-discovery.test.ts +29 -0
- package/tests/orchestrator-rdd-ownership.test.ts +3 -1
- package/tests/orchestrator-scope.test.ts +66 -0
- package/tests/orchestrator-status-publication-contract.test.ts +12 -8
- package/tests/package-manifest.test.ts +24 -20
- package/tests/pi-update-launcher.test.ts +21 -2
- package/tests/primary-session-prompt.test.ts +101 -0
- package/tests/provider-contract-bundle.test.ts +118 -0
- package/tests/provider-contract-mirror.test.ts +24 -0
- package/tests/quiet-tool-rendering.test.ts +23 -0
- package/tests/rdd-aware-verification-contract.test.ts +29 -11
- package/tests/rdd-status-line.test.ts +79 -0
- package/tests/review-agent-end-preflight.test.ts +6 -0
- package/tests/review-candidate-view.test.ts +49 -0
- package/tests/review-contract-prompt.test.ts +5 -2
- package/tests/review-controller-native-routing.test.ts +14 -1
- package/tests/review-controller.test.ts +1 -1
- package/tests/review-host-relay-routing.test.ts +59 -2
- package/tests/review-loadout-rdd-off-sdk.test.ts +237 -0
- package/tests/review-owner-identity.test.ts +132 -0
- package/tests/review-risk-assessment.test.ts +163 -76
- package/tests/review-session-standing-permission-controller.test.ts +14 -1
- package/tests/review-session-standing-permission.test.ts +27 -1
- package/tests/run-test-suite.test.ts +15 -0
- package/tests/runtime-harness.mjs +16 -6
- package/tests/session-changes-shell.test.ts +1 -1
- package/tests/session-profile-freeze.test.ts +23 -0
- package/tests/session-scratch.test.ts +945 -0
- package/tests/session-subject-routing-contract.test.ts +2 -1
- package/tests/shell-bar.test.ts +7 -0
- package/tests/shell-changes-view.test.ts +137 -1
- package/tests/shell-gauge.test.ts +42 -0
- package/tests/small-task-inline-contract.test.ts +37 -0
- package/tests/standalone-config-home.test.ts +324 -0
- package/tests/startup-banner.test.ts +5 -4
- package/tests/startup-home-repo.test.ts +34 -0
- package/tests/support/orchestrator-modules.ts +1 -0
- package/tests/task-size-routing-contract.test.ts +69 -9
- package/tests/test-scratch-contract.test.ts +102 -0
- package/tests/test-strength-contract.test.ts +36 -1
- package/tests/tool-batching-contract.test.ts +64 -0
- package/tests/verification-proportionality.test.ts +46 -0
- package/tests/verifier-admission.test.ts +137 -0
- package/tests/verify-agent-contract.test.ts +8 -8
- package/tests/windows-hidden-processes.test.ts +5 -0
- package/tests/writer-edit-surface-scope.test.ts +3 -0
- package/tests/writer-feature-document-admission.test.ts +156 -0
- package/tests/yolo-mode.test.ts +18 -0
- package/tests/yolo-temp-scratch.test.ts +71 -0
- package/scripts/mirror-odd-routing.mjs +0 -242
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/orchestration/pi.md +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/targeted-validator.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/targeted-validator.json +0 -0
|
@@ -48,7 +48,11 @@ test("applicability, fallback and honest evidence flow through ODD actors", () =
|
|
|
48
48
|
test("retired SDD routes and assets are absent while ODD entry and generic workers remain", () => {
|
|
49
49
|
const core = read("assets/orchestrator.md");
|
|
50
50
|
const delegation = readDelegationDetail();
|
|
51
|
-
|
|
51
|
+
// S11 T3: deduplicated. The core's "ODD (Default Workflow, harness section
|
|
52
|
+
// above) is mandatory on every request" restated the harness; the mandatory
|
|
53
|
+
// ODD entry is stated once, in the harness that renders above the core.
|
|
54
|
+
assert.match(read("extensions/gentle-ai.ts"), /Default workflow: Organic Driven Development \(MANDATORY\)\nOrganic Driven Development \(ODD\) is the predefined workflow of this orchestrator\. Every request enters it/);
|
|
55
|
+
assert.doesNotMatch(core, /ODD \(Default Workflow, harness section above\)/);
|
|
52
56
|
assert.match(delegation, /generic writer chain is unavailable/);
|
|
53
57
|
for (const path of [
|
|
54
58
|
"assets/agents/gentle-ai-worker.md",
|
|
@@ -1,46 +1,36 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import {
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
ODD_ROUTING_SOURCE_PATH,
|
|
9
|
-
ODD_ROUTING_SOURCE_REPO,
|
|
10
|
-
assertCleanGentleAiCheckout,
|
|
11
|
-
parseOddRoutingFixture,
|
|
12
|
-
renderOddRoutingFixture,
|
|
13
|
-
resolveOddRoutingProvenance,
|
|
14
|
-
sha256Hex,
|
|
15
|
-
} from "../scripts/mirror-odd-routing.mjs";
|
|
5
|
+
import { __testing } from "../extensions/gentle-ai.ts";
|
|
6
|
+
import { PI_RUNTIME_IDENTITY, ROUTING_REQUIRED_SINCE_MINOR, verifyProviderContractBundleTree } from "../lib/provider-contract-bundle.ts";
|
|
7
|
+
import { PROVIDER_CONTRACT_LOCK_FILE, PROVIDER_CONTRACT_MIRROR_ROOT } from "../scripts/mirror-provider-contract.mjs";
|
|
16
8
|
|
|
17
9
|
// ---------------------------------------------------------------------------
|
|
18
10
|
// ODD routing drift ratchet (gentle-pi#1147 follow-up)
|
|
19
11
|
//
|
|
20
|
-
// gentle-
|
|
21
|
-
// `internal/components/agentguidance/routing.go`
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// `
|
|
25
|
-
//
|
|
26
|
-
// either from the canonical
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// assets, so drift stays visible as a deliberate diff.
|
|
12
|
+
// gentle-ai renders the always-on ODD routing block for Pi
|
|
13
|
+
// (`internal/components/agentguidance/routing.go`) and publishes it in the
|
|
14
|
+
// provider contract bundle as `routing/pi.md` (contract 1.4.0). The mirrored
|
|
15
|
+
// bundle (`contracts/review-provider-contract-mirror/`, refreshed with
|
|
16
|
+
// `scripts/mirror-provider-contract.mjs`) is the canonical reference: the
|
|
17
|
+
// harness ODD steps are rendered from it, and this ratchet fails when a
|
|
18
|
+
// mandatory-delegation clause is dropped either from the canonical block or
|
|
19
|
+
// from a Pi surface that restates it (the `assets/orchestrator*.md` modules
|
|
20
|
+
// stay Pi's own and keep their anchors).
|
|
30
21
|
// ---------------------------------------------------------------------------
|
|
31
22
|
|
|
32
23
|
const REPO_ROOT = join(import.meta.dirname, "..");
|
|
33
|
-
const
|
|
24
|
+
const MIRROR_ROOT = join(REPO_ROOT, ...PROVIDER_CONTRACT_MIRROR_ROOT.split("/"));
|
|
34
25
|
const DELEGATION_PATH = join(REPO_ROOT, "assets", "orchestrator-delegation.md");
|
|
35
26
|
const CORE_PATH = join(REPO_ROOT, "assets", "orchestrator.md");
|
|
36
|
-
const EXTENSION_PATH = join(REPO_ROOT, "extensions", "gentle-ai.ts");
|
|
37
27
|
|
|
38
28
|
const readRepo = (absolutePath: string): string => readFileSync(absolutePath, "utf8");
|
|
39
29
|
|
|
40
30
|
// A mandatory-delegation semantic anchor: the clause the canonical block must
|
|
41
31
|
// still carry, plus the wording each pi mirror surface is expected to carry.
|
|
42
|
-
// A canonical change that drops `canonical` is caught here
|
|
43
|
-
// because the
|
|
32
|
+
// A canonical change that drops `canonical` is caught here once the bundle is re-mirrored
|
|
33
|
+
// because the mirrored routing block no longer carries it.
|
|
44
34
|
interface RoutingAnchor {
|
|
45
35
|
label: string;
|
|
46
36
|
canonical: string;
|
|
@@ -49,23 +39,33 @@ interface RoutingAnchor {
|
|
|
49
39
|
|
|
50
40
|
const DELEGATION = "assets/orchestrator-delegation.md";
|
|
51
41
|
const CORE = "assets/orchestrator.md";
|
|
52
|
-
|
|
42
|
+
// The harness prompt rendered by extensions/gentle-ai.ts, including the ODD
|
|
43
|
+
// steps it takes from the mirrored routing/pi.md.
|
|
44
|
+
const EXTENSION = "rendered harness prompt";
|
|
53
45
|
|
|
54
46
|
const ANCHORS: readonly RoutingAnchor[] = [
|
|
55
47
|
{
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
// S9 canon change (gentle-ai 87a3b2eb): the heading dropped "Mandatory";
|
|
49
|
+
// a trigger is mandatory only once its condition truly holds.
|
|
50
|
+
label: "delegation triggers heading",
|
|
51
|
+
canonical: "### Delegation Triggers",
|
|
58
52
|
mirrors: [
|
|
59
|
-
{ surface: DELEGATION, includes: "####
|
|
60
|
-
|
|
53
|
+
{ surface: DELEGATION, includes: "#### Delegation Triggers" },
|
|
54
|
+
// Writer-rule fix: the core names them "Delegation Triggers" and says
|
|
55
|
+
// each holds only when its condition truly holds; the lazy port keeps
|
|
56
|
+
// the canonical heading.
|
|
57
|
+
{ surface: CORE, includes: "Delegation Triggers — each mechanism turns on only when its own trigger condition truly holds" },
|
|
61
58
|
],
|
|
62
59
|
},
|
|
63
60
|
{
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
// S9 canon change: "These triggers are mandatory, not advisory." read as a
|
|
62
|
+
// default to delegate (L8); a trigger now holds only when its condition
|
|
63
|
+
// truly holds, none by default, inline when in doubt.
|
|
64
|
+
label: "triggers hold only when their condition truly holds",
|
|
65
|
+
canonical: "Each trigger holds only when its condition truly holds, and only then is it mandatory, not advisory; no trigger holds by default, and when in doubt work inline.",
|
|
66
66
|
mirrors: [
|
|
67
|
-
{ surface: DELEGATION, includes: "
|
|
68
|
-
{ surface: EXTENSION, includes: "
|
|
67
|
+
{ surface: DELEGATION, includes: "Each trigger holds only when its condition truly holds, and only then is it mandatory, not advisory; no trigger holds by default, and when in doubt work inline." },
|
|
68
|
+
{ surface: EXTENSION, includes: "No trigger holds by default and, when in doubt, work inline" },
|
|
69
69
|
],
|
|
70
70
|
},
|
|
71
71
|
{
|
|
@@ -94,17 +94,64 @@ const ANCHORS: readonly RoutingAnchor[] = [
|
|
|
94
94
|
],
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
// S9 canon change: exploration never reads files an inline write reads anyway.
|
|
98
|
+
label: "never explore to read files an inline write reads anyway",
|
|
99
|
+
canonical: "Never explore to read files you will read anyway before writing inline.",
|
|
100
|
+
mirrors: [
|
|
101
|
+
{ surface: DELEGATION, includes: "Never explore to read files you will read anyway before writing inline." },
|
|
102
|
+
{ surface: CORE, includes: "never for reading before an inline write" },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
label: "writer trigger fires only for a named reason, never by size or file count",
|
|
107
|
+
// S9 canon change (gentle-ai 87a3b2eb): the canon caught up with Gentle
|
|
108
|
+
// Shell (gentle-shell#1731): the writer fires on named reasons
|
|
109
|
+
// (parallelism, context), never on size alone or a price ratio.
|
|
110
|
+
canonical: "**Writer trigger:** a writer is delegated only for a named reason, one bounded writer per unit",
|
|
103
111
|
mirrors: [
|
|
104
112
|
{ surface: DELEGATION, includes: "**Writer trigger (Writer rule):** a large task alone never delegates, and file count never fires this trigger" },
|
|
105
113
|
{ surface: CORE, includes: "**Writer rule** — never by file count or a large task alone" },
|
|
106
114
|
],
|
|
107
115
|
},
|
|
116
|
+
{
|
|
117
|
+
// S9 canon change: a writer unit is one independent behavior with its
|
|
118
|
+
// code, tests, and docs together, never split across writers.
|
|
119
|
+
label: "a writer unit is one independent behavior",
|
|
120
|
+
canonical: "A unit is one independent piece of behavior with its code, tests, and docs together",
|
|
121
|
+
mirrors: [
|
|
122
|
+
{ surface: DELEGATION, includes: "A unit is one independent piece of behavior with its code, tests, and docs together" },
|
|
123
|
+
{ surface: CORE, includes: "Tests, docs, and code of the same behavior are one unit, never separate writers" },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
// S9 canon change: a unit qualifies only with several minutes of work,
|
|
128
|
+
// because a subagent spends one to two minutes reading before it writes.
|
|
129
|
+
label: "a writer unit needs several minutes of work",
|
|
130
|
+
canonical: "several files or roughly 100+ changed lines",
|
|
131
|
+
mirrors: [
|
|
132
|
+
{ surface: DELEGATION, includes: "several files or ~100+ changed lines; a subagent spends 1-2 minutes reading before it writes" },
|
|
133
|
+
{ surface: CORE, includes: "several files or roughly 100+ changed lines" },
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
// S9 canon change (gentle-ai c3763aa7, C3): whoever writes reuses the
|
|
138
|
+
// existing shared-state helper and keeps its invariants.
|
|
139
|
+
label: "writers reuse the existing shared-state function",
|
|
140
|
+
canonical: "reuses the existing function for shared state",
|
|
141
|
+
mirrors: [
|
|
142
|
+
{ surface: DELEGATION, includes: "reuses the existing function for shared state and keeps its invariants (ordering, uniqueness, validation, normalization)" },
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
// S9 canon change (gentle-ai c3763aa7, C8): PRESERVE diffs each existing
|
|
147
|
+
// command that shares the changed code against the previous code.
|
|
148
|
+
label: "differential check against the previous code",
|
|
149
|
+
canonical: "run a differential check",
|
|
150
|
+
mirrors: [
|
|
151
|
+
{ surface: DELEGATION, includes: "**Differential check (PRESERVE):**" },
|
|
152
|
+
{ surface: EXTENSION, includes: "run a differential check" },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
108
155
|
{
|
|
109
156
|
label: "task size decides the route (gentle-shell#1494)",
|
|
110
157
|
canonical: "### Task Size",
|
|
@@ -114,11 +161,14 @@ const ANCHORS: readonly RoutingAnchor[] = [
|
|
|
114
161
|
],
|
|
115
162
|
},
|
|
116
163
|
{
|
|
117
|
-
label: "verification trigger is reserved for high
|
|
118
|
-
|
|
164
|
+
label: "verification trigger is reserved for a named high-risk item",
|
|
165
|
+
// S9 canon change (gentle-ai 6b9a71ef): only a change whose Risk line
|
|
166
|
+
// names a high-risk item gets the independent verifier.
|
|
167
|
+
canonical: "**Verification trigger:** only a change whose Risk line names a high-risk item gets an independent verifier",
|
|
119
168
|
mirrors: [
|
|
120
|
-
|
|
121
|
-
{ surface:
|
|
169
|
+
// Verification by named risk: the core row names the escalate-only verifier.
|
|
170
|
+
{ surface: CORE, includes: "**Verification rule** — only a named high-risk item (assess `escalate`) → independent `gentle-ai-verify`" },
|
|
171
|
+
{ surface: DELEGATION, includes: "only a change whose Risk line names a high-risk item (Task Size, assess `escalate`) gets an independent `gentle-ai-verify` run" },
|
|
122
172
|
],
|
|
123
173
|
},
|
|
124
174
|
{
|
|
@@ -138,10 +188,12 @@ const ANCHORS: readonly RoutingAnchor[] = [
|
|
|
138
188
|
label: "long-session backstop",
|
|
139
189
|
canonical: "**Long-session backstop:**",
|
|
140
190
|
// Gentle Shell intentionally leads the canon here: a parent-context token
|
|
141
|
-
// backstop replaces the tool-call count (tracked by gentle-ai#5139)
|
|
191
|
+
// backstop replaces the tool-call count (tracked by gentle-ai#5139), and the
|
|
192
|
+
// runtime measures it and signals it instead of the canon's advisory ~150k
|
|
193
|
+
// estimate (harness roadmap R1), so the Pi anchor names the runtime signal.
|
|
142
194
|
mirrors: [
|
|
143
|
-
{ surface: DELEGATION, includes: "**Context backstop:** when the
|
|
144
|
-
{ surface: CORE, includes: "**Context backstop** —
|
|
195
|
+
{ surface: DELEGATION, includes: "**Context backstop:** when the runtime signals it" },
|
|
196
|
+
{ surface: CORE, includes: "**Context backstop** — when the runtime signals it" },
|
|
145
197
|
],
|
|
146
198
|
},
|
|
147
199
|
{
|
|
@@ -173,58 +225,52 @@ const ANCHORS: readonly RoutingAnchor[] = [
|
|
|
173
225
|
],
|
|
174
226
|
},
|
|
175
227
|
{
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
228
|
+
// S9 canon change: step 6 honors its triggers only when their conditions truly hold.
|
|
229
|
+
label: "ODD step 6 honors its delegation triggers when their conditions hold",
|
|
230
|
+
canonical: "honoring its delegation triggers when their conditions truly hold",
|
|
231
|
+
mirrors: [{ surface: EXTENSION, includes: "honoring its delegation triggers when their conditions truly hold" }],
|
|
179
232
|
},
|
|
180
233
|
];
|
|
181
234
|
|
|
182
235
|
// Condensed trigger rows that live only in the always-on core prompt; the
|
|
183
236
|
// canonical RenderRouting block does not carry the incident or verification
|
|
184
|
-
// rows, so they are mirror-only and not
|
|
237
|
+
// rows, so they are mirror-only and not anchors of the canonical routing block.
|
|
185
238
|
const CORE_ONLY_TRIGGERS = [
|
|
186
239
|
"**Incident rule** — diagnose wrong cwd/worktree/git/tooling incidents separately",
|
|
187
|
-
"**Verification rule** — high
|
|
240
|
+
"**Verification rule** — only a named high-risk item (assess `escalate`) → independent `gentle-ai-verify`",
|
|
188
241
|
] as const;
|
|
189
242
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
);
|
|
195
|
-
|
|
243
|
+
// The verified mirrored routing/pi.md: the bundle tree must pass the strict
|
|
244
|
+
// decoder, so a hand edit to the canonical block fails here as well as in
|
|
245
|
+
// `pnpm run check:provider-contract`.
|
|
246
|
+
function canonicalRoutingBlock(): string {
|
|
247
|
+
const lock = JSON.parse(readFileSync(join(MIRROR_ROOT, PROVIDER_CONTRACT_LOCK_FILE), "utf8")) as { contract_semver: string };
|
|
248
|
+
const bundle = verifyProviderContractBundleTree(join(MIRROR_ROOT, `v${lock.contract_semver}`, "bundle"));
|
|
249
|
+
const block = bundle.routing.get(PI_RUNTIME_IDENTITY);
|
|
250
|
+
assert.ok(block !== undefined, `the mirrored provider contract ${bundle.contractSemver} carries no routing/${PI_RUNTIME_IDENTITY}.md`);
|
|
251
|
+
return block;
|
|
196
252
|
}
|
|
197
253
|
|
|
198
254
|
// ---------------------------------------------------------------------------
|
|
199
|
-
// 1 —
|
|
255
|
+
// 1 — The canonical reference is the verified mirrored bundle
|
|
200
256
|
// ---------------------------------------------------------------------------
|
|
201
257
|
|
|
202
|
-
test("the canonical routing
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
assert.equal(header.source_repo, ODD_ROUTING_SOURCE_REPO);
|
|
209
|
-
assert.equal(header.source_path, ODD_ROUTING_SOURCE_PATH);
|
|
210
|
-
assert.match(header.source_commit ?? "", /^[0-9a-f]{40}$/, "fixture must record the gentle-ai source commit");
|
|
211
|
-
assert.match(header.generated_at ?? "", /^\d{4}-\d{2}-\d{2}T/, "fixture must record a generation date");
|
|
212
|
-
assert.match(header.block_sha256 ?? "", /^[0-9a-f]{64}$/, "fixture must record the block body digest");
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test("the fixture digest matches its block body byte-for-byte", () => {
|
|
216
|
-
const { header, body } = parseOddRoutingFixture(readRepo(FIXTURE_PATH));
|
|
217
|
-
assert.equal(header.block_sha256, sha256Hex(body), "the fixture body was hand-edited without regenerating its digest");
|
|
258
|
+
test("the canonical routing reference is the verified mirrored routing/pi.md (contract >= 1.4.0)", () => {
|
|
259
|
+
const lock = JSON.parse(readFileSync(join(MIRROR_ROOT, PROVIDER_CONTRACT_LOCK_FILE), "utf8")) as { contract_semver: string; entries: Record<string, string> };
|
|
260
|
+
const [major, minor] = lock.contract_semver.split(".").map(Number);
|
|
261
|
+
assert.ok(major === 1 && (minor ?? 0) >= ROUTING_REQUIRED_SINCE_MINOR, `mirrored contract ${lock.contract_semver} predates routing`);
|
|
262
|
+
assert.match(lock.entries[`routing/${PI_RUNTIME_IDENTITY}.md`] ?? "", /^[0-9a-f]{64}$/);
|
|
263
|
+
assert.ok(canonicalRoutingBlock().startsWith("## Implementation Routing\n"));
|
|
218
264
|
});
|
|
219
265
|
|
|
220
266
|
// ---------------------------------------------------------------------------
|
|
221
|
-
// 2 —
|
|
267
|
+
// 2 — The canonical block carries every mandatory anchor
|
|
222
268
|
// ---------------------------------------------------------------------------
|
|
223
269
|
|
|
224
|
-
test("the canonical
|
|
225
|
-
const body =
|
|
270
|
+
test("the canonical routing block carries every mandatory-delegation anchor", () => {
|
|
271
|
+
const body = canonicalRoutingBlock();
|
|
226
272
|
for (const anchor of ANCHORS) {
|
|
227
|
-
assert.ok(body.includes(anchor.canonical), `canonical
|
|
273
|
+
assert.ok(body.includes(anchor.canonical), `canonical routing block dropped anchor: ${anchor.label}`);
|
|
228
274
|
}
|
|
229
275
|
});
|
|
230
276
|
|
|
@@ -236,7 +282,7 @@ test("the pi mirror surfaces carry every mapped mandatory-delegation anchor", ()
|
|
|
236
282
|
const surfaces: Record<string, string> = {
|
|
237
283
|
[DELEGATION]: readRepo(DELEGATION_PATH),
|
|
238
284
|
[CORE]: readRepo(CORE_PATH),
|
|
239
|
-
[EXTENSION]:
|
|
285
|
+
[EXTENSION]: __testing.buildGentlePrompt("gentleman"),
|
|
240
286
|
};
|
|
241
287
|
for (const anchor of ANCHORS) {
|
|
242
288
|
for (const mirror of anchor.mirrors) {
|
|
@@ -256,84 +302,3 @@ test("the always-on core prompt carries the condensed incident and verification
|
|
|
256
302
|
assert.ok(core.includes(row), `assets/orchestrator.md is missing condensed trigger row: ${JSON.stringify(row)}`);
|
|
257
303
|
}
|
|
258
304
|
});
|
|
259
|
-
|
|
260
|
-
// ---------------------------------------------------------------------------
|
|
261
|
-
// 4 — Regeneration provenance: idempotent and fail-closed
|
|
262
|
-
// ---------------------------------------------------------------------------
|
|
263
|
-
|
|
264
|
-
// The fixture header claims provenance from the source commit, so regeneration
|
|
265
|
-
// must be byte-reproducible: the same gentle-ai commit must not produce a
|
|
266
|
-
// date-only diff. `generated_at` therefore tracks the source commit's committer
|
|
267
|
-
// date instead of wall-clock time.
|
|
268
|
-
test("fixture rendering is idempotent and derives generated_at from the source commit", () => {
|
|
269
|
-
const sourceCommit = "e7729359fd9d6cb691ed2a88e8f72b1372f7c92e";
|
|
270
|
-
const committerDate = "2026-09-18T13:49:03+02:00";
|
|
271
|
-
const fakeGit = (args: readonly string[]): string => {
|
|
272
|
-
switch (args[0]) {
|
|
273
|
-
case "status":
|
|
274
|
-
return "";
|
|
275
|
-
case "rev-parse":
|
|
276
|
-
return `${sourceCommit}\n`;
|
|
277
|
-
case "show":
|
|
278
|
-
return `${committerDate}\n`;
|
|
279
|
-
default:
|
|
280
|
-
throw new Error(`unexpected git invocation: ${args.join(" ")}`);
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
const block = `${ODD_ROUTING_BLOCK_PREFIX}\n\n- a canonical clause\n`;
|
|
284
|
-
const first = renderOddRoutingFixture(block, resolveOddRoutingProvenance("/fake/gentle-ai", fakeGit));
|
|
285
|
-
const second = renderOddRoutingFixture(block, resolveOddRoutingProvenance("/fake/gentle-ai", fakeGit));
|
|
286
|
-
assert.equal(first, second, "rendering the same source commit twice must be byte-identical");
|
|
287
|
-
const { header } = parseOddRoutingFixture(first);
|
|
288
|
-
assert.equal(header.source_commit, sourceCommit);
|
|
289
|
-
assert.equal(
|
|
290
|
-
header.generated_at,
|
|
291
|
-
committerDate,
|
|
292
|
-
"generated_at must track the source commit's committer date, not wall-clock time",
|
|
293
|
-
);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// A dirty checkout would render uncommitted content under the committed
|
|
297
|
-
// provenance claim, so the mirror fails closed before rendering and names the
|
|
298
|
-
// tracked paths. Untracked files (the odd/tasks notes) must never block.
|
|
299
|
-
test("the mirror fails closed on a dirty source checkout and names the tracked paths", () => {
|
|
300
|
-
const recordedCalls: string[][] = [];
|
|
301
|
-
const cleanGit = (args: readonly string[]): string => {
|
|
302
|
-
recordedCalls.push([...args]);
|
|
303
|
-
switch (args[0]) {
|
|
304
|
-
case "status":
|
|
305
|
-
return "";
|
|
306
|
-
case "rev-parse":
|
|
307
|
-
return `${"a".repeat(40)}\n`;
|
|
308
|
-
case "show":
|
|
309
|
-
return "2026-09-18T13:49:03+02:00\n";
|
|
310
|
-
default:
|
|
311
|
-
throw new Error(`unexpected git invocation: ${args.join(" ")}`);
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
resolveOddRoutingProvenance("/fake/gentle-ai", cleanGit);
|
|
315
|
-
assert.deepEqual(
|
|
316
|
-
recordedCalls.find((args) => args[0] === "status"),
|
|
317
|
-
["status", "--porcelain", "--untracked-files=no"],
|
|
318
|
-
"the dirty guard must ignore untracked files",
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
assert.doesNotThrow(() => assertCleanGentleAiCheckout(""));
|
|
322
|
-
const dirtyGit = (args: readonly string[]): string => {
|
|
323
|
-
if (args[0] === "status") {
|
|
324
|
-
return " M internal/components/agentguidance/routing.go\n M scripts/other.mjs\n";
|
|
325
|
-
}
|
|
326
|
-
throw new Error(`unexpected git invocation: ${args.join(" ")}`);
|
|
327
|
-
};
|
|
328
|
-
assert.throws(
|
|
329
|
-
() => resolveOddRoutingProvenance("/fake/gentle-ai", dirtyGit),
|
|
330
|
-
(error: unknown) => {
|
|
331
|
-
assert.ok(error instanceof Error);
|
|
332
|
-
assert.match(
|
|
333
|
-
error.message,
|
|
334
|
-
/gentle-ai checkout is dirty; provenance would be unverifiable: internal\/components\/agentguidance\/routing\.go, scripts\/other\.mjs\. Commit or stash first\./,
|
|
335
|
-
);
|
|
336
|
-
return true;
|
|
337
|
-
},
|
|
338
|
-
);
|
|
339
|
-
});
|