foundry-implementation-actor 0.6.0__tar.gz → 0.7.0__tar.gz

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 (50) hide show
  1. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/PKG-INFO +1 -1
  2. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/pyproject.toml +1 -1
  3. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/config.py +30 -9
  4. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/schemas/agentic-context.schema.yaml +2 -1
  5. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_config.py +42 -0
  6. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/uv.lock +1 -1
  7. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/.github/workflows/ci.yml +0 -0
  8. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/.github/workflows/release.yml +0 -0
  9. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/.gitignore +0 -0
  10. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/CLAUDE.md +0 -0
  11. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/README.md +0 -0
  12. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/ADR-FIA-0001-the-machinery-leaves-the-capability.md +0 -0
  13. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/ADR-FIA-0002-testing-leaves-this-actors-contract.md +0 -0
  14. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/ADR-FIA-0003-the-dev-test-agreement-has-no-home-yet.md +0 -0
  15. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/ADR-FIA-0004-the-three-amigos-round.md +0 -0
  16. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/ADR-FIA-0005-the-actor-ships-an-image.md +0 -0
  17. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/ADR-FIA-0006-the-image-is-published-to-two-registries.md +0 -0
  18. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/ADR-FIA-0007-a-sessions-budget-is-an-environment-setting.md +0 -0
  19. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/README.md +0 -0
  20. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/adr/template.md +0 -0
  21. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/docker/Dockerfile +0 -0
  22. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/examples/ACME.PARTS.CAP.SUP.007.WID-implementation/Dockerfile +0 -0
  23. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/examples/ACME.PARTS.CAP.SUP.007.WID-implementation/actor-agentic-context.yaml +0 -0
  24. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/examples/README.md +0 -0
  25. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/scripts/probe_grounding.py +0 -0
  26. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/__init__.py +0 -0
  27. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/cards/actor-data.yaml +0 -0
  28. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/cards/actor-message.yaml +0 -0
  29. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/cards/actor-synchronous-messaging.yaml +0 -0
  30. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/cards/actor.yaml +0 -0
  31. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/cli.py +0 -0
  32. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/conformance.py +0 -0
  33. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/correlation.py +0 -0
  34. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/engine.py +0 -0
  35. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/grounding.py +0 -0
  36. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/handler.py +0 -0
  37. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/instance.py +0 -0
  38. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/serve.py +0 -0
  39. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/src/foundry_implementation_actor/settings.py +0 -0
  40. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/conftest.py +0 -0
  41. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/fixtures/broken/actor-agentic-context.yaml +0 -0
  42. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_cards.py +0 -0
  43. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_cli.py +0 -0
  44. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_conformance.py +0 -0
  45. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_engine.py +0 -0
  46. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_grounding.py +0 -0
  47. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_handler.py +0 -0
  48. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_instance.py +0 -0
  49. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_portability.py +0 -0
  50. {foundry_implementation_actor-0.6.0 → foundry_implementation_actor-0.7.0}/tests/test_settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: foundry-implementation-actor
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
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
5
  Project-URL: Homepage, https://github.com/papeete-hub/foundry-implementation-actor
6
6
  Author-email: Papeete Consulting <yoann.remy@outlook.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "foundry-implementation-actor"
3
- version = "0.6.0"
3
+ version = "0.7.0"
4
4
  description = "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
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -12,7 +12,7 @@ They are now derivations of two fields. Nothing in this package spells a capabil
12
12
  capability ACME.PARTS.CAP.SUP.007.WID ← the only id anyone writes
13
13
  source_repo <owner>/ACME.PARTS.CAP.SUP.007.WID-impl ← and the only repo
14
14
 
15
- actor_name <repo half of source_repo>
15
+ actor_name {capability}-implementation
16
16
  actor_slug same, lowercased, dots to hyphens
17
17
  git_author_name actor_name
18
18
  git_author_email {actor_slug}@users.noreply.github.com
@@ -21,6 +21,15 @@ They are now derivations of two fields. Nothing in this package spells a capabil
21
21
  image_name(c) {capability lowercased}-{c}
22
22
  image_ref(r, c, v) {r}/{capability_path}/{c}:{v}
23
23
 
24
+ IDENTITY IS `capability` + ROLE, NOT THE REPO HALF. `actor_name` used to be whatever came after
25
+ the `/` in `source_repo`, which reads as a derivation but is really an assumption: that one
26
+ repository holds exactly one actor. Every sidecar in existence satisfies
27
+ `source_repo == "<owner>/" + capability + "-" + ROLE`, so deriving the name from the two facts it
28
+ was always shorthand for produces the identical string — and it keeps producing the right one when
29
+ a capability's three actors come to share a repository, where the repo half would name all three
30
+ the same thing. `source_repo` is unchanged and still required: it is where this actor clones from
31
+ and pushes to, which is a different question from who it is.
32
+
24
33
  THE IMAGE REF IS A THREE-WAY CONTRACT. The testing actor recomputes the identical string and the
25
34
  orchestration actor parses it back apart. `image_ref` must therefore stay byte-identical to what
26
35
  those two agree on; it is derivation output or nothing, and no tag scheme is invented here.
@@ -53,6 +62,10 @@ _CARDS_PATH = Path(__file__).resolve().parent / "cards"
53
62
  # segments rather than concatenated.
54
63
  _CAPABILITY_SEGMENT = "cap"
55
64
 
65
+ # The role this package plays for the capability it serves. Half of this actor's identity — see
66
+ # `CapabilityConfig.actor_name` — and the half that is a property of the package, not of the use.
67
+ ROLE = "implementation"
68
+
56
69
  # What an unsubstituted placeholder looks like: a bare lowercase word in braces, and nothing else.
57
70
  # Narrow on purpose — see `CapabilityConfig.expand`.
58
71
  _PLACEHOLDER = re.compile(r"\{([a-z_]+)\}")
@@ -187,9 +200,17 @@ class CapabilityConfig:
187
200
  ground_in = tuple(_grounding(entry, source, i)
188
201
  for i, entry in enumerate(raw["ground_in"] or ()))
189
202
 
203
+ source_repo = str(raw["source_repo"])
204
+ owner, _, repo = source_repo.partition("/")
205
+ if not owner or not repo:
206
+ # `actor_name` used to be the repo half and validated this shape on the way past. It
207
+ # is derived from `capability` now, so the field every clone and push URL is built
208
+ # from needs checking in its own right rather than by a side effect.
209
+ raise ConfigError(f"{source}: source_repo '{source_repo}' is not '<owner>/<repo>'")
210
+
190
211
  config = cls(
191
212
  capability=str(raw["capability"]),
192
- source_repo=str(raw["source_repo"]),
213
+ source_repo=source_repo,
193
214
  registry_repo=str(raw["registry_repo"]),
194
215
  engine=str(raw["engine"]),
195
216
  components=components,
@@ -204,13 +225,13 @@ class CapabilityConfig:
204
225
 
205
226
  @property
206
227
  def actor_name(self) -> str:
207
- """The repo half of `source_repo` — this actor's own name, and its git author name."""
208
- owner, _, repo = self.source_repo.partition("/")
209
- if not owner or not repo:
210
- raise ConfigError(
211
- f"source_repo '{self.source_repo}' is not '<owner>/<repo>'"
212
- )
213
- return repo
228
+ """`{capability}-{ROLE}` — this actor's own name, and its git author name.
229
+
230
+ Not the repo half of `source_repo`, which is the same string for every sidecar that
231
+ exists but stops being this actor's name alone the moment a capability's actors share a
232
+ repository. See this module's own docstring.
233
+ """
234
+ return f"{self.capability}-{ROLE}"
214
235
 
215
236
  @property
216
237
  def actor_slug(self) -> str:
@@ -76,7 +76,8 @@ fields:
76
76
  type: string
77
77
  doc: >-
78
78
  `<owner>/<repo>` of the repository this actor clones, writes to, and pushes a branch to.
79
- The actor's own name and its git commit identity are derived from the repo half.
79
+ Where it works, not who it is: the actor's own name and its git commit identity are
80
+ `<capability>-implementation`, derived from `capability` and this package's own role.
80
81
 
81
82
  registry_repo:
82
83
  type: string
@@ -205,3 +205,45 @@ def test_components_may_not_be_empty(sidecar_dict, write_sidecar):
205
205
  sidecar_dict["components"] = []
206
206
  with pytest.raises(ConfigError, match="empty"):
207
207
  CapabilityConfig.load(write_sidecar(sidecar_dict))
208
+
209
+
210
+ # ── identity: capability + role, not the repo half ──────────────────────────────────────────
211
+
212
+ def test_the_actor_name_is_exactly_what_the_repo_half_used_to_be(config):
213
+ """The no-op half of the change, pinned.
214
+
215
+ `actor_name` was `source_repo.partition("/")[2]`. It is `{capability}-{ROLE}` now. Every
216
+ sidecar that exists satisfies `source_repo == "<owner>/" + capability + "-" + ROLE`, so the
217
+ two derivations agree on all of them — which is what makes this releasable on its own, ahead
218
+ of any repository moving.
219
+ """
220
+ assert config.source_repo == f"acme-lab/{config.capability}-implementation"
221
+ assert config.actor_name == config.source_repo.partition("/")[2]
222
+ assert config.actor_name == "ACME.PARTS.CAP.SUP.007.WID-implementation"
223
+
224
+
225
+ def test_the_actor_name_no_longer_follows_the_repository(sidecar_dict, write_sidecar):
226
+ """The point of the change: three actors in one repository still have three names.
227
+
228
+ A consolidated capability repository is named for the capability alone, with no role suffix,
229
+ because all three of its actors live in it. Under the old derivation this actor would have
230
+ been called `ACME.PARTS.CAP.SUP.007.WID` — and so would both of its siblings.
231
+ """
232
+ sidecar_dict["source_repo"] = "acme-lab/ACME.PARTS.CAP.SUP.007.WID"
233
+ config = CapabilityConfig.load(write_sidecar(sidecar_dict))
234
+ assert config.actor_name == "ACME.PARTS.CAP.SUP.007.WID-implementation"
235
+ assert config.git_author_name == config.actor_name
236
+ assert config.actor_slug == "acme-parts-cap-sup-007-wid-implementation"
237
+ assert config.clone_prefix("TASK-042") == \
238
+ "acme-parts-cap-sup-007-wid-implementation-TASK-042-"
239
+
240
+
241
+ def test_a_malformed_source_repo_is_still_refused(sidecar_dict, write_sidecar):
242
+ """`actor_name` used to validate this shape on its way past, and nothing else did.
243
+
244
+ `source_repo` is still what every clone and push URL is built from, so a `source_repo` that
245
+ is not `<owner>/<repo>` has to keep failing at load rather than at the first push.
246
+ """
247
+ sidecar_dict["source_repo"] = "just-a-name"
248
+ with pytest.raises(ConfigError, match="<owner>/<repo>"):
249
+ CapabilityConfig.load(write_sidecar(sidecar_dict))
@@ -18,7 +18,7 @@ wheels = [
18
18
 
19
19
  [[package]]
20
20
  name = "foundry-implementation-actor"
21
- version = "0.6.0"
21
+ version = "0.7.0"
22
22
  source = { editable = "." }
23
23
  dependencies = [
24
24
  { name = "papeete-actor-synchronous-messaging" },