foundry-implementation-actor 0.1.0__tar.gz → 0.2.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.
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/.github/workflows/ci.yml +10 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/.github/workflows/release.yml +9 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/CLAUDE.md +12 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/PKG-INFO +30 -3
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/README.md +29 -2
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/pyproject.toml +1 -1
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/src/foundry_implementation_actor/__init__.py +3 -1
- foundry_implementation_actor-0.2.0/src/foundry_implementation_actor/cards/actor-data.yaml +38 -0
- foundry_implementation_actor-0.2.0/src/foundry_implementation_actor/cards/actor-message.yaml +17 -0
- foundry_implementation_actor-0.2.0/src/foundry_implementation_actor/cards/actor-synchronous-messaging.yaml +21 -0
- foundry_implementation_actor-0.2.0/src/foundry_implementation_actor/cards/actor.yaml +19 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/src/foundry_implementation_actor/config.py +16 -0
- foundry_implementation_actor-0.2.0/tests/test_cards.py +49 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/uv.lock +1 -1
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/.gitignore +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/adr/ADR-FIA-0001-the-machinery-leaves-the-capability.md +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/adr/README.md +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/adr/template.md +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/scripts/probe_grounding.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/src/foundry_implementation_actor/cli.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/src/foundry_implementation_actor/correlation.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/src/foundry_implementation_actor/engine.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/src/foundry_implementation_actor/grounding.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/src/foundry_implementation_actor/handler.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/src/foundry_implementation_actor/schemas/agentic-context.schema.yaml +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/conftest.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/fixtures/broken/actor-agentic-context.yaml +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/fixtures/valid/actor-agentic-context.yaml +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_cli.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_config.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_engine.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_grounding.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_handler.py +0 -0
- {foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_portability.py +0 -0
{foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/.github/workflows/ci.yml
RENAMED
|
@@ -32,6 +32,16 @@ jobs:
|
|
|
32
32
|
--registry reg.example.com | tee /tmp/show.txt
|
|
33
33
|
grep -q 'reg.example.com/acme.parts/sup.007.wid/backend:<version>' /tmp/show.txt
|
|
34
34
|
|
|
35
|
+
- name: the wheel must carry the actor's own cards
|
|
36
|
+
# The `-actor` suffix asserts a papeete-actor underneath, which lint-card can check
|
|
37
|
+
# (ADR-ECO-0022). The suite proves the cards are conformant in a source checkout; this
|
|
38
|
+
# proves they SHIPPED. A card folder left out of the build passes every test in tests/ and
|
|
39
|
+
# makes the name a claim the artifact cannot honour. `papeete-actor-synchronous-messaging`
|
|
40
|
+
# is already a runtime dependency, so its gate is in the probe venv with nothing to add.
|
|
41
|
+
run: |
|
|
42
|
+
/tmp/probe/bin/papeete-actor-synchronous-messaging lint-card \
|
|
43
|
+
"$(/tmp/probe/bin/python -c 'from foundry_implementation_actor import cards_path; print(cards_path())')"
|
|
44
|
+
|
|
35
45
|
- name: the gate must run
|
|
36
46
|
# A gate that cannot fail is not a gate. Lint a deliberately non-conformant sidecar with
|
|
37
47
|
# the INSTALLED wheel and require a non-zero exit.
|
|
@@ -29,6 +29,15 @@ jobs:
|
|
|
29
29
|
uv pip install --python /tmp/probe/bin/python -q dist/*.whl
|
|
30
30
|
/tmp/probe/bin/foundry-implementation-actor lint tests/fixtures/valid
|
|
31
31
|
|
|
32
|
+
- name: the wheel must carry the actor's own cards
|
|
33
|
+
# Same reason as the line above, for the other half of what this package ships. The
|
|
34
|
+
# `-actor` suffix asserts a papeete-actor underneath (ADR-ECO-0022); a wheel published
|
|
35
|
+
# without its cards makes the name a claim the artifact cannot honour, and a published
|
|
36
|
+
# version is not something to discover that from.
|
|
37
|
+
run: |
|
|
38
|
+
/tmp/probe/bin/papeete-actor-synchronous-messaging lint-card \
|
|
39
|
+
"$(/tmp/probe/bin/python -c 'from foundry_implementation_actor import cards_path; print(cards_path())')"
|
|
40
|
+
|
|
32
41
|
- name: grounding renders a CLAUDE.md whose imports resolve
|
|
33
42
|
# BEFORE the upload, not after. A wheel that ships without its schema, or whose grounding
|
|
34
43
|
# emits an @-import to a file it never wrote, produces sessions grounded in nothing that
|
|
@@ -46,8 +46,20 @@ Six modules under `src/foundry_implementation_actor/`:
|
|
|
46
46
|
was extracted from; its code below the docstring is byte-identical.
|
|
47
47
|
- **`cli.py`** — argparse wiring only, no logic of its own.
|
|
48
48
|
|
|
49
|
+
Beside them, two folders of committed contract, both shipped in the wheel:
|
|
50
|
+
|
|
51
|
+
- **`schemas/agentic-context.schema.yaml`** — the sidecar contract this package owns, which
|
|
52
|
+
`lint` checks a use against.
|
|
53
|
+
- **`cards/`** — the actor's own four cards: what this actor IS, as opposed to which capability a
|
|
54
|
+
use of it serves. `cards_path()` returns the folder. They name no capability, and they are what
|
|
55
|
+
a spawned instance would be rendered from once a use is not a static repository.
|
|
56
|
+
|
|
49
57
|
## Core invariants that any change must preserve
|
|
50
58
|
|
|
59
|
+
- **The `-actor` suffix is a claim, and it is checked.** `ADR-ECO-0022`: a package ending in
|
|
60
|
+
`-actor` asserts a `papeete-actor` underneath, and one that ships no conformant card is
|
|
61
|
+
misnamed. `tests/test_cards.py` runs `lint-card` on `cards/` in the suite; CI and the release
|
|
62
|
+
workflow run it again against the built wheel, so the cards cannot silently stop shipping.
|
|
51
63
|
- **No capability literal, ever.** `tests/test_portability.py` greps `src/` for the originating
|
|
52
64
|
instance's identifiers *and* for any knowledge tool name (`kpack`, `kontract`, …), and fails on
|
|
53
65
|
either. The tools a capability grounds itself in are the consumer's dependencies. A fourth
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: foundry-implementation-actor
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.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>
|
|
@@ -34,8 +34,8 @@ pip install foundry-implementation-actor
|
|
|
34
34
|
|
|
35
35
|
## What it is
|
|
36
36
|
|
|
37
|
-
The
|
|
38
|
-
sidecar the consuming repo writes:
|
|
37
|
+
The actor's **definition** — its four cards, and the machinery behind them. It carries **no
|
|
38
|
+
capability of its own**: the capability it serves arrives in a sidecar the consuming repo writes:
|
|
39
39
|
|
|
40
40
|
```yaml
|
|
41
41
|
# actor-agentic-context.yaml
|
|
@@ -74,6 +74,33 @@ actor = Actor.from_card(".", mailbox=mailbox,
|
|
|
74
74
|
A second capability instantiates the same actor by writing that file. Nothing here is subclassed,
|
|
75
75
|
hooked, or configured with a strategy object — there is one shape, and it is this one.
|
|
76
76
|
|
|
77
|
+
## The definition, and a use
|
|
78
|
+
|
|
79
|
+
This package is where the actor is **defined**. `src/foundry_implementation_actor/cards/` holds its
|
|
80
|
+
four cards — who it is, the data it knows, the messages it exchanges, and the one `implement-task`
|
|
81
|
+
door it answers — and they ship in the wheel, reachable as `cards_path()`. They name no capability,
|
|
82
|
+
because which capability an instance serves is not part of what the actor *is*.
|
|
83
|
+
|
|
84
|
+
A **use** of this actor is one capability's own folder: its own copy of those four cards, named for
|
|
85
|
+
the capability it serves, beside the `actor-agentic-context.yaml` that binds it to that capability's
|
|
86
|
+
repository and knowledge base. Today that folder is a static repository, and the copy is made by
|
|
87
|
+
hand. Once an instance can be spawned from a capability id alone, the cards here are what it would
|
|
88
|
+
be rendered from — which is why they live in the wheel rather than in an `examples/` folder.
|
|
89
|
+
|
|
90
|
+
The split is what the name asserts. `ADR-ECO-0022` makes the `-actor` suffix an obligation: a
|
|
91
|
+
package ending in `-actor` claims a `papeete-actor` underneath, *"and a `<use>-<tier>-actor` that
|
|
92
|
+
ships no conformant card is misnamed, not merely unusual."* `tests/test_cards.py` runs that check
|
|
93
|
+
in the suite, and CI runs it again against the built wheel:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
papeete-actor-synchronous-messaging lint-card \
|
|
97
|
+
"$(python -c 'from foundry_implementation_actor import cards_path; print(cards_path())')"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Because the cards sit under `src/`, `tests/test_portability.py` greps them too — a capability id or
|
|
101
|
+
a knowledge tool name written into the actor's own definition fails the build exactly as it would
|
|
102
|
+
in the code.
|
|
103
|
+
|
|
77
104
|
## What one request does
|
|
78
105
|
|
|
79
106
|
```
|
|
@@ -14,8 +14,8 @@ pip install foundry-implementation-actor
|
|
|
14
14
|
|
|
15
15
|
## What it is
|
|
16
16
|
|
|
17
|
-
The
|
|
18
|
-
sidecar the consuming repo writes:
|
|
17
|
+
The actor's **definition** — its four cards, and the machinery behind them. It carries **no
|
|
18
|
+
capability of its own**: the capability it serves arrives in a sidecar the consuming repo writes:
|
|
19
19
|
|
|
20
20
|
```yaml
|
|
21
21
|
# actor-agentic-context.yaml
|
|
@@ -54,6 +54,33 @@ actor = Actor.from_card(".", mailbox=mailbox,
|
|
|
54
54
|
A second capability instantiates the same actor by writing that file. Nothing here is subclassed,
|
|
55
55
|
hooked, or configured with a strategy object — there is one shape, and it is this one.
|
|
56
56
|
|
|
57
|
+
## The definition, and a use
|
|
58
|
+
|
|
59
|
+
This package is where the actor is **defined**. `src/foundry_implementation_actor/cards/` holds its
|
|
60
|
+
four cards — who it is, the data it knows, the messages it exchanges, and the one `implement-task`
|
|
61
|
+
door it answers — and they ship in the wheel, reachable as `cards_path()`. They name no capability,
|
|
62
|
+
because which capability an instance serves is not part of what the actor *is*.
|
|
63
|
+
|
|
64
|
+
A **use** of this actor is one capability's own folder: its own copy of those four cards, named for
|
|
65
|
+
the capability it serves, beside the `actor-agentic-context.yaml` that binds it to that capability's
|
|
66
|
+
repository and knowledge base. Today that folder is a static repository, and the copy is made by
|
|
67
|
+
hand. Once an instance can be spawned from a capability id alone, the cards here are what it would
|
|
68
|
+
be rendered from — which is why they live in the wheel rather than in an `examples/` folder.
|
|
69
|
+
|
|
70
|
+
The split is what the name asserts. `ADR-ECO-0022` makes the `-actor` suffix an obligation: a
|
|
71
|
+
package ending in `-actor` claims a `papeete-actor` underneath, *"and a `<use>-<tier>-actor` that
|
|
72
|
+
ships no conformant card is misnamed, not merely unusual."* `tests/test_cards.py` runs that check
|
|
73
|
+
in the suite, and CI runs it again against the built wheel:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
papeete-actor-synchronous-messaging lint-card \
|
|
77
|
+
"$(python -c 'from foundry_implementation_actor import cards_path; print(cards_path())')"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Because the cards sit under `src/`, `tests/test_portability.py` greps them too — a capability id or
|
|
81
|
+
a knowledge tool name written into the actor's own definition fails the build exactly as it would
|
|
82
|
+
in the code.
|
|
83
|
+
|
|
57
84
|
## What one request does
|
|
58
85
|
|
|
59
86
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "foundry-implementation-actor"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.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"
|
|
@@ -18,7 +18,8 @@ Wiring one up is four lines:
|
|
|
18
18
|
`correlation` is exported too — an entrypoint installs its filter on the root logger's handlers
|
|
19
19
|
after configuring observability, so every record the process emits carries this request's ids.
|
|
20
20
|
"""
|
|
21
|
-
from .config import CapabilityConfig, Component, ConfigError, Grounding, Report,
|
|
21
|
+
from .config import (CapabilityConfig, Component, ConfigError, Grounding, Report,
|
|
22
|
+
cards_path, lint)
|
|
22
23
|
from .engine import ClaudeCodeEngine
|
|
23
24
|
from .handler import HandlerError, make_implement_task
|
|
24
25
|
from . import correlation, grounding
|
|
@@ -31,6 +32,7 @@ __all__ = [
|
|
|
31
32
|
"Grounding",
|
|
32
33
|
"HandlerError",
|
|
33
34
|
"Report",
|
|
35
|
+
"cards_path",
|
|
34
36
|
"correlation",
|
|
35
37
|
"grounding",
|
|
36
38
|
"lint",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# WHAT DATA EXISTS. `papeete-actor-data/v0`, owned by `papeete-actor-message` — a named, minimally
|
|
2
|
+
# typed dictionary. Every field here belongs to the ACTOR, not to any capability: a task id, the
|
|
3
|
+
# caller's statement of the task, and what came back. Nothing names a capability, a component or a
|
|
4
|
+
# knowledge tool, because none of those are known until a sidecar supplies them.
|
|
5
|
+
data: papeete-actor-data/v0
|
|
6
|
+
items:
|
|
7
|
+
- name: task_id
|
|
8
|
+
type: string
|
|
9
|
+
description: the TASK-NNN card id this actor was asked about (e.g. "TASK-009")
|
|
10
|
+
- name: title
|
|
11
|
+
type: string
|
|
12
|
+
description: the task's short title/summary, supplied by the caller — no card lookup is performed here
|
|
13
|
+
- name: context
|
|
14
|
+
type: string
|
|
15
|
+
description: free-text elaboration beyond the title — background, constraints, links; optional
|
|
16
|
+
- name: definition_of_done
|
|
17
|
+
type: list
|
|
18
|
+
description: the acceptance criteria this task must satisfy, as the caller states them
|
|
19
|
+
- name: remediation_context
|
|
20
|
+
type: string
|
|
21
|
+
description: >-
|
|
22
|
+
on a retry, the prior attempt's failing test criteria — what the implementing session should
|
|
23
|
+
fix this time; optional, absent on a first attempt
|
|
24
|
+
- name: accepted
|
|
25
|
+
type: boolean
|
|
26
|
+
description: whether the implement-task request was accepted, pushed, and its images published
|
|
27
|
+
- name: because
|
|
28
|
+
type: string
|
|
29
|
+
description: why a request was refused (containment violation, nothing staged, ...)
|
|
30
|
+
- name: branch
|
|
31
|
+
type: string
|
|
32
|
+
description: the branch the implementation was pushed to (impl/TASK-NNN)
|
|
33
|
+
- name: images
|
|
34
|
+
type: list
|
|
35
|
+
description: >-
|
|
36
|
+
the full name:version ref of each touched component's published image (e.g.
|
|
37
|
+
"acme.parts.cap.sup.007.wid-backend:0.1.0-TASK-011-a1b2c3d"), by convention — no other actor
|
|
38
|
+
is ever told these tags explicitly
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# WHAT MESSAGES EXIST. `papeete-actor-message/v1`, owned by `papeete-actor-message` — named
|
|
2
|
+
# messages, each a pure regrouping of references into the data dictionary, under an intent.
|
|
3
|
+
# Wire-agnostic by that package's own design: WHAT a message is, never HOW it is carried.
|
|
4
|
+
message: papeete-actor-message/v1
|
|
5
|
+
messages:
|
|
6
|
+
- name: implement-task-cmd
|
|
7
|
+
intent: ask this actor to implement a TASK-NNN card for the capability it serves
|
|
8
|
+
references: [task_id, title, definition_of_done, context, remediation_context]
|
|
9
|
+
optional: [context, remediation_context]
|
|
10
|
+
|
|
11
|
+
- name: task-implemented-result
|
|
12
|
+
intent: report that the task was implemented, pushed, and its touched components' images published
|
|
13
|
+
references: [accepted, branch, images]
|
|
14
|
+
|
|
15
|
+
- name: task-refused-result
|
|
16
|
+
intent: report that the task was not implemented, and why
|
|
17
|
+
references: [accepted, because]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# THE DOORS. `synchronous-messaging-doors/v1` — one non-deterministic door, and no queries.
|
|
2
|
+
#
|
|
3
|
+
# `engine: claude-code` is this actor kind's declared engine. A use's sidecar repeats it in its own
|
|
4
|
+
# `engine:` field, and the entrypoint registers the engine under exactly that key — so a use whose
|
|
5
|
+
# sidecar names a different engine is caught by its own card rather than at the first request.
|
|
6
|
+
manifest: synchronous-messaging-doors/v1
|
|
7
|
+
actions:
|
|
8
|
+
- id: implement-task
|
|
9
|
+
means: >-
|
|
10
|
+
the door for "implement TASK-NNN for the capability I serve". Send it here with the task id,
|
|
11
|
+
title, definition of done, and (optionally) context and remediation_context — the caller
|
|
12
|
+
supplies everything, no task card is looked up here. I clone my capability's repository into
|
|
13
|
+
my own private copy, ground myself in that capability's own standing context, judge how to
|
|
14
|
+
implement the task (writing only under whichever of my declared components it touches), then
|
|
15
|
+
commit and push to impl/TASK-NNN, and build each touched component's image in the cluster's
|
|
16
|
+
shared buildkit and push it to the registry, named and versioned by convention. I never open
|
|
17
|
+
a pull request — an orchestrating actor does, once a testing actor also confirms.
|
|
18
|
+
completion: whether I accepted, pushed, and published images (with the branch and image refs), or refused, and why.
|
|
19
|
+
door_schema: implement-task-cmd
|
|
20
|
+
completion_schema: [task-implemented-result, task-refused-result]
|
|
21
|
+
engine: claude-code
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# WHO. The actor this package defines — `papeete-actor-manifest/v0`, owned by `papeete-actor`.
|
|
2
|
+
#
|
|
3
|
+
# THIS FOLDER IS THE DEFINITION, NOT A USE. The four cards here say what a foundry implementation
|
|
4
|
+
# actor IS: the data it knows, the messages it exchanges, and the one door it answers. They name no
|
|
5
|
+
# capability, because which capability an instance serves is not part of what the actor is — that
|
|
6
|
+
# is `actor-agentic-context.yaml`, supplied per use.
|
|
7
|
+
#
|
|
8
|
+
# One use of this actor is a folder carrying its own copy of these four cards, named for the
|
|
9
|
+
# capability it serves, beside the sidecar that binds it to that capability's knowledge base. Today
|
|
10
|
+
# that folder is a static repository; the cards here are what a spawned instance would be rendered
|
|
11
|
+
# from once it is not.
|
|
12
|
+
manifest: papeete-actor-manifest/v0
|
|
13
|
+
name: foundry-implementation-actor
|
|
14
|
+
description: >-
|
|
15
|
+
Implements one TASK-NNN card for the business capability its sidecar names. Clones that
|
|
16
|
+
capability's own repository into a private copy, grounds the session in the capability's own
|
|
17
|
+
standing context, and writes only under the components the sidecar declares — then commits,
|
|
18
|
+
pushes a branch, and publishes one image per component the task actually touched. Never opens a
|
|
19
|
+
pull request: an orchestrating actor does that, once a testing actor also confirms.
|
|
@@ -45,6 +45,7 @@ CONTRACT = "foundry-implementation-actor/agentic-context/v1"
|
|
|
45
45
|
SIDECAR = "actor-agentic-context.yaml"
|
|
46
46
|
|
|
47
47
|
_SCHEMA_PATH = Path(__file__).resolve().parent / "schemas" / "agentic-context.schema.yaml"
|
|
48
|
+
_CARDS_PATH = Path(__file__).resolve().parent / "cards"
|
|
48
49
|
|
|
49
50
|
# The segment a capability id carries to say "capability". It is dropped from the registry path
|
|
50
51
|
# because the path position already says it — every other token of the id survives, across
|
|
@@ -56,6 +57,21 @@ _CAPABILITY_SEGMENT = "cap"
|
|
|
56
57
|
_PLACEHOLDER = re.compile(r"\{([a-z_]+)\}")
|
|
57
58
|
|
|
58
59
|
|
|
60
|
+
def cards_path() -> Path:
|
|
61
|
+
"""The folder holding this actor's own four cards — its definition, shipped in the wheel.
|
|
62
|
+
|
|
63
|
+
The cards say what a foundry implementation actor IS: its data dictionary, its message
|
|
64
|
+
catalog, and the one door it answers. They name no capability, because which capability an
|
|
65
|
+
instance serves is not part of what the actor is — that is the sidecar, supplied per use.
|
|
66
|
+
|
|
67
|
+
A use is today a static folder carrying its own copy of these four, named for the capability
|
|
68
|
+
it serves. This path is what a spawned instance would be rendered from once it is not, and it
|
|
69
|
+
is what `papeete-actor-synchronous-messaging lint-card` is pointed at to check that the
|
|
70
|
+
`-actor` suffix in this package's name is a claim it actually honours (ADR-ECO-0022).
|
|
71
|
+
"""
|
|
72
|
+
return _CARDS_PATH
|
|
73
|
+
|
|
74
|
+
|
|
59
75
|
def load_schema() -> dict:
|
|
60
76
|
"""The contract, as committed source inside this package.
|
|
61
77
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""The `-actor` suffix, checked rather than asserted.
|
|
2
|
+
|
|
3
|
+
`ADR-ECO-0022` makes the suffix an obligation: "A package ending in `-actor` asserts a
|
|
4
|
+
`papeete-actor` underneath, which lint-card can check. A future `<use>-<tier>-actor` that ships no
|
|
5
|
+
conformant card is misnamed, not merely unusual."
|
|
6
|
+
|
|
7
|
+
This file is that check. It runs the same gate `papeete-actor-synchronous-messaging lint-card` runs,
|
|
8
|
+
against the cards this package ships — so the claim in the name is a test, not a sentence in a
|
|
9
|
+
record.
|
|
10
|
+
|
|
11
|
+
The cards live under `src/`, which means `test_portability.py` greps them too: a capability id or a
|
|
12
|
+
knowledge tool name written into the actor's own definition fails the build exactly as it would in
|
|
13
|
+
the code.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from papeete_actor_synchronous_messaging import card
|
|
18
|
+
|
|
19
|
+
from foundry_implementation_actor import cards_path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_the_package_ships_the_four_cards():
|
|
23
|
+
"""A card folder is four files; `Actor.from_card` opens exactly these and never globs."""
|
|
24
|
+
folder = cards_path()
|
|
25
|
+
missing = [name for name in ("actor.yaml", "actor-data.yaml", "actor-message.yaml",
|
|
26
|
+
"actor-synchronous-messaging.yaml")
|
|
27
|
+
if not (folder / name).exists()]
|
|
28
|
+
assert not missing, f"the actor's definition is incomplete — missing: {', '.join(missing)}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_the_shipped_cards_are_conformant():
|
|
32
|
+
"""The whole of the `-actor` claim: this folder is a papeete-actor, or the name is wrong."""
|
|
33
|
+
report = card.lint(cards_path())
|
|
34
|
+
assert not report.errors, ("the cards this package ships are not conformant:\n "
|
|
35
|
+
+ "\n ".join(report.errors))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_the_door_names_the_engine_a_sidecar_must_declare():
|
|
39
|
+
"""The card's door and a use's sidecar name the same engine key.
|
|
40
|
+
|
|
41
|
+
The entrypoint registers the engine under the sidecar's `engine:`, and the door resolves
|
|
42
|
+
through the card's. They are two statements of one fact, in two repos; this pins the half that
|
|
43
|
+
lives here so a use's `lint` failure is the only way they can disagree.
|
|
44
|
+
"""
|
|
45
|
+
loaded = card.load(cards_path())
|
|
46
|
+
assert set(loaded.actions) == {"implement-task"}, (
|
|
47
|
+
f"expected the one implement-task door, got {sorted(loaded.actions)}")
|
|
48
|
+
engines = {offer.engine for offer in loaded.actions.values()}
|
|
49
|
+
assert engines == {"claude-code"}, f"expected the one claude-code door, got {engines}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/scripts/probe_grounding.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_config.py
RENAMED
|
File without changes
|
{foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_engine.py
RENAMED
|
File without changes
|
{foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_grounding.py
RENAMED
|
File without changes
|
{foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_handler.py
RENAMED
|
File without changes
|
{foundry_implementation_actor-0.1.0 → foundry_implementation_actor-0.2.0}/tests/test_portability.py
RENAMED
|
File without changes
|