fw: A8 template migration (drop on_fail: continue) and cp-docs-token/v1 layer (be110e25, 1d7ef09a)
Claude-Session: https://claude.ai/code/session_01N218bERyNeLhbLmDRDvbWF
This commit is contained in:
48
harnesses/contexts/cp-docs-token/v1/CLAUDE.md
Normal file
48
harnesses/contexts/cp-docs-token/v1/CLAUDE.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Step documents (cp-docs-token)
|
||||
|
||||
This step can read and write planner documents through `python3 -m lib.cp_cli`
|
||||
in step-docs mode (PLN-CLI-16). The mode is active because `CP_DOCS_TOKEN_FILE`
|
||||
and `CP_DOCS_CA_FILE` are set and the listener URL is in `CP_DOCS_URL`.
|
||||
|
||||
## Credential
|
||||
|
||||
- The token is a file at `$CP_DOCS_TOKEN_FILE`. cp-cli reads it on every call.
|
||||
- Never print, copy, log or pass the token on a command line.
|
||||
- Do not use `~/.cpconfig`; step-docs mode does not read it.
|
||||
|
||||
## Allowed commands
|
||||
|
||||
Only these commands work; anything else exits 2 `cp_docs_mode_command_not_allowed`:
|
||||
|
||||
```
|
||||
python3 -m lib.cp_cli planner items documents list <item-uuid>
|
||||
python3 -m lib.cp_cli planner items documents get <item-uuid> <name> [--json]
|
||||
python3 -m lib.cp_cli planner items documents put <item-uuid> <name> --file <path> (--create-only | --if-match <rev>)
|
||||
python3 -m lib.cp_cli planner items documents related [--json]
|
||||
```
|
||||
|
||||
Item arguments are full UUIDs. Do not pass a project id.
|
||||
|
||||
## Scope
|
||||
|
||||
- You may write only documents on this step's own item.
|
||||
- You may read documents on this step's item, its parent and its siblings.
|
||||
- `related` prints `item_uuid`, `parent_uuid`, `sibling_uuids` and `siblings_truncated`.
|
||||
|
||||
## Writes need a precondition
|
||||
|
||||
Exactly one of these is required on every `put` (neither or both exits 2):
|
||||
|
||||
- `--create-only` creates a new document (sends `If-None-Match: *`).
|
||||
- `--if-match <rev>` updates an existing one (sends `If-Match`). Take `<rev>`
|
||||
from `get --json` (the ETag revision); surrounding quotes are stripped.
|
||||
|
||||
## Errors
|
||||
|
||||
- 412 `precondition_failed`: the document changed or already exists. Re-run
|
||||
`get --json`, merge your change into the current content, and `put` again
|
||||
with the new revision.
|
||||
- 428 (precondition missing): add `--create-only` or `--if-match`.
|
||||
- 429 `step_docs_write_budget_exhausted` or `step_docs_rate_limited`: stop
|
||||
writing; do not retry in a loop.
|
||||
- 404 `not_found`, 400 `invalid_document_name`: fix the item uuid or name.
|
||||
16
harnesses/contexts/cp-docs-token/v1/harness.yaml
Normal file
16
harnesses/contexts/cp-docs-token/v1/harness.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
kind: context
|
||||
name: cp-docs-token
|
||||
version: 1
|
||||
description: "Step-docs mode env contract for lib.cp_cli (PLN-CLI-16): per-step wfcd_ token file and CA file delivered by the dispatcher; document access scoped to this step's item (spec/dispatcher.md D-40, spec/authentication.md AU-59)"
|
||||
requires: []
|
||||
provides: [cp-docs-token]
|
||||
|
||||
# Env-only layer: the dispatcher delivers the token and CA files into the
|
||||
# container; this layer sets only the paths. No secrets_required, no init script.
|
||||
env:
|
||||
CP_DOCS_TOKEN_FILE: /run/agent/cp-docs/token
|
||||
CP_DOCS_CA_FILE: /run/agent/cp-docs/ca.crt
|
||||
|
||||
context_files:
|
||||
- source: ./CLAUDE.md
|
||||
target: /opt/harness/context/cp-docs-token/CLAUDE.md
|
||||
@@ -24,7 +24,6 @@ nodes:
|
||||
kind: script
|
||||
cmd: scripts/workflow/state-tx
|
||||
cp_write: true
|
||||
on_fail: continue
|
||||
runtime_env:
|
||||
CP_URL: "https://agents.oreillyit.nz/api"
|
||||
STATE_TX_NEW_STATE: test-skeleton-ready
|
||||
|
||||
@@ -29,12 +29,10 @@ nodes:
|
||||
kind: script
|
||||
cmd: scripts/workflow/preflight-test-fail-mode
|
||||
cp_write: false
|
||||
on_fail: continue
|
||||
- id: state-tx
|
||||
kind: script
|
||||
cmd: scripts/workflow/state-tx
|
||||
cp_write: true
|
||||
on_fail: continue
|
||||
runtime_env:
|
||||
CP_URL: "https://agents.oreillyit.nz/api"
|
||||
STATE_TX_NEW_STATE: test-validated
|
||||
|
||||
56
tests/invariants/test_cp_docs_token_layer.py
Normal file
56
tests/invariants/test_cp_docs_token_layer.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""cp-docs-token/v1 context: env-only step-docs layer (PLN-CLI-16, D-40, AU-59).
|
||||
|
||||
The per-step wfcd_ token and CA file are delivered by the dispatcher. The layer
|
||||
sets the env contract and ships one context doc; no ESO mount, no init script,
|
||||
and no reference to the cp-service-token layer.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
_LAYER = (
|
||||
Path(__file__).resolve().parents[2] / "harnesses" / "contexts" / "cp-docs-token" / "v1"
|
||||
)
|
||||
|
||||
|
||||
def _doc() -> dict:
|
||||
return yaml.safe_load((_LAYER / "harness.yaml").read_text())
|
||||
|
||||
|
||||
def test_cp_docs_token_layer_parses_with_env_keys():
|
||||
doc = _doc()
|
||||
assert doc["kind"] == "context"
|
||||
assert doc["name"] == "cp-docs-token"
|
||||
assert doc["version"] == 1
|
||||
assert doc["requires"] == []
|
||||
assert doc["provides"] == ["cp-docs-token"]
|
||||
assert doc["env"] == {
|
||||
"CP_DOCS_TOKEN_FILE": "/run/agent/cp-docs/token",
|
||||
"CP_DOCS_CA_FILE": "/run/agent/cp-docs/ca.crt",
|
||||
}
|
||||
|
||||
|
||||
def test_cp_docs_token_layer_declares_no_secrets_and_no_init():
|
||||
doc = _doc()
|
||||
assert not doc.get("secrets_required")
|
||||
assert not doc.get("scripts")
|
||||
assert not (_LAYER / "init.sh").exists()
|
||||
|
||||
|
||||
def test_cp_docs_token_layer_context_file_mapping():
|
||||
doc = _doc()
|
||||
assert doc["context_files"] == [
|
||||
{
|
||||
"source": "./CLAUDE.md",
|
||||
"target": "/opt/harness/context/cp-docs-token/CLAUDE.md",
|
||||
}
|
||||
]
|
||||
assert (_LAYER / "CLAUDE.md").is_file()
|
||||
|
||||
|
||||
def test_cp_docs_token_layer_never_mentions_cp_service_token():
|
||||
for path in _LAYER.rglob("*"):
|
||||
if path.is_file():
|
||||
assert "cp-service-token" not in path.read_text(), path
|
||||
Reference in New Issue
Block a user