gentle-pi 4.1.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 +352 -128
- 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/engram-guard +1 -1
- package/bin/gentle-shell.mjs +188 -35
- 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/auto-permission-answers.md +25 -0
- package/docs/auto-permission-evidence.md +39 -0
- package/docs/bundled-install.md +40 -8
- 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 +20 -12
- package/docs/install-wizard.md +17 -10
- package/docs/permission-modes.md +51 -6
- package/docs/readme-reference.md +155 -31
- package/docs/session-profile-format.md +39 -0
- package/docs/yolo-mode.md +21 -7
- package/extensions/ask-user-choice.ts +4 -0
- package/extensions/ask-user-question.ts +6 -1
- package/extensions/codegraph-tools.ts +68 -6
- package/extensions/gentle-agents.ts +146 -28
- package/extensions/gentle-ai.ts +380 -68
- package/extensions/gentle-herdr-activity.ts +2 -3
- package/extensions/gentle-shell.ts +53 -43
- package/extensions/gentle-todo.ts +62 -18
- 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/auto-permission-answers.ts +133 -0
- package/lib/auto-permission-classifier.ts +215 -36
- package/lib/auto-permission-evidence.ts +88 -0
- package/lib/auto-permission-memory.ts +69 -0
- package/lib/auto-permission-runtime.ts +251 -35
- package/lib/auto-permission-tool-context.ts +61 -0
- 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 +215 -28
- package/lib/gauge-theme.ts +146 -0
- package/lib/gentle-ai-binary.ts +28 -7
- package/lib/gentle-shell-bundled.ts +62 -8
- package/lib/gentle-shell-import-pi.ts +233 -0
- package/lib/gentle-shell-launcher.ts +146 -12
- package/lib/gentle-shell-uninstall.ts +18 -1
- package/lib/herdr-activity.ts +24 -0
- package/lib/model-routing-authority.ts +6 -1
- 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/selection-engine.ts +4 -1
- package/lib/session-profile-freeze.ts +40 -16
- package/lib/session-profile-persistence.ts +187 -0
- 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-changes.ts +2 -0
- package/lib/shell-gauge.ts +49 -6
- package/lib/shell-prompt.ts +6 -4
- package/lib/shell-todo.ts +39 -8
- package/lib/shell-usage-view.ts +5 -1
- package/lib/shell-usage.ts +7 -7
- package/lib/verifier-admission.ts +29 -0
- package/lib/vim-editor-adapter.ts +22 -4
- package/lib/yolo-temp-scratch.ts +69 -0
- package/package.json +5 -6
- package/runtime/gentle-ai-binary.mjs +28 -7
- package/runtime/gentle-shell-bundled.mjs +61 -7
- package/runtime/gentle-shell-import-pi.mjs +234 -0
- package/runtime/gentle-shell-launcher.mjs +144 -10
- 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/install-wizard-preview.mjs +2 -2
- package/scripts/installer-downloads.mjs +7 -7
- package/scripts/installer-preflight.mjs +1 -1
- package/scripts/installer-server.mjs +14 -4
- package/scripts/main-channel.mjs +3 -2
- package/scripts/pi-update.mjs +223 -0
- 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/append-system-prompt-route.test.ts +18 -1
- package/tests/auto-mode-human-answers.test.ts +68 -0
- package/tests/auto-mode-invariants.test.ts +23 -5
- package/tests/auto-mode-sdk.test.ts +29 -2
- package/tests/auto-permission-answers.test.ts +170 -0
- package/tests/auto-permission-classifier.test.ts +353 -2
- package/tests/auto-permission-compound.test.ts +24 -0
- package/tests/auto-permission-evidence.test.ts +88 -0
- package/tests/auto-permission-memory.test.ts +46 -0
- package/tests/auto-permission-public-regressions.test.ts +111 -0
- package/tests/auto-permission-runtime.test.ts +270 -12
- package/tests/autonomous-guard.test.ts +208 -16
- package/tests/bounded-writer-admission.test.ts +11 -3
- package/tests/bundled-install.test.ts +3 -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 +35 -7
- 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 +443 -8
- 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 +180 -17
- package/tests/gentle-shell-bundled.test.ts +235 -38
- package/tests/gentle-shell-import-pi.test.ts +262 -0
- package/tests/gentle-shell-launcher.test.ts +97 -13
- package/tests/gentle-shell-uninstall.test.ts +30 -0
- package/tests/gentle-shell.test.ts +113 -14
- package/tests/gentle-todo.test.ts +258 -27
- package/tests/headless-delivery-question.test.ts +58 -0
- package/tests/herdr-activity.test.ts +34 -1
- package/tests/install-wizard.test.ts +2 -2
- package/tests/installer-posix-bootstrap.test.ts +8 -8
- package/tests/installer-preflight.test.ts +3 -3
- package/tests/installer-probes.test.ts +8 -8
- package/tests/installer-runner.test.ts +14 -13
- package/tests/installer-server.test.ts +13 -5
- package/tests/installer-windows-bootstrap.test.ts +33 -4
- package/tests/main-channel-upgrade.test.ts +7 -1
- 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/odd-runtime-delegation-gate.test.ts +4 -1
- 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 +28 -24
- package/tests/pi-update-launcher.test.ts +106 -0
- package/tests/pi-update.test.ts +186 -0
- 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 +131 -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/selection-engine.test.ts +35 -5
- package/tests/session-changes-shell.test.ts +1 -1
- package/tests/session-profile-freeze.test.ts +37 -1
- package/tests/session-profile-persistence.test.ts +334 -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-changes.test.ts +18 -0
- package/tests/shell-gauge.test.ts +42 -0
- package/tests/shell-todo.test.ts +15 -12
- 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/vim-editor-adapter.test.ts +41 -15
- 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 +24 -1
- 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
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
## Implementation Routing
|
|
2
|
+
|
|
3
|
+
Organic Driven Development (ODD) is the predefined workflow of this orchestrator. Every request enters it, on every runtime, without the user asking for a workflow, a plan, or task tracking. Never describe this workflow only when asked about it: run it.
|
|
4
|
+
|
|
5
|
+
### ODD protocol (MANDATORY, in this order, on every request)
|
|
6
|
+
|
|
7
|
+
1. **Authorize.** Investigation, explanation, review, comparison, and proposal-only requests stay read-only: no writer, apply, or implementation artifacts. Ambiguous or conditional change intent (unclear whether a change is authorized at all) gets one clarification; stop and wait. A user saying they may stop you or resume later asks for notes and separate commits, not a stop.
|
|
8
|
+
2. **Explore.** Explore existing code and requirements first, proportionately to the request, before proposing or writing anything. Do not delegate exploration of files you will read anyway to work inline; explore only for a map you need to decide or route.
|
|
9
|
+
3. **Resolve uncertainty.** Recommend optional research only for a named uncertainty; ask one focused user question only for a real unresolved product decision, then stop and wait; use at most one scoped read-only assumption challenge for a high-consequence unproven premise.
|
|
10
|
+
4. **Classify.** Size the task by the orchestrator's Task Size section; its criteria and resume test alone decide small or large, and small work follows its small path.
|
|
11
|
+
5. **Track before the first write.** For large authorized implementation, create `odd/tasks/<feature-name>.md` and its Engram mirror `odd/<feature-name>/tasks` automatically, then create or rebuild the visible `todo` list from the reconciled feature tasks, all before the first source write and without asking permission for tasks or storage. Tell the user in one line which feature document was created and how many tasks it holds. The document is the specification subagents read, in this order: a two- or three-line header; `## Specs` with numbered `S#` that quote the user's exact strings, error messages, and examples verbatim, never summarized and never adding unrequested requirements; `## Tasks` with one line per task (ID, linked `S#`, route, commit); `## Log` last, where `L1` is the user's original request verbatim and later user corrections, evidence, and decisions are appended. A requirement change appends its verbatim Log entry, rewrites only the affected `S#`, and reopens only its task.
|
|
12
|
+
6. **Implement task by task.** Hand off by reference, never by paraphrase: name the document, task, and specs (for example `Spec: odd/tasks/<feature>.md, T2, S3-S4`), tell workers to read until `## Log`, and ask which `S#` were covered. Without a feature document, include the user's request verbatim. Verify reads the whole document, runs the spec's examples the parent authorized, against isolated state when they mutate data, and returns a verdict per `S#`. When the user reports a failure, reproduce it before deciding it already works. Each test asserts every observable effect of the rule it covers (exit code, exact stdout and stderr, and that rejected input leaves stored data and counters unchanged), covers the cases the rule itself names (its examples, boundaries, and errors), and checks through the public interface, never internal storage. When you add or change a command, option, or message, update the help text and docs that describe it. Route each task through the orchestrator's Mechanisms, honoring its delegation triggers when their conditions truly hold, with applicable test-first development and checks. No trigger holds by default and, when in doubt, work inline; executing past a fired trigger inline is a routing defect even if the work succeeds. Check an item off only after its outcome and checks were observed; update the file, mirror, and visible `todo` projection after every task transition and material plan change. Commit work units in reviewable slices of about 400 authored changed lines on the feature branch (branch first when on the default branch), with tests and docs alongside the behavior and a Conventional Commit message; a unit whose Risk line names a high-risk item gets its own commit so native review can take it alone, and the last slice closes the feature. Record the commit that holds each task in the feature document as evidence. Work-unit commits on the feature branch are part of authorized large ODD implementation; push, pull request creation, and merge remain the user's decisions.
|
|
13
|
+
7. **Close.** Report the verified outcome, every failed, skipped, or pending check, and the next step. Before writing `Risk: none`, check whether your diff changes code that existing behavior the request did not mention also uses (shared options, parsers, helpers); if it does, that is item 3. Never end with a tracked task pending unless you quote the user's explicit stop. An applicable quick check runs once; an unavailable verifier or subagent is reported as unavailable, never retried or escalated into extra ceremony. Partial, blocked, unavailable, or exhausted proof becomes one **Needs your decision** result naming the open blockers or missing proof, never more verification; that result is a valid stop, hedged wording is not. The native review candidate is a work-unit commit or a PR slice, never a TODO checkbox and never the accumulated feature branch; native review runs only under the user-owned RDD switch.
|
|
14
|
+
|
|
15
|
+
Phase reporting: the Gentle Shell prompt label is inferred automatically from the primary session's tool activity (reads show `exploring`, edits `implementing`, test runs `checking`, user questions `deciding`). When the `gentle_odd_phase` tool is available, use it to refine that label with phases tools cannot show (`authorizing`, `researching`, `deciding`, `closing`): call `gentle_odd_phase` only when the primary session's ODD phase actually changes, never per tool call or on a fixed cadence, and never from a subagent. It drives the Gentle Shell prompt label only.
|
|
16
|
+
|
|
17
|
+
Resume an interrupted feature with `mem_context`, then project- and feature-scoped `mem_search`, then `mem_get_observation` for the full document, then the task file itself; reconcile before continuing the next unfinished task. Detail for steps 3–7: `orchestrator-delegation.md` and `orchestrator-memory.md`.
|
|
18
|
+
|
|
19
|
+
### Task Size
|
|
20
|
+
|
|
21
|
+
A task is **small** when all three hold:
|
|
22
|
+
|
|
23
|
+
1. **Understood**: the outcome is specified, what and where to change is known, and no product or design decision is open; prove it within one bounded read batch (at most 3 calls, approximately 10k tokens).
|
|
24
|
+
2. **Contained risk**: it touches no high-risk item below.
|
|
25
|
+
3. **Resumable from the diff**: resume test, if the session stopped now, someone could finish the work from the original request and `git diff` alone.
|
|
26
|
+
|
|
27
|
+
The number of files, commands or tests, fixes, or a requested todo list never decides size. A task is **large** only when the resume test fails: several sessions, waiting on something external, separate deliverables the user wants apart (never several features asked at once), or requirements too long to survive compaction. A large task gets tracking; it gets a writer only by the Writer trigger, otherwise the parent works inline.
|
|
28
|
+
|
|
29
|
+
Small path: work inline. Read and edit, several files of one understood change included; observe RED inline before the fix; run the focused test and the suite inline, once each. No explore, worker, or verifier; no feature document, mirror, or commits unless the user asks; a todo list is optional.
|
|
30
|
+
|
|
31
|
+
**High risk**: a mistake would be hard to detect, hard to undo, or reaches beyond the change. It touches (1) data or irreversible effects: migrations, changing or deleting existing stored data, persisted format changes (not saving new records); (2) security: auth, permissions, credentials, secrets, guards, sandbox; (3) contracts others consume: changing or removing public API, CLI flags, config formats, published exports, or mirrored prompts or contracts others already consume (not adding a flag, command, or optional field; requested changes are not high risk by themselves; unrequested breaks in shared code are); (4) concurrency: locks, races, async ordering; (5) delivery or environment: installers, release, CI, deploy, dependencies; (6) no test would catch a regression in what changes. Count "unclear" as high only when a bounded look cannot tell whether (1)-(5) apply. When RDD is on, the native assess tier decides native review and you may only raise it; it never adds an independent verifier by itself, only a named high-risk item does.
|
|
32
|
+
|
|
33
|
+
**Risk line**: close every code change, small path or delegated, with `Risk: item N (reason)` or `Risk: none` per the list; any item fires the Verification trigger. Before writing `Risk: none`, check whether the diff changes code that existing behavior the request did not mention also uses (shared options, parsers, helpers); if it does, that is item 3.
|
|
34
|
+
|
|
35
|
+
After explicit change intent is established, route work for the requested outcome with the smallest useful topology. Every authorized change takes exactly one implementation route: direct inline or delegated direct.
|
|
36
|
+
|
|
37
|
+
- **Direct inline:** small tasks, by the Task Size section, and large tasks without a Writer trigger reason, which follow their logbook. Decide or verify with one parallel batch (1 maximum), at most 3 calls and approximately 10k tokens of evidence. Use bounded search/line ranges, not whole large files. One understood change stays inline even when it spans files, and its focused test and suite run inline.
|
|
38
|
+
- **Delegated direct:** understanding that needs more evidence or more than approximately 5 sequential lookups takes one read-only explorer, then task size is re-evaluated; a writer is delegated only by the Writer trigger.
|
|
39
|
+
- File count, changed lines, size, or perceived risk alone never forces a heavier route.
|
|
40
|
+
- These are implementation routes, not a ban on per-action delegation. Tests, builds, installs, and review actors may still use fresh workers without changing the selected route.
|
|
41
|
+
|
|
42
|
+
### Delegation Triggers
|
|
43
|
+
|
|
44
|
+
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. Each mechanism turns on only by its own trigger. When one fires, stop and delegate through the runtime's subagent mechanism before continuing; executing past a fired trigger inline is a routing defect even if the work succeeds. When it resolves, re-evaluate task size and stay small while the three criteria hold. Delegation keeps the parent context thin enough to orchestrate; it does not slow the work down.
|
|
45
|
+
|
|
46
|
+
- **Ask trigger:** an open product or design decision gets one focused user question; stop and wait.
|
|
47
|
+
- **Mapping trigger:** when understanding exceeds the inline batch budget or needs more than approximately 5 sequential lookups, delegate one read-only explorer before deciding or writing anything. Return a handoff of at most approximately 2k tokens with path:line evidence; use one parent spot check (1 maximum). Do not reread the entire mapped evidence. Never explore to read files you will read anyway before writing inline.
|
|
48
|
+
- **Verification trigger:** only a change whose Risk line names a high-risk item gets an independent verifier after the change's own checks, as rarely as possible; a native tier, an unclear or failed assessment, and the writer's model or effort never add one by themselves. Otherwise whoever made the change runs its focused test and suite inline, small tasks included.
|
|
49
|
+
- **Tracking trigger:** a large task gets the feature document, its mirror, and work-unit commits.
|
|
50
|
+
- **Writer trigger:** a writer is delegated only for a named reason, one bounded writer per unit: (a) parallelism: two or more independent units of behavior with disjoint edit surfaces, each needing several minutes of work (several files or roughly 100+ changed lines; a subagent spends one to two minutes reading before it writes), launched together; (b) context: the long-session backstop below. A unit is one independent piece of behavior with its code, tests, and docs together: never split one behavior into test, docs, and code writers, and a delivery that depends on another is not independent; several small independent changes run inline, one after another. Units of about 100 changed lines or less always run inline: writing them costs less than starting workers. Never delegate a writer for size, a large task alone, file count, or a price ratio; file count never fires this trigger. Without a reason, the parent works inline, following its logbook (feature document, mirror, work-unit commits).
|
|
51
|
+
- **Parallel writers:** work in another repository goes in a fresh worktree based on that repository's main, never in a checkout someone else may be using. Parallel units in the same local repository run in the same tree, each with declared disjoint edit surfaces: the parent owns git; writers run no repository-wide formatters, generators, or installs; a shared file has one owner or the parent edits it at the end; the parent checks each writer's diff against its surface before accepting it; and one seam check runs at the end (the full suite, once). Units that need the same file use isolated worktrees.
|
|
52
|
+
- **Invariants:** each writer prompt names the existing helpers its unit must reuse for shared state (for example the function that adds an entry other commands also add) and the invariants they keep (ordering, uniqueness, validation, normalization); units built in parallel cannot see each other, so the prompt is the only place they learn them. Whoever writes, inline or delegated, reuses the existing function for shared state and never re-implements that step with a different shape.
|
|
53
|
+
- **Preparation trigger:** when the Writer trigger delegates a write, reading that prepares it, and broad research or context compression, delegate together with or ahead of the write instead of filling the parent context. Inline writes read inline.
|
|
54
|
+
- **Output budget:** keep parent bash output bounded to counts, --stat, tail, or summaries. On a large task, delegate long suites and builds and return concise observed results, including failures.
|
|
55
|
+
- **Long-session backstop:** at approximately 150k parent-context tokens, pause and delegate the next bounded unit. This is advisory context guidance, not mechanically observed or enforced; do not claim runtime telemetry or enforcement.
|
|
56
|
+
- **Route declaration:** for large work, record the chosen route per task (inline or delegated) and the trigger evidence in the feature document, so skipped delegation is observable instead of silent.
|
|
57
|
+
- These triggers only choose between direct inline and delegated direct inside the organic flow.
|
|
58
|
+
|
|
59
|
+
### Organic Driven Development
|
|
60
|
+
|
|
61
|
+
Use this flow for direct and delegated work. Explore the existing code and requirements first, proportionately to the request; the mutation-authorization guard above still applies.
|
|
62
|
+
|
|
63
|
+
This section is the reference detail for the protocol above.
|
|
64
|
+
|
|
65
|
+
- Recommend optional research only for a named uncertainty. If declined, continue within authorized scope only where safe without the missing evidence; disclose unresolved uncertainty and pause affected unsafe decisions. Offer a concise proposal only when a real scope or product decision needs it. Neither research nor a proposal is mandatory.
|
|
66
|
+
- Establish the problem, intended outcome, constraints, and current evidence; inspect relevant code. Adapt depth to uncertainty and consequence, not a fixed questionnaire or mandatory rounds. The parent owns product decisions: ask one focused user question only for a real unresolved product decision, then stop and wait. Workers return gaps to the parent rather than assuming choices.
|
|
67
|
+
- When the question needs external evidence, use available authorized documentation/web tools and prefer primary sources. Attribute material claims to source URLs or code locations; distinguish verified facts, assumptions, contradictions, freshness, and gaps. If tools are unavailable, disclose limitations without inventing access or evidence; pause only unsafe decisions dependent on missing evidence.
|
|
68
|
+
- Return concise findings, recommendation, tradeoffs, open questions, and implementation implications. When delegating research, forward these research instructions to a fresh general exploration/research worker through existing delegation; do not create a specialized research agent. Keep research read-only; its findings do not authorize implementation or require new persistence or readiness machinery.
|
|
69
|
+
- Use at most one scoped independent read-only assumption challenge for a high-consequence unproven premise, even in a small security-critical change. Name the premise, evidence, and consequence; do not start a debate loop. Deterministic failures need fixes, not model debate. The native RDD refuter owns native review claims; never duplicate or bypass it.
|
|
70
|
+
- Validate consequential premises against available evidence before building; reuse relevant sibling investigation instead of repeating it. Run focused checks during iteration and all applicable full checks at task closure. Keep spend proportional without a hard spend or line gate; preserve the applicable test-first policy, native RDD, safety, and consent requirements.
|
|
71
|
+
- Size every task by the Task Size section; small work creates no durable task artifacts, and counts of files, commands, tests, fixes, or lines never make it large. For large authorized implementation, automatically create the feature document after exploration, without a task or storage permission prompt.
|
|
72
|
+
- Use about 400 authored changed lines per ODD task only as a planning heuristic, counting additions plus deletions; prefer the smallest coherent behavior with its tests and docs. This is not a task acceptance criterion, hard cap, counter-trigger, automatic stop, forced split, or RDD trigger. If the correct, clear solution naturally exceeds it, briefly explain why and continue without size-only rework loops. Never delete spaces, blank lines, or comments for cosmetic line savings; never omit tests, minify, add gratuitous abstractions, or split artificially to fit the heuristic. Forward this same advisory-only instruction when delegating tasks to subagents. The delivery budget below reads the accumulated branch, not this per-task heuristic. Existing PR size gates remain unchanged; continue under existing repository policy.
|
|
73
|
+
- Keep `odd/tasks/<feature-name>.md` and an Engram recovery copy under topic `odd/<feature-name>/tasks`, scoped to the current project. Use a descriptive filename-safe feature name. Reuse the same feature identity; never overwrite another feature. Keep one feature document, not a separate plan file or topic. It is the specification subagents read by reference, so keep this fixed order, stable content first and the growing log last: a header of two or three lines with the objective, branch, delivery strategy, and test runner; `## Specs` with numbered `S1`..`Sn` requirements and their authorized scope, acceptance criteria, and applicable checks, quoting the user's exact strings, error messages, commands, and examples verbatim in the user's language (number and order them, but never summarize or reword those fragments; Do not add requirements the user never asked for, and mark assumptions as assumptions); `## Tasks` with one line per task, using stable task IDs, with its linked `S#`, route (inline or delegated), status, and commit identity or RED/GREEN evidence; `## Log` as the logbook, where `L1` holds the user's original request verbatim, later user corrections are logged verbatim with their date, and problem analysis, rationale for meaningful accepted changes, verification evidence, progress, and next step are appended. Routine corrections stay brief; no exhaustive decision journal. Mirror the full current document and repository-relative file locator, not just a summary or completion notice.
|
|
74
|
+
- Accepted user, review, or verification changes automatically update affected intent and TODOs, unlike a process that regenerates every artifact: a requirement change appends its verbatim `## Log` entry, rewrites only the affected `S#`, and reopens only its linked task; preserve valid completed and unrelated work; add genuinely new tasks or reopen invalidated items with a reason, and revise their checks. Findings alone never authorize scope expansion or automatic acceptance. Business scope changes still require user authorization. Check off only observed outcomes with applicable proof; record failed, unavailable, skipped, or pending checks honestly. Checkboxes grant no approval or receipt.
|
|
75
|
+
- Read back both writes; they are not atomic. If Engram is unavailable, preserve local progress and explicitly mark the mirror pending; do not claim success or block unrelated safe work. Resynchronize when available. If a file write is unsafe or unavailable, preserve existing state and report the limitation. Preserve both versions on irreconcilable edits and ask only about the real conflict.
|
|
76
|
+
- On resume, use `mem_context`, then `mem_search` scoped to the current project and feature, and `mem_get_observation` for the full saved document; read the actual task file. Do not infer active work from the newest global memory. Reconcile current requirements, code, and proof before resuming the next unfinished task; preserve pending mirrors and conflicting edits.
|
|
77
|
+
- Before implementation or resume, the parent reads both the actual file and full observation, reconciles them, and passes the locator, task IDs, and linked `S#`; workers read the document until `## Log` before edits and report which `S#` they covered. Small work without a document still receives its authorized scope and checks.
|
|
78
|
+
- Apply one default test-first policy to ODD behavior changes: when a relevant runnable deterministic test and clear expected outcome exist, observe RED before implementation, implement GREEN, then refactor while tests stay green. Write one RED test per requested rule. For every existing command or option the change touches, add one test proving its previous behavior still holds; add no other cases. An existing behavior counts as touched when it shares the code you changed (options, parsers, helpers, validation). Before finishing, run a differential check: for each existing command that shares the code you changed, run it with the new or changed input (for example a new option passed to a sibling subcommand) against the previous code (a copy of the base commit in a `mktemp -d` scratch directory, never committed as a test; committed tests never read git history or a fixed commit) and against your change, and compare stdout, stderr, and exit code; any difference nobody asked for is a regression to fix and pin with a test. Tests or frameworks being present alone do not establish applicability. For passive documentation, unavailable runners, or no meaningful runnable RED, explain the exception and run proportionate functional or structural checks. Keep test scratch state (temporary directories, `HOME`, `TMPDIR`, tool caches) outside the worktree in a `mktemp -d` directory; when it must live inside the worktree, use a path git excludes and remove it before finishing, so no tool cache or scratch path remains in the change. Subagents never delete scratch: they report its exact paths, and the parent removes outside scratch after use, including the paths a worker or verifier reports. Forward the applicable runner, this test discipline, and the evidence or exception to workers; never invent RED/GREEN evidence or a runner.
|
|
79
|
+
- Preserve existing native risk selection and applicable functional verification. Run applicable functional checks per task, not a review cycle per TODO checkbox. The native review candidate is a work-unit commit or a PR slice, never a TODO checkbox and never the accumulated feature branch. After each work-unit commit, when RDD is enabled, run `gentle-ai review assess --cwd <repo> --agent pi --base-ref <last reviewed boundary> --committed-only --json` on that commit and read `review_due` and `review_due_reason`. When `review_due` is true (`high_risk`), or when the user explicitly asks for a review, execute the returned `next_transition.command` verbatim with your `--lenses`/`--lenses-reason` selection appended: it is the exact preflight STATUS for the same `--base-ref`/`--committed-only` selectors; follow the transitions it returns, and the reviewed boundary advances to this commit once that review is acknowledged. When `review_due` is false, record `review_due_reason` and continue: `passive` needs no review and the boundary advances; `under_budget` stays pending in the slice until a later commit reaches the budget; `already_reviewed` means this exact range is already covered by terminal authority. When a high-risk unit follows pending `under_budget` commits, close the open slice first, assessing and reviewing it as the slice, so the high-risk commit is then reviewed alone against the new boundary. The first boundary is the branch point, and every reviewed boundary becomes the next base. Record per commit the assessed tier and outcome: granted, declined, passive, under budget, already reviewed, or unavailable. An unavailable or failed assessment never lowers the tier: treat the commit as due and run the preflight STATUS with `--base-ref <last reviewed boundary> --committed-only`. Never infer low risk from a failed assessment, and keep existing risk, consent, and authority unchanged. Never skip an existing delivery gate. A task list or assumption challenge never enables RDD, replaces its roles or candidate consent, or adds an execution harness.
|
|
80
|
+
- Delivery follows work units. At feature-document creation, forecast authored changed lines (additions plus deletions, generated files excluded) from the task list, and keep a running count from work-unit commits. Choose one delivery strategy per feature: `ask-on-risk` (default), `auto-chain`, `single-pr`, or `exception-ok`. When the forecast or the running count exceeds about 400 authored changed lines, apply the chosen strategy before the next commit: `ask-on-risk` asks once for the chain strategy, `stacked-to-main` or `feature-branch-chain`; `auto-chain` asks only for a missing chain strategy and slices automatically. Cache both choices, and record slice boundaries, which commits each pull request holds, in the feature document. Resolve the `work-unit-commits` and `chained-pr` skills by registry name before planning or creating any pull request, never hardcode their paths.
|
|
81
|
+
- When RDD is enabled, first use the existing native candidate risk assessment (`gentle-ai review assess --cwd <repo> --agent pi --json`). Passive/low uses silent structural checks with no reviewer or consent ceremony. Medium/high relays the existing candidate consent and follows the native plan: native review runs only on grant; a decline continues under ordinary policy. Do not substitute model judgment, task size, or defect severity to lower prospective candidate risk; never infer low risk from a failed assessment. You may raise a candidate you know is high risk with `--escalate-item <1-6> --escalate-reason "<one line>"` on that same assessment command, citing the high-risk list; never lower it. Follow native continuations and authority without bypassing gates. When RDD is disabled, do not start or prompt for RDD; ordinary checks remain.
|
|
82
|
+
|
|
83
|
+
### Receipt-driven development is user-owned
|
|
84
|
+
|
|
85
|
+
The user controls receipt-driven development with a switch: `gentle-ai review mode enable|disable|status`.
|
|
86
|
+
|
|
87
|
+
- It is **on by default and opt-out**. An unset preference permits review without recording a user decision; explicit global or clone-local OFF still wins. Candidate consent remains separate from the mode default.
|
|
88
|
+
- `status` is read-only. It reports the deciding source and the effective mode, and changes nothing. A `default` deciding source means nobody has chosen, so the effective mode is on.
|
|
89
|
+
- When the user asks to stop using receipt-driven development, run `disable`. Do not argue, do not work around it, and do not propose alternatives first.
|
|
90
|
+
- While it is disabled, keep implementing organically through direct inline or delegated direct: do not start reviews, do not retry, do not reactivate it, and do not fall back to any retired path.
|
|
91
|
+
- Delivery under a disabled switch follows ordinary repository policy and reports `disabled/unmanaged`, never a fabricated approval.
|
|
92
|
+
- Never toggle the mode automatically or persist a preference just because the default is on. Never enable receipt-driven development on the user's behalf unless the user explicitly asks for it.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://gentle-ai.dev/schema/review/reviewer/v1",
|
|
4
|
+
"title": "Gentle AI reviewer result",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["subject_hash", "inspection", "findings", "evidence"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"subject_hash": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
|
|
10
|
+
"inspection": {"type": "object", "additionalProperties": false, "required": ["status", "paths"], "allOf": [{"if": {"properties": {"status": {"const": "unavailable"}}, "required": ["status"]}, "then": {"required": ["reason"]}}], "properties": {"status": {"type": "string", "enum": ["completed", "unavailable"], "description": "\"completed\" asserts every changed_path_manifest path was actually inspected. \"unavailable\" asserts the candidate could not be inspected at all and requires a non-empty reason; this is the typed admission-completeness signal; evidence prose is not a substitute for it."}, "paths": {"type": "array", "description": "Complete unique unordered set of every changed_path_manifest.path when status is \"completed\"; empty when status is \"unavailable\".", "uniqueItems": true, "items": {"type": "string", "minLength": 1}}, "reason": {"type": "string", "minLength": 1, "description": "Required and non-empty only when status is \"unavailable\": why the candidate could not be inspected."}}},
|
|
11
|
+
"lens": {"type": "string", "description": "Optional selected lens binding. Omission canonicalizes to the selected subject lens.", "enum": ["risk", "resilience", "readability", "reliability", "review-risk", "review-resilience", "review-readability", "review-reliability"]},
|
|
12
|
+
"findings": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["location", "severity", "claim", "proof_refs"], "allOf": [{"if": {"properties": {"severity": {"enum": ["BLOCKER", "CRITICAL"]}}, "required": ["severity"]}, "then": {"required": ["evidence_class", "causal_disposition"]}}], "properties": {"id": {"type": "string", "pattern": "^R[1-4]-[A-Za-z0-9][A-Za-z0-9._-]*$", "description": "Optional explicit ID; omit it to receive a native-assigned ID. When present it must carry the prefix bound to the selected lens, not the selection order: review-risk=R1-, review-readability=R2-, review-reliability=R3-, review-resilience=R4-."}, "lens": {"type": "string", "enum": ["risk", "resilience", "readability", "reliability", "review-risk", "review-resilience", "review-readability", "review-reliability"]}, "location": {"type": "string", "description": "One canonical repository-relative path:line or inclusive path:start-end span.", "pattern": "^.+:[1-9][0-9]*(?:-[1-9][0-9]*)?$"}, "severity": {"type": "string", "enum": ["BLOCKER", "CRITICAL", "WARNING", "SUGGESTION"]}, "claim": {"type": "string", "minLength": 1}, "proof_refs": {"type": "array", "minItems": 1, "items": {"type": "string", "pattern": "\\S", "not": {"pattern": "^\\s*(?:[nN]/[aA]|[nN][aA]|[nN][oO][nN][eE]|[tT][oO][dD][oO]|[tT][bB][dD]|[pP][aA][sS][sS]|[pP][aA][sS][sS][eE][dD]|[sS][uU][cC][cC][eE][sS][sS]|[pP][lL][aA][cC][eE][hH][oO][lL][dD][eE][rR])\\s*$"}}}, "evidence_class": {"type": "string", "enum": ["deterministic", "inferential", "insufficient"]}, "causal_disposition": {"type": "string", "enum": ["introduced", "behavior-activated", "worsened", "pre-existing", "base-only", "unknown"]}}}},
|
|
13
|
+
"evidence": {"type": "array", "minItems": 1, "items": {"type": "string", "pattern": "\\S", "not": {"pattern": "^\\s*(?:[nN]/[aA]|[nN][aA]|[nN][oO][nN][eE]|[tT][oO][dD][oO]|[tT][bB][dD]|[pP][aA][sS][sS]|[pP][aA][sS][sS][eE][dD]|[sS][uU][cC][cC][eE][sS][sS]|[pP][lL][aA][cC][eE][hH][oO][lL][dD][eE][rR])\\s*$"}}}
|
|
14
|
+
},
|
|
15
|
+
"examples": [{"subject_hash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "inspection": {"status": "completed", "paths": ["internal/example.go"]}, "findings": [], "evidence": ["reviewed the complete candidate scope"]}, {"subject_hash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "inspection": {"status": "unavailable", "paths": [], "reason": "the immutable inspection command timed out before any path could be read"}, "findings": [], "evidence": ["inspection was unavailable; see inspection.reason"]}]
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://gentle-ai.dev/schema/review/refuter/v1","title":"Gentle AI refuter result","type":"object","additionalProperties":false,"required":["refuter_request_hash","results"],"properties":{"refuter_request_hash":{"$ref":"#/$defs/sha256"},"results":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["finding_id","outcome","proof_refs"],"properties":{"finding_id":{"type":"string"},"outcome":{"type":"string","enum":["corroborated","refuted","inconclusive"]},"proof_refs":{"type":"array","minItems":1,"items":{"type":"string","pattern":"\\S"}}}}}},"$defs":{"sha256":{"type":"string","pattern":"^sha256:[0-9a-f]{64}$"}},"examples":[{"refuter_request_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","results":[]}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://gentle-ai.dev/schema/review/validator/v1","title":"Gentle AI targeted validator result","type":"object","additionalProperties":false,"required":["targeted_validation_request_hash","correction_target_identity","original_criteria","correction_regression","follow_ups"],"properties":{"targeted_validation_request_hash":{"$ref":"#/$defs/sha256"},"correction_target_identity":{"$ref":"#/$defs/sha256"},"original_criteria":{"$ref":"#/$defs/check"},"correction_regression":{"$ref":"#/$defs/check"},"follow_ups":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["observation","proof_refs"],"properties":{"observation":{"type":"string"},"proof_refs":{"type":"array","minItems":1,"items":{"type":"string","pattern":"\\S"}}}}}},"allOf":[{"if":{"properties":{"correction_regression":{"type":"object","properties":{"passed":{"const":false}},"required":["passed"]}},"required":["correction_regression"]},"then":{"properties":{"correction_regression":{"type":"object","properties":{"regressions":{"minItems":1}},"required":["regressions"]}}}}],"$defs":{"sha256":{"type":"string","pattern":"^sha256:[0-9a-f]{64}$"},"check":{"type":"object","additionalProperties":false,"required":["passed","evidence"],"properties":{"passed":{"type":"boolean","description":"true means the named check passed; false means the named check failed."},"evidence":{"type":"array","minItems":1,"items":{"type":"string"}},"regressions":{"type":"array","items":{"$ref":"#/$defs/regression"},"description":"Required with at least one entry when this is correction_regression and passed is false: one entry per observed regression, omitted or empty otherwise."},"inspection":{"$ref":"#/$defs/inspection"}},"allOf":[{"if":{"properties":{"inspection":{"type":"object","properties":{"status":{"const":"unavailable"}},"required":["status"]}},"required":["inspection"]},"then":{"properties":{"inspection":{"type":"object","required":["reason"]}}}}]},"inspection":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","enum":["completed","unavailable"],"description":"completed means this check's verdict came from actually reading the frozen candidate trees. unavailable means it did not, and this check produced no verdict."},"reason":{"type":"string","minLength":1,"description":"Required when status is unavailable: why the frozen candidate trees could not be read."}},"description":"Optional. Omit this field entirely when inspection completed normally -- every check that predates this field already assumed that default. Never infer unavailable from evidence wording; only this typed field marks a check inconclusive."},"regression":{"type":"object","additionalProperties":false,"required":["location","claim","proof_refs"],"properties":{"id":{"type":"string","description":"Optional explicit ID; omit it to receive a native-assigned ID."},"location":{"type":"string","description":"One canonical repository-relative path:line or inclusive path:start-end span.","pattern":"^.+:[1-9][0-9]*(?:-[1-9][0-9]*)?$"},"claim":{"type":"string","minLength":1},"proof_refs":{"type":"array","minItems":1,"items":{"type":"string","pattern":"\\S"}}}}},"examples":[{"targeted_validation_request_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","correction_target_identity":"sha256:1111111111111111111111111111111111111111111111111111111111111111","original_criteria":{"passed":true,"evidence":["acceptance test passed"]},"correction_regression":{"passed":true,"evidence":["regression test passed"]},"follow_ups":[]}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"subject_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","inspection":{"status":"completed","paths":["internal/example.go"]},"findings":[],"evidence":["reviewed the complete candidate scope"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"refuter_request_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","results":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"targeted_validation_request_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","correction_target_identity":"sha256:1111111111111111111111111111111111111111111111111111111111111111","original_criteria":{"passed":true,"evidence":["acceptance test passed"]},"correction_regression":{"passed":true,"evidence":["regression test passed"]},"follow_ups":[]}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { mkdtempSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { clearGaugePaintCache, gaugePaintFor, resolveGaugePaint } from "../lib/gauge-theme.ts";
|
|
7
|
+
import { paintGauge } from "../lib/shell-gauge.ts";
|
|
8
|
+
|
|
9
|
+
// Issue #2222: reserved `vars` keys (`gauge.*`) give the gauges their own
|
|
10
|
+
// roles and glyphs; everything else falls back. `vars` is the only open map
|
|
11
|
+
// in Pi's theme schema, and these tests run every fixture through the same
|
|
12
|
+
// validation the theme loader applies.
|
|
13
|
+
|
|
14
|
+
const taggedTheme = {
|
|
15
|
+
fg(color: string, text: string) {
|
|
16
|
+
return `<${color}>${text}</${color}>`;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function withThemeSource(body: unknown): string {
|
|
21
|
+
const directory = mkdtempSync(join(tmpdir(), "gauge-theme-"));
|
|
22
|
+
const path = join(directory, "Theme.json");
|
|
23
|
+
writeFileSync(path, typeof body === "string" ? body : JSON.stringify(body));
|
|
24
|
+
return path;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const BASE_THEME = {
|
|
28
|
+
name: "Test",
|
|
29
|
+
colors: { accent: "magenta", warning: "yellow", error: "red", border: "cyan", success: "green", syntaxString: "blue" },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
test("resolveGaugePaint reads token references and glyphs from the gauge vars", () => {
|
|
33
|
+
const path = withThemeSource({
|
|
34
|
+
...BASE_THEME,
|
|
35
|
+
vars: { "gauge.ok": "success", "gauge.warning": "syntaxString", "gauge.error": "error", "gauge.empty": "border", "gauge.filled": "█", "gauge.emptyGlyph": "░" },
|
|
36
|
+
});
|
|
37
|
+
const paint = resolveGaugePaint(path);
|
|
38
|
+
assert.deepEqual(paint, { ok: "success", warning: "syntaxString", error: "error", empty: "border", filled: "█", emptyGlyph: "░" });
|
|
39
|
+
assert.equal(paintGauge(45, taggedTheme, 8, paint), `<success>████</success><border>░░░░</border>`);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("a theme without gauge vars resolves no paint", () => {
|
|
43
|
+
assert.equal(resolveGaugePaint(withThemeSource(BASE_THEME)), undefined);
|
|
44
|
+
assert.equal(resolveGaugePaint(withThemeSource({ ...BASE_THEME, vars: { brand: "#ff0000" } })), undefined);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("invalid entries fall back per role and glyphs must be single visible characters", () => {
|
|
48
|
+
const path = withThemeSource({
|
|
49
|
+
...BASE_THEME,
|
|
50
|
+
vars: { "gauge.ok": 300, "gauge.warning": "noSuchToken", "gauge.error": "error", "gauge.filled": "██", "gauge.emptyGlyph": "" },
|
|
51
|
+
});
|
|
52
|
+
assert.deepEqual(resolveGaugePaint(path), { error: "error" });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("gauge colors accept direct hex, okhsl, 256 indexes, and vars references", () => {
|
|
56
|
+
const path = withThemeSource({
|
|
57
|
+
name: "Test",
|
|
58
|
+
colors: { accent: "brand" },
|
|
59
|
+
vars: { brand: "#ff0000", "gauge.ok": "#22c55e", "gauge.warning": "brand", "gauge.error": 196, "gauge.empty": "okhsl(70 0.1 0.5)" },
|
|
60
|
+
});
|
|
61
|
+
const paint = resolveGaugePaint(path);
|
|
62
|
+
assert.equal(paint?.ok, "\x1b[38;2;34;197;94m");
|
|
63
|
+
assert.equal(paint?.warning, "\x1b[38;2;255;0;0m");
|
|
64
|
+
assert.match(paint?.error ?? "", /^\x1b\[38;5;196m$/);
|
|
65
|
+
assert.match(paint?.empty ?? "", /^\x1b\[38;2;\d+;\d+;\d+m$/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("direct colors use the same color grammar themes use everywhere: 3-digit hex, oklch, and case-insensitive okhsl", () => {
|
|
69
|
+
const path = withThemeSource({
|
|
70
|
+
name: "Test",
|
|
71
|
+
colors: {},
|
|
72
|
+
vars: { "gauge.ok": "#f00", "gauge.warning": "OKHSL(120 50% 50%)", "gauge.error": "oklch(0.7 0.1 200)", "gauge.empty": "okhsl(bogus)" },
|
|
73
|
+
});
|
|
74
|
+
const paint = resolveGaugePaint(path);
|
|
75
|
+
assert.equal(paint?.ok, "\x1b[38;2;255;0;0m");
|
|
76
|
+
assert.match(paint?.warning ?? "", /^\x1b\[38;2;\d+;\d+;\d+m$/);
|
|
77
|
+
assert.match(paint?.error ?? "", /^\x1b\[38;2;\d+;\d+;\d+m$/);
|
|
78
|
+
assert.equal(paint?.empty, undefined);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("glyphs must occupy exactly one terminal column", () => {
|
|
82
|
+
const path = withThemeSource({
|
|
83
|
+
...BASE_THEME,
|
|
84
|
+
vars: { "gauge.filled": "0", "gauge.emptyGlyph": "\u200b" },
|
|
85
|
+
});
|
|
86
|
+
assert.deepEqual(resolveGaugePaint(path), {});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("paintGauge paints raw ANSI roles directly and token roles through the theme", () => {
|
|
90
|
+
const raw = { ok: "\x1b[38;2;34;197;94m" };
|
|
91
|
+
assert.equal(paintGauge(45, taggedTheme, 8, raw), "\x1b[38;2;34;197;94m▰▰▰▰\x1b[39m<border>▱▱▱▱</border>");
|
|
92
|
+
assert.equal(paintGauge(45, taggedTheme, 8, { ok: "success" }), "<success>▰▰▰▰</success><border>▱▱▱▱</border>");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("paintGauge tolerates regex metacharacters in theme-authored glyphs", () => {
|
|
96
|
+
assert.equal(paintGauge(50, taggedTheme, 4, { emptyGlyph: "*" }), "<accent>▰▰</accent><border>**</border>");
|
|
97
|
+
assert.equal(paintGauge(50, taggedTheme, 4, { emptyGlyph: "." }), "<accent>▰▰</accent><border>..</border>");
|
|
98
|
+
assert.equal(paintGauge(100, taggedTheme, 4, { filled: "(" }), "<error>((((</error><border></border>");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("a malformed theme file throws for the raw resolver and is swallowed by the memoized one", () => {
|
|
102
|
+
assert.throws(() => resolveGaugePaint(withThemeSource("not json")));
|
|
103
|
+
assert.equal(gaugePaintFor(withThemeSource("not json")), undefined);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("gaugePaintFor memoizes by stat and picks up file changes", () => {
|
|
107
|
+
const path = withThemeSource(BASE_THEME);
|
|
108
|
+
try {
|
|
109
|
+
clearGaugePaintCache();
|
|
110
|
+
assert.equal(gaugePaintFor(path), undefined);
|
|
111
|
+
assert.equal(gaugePaintFor(undefined), undefined);
|
|
112
|
+
writeFileSync(path, JSON.stringify({ ...BASE_THEME, vars: { "gauge.ok": "success", "gauge.filled": "█" } }));
|
|
113
|
+
// Same-second writes are not enough: touch mtime explicitly.
|
|
114
|
+
utimesSync(path, new Date(), new Date(Date.now() + 5_000));
|
|
115
|
+
assert.deepEqual(gaugePaintFor(path), { ok: "success", filled: "█" });
|
|
116
|
+
} finally {
|
|
117
|
+
clearGaugePaintCache();
|
|
118
|
+
rmSync(join(path, ".."), { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
});
|