docs(plugins): mark skill mirroring plan complete
This commit is contained in:
@ -82,7 +82,7 @@ Add tests:
|
|||||||
- Test: `app-instance/backend/tests/unit/test_plugin_hashing.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_hashing.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_config_loader.py`
|
- Test: `app-instance/backend/tests/unit/test_config_loader.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing manifest validation tests**
|
- [x] **Step 1: Write failing manifest validation tests**
|
||||||
|
|
||||||
Create tests covering:
|
Create tests covering:
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ def test_skill_tree_hash_changes_when_supporting_file_changes(tmp_path: Path) ->
|
|||||||
Also verify path changes and executable-bit changes affect `skill_tree_hash`, while mtime
|
Also verify path changes and executable-bit changes affect `skill_tree_hash`, while mtime
|
||||||
and non-executable permission changes do not.
|
and non-executable permission changes do not.
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ pytest tests/unit/test_plugin_manifest.py tests/unit/test_plugin_hashing.py test
|
|||||||
|
|
||||||
Expected: FAIL because `beaver.plugins` and `PluginsConfig` do not exist.
|
Expected: FAIL because `beaver.plugins` and `PluginsConfig` do not exist.
|
||||||
|
|
||||||
- [ ] **Step 3: Implement immutable plugin models and config**
|
- [x] **Step 3: Implement immutable plugin models and config**
|
||||||
|
|
||||||
Put plugin package models in `beaver/plugins/models.py`:
|
Put plugin package models in `beaver/plugins/models.py`:
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ def _parse_plugins(raw: Any) -> PluginsConfig:
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 4: Implement strict JSON manifest loading**
|
- [x] **Step 4: Implement strict JSON manifest loading**
|
||||||
|
|
||||||
`load_plugin_manifest()` must:
|
`load_plugin_manifest()` must:
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ def _parse_plugins(raw: Any) -> PluginsConfig:
|
|||||||
7. initialize `display_path` without exposing an absolute path;
|
7. initialize `display_path` without exposing an absolute path;
|
||||||
8. return frozen dataclasses.
|
8. return frozen dataclasses.
|
||||||
|
|
||||||
- [ ] **Step 5: Implement deterministic dual hashing**
|
- [x] **Step 5: Implement deterministic dual hashing**
|
||||||
|
|
||||||
`hash_plugin_skill_tree(root)` must:
|
`hash_plugin_skill_tree(root)` must:
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ def _parse_plugins(raw: Any) -> PluginsConfig:
|
|||||||
Use length-prefixed binary fields in the digest input instead of ambiguous string
|
Use length-prefixed binary fields in the digest input instead of ambiguous string
|
||||||
concatenation.
|
concatenation.
|
||||||
|
|
||||||
- [ ] **Step 6: Run focused tests**
|
- [x] **Step 6: Run focused tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -267,7 +267,7 @@ pytest tests/unit/test_plugin_manifest.py tests/unit/test_plugin_hashing.py test
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 7: Commit**
|
- [x] **Step 7: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/plugins app-instance/backend/beaver/foundation/config app-instance/backend/tests/unit/test_plugin_manifest.py app-instance/backend/tests/unit/test_plugin_hashing.py app-instance/backend/tests/unit/test_config_loader.py
|
git add app-instance/backend/beaver/plugins app-instance/backend/beaver/foundation/config app-instance/backend/tests/unit/test_plugin_manifest.py app-instance/backend/tests/unit/test_plugin_hashing.py app-instance/backend/tests/unit/test_config_loader.py
|
||||||
@ -287,7 +287,7 @@ git commit -m "feat(plugins): add declarative skill manifest"
|
|||||||
- Test: `app-instance/backend/tests/unit/test_plugin_state.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_state.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_workspace_write_lock.py`
|
- Test: `app-instance/backend/tests/unit/test_workspace_write_lock.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing discovery and state tests**
|
- [x] **Step 1: Write failing discovery and state tests**
|
||||||
|
|
||||||
Cover workspace discovery, configured search paths, duplicate plugin IDs, malformed
|
Cover workspace discovery, configured search paths, duplicate plugin IDs, malformed
|
||||||
manifests reported as errors instead of crashing the full scan, and state round trips:
|
manifests reported as errors instead of crashing the full scan, and state round trips:
|
||||||
@ -321,7 +321,7 @@ Add a multiprocess lock test in which two processes enter the same workspace loc
|
|||||||
assert their critical sections never overlap. Add a reentrancy test in which nested
|
assert their critical sections never overlap. Add a reentrancy test in which nested
|
||||||
acquisitions in one process complete without deadlock.
|
acquisitions in one process complete without deadlock.
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -330,7 +330,7 @@ pytest tests/unit/test_plugin_state.py tests/unit/test_workspace_write_lock.py -
|
|||||||
|
|
||||||
Expected: FAIL because discovery and state stores are missing.
|
Expected: FAIL because discovery and state stores are missing.
|
||||||
|
|
||||||
- [ ] **Step 3: Implement state dataclasses**
|
- [x] **Step 3: Implement state dataclasses**
|
||||||
|
|
||||||
Add backward-compatible `to_dict()` and `from_dict()` methods for:
|
Add backward-compatible `to_dict()` and `from_dict()` methods for:
|
||||||
|
|
||||||
@ -358,7 +358,7 @@ class PluginState:
|
|||||||
skills: dict[str, PluginSkillBinding] = field(default_factory=dict)
|
skills: dict[str, PluginSkillBinding] = field(default_factory=dict)
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 4: Implement atomic state persistence**
|
- [x] **Step 4: Implement atomic state persistence**
|
||||||
|
|
||||||
Store data at `<workspace>/.beaver/plugins/state.json`. Write a complete JSON document to
|
Store data at `<workspace>/.beaver/plugins/state.json`. Write a complete JSON document to
|
||||||
`state.json.tmp`, flush it, then replace `state.json`. Public methods:
|
`state.json.tmp`, flush it, then replace `state.json`. Public methods:
|
||||||
@ -371,7 +371,7 @@ upsert_plugin(plugin_state)
|
|||||||
update_skill_binding(plugin_id, skill_name, binding)
|
update_skill_binding(plugin_id, skill_name, binding)
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 5: Implement the shared workspace write lock**
|
- [x] **Step 5: Implement the shared workspace write lock**
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
|
|
||||||
@ -395,7 +395,7 @@ Requirements:
|
|||||||
- raise `WorkspaceWriteLockBusy` on timeout/contention;
|
- raise `WorkspaceWriteLockBusy` on timeout/contention;
|
||||||
- keep the lock file separate from atomically replaced data files.
|
- keep the lock file separate from atomically replaced data files.
|
||||||
|
|
||||||
- [ ] **Step 6: Implement discovery**
|
- [x] **Step 6: Implement discovery**
|
||||||
|
|
||||||
Scan:
|
Scan:
|
||||||
|
|
||||||
@ -409,7 +409,7 @@ manifest display path when possible and a redacted
|
|||||||
`<external>/<plugin-dir>/beaver.plugin.json` path otherwise; absolute paths remain
|
`<external>/<plugin-dir>/beaver.plugin.json` path otherwise; absolute paths remain
|
||||||
internal.
|
internal.
|
||||||
|
|
||||||
- [ ] **Step 7: Run focused tests**
|
- [x] **Step 7: Run focused tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -418,7 +418,7 @@ pytest tests/unit/test_plugin_state.py tests/unit/test_workspace_write_lock.py t
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 8: Commit**
|
- [x] **Step 8: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/plugins app-instance/backend/beaver/foundation/utils/file_lock.py app-instance/backend/tests/unit/test_plugin_state.py app-instance/backend/tests/unit/test_workspace_write_lock.py
|
git add app-instance/backend/beaver/plugins app-instance/backend/beaver/foundation/utils/file_lock.py app-instance/backend/tests/unit/test_plugin_state.py app-instance/backend/tests/unit/test_workspace_write_lock.py
|
||||||
@ -436,7 +436,7 @@ git commit -m "feat(plugins): discover packages and persist state"
|
|||||||
- Modify: `app-instance/backend/beaver/skills/specs/__init__.py`
|
- Modify: `app-instance/backend/beaver/skills/specs/__init__.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_plugin_skill_storage.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_skill_storage.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing snapshot storage tests**
|
- [x] **Step 1: Write failing snapshot storage tests**
|
||||||
|
|
||||||
Test exact content, supporting files, idempotence, symlink rejection, and source
|
Test exact content, supporting files, idempotence, symlink rejection, and source
|
||||||
immutability:
|
immutability:
|
||||||
@ -478,7 +478,7 @@ Also test:
|
|||||||
- promoting a staged snapshot uses `os.replace()` and is idempotent;
|
- promoting a staged snapshot uses `os.replace()` and is idempotent;
|
||||||
- a failed metadata write leaves no current pointer to the staged version.
|
- a failed metadata write leaves no current pointer to the staged version.
|
||||||
|
|
||||||
- [ ] **Step 2: Run test and verify failure**
|
- [x] **Step 2: Run test and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -487,7 +487,7 @@ pytest tests/unit/test_plugin_skill_storage.py -q
|
|||||||
|
|
||||||
Expected: FAIL because upstream snapshot APIs do not exist.
|
Expected: FAIL because upstream snapshot APIs do not exist.
|
||||||
|
|
||||||
- [ ] **Step 3: Add upstream snapshot models**
|
- [x] **Step 3: Add upstream snapshot models**
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
|
|
||||||
@ -510,7 +510,7 @@ Add `LoadedSkillUpstreamSnapshot(snapshot, content, root)` for storage reads. Ex
|
|||||||
complete version-tree hash, while `read_published_skill()` derives it for legacy metadata
|
complete version-tree hash, while `read_published_skill()` derives it for legacy metadata
|
||||||
that lacks the field.
|
that lacks the field.
|
||||||
|
|
||||||
- [ ] **Step 4: Add safe tree-copy helper**
|
- [x] **Step 4: Add safe tree-copy helper**
|
||||||
|
|
||||||
Refactor a private `SkillSpecStore._copy_regular_tree(source_root, target_root)` that:
|
Refactor a private `SkillSpecStore._copy_regular_tree(source_root, target_root)` that:
|
||||||
|
|
||||||
@ -522,7 +522,7 @@ Refactor a private `SkillSpecStore._copy_regular_tree(source_root, target_root)`
|
|||||||
|
|
||||||
Use it for transaction staging now; Task 4 will reuse it for mirrored versions.
|
Use it for transaction staging now; Task 4 will reuse it for mirrored versions.
|
||||||
|
|
||||||
- [ ] **Step 5: Implement same-filesystem staging and promotion**
|
- [x] **Step 5: Implement same-filesystem staging and promotion**
|
||||||
|
|
||||||
`PluginSkillTransaction` creates:
|
`PluginSkillTransaction` creates:
|
||||||
|
|
||||||
@ -542,7 +542,7 @@ cleanup()
|
|||||||
`promote_directory()` uses `os.replace()` and never replaces an existing non-identical
|
`promote_directory()` uses `os.replace()` and never replaces an existing non-identical
|
||||||
immutable directory. Cleanup removes only the transaction's staging root.
|
immutable directory. Cleanup removes only the transaction's staging root.
|
||||||
|
|
||||||
- [ ] **Step 6: Implement snapshot APIs**
|
- [x] **Step 6: Implement snapshot APIs**
|
||||||
|
|
||||||
Write snapshots to:
|
Write snapshots to:
|
||||||
|
|
||||||
@ -561,14 +561,14 @@ promote_upstream_snapshot(transaction, snapshot)
|
|||||||
read_upstream_snapshot(skill_name, source_id, skill_tree_hash)
|
read_upstream_snapshot(skill_name, source_id, skill_tree_hash)
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 7: Make JSON/current/index writes atomic**
|
- [x] **Step 7: Make JSON/current/index writes atomic**
|
||||||
|
|
||||||
Change `SkillSpecStore._write_json()` and current/index pointer writes to create a temporary
|
Change `SkillSpecStore._write_json()` and current/index pointer writes to create a temporary
|
||||||
file in the target directory, flush and `fsync`, then `os.replace()`. Immutable version
|
file in the target directory, flush and `fsync`, then `os.replace()`. Immutable version
|
||||||
directories are promoted first; runtime visibility changes only when `current.json`,
|
directories are promoted first; runtime visibility changes only when `current.json`,
|
||||||
`skill.json`, and the published index are atomically replaced under the workspace lock.
|
`skill.json`, and the published index are atomically replaced under the workspace lock.
|
||||||
|
|
||||||
- [ ] **Step 8: Run focused and existing storage tests**
|
- [x] **Step 8: Run focused and existing storage tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -577,7 +577,7 @@ pytest tests/unit/test_plugin_skill_storage.py tests/unit/test_phase5_skills_run
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 9: Commit**
|
- [x] **Step 9: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/plugins/transaction.py app-instance/backend/beaver/skills/specs app-instance/backend/tests/unit/test_plugin_skill_storage.py
|
git add app-instance/backend/beaver/plugins/transaction.py app-instance/backend/beaver/skills/specs app-instance/backend/tests/unit/test_plugin_skill_storage.py
|
||||||
@ -595,7 +595,7 @@ git commit -m "feat(skills): store immutable plugin upstream snapshots"
|
|||||||
- Modify: `app-instance/backend/beaver/skills/specs/storage.py`
|
- Modify: `app-instance/backend/beaver/skills/specs/storage.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_plugin_skill_sync.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_skill_sync.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing initial mirror tests**
|
- [x] **Step 1: Write failing initial mirror tests**
|
||||||
|
|
||||||
Cover:
|
Cover:
|
||||||
|
|
||||||
@ -626,7 +626,7 @@ assert loaded.version.provenance["upstream_skill_content_hash"]
|
|||||||
assert loaded.version.provenance["upstream_skill_tree_hash"]
|
assert loaded.version.provenance["upstream_skill_tree_hash"]
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -635,7 +635,7 @@ pytest tests/unit/test_plugin_skill_sync.py -q
|
|||||||
|
|
||||||
Expected: FAIL because `PluginManager` does not exist.
|
Expected: FAIL because `PluginManager` does not exist.
|
||||||
|
|
||||||
- [ ] **Step 3: Implement `PluginManager` constructor and discovery view**
|
- [x] **Step 3: Implement `PluginManager` constructor and discovery view**
|
||||||
|
|
||||||
Constructor dependencies:
|
Constructor dependencies:
|
||||||
|
|
||||||
@ -659,7 +659,7 @@ class PluginManager:
|
|||||||
|
|
||||||
Keep all filesystem and lifecycle dependencies injectable for tests.
|
Keep all filesystem and lifecycle dependencies injectable for tests.
|
||||||
|
|
||||||
- [ ] **Step 4: Implement exact initial mirror publication**
|
- [x] **Step 4: Implement exact initial mirror publication**
|
||||||
|
|
||||||
Acquire the workspace write lock before reading state, allocating versions, or writing
|
Acquire the workspace write lock before reading state, allocating versions, or writing
|
||||||
candidates. For each declared skill:
|
candidates. For each declared skill:
|
||||||
@ -689,7 +689,7 @@ Use provenance:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 5: Promote the complete staged transaction**
|
- [x] **Step 5: Promote the complete staged transaction**
|
||||||
|
|
||||||
After every declared skill passes validation:
|
After every declared skill passes validation:
|
||||||
|
|
||||||
@ -704,7 +704,7 @@ metadata write fails, those directories remain unreferenced and harmless; the pr
|
|||||||
current pointers remain authoritative. Add startup cleanup for staging directories older
|
current pointers remain authoritative. Add startup cleanup for staging directories older
|
||||||
than 24 hours.
|
than 24 hours.
|
||||||
|
|
||||||
- [ ] **Step 6: Run focused and loader tests**
|
- [x] **Step 6: Run focused and loader tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -713,7 +713,7 @@ pytest tests/unit/test_plugin_skill_sync.py tests/unit/test_phase5_skills_runtim
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 7: Commit**
|
- [x] **Step 7: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/plugins app-instance/backend/beaver/skills/specs/storage.py app-instance/backend/tests/unit/test_plugin_skill_sync.py
|
git add app-instance/backend/beaver/plugins app-instance/backend/beaver/skills/specs/storage.py app-instance/backend/tests/unit/test_plugin_skill_sync.py
|
||||||
@ -731,7 +731,7 @@ git commit -m "feat(plugins): mirror enabled plugin skills"
|
|||||||
- Test: `app-instance/backend/tests/unit/test_plugin_skill_sync.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_skill_sync.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_skill_learning_candidate_state.py`
|
- Test: `app-instance/backend/tests/unit/test_skill_learning_candidate_state.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing upgrade classification tests**
|
- [x] **Step 1: Write failing upgrade classification tests**
|
||||||
|
|
||||||
Create four tree-hash fixtures representing `B`, `L`, and `U`:
|
Create four tree-hash fixtures representing `B`, `L`, and `U`:
|
||||||
|
|
||||||
@ -758,7 +758,7 @@ Also test:
|
|||||||
- legacy candidate payloads still parse.
|
- legacy candidate payloads still parse.
|
||||||
- two processes syncing the same update append only one candidate record.
|
- two processes syncing the same update append only one candidate record.
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -767,7 +767,7 @@ pytest tests/unit/test_plugin_skill_sync.py tests/unit/test_skill_learning_candi
|
|||||||
|
|
||||||
Expected: FAIL because update classification and candidate kind are missing.
|
Expected: FAIL because update classification and candidate kind are missing.
|
||||||
|
|
||||||
- [ ] **Step 3: Add `plugin_skill_update` candidate support**
|
- [x] **Step 3: Add `plugin_skill_update` candidate support**
|
||||||
|
|
||||||
Do not add a special status. Existing candidate statuses remain sufficient. Ensure
|
Do not add a special status. Existing candidate statuses remain sufficient. Ensure
|
||||||
`SkillLearningCandidate.from_dict()` accepts the new `kind` without changing legacy
|
`SkillLearningCandidate.from_dict()` accepts the new `kind` without changing legacy
|
||||||
@ -789,7 +789,7 @@ Use evidence:
|
|||||||
|
|
||||||
Set `priority=10`, `confidence=1.0`, `trigger_reason="plugin_update"`.
|
Set `priority=10`, `confidence=1.0`, `trigger_reason="plugin_update"`.
|
||||||
|
|
||||||
- [ ] **Step 4: Implement update classification and candidate creation**
|
- [x] **Step 4: Implement update classification and candidate creation**
|
||||||
|
|
||||||
Use canonical hashes and deterministic IDs:
|
Use canonical hashes and deterministic IDs:
|
||||||
|
|
||||||
@ -804,7 +804,7 @@ For `already_applied`, advance state without a candidate. For `fast_forward` and
|
|||||||
`three_way`, record an open candidate. If the same ID exists in any status, do not append
|
`three_way`, record an open candidate. If the same ID exists in any status, do not append
|
||||||
another JSONL record.
|
another JSONL record.
|
||||||
|
|
||||||
- [ ] **Step 5: Make candidate mutation atomic under the shared lock**
|
- [x] **Step 5: Make candidate mutation atomic under the shared lock**
|
||||||
|
|
||||||
Add an optional `WorkspaceWriteLock` to `SkillLearningStore`; EngineLoader supplies the
|
Add an optional `WorkspaceWriteLock` to `SkillLearningStore`; EngineLoader supplies the
|
||||||
shared workspace instance, while isolated unit-test construction falls back to a
|
shared workspace instance, while isolated unit-test construction falls back to a
|
||||||
@ -818,7 +818,7 @@ Inside one lock acquisition, read current candidates, check the deterministic ID
|
|||||||
atomically rewrite or append the JSONL record. Apply the same lock to candidate update and
|
atomically rewrite or append the JSONL record. Apply the same lock to candidate update and
|
||||||
transition methods. Nested calls from `PluginManager` reuse the reentrant lock.
|
transition methods. Nested calls from `PluginManager` reuse the reentrant lock.
|
||||||
|
|
||||||
- [ ] **Step 6: Supersede stale pending updates**
|
- [x] **Step 6: Supersede stale pending updates**
|
||||||
|
|
||||||
When a different pending candidate exists for the same plugin skill:
|
When a different pending candidate exists for the same plugin skill:
|
||||||
|
|
||||||
@ -831,7 +831,7 @@ learning_store.transition_learning_candidate(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 7: Run focused tests**
|
- [x] **Step 7: Run focused tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -840,7 +840,7 @@ pytest tests/unit/test_plugin_skill_sync.py tests/unit/test_skill_learning_candi
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 8: Commit**
|
- [x] **Step 8: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/plugins/skills.py app-instance/backend/beaver/memory/skills/models.py app-instance/backend/beaver/memory/skills/store.py app-instance/backend/tests/unit/test_plugin_skill_sync.py app-instance/backend/tests/unit/test_skill_learning_candidate_state.py
|
git add app-instance/backend/beaver/plugins/skills.py app-instance/backend/beaver/memory/skills/models.py app-instance/backend/beaver/memory/skills/store.py app-instance/backend/tests/unit/test_plugin_skill_sync.py app-instance/backend/tests/unit/test_skill_learning_candidate_state.py
|
||||||
@ -859,7 +859,7 @@ git commit -m "feat(plugins): enqueue skill upgrade candidates"
|
|||||||
- Test: `app-instance/backend/tests/unit/test_plugin_skill_learning.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_skill_learning.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_skill_learning_pipeline.py`
|
- Test: `app-instance/backend/tests/unit/test_skill_learning_pipeline.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing model and fast-forward tests**
|
- [x] **Step 1: Write failing model and fast-forward tests**
|
||||||
|
|
||||||
Test backward-compatible draft parsing and exact upstream fast-forward:
|
Test backward-compatible draft parsing and exact upstream fast-forward:
|
||||||
|
|
||||||
@ -877,7 +877,7 @@ assert provider.calls == []
|
|||||||
After publish, assert the new version contains the new upstream supporting files even when
|
After publish, assert the new version contains the new upstream supporting files even when
|
||||||
`SKILL.md` did not change.
|
`SKILL.md` did not change.
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -887,7 +887,7 @@ pytest tests/unit/test_plugin_skill_learning.py tests/unit/test_skill_learning_p
|
|||||||
Expected: FAIL because drafts have no provenance and the learning service has no plugin
|
Expected: FAIL because drafts have no provenance and the learning service has no plugin
|
||||||
update branch.
|
update branch.
|
||||||
|
|
||||||
- [ ] **Step 3: Add backward-compatible draft provenance**
|
- [x] **Step 3: Add backward-compatible draft provenance**
|
||||||
|
|
||||||
Extend `SkillDraft`:
|
Extend `SkillDraft`:
|
||||||
|
|
||||||
@ -897,7 +897,7 @@ provenance: dict[str, Any] = field(default_factory=dict)
|
|||||||
|
|
||||||
Include it in `to_dict()` and parse missing values as `{}` in `from_dict()`.
|
Include it in `to_dict()` and parse missing values as `{}` in `from_dict()`.
|
||||||
|
|
||||||
- [ ] **Step 4: Add a focused draft constructor**
|
- [x] **Step 4: Add a focused draft constructor**
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
|
|
||||||
@ -918,7 +918,7 @@ def create_plugin_update_draft(
|
|||||||
|
|
||||||
It writes `proposal_kind="plugin_skill_update"`.
|
It writes `proposal_kind="plugin_skill_update"`.
|
||||||
|
|
||||||
- [ ] **Step 5: Implement fast-forward synthesis**
|
- [x] **Step 5: Implement fast-forward synthesis**
|
||||||
|
|
||||||
In `SkillLearningService.synthesize_draft()`, branch before ordinary revision:
|
In `SkillLearningService.synthesize_draft()`, branch before ordinary revision:
|
||||||
|
|
||||||
@ -930,7 +930,7 @@ if candidate.kind == "plugin_skill_update":
|
|||||||
For `merge_mode == "fast_forward"`, load `U` from `SkillSpecStore`, parse its
|
For `merge_mode == "fast_forward"`, load `U` from `SkillSpecStore`, parse its
|
||||||
frontmatter/body, and create a draft exactly equal to `U`. Do not call the provider.
|
frontmatter/body, and create a draft exactly equal to `U`. Do not call the provider.
|
||||||
|
|
||||||
- [ ] **Step 6: Serialize all skill publication**
|
- [x] **Step 6: Serialize all skill publication**
|
||||||
|
|
||||||
Add an optional `WorkspaceWriteLock` to `SkillPublisher`; EngineLoader supplies the shared
|
Add an optional `WorkspaceWriteLock` to `SkillPublisher`; EngineLoader supplies the shared
|
||||||
workspace instance and isolated tests use a publisher-local fallback. Hold it across
|
workspace instance and isolated tests use a publisher-local fallback. Hold it across
|
||||||
@ -938,14 +938,14 @@ workspace instance and isolated tests use a publisher-local fallback. Hold it ac
|
|||||||
and disable. This protects ordinary learned skills as well as plugin-origin skills from
|
and disable. This protects ordinary learned skills as well as plugin-origin skills from
|
||||||
racing with boot or explicit plugin sync.
|
racing with boot or explicit plugin sync.
|
||||||
|
|
||||||
- [ ] **Step 7: Materialize referenced supporting files during publish**
|
- [x] **Step 7: Materialize referenced supporting files during publish**
|
||||||
|
|
||||||
For `proposal_kind="plugin_skill_update"`, resolve the snapshot and supporting-file plan
|
For `proposal_kind="plugin_skill_update"`, resolve the snapshot and supporting-file plan
|
||||||
from draft provenance. Stage the complete next version directory, including `SKILL.md`
|
from draft provenance. Stage the complete next version directory, including `SKILL.md`
|
||||||
and supporting files, before promoting it. Reject missing snapshots, path conflicts, or
|
and supporting files, before promoting it. Reject missing snapshots, path conflicts, or
|
||||||
tree-hash mismatches. Ordinary skill publication keeps its current behavior.
|
tree-hash mismatches. Ordinary skill publication keeps its current behavior.
|
||||||
|
|
||||||
- [ ] **Step 8: Preserve draft provenance on publish**
|
- [x] **Step 8: Preserve draft provenance on publish**
|
||||||
|
|
||||||
Change `SkillPublisher.publish()` provenance construction to:
|
Change `SkillPublisher.publish()` provenance construction to:
|
||||||
|
|
||||||
@ -959,7 +959,7 @@ provenance={
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 9: Run focused tests**
|
- [x] **Step 9: Run focused tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -968,7 +968,7 @@ pytest tests/unit/test_plugin_skill_learning.py tests/unit/test_skill_learning_p
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 10: Commit**
|
- [x] **Step 10: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/skills app-instance/backend/tests/unit/test_plugin_skill_learning.py app-instance/backend/tests/unit/test_skill_learning_pipeline.py
|
git add app-instance/backend/beaver/skills app-instance/backend/tests/unit/test_plugin_skill_learning.py app-instance/backend/tests/unit/test_skill_learning_pipeline.py
|
||||||
@ -986,7 +986,7 @@ git commit -m "feat(skill-learning): create plugin update drafts"
|
|||||||
- Test: `app-instance/backend/tests/unit/test_plugin_skill_learning.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_skill_learning.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_skill_learning_synthesizer_preservation.py`
|
- Test: `app-instance/backend/tests/unit/test_skill_learning_synthesizer_preservation.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing three-way prompt and parse tests**
|
- [x] **Step 1: Write failing three-way prompt and parse tests**
|
||||||
|
|
||||||
Assert the prompt contains labeled `OLD UPSTREAM`, `CURRENT LOCAL`, and `NEW UPSTREAM`
|
Assert the prompt contains labeled `OLD UPSTREAM`, `CURRENT LOCAL`, and `NEW UPSTREAM`
|
||||||
sections and does not confuse the current local version with the merge base.
|
sections and does not confuse the current local version with the merge base.
|
||||||
@ -1019,7 +1019,7 @@ def test_supporting_file_merge_blocks_divergent_edits() -> None:
|
|||||||
assert plan.conflicts[0].path == "a.txt"
|
assert plan.conflicts[0].path == "a.txt"
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1028,7 +1028,7 @@ pytest tests/unit/test_plugin_skill_learning.py tests/unit/test_skill_learning_s
|
|||||||
|
|
||||||
Expected: FAIL because three-way synthesis does not exist.
|
Expected: FAIL because three-way synthesis does not exist.
|
||||||
|
|
||||||
- [ ] **Step 3: Add `synthesize_plugin_update()`**
|
- [x] **Step 3: Add `synthesize_plugin_update()`**
|
||||||
|
|
||||||
Signature:
|
Signature:
|
||||||
|
|
||||||
@ -1054,7 +1054,7 @@ The system message must require JSON only and state:
|
|||||||
- list every intentional drop;
|
- list every intentional drop;
|
||||||
- leave `resolved_conflicts` empty only when no semantic conflict exists.
|
- leave `resolved_conflicts` empty only when no semantic conflict exists.
|
||||||
|
|
||||||
- [ ] **Step 4: Load all three snapshots in the learning service**
|
- [x] **Step 4: Load all three snapshots in the learning service**
|
||||||
|
|
||||||
Resolve:
|
Resolve:
|
||||||
|
|
||||||
@ -1065,7 +1065,7 @@ Resolve:
|
|||||||
Raise a specific `ValueError` when any referenced snapshot/version is missing. Do not
|
Raise a specific `ValueError` when any referenced snapshot/version is missing. Do not
|
||||||
fallback to a two-way merge.
|
fallback to a two-way merge.
|
||||||
|
|
||||||
- [ ] **Step 5: Build the deterministic supporting-file merge plan**
|
- [x] **Step 5: Build the deterministic supporting-file merge plan**
|
||||||
|
|
||||||
Compare files by path and content/executable digest:
|
Compare files by path and content/executable digest:
|
||||||
|
|
||||||
@ -1078,7 +1078,7 @@ Compare files by path and content/executable digest:
|
|||||||
Exclude `SKILL.md` because the synthesizer handles it. Store selected source references
|
Exclude `SKILL.md` because the synthesizer handles it. Store selected source references
|
||||||
and conflict records in draft provenance; do not duplicate file bytes in JSON.
|
and conflict records in draft provenance; do not duplicate file bytes in JSON.
|
||||||
|
|
||||||
- [ ] **Step 6: Create the plugin update draft**
|
- [x] **Step 6: Create the plugin update draft**
|
||||||
|
|
||||||
Store merge decisions in draft provenance:
|
Store merge decisions in draft provenance:
|
||||||
|
|
||||||
@ -1097,7 +1097,7 @@ Store merge decisions in draft provenance:
|
|||||||
If the supporting-file plan contains conflicts, the draft may be inspected but cannot be
|
If the supporting-file plan contains conflicts, the draft may be inspected but cannot be
|
||||||
published. V1 does not ask the LLM to merge arbitrary or binary files.
|
published. V1 does not ask the LLM to merge arbitrary or binary files.
|
||||||
|
|
||||||
- [ ] **Step 7: Run focused tests**
|
- [x] **Step 7: Run focused tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1106,7 +1106,7 @@ pytest tests/unit/test_plugin_skill_learning.py tests/unit/test_skill_learning_s
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 8: Commit**
|
- [x] **Step 8: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/plugins/tree_merge.py app-instance/backend/beaver/skills/learning app-instance/backend/tests/unit/test_plugin_skill_learning.py app-instance/backend/tests/unit/test_skill_learning_synthesizer_preservation.py
|
git add app-instance/backend/beaver/plugins/tree_merge.py app-instance/backend/beaver/skills/learning app-instance/backend/tests/unit/test_plugin_skill_learning.py app-instance/backend/tests/unit/test_skill_learning_synthesizer_preservation.py
|
||||||
@ -1125,7 +1125,7 @@ git commit -m "feat(skill-learning): synthesize three-way plugin updates"
|
|||||||
- Test: `app-instance/backend/tests/unit/test_skill_learning_eval.py`
|
- Test: `app-instance/backend/tests/unit/test_skill_learning_eval.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_skill_learning_pipeline.py`
|
- Test: `app-instance/backend/tests/unit/test_skill_learning_pipeline.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing plugin merge preservation tests**
|
- [x] **Step 1: Write failing plugin merge preservation tests**
|
||||||
|
|
||||||
Cover:
|
Cover:
|
||||||
|
|
||||||
@ -1148,7 +1148,7 @@ assert report.preservation_report == {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1157,7 +1157,7 @@ pytest tests/unit/test_skill_learning_preservation.py tests/unit/test_skill_lear
|
|||||||
|
|
||||||
Expected: FAIL because preservation only checks one base skill.
|
Expected: FAIL because preservation only checks one base skill.
|
||||||
|
|
||||||
- [ ] **Step 3: Add plugin merge preservation helper**
|
- [x] **Step 3: Add plugin merge preservation helper**
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
|
|
||||||
@ -1174,13 +1174,13 @@ def check_plugin_merge_preservation(
|
|||||||
It calls existing `check_preservation()` for local and upstream content, gives Safety and
|
It calls existing `check_preservation()` for local and upstream content, gives Safety and
|
||||||
Required Tools sections blocking weight, and reports unresolved conflicts separately.
|
Required Tools sections blocking weight, and reports unresolved conflicts separately.
|
||||||
|
|
||||||
- [ ] **Step 4: Use current local as replay baseline**
|
- [x] **Step 4: Use current local as replay baseline**
|
||||||
|
|
||||||
When `draft.proposal_kind == "plugin_skill_update"`, load `draft.base_version` as the
|
When `draft.proposal_kind == "plugin_skill_update"`, load `draft.base_version` as the
|
||||||
baseline skill. Continue to run the candidate arm with the draft context. Do not use raw
|
baseline skill. Continue to run the candidate arm with the draft context. Do not use raw
|
||||||
upstream `B` or `U` as the replay baseline.
|
upstream `B` or `U` as the replay baseline.
|
||||||
|
|
||||||
- [ ] **Step 5: Tighten publish gate**
|
- [x] **Step 5: Tighten publish gate**
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
|
|
||||||
@ -1197,7 +1197,7 @@ if draft.proposal_kind == "plugin_skill_update":
|
|||||||
|
|
||||||
The existing `passed is False` gate remains active.
|
The existing `passed is False` gate remains active.
|
||||||
|
|
||||||
- [ ] **Step 6: Run focused tests**
|
- [x] **Step 6: Run focused tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1206,7 +1206,7 @@ pytest tests/unit/test_skill_learning_preservation.py tests/unit/test_skill_lear
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 7: Commit**
|
- [x] **Step 7: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/skills/learning app-instance/backend/tests/unit/test_skill_learning_preservation.py app-instance/backend/tests/unit/test_skill_learning_eval.py app-instance/backend/tests/unit/test_skill_learning_pipeline.py
|
git add app-instance/backend/beaver/skills/learning app-instance/backend/tests/unit/test_skill_learning_preservation.py app-instance/backend/tests/unit/test_skill_learning_eval.py app-instance/backend/tests/unit/test_skill_learning_pipeline.py
|
||||||
@ -1224,7 +1224,7 @@ git commit -m "feat(skill-learning): gate plugin merge preservation"
|
|||||||
- Test: `app-instance/backend/tests/unit/test_plugin_skill_sync.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_skill_sync.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_skill_learning_pipeline.py`
|
- Test: `app-instance/backend/tests/unit/test_skill_learning_pipeline.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing lifecycle tests**
|
- [x] **Step 1: Write failing lifecycle tests**
|
||||||
|
|
||||||
Test:
|
Test:
|
||||||
|
|
||||||
@ -1242,7 +1242,7 @@ Test:
|
|||||||
active;
|
active;
|
||||||
- adopt changes `source_kind` to `managed`, removes binding, and keeps the skill active.
|
- adopt changes `source_kind` to `managed`, removes binding, and keeps the skill active.
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1251,7 +1251,7 @@ pytest tests/unit/test_plugin_skill_sync.py tests/unit/test_skill_learning_pipel
|
|||||||
|
|
||||||
Expected: FAIL because publication has no plugin acknowledgement callback.
|
Expected: FAIL because publication has no plugin acknowledgement callback.
|
||||||
|
|
||||||
- [ ] **Step 3: Add a narrow publication observer**
|
- [x] **Step 3: Add a narrow publication observer**
|
||||||
|
|
||||||
Extend pipeline construction with:
|
Extend pipeline construction with:
|
||||||
|
|
||||||
@ -1265,7 +1265,7 @@ or turn the publish API response into a failure. Mark the learning candidate pub
|
|||||||
before invoking the best-effort observer so clients do not retry a successful publish.
|
before invoking the best-effort observer so clients do not retry a successful publish.
|
||||||
The next sync is responsible for reconciliation.
|
The next sync is responsible for reconciliation.
|
||||||
|
|
||||||
- [ ] **Step 4: Implement `PluginManager.on_skill_published()`**
|
- [x] **Step 4: Implement `PluginManager.on_skill_published()`**
|
||||||
|
|
||||||
For `proposal_kind="plugin_skill_update"`:
|
For `proposal_kind="plugin_skill_update"`:
|
||||||
|
|
||||||
@ -1277,7 +1277,7 @@ For `proposal_kind="plugin_skill_update"`:
|
|||||||
6. clear `pending_candidate_id`;
|
6. clear `pending_candidate_id`;
|
||||||
7. set status `synced`.
|
7. set status `synced`.
|
||||||
|
|
||||||
- [ ] **Step 5: Implement sync-time reconciliation**
|
- [x] **Step 5: Implement sync-time reconciliation**
|
||||||
|
|
||||||
At the beginning of `sync_enabled()`, inspect each linked skill's current published
|
At the beginning of `sync_enabled()`, inspect each linked skill's current published
|
||||||
version. When provenance contains:
|
version. When provenance contains:
|
||||||
@ -1294,7 +1294,7 @@ and the referenced upstream snapshot exists, advance state only if the current v
|
|||||||
number is newer than `accepted_beaver_version`. Clear only the matching pending candidate.
|
number is newer than `accepted_beaver_version`. Clear only the matching pending candidate.
|
||||||
Never regress state when the runtime current pointer was rolled back to an older version.
|
Never regress state when the runtime current pointer was rolled back to an older version.
|
||||||
|
|
||||||
- [ ] **Step 6: Implement pause, resume, disable, missing, and adopt**
|
- [x] **Step 6: Implement pause, resume, disable, missing, and adopt**
|
||||||
|
|
||||||
`pause(plugin_id)` sets `updates_paused=True` and leaves linked skills unchanged.
|
`pause(plugin_id)` sets `updates_paused=True` and leaves linked skills unchanged.
|
||||||
`resume(plugin_id)` clears the flag and performs reconciliation/sync.
|
`resume(plugin_id)` clears the flag and performs reconciliation/sync.
|
||||||
@ -1313,7 +1313,7 @@ When discovery cannot find a previously known plugin, set status `missing`, pres
|
|||||||
`enabled` and `updates_paused`, skip update generation, and do not disable any linked
|
`enabled` and `updates_paused`, skip update generation, and do not disable any linked
|
||||||
skill.
|
skill.
|
||||||
|
|
||||||
- [ ] **Step 7: Run focused tests**
|
- [x] **Step 7: Run focused tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1322,7 +1322,7 @@ pytest tests/unit/test_plugin_skill_sync.py tests/unit/test_skill_learning_pipel
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 8: Commit**
|
- [x] **Step 8: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/plugins/skills.py app-instance/backend/beaver/skills/learning/pipeline.py app-instance/backend/beaver/skills/publisher/service.py app-instance/backend/tests/unit/test_plugin_skill_sync.py app-instance/backend/tests/unit/test_skill_learning_pipeline.py
|
git add app-instance/backend/beaver/plugins/skills.py app-instance/backend/beaver/skills/learning/pipeline.py app-instance/backend/beaver/skills/publisher/service.py app-instance/backend/tests/unit/test_plugin_skill_sync.py app-instance/backend/tests/unit/test_skill_learning_pipeline.py
|
||||||
@ -1339,7 +1339,7 @@ git commit -m "feat(plugins): track published updates and ownership"
|
|||||||
- Test: `app-instance/backend/tests/unit/test_plugin_runtime.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_runtime.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_phase5_skills_runtime.py`
|
- Test: `app-instance/backend/tests/unit/test_phase5_skills_runtime.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing runtime assembly tests**
|
- [x] **Step 1: Write failing runtime assembly tests**
|
||||||
|
|
||||||
Test:
|
Test:
|
||||||
|
|
||||||
@ -1352,7 +1352,7 @@ Test:
|
|||||||
workspace lock;
|
workspace lock;
|
||||||
- `EngineLoadResult.plugin_manager` and plugin summaries are available.
|
- `EngineLoadResult.plugin_manager` and plugin summaries are available.
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1361,7 +1361,7 @@ pytest tests/unit/test_plugin_runtime.py tests/unit/test_phase5_skills_runtime.p
|
|||||||
|
|
||||||
Expected: FAIL because `EngineLoader` does not assemble plugin services.
|
Expected: FAIL because `EngineLoader` does not assemble plugin services.
|
||||||
|
|
||||||
- [ ] **Step 3: Extend `EngineLoadResult` and loader injection**
|
- [x] **Step 3: Extend `EngineLoadResult` and loader injection**
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
|
|
||||||
@ -1372,7 +1372,7 @@ plugins: list[dict] = field(default_factory=list)
|
|||||||
|
|
||||||
Allow `plugin_manager` injection in `EngineLoader.__init__()` for tests.
|
Allow `plugin_manager` injection in `EngineLoader.__init__()` for tests.
|
||||||
|
|
||||||
- [ ] **Step 4: Assemble in dependency order**
|
- [x] **Step 4: Assemble in dependency order**
|
||||||
|
|
||||||
Required order:
|
Required order:
|
||||||
|
|
||||||
@ -1390,7 +1390,7 @@ Do not use `SkillsLoader.extra_dirs` for plugin skills. Explicit API enable/sync
|
|||||||
bounded blocking lock timeout; Engine boot uses a non-blocking attempt and proceeds with
|
bounded blocking lock timeout; Engine boot uses a non-blocking attempt and proceeds with
|
||||||
the current published skill set if another writer owns the lock.
|
the current published skill set if another writer owns the lock.
|
||||||
|
|
||||||
- [ ] **Step 5: Run runtime tests**
|
- [x] **Step 5: Run runtime tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1399,7 +1399,7 @@ pytest tests/unit/test_plugin_runtime.py tests/unit/test_phase5_skills_runtime.p
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 6: Commit**
|
- [x] **Step 6: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/engine/loader.py app-instance/backend/beaver/plugins app-instance/backend/tests/unit/test_plugin_runtime.py app-instance/backend/tests/unit/test_phase5_skills_runtime.py
|
git add app-instance/backend/beaver/engine/loader.py app-instance/backend/beaver/plugins app-instance/backend/tests/unit/test_plugin_runtime.py app-instance/backend/tests/unit/test_phase5_skills_runtime.py
|
||||||
@ -1414,7 +1414,7 @@ git commit -m "feat(runtime): sync declarative plugins at boot"
|
|||||||
- Modify: `app-instance/backend/beaver/interfaces/web/app.py`
|
- Modify: `app-instance/backend/beaver/interfaces/web/app.py`
|
||||||
- Test: `app-instance/backend/tests/unit/test_plugin_web_api.py`
|
- Test: `app-instance/backend/tests/unit/test_plugin_web_api.py`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing API tests**
|
- [x] **Step 1: Write failing API tests**
|
||||||
|
|
||||||
Cover:
|
Cover:
|
||||||
|
|
||||||
@ -1433,7 +1433,7 @@ manifest/sync errors. Assert lock timeout maps to `409 plugin_write_busy`. Asser
|
|||||||
payload contains the real absolute workspace or external search-root path. Assert disable
|
payload contains the real absolute workspace or external search-root path. Assert disable
|
||||||
without `{"disable_linked_skills": true}` is rejected.
|
without `{"disable_linked_skills": true}` is rejected.
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests and verify failure**
|
- [x] **Step 2: Run tests and verify failure**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1442,7 +1442,7 @@ pytest tests/unit/test_plugin_web_api.py -q
|
|||||||
|
|
||||||
Expected: FAIL with missing routes.
|
Expected: FAIL with missing routes.
|
||||||
|
|
||||||
- [ ] **Step 3: Add normalized plugin payload helper**
|
- [x] **Step 3: Add normalized plugin payload helper**
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
|
|
||||||
@ -1473,12 +1473,12 @@ Return:
|
|||||||
|
|
||||||
Never return arbitrary plugin file content, secrets, or absolute server paths.
|
Never return arbitrary plugin file content, secrets, or absolute server paths.
|
||||||
|
|
||||||
- [ ] **Step 4: Implement routes**
|
- [x] **Step 4: Implement routes**
|
||||||
|
|
||||||
Each mutating endpoint boots one runtime, invokes its `plugin_manager`, and returns the
|
Each mutating endpoint boots one runtime, invokes its `plugin_manager`, and returns the
|
||||||
updated plugin payload. Map `ValueError` messages to stable HTTP status codes.
|
updated plugin payload. Map `ValueError` messages to stable HTTP status codes.
|
||||||
|
|
||||||
- [ ] **Step 5: Run focused and existing web tests**
|
- [x] **Step 5: Run focused and existing web tests**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1487,7 +1487,7 @@ pytest tests/unit/test_plugin_web_api.py tests/unit/test_skill_learning_web_api.
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 6: Commit**
|
- [x] **Step 6: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/beaver/interfaces/web/app.py app-instance/backend/tests/unit/test_plugin_web_api.py
|
git add app-instance/backend/beaver/interfaces/web/app.py app-instance/backend/tests/unit/test_plugin_web_api.py
|
||||||
@ -1504,12 +1504,12 @@ git commit -m "feat(api): manage declarative plugins"
|
|||||||
- Modify: `app-instance/frontend/app/(app)/skills/page.tsx`
|
- Modify: `app-instance/frontend/app/(app)/skills/page.tsx`
|
||||||
- Test: `app-instance/frontend/lib/plugin-api.test.ts`
|
- Test: `app-instance/frontend/lib/plugin-api.test.ts`
|
||||||
|
|
||||||
- [ ] **Step 1: Write failing API client tests**
|
- [x] **Step 1: Write failing API client tests**
|
||||||
|
|
||||||
Test URL, method, and response typing for list, sync, enable, pause, resume, disable, and
|
Test URL, method, and response typing for list, sync, enable, pause, resume, disable, and
|
||||||
adopt.
|
adopt.
|
||||||
|
|
||||||
- [ ] **Step 2: Run frontend test and verify failure**
|
- [x] **Step 2: Run frontend test and verify failure**
|
||||||
|
|
||||||
Run the repository's existing frontend test command targeting:
|
Run the repository's existing frontend test command targeting:
|
||||||
|
|
||||||
@ -1520,7 +1520,7 @@ npx vitest run lib/plugin-api.test.ts
|
|||||||
|
|
||||||
Expected: FAIL because plugin API functions do not exist.
|
Expected: FAIL because plugin API functions do not exist.
|
||||||
|
|
||||||
- [ ] **Step 3: Add frontend types**
|
- [x] **Step 3: Add frontend types**
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
|
|
||||||
@ -1549,7 +1549,7 @@ export interface BeaverPlugin {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 4: Add API functions**
|
- [x] **Step 4: Add API functions**
|
||||||
|
|
||||||
Implement:
|
Implement:
|
||||||
|
|
||||||
@ -1563,7 +1563,7 @@ disablePlugin(pluginId, { disable_linked_skills: true })
|
|||||||
adoptPluginSkill(pluginId, skillName)
|
adoptPluginSkill(pluginId, skillName)
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 5: Add a `plugins` Skills tab**
|
- [x] **Step 5: Add a `plugins` Skills tab**
|
||||||
|
|
||||||
Extend `SkillsTab` and render a compact table with:
|
Extend `SkillsTab` and render a compact table with:
|
||||||
|
|
||||||
@ -1578,7 +1578,7 @@ Extend `SkillsTab` and render a compact table with:
|
|||||||
|
|
||||||
Do not add a separate marketing-style page or nested cards.
|
Do not add a separate marketing-style page or nested cards.
|
||||||
|
|
||||||
- [ ] **Step 6: Label plugin-origin skills and update candidates**
|
- [x] **Step 6: Label plugin-origin skills and update candidates**
|
||||||
|
|
||||||
In existing Published/Candidates/Drafts views:
|
In existing Published/Candidates/Drafts views:
|
||||||
|
|
||||||
@ -1586,7 +1586,7 @@ In existing Published/Candidates/Drafts views:
|
|||||||
- render `plugin_skill_update` as `插件升级合并 / Plugin update merge`;
|
- render `plugin_skill_update` as `插件升级合并 / Plugin update merge`;
|
||||||
- show `fast_forward` or `three_way` from candidate evidence/provenance.
|
- show `fast_forward` or `three_way` from candidate evidence/provenance.
|
||||||
|
|
||||||
- [ ] **Step 7: Run frontend tests and type checks**
|
- [x] **Step 7: Run frontend tests and type checks**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/frontend
|
cd app-instance/frontend
|
||||||
@ -1597,7 +1597,7 @@ npx tsc --noEmit
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 8: Commit**
|
- [x] **Step 8: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/frontend/types/index.ts app-instance/frontend/lib/api.ts app-instance/frontend/lib/plugin-api.test.ts 'app-instance/frontend/app/(app)/skills/page.tsx'
|
git add app-instance/frontend/types/index.ts app-instance/frontend/lib/api.ts app-instance/frontend/lib/plugin-api.test.ts 'app-instance/frontend/app/(app)/skills/page.tsx'
|
||||||
@ -1613,7 +1613,7 @@ git commit -m "feat(skills-ui): manage plugin skill mirrors"
|
|||||||
- Create: `docs/plugins/skill-plugins.md`
|
- Create: `docs/plugins/skill-plugins.md`
|
||||||
- Modify: `docs/product-discovery/beaver/README.md`
|
- Modify: `docs/product-discovery/beaver/README.md`
|
||||||
|
|
||||||
- [ ] **Step 1: Write the end-to-end lifecycle test**
|
- [x] **Step 1: Write the end-to-end lifecycle test**
|
||||||
|
|
||||||
The test must:
|
The test must:
|
||||||
|
|
||||||
@ -1634,7 +1634,7 @@ The test must:
|
|||||||
remains active;
|
remains active;
|
||||||
15. run two sync processes and assert no duplicate version or candidate is created.
|
15. run two sync processes and assert no duplicate version or candidate is created.
|
||||||
|
|
||||||
- [ ] **Step 2: Run the integration test and fix only lifecycle defects**
|
- [x] **Step 2: Run the integration test and fix only lifecycle defects**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1643,7 +1643,7 @@ pytest tests/integration/test_plugin_skill_lifecycle.py -v
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 3: Write operator documentation**
|
- [x] **Step 3: Write operator documentation**
|
||||||
|
|
||||||
Document:
|
Document:
|
||||||
|
|
||||||
@ -1658,7 +1658,7 @@ Document:
|
|||||||
- workspace locking, deferred boot sync, and publication reconciliation;
|
- workspace locking, deferred boot sync, and publication reconciliation;
|
||||||
- why plugin Python code is not executed in V1.
|
- why plugin Python code is not executed in V1.
|
||||||
|
|
||||||
- [ ] **Step 4: Run the complete relevant backend suite**
|
- [x] **Step 4: Run the complete relevant backend suite**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/backend
|
cd app-instance/backend
|
||||||
@ -1683,7 +1683,7 @@ pytest \
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 5: Run frontend verification**
|
- [x] **Step 5: Run frontend verification**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd app-instance/frontend
|
cd app-instance/frontend
|
||||||
@ -1694,7 +1694,7 @@ npx tsc --noEmit
|
|||||||
|
|
||||||
Expected: PASS.
|
Expected: PASS.
|
||||||
|
|
||||||
- [ ] **Step 6: Run a dirty-worktree-safe diff review**
|
- [x] **Step 6: Run a dirty-worktree-safe diff review**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git status --short
|
git status --short
|
||||||
@ -1708,7 +1708,7 @@ Expected:
|
|||||||
- only plugin/skill lifecycle files and planned docs/tests are included in this feature;
|
- only plugin/skill lifecycle files and planned docs/tests are included in this feature;
|
||||||
- unrelated pre-existing user changes remain untouched.
|
- unrelated pre-existing user changes remain untouched.
|
||||||
|
|
||||||
- [ ] **Step 7: Commit**
|
- [x] **Step 7: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add app-instance/backend/tests/integration/test_plugin_skill_lifecycle.py docs/plugins/skill-plugins.md docs/product-discovery/beaver/README.md
|
git add app-instance/backend/tests/integration/test_plugin_skill_lifecycle.py docs/plugins/skill-plugins.md docs/product-discovery/beaver/README.md
|
||||||
|
|||||||
Reference in New Issue
Block a user