@printpractical/cruze 0.0.7 → 0.0.8

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.
Files changed (73) hide show
  1. package/README.md +6 -5
  2. package/dist/adapters/inbound/cli/commands/journal.js +1 -1
  3. package/dist/adapters/inbound/cli/commands/show.js +27 -0
  4. package/dist/adapters/inbound/cli/commands/status.js +19 -3
  5. package/dist/adapters/inbound/cli/commands.js +3 -1
  6. package/dist/app/use_cases/approve_artifact.js +19 -1
  7. package/dist/app/use_cases/check_code.js +10 -3
  8. package/dist/app/use_cases/journal_events.js +8 -5
  9. package/dist/app/use_cases/show_scope.js +35 -0
  10. package/dist/domain/check/budget_text.js +24 -0
  11. package/dist/domain/check/check_source.js +6 -3
  12. package/dist/domain/config.js +11 -1
  13. package/dist/domain/project/scope_view.js +118 -0
  14. package/dist/domain/status/work_status.js +13 -2
  15. package/dist/domain/validation/element_rules.js +5 -0
  16. package/dist/domain/validation/size_rules.js +14 -5
  17. package/dist/domain/validation/work_item_rules.js +25 -3
  18. package/dist/domain/versions/rule_versions.js +1 -0
  19. package/dist/main.js +6 -0
  20. package/package.json +1 -1
  21. package/skills/architect/SKILL.md +2 -0
  22. package/skills/architect/feature.md +1 -1
  23. package/skills/architect/walkthrough.md +7 -4
  24. package/skills/build/SKILL.md +9 -8
  25. package/skills/dependency-approval/SKILL.md +8 -1
  26. package/skills/envision/SKILL.md +2 -0
  27. package/skills/envision/feature.md +1 -1
  28. package/skills/explore/SKILL.md +1 -1
  29. package/skills/formats/reference/architecture.md +1 -1
  30. package/skills/formats/reference/config.md +5 -1
  31. package/skills/formats/reference/specs.md +1 -1
  32. package/skills/formats/reference/work-items.md +1 -1
  33. package/skills/grilling/SKILL.md +19 -7
  34. package/skills/grilling/illustrating.md +37 -0
  35. package/skills/hexagonal-design/SKILL.md +1 -0
  36. package/skills/hexagonal-design/languages/rust.md +2 -1
  37. package/skills/improve/SKILL.md +3 -3
  38. package/skills/land/SKILL.md +2 -2
  39. package/skills/next/SKILL.md +1 -1
  40. package/skills/plan/SKILL.md +3 -3
  41. package/skills/plan/tasks.md +9 -5
  42. package/skills/realign/SKILL.md +1 -1
  43. package/skills/realign/notes/0.0.8.md +97 -0
  44. package/skills/realign/notes.md +1 -0
  45. package/skills/research/SKILL.md +2 -1
  46. package/skills/rethink/SKILL.md +1 -1
  47. package/skills/roadmap/SKILL.md +2 -0
  48. package/skills/roles/SKILL.md +7 -9
  49. package/skills/roles/code-reviewer.md +8 -8
  50. package/skills/roles/design-reviewer.md +9 -11
  51. package/skills/roles/surveyor.md +1 -1
  52. package/skills/roles/verifier.md +6 -5
  53. package/skills/triage/SKILL.md +2 -0
  54. package/skills/verify/SKILL.md +3 -1
  55. package/src/adapters/inbound/cli/commands/journal.ts +1 -1
  56. package/src/adapters/inbound/cli/commands/show.ts +27 -0
  57. package/src/adapters/inbound/cli/commands/status.ts +19 -3
  58. package/src/adapters/inbound/cli/commands.ts +3 -1
  59. package/src/app/use_cases/approve_artifact.ts +19 -1
  60. package/src/app/use_cases/check_code.ts +10 -3
  61. package/src/app/use_cases/journal_events.ts +8 -5
  62. package/src/app/use_cases/show_scope.ts +35 -0
  63. package/src/domain/check/budget_text.ts +24 -0
  64. package/src/domain/check/check_source.ts +6 -3
  65. package/src/domain/config.ts +11 -1
  66. package/src/domain/project/scope_view.ts +118 -0
  67. package/src/domain/status/work_status.ts +13 -2
  68. package/src/domain/validation/element_rules.ts +5 -0
  69. package/src/domain/validation/size_rules.ts +14 -5
  70. package/src/domain/validation/work_item_rules.ts +25 -3
  71. package/src/domain/versions/rule_versions.ts +1 -0
  72. package/src/main.ts +6 -0
  73. package/templates/init/AGENTS.md +1 -1
@@ -0,0 +1,97 @@
1
+ # Realign notes: 0.0.8
2
+
3
+ ## Existing code
4
+
5
+ ### module-path-glob: A module's Path is a directory or a file, never a glob
6
+ - Applies to: existing code
7
+ - Rule: `.agents/skills/cruze-formats/reference/architecture.md`, "Element facts"
8
+ - Detect: validate:module-path-glob
9
+ - Migrate: replace each glob in a `MOD` element's `Path` with the directories it meant, each ending in `/`, or the files, through `rethink` at architecture level.
10
+
11
+ ### budget-not-a-shape: No code is bent to fit a file budget
12
+ - Applies to: existing code
13
+ - Rule: `.agents/skills/cruze-hexagonal-design/SKILL.md`, "Many small modules"
14
+ - Ask: is any code shaped to stay under `check.max_lines` or `check.max_types`, such as a type alias, a string where an error type belongs, merged types, a function placed away from its owner, or Rust domain tests moved out of their file only for its length?
15
+ - Migrate: write the code as it would be with no budget. Split a file that then passes its budget along a second responsibility, or list it under `check.exceptions` with the reason it has one. Rust domain tests may move back inline, since their `#[cfg(test)]` modules no longer count.
16
+
17
+ ### no-dependency-workarounds: No code works around a dependency or feature nobody asked for
18
+ - Applies to: existing code
19
+ - Rule: `.agents/skills/cruze-dependency-approval/SKILL.md`, "Weighing"
20
+ - Ask: does any code hand-write, shell out for, or use a less idiomatic construct for something a dependency or a feature of an approved dependency does cleanly, where the design or a deviation gives avoiding the dependency as the reason?
21
+ - Migrate: ask the user for the dependency as the dependency-approval skill says, then replace the workaround through `rethink` at feature level.
22
+
23
+ ### size-exceptions: An accepted size warning is recorded under changes.exceptions
24
+ - Applies to: existing code
25
+ - Rule: `.agents/skills/cruze-formats/reference/config.md`, "changes.exceptions"
26
+ - Ask: does `cruze validate` report a `change-too-small`, `change-too-large`, `plan-too-large` or `feature-size` warning that a `D<n>` line records the user accepting? `cruze approve` now refuses the item until it is listed.
27
+ - Migrate: list each such feature or change under `changes.exceptions` in `.cruze/config.yaml`, as its ref and the decision's reason.
28
+
29
+ ## New work only
30
+
31
+ ### fresh-session: Every step runs in a new session
32
+ - Applies to: new work only
33
+ - Rule: `.agents/skills/cruze-next/SKILL.md`, "Steps", step 3
34
+
35
+ ### show-scope: Plan, build and the roles read the scope through cruze show
36
+ - Applies to: new work only
37
+ - Rule: `.agents/skills/cruze-plan/SKILL.md`, "Steps", step 6
38
+
39
+ ### scoped-ledger: The settled ledger is the item's, and journal list leaves landed work out
40
+ - Applies to: new work only
41
+ - Rule: `.agents/skills/cruze-roles/SKILL.md`, "The settled ledger"
42
+
43
+ ### round-2-by-agent: Round 2 is checked by the agent
44
+ - Applies to: new work only
45
+ - Rule: `.agents/skills/cruze-roles/SKILL.md`, "Round 2"
46
+
47
+ ### verifier-front-doors: The verifier drives the scenarios that have a front door and never idles
48
+ - Applies to: new work only
49
+ - Rule: `.agents/skills/cruze-roles/verifier.md`, "Work", step 3
50
+
51
+ ### design-gives-every-value: The design gives every error, limit, timeout, transition and message owner
52
+ - Applies to: new work only
53
+ - Rule: `.agents/skills/cruze-roles/design-reviewer.md`, "Rubric", item 6
54
+
55
+ ### one-owner-per-rule: Plan gives each rule one owner, and each item to its first handler's task
56
+ - Applies to: new work only
57
+ - Rule: `.agents/skills/cruze-plan/tasks.md`, steps 2 and 4
58
+
59
+ ### deviations-reviewed: The code reviewer reports a deviation that needed a rethink
60
+ - Applies to: new work only
61
+ - Rule: `.agents/skills/cruze-roles/code-reviewer.md`, "Rubric", item 11
62
+
63
+ ### depends-on-gate: Building waits for every change a change depends on to land
64
+ - Applies to: new work only
65
+ - Rule: `.agents/skills/cruze-build/SKILL.md`, "Steps", step 1
66
+
67
+ ### build-checks-ci: Build runs the same check as verify and CI
68
+ - Applies to: new work only
69
+ - Rule: `.agents/skills/cruze-build/SKILL.md`, "The task loop", step 4
70
+
71
+ ### check-in-flight-modules: cruze check knows the modules that work in progress adds
72
+ - Applies to: new work only
73
+ - Rule: `.agents/skills/cruze-build/SKILL.md`, "The task loop", step 4
74
+
75
+ ### cites-dropped: Work doesn't cite a scenario its delta drops
76
+ - Applies to: new work only
77
+ - Rule: `.agents/skills/cruze-formats/reference/specs.md`, "Spec delta"
78
+
79
+ ### planned-breaks: Plans name a break as the wrong behaviour, and put each test in the task that builds what it checks
80
+ - Applies to: new work only
81
+ - Rule: `.agents/skills/cruze-plan/tasks.md`, step 2
82
+
83
+ ### declaring-files: A task lists the file that declares what it adds
84
+ - Applies to: new work only
85
+ - Rule: `.agents/skills/cruze-plan/tasks.md`, step 3
86
+
87
+ ### named-dependencies: Every outside name the design uses comes from a dependency the using target declares
88
+ - Applies to: new work only
89
+ - Rule: `.agents/skills/cruze-research/SKILL.md`, "Adopt or build", step 7
90
+
91
+ ### nits-are-counts: Review nits get no disposition
92
+ - Applies to: new work only
93
+ - Rule: `.agents/skills/cruze-roles/SKILL.md`, "Running a review"
94
+
95
+ ### illustrated-questions: Questions, review exceptions and the walkthrough show what each option looks like
96
+ - Applies to: new work only
97
+ - Rule: `.agents/skills/cruze-grilling/illustrating.md`
@@ -10,6 +10,7 @@ Each release that changes what Cruze expects of a project ships a notes file, `n
10
10
  - [0.0.5](notes/0.0.5.md)
11
11
  - [0.0.6](notes/0.0.6.md)
12
12
  - [0.0.7](notes/0.0.7.md)
13
+ - [0.0.8](notes/0.0.8.md)
13
14
 
14
15
  ## Format
15
16
 
@@ -14,11 +14,12 @@ Run these steps for each component in a design that isn't domain logic. Domain l
14
14
  1. State the capability in one line, with the requirements that matter: platforms, performance, footprint, and license limits from the vision's constraints.
15
15
  2. Look in order: the language's standard library, the project's already-approved dependencies, then mature libraries in the ecosystem. Done when you have checked all three.
16
16
  3. For the two or three serious candidates, gather the evidence in [evaluation.md](evaluation.md).
17
- 4. Recommend one. Adopt a library, or build with a concrete reason: the behaviour is specific to this product, no candidate meets a stated constraint, every candidate brings disproportionate risk or complexity, or the capability is the product itself. Avoiding a dependency is not a reason on its own.
17
+ 4. Recommend one. Adopt a library, or build with a concrete reason: the behaviour is specific to this product, no candidate meets a stated constraint, every candidate brings disproportionate risk or complexity, or the capability is the product itself. Weigh the candidates as `.agents/skills/cruze-dependency-approval/SKILL.md` says under Weighing, where avoiding a dependency is never a reason.
18
18
  5. Record the result as a row in the `## Adopt or build` table of the feature or standalone change, as Component, Decision, Choice and Reason. At project scope, record it as the adapter's `Adopts` fact, and put rows for anything no adapter owns in the walking-skeleton change's `## Adopt or build`. When the evidence runs longer than the reason, write it to `.cruze/notes/<date>-<slug>.md` and cite that note in the reason.
19
19
  6. Take the recommendation to the user. Adopting means a new dependency, which follows `.agents/skills/cruze-dependency-approval/SKILL.md`. Building commodity functionality is the user's call as well.
20
20
  7. Verify what the design will rely on. List each claim the design or its testing approach makes about anything it doesn't build:
21
21
  - an adopted library or tool: the features or flags it needs, and each command the design spells out, such as a CI step;
22
+ - each type, trait, macro or API the design names from a package: that the package exports it at that version, and every crate, package or target that must declare the package to use it, test support included;
22
23
  - the language or runtime, such as whether a trait with async methods can be used as a trait object;
23
24
  - the platform's test environment, such as which permissions a test process gets;
24
25
  - an external system the product talks to, such as what a broker logs or how it answers.
@@ -56,4 +56,4 @@ Landed work is history, so rethink never edits an archived folder.
56
56
 
57
57
  ## Next step
58
58
 
59
- Run `cruze next`, and name what it says.
59
+ Run `cruze next`, and name what it says. Start it in a new session, so its context starts empty.
@@ -49,3 +49,5 @@ Name the first item whose blockers have all landed:
49
49
  - A feature whose requirements are settled: `architect "<feature>"`.
50
50
  - A standalone change whose design exists: `plan`.
51
51
  - A tweak without a design yet: `architect "<tweak>"`.
52
+
53
+ Start it in a new session, so its context starts empty.
@@ -38,8 +38,8 @@ A review has at most two rounds. Only blockers can force the second one.
38
38
  - You recommend anything but `fixed`: waiving, deferring or rejecting it, or a rethink because it needs a design change.
39
39
  - You would fix it differently from the reviewer's proposed fix.
40
40
 
41
- Every other finding is yours, blockers included: you fix it as the reviewer proposes. When `review.decide` in `.cruze/config.yaml` is `all`, every finding is an exception.
42
- 4. **Present them in one message.** First each exception, with your recommended disposition and why:
41
+ Every other finding is yours, blockers included: you fix it as the reviewer proposes. When `review.decide` in `.cruze/config.yaml` is `all`, every finding is an exception. Nits are a count, not findings: when a report lists any, they get no disposition and you don't bring them to the user.
42
+ 4. **Present them in one message,** written to be read cold and illustrated as `.agents/skills/cruze-grilling/illustrating.md` says. First each exception: what goes wrong, shown as the reviewer's failure scenario played out (the interaction, the code or the diagram), what the fix changes, shown before and after, and your recommended disposition and why:
43
43
  - `fixed`: you will change the work as the finding proposes.
44
44
  - `waived`: the finding is right, but the user accepts the risk.
45
45
  - `deferred`: it belongs to later work. When it is future scope, add it with `cruze features add <slug> --summary <text> --goals <ids>`.
@@ -54,13 +54,11 @@ A review has at most two rounds. Only blockers can force the second one.
54
54
 
55
55
  ### Round 2
56
56
 
57
- Run round 2 only when a round-1 blocker was fixed. It checks only whether those blockers are fixed, and raises nothing new.
57
+ Run round 2 only when a round-1 blocker was fixed. It checks only whether those blockers are fixed, and raises nothing new. You check them: in 49 recorded rounds across two projects, no reviewer re-reading a fix found a blocker still open, and the user sees every fix at the walkthrough, in the plan, or in verify's report.
58
58
 
59
- 1. **Choose who checks.** When every blocker was fixed exactly as the reviewer proposed, check the fixes yourself: a fresh reviewer re-reading a fix it proposed finds nothing new. When any blocker was fixed differently from the proposal, or its fix reaches past what the finding names, run the reviewer.
60
- 2. **Check them yourself:** read each blocker's finding against the diff of its fix, and run `cruze validate`. A blocker is fixed when the diff does what the proposal says and validate reports no new errors.
61
- 3. **Or run the reviewer** again with its usual inputs, plus the round-1 blockers and the diff of their fixes. It may be the round-1 reviewer resumed, since that context never saw the conversation that produced the work (with `cruze review`: `--round 2 --blockers <file> --base <round-1 commit>`).
62
- 4. Record the round with `round=2`, the blockers still open, `concerns=0`, and `checked_by=agent` or `checked_by=reviewer`.
63
- 5. Take any blocker still open to the user, who fixes it now, waives it or starts a rethink. There is no round 3.
59
+ 1. **Check each blocker** against the diff of its fix, and run `cruze validate`. A blocker is fixed when the diff does what the proposal says and validate reports no new errors. When a fix differs from the proposal or reaches past what the finding names, say so in the message that presents it, so the user can object.
60
+ 2. Record the round with `round=2`, the blockers still open, `concerns=0` and `checked_by=agent`.
61
+ 3. Take any blocker still open to the user, who fixes it now, waives it or starts a rethink. There is no round 3.
64
62
 
65
63
  Done when every finding has a journaled disposition, and every exception, and every blocker left open in round 2, has the user's decision.
66
64
 
@@ -70,4 +68,4 @@ Reviewers read the settled ledger so they never re-raise a decision. It consists
70
68
 
71
69
  - The `## Settled decisions` of the feature or change under review.
72
70
  - The `D<n>` lines in the `## Decisions` section of `docs/architecture.md`, and the accepted ADRs in `docs/adr/`.
73
- - Earlier dispositions, from `cruze journal list --event disposition`.
71
+ - Earlier dispositions of the item under review, from `cruze journal list --event disposition --item <ref>`. `cruze show <ref>` includes them. Never list the whole project's: two projects' reviewers read every disposition ever recorded, about 36k tokens a time, a hundred times each.
@@ -4,13 +4,13 @@ You review the code a change adds, before it lands. You work in two lanes. The s
4
4
 
5
5
  ## Inputs
6
6
 
7
- - The change's `change.md`, and its `feature.md` when it belongs to a feature.
7
+ - The change's `change.md`, and the output of `cruze show <ref>`: the elements it builds, the scenarios it delivers, the settled decisions and the ledger. Read an element it lists as cited only when the diff calls it.
8
8
  - The diff under review, from `git diff <base>` for the base commit you are given.
9
- - `docs/architecture.md`, the ADRs in `docs/adr/`, and the specs in `docs/specs/` for the capabilities the change touches.
10
- - The settled ledger, as `.agents/skills/cruze-roles/SKILL.md` defines it, with the output of `cruze journal list --event disposition`.
9
+ - The ADRs in `docs/adr/`, and the specs in `docs/specs/` for the capabilities the change touches.
10
+ - The settled ledger, as `.agents/skills/cruze-roles/SKILL.md` defines it, with the output of `cruze journal list --event disposition --item <ref>`.
11
+ - The deviations the change recorded, from `cruze journal list --event deviation --item <ref>`.
11
12
  - The output of `cruze check` and `cruze trace --change <ref>`.
12
13
  - The standards: `.agents/skills/cruze-hexagonal-design/SKILL.md`, its language file for the project's language when one exists, `.agents/skills/cruze-behavioural-testing/SKILL.md` and `.agents/skills/cruze-dependency-approval/SKILL.md`.
13
- - In round 2 only: the round-1 blockers and the diff of their fixes.
14
14
 
15
15
  ## Rubric
16
16
 
@@ -27,10 +27,11 @@ Review against these items and nothing else. Each finding names one.
27
27
 
28
28
  5. **Placement.** Every file the diff adds or changes is where the architecture puts its owner, and matches the task's file targets or a recorded deviation.
29
29
  6. **Direction.** Dependencies point inward. No technology type crosses a port. Inbound adapters hold no domain rules or workflow.
30
- 7. **Size.** No file holds two responsibilities or passes its budget without a recorded exception.
30
+ 7. **Size.** No file holds two responsibilities or passes its budget without a recorded exception. No code is bent to fit a budget, such as a type alias, a string where an error type belongs, types merged or a function placed away from its owner to stay under a limit.
31
31
  8. **Idiom.** The code is idiomatic for the language, uses its standard tooling, and follows the language file's rules, including where tests live. Tests reuse the fixtures and harnesses in the test support module rather than redefining them.
32
- 9. **Libraries.** Nothing hand-writes what a mature library does without a recorded `build` decision, and every new dependency has a recorded approval.
32
+ 9. **Libraries.** Nothing hand-writes what a mature library does without a recorded `build` decision, and every new dependency has a recorded approval. No code works around a dependency or a feature: hand-written code, a shell command or a less idiomatic construct chosen because the clean one needs a dependency is a finding.
33
33
  10. **Contracts.** Each adapter honours its port's `Operations`, and each fake passes the same contract tests as the real adapter.
34
+ 11. **Deviations.** Each deviation the change recorded is a choice build may make on its own: a private helper or a new file inside the owner's module, a `check.exceptions` entry, a fixture in the test support module, or two tasks swapped. A deviation that added an element, port operation or scenario, wrote a file outside the owner's module, changed an approved scenario's behaviour or its test, worked around a dependency or a feature, or settled a limit, timeout, message or error case the design doesn't give, needed a rethink: a blocker, with `Needs design change` naming the element. Two projects recorded 13 such deviations.
34
35
 
35
36
  ## Findings
36
37
 
@@ -46,10 +47,9 @@ Each finding has this shape:
46
47
  ```
47
48
 
48
49
  - A **blocker** means a scenario fails or is untested, a protected test was weakened, a layering rule is broken, an unapproved dependency was added, or an adapter can't honour its port's `Operations`. A finding that needs a design change is a blocker. Everything else is a **concern**.
49
- - List every blocker. List at most 5 concerns, or one per 5 tasks of the change when that is more, the most important first. Count the rest as nits without listing them. Report only what would go wrong: no concerns is a valid report, and a small change usually has few.
50
+ - List every blocker. List at most 5 concerns, or one per 5 tasks of the change when that is more, the most important first. Count the rest as nits: give only their number, never their text. Report only what would go wrong: no concerns is a valid report, and a small change usually has few.
50
51
  - Drop any finding that contradicts the settled ledger. The only exception is a blocker with new evidence, which you mark `New evidence:` and explain.
51
52
  - When a finding could be caught mechanically, add `Check: <the cruze check rule or linter setting that would catch it>`.
52
- - In round 2, report only whether each round-1 blocker is fixed, as `fixed` or `open` with the reason. Raise nothing new.
53
53
 
54
54
  ## Report
55
55
 
@@ -5,14 +5,13 @@ You review a design, or a change's plan, before any code exists. You find the pl
5
5
  ## Inputs
6
6
 
7
7
  - The artifact under review:
8
- - Feature scope: the feature's `feature.md`.
8
+ - Feature scope: the feature's `feature.md`, and the output of `cruze show <ref>` for the elements its delta cites, each with where it sits, and the settled ledger.
9
9
  - Project scope: `docs/architecture.md`, `.cruze/config.yaml` and `docs/roadmap.md`, plus the walking-skeleton change once it exists.
10
- - A plan: the change's `change.md`, its `feature.md` when it has one, and the output of `cruze status --overlap --change <ref>`.
11
- - The living docs it builds on: `docs/vision.md`, `docs/glossary.md`, `docs/architecture.md`, the ADRs in `docs/adr/`, and the specs in `docs/specs/` that the artifact cites.
12
- - The settled ledger, as `.agents/skills/cruze-roles/SKILL.md` defines it, with the output of `cruze journal list --event disposition`.
10
+ - A plan: the change's `change.md`, the output of `cruze show <ref>`, and the output of `cruze status --overlap --change <ref>`.
11
+ - The living docs it builds on: `docs/vision.md`, `docs/glossary.md`, the ADRs in `docs/adr/`, the specs in `docs/specs/` that the artifact cites, and, from `docs/architecture.md`, its Overview, Dependency rules, Cross-cutting concerns and Decisions sections plus each element `cruze show` lists as cited. Read the whole architecture only at project scope.
12
+ - The settled ledger, as `.agents/skills/cruze-roles/SKILL.md` defines it, with the output of `cruze journal list --event disposition --item <ref>`.
13
13
  - The output of `cruze validate`.
14
14
  - The standards: `.agents/skills/cruze-hexagonal-design/SKILL.md` and `.agents/skills/cruze-formats/SKILL.md`.
15
- - In round 2 only: the round-1 blockers and the diff of their fixes.
16
15
 
17
16
  ## Rubric
18
17
 
@@ -22,8 +21,8 @@ Review against these items and nothing else. Each finding names one.
22
21
  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
22
  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
23
  4. **Direction.** Every dependency points inward and is allowed by the `RULE` elements and the `layers:` in `.cruze/config.yaml`.
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
- 6. **Contracts.** Every new or changed port's `Operations` state what each operation guarantees, how it fails, and who owns cleanup.
24
+ 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 type, trait, macro, framework or tool the design names from outside the project comes from a dependency and feature the design approves, declared by each crate, package or target that uses it, test support included, and allowed by the import rules in `XC` and the `layers:` in `.cruze/config.yaml`. A choice whose reason is avoiding a dependency, a crate or a feature, where the idiomatic design needs one, is a concern: name the idiomatic design. 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.
25
+ 6. **Contracts.** Every new or changed port's `Operations` state what each operation guarantees, how it fails, and who owns cleanup. Check each use case and each port operation for the values build would otherwise have to pick: a named error for every failure a caller can meet, where a panic, an `expect` or "caller error" counts as missing; a number for every timeout, limit, buffer and poll interval; what a caller observes after each state transition, and what happens to queued or in-flight work at it; and one owner for every message a scenario quotes. Each one missing is a blocker: two projects settled 35 of them during build, as deviations.
27
26
  7. **Flows.** The key scenarios have a `FLOW` whose participants are all in its `Elements`, including the failure paths the scenarios need.
28
27
  8. **Consistency.** The design agrees with `docs/architecture.md`, the accepted ADRs and what earlier changes landed, or changes them explicitly through a `MODIFIED` operation or a new ADR. Its own elements agree with each other. Check every pair, not a sample:
29
28
  - each use case's `Output` and `Errors` against the `FLOW`s that serve it and the `XC` rules that apply to it;
@@ -40,8 +39,8 @@ Review against these items and nothing else. Each finding names one.
40
39
  When the artifact is a change's plan, review against these items instead.
41
40
 
42
41
  1. **Test plan.** Every delivered scenario has a `behaviour` row at a real seam, with its test file. Every port that a built adapter implements has a `contract` row. Seams need fakes of driven ports only, never mocks of internal code.
43
- 2. **Tasks.** Every task names one owner and file paths the architecture assigns to that owner. Every scope ID is proved by a task. Each task is small enough to build red to green and commit on its own. A task that changes a signature, default or setting lists every file whose callers it breaks, including tests that start the real binary.
44
- 3. **Order.** Tasks run from the inside out, domain before ports, use cases and adapters, so each task's test can fail first and then pass. The last tasks wire the composition root.
42
+ 2. **Tasks.** Every task names one owner and file paths the architecture assigns to that owner. Every scope ID is proved by a task. Each task is small enough to build red to green and commit on its own. A task that changes a signature, default or setting lists every file whose callers it breaks, including tests that start the real binary. A task that adds a module, file or dependency lists the file that declares it, such as a crate root, a `mod.rs`, a package manifest or an Xcode project. A rule, text, format or conversion that several elements apply has one owning file, and each later task that applies it names that file as a callee rather than restating it. An accessor, variant or type arrives in the task whose code first handles, matches or constructs it, never in the task that only tests it.
43
+ 3. **Order.** Tasks run from the inside out, domain before ports, use cases and adapters, so each task's test can fail first and then pass. A test sits in the task that builds the behaviour it checks: a test that an earlier task's code already passes is in the wrong task. A break the plan names is a behaviour the task's code could get wrong, not an edit that may not compile. The last tasks wire the composition root.
45
44
  4. **No design.** Nothing in the plan changes the architecture or the specs. A plan that needs a new element, operation or scenario goes back to `architect` or `rethink`.
46
45
  5. **Risks and overlap.** The riskiest task is named, with how it is checked. Overlap with work on other branches is resolved or accepted.
47
46
  6. **Ambiguity.** Name each task a fresh implementer could reasonably build differently from what the plan means.
@@ -59,10 +58,9 @@ Each finding has this shape:
59
58
  ```
60
59
 
61
60
  - A **blocker** means building from the design as written would produce wrong behaviour, a layering violation or an untestable scenario. Two elements that contradict each other are always a blocker, because an implementer has to pick one. Everything else is a **concern**.
62
- - List every blocker. List at most 5 concerns, or for a plan one per 5 tasks when that is more, the most important first. Count the rest as nits without listing them. Report only what would go wrong: no concerns is a valid report, and a small artifact usually has few.
61
+ - List every blocker. List at most 5 concerns, or one per 5 scenarios in a feature's spec delta, or for a plan one per 5 tasks, when that is more, the most important first. Count the rest as nits: give only their number, never their text. Report only what would go wrong: no concerns is a valid report, and a small artifact usually has few.
63
62
  - Drop any finding that contradicts the settled ledger. The only exception is a blocker with new evidence, which you mark `New evidence:` and explain.
64
63
  - When a finding could be caught mechanically, add `Check: <the cruze check rule or linter setting that would catch it>`.
65
- - In round 2, report only whether each round-1 blocker is fixed, as `fixed` or `open` with the reason. Raise nothing new.
66
64
 
67
65
  ## Report
68
66
 
@@ -8,7 +8,7 @@ You survey existing code for structure that departs from Cruze's current standar
8
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
9
  - `docs/architecture.md`, for the module map and the elements the code implements, and `.cruze/config.yaml`.
10
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`.
11
+ - The journal, landed work included: the output of `cruze journal list --event deviation --all`, `cruze journal list --event rethink --all` and `cruze journal list --event disposition --all`.
12
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
13
 
14
14
  ## Work
@@ -4,23 +4,24 @@ You check that a change actually works when someone uses it. The tests passing i
4
4
 
5
5
  ## Inputs
6
6
 
7
- - The change's `change.md`, and its `feature.md` when it belongs to a feature. The scenarios it delivers are listed under `Delivers` in its scope, and written out in the spec delta.
7
+ - The change's `change.md`, for its test plan, and the output of `cruze show <ref>`: the scenarios it delivers, written out, and the flows and elements it builds.
8
8
  - The project's commands, from `commands:` in `.cruze/config.yaml`, and from `README.md` when they aren't there.
9
9
  - The mechanical checks' results, with the commit they ran at.
10
- - The flows in `docs/architecture.md` that serve those scenarios.
11
10
 
12
11
  ## Work
13
12
 
14
13
  1. **Build.** Build the system with the project's build command, and record whether it passed. The test suite already ran at the commit in your inputs, and CI runs it again before the merge, so don't run it. Run a single test only when it is how a scenario reaches its real front door, such as a UI test that drives the app.
15
14
  2. **Trace.** Run `cruze trace --change <ref>` and record its result.
16
- 3. **Run each scenario.** For every scenario the change delivers:
15
+ 3. **Run each scenario that has a front door.** A scenario whose test plan row names an inbound adapter, a screen or a real adapter as its seam is one a user meets, so run it:
17
16
  - Set up its GIVEN with real things where you can: configuration files, directories, a local test server, a pseudo-terminal. Put them in a temporary directory outside the project.
18
17
  - Perform its WHEN through the real inbound adapter, such as the CLI binary or an HTTP request, the way a user or client would.
19
18
  - Compare what you observe with its THEN, word for word where the scenario quotes output.
20
- 4. **Probe one step further.** For each scenario, try the nearest thing a user would plausibly do next or get wrong, such as the same command twice, a typo in the input, or an interrupt. Report surprises; they are not failures of the scenario.
19
+
20
+ A scenario whose row runs through a use case with fakes is proved by its behaviour test, which passed at the checks commit: give the test's file and name as `Ran:` and the commit as `Observed:`, and don't drive it. Driving every scenario cost one app project 31 minutes a change, for one defect in 28 runs.
21
+ 4. **Probe one step further.** For each scenario you ran, try the nearest thing a user would plausibly do next or get wrong, such as the same command twice, a typo in the input, or an interrupt. Report surprises; they are not failures of the scenario.
21
22
  5. **Clean up** everything you created outside the project.
22
23
 
23
- When a scenario needs something you don't have, such as real hardware or a remote host, don't fake it. Mark it `could not run`, and say exactly what it needs, so a person can run it by hand.
24
+ When a scenario needs something you don't have, such as real hardware or a remote host, don't fake it. Mark it `could not run`, and say exactly what it needs, so a person can run it by hand. The same holds for time: don't idle through a timer, a scheduled notification or a device settling. Drive time through the project's seam for it when there is one; otherwise a wait over 30 seconds is `could not run`, with the wait a person needs.
24
25
 
25
26
  ## Report
26
27
 
@@ -33,3 +33,5 @@ Triage starts from a symptom and ends with a fix, or with the right step to fix
33
33
  - Code defect fixed: `land`, which lands bug fixes too.
34
34
  - Spec gap: `architect "<tweak>"`.
35
35
  - Design flaw: `rethink`.
36
+
37
+ Start it in a new session, so its context starts empty.
@@ -16,7 +16,7 @@ Verify answers one question: does this change work when someone uses it? Green t
16
16
  - When one fails, fix the code in a commit and run them again. When a fix needs design, stop and offer `rethink`.
17
17
  3. **Check the dependencies.** Follow "Checking" in `.agents/skills/cruze-dependency-approval/SKILL.md` against the base commit. An unrecorded dependency is a blocker.
18
18
  4. **Run the verifier.** Run the verifier role from `.agents/skills/cruze-roles/SKILL.md` for the change, with the mechanical checks' results and the commit they ran at. It must be able to build and run the system. When it runs through `cruze review`, check that `review.command` in `.cruze/config.yaml` allows the project's build and run commands, and add them with the user's agreement. Done when you have its report, with a result for every delivered scenario.
19
- 5. **Review the code.** Run the review in the roles skill with the code reviewer, `review=code`, `--item <ref>` and the base commit. Fix each finding whose disposition is `fixed` in a commit, running the tests each fix touches, and `test` when `commands:` has a `test_full`. Then check the fixes in round 2.
19
+ 5. **Review the code.** Run the review in the roles skill with the code reviewer, `review=code`, `--item <ref>` and the base commit. Fix each finding whose disposition is `fixed` in a commit, running the tests each fix touches, and `test` when `commands:` has a `test_full`. Then check the fixes yourself, as round 2 in the roles skill says.
20
20
  6. **Write the manual test script** as `manual-test.md` in the change folder, following [manual-test.md](manual-test.md). Commit it.
21
21
  7. **Check the fixes once.** When code changed since the last `checks` entry, run the format, lint and build commands and the full suite once more, and record them as in step 2. Done when the last `checks` entry is at a commit with no code changes since.
22
22
  8. **Report.** Show the user four things:
@@ -41,3 +41,5 @@ Done when the decision is recorded.
41
41
 
42
42
  - Accepted: `land`.
43
43
  - Sent back: `build` for reopened tasks, or `rethink` for design.
44
+
45
+ Start it in a new session, so its context starts empty.
@@ -15,7 +15,7 @@ export async function runJournal(context: CliContext, args: string[], options: O
15
15
  return { json: entry, human: `Recorded ${entry.event}.` };
16
16
  }
17
17
  if (args[0] === "list") {
18
- const entries = await listEvents(context, options.event, options.item);
18
+ const entries = await listEvents(context, options.event, options.item, options.all);
19
19
  return { json: entries, human: entries.map((e) => `${e.at} ${e.event} ${JSON.stringify({ ...e, at: undefined, event: undefined, by: undefined })}`).join("\n") || "No entries." };
20
20
  }
21
21
  throw new UsageError(`cruze journal takes add or list, not "${args[0]}"`);
@@ -0,0 +1,27 @@
1
+ import { showScope, type ScopeReport } from "../../../../app/use_cases/show_scope.ts";
2
+ import type { ShownElement } from "../../../../domain/project/scope_view.ts";
3
+ import { requireArgs, type CliContext, type CommandResult } from "../cli_context.ts";
4
+
5
+ export async function runShow(context: CliContext, args: string[]): Promise<CommandResult> {
6
+ requireArgs(args, ["ref"]);
7
+ const report = await showScope(context, args[0] ?? "");
8
+ return { json: report, human: render(report) };
9
+ }
10
+
11
+ /** The scope as a document an agent reads in place of the living docs. */
12
+ function render(report: ScopeReport): string {
13
+ const lines = [`# ${report.ref}${report.feature === undefined ? "" : ` (feature ${report.feature})`}`, ""];
14
+ lines.push("## Elements", "", ...blocks(report.elements), "## Scenarios", "", ...blocks(report.scenarios));
15
+ lines.push("## Cited, not shown", "");
16
+ lines.push(...(report.cited.length === 0 ? ["None."] : report.cited.map((c) => `- ${c.id}: ${c.title} (${c.source})`)), "");
17
+ lines.push("## Settled decisions", "", ...(report.decisions.length === 0 ? ["None."] : report.decisions), "");
18
+ lines.push("## Dispositions", "");
19
+ lines.push(...(report.dispositions.length === 0 ? ["None."] : report.dispositions.map((d) => `- [${d.disposition}, ${d.review}] ${d.finding}: ${d.reason}`)), "");
20
+ if (report.missing.length > 0) lines.push("## Missing", "", ...report.missing.map((id) => `- ${id} is defined nowhere`), "");
21
+ return lines.join("\n").trimEnd();
22
+ }
23
+
24
+ function blocks(elements: ShownElement[]): string[] {
25
+ if (elements.length === 0) return ["None.", ""];
26
+ return elements.flatMap((element) => [`<!-- ${element.source} -->`, ...element.body.join("\n").trimEnd().split("\n"), ""]);
27
+ }
@@ -32,9 +32,25 @@ export async function runStatus(context: CliContext, _args: string[], options: O
32
32
  }
33
33
  }
34
34
  for (const change of report.standalone) lines.push(`${describe(`change ${change.ref}`, change.approval)}, tasks ${change.tasksDone}/${change.tasksTotal}`);
35
- return { json: report, human: lines.join("\n") };
35
+ const json = {
36
+ ...report,
37
+ documents: report.documents.map(compact),
38
+ features: report.features.map((f) => ({ ...f, approval: compact(f.approval), changes: f.changes.map((c) => ({ ...c, approval: compact(c.approval) })) })),
39
+ standalone: report.standalone.map((c) => ({ ...c, approval: compact(c.approval) })),
40
+ };
41
+ return { json, human: lines.join("\n") };
36
42
  }
37
43
 
44
+ /** The state without the record's hashes: an agent reads status often, and the hashes were a third of each read. */
45
+ function compact(status: ApprovalStatus): Omit<ApprovalStatus, "record"> & { approvedAt?: string; by?: string; basis?: string } {
46
+ const { record, ...rest } = status;
47
+ if (record === undefined) return rest;
48
+ return { ...rest, approvedAt: record.approvedAt, by: record.by, ...(record.basis === undefined ? {} : { basis: record.basis }) };
49
+ }
50
+
51
+ /** Every step starts a new session: one session that ran plan, build, verify and land reached 745k tokens of context. */
52
+ const FRESH_SESSION = "Run it in a new session, so its context starts empty.";
53
+
38
54
  const BASIS: Record<string, string> = { "plan-review": " (by the agent, after its plan review)", rethink: " (re-stamped by the agent after a rethink)" };
39
55
 
40
56
  function describe(label: string, status: ApprovalStatus): string {
@@ -46,6 +62,6 @@ function describe(label: string, status: ApprovalStatus): string {
46
62
  export async function runNext(context: CliContext): Promise<CommandResult> {
47
63
  const report = await suggestNext(context);
48
64
  const describe = (s: { step: string; target?: string; reason: string }): string => `${s.step}${s.target === undefined ? "" : ` ${s.target}`}: ${s.reason}`;
49
- const lines = [`Next: ${describe(report.next)}`, ...report.also.map((s) => `Also: ${describe(s)}`)];
50
- return { json: report, human: lines.join("\n") };
65
+ const lines = [`Next: ${describe(report.next)}`, ...report.also.map((s) => `Also: ${describe(s)}`), FRESH_SESSION];
66
+ return { json: { ...report, session: FRESH_SESSION }, human: lines.join("\n") };
51
67
  }
@@ -7,6 +7,7 @@ import { runRealign } from "./commands/realign.ts";
7
7
  import { runFeedback, runJournal } from "./commands/journal.ts";
8
8
  import { runAbandon, runFeatures, runRoadmap, runTask } from "./commands/progress.ts";
9
9
  import { runReviewCommand } from "./commands/review.ts";
10
+ import { runShow } from "./commands/show.ts";
10
11
  import { runNext, runStatus } from "./commands/status.ts";
11
12
 
12
13
  /** A command, and the options it takes: one list, or one per subcommand for a command that has them. */
@@ -22,6 +23,7 @@ export const COMMANDS: Record<string, Command> = {
22
23
  validate: { handler: runValidate, options: [], usage: "validate check every document against the formats" },
23
24
  status: { handler: runStatus, options: ["gate", "override", "overlap", "change"], usage: "status [--gate build [--override <reason>]] [--overlap [--change <ref>]]" },
24
25
  next: { handler: runNext, options: [], usage: "next the step to run next on this branch, and why" },
26
+ show: { handler: runShow, options: [], usage: "show <ref> the design a change builds, or a feature's delta, with what it cites and its settled ledger" },
25
27
  approve: {
26
28
  handler: runApprove,
27
29
  options: ["replan", "rebase", "by-agent", "restamp-unchanged"],
@@ -35,7 +37,7 @@ export const COMMANDS: Record<string, Command> = {
35
37
  task: { handler: runTask, options: { done: ["change", "commit"], deviation: ["change"], reopen: ["change"] }, usage: "task <done|deviation|reopen> <T#> [text] [--change <ref>] [--commit <sha>]" },
36
38
  features: { handler: runFeatures, options: { add: ["summary", "goals"], update: ["summary"], drop: ["reason"] }, usage: "features <add|update|drop> <slug> [--summary <text>] [--goals <ids>] [--reason <text>]" },
37
39
  roadmap: { handler: runRoadmap, options: { prune: [] }, usage: "roadmap prune clear landed items from the roadmap status when a release closes" },
38
- journal: { handler: runJournal, options: { add: ["set", "item"], list: ["event", "item"] }, usage: "journal <add <event> --set key=value... [--item <ref>]|list [--event <event>] [--item <ref>]>" },
40
+ journal: { handler: runJournal, options: { add: ["set", "item"], list: ["event", "item", "all"] }, usage: "journal <add <event> --set key=value... [--item <ref>]|list [--event <event>] [--item <ref>] [--all]>" },
39
41
  review: { handler: runReviewCommand, options: ["item", "round", "blockers", "base"], usage: "review <role> [--item <ref>] [--round 1|2 --blockers <file>] [--base <commit>] run a role in a fresh agent context" },
40
42
  feedback: { handler: runFeedback, options: { export: ["out", "no-redact"] }, usage: "feedback export [--out <file>] [--no-redact]" },
41
43
  trace: { handler: runTrace, options: ["all", "change"], usage: "trace [--all] [--change <ref>] link scenarios to the tests that prove them" },
@@ -15,6 +15,8 @@ import { isPlanned, readChangesTable } from "../../domain/project/plan_parts.ts"
15
15
  import { readProgress } from "../../domain/project/progress.ts";
16
16
  import { approvalFolder, approvalsPath, type ProjectView } from "../../domain/project/project_view.ts";
17
17
  import { featureOf } from "../../domain/project/work_items.ts";
18
+ import type { Problem } from "../../domain/validation/problem.ts";
19
+ import { SIZE_RULES } from "../../domain/validation/size_rules.ts";
18
20
  import { validateProject } from "../../domain/validation/validate_project.ts";
19
21
  import { appendJournal, loadView, withFiles, type ProjectDeps } from "../project_context.ts";
20
22
 
@@ -87,11 +89,13 @@ export async function approveArtifact(deps: ProjectDeps, ref: string, options: A
87
89
  if (text !== view.snapshot.get(path)) edits.set(path, text);
88
90
  view = withFiles(view, edits);
89
91
 
90
- const errors = validateProject(view).filter((p) => p.path === path && p.severity === "error");
92
+ const problems = validateProject(view).filter((p) => p.path === path);
93
+ const errors = problems.filter((p) => p.severity === "error");
91
94
  if (errors.length > 0) {
92
95
  const listing = errors.map((p) => ` ${p.path}:${p.line ?? ""} ${p.message}`).join("\n");
93
96
  throw new CruzeError("invalid", `${path} has ${errors.length} problem(s) to fix before approval:\n${listing}`);
94
97
  }
98
+ requireSizesAccepted(problems, path);
95
99
 
96
100
  const { hash, upstream } = fingerprint(view, path, parseMarkdown(text));
97
101
  const folder = approvalFolder(view, path);
@@ -154,6 +158,20 @@ function requireRebased(view: ProjectView, deltaIds: Set<string>, previous: Appr
154
158
  }
155
159
  }
156
160
 
161
+ /**
162
+ * A size warning stands until the work is split or the user accepts it under `changes.exceptions`.
163
+ * A 28-task change approved over its warning went on to 26 deviations, so approval waits for one or the other.
164
+ */
165
+ function requireSizesAccepted(problems: Problem[], path: string): void {
166
+ const standing = problems.filter((p) => SIZE_RULES.includes(p.rule));
167
+ if (standing.length === 0) return;
168
+ const listing = standing.map((p) => ` ${p.message} [${p.rule}]`).join("\n");
169
+ throw new CruzeError(
170
+ "size-unaccepted",
171
+ `${path} has ${standing.length} size warning(s). Split the work through rethink, or record the user's acceptance under changes.exceptions in .cruze/config.yaml, as the item's ref and their reason:\n${listing}`,
172
+ );
173
+ }
174
+
157
175
  function notWork(path: string): never {
158
176
  throw new CruzeError("needs-user", `${path} is a project document, so the user approves it`);
159
177
  }
@@ -6,6 +6,7 @@ import { FIRST_RELEASE, isPending } from "../../domain/versions/rule_versions.ts
6
6
  import type { CruzeConfig } from "../../domain/config.ts";
7
7
  import type { ProjectView } from "../../domain/project/project_view.ts";
8
8
  import { resolveChange } from "../../domain/project/artifact_ref.ts";
9
+ import { readDelta } from "../../domain/project/deltas.ts";
9
10
  import { activeChange } from "../../domain/status/work_status.ts";
10
11
  import { traceScenarios, type TraceReport } from "../../domain/trace.ts";
11
12
  import { loadView, type ProjectDeps } from "../project_context.ts";
@@ -88,8 +89,14 @@ function globBase(glob: string): string {
88
89
  return fixed.join("/");
89
90
  }
90
91
 
92
+ /**
93
+ * Every module's Path: the living docs' modules, and the modules that work in progress adds or
94
+ * changes in its architecture delta, since their files are written before land merges them.
95
+ */
91
96
  function modulePaths(view: ProjectView): string[] {
92
- return [...view.living.elements.values()]
93
- .filter((e) => e.id.startsWith("MOD-"))
94
- .flatMap((e) => (factsOf(e.doc, e.element).get("Path") ?? []).flatMap((value) => [...value.matchAll(/`([^`]+)`/g)].map((m) => m[1] ?? "")));
97
+ const living = [...view.living.elements.values()].filter((e) => e.id.startsWith("MOD-")).map((e) => ({ doc: e.doc, element: e.element }));
98
+ const inFlight = view.items
99
+ .filter((item) => item.kind !== "change" && !item.archived)
100
+ .flatMap((item) => readDelta(item.doc).entries.filter((entry) => entry.element.id.startsWith("MOD-") && entry.element.op !== "REMOVED").map((entry) => ({ doc: item.doc, element: entry.element })));
101
+ return [...living, ...inFlight].flatMap(({ doc, element }) => (factsOf(doc, element).get("Path") ?? []).flatMap((value) => [...value.matchAll(/`([^`]+)`/g)].map((m) => m[1] ?? "")));
95
102
  }
@@ -18,16 +18,19 @@ export async function recordEvent(deps: ProjectDeps, event: string, fields: Reco
18
18
  return appendJournal(deps, folder, event, { ...recorded, cruze: deps.bundle.version });
19
19
  }
20
20
 
21
- /** The journal, or one feature's or change's part of it: its own journal, and a feature's changes' too. */
22
- export async function listEvents(deps: ProjectDeps, event?: string, item?: string): Promise<JournalEntry[]> {
21
+ /**
22
+ * The journal of the work in flight and the project, or of one feature or change (a feature's changes' too).
23
+ * Landed work's entries are history, listed only with `all`, since every entry ever recorded is too much to read.
24
+ */
25
+ export async function listEvents(deps: ProjectDeps, event?: string, item?: string, all = false): Promise<JournalEntry[]> {
23
26
  const view = await loadView(deps.files);
24
- let entries = view.journal;
27
+ let folders = [".cruze", ...view.items.filter((i) => all || !i.archived).map((i) => i.folder)];
25
28
  if (item !== undefined) {
26
29
  const resolved = resolveArtifact(view, item).item;
27
30
  if (resolved === undefined) throw new CruzeError("not-found", `"${item}" is not a feature or change`);
28
- const parts = [resolved, ...view.items.filter((i) => resolved.kind === "feature" && i.featureRef === resolved.ref)];
29
- entries = parts.flatMap((part) => parseJournal(view.snapshot.get(journalPath(part.folder))).entries).sort((a, b) => a.at.localeCompare(b.at));
31
+ folders = [resolved, ...view.items.filter((i) => resolved.kind === "feature" && i.featureRef === resolved.ref)].map((part) => part.folder);
30
32
  }
33
+ const entries = folders.flatMap((folder) => parseJournal(view.snapshot.get(journalPath(folder))).entries).sort((a, b) => a.at.localeCompare(b.at));
31
34
  return event === undefined ? entries : entries.filter((entry) => entry.event === event);
32
35
  }
33
36
 
@@ -0,0 +1,35 @@
1
+ import { CruzeError } from "../../domain/cruze_error.ts";
2
+ import { resolveArtifact } from "../../domain/project/artifact_ref.ts";
3
+ import { scopeView, type ScopeView } from "../../domain/project/scope_view.ts";
4
+ import { loadView, type ProjectDeps } from "../project_context.ts";
5
+ import { listEvents } from "./journal_events.ts";
6
+
7
+ export interface Disposition {
8
+ finding: string;
9
+ disposition: string;
10
+ reason: string;
11
+ review: string;
12
+ }
13
+
14
+ export interface ScopeReport extends ScopeView {
15
+ /** The settled ledger of the item: the feature's dispositions and its changes', or a standalone change's. */
16
+ dispositions: Disposition[];
17
+ }
18
+
19
+ /**
20
+ * The design a change builds, or a feature's whole delta, with the elements it cites and the
21
+ * item's settled ledger: what plan, build and the reviewers read instead of the whole living docs.
22
+ */
23
+ export async function showScope(deps: ProjectDeps, ref: string): Promise<ScopeReport> {
24
+ const view = await loadView(deps.files);
25
+ const { item } = resolveArtifact(view, ref);
26
+ if (item === undefined) throw new CruzeError("not-work", `"${ref}" is not a feature or change; show takes a feature or change ID`);
27
+ const scope = scopeView(view, item);
28
+ const dispositions = (await listEvents(deps, "disposition", scope.feature ?? item.ref, true)).map((entry) => ({
29
+ finding: String(entry["finding"] ?? ""),
30
+ disposition: String(entry["disposition"] ?? ""),
31
+ reason: String(entry["reason"] ?? ""),
32
+ review: String(entry["review"] ?? ""),
33
+ }));
34
+ return { ...scope, dispositions };
35
+ }