@evref-bl/pharo-mcp 2.3.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,6 +32,22 @@ Metacello new
32
32
  load.
33
33
  ```
34
34
 
35
+ The baseline groups are:
36
+
37
+ - `Core`: the server, its transport and schema machinery, and the catalog
38
+ tools it dispatches through
39
+ - `IDE`: the image tools — classes, methods, packages, tests, critiques,
40
+ repositories, change history, debugging
41
+ - `UI`: the world-menu server list
42
+ - `Telemetry`: the OpenTelemetry backend
43
+ - `default`: `Core`, `IDE`, `UI` and their tests
44
+
45
+ A project embedding the server and serving tools of its own loads `Core` alone,
46
+ which leaves out the image tools and everything behind them. Loading its package
47
+ is all its tools need: a tool that names itself is in the catalog.
48
+ [Building on MCP](docs/user/building-on-mcp.md) walks through
49
+ a tool, a resource and a prompt of your own.
50
+
35
51
  Start the Streamable HTTP server:
36
52
 
37
53
  ```smalltalk
@@ -43,17 +59,16 @@ again after a restart is safe. `MCP allServers` lists what the image holds, and
43
59
  `Library → MCP Servers` in the world menu shows the same list.
44
60
 
45
61
  Connect a local MCP client to `http://127.0.0.1:4000`. Client configuration
46
- formats differ; see [Getting started](templates/pharo-references/getting-started.md) for setup
62
+ formats differ; see [Getting started](docs/user/getting-started.md) for setup
47
63
  and first calls.
48
64
 
49
65
  ## Documentation
50
66
 
51
67
  - [Documentation index](docs/README.md)
52
- - [Getting started](templates/pharo-references/getting-started.md)
53
- - [Using MCP from an agent](templates/pharo-references/using-pharo-mcp.md)
68
+ - [Getting started](docs/user/getting-started.md)
69
+ - [Building on MCP](docs/user/building-on-mcp.md)
70
+ - [Observability and UI](docs/user/observability.md)
54
71
  - [Tool reference](templates/pharo-references/tool-reference.md)
55
- - [Safety and ecosystem integration](templates/pharo-references/safety-and-ecosystem.md)
56
- - [Troubleshooting](templates/pharo-references/troubleshooting.md)
57
72
  - [Developing MCP](docs/developing-mcp.md)
58
73
 
59
74
  ## Agent Templates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evref-bl/pharo-mcp",
3
- "version": "2.3.0",
3
+ "version": "3.1.0",
4
4
  "description": "Pharo MCP agent-host templates",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,105 @@
1
+ # Pharo Coding
2
+
3
+ What is particular to Pharo, for writing, reviewing or refactoring its code
4
+ with whatever tool is at hand: through MCP, through a source projection, or in
5
+ the files themselves.
6
+
7
+ General programming practice is not repeated here, and neither are the smells a
8
+ rule recognises: Pharo's own critiques report those.
9
+
10
+ ## Dispatch Over Inspection
11
+
12
+ Pharo code communicates through messages and protocols. Send a domain message
13
+ to the receiver, or add the protocol to the object that owns the behavior,
14
+ rather than asking what the receiver is.
15
+
16
+ The reflective escapes are not equally defensible:
17
+
18
+ - `respondsTo:` as control flow is almost never right. It asks whether a send
19
+ would work instead of sending it. Keep it for genuinely reflexive code that
20
+ reads a protocol as data.
21
+ - `isKindOf:` and `isMemberOf:` are fine where a type really must be checked
22
+ and nothing else can say it, such as a boundary receiving foreign objects.
23
+ - `perform:` is fine where the selector is data rather than a disguised
24
+ switch, such as turning a transport message into a send.
25
+ - `class = ...`, method dictionary and instance-variable reflection belong to
26
+ tools, proxies and compatibility shims.
27
+
28
+ Keep reflective behavior isolated and named as tooling or boundary behavior;
29
+ do not spread it through ordinary domain logic.
30
+
31
+ ## The Main Send
32
+
33
+ A Pharo conditional is an expression, so the send can stand outside it. When
34
+ both branches perform the same operation, lift it out and let the conditional
35
+ answer only the receiver or argument that varies.
36
+
37
+ ```smalltalk
38
+ result := self transform: (condition ifTrue: [ selectedValue ])
39
+ ```
40
+
41
+ This applies to return expressions, assigned values, message arguments, and
42
+ nested expressions.
43
+
44
+ ## Nil
45
+
46
+ - Use `ifNotNil:` for local nil traversal when a boundary API can genuinely
47
+ return `nil` and the next step is asking the non-nil object for a value.
48
+ - Keep simple boolean predicates simple. Expressions such as
49
+ `value isNotNil and: [ value isReady ]` are idiomatic when the result is a
50
+ boolean. Do not rewrite them into `ifNil:`/`ifNotNil:` only to return a
51
+ boolean literal from one branch.
52
+
53
+ ## Intermediate Objects
54
+
55
+ A step that builds a collection or a string only to hand it to the next step
56
+ allocates something nobody asked for. Pharo's fused messages exist for that:
57
+ `collect:as:` rather than `collect:` and a conversion, `select:thenDo:` rather
58
+ than a filtered copy to iterate over, `String streamContents:` rather than a
59
+ chain of `,`, where every comma copies everything to its left.
60
+
61
+ Sorting is the usual case. `sorted` and `sorted:` answer a sorted copy as an
62
+ Array; `sort` and `sort:` sort a `SequenceableCollection` in place and answer
63
+ it. So `values sorted` says what `values asSortedCollection asArray` says, with
64
+ no `SortedCollection` in between. One earns its keep when a collection must
65
+ *stay* sorted as things are added to it — not for sorting something once.
66
+
67
+ ## Initialization
68
+
69
+ - Initialize sensible defaults in `initialize`.
70
+ - Do not redefine class-side `new` just to call `initialize`.
71
+ - Use class-side creation messages such as `on:`, `with:`, or domain-specific
72
+ constructors when required data exists.
73
+ - Use `basicNew` only when deliberately bypassing initialization.
74
+
75
+ ## Selectors
76
+
77
+ - The getter selector is the variable name; the setter selector is `name:`.
78
+ Not `getName` or `setName:`.
79
+ - Predicates read as predicates: `isReady`, `hasErrors`, `canHandle:`.
80
+ - Action selectors use verbs: `parseRequest:`, `addTool:`, `writeResponseOn:`.
81
+ - Conversion selectors start with `as`.
82
+
83
+ ## Collections
84
+
85
+ - Prefer collection protocol such as `collect:`, `select:`, `reject:`,
86
+ `detect:ifNone:`, `inject:into:`, `anySatisfy:`, and `allSatisfy:`.
87
+ - Do not mutate a collection while iterating it unless iterating a copy is
88
+ intentional.
89
+ - Do not rely on `add:` answering the collection; use a variable, or cascade
90
+ with `yourself`.
91
+
92
+ ## Class Extensions
93
+
94
+ Be conservative with extensions on broad system classes such as `Object`,
95
+ `Behavior`, `String`, or `Exception`. They should model a real boundary
96
+ protocol, not hypothetical defense against wrong callers.
97
+
98
+ ## Tests
99
+
100
+ - Use `assert:equals:` for comparisons, and `deny:` for negative assertions.
101
+ - Use `should:raise:` and `shouldnt:raise:` with blocks for exception
102
+ behavior.
103
+ - Use memory filesystems, fakes, or injected collaborators where a unit test
104
+ would otherwise reach outside the image.
105
+ - Keep live-image and external-state tests separate from source-only checks.
@@ -1,193 +1,113 @@
1
1
  # Tool Reference
2
2
 
3
- MCP exposes tools through `tools/list` and `tools/call`. Common tools can be
4
- advertised directly; less common tools can be found through the catalog tools.
5
- Tool names use `snake_case` names that generally put the domain before the
6
- action.
7
-
8
- Every tool returns MCP call `content` as the canonical response for LLMs, in
9
- plain text: the summary first, then any warnings, then the payload as lines.
10
- The fields that identify one record share a line as `key=value` pairs, and a
11
- value carrying spaces is quoted, as in `protocol="private - results"`. A record
12
- that names a method opens with the method, written the way Pharo writes it:
13
- `MCPTool>>batchSummaryNoun`, or `MCPTool class>>toolName` on the class side. A
14
- list of plain values is bracketed, as in `required: [name, slotName]`, and a
15
- list of records is named before its entries. Text with its own lines, such as
16
- method source, follows under its own label. `tool_get` answers a tool's
17
- contract as YAML.
3
+ How this server answers, and what it does when a call is not simply a success.
4
+ What a tool takes is in its own schema: read that with `tool_get`, not from
5
+ here.
18
6
 
19
- A result does not repeat itself: what the summary states in prose, and what a
20
- record already says another way, is left out of the lines.
21
-
22
- The same payload is also carried as `structuredContent`:
7
+ ## Reading An Answer
23
8
 
24
- ```text
25
- summary: human-readable summary
26
- warnings: array of warning strings
27
- data: structured success payload
28
- error: structured error payload
29
- ```
30
-
31
- `data` is present on success. `error` is present on failure, where the call is
32
- also flagged with the protocol's own `isError`.
9
+ A call answers text: the summary first, then any warnings, then the payload as
10
+ lines. The fields that identify one record share a line as `key=value` pairs,
11
+ and a value carrying spaces is quoted, as in `protocol="private - results"`. A
12
+ record that names a method opens with the method, written the way Pharo writes
13
+ it: `MCPTool>>batchSummaryNoun`, or `MCPTool class>>toolName` on the class side.
14
+ A list of plain values is bracketed, as in `required: [name, slotName]`, and
15
+ text with its own lines follows under its own label.
33
16
 
34
- For clients or integrations that need programmatic payloads, configure the
35
- server response fields before `start`:
17
+ A result does not repeat itself: what the summary states in prose, and what a
18
+ record already says another way, is left out of the lines.
36
19
 
37
- ```smalltalk
38
- mcp useContentOnlyToolResponses. "default"
39
- mcp useStructuredContentOnlyToolResponses.
40
- mcp useContentAndStructuredContentToolResponses.
41
- ```
20
+ The same payload is carried as `structuredContent` where the server is
21
+ configured to send it, with `data` on success and `error` on failure, the call
22
+ then also carrying the protocol's own `isError`.
42
23
 
43
- ## Compact Paging
24
+ ## Paging
44
25
 
45
- List and search tools are optimized for low token usage. A page is the tool's
46
- own size, chosen for what one of its entries costs to read: fifty classes,
47
- twenty-five methods or packages, twenty critique hits, ten repositories. When
48
- another page exists, `data` includes `nextOffset`; call the same tool again
49
- with that `offset` to continue.
26
+ A page is the tool's own size, chosen for what one of its entries costs to
27
+ read, so a page of classes is shorter than a page of repositories. When another
28
+ page exists, `data` carries `nextOffset`; call the same tool again with that
29
+ `offset`.
50
30
 
51
31
  A search answers what identifies each hit, not everything known about it.
52
32
  `repository_search` says how many packages a repository holds rather than
53
- naming them, which `package_search` answers; `critique_run` names the rule
54
- rather than restating its documentation on every hit.
55
-
56
- ## Naming Several Targets
33
+ naming them, which `package_search` answers.
57
34
 
58
- Four tools act on several targets in one call. The argument that names the
59
- target holds either one value or a list of them, and every other argument
60
- applies to all of them:
35
+ ## Several Targets At Once
61
36
 
62
- | Tool | Argument | One call covers |
63
- | --- | --- | --- |
64
- | `method_get` | `selector` | Methods of one class, on one side. |
65
- | `method_compile` | `source` | Sources compiled into one class, under one protocol. |
66
- | `class_get` | `name` | Classes read with the same options. |
67
- | `class_create` | `name` | Classes sharing a superclass, package and slots. |
37
+ Some tools take either one value or a list where they name their target, and
38
+ every other argument then applies to all of them: `method_get` and
39
+ `method_compile` by `selector` and `source`, `class_get` and `class_create` by
40
+ `name`.
68
41
 
69
- Every target is attempted. The summary states how many succeeded, each target
70
- then reads as it would have read on its own, and `data` carries one entry per
71
- target under `results`, in request order. An entry that failed carries its
72
- `error`. The call is flagged `isError` only when every target failed.
42
+ Every target is attempted. The summary says how many succeeded, `data` carries
43
+ one entry per target under `results` in request order, and an entry that failed
44
+ carries its own `error`. The call is flagged `isError` only when every target
45
+ failed.
73
46
 
74
- A call naming one target answers exactly what it answers when the argument
75
- holds a single value.
47
+ `class_update` and `method_update` are the opposite case: they take the whole
48
+ change to one target and stop at the first part that fails, rather than leaving
49
+ half a definition nobody asked for.
76
50
 
77
- ## Changing A Class Or A Method
51
+ ## Warnings Are Not Failures
78
52
 
79
- `class_update` and `method_update` take the whole change rather than one
80
- property each. Every property is optional and at least one is required;
81
- omitted ones are left alone.
53
+ Pharo signals a notification for a remark rather than a fault, and every
54
+ `Warning` is one. The tool boundary resumes them with the value the image would
55
+ have produced and the call proceeds, so a deprecated send, or an undeclared
56
+ variable during a load, does not fail it.
82
57
 
83
- Scalar properties are replaced by value, and so are the collections that hold
84
- no state:
58
+ `RBRefactoringWarning` is the exception. It reports a change that breaks
59
+ something, so it stops the call, and the error carries `impactMessages`,
60
+ `howToProceed` and `forceSupported`. Read the impact, then send the same request
61
+ with `force=true` only when the warning is acceptable; forced warnings come back
62
+ in the normal `warnings` array.
85
63
 
86
- ```text
87
- class_update name, and any of newName, superclass, package, tag, comment,
88
- layout, traits, classTraits, sharedVariables, sharedPools
89
- ```
90
-
91
- Slots are patched instead, through `slots` and `classSlots`, because they hold
92
- the instance's values. A replacement list cannot tell a rename from a
93
- remove-plus-add, and the two differ in whether the value survives; it also has
94
- to be complete, so a stale one silently drops a slot the caller did not know
95
- about.
96
-
97
- ```text
98
- slots: { add: [names]
99
- remove: [names]
100
- rename: { current: new }
101
- pullUp: [names] moves them to the superclass
102
- pushDown: [names] moves them to every direct subclass
103
- replace: [names] } the whole list; prefer add and remove
104
- ```
64
+ A method can compile and still answer critiques. They are review evidence about
65
+ code that now exists, not a transport failure.
105
66
 
106
- `method_update` takes `class` and `selector`, then `protocol`, `newSelector`
107
- or both. A selector change is classified by arity, so the one argument renames,
108
- adds or removes arguments and updates senders. Name the new argument list in
109
- `arguments` and the index map is worked out: names that match the current ones
110
- are kept, new names are added arguments needing `argumentValueExpressions`, and
111
- current names left out are removed. `permutation` is only for reordering, which
112
- names cannot express.
67
+ ## Rewriting
113
68
 
114
- The changes are applied in dependency order, not the order they were written:
115
- a superclass change first, because it decides what the rest validates against,
116
- slots added before anything refers to them and removed last, and a rename last,
117
- because every other change resolves the target by the name the caller wrote.
69
+ `method_rewrite` takes Smalltalk AST patterns, not regex, and a scope is
70
+ required: rewriting everything is something a caller asks for by naming the
71
+ packages, not by leaving an argument out.
118
72
 
119
- A change that fails stops the ones after it. This is the opposite of a batch
120
- over several targets, and deliberately so: these all land on one class or one
121
- method, so carrying on would leave half a definition nobody asked for. The
122
- error names the change that failed, the ones already applied, and the ones not
123
- attempted.
73
+ A metavariable starts with a backquote and stands for whatever is in its place,
74
+ however big, so one rule rewrites `anObject notNil` and `(anObject foo) notNil`
75
+ alike. The names must be the same on both sides; a name that appears only in
76
+ the replacement is bound to nothing. `confirm` is false by default, and the
77
+ preview carries the old and new source of every change.
124
78
 
125
- ## The Advertised Surface
126
-
127
- Fifteen tools are advertised directly: `tool_search`, `tool_get`, `tool_call`, `class_search`, `class_get`, `class_create`, `class_remove`, `method_search`, `method_source_search`, `method_reference_search`, `method_get`, `method_compile`, `method_remove`, `test_run` and `image_evaluate`. Every other tool is found with `tool_search` and called by name like any other; `tool_call` does the same for a client that will only send a name its own list holds.
128
-
129
- A server chooses its own surface before `start`:
130
-
131
- ```smalltalk
132
- mcp advertiseTools: #( 'tool_search' 'tool_get' 'tool_call' 'method_get' ).
79
+ ```json
80
+ {
81
+ "rules": [{ "match": "`@receiver notNil", "replacement": "`@receiver isNotNil" }],
82
+ "scope": { "packages": ["MyPackage"] }
83
+ }
133
84
  ```
134
85
 
135
- ## Finding A Tool
136
-
137
- The server answers for its own catalogue. `tool_search` ranks it by keyword,
138
- best match first, and answers the closest tools even when none matches every
139
- word; add words to narrow the ranking. With no query it answers the catalogue's
140
- size and its groups. `tool_get` returns one tool's metadata and input schema,
141
- and `tool_call` invokes a tool by nesting its arguments, for a client that cannot
142
- send a name outside its own list.
143
-
144
- Search before calling anything less common or high-risk, and read the schema
145
- before invoking it.
146
-
147
- Tools are named `domain_action`, and the domains are:
148
-
149
- | Group | Covers |
150
- | --- | --- |
151
- | `classes` | Defining classes and changing their slots, traits, shared variables, pools, layout, package and comment, including the slot refactorings. |
152
- | `methods` | Compiling, reading, removing and rewriting methods, renaming selectors and protocols, and searching by source, metadata, senders, implementors, references or equivalence. |
153
- | `repositories` | What the image knows about its Iceberg repositories: identity, package membership, working-copy changes, export, commit, and reloading the image from the files. Branches, remotes and transfers are git's, and belong in a shell. |
154
- | `debugging` | Reading the debuggers a person opened, with their exceptions and nearest frames, and watching what variables hold while an expression runs. Neither attaches to a debugger or leaves a session behind. |
155
- | `history` | Epicea change history: locating files, listing entries, and applying or reverting them. |
156
- | `tests` | Running tests and running them under coverage. |
157
- | `packages` | Searching packages. |
158
- | `runtime` | `image_evaluate`, the escape hatch. |
159
- | `ui` | Reading what is on screen, as a tree of windows and the presenters or morphs inside them, or as a screenshot. Either one can take a single window instead of the whole world, for a fraction of the cost. |
160
- | `tool-catalog` | `tool_search`, `tool_get` and `tool_call`. |
161
-
162
- ## Scripting
163
-
164
- Use `image_evaluate` only when no dedicated tool fits. A successful evaluation
165
- can mutate the running image.
86
+ Any rewrite rule stands for its own patterns, Pharo's own included, so
87
+ `ruleClasses` can name one instead of writing its patterns out. A rule that
88
+ reports a critique is never applied by the tool that compiles: reporting is
89
+ that tool's job, changing it is this one's, and the caller asks.
166
90
 
167
91
  ## Mutation And Saving
168
92
 
169
93
  No tool saves the image. Mutating tools change the running image only, so
170
94
  closing it discards every change since its last save, and a mistake costs the
171
- session rather than the image file. Saving is the caller's responsibility:
172
- evaluate `Smalltalk saveSession` when a change should survive, or leave it to
173
- whoever owns the image lifecycle.
95
+ session rather than the image file. Evaluate `Smalltalk saveSession` when a
96
+ change should survive, or leave it to whoever owns the image lifecycle.
174
97
 
175
- Use `history_entry_list` to see what changed and `history_entry_revert` to undo
176
- a change in the running image; both preview first and perform with
177
- `confirm=true`.
98
+ `history_entry_list` shows what changed and `history_entry_revert` undoes it in
99
+ the running image; both preview, and perform with `confirm=true`.
178
100
 
179
- `repository_identity_verify` and `repository_change_list` are read-only.
101
+ ## The Escape Hatch
180
102
 
181
- ## Refactoring Warnings
103
+ `image_evaluate` runs arbitrary Smalltalk. Prefer a dedicated tool when one
104
+ exists: it validates its input and answers structured data.
182
105
 
183
- For edit tools that use refactoring operations, `force=false` is the default.
184
- When Pharo raises `RBRefactoringWarning`, the tool returns an error with:
185
-
186
- ```text
187
- impactMessages
188
- howToProceed
189
- forceSupported: true
190
- ```
106
+ Compiling is the case that bites. Method source separates lines with CR, and
107
+ `method_compile` normalizes what it is given, while a bare `compile:` from
108
+ `image_evaluate` stores whatever it receives, so source written with LF compares
109
+ unequal to every other method in the image.
191
110
 
192
- Rerun with `force=true` only after reviewing the impact. Forced warnings are
193
- returned as normal warnings in the successful result.
111
+ A debugger somebody opened is theirs. Read it, act on what it shows with the
112
+ ordinary class and method tools, and leave stepping, resuming and terminating
113
+ to the person at the debugger.
@@ -56,30 +56,6 @@ SmalltalkCISpec {
56
56
 
57
57
  Adjust the baseline, directory, and class for the project under test.
58
58
 
59
- ## Pharo 13 / Iceberg / libgit2 Pattern
60
-
61
- In a Pharo MCP investigation on 2026-05-16, a P13 flaky CI crash reproduced
62
- locally inside Docker. It was not a dependency download issue: loading
63
- completed and testing had started.
64
-
65
- Crash signatures included:
66
-
67
- - C stack in `libgit2.so.1.4.4`
68
- - `git_repository_head_unborn`
69
- - `git_commit_lookup`
70
- - Smalltalk frames through `IceLibgitRepository`, `IceRepository>>headCommit`,
71
- or `IceRepository>>validateCanCommit`
72
- - Test frames in a repository-management test
73
-
74
- The practical mitigation was to avoid unnecessary test-side `repository
75
- headCommit` reads and skip a P13 test that deliberately forced empty libgit2
76
- config values for a P12-only fallback path.
77
-
78
- General lesson: if a Pharo 13 Linux CI segfault points into libgit2 while
79
- testing fresh temporary repositories, treat it as a native stability surface.
80
- Avoid poking Iceberg head/commit metadata in tests unless the assertion is
81
- essential.
82
-
83
59
  ## Interpreting Common Noise
84
60
 
85
61
  - `fatal: not a git repository`: often harmless when running smalltalkCI from a
@@ -95,8 +71,8 @@ essential.
95
71
 
96
72
  After a fix:
97
73
 
98
- - Run the failing Pharo platform full suite.
99
- - Run adjacent supported platforms, usually P12 and P13 for MCP.
100
- - Run focused repeats for the flaky class/method if the original failure was
101
- intermittent.
74
+ - Run the failing Pharo platform's full suite.
75
+ - Run the adjacent versions in the project's matrix.
76
+ - Run focused repeats for the flaky class or method when the original failure
77
+ was intermittent.
102
78
  - Keep the captured failing log path in the report.
@@ -52,10 +52,9 @@ String streamContents: [ :stream | tally report: stream cutoff: 2 ]
52
52
  ## Choosing The Cutoff
53
53
 
54
54
  The cutoff is the percentage of all samples a node must hold to be printed, in
55
- the tree and in the leaves alike. Use 2 and read the report once. Over a
56
- 2400-sample run of the same workload it printed 79 lines where 5% printed 52
57
- and 1% printed 84, so it is close enough to the whole tree that a second look
58
- rarely pays, and still under a thousand tokens.
55
+ the tree and in the leaves alike. Use 2 and read the report once: over a 2400-sample
56
+ run it printed 79 lines where 1% printed 84, close enough to the whole tree
57
+ that a second look rarely pays.
59
58
 
60
59
  Raise it when the report is larger than the question. Rendering again costs no
61
60
  new run, because the tally is still in hand:
@@ -74,10 +73,9 @@ The one that decides the report is on `report:cutoff:`.
74
73
 
75
74
  ## Profiling A Workload Longer Than The Call
76
75
 
77
- Fork the run, write the report to a file, and come back for it. The fork
78
- returns the call immediately and leaves the image answering, so the profile can
79
- take as long as the workload needs. Keep the report out of a global: a file
80
- survives, is readable from outside the image, and needs no cleanup inside it.
76
+ Fork the run and write the report to a file. The fork returns the call at
77
+ once and leaves the image answering, so the profile can take as long as the
78
+ workload needs.
81
79
 
82
80
  ```smalltalk
83
81
  | path |
@@ -99,26 +97,13 @@ path writeStreamDo: [ :out |
99
97
  path fullName
100
98
  ```
101
99
 
102
- The file is written only once the run ends, so `path exists` answers whether it
103
- is done. To tell a slow run from a dead one, look for the process by the name
104
- it was forked under:
105
-
106
- ```smalltalk
107
- Process allInstances anySatisfy: [ :each |
108
- each name = 'pharo-profile' and: [ each isTerminated not ] ]
109
- ```
110
-
111
- Pharo writes CR line endings, so convert to LF as above, or the report arrives
112
- as a single line everywhere outside the image.
113
-
114
- The tally dies with the fork, so this report is written once and cannot be
115
- rendered again at another cutoff. The file costs nothing to keep, and it can be
116
- read in parts rather than whole.
117
-
118
- Forking at `userBackgroundPriority` keeps the image answering calls while the
119
- profile runs. The samples it loses to those calls are charged to another
120
- process and dropped from the report, which lowers every percentage by the same
121
- factor: read the proportions between nodes, not their absolute values.
100
+ The file appears only when the run ends, so `path exists` answers whether it
101
+ is done; a forked process that is still alive is found by the name it was
102
+ forked under. Convert CR to LF as above, or the report arrives as a single
103
+ line outside the image. The tally dies with the fork, so this report cannot
104
+ be rendered again at another cutoff. Forking at `userBackgroundPriority`
105
+ keeps the image answering, and the samples lost to those calls lower every
106
+ percentage by the same factor: read the proportions, not the values.
122
107
 
123
108
  ## Reading The Report
124
109
 
@@ -145,8 +130,6 @@ Process: (60) ...
145
130
  defined on `SequenceableCollection`.
146
131
  - `primitives` is not a method. It is the part of the parent's time no child
147
132
  accounts for: its own work, primitives and inlined code included.
148
- - The `|` bars are tree drawing. A node gets one when a sibling follows it at
149
- that level, and carries it down its subtree. They mean nothing else.
150
133
  - `**Leaves**` ranks by own time and is usually what to act on. The tree says
151
134
  who is responsible, the leaves say what is expensive.
152
135
  - `**Memory**` is the allocation delta over the run and `**GCs**` the
@@ -167,11 +150,9 @@ String streamContents: [ :stream | profiler report: stream cutoff: 2 ]
167
150
  ```
168
151
 
169
152
  Never send `spyOn:` to the class: it ends in `doReport`, which opens a window.
170
- The instance-side `spyOn:` is the headless path.
171
-
172
- Its tree is rooted at the image, so a report taken through MCP opens with the
173
- frames of the server handling the call. Find the workload's entry method and
174
- read the percentages from that node down.
153
+ Its tree is rooted at the image, so a report taken through a tool call opens
154
+ with the frames of the server handling it; read from the workload's entry
155
+ method down.
175
156
 
176
157
  ## Measuring Instead Of Profiling
177
158
 
@@ -200,6 +181,6 @@ mean: the minimum is the run least disturbed by GC and scheduling.
200
181
 
201
182
  ## Report
202
183
 
203
- Report the workload and how long it ran, the sampler and cutoff, the top tree
204
- nodes and leaves with their percentages, the change made, the before-and-after
205
- comparison, and the tests that showed behavior unchanged.
184
+ Report the workload, the sampler and cutoff, the top tree nodes and leaves with
185
+ their percentages, the change made, the before-and-after comparison, and the
186
+ tests that showed behavior unchanged.
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: pharo-image-sync
3
+ description: Move code between a live Pharo image and its Tonel files, and tell the two apart. Use before exporting image changes, after a checkout moves under the image, and whenever Iceberg, the image and the files disagree.
4
+ ---
5
+
6
+ # Pharo Image Sync
7
+
8
+ The image holds objects, the repository holds files, and Iceberg holds its own
9
+ idea of both. A clean `git status` does not mean the image is clean, and an
10
+ Iceberg diff does not mean anything was written to disk.
11
+
12
+ Git itself is ordinary shell work. These tools only cross the boundary between
13
+ the image and the files.
14
+
15
+ ## Out Of The Image
16
+
17
+ 1. Verify which repository you are about to write to: `repository_search` for
18
+ its location, branch and packages, then `repository_identity_verify` with
19
+ what you expect, which fails rather than guessing.
20
+ 2. Read the image-side diff with `repository_change_list` before writing
21
+ anything.
22
+ 3. `repository_export` writes the changed packages to Tonel and updates the
23
+ Iceberg index. It stages and commits nothing.
24
+ 4. Read the files it touched with `git diff`. Export writes what the image
25
+ holds, which includes anything loaded into it that you did not put there.
26
+
27
+ ## Into The Image
28
+
29
+ A checkout that moves under a running image leaves it running the old code.
30
+ After a branch switch, a pull, a merge or a rebase, `repository_reload` loads
31
+ the packages from the files again.
32
+
33
+ Reloading can leave classes behind in `Undeclared` when a superclass moved,
34
+ with subclasses still parented to an obsolete one. The symptom is tests failing
35
+ on classes nobody edited. Load that package again by name to repair it.
36
+
37
+ ## Stop And Ask
38
+
39
+ Stop when two registrations claim the same packages, when the branch, source
40
+ directory or package membership is not what you expect, when a head is detached
41
+ or missing, or when Iceberg and the files disagree for no reason you can name.
42
+ Resolving real divergence, or discarding image changes, is the user's call.
43
+
44
+ ## Report
45
+
46
+ Say what the image held, what was written, which files changed, and what you
47
+ did not verify.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Pharo Image Sync"
3
+ short_description: "Move code between image and files"
4
+ default_prompt: "Use $pharo-image-sync to move these changes between the Pharo image and its Tonel files."
@@ -19,7 +19,7 @@ standalone image without a safer environment-specific recovery surface.
19
19
  4. Create a replacement image only after preserving the original. Use
20
20
  `pharo-project-load` to restore MCP and the target project.
21
21
  5. Recover changes by inspecting Epicea history before confirmed application.
22
- Use `pharo-image-git-handoff` before repository attachment or Tonel export.
22
+ Use `pharo-image-sync` before repository attachment or Tonel export.
23
23
  6. Verify the recovered behavior with focused inspection or tests.
24
24
 
25
25
  ## Stop And Ask