@printpractical/cruze 0.0.6 → 0.0.7
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 +7 -6
- package/dist/app/use_cases/run_review.js +1 -1
- package/dist/domain/journal.js +2 -2
- package/dist/domain/validation/element_rules.js +1 -1
- package/package.json +1 -1
- package/skills/architect/project.md +1 -1
- package/skills/formats/reference/architecture.md +1 -1
- package/skills/formats/templates/architecture.md +2 -2
- package/skills/hexagonal-design/SKILL.md +9 -3
- package/skills/hexagonal-design/languages/rust.md +1 -1
- package/skills/hexagonal-design/languages/swift.md +46 -0
- package/skills/hexagonal-design/shapes.md +49 -0
- package/skills/improve/SKILL.md +51 -0
- package/skills/next/SKILL.md +1 -1
- package/skills/realign/notes/0.0.7.md +11 -0
- package/skills/realign/notes.md +1 -0
- package/skills/rethink/SKILL.md +1 -1
- package/skills/roles/SKILL.md +2 -1
- package/skills/roles/design-reviewer.md +1 -1
- package/skills/roles/surveyor.md +40 -0
- package/src/app/use_cases/run_review.ts +1 -1
- package/src/domain/journal.ts +2 -2
- package/src/domain/validation/element_rules.ts +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ It works with any agent that supports the [Agent Skills](https://agentskills.io)
|
|
|
10
10
|
|
|
11
11
|
## Status
|
|
12
12
|
|
|
13
|
-
Version 0.0.
|
|
13
|
+
Version 0.0.7 is the seventh preview. It fits the structure to the kind of system: layers are roles rather than folders, a context starts as one core module with its adapters, architect starts from the layout that is normal for a library, a daemon, an app or a CLI, and the new `improve` skill restructures existing code to those standards when you choose. Early development. The CLI is complete for the V1 lifecycle. The document formats ship as the `cruze-formats` skill, the standards an agent designs and codes to ship as knowledge skills, and the whole workflow, from explore to land, ships as workflow skills. Claude Code is the agent tested so far.
|
|
14
14
|
|
|
15
15
|
## Why
|
|
16
16
|
|
|
@@ -35,7 +35,7 @@ The skills run `cruze` commands, so the agent needs `cruze` on its path. Install
|
|
|
35
35
|
From a GitHub Release, without npm. Each release attaches the packed package:
|
|
36
36
|
|
|
37
37
|
```sh
|
|
38
|
-
npm install -g https://github.com/PrintPractical/cruze/releases/download/v0.0.
|
|
38
|
+
npm install -g https://github.com/PrintPractical/cruze/releases/download/v0.0.7/printpractical-cruze-0.0.7.tgz
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
From npm, once a version is published there:
|
|
@@ -47,7 +47,7 @@ npm install -g @printpractical/cruze
|
|
|
47
47
|
From the git repository, at a tag or `#main`. This needs git and access to the repository. npm 11 needs `--install-links` for a global git install, and the first `cruze` command builds the CLI, which takes a moment:
|
|
48
48
|
|
|
49
49
|
```sh
|
|
50
|
-
npm install -g --install-links github:PrintPractical/cruze#v0.0.
|
|
50
|
+
npm install -g --install-links github:PrintPractical/cruze#v0.0.7
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
### 2. Set up a repository
|
|
@@ -61,7 +61,7 @@ cruze init
|
|
|
61
61
|
When you didn't install from npm, tell `init` where Cruze came from, so the CI it writes runs the same source instead of the npm package:
|
|
62
62
|
|
|
63
63
|
```sh
|
|
64
|
-
cruze init --package https://github.com/PrintPractical/cruze/releases/download/v0.0.
|
|
64
|
+
cruze init --package https://github.com/PrintPractical/cruze/releases/download/v0.0.7/printpractical-cruze-0.0.7.tgz
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
`init` asks for the project name, and which coding agent you use: `claude` for Claude Code, or `other` for any agent that reads `AGENTS.md` and `.agents/skills/`, such as Codex or OpenCode. It doesn't ask when you pass `--agent claude` or `--agent other`, or when the repository already has `CLAUDE.md` or `.claude/`. With `--yes`, or without a terminal, it sets up Claude Code. Then it creates:
|
|
@@ -156,10 +156,11 @@ Stepping back is editing: change an upstream document and every approval that ci
|
|
|
156
156
|
| `cruze-triage` | Reproduces a bug, finds its root cause against the living docs, and fixes it test-first or routes it to architect or rethink |
|
|
157
157
|
| `cruze-rethink` | Steps back to the level a discovery touches, keeps completed work, re-approves what went stale and records why |
|
|
158
158
|
| `cruze-realign` | Brings existing code up to what a new Cruze expects: audits it against each release's notes, decides every finding with you, and schedules the fixes as prefactor changes |
|
|
159
|
+
| `cruze-improve` | Restructures existing code to meet today's standards: surveys it for structure that departs from them or keeps causing friction, lets you pick any or all of the candidates, and schedules the moves as prefactor changes |
|
|
159
160
|
| `cruze-next` | Says which step to run next and why |
|
|
160
|
-
| `cruze-roles` | The fresh-context design reviewer, code reviewer, verifier, researcher and
|
|
161
|
+
| `cruze-roles` | The fresh-context design reviewer, code reviewer, verifier, researcher, auditor and surveyor, and how a review runs to a disposition for every finding |
|
|
161
162
|
| `cruze-formats` | The format of every Cruze document, with templates |
|
|
162
|
-
| `cruze-hexagonal-design` | Ports and adapters with domain-driven design: who owns each rule, dependency direction, many small modules, contracts, runtime ownership, and notes for Rust and C++ |
|
|
163
|
+
| `cruze-hexagonal-design` | Ports and adapters with domain-driven design: who owns each rule, dependency direction, many small modules, contracts, runtime ownership, layouts by kind of system, and notes for Rust, Swift and C++ |
|
|
163
164
|
| `cruze-behavioural-testing` | Which tests to write and at which seam, fakes instead of mocks, and protecting approved scenarios' tests |
|
|
164
165
|
| `cruze-grilling` | Interviewing the user in rounds until decisions settle, and challenging once before deferring |
|
|
165
166
|
| `cruze-domain-language` | Building the glossary and using its words in documents, IDs and code |
|
|
@@ -4,7 +4,7 @@ import { featureOf } from "../../domain/project/work_items.js";
|
|
|
4
4
|
|
|
5
5
|
import { loadView, } from "../project_context.js";
|
|
6
6
|
|
|
7
|
-
export const ROLES = ["design-reviewer", "code-reviewer", "verifier", "researcher", "auditor"] ;
|
|
7
|
+
export const ROLES = ["design-reviewer", "code-reviewer", "verifier", "researcher", "auditor", "surveyor"] ;
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
package/dist/domain/journal.js
CHANGED
|
@@ -26,14 +26,14 @@ const OPTIONAL_FIELDS = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
/** The workflow steps and checks that find or miss a problem, so a retro can count them. */
|
|
29
|
-
export const STEPS = ["envision", "architect", "research", "design-review", "walkthrough", "plan", "plan-review", "build", "code-review", "verify", "land", "realign", "validate", "check", "user"];
|
|
29
|
+
export const STEPS = ["envision", "architect", "research", "design-review", "walkthrough", "plan", "plan-review", "build", "code-review", "verify", "land", "realign", "improve", "validate", "check", "user"];
|
|
30
30
|
|
|
31
31
|
export const FIELD_VALUES = {
|
|
32
32
|
level: ["task", "change", "feature", "architecture", "vision"],
|
|
33
33
|
kind: ["defect", "discovery"],
|
|
34
34
|
disposition: ["fixed", "waived", "deferred", "rejected"],
|
|
35
35
|
decided: ["agent", "user"],
|
|
36
|
-
review: ["design", "plan", "code", "realign"],
|
|
36
|
+
review: ["design", "plan", "code", "realign", "improve"],
|
|
37
37
|
result: ["accepted", "sent-back"],
|
|
38
38
|
checked_by: ["agent", "reviewer"],
|
|
39
39
|
found_by: STEPS,
|
|
@@ -18,7 +18,7 @@ const REQUIRED_FACTS = {
|
|
|
18
18
|
const ALLOWED_VALUES = {
|
|
19
19
|
Kind: { kinds: ["ENT"], values: ["aggregate", "entity", "value", "service", "policy", "event"] },
|
|
20
20
|
Direction: { kinds: ["PORT"], values: ["driven", "driving"] },
|
|
21
|
-
Layer: { kinds: ["MOD"], values: ["domain", "application", "adapter", "composition"] },
|
|
21
|
+
Layer: { kinds: ["MOD"], values: ["core", "domain", "application", "adapter", "composition"] },
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export function elementProblems(path , doc , element ) {
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ The result has four parts, reviewed together and shown at one walkthrough:
|
|
|
16
16
|
5. **Ports (`PORT`).** Define every capability the use cases need from outside, named by capability. Write each operation's contract, following `.agents/skills/cruze-hexagonal-design/contracts.md`. Add driving ports only where the hexagonal-design skill calls for them.
|
|
17
17
|
6. **Adapters (`ADP`).** Name the technology behind each driven port and each inbound adapter that drives use cases. Run the researcher role on every adapter's technology first, and record the result in the adapter's `Adopts` fact. Take every new dependency to the user.
|
|
18
18
|
7. **Flows (`FLOW`).** Write a `sequenceDiagram` for each of the 2 to 5 most important interactions, including their failure paths. Leave out `Serves` until scenarios exist.
|
|
19
|
-
8. **Modules (`MOD`).** Give each context its
|
|
19
|
+
8. **Modules (`MOD`).** Name the kind of system in the Overview, and choose the layout that is normal for that kind and the language, following `.agents/skills/cruze-hexagonal-design/shapes.md` and the language file. Give each context the fewest modules that keep its layer rules, usually one `core` module and its adapters, and give the shared CLI shell and the composition root the `system` scope. Add a `system` module in the `composition` layer for the build and CI files, such as the package manifest and `.github/workflows/`, so the tasks that write them have an owner. Every source file must fall inside some module's `Path`, including namespace files that join a context's modules, such as `src/inventory/mod.rs` or `index.ts`; list them in a `composition` module's `Path` when no other module fits. Every module gets a `Path` and a `Layer`, and every element above gets its `Module` and `File`.
|
|
20
20
|
9. **Dependency rules (`RULE`).** State what may import what, in one or two sentences each.
|
|
21
21
|
10. **Cross-cutting concerns (`XC`).** Cover errors, configuration, logging, concurrency and security, plus build and delivery (CI, formatting, linting). Read `.agents/skills/cruze-hexagonal-design/runtime.md` for concurrency.
|
|
22
22
|
11. **Decisions.** Write an ADR, with `cruze new adr <slug> --title "<title>"`, for each decision that passes the ADR test. Record the other project decisions as `D<n>` lines under `## Decisions`.
|
|
@@ -35,7 +35,7 @@ Facts are `- Key: value` items. Keys marked required must be present; the others
|
|
|
35
35
|
| `PORT` | `Direction` (`driven` or `driving`), `Operations`, `Module`, `File` | `Implemented by`, `Future` |
|
|
36
36
|
| `ADP` | `Implements` (a driven port) or `Drives` (use cases), `Technology`, `Module`, `File` | `Adopts` (library and adopt-or-build reason), `Format` (the stored or wire format it owns, or where its schema lives) |
|
|
37
37
|
| `FLOW` | `Elements`, a Mermaid `sequenceDiagram` | `Serves` (SCN IDs, once they exist), `Failure paths` |
|
|
38
|
-
| `MOD` | `Path`, `Layer` (`domain`, `application`, `adapter` or `composition`) | |
|
|
38
|
+
| `MOD` | `Path`, `Layer` (`core`, `domain`, `application`, `adapter` or `composition`) | |
|
|
39
39
|
| `RULE` | one or two sentences of rule | `Enforced by` (config layer names) |
|
|
40
40
|
| `XC` | prose | |
|
|
41
41
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
<One paragraph: what the system is, how it is used, and the architectural style.>
|
|
5
|
+
<One paragraph: what the system is and what kind (a library, a daemon or service, a mobile or desktop app, a CLI, or several deliverables), how it is used, and the architectural style.>
|
|
6
6
|
|
|
7
7
|
### VIEW-context: System context
|
|
8
8
|
|
|
@@ -81,7 +81,7 @@ sequenceDiagram
|
|
|
81
81
|
|
|
82
82
|
### MOD-<context>.<name>: <Module title>
|
|
83
83
|
- Path: `<directory or file>`
|
|
84
|
-
- Layer: <domain | application | adapter | composition>
|
|
84
|
+
- Layer: <core | domain | application | adapter | composition>
|
|
85
85
|
|
|
86
86
|
## Dependency rules
|
|
87
87
|
|
|
@@ -37,6 +37,7 @@ When two rules pull apart, the earlier one wins. Prefer the simplest design that
|
|
|
37
37
|
|
|
38
38
|
| Layer | Owns | May depend on |
|
|
39
39
|
| --- | --- | --- |
|
|
40
|
+
| `core` | A context's concepts, use cases and ports together (`ENT`, `UC`, `PORT`), when its domain needs no boundary of its own | The standard library, and approved libraries with no infrastructure in them |
|
|
40
41
|
| `domain` | Concepts, state, rules, invariants and domain errors (`ENT`) | Domain code, the standard library, and approved libraries with no infrastructure in them, such as an error-derive crate |
|
|
41
42
|
| `application` | Use cases (`UC`) and the ports they define (`PORT`) | The domain |
|
|
42
43
|
| `adapter` | Inbound adapters that drive use cases, and outbound adapters that implement driven ports (`ADP`) | Application, domain and third-party libraries |
|
|
@@ -44,6 +45,10 @@ When two rules pull apart, the earlier one wins. Prefer the simplest design that
|
|
|
44
45
|
|
|
45
46
|
`cruze check` enforces imports between the project's own layers. It allows every third-party import, so review checks that infrastructure libraries appear only in adapters.
|
|
46
47
|
|
|
48
|
+
Use the fewest layers that keep the rules. A context starts with `core`, its adapters and the composition root. The boundary that matters most is between the core and infrastructure, because it is what lets tests fake the outside world. Split a context's core into `domain` and `application` when its domain has rules and invariants that several use cases share and a workflow could break, and record the split as a `D<n>` decision. A `core` module follows the domain and the application rules below together.
|
|
49
|
+
|
|
50
|
+
Layers are roles, not folders. Choose the layout that is normal for the kind of system and its language, following [shapes.md](shapes.md), and give each module the layer of what it holds.
|
|
51
|
+
|
|
47
52
|
Layers are dependency boundaries, not runtime hops. A port can be a plain function call. Add a task, thread, channel or process only for a concrete ownership, concurrency, isolation or backpressure need, and read [runtime.md](runtime.md) first.
|
|
48
53
|
|
|
49
54
|
### Domain
|
|
@@ -83,13 +88,13 @@ Layers are dependency boundaries, not runtime hops. A port can be a plain functi
|
|
|
83
88
|
|
|
84
89
|
## Many small modules
|
|
85
90
|
|
|
86
|
-
- Give each responsibility its own module, grouped by domain concept, with the hexagonal roles inside. A file may hold a few closely related types.
|
|
91
|
+
- Give each responsibility its own module, grouped by domain concept, with the hexagonal roles inside. A context's core is usually one module, with each port beside the use cases that define it, and grows submodules by concept, not by layer. A file may hold a few closely related types.
|
|
87
92
|
- When a file passes its budget (`check:` in `.cruze/config.yaml`, enforced by `cruze check`) or gains a second responsibility, split it into a submodule named for what it owns.
|
|
88
93
|
- Entry-point and namespace files, such as `main`, `lib.rs`, `mod.rs` and `index.ts`, hold only wiring and declarations.
|
|
89
94
|
- Don't create generic modules such as `utils`, `helpers`, `common`, `services`, `models`, `types` or `manager`. Shared behaviour belongs with the concept that owns it.
|
|
90
95
|
- Duplication is semantic. Two implementations of one rule are duplication even when the text differs, so consolidate them in the owner, not in a helper.
|
|
91
|
-
- Structure follows the module map, never a per-language template. Write idiomatic code for the language and use its standard tooling. [languages/rust.md](languages/rust.md) and [languages/cpp.md](languages/cpp.md) add rules for those languages.
|
|
92
|
-
- In an existing codebase, its established layout and naming win over these structure rules. Ownership and dependency direction still apply.
|
|
96
|
+
- Structure follows the module map, never a per-language template. Write idiomatic code for the language and use its standard tooling. [languages/rust.md](languages/rust.md), [languages/swift.md](languages/swift.md) and [languages/cpp.md](languages/cpp.md) add rules for those languages.
|
|
97
|
+
- In an existing codebase, its established layout and naming win over these structure rules until the user restructures it with the improve skill. Ownership and dependency direction still apply.
|
|
93
98
|
|
|
94
99
|
## Representations and errors
|
|
95
100
|
|
|
@@ -119,6 +124,7 @@ These are review signals, not automatic failures. When one could be checked mech
|
|
|
119
124
|
|
|
120
125
|
## Further reading
|
|
121
126
|
|
|
127
|
+
- [shapes.md](shapes.md): choosing the layout for a library, a daemon or service, a mobile or desktop app, or a CLI.
|
|
122
128
|
- [contracts.md](contracts.md): writing a port's `Operations`, a public API, an IPC protocol or a stored format.
|
|
123
129
|
- [runtime.md](runtime.md): concurrency, long-lived resources (connections, sessions, devices, subprocesses), state machines, cancellation or a hot path.
|
|
124
130
|
- [fundamentals.md](fundamentals.md): choosing a data structure, a state representation or an abstraction mechanism; defining equality or ordering; bounding loops, retries and fan-out.
|
|
@@ -6,7 +6,7 @@ These add to the hexagonal design rules for Rust projects. The module map decide
|
|
|
6
6
|
|
|
7
7
|
- `main.rs` is the composition root and stays thin. `lib.rs` and every `mod.rs` hold only module declarations, visibility and deliberate re-exports, and `lib.rs` exposes the use cases, ports and domain types that tests drive.
|
|
8
8
|
- Name modules for the concept they own. Don't put domain definitions in a `mod.rs`, and don't create `domain.rs`, `services.rs`, `models.rs` or `utils.rs` catch-alls.
|
|
9
|
-
-
|
|
9
|
+
- Split crates by deliverable, such as the library, a client, a daemon and a CLI, in a Cargo workspace when there is more than one. Inside a crate, modules carry the concepts and the layers, and `cruze check` enforces the layer rules between them. A crate per layer is unusual in Rust and puts a public API between the layers: use one only when the user wants the compiler to enforce a boundary, and record it as a decision.
|
|
10
10
|
|
|
11
11
|
## Modelling
|
|
12
12
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Swift
|
|
2
|
+
|
|
3
|
+
These add to the hexagonal design rules for Swift projects, such as iOS and macOS apps and Swift packages. The module map decides the layout; this file never does.
|
|
4
|
+
|
|
5
|
+
## Packages and targets
|
|
6
|
+
|
|
7
|
+
- Every target is a Swift module, and anything another target uses must be `public`. Each target boundary costs access-control boilerplate and build time, so use few:
|
|
8
|
+
- one core target for the app's contexts, with a folder per feature or concept;
|
|
9
|
+
- adapter targets only where a framework or package must stay out of the core, or one adapters target for all of them;
|
|
10
|
+
- the app target, holding the screens, their models and the composition root, or a feature target per feature once the app is large enough to build and preview features alone;
|
|
11
|
+
- one test-support target with the fakes and fixtures every test target shares.
|
|
12
|
+
- Keep `internal`, the default, for everything used inside its target. Use `package` for what the package's own targets share, and `public` only for what the app target or another package needs.
|
|
13
|
+
- A target can import only the targets and packages it declares in `Package.swift`, so declared dependencies enforce those boundaries. They don't stop a target importing a platform framework, such as `CoreLocation` or `SwiftUI`, which any target can import. Review checks framework imports against the layer rules, or a short CI script lists them per target.
|
|
14
|
+
- Keep the core free of UIKit, SwiftUI and other platform frameworks. Its tests then build and run without the app, and with a macOS platform in `Package.swift`, without a simulator.
|
|
15
|
+
|
|
16
|
+
## Modelling
|
|
17
|
+
|
|
18
|
+
- Use structs for values and entities, enums for closed alternatives and states, and classes only where identity needs reference semantics. Wrap primitives with meaning, such as `struct OfficeName { let value: String }`, with a failable or throwing initializer that enforces the invariant.
|
|
19
|
+
- Use concrete types for single-strategy behaviour. Use protocols for ports and for strategies that really vary.
|
|
20
|
+
|
|
21
|
+
## Ports and injection
|
|
22
|
+
|
|
23
|
+
- Ports are protocols owned by the core, named for the capability. Platform types, such as `CLLocation` or `UNNotificationRequest`, never appear in their signatures.
|
|
24
|
+
- Inject through initializers. The composition root at the app's entry point builds the adapters and the use cases, and hands each screen's model what it calls.
|
|
25
|
+
|
|
26
|
+
## Screens
|
|
27
|
+
|
|
28
|
+
- A SwiftUI view and its `@Observable` model are the screen's inbound adapter. The model calls use cases and maps their results to what the view shows. Domain rules stay out of both.
|
|
29
|
+
- Test the model with fakes behind its use cases' ports. Keep UI tests for what only the running screen can show.
|
|
30
|
+
|
|
31
|
+
## Errors and logging
|
|
32
|
+
|
|
33
|
+
- Throw a typed error, `throws(OfficeChangeError)`, where callers handle its cases differently, and use plain `throws` elsewhere. Never `try!` or `fatalError` for a condition that can happen in production. Tests may use them.
|
|
34
|
+
- Log with `os.Logger` in adapters, use cases and screen models. The domain doesn't log.
|
|
35
|
+
|
|
36
|
+
## Concurrency
|
|
37
|
+
|
|
38
|
+
- Use the Swift 6 language mode. Domain values are `Sendable` structs and enums. A use case is a struct, or an actor when it owns mutable state that several callers reach.
|
|
39
|
+
- `@MainActor` belongs on views and their models, not on the core.
|
|
40
|
+
- Don't mark a type `@unchecked Sendable` without a comment giving the reason.
|
|
41
|
+
|
|
42
|
+
## Tooling and tests
|
|
43
|
+
|
|
44
|
+
- Format with `swift format format --in-place --recursive`, and lint with `swift format lint --strict --recursive`.
|
|
45
|
+
- Package tests live in `Tests/<Target>Tests/`. They import the target with `@testable import`, which makes its `internal` declarations visible, so nothing is made `public` only for tests. UI tests live in the app's UI test target.
|
|
46
|
+
- Put the package's tests in the `test` command and the app's UI tests in `test_full`, as `.agents/skills/cruze-formats/reference/config.md` describes, since UI tests run through the simulator and are by far the slowest.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Shapes by kind of system
|
|
2
|
+
|
|
3
|
+
Start from the layout that is normal for the kind of system and its language, then place each owner in it. The hexagonal rules decide what depends on what. They don't decide the folders: a reader who knows the ecosystem should find the code where they expect it.
|
|
4
|
+
|
|
5
|
+
Name the kind in the architecture's Overview. A system can be more than one kind, such as a daemon that ships a client library and a CLI. Each is then its own deliverable, with its own entry point, and in a language with packages, its own package.
|
|
6
|
+
|
|
7
|
+
## Library
|
|
8
|
+
|
|
9
|
+
A crate, package or SDK that other code calls.
|
|
10
|
+
|
|
11
|
+
- The public API is the inbound adapter. Callers drive the use cases through it, so keep it small and treat it as a contract: read [contracts.md](contracts.md).
|
|
12
|
+
- A pure library, with no I/O, has a core only: no ports and no application layer.
|
|
13
|
+
- A client library that talks to a server keeps the transport and the wire format behind driven ports, so its tests run against a fake server. Group its modules by what callers do with it, such as `connection`, `consumer` and `provider`, with the wire format and the transport as adapters.
|
|
14
|
+
|
|
15
|
+
## Daemon or service
|
|
16
|
+
|
|
17
|
+
A process that runs until it is stopped, serving requests or reconciling state.
|
|
18
|
+
|
|
19
|
+
- `main` is the composition root and stays thin: configuration, construction, signals and shutdown.
|
|
20
|
+
- One inbound adapter per interface it offers, such as HTTP, IPC, a message consumer or a signal handler. Each decodes, calls one use case and encodes.
|
|
21
|
+
- One outbound adapter per technology it uses, such as storage, the OS, a subprocess or another service.
|
|
22
|
+
- Each context's core is one module, by concept. Long-lived tasks and their shutdown follow [runtime.md](runtime.md).
|
|
23
|
+
|
|
24
|
+
## Mobile or desktop app
|
|
25
|
+
|
|
26
|
+
An app with screens, on a platform that hosts it.
|
|
27
|
+
|
|
28
|
+
- Group modules by feature, such as `Offices` or `Dashboard`, each with its core and its screens.
|
|
29
|
+
- A screen's view and its model form the inbound adapter: the model maps what the person does to use cases, and use-case results to what the view shows. Neither holds a domain rule.
|
|
30
|
+
- Platform frameworks, such as location, notifications, maps and storage, are outbound adapters behind ports named for the capability, such as `ZoneMonitor` rather than `CoreLocationClient`.
|
|
31
|
+
- The app's entry point is the composition root.
|
|
32
|
+
- Most tests drive the core and the screen models with fakes. UI tests prove only what needs the screen itself, one or two per flow, because they are the slowest tests the project has.
|
|
33
|
+
|
|
34
|
+
## CLI tool
|
|
35
|
+
|
|
36
|
+
A program a person or a script runs and that exits.
|
|
37
|
+
|
|
38
|
+
- The commands are the inbound adapter, one module per command or command group. Argument parsing and output formatting stay there, and each command calls one use case.
|
|
39
|
+
- The filesystem, the network, the terminal and the clock are outbound adapters behind ports, like any other technology, so tests run the commands against fakes.
|
|
40
|
+
|
|
41
|
+
## Splitting further
|
|
42
|
+
|
|
43
|
+
Split a deliverable into more packages, crates or targets only for a concrete need, and record it as a `D<n>` decision:
|
|
44
|
+
|
|
45
|
+
- a second deliverable that shares the code, such as a client library, a CLI or an app extension;
|
|
46
|
+
- a dependency boundary the build should enforce, such as keeping a platform framework out of the core's tests;
|
|
47
|
+
- build time, when one package has grown too slow to build and test alone.
|
|
48
|
+
|
|
49
|
+
A package per layer, or per layer per context, is rarely normal in any ecosystem. Each boundary costs a public API between the layers and slows the build, and `cruze check` enforces the layer rules inside a package where it reads the language's imports.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cruze-improve
|
|
3
|
+
description: Improves the structure of existing code to meet Cruze's current standards, by surveying it for structure that departs from them or keeps causing friction, letting you pick any or all of the candidates, designing the target through an architecture rethink, and scheduling the moves as prefactor changes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Improve
|
|
7
|
+
|
|
8
|
+
Improve restructures code that already works, so it meets the standards Cruze holds new work to, such as a layout that is normal for the kind of system, or so it stops causing the same friction. It is design work with one approval gate, and it never changes behaviour: the behaviour tests are the safety net, and they pass unchanged at every step.
|
|
9
|
+
|
|
10
|
+
Realign is different. It applies what each release's notes expect of existing code, and moves `standards:`. Improve runs whenever the user asks, whatever the version, and can take on structure that realign leaves alone, such as a layout the notes mark as new work only.
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
1. **Check what it builds on.** Run `cruze status`. `docs/architecture.md` must be approved and current; when it isn't, stop and name `rethink`. Note the features and changes in flight, which step 8 schedules around. When the user names an area, such as a context or a module, keep to it.
|
|
15
|
+
2. **Check the safety net.** Run `cruze trace --all`, and the full suite as `.agents/skills/cruze-formats/reference/config.md` defines it under `commands`. Done when both pass, and you have listed each module whose scenarios have no test. Moving such a module first needs tests that record what it does today.
|
|
16
|
+
3. **Load the standards.** Read these, and follow them for the rest of this skill:
|
|
17
|
+
- `.agents/skills/cruze-hexagonal-design/SKILL.md`, `.agents/skills/cruze-hexagonal-design/shapes.md`, and the language file when one exists for the project's language
|
|
18
|
+
- `.agents/skills/cruze-behavioural-testing/SKILL.md`
|
|
19
|
+
- `.agents/skills/cruze-formats/reference/architecture.md`
|
|
20
|
+
- `.agents/skills/cruze-grilling/SKILL.md`
|
|
21
|
+
4. **Survey.** Run the surveyor role from `.agents/skills/cruze-roles/SKILL.md` once for the system's layout, then once for each module in the module map, or in the user's area, giving it the module's `Path`. Run them in parallel where the agent can. Check each candidate's evidence against the files and the journal before you use it. Done when the layout and every module in scope have a report.
|
|
22
|
+
5. **List the candidates** in one message, numbered, merging candidates that share a cause across modules. For each, give:
|
|
23
|
+
- the problem in one sentence, and its evidence;
|
|
24
|
+
- the standard it departs from, or the friction it causes;
|
|
25
|
+
- the target shape;
|
|
26
|
+
- its size in prefactor changes, and its risk.
|
|
27
|
+
|
|
28
|
+
Recommend which to take and why, then ask the user to pick any or all of them.
|
|
29
|
+
6. **Record each decision:** `cruze journal add disposition --set review=improve --set finding="<module or area>: <summary>" --set disposition=<fixed|waived|deferred> --set reason="<reason>" --set decided=user`. A picked candidate is `fixed`. The rest are `waived`, with the user's reason, or `deferred`. A recorded waiver is never raised again.
|
|
30
|
+
7. **Design the target** for every picked candidate together, as a rethink at architecture level, following `.agents/skills/cruze-rethink/SKILL.md` with `kind=discovery` and `found_by=improve`:
|
|
31
|
+
- Amend `docs/architecture.md`: the module map and layers, the moved elements' `Module` and `File` facts, the `layers:` in `.cruze/config.yaml`, and a `D<n>` or an ADR for each structural decision. Change no requirement, scenario or port contract unless the user decided that in step 5.
|
|
32
|
+
- When a candidate changes an interface, such as a port or a module's public API, sketch two or three shapes, compare them, and recommend one before writing it.
|
|
33
|
+
- Before the rethink's walkthrough, run the design review from the roles skill on the diff, with `review=design`.
|
|
34
|
+
- Approve it as the rethink's architecture level says, with `--replan` for each built element that moves.
|
|
35
|
+
8. **Schedule the moves.** Group the replanned elements into prefactor changes, as "A prefactor" in `.agents/skills/cruze-architect/tweak.md` describes, each sized as step 12 of `.agents/skills/cruze-architect/feature.md` says.
|
|
36
|
+
- Order them so each lands on its own and the code builds after every one: first the tests that record behaviour a module lacks, then one context or feature at a time.
|
|
37
|
+
- Put each on the roadmap, following `.agents/skills/cruze-roadmap/SKILL.md`. Run `cruze status --overlap --change <ref>`, and block it on every in-flight item it overlaps.
|
|
38
|
+
- Commit, with a message such as `docs: plan the <area> restructure`.
|
|
39
|
+
|
|
40
|
+
Done when every candidate has a journaled disposition, the target architecture is approved, and every move is a prefactor change on the roadmap.
|
|
41
|
+
|
|
42
|
+
## Rules
|
|
43
|
+
|
|
44
|
+
- **Behaviour stays.** Wire formats, stored formats, messages, exit codes, and every approved scenario and its test stay as they are, unless the user decided otherwise in step 5.
|
|
45
|
+
- **Small files stay.** Many small modules are the standard, so a candidate never merges files only to make a module deeper. Merging is a candidate when two modules own one responsibility, or one only forwards calls to another.
|
|
46
|
+
- **Ports and adapters stay.** A restructure moves where the core, the ports and the adapters live, never whether a technology sits behind a port.
|
|
47
|
+
- **The existing layout wins** until the user picks a candidate that changes it. Improve never restructures code the user didn't pick.
|
|
48
|
+
|
|
49
|
+
## Next step
|
|
50
|
+
|
|
51
|
+
`plan` for the first prefactor change. When the user picked nothing, `next`.
|
package/skills/next/SKILL.md
CHANGED
|
@@ -28,4 +28,4 @@ Next reads the project's state and says what to do now. The CLI works it out fro
|
|
|
28
28
|
| `realign` | `realign`: the installed Cruze expects more of existing code than it meets |
|
|
29
29
|
| `switch-branch` | Switch to the named branch, where that change is built |
|
|
30
30
|
|
|
31
|
-
When the user asks about something `cruze next` didn't name,
|
|
31
|
+
When the user asks about something `cruze next` didn't name, point them to `triage` for a bug, `explore` for an idea, or `improve` for existing code whose structure they want to change.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Realign notes: 0.0.7
|
|
2
|
+
|
|
3
|
+
## New work only
|
|
4
|
+
|
|
5
|
+
### improve-skill: Existing code can be restructured to today's standards with the improve skill
|
|
6
|
+
- Applies to: new work only
|
|
7
|
+
- Rule: `.agents/skills/cruze-improve/SKILL.md`
|
|
8
|
+
|
|
9
|
+
### layout-by-kind: The layout is the one normal for the kind of system and its language, with the fewest layers that keep the rules
|
|
10
|
+
- Applies to: new work only
|
|
11
|
+
- Rule: `.agents/skills/cruze-hexagonal-design/SKILL.md`, "Layers"
|
package/skills/realign/notes.md
CHANGED
package/skills/rethink/SKILL.md
CHANGED
|
@@ -32,7 +32,7 @@ Finding a design flaw mid-build is information, not failure. Rethink amends the
|
|
|
32
32
|
--set agent="<the agent you run in>" [--item <ref>]
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
`found_by` is the step that found it. A defect also needs `missed_by`, the earliest step whose job was to catch it. Both take one of `envision`, `architect`, `research`, `design-review`, `walkthrough`, `plan`, `plan-review`, `build`, `code-review`, `verify`, `land`, `validate`, `check` or `user`.
|
|
35
|
+
`found_by` is the step that found it. A defect also needs `missed_by`, the earliest step whose job was to catch it. Both take one of `envision`, `architect`, `research`, `design-review`, `walkthrough`, `plan`, `plan-review`, `build`, `code-review`, `verify`, `land`, `improve`, `validate`, `check` or `user`.
|
|
36
36
|
|
|
37
37
|
Done when `cruze status` shows every document approved and current, or names only work the user chose to leave for later.
|
|
38
38
|
|
package/skills/roles/SKILL.md
CHANGED
|
@@ -14,12 +14,13 @@ A role is a prompt with fixed inputs and a report as its only output. It runs in
|
|
|
14
14
|
| Verifier | [verifier.md](verifier.md) | verify, to run the system |
|
|
15
15
|
| Researcher | [researcher.md](researcher.md) | architect, for adopt-or-build decisions |
|
|
16
16
|
| Auditor | [auditor.md](auditor.md) | realign, to compare existing code with what a release now expects |
|
|
17
|
+
| Surveyor | [surveyor.md](surveyor.md) | improve, to find structure that departs from the standards or keeps causing friction |
|
|
17
18
|
|
|
18
19
|
## Running a role
|
|
19
20
|
|
|
20
21
|
1. Read the role file and gather exactly the inputs it lists. Done when you have every input path, plus any command output the role asks for.
|
|
21
22
|
2. Start a fresh context: a helper agent with no conversation history, where the agent can start one. Give it the role file's full text and the inputs the role lists, including draft documents it names, and nothing else. Your own opinions, the conversation and your reasons for the design stay out.
|
|
22
|
-
3. If the agent can't start a helper, run `cruze review <role> --item <ref>`, where the role is `design-reviewer`, `code-reviewer`, `verifier`, `researcher` or `
|
|
23
|
+
3. If the agent can't start a helper, run `cruze review <role> --item <ref>`, where the role is `design-reviewer`, `code-reviewer`, `verifier`, `researcher`, `auditor` or `surveyor`. Add `--base <commit>` for the code under review. It starts the agent configured under `review:` in `.cruze/config.yaml` in a fresh context and prints the role's report. When no agent is configured, or it isn't installed, it reports `status: run-in-helper` with the role's full prompt instead: start a helper, such as a subagent, and give it exactly that prompt. If no fresh context can run, the reviewers, the verifier and the auditor must still run fresh: tell the user, and have them run the role in a new session. The researcher may run in your own context, since its evidence is checkable; say that you did.
|
|
23
24
|
4. Take the report as it comes back. Check its evidence against the files before you act on any finding.
|
|
24
25
|
|
|
25
26
|
## Running a review
|
|
@@ -20,7 +20,7 @@ Review against these items and nothing else. Each finding names one.
|
|
|
20
20
|
|
|
21
21
|
1. **Behaviour.** Every requirement has at least one scenario. Every scenario has concrete values, one WHEN, and a THEN a person could check. Failure behaviour the user will meet has its own scenario.
|
|
22
22
|
2. **Seams.** Every scenario can be driven through a use case, or through an inbound adapter when the outcome is what that adapter shows. Every driven port the use case needs can be faked.
|
|
23
|
-
3. **Placement.** Every new or changed element with code has a `Module` whose `Layer` fits its role and a `File` with a real path. No generic modules, and no file is likely to hold two responsibilities. Every element, fact and value serves a requirement in scope or a `Future` fact the feature map calls for. Anything reserved for a need nobody stated is a finding.
|
|
23
|
+
3. **Placement.** Every new or changed element with code has a `Module` whose `Layer` fits its role and a `File` with a real path. No generic modules, and no file is likely to hold two responsibilities. The layout is the one normal for the kind of system and the language, and every layer, module, package or target split beyond a context's `core` and its adapters has a recorded reason. Every element, fact and value serves a requirement in scope or a `Future` fact the feature map calls for. Anything reserved for a need nobody stated is a finding.
|
|
24
24
|
4. **Direction.** Every dependency points inward and is allowed by the `RULE` elements and the `layers:` in `.cruze/config.yaml`.
|
|
25
25
|
5. **Adopt or build.** Every component that isn't domain logic has an adopt-or-build decision with a reason, and every new dependency is named with the features the design uses. Every command the design spells out, such as a CI step, is spelled as that version's `--help` or source shows it. A claim the design relies on that the research left unverified is a blocker.
|
|
26
26
|
6. **Contracts.** Every new or changed port's `Operations` state what each operation guarantees, how it fails, and who owns cleanup.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Surveyor
|
|
2
|
+
|
|
3
|
+
You survey existing code for structure that departs from Cruze's current standards, or that keeps causing friction, and say what it should become. You report candidates, and you edit nothing.
|
|
4
|
+
|
|
5
|
+
## Inputs
|
|
6
|
+
|
|
7
|
+
- What to survey: the system's layout, or one module, named with its `Path`.
|
|
8
|
+
- The standards: `.agents/skills/cruze-hexagonal-design/SKILL.md`, `.agents/skills/cruze-hexagonal-design/shapes.md`, the language file for the project's language when one exists, and `.agents/skills/cruze-behavioural-testing/SKILL.md`.
|
|
9
|
+
- `docs/architecture.md`, for the module map and the elements the code implements, and `.cruze/config.yaml`.
|
|
10
|
+
- The files in scope: for the layout, the build manifests, such as `Cargo.toml` or `Package.swift`, and each module's `Path`; for a module, every source and test file under its `Path`.
|
|
11
|
+
- The journal: the output of `cruze journal list --event deviation`, `cruze journal list --event rethink` and `cruze journal list --event disposition`.
|
|
12
|
+
- The settled ledger, as `.agents/skills/cruze-roles/SKILL.md` defines it, and the earlier `improve` and `realign` dispositions. Never raise a candidate they waived.
|
|
13
|
+
|
|
14
|
+
## Work
|
|
15
|
+
|
|
16
|
+
1. **The layout.** Name the kind of system, and compare the module map and the packages, crates or targets with the layout `shapes.md` and the language file describe. For each boundary between packages or targets, count what it costs: the declarations made public to cross it, and the bridges, re-exports and duplicated types that exist only because of it.
|
|
17
|
+
2. **The module's code.** Read every file in scope, not a sample. Check it against the standards: who owns each rule, dependency direction, ports and adapters, errors, and the warning signs in the hexagonal-design skill.
|
|
18
|
+
3. **The journal.** Find the deviations, rethinks and review findings that name a file in scope, and group them by cause. A cause that recurs is friction.
|
|
19
|
+
4. **The history.** Run `git log --format=%h --name-only -n 300 -- <Path>`. Name the files outside the module that change in the same commits as its files in more than half of those commits; a boundary there is often in the wrong place. Name the files that change most.
|
|
20
|
+
5. **One operation.** Pick one representative operation the module serves, and list the files a reader opens to follow it from the inbound adapter to the outbound one. Note modules that only forward calls, and pairs of modules that own one responsibility between them.
|
|
21
|
+
6. **Shape each candidate.** Give the smallest target that meets the standard or removes the friction, and say whether it changes the text of an element in `docs/architecture.md`. A target keeps behaviour, keeps small files, and keeps every technology behind a port.
|
|
22
|
+
|
|
23
|
+
Done when every file in scope was read, and every candidate has evidence.
|
|
24
|
+
|
|
25
|
+
## Report
|
|
26
|
+
|
|
27
|
+
One block per candidate:
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
### <n>. <one-line summary>
|
|
31
|
+
- Kind: standard | friction
|
|
32
|
+
- Evidence: <path:line quoting the code, journal entries with their timestamps, or commit counts>
|
|
33
|
+
- Cost: <the rule text it departs from, or what the friction cost, such as five deviations>
|
|
34
|
+
- Target: <the specific shape it should take>
|
|
35
|
+
- Size: <prefactor changes, and tasks in each>
|
|
36
|
+
- Risk: <what could break, and which tests cover it>
|
|
37
|
+
- Design change: <the elements in docs/architecture.md whose text must change, or `no`>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Report every candidate that has evidence, and none that doesn't. End with one line: `Candidates: <n>. Standard: <n>. Friction: <n>.`
|
|
@@ -4,7 +4,7 @@ import { featureOf } from "../../domain/project/work_items.ts";
|
|
|
4
4
|
import type { AgentRunner } from "../ports/agent_runner.ts";
|
|
5
5
|
import { loadView, type ProjectDeps } from "../project_context.ts";
|
|
6
6
|
|
|
7
|
-
export const ROLES = ["design-reviewer", "code-reviewer", "verifier", "researcher", "auditor"] as const;
|
|
7
|
+
export const ROLES = ["design-reviewer", "code-reviewer", "verifier", "researcher", "auditor", "surveyor"] as const;
|
|
8
8
|
export type Role = (typeof ROLES)[number];
|
|
9
9
|
|
|
10
10
|
export interface ReviewRequest {
|
package/src/domain/journal.ts
CHANGED
|
@@ -26,14 +26,14 @@ const OPTIONAL_FIELDS: Record<string, string[]> = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
/** The workflow steps and checks that find or miss a problem, so a retro can count them. */
|
|
29
|
-
export const STEPS = ["envision", "architect", "research", "design-review", "walkthrough", "plan", "plan-review", "build", "code-review", "verify", "land", "realign", "validate", "check", "user"];
|
|
29
|
+
export const STEPS = ["envision", "architect", "research", "design-review", "walkthrough", "plan", "plan-review", "build", "code-review", "verify", "land", "realign", "improve", "validate", "check", "user"];
|
|
30
30
|
|
|
31
31
|
export const FIELD_VALUES: Record<string, string[]> = {
|
|
32
32
|
level: ["task", "change", "feature", "architecture", "vision"],
|
|
33
33
|
kind: ["defect", "discovery"],
|
|
34
34
|
disposition: ["fixed", "waived", "deferred", "rejected"],
|
|
35
35
|
decided: ["agent", "user"],
|
|
36
|
-
review: ["design", "plan", "code", "realign"],
|
|
36
|
+
review: ["design", "plan", "code", "realign", "improve"],
|
|
37
37
|
result: ["accepted", "sent-back"],
|
|
38
38
|
checked_by: ["agent", "reviewer"],
|
|
39
39
|
found_by: STEPS,
|
|
@@ -18,7 +18,7 @@ const REQUIRED_FACTS: Partial<Record<Kind, string[]>> = {
|
|
|
18
18
|
const ALLOWED_VALUES: Record<string, { kinds: Kind[]; values: string[] }> = {
|
|
19
19
|
Kind: { kinds: ["ENT"], values: ["aggregate", "entity", "value", "service", "policy", "event"] },
|
|
20
20
|
Direction: { kinds: ["PORT"], values: ["driven", "driving"] },
|
|
21
|
-
Layer: { kinds: ["MOD"], values: ["domain", "application", "adapter", "composition"] },
|
|
21
|
+
Layer: { kinds: ["MOD"], values: ["core", "domain", "application", "adapter", "composition"] },
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export function elementProblems(path: string, doc: MarkdownDoc, element: Element): Problem[] {
|