foundry-implementation-actor 0.1.0__py3-none-any.whl
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.
- foundry_implementation_actor/__init__.py +38 -0
- foundry_implementation_actor/cli.py +107 -0
- foundry_implementation_actor/config.py +405 -0
- foundry_implementation_actor/correlation.py +193 -0
- foundry_implementation_actor/engine.py +434 -0
- foundry_implementation_actor/grounding.py +197 -0
- foundry_implementation_actor/handler.py +220 -0
- foundry_implementation_actor/schemas/agentic-context.schema.yaml +143 -0
- foundry_implementation_actor-0.1.0.dist-info/METADATA +217 -0
- foundry_implementation_actor-0.1.0.dist-info/RECORD +12 -0
- foundry_implementation_actor-0.1.0.dist-info/WHEEL +4 -0
- foundry_implementation_actor-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: foundry-implementation-actor
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Runs a headless Claude Code implementation session against one capability's own repo — a papeete-actor for one use, with the capability supplied by a sidecar.
|
|
5
|
+
Project-URL: Homepage, https://github.com/papeete-hub/foundry-implementation-actor
|
|
6
|
+
Author-email: Papeete Consulting <yoann.remy@outlook.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Keywords: actor-model,agentic,capability,claude-code,papeete
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: papeete-actor-synchronous-messaging>=0.2.1
|
|
15
|
+
Requires-Dist: papeete-version>=0.1.0
|
|
16
|
+
Requires-Dist: pyyaml>=6.0
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# foundry-implementation-actor
|
|
22
|
+
|
|
23
|
+
Runs a headless [Claude Code](https://claude.com/claude-code) implementation session against one
|
|
24
|
+
capability's own repository, then commits, pushes and publishes what it produced — inside a write
|
|
25
|
+
boundary the actor enforces rather than requests.
|
|
26
|
+
|
|
27
|
+
An **actor, for one use**, with a [`papeete-actor`](https://github.com/papeete-hub/papeete-actor)
|
|
28
|
+
underneath. The `-actor` suffix is that claim; `papeete-actor-*` names, by contrast, are transverse
|
|
29
|
+
features *of* the framework (`ADR-ECO-0022`).
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install foundry-implementation-actor
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## What it is
|
|
36
|
+
|
|
37
|
+
The machinery. It carries **no capability of its own** — the capability it serves arrives in a
|
|
38
|
+
sidecar the consuming repo writes:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
# actor-agentic-context.yaml
|
|
42
|
+
context: foundry-implementation-actor/agentic-context/v1
|
|
43
|
+
engine: claude-code
|
|
44
|
+
capability: ACME.PARTS.CAP.SUP.007.WID
|
|
45
|
+
source_repo: acme-lab/ACME.PARTS.CAP.SUP.007.WID-implementation
|
|
46
|
+
registry_repo: acme-lab/acme-governance
|
|
47
|
+
components:
|
|
48
|
+
- {name: backend, path: backend/, tests: backend/tests/, dockerfile: backend/deployment/local}
|
|
49
|
+
- {name: stub, path: stub/, tests: stub/tests/, dockerfile: stub/deployment/local}
|
|
50
|
+
ground_in:
|
|
51
|
+
- name: business
|
|
52
|
+
answers: the WHAT/WHY — domain vision, business events, ubiquitous language
|
|
53
|
+
fetch: [kpack, pack, "{capability}", --deep, --compact, --registry-repo, "{registry_repo}"]
|
|
54
|
+
into: .foundry/business.md
|
|
55
|
+
load: eager
|
|
56
|
+
- name: process
|
|
57
|
+
answers: the HOW — aggregates, commands, policies, read-models, bus, api, JSON schemas
|
|
58
|
+
fetch: [kontract, fetch, "{capability}", --compact, --registry-repo, "{registry_repo}"]
|
|
59
|
+
into: .foundry/process.md
|
|
60
|
+
load: on-demand
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
and four lines in the consuming entrypoint:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from foundry_implementation_actor import CapabilityConfig, ClaudeCodeEngine, make_implement_task
|
|
67
|
+
|
|
68
|
+
config = CapabilityConfig.load(".")
|
|
69
|
+
actor = Actor.from_card(".", mailbox=mailbox,
|
|
70
|
+
engines={config.engine: ClaudeCodeEngine(config)},
|
|
71
|
+
actions={"implement-task": make_implement_task(config)})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
A second capability instantiates the same actor by writing that file. Nothing here is subclassed,
|
|
75
|
+
hooked, or configured with a strategy object — there is one shape, and it is this one.
|
|
76
|
+
|
|
77
|
+
## What one request does
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
clone (full, not --depth 1)
|
|
81
|
+
→ checkout impl/TASK-NNN
|
|
82
|
+
→ run every ground_in fetch, write it into the clone, render CLAUDE.md
|
|
83
|
+
→ claude --print --output-format stream-json --permission-mode acceptEdits
|
|
84
|
+
→ git add <each component root>; refuse anything staged outside them
|
|
85
|
+
→ commit as the actor, push the branch
|
|
86
|
+
→ buildctl build + push one image per touched component
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
It never opens a pull request. Rendering a verdict and opening one belongs to whichever actor
|
|
90
|
+
orchestrates the pipeline, once its other members have also confirmed.
|
|
91
|
+
|
|
92
|
+
## Grounding is a precondition, not a request
|
|
93
|
+
|
|
94
|
+
`CLAUDE.md` at the working directory's root is loaded by the `claude` CLI **before the first
|
|
95
|
+
turn**, and its `@relative/path.md` imports resolve eagerly at the same moment. Verified live in
|
|
96
|
+
this actor's exact invocation shape: a question only the fetched context could answer came back in
|
|
97
|
+
`num_turns: 1` with zero tool calls.
|
|
98
|
+
|
|
99
|
+
So the envelopes are written **inside the clone** and named from a generated `CLAUDE.md`. The
|
|
100
|
+
earlier arrangement — fetch into a sibling tempdir, then ask the session in prose to "read it
|
|
101
|
+
before you start" — was not broken, it was *advisory*: whether the context entered the window was
|
|
102
|
+
the model's choice, re-made every session, and a session that skipped it looked exactly like one
|
|
103
|
+
that had read it. An `@`-import resolves relative to the file containing it, so the sibling-tempdir
|
|
104
|
+
arrangement could not have been fixed by writing a better prompt.
|
|
105
|
+
|
|
106
|
+
If the repo commits its own `CLAUDE.md`, the generated block is **appended**, never substituted.
|
|
107
|
+
|
|
108
|
+
### The two tiers
|
|
109
|
+
|
|
110
|
+
| `load:` | cost | when |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `eager` | its full token weight, every session, unconditionally | the session cannot do the work without it |
|
|
113
|
+
| `on-demand` | one line — its `answers:` and its path | useful sometimes; the session opens it if the task needs it |
|
|
114
|
+
|
|
115
|
+
Choose from a measurement of the envelope, not from taste. One such read has already been recorded
|
|
116
|
+
as putting *"40 kB of JSON on screen"*.
|
|
117
|
+
|
|
118
|
+
## One capability id, eight renderings, zero literals
|
|
119
|
+
|
|
120
|
+
Every identifier is derived from `capability` and `source_repo`. `foundry-implementation-actor
|
|
121
|
+
show` prints the table for a given sidecar:
|
|
122
|
+
|
|
123
|
+
| rendering | from |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `ACME.PARTS.CAP.SUP.007.WID` | `capability` |
|
|
126
|
+
| `acme-lab/ACME.PARTS.CAP.SUP.007.WID-implementation` | `source_repo` |
|
|
127
|
+
| `acme.parts/sup.007.wid` | the id, split at its `CAP` segment |
|
|
128
|
+
| `acme.parts.cap.sup.007.wid-backend` | the image name `papeete-version` versions |
|
|
129
|
+
| `acme-parts-cap-sup-007-wid-implementation-TASK-042-` | the clone's tempdir prefix |
|
|
130
|
+
| `ACME.PARTS.CAP.SUP.007.WID-implementation` | git `user.name` |
|
|
131
|
+
| `acme-parts-cap-sup-007-wid-implementation@users.noreply.github.com` | git `user.email` |
|
|
132
|
+
| `<registry>/acme.parts/sup.007.wid/<component>:<version>` | the published image ref |
|
|
133
|
+
|
|
134
|
+
**The image ref is a three-way contract.** Peer actors recompute the identical string and parse it
|
|
135
|
+
back apart, so it is derivation output or nothing. Never invent a tag scheme.
|
|
136
|
+
|
|
137
|
+
## The write boundary is stated once
|
|
138
|
+
|
|
139
|
+
`components[].path` — and nothing else. It used to be declared in the sidecar *and* hardcoded in
|
|
140
|
+
the module that actually enforces containment, which is how a boundary comes to be stated twice
|
|
141
|
+
and eventually stated differently.
|
|
142
|
+
|
|
143
|
+
Containment is `git add <each root>`, never `-A` and never a bare `.`, then an assertion that every
|
|
144
|
+
staged path starts with one of them. A staged path outside is **refused**, the index reset, and the
|
|
145
|
+
door answers a refusal. The session's own prompt only asks for the boundary; this is what holds it.
|
|
146
|
+
|
|
147
|
+
Which component a staged path belongs to is resolved by **longest declared prefix**, not by taking
|
|
148
|
+
the path's first segment — that shortcut is correct only while every root is one segment deep, and
|
|
149
|
+
reports the wrong component the day one of them is `src/gateway/`.
|
|
150
|
+
|
|
151
|
+
## Credentials
|
|
152
|
+
|
|
153
|
+
Two, both passed at run time, never baked into an image:
|
|
154
|
+
|
|
155
|
+
| variable | what for |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `GITHUB_TOKEN` | fine-grained PAT: `contents:write` on `source_repo`, read-only `contents` on whatever the `ground_in` fetches resolve through |
|
|
158
|
+
| `CLAUDE_CODE_OAUTH_TOKEN` | from `claude setup-token` on a machine with a browser, tied to a Pro/Max/Team/Enterprise subscription |
|
|
159
|
+
|
|
160
|
+
> **Do not also set `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN`.** In `claude -p`
|
|
161
|
+
> non-interactive mode an API key present in the environment is ALWAYS preferred over
|
|
162
|
+
> `CLAUDE_CODE_OAUTH_TOKEN`, silently routing every session through metered billing. There is no
|
|
163
|
+
> warning and no visible difference in the transcript; the only symptom is the bill.
|
|
164
|
+
|
|
165
|
+
Publishing additionally needs `IMAGE_REGISTRY` and `BUILDKIT_HOST`. There is no Docker daemon and
|
|
166
|
+
no docker socket anywhere in this design — `buildctl` is a client, which is why an actor running
|
|
167
|
+
this can be an ordinary Pod.
|
|
168
|
+
|
|
169
|
+
## CLI
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
foundry-implementation-actor lint . # validate the sidecar
|
|
173
|
+
foundry-implementation-actor show . --registry reg.example.com # every derived rendering
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`lint` is the gate CI runs. A sidecar declaring some other `context:` is read, warned, and not
|
|
177
|
+
checked further — UNMIGRATED is not the same as non-conformant, and migrating is the owning pair's
|
|
178
|
+
own act.
|
|
179
|
+
|
|
180
|
+
## Observability
|
|
181
|
+
|
|
182
|
+
One JSON object per step, so a dashboard reads them with the same `| json` it uses for the session
|
|
183
|
+
transcript and tells them apart by `event`:
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
{level, event: "step", step, phase: start|ok|failed, duration_ms, error}
|
|
187
|
+
{level, event: "event", step, **fields}
|
|
188
|
+
{level, event: "result", cost_usd, input_tokens, output_tokens}
|
|
189
|
+
correlation_id, task_id # structured metadata
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**That schema is the contract; the step names are not.** Step names may be renamed freely — a
|
|
193
|
+
drill-down discovers them. `correlation_id` is the W3C trace id the caller propagated, not a second
|
|
194
|
+
identifier invented here, so it pastes straight from Loki into Tempo.
|
|
195
|
+
|
|
196
|
+
Every emitted line is budgeted to 64 KB. Loki's `max_line_size` is 256 KB with
|
|
197
|
+
`max_line_size_truncate: false` — an oversized line is **rejected outright**, not trimmed, so one
|
|
198
|
+
large `Read` would silently delete exactly the turn worth reading while leaving the rest intact.
|
|
199
|
+
|
|
200
|
+
## Where this came from
|
|
201
|
+
|
|
202
|
+
Extracted from one capability's own implementation actor, where 891 lines of this machinery sat
|
|
203
|
+
beside the business capability's source. Both files that moved said so themselves: the engine's
|
|
204
|
+
docstring called extraction *"a near-mechanical move"*, and the sidecar's header said it was kept
|
|
205
|
+
structural *"specifically so extracting this into a real published package later (once proven) is a
|
|
206
|
+
move, not a rewrite."*
|
|
207
|
+
|
|
208
|
+
`adr/` records the decisions. Design rationale belongs there, not in commit messages.
|
|
209
|
+
|
|
210
|
+
## Development
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
uv run --extra dev pytest -q # what CI runs
|
|
214
|
+
uv build
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
There is no separate lint/format command configured in this repo.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
foundry_implementation_actor/__init__.py,sha256=MN--k8NLJM5erROXO2sWH-7kRUaht_myBYmVRgAWFCM,1448
|
|
2
|
+
foundry_implementation_actor/cli.py,sha256=tgVtsMeX7AC97monFrNF85vfWPhN1T_aPk65OGcuyd0,3887
|
|
3
|
+
foundry_implementation_actor/config.py,sha256=OT3EthNUvSUIHiWv_FQQ9kwjpeTQgQhAQjw3NlWFP_Y,18318
|
|
4
|
+
foundry_implementation_actor/correlation.py,sha256=PmnlGDKzu9ZBB8NEHaB4mNTimYZSHdWw31BNFbb0nNo,10658
|
|
5
|
+
foundry_implementation_actor/engine.py,sha256=6C5_DM5sxrdRruUgzgWAeJlDObDdcaDp0jDyeqSig7o,22898
|
|
6
|
+
foundry_implementation_actor/grounding.py,sha256=X-hZ6XuYNrHcNoDx7uNVCUMbUUBvKoVbM4CohSJa6as,8907
|
|
7
|
+
foundry_implementation_actor/handler.py,sha256=WbpEPdLqEH0EVEl1Kl18u8vk49GUEsQlaEmqT4aJU0Q,10697
|
|
8
|
+
foundry_implementation_actor/schemas/agentic-context.schema.yaml,sha256=Wxh3e1Eqk4z0lqV2Tlvvofyc0nYdz94JruWqm-QKqbY,6651
|
|
9
|
+
foundry_implementation_actor-0.1.0.dist-info/METADATA,sha256=gJn0UP0AkKyiaHyjy-z-wvp4wyOR_lh7o7w1c6Rjygw,10122
|
|
10
|
+
foundry_implementation_actor-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
11
|
+
foundry_implementation_actor-0.1.0.dist-info/entry_points.txt,sha256=gTDSlXCPXPDXpi8WflBUapIfx_fuqsq7qsBUkVDIXOU,87
|
|
12
|
+
foundry_implementation_actor-0.1.0.dist-info/RECORD,,
|