@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 +20 -5
- package/package.json +1 -1
- package/templates/pharo-references/pharo-coding.md +105 -0
- package/templates/pharo-references/tool-reference.md +78 -158
- package/templates/pharo-skills/pharo-ci-repro/references/pharo-smalltalkci.md +4 -28
- package/templates/pharo-skills/pharo-code-profiling/SKILL.md +19 -38
- package/templates/pharo-skills/pharo-image-sync/SKILL.md +47 -0
- package/templates/pharo-skills/pharo-image-sync/agents/openai.yaml +4 -0
- package/templates/pharo-skills/pharo-mcp-recovery/SKILL.md +1 -1
- package/templates/pharo-skills/pharo-project-load/SKILL.md +2 -1
- package/templates/pharo.AGENTS.md +16 -12
- package/templates/pharo-references/debugging.md +0 -70
- package/templates/pharo-references/getting-started.md +0 -151
- package/templates/pharo-references/pharo-coding-rules.md +0 -147
- package/templates/pharo-references/pharo-coding-style.md +0 -141
- package/templates/pharo-references/safety-and-ecosystem.md +0 -322
- package/templates/pharo-references/source-vs-live-image.md +0 -75
- package/templates/pharo-references/troubleshooting.md +0 -145
- package/templates/pharo-references/using-pharo-mcp.md +0 -172
- package/templates/pharo-skills/pharo-image-git-handoff/SKILL.md +0 -40
- package/templates/pharo-skills/pharo-image-git-handoff/agents/openai.yaml +0 -4
- package/templates/pharo-skills/pharo-version-compat/SKILL.md +0 -54
- package/templates/pharo-skills/pharo-version-compat/agents/openai.yaml +0 -4
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](
|
|
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](
|
|
53
|
-
- [
|
|
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
|
@@ -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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
##
|
|
24
|
+
## Paging
|
|
44
25
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
54
|
-
rather than restating its documentation on every hit.
|
|
55
|
-
|
|
56
|
-
## Naming Several Targets
|
|
33
|
+
naming them, which `package_search` answers.
|
|
57
34
|
|
|
58
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
##
|
|
51
|
+
## Warnings Are Not Failures
|
|
78
52
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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.
|
|
172
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
101
|
+
## The Escape Hatch
|
|
180
102
|
|
|
181
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
193
|
-
|
|
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
|
|
100
|
-
- Run focused repeats for the flaky class
|
|
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
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
103
|
-
is done
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
|
204
|
-
|
|
205
|
-
|
|
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.
|
|
@@ -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-
|
|
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
|