devlyn-cli 4.3.21 → 4.3.24

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 (51) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/README.md +1 -1
  4. package/bin/devlyn.js +26 -1
  5. package/bin/harness-activation.js +11 -13
  6. package/bin/instruction-templates.json +26 -0
  7. package/bin/instructions.js +1 -1
  8. package/bin/skill-history.json +15 -1
  9. package/config/skills/_shared/bring-in.py +810 -0
  10. package/config/skills/_shared/harness-store.py +18 -6
  11. package/config/skills/_shared/runtime-principles.md +2 -2
  12. package/config/skills/_shared/storage-checkout.py +117 -0
  13. package/config/skills/_shared/storage-client.py +1180 -0
  14. package/config/skills/_shared/task-complete.py +900 -88
  15. package/config/skills/_shared/task-completion.md +78 -6
  16. package/config/skills/_shared/warm-build-dirs.py +53 -3
  17. package/config/skills/devlyn-ideate/SKILL.md +2 -2
  18. package/config/skills/devlyn-ideate/references/loop.md +4 -4
  19. package/config/skills/devlyn-ideate/scripts/queue.py +70 -19
  20. package/config/storage-client-contract.json +7 -0
  21. package/harness/AGENTS.md +2 -2
  22. package/harness/CLAUDE.md +2 -2
  23. package/native/claude/.claude-plugin/plugin.json +1 -1
  24. package/native/claude/skills/_shared/bring-in.py +810 -0
  25. package/native/claude/skills/_shared/harness-store.py +18 -6
  26. package/native/claude/skills/_shared/runtime-principles.md +2 -2
  27. package/native/claude/skills/_shared/storage-checkout.py +117 -0
  28. package/native/claude/skills/_shared/storage-client.py +1180 -0
  29. package/native/claude/skills/_shared/task-complete.py +900 -88
  30. package/native/claude/skills/_shared/task-completion.md +78 -6
  31. package/native/claude/skills/_shared/warm-build-dirs.py +53 -3
  32. package/native/claude/skills/devlyn-ideate/SKILL.md +2 -2
  33. package/native/claude/skills/devlyn-ideate/references/loop.md +4 -4
  34. package/native/claude/skills/devlyn-ideate/scripts/queue.py +70 -19
  35. package/native/codex/.agents/plugins/marketplace.json +1 -1
  36. package/native/codex/plugins/devlyn/.codex-plugin/plugin.json +1 -1
  37. package/native/codex/plugins/devlyn/skills/_shared/bring-in.py +810 -0
  38. package/native/codex/plugins/devlyn/skills/_shared/harness-store.py +18 -6
  39. package/native/codex/plugins/devlyn/skills/_shared/runtime-principles.md +2 -2
  40. package/native/codex/plugins/devlyn/skills/_shared/storage-checkout.py +117 -0
  41. package/native/codex/plugins/devlyn/skills/_shared/storage-client.py +1180 -0
  42. package/native/codex/plugins/devlyn/skills/_shared/task-complete.py +900 -88
  43. package/native/codex/plugins/devlyn/skills/_shared/task-completion.md +78 -6
  44. package/native/codex/plugins/devlyn/skills/_shared/warm-build-dirs.py +53 -3
  45. package/native/codex/plugins/devlyn/skills/devlyn-ideate/SKILL.md +2 -2
  46. package/native/codex/plugins/devlyn/skills/devlyn-ideate/references/loop.md +4 -4
  47. package/native/codex/plugins/devlyn/skills/devlyn-ideate/scripts/queue.py +70 -19
  48. package/optional-skills/better-auth-setup/SKILL.md +9 -3
  49. package/optional-skills/better-auth-setup/references/proxy-setup.md +10 -3
  50. package/package.json +1 -1
  51. package/release.json +93 -43
@@ -169,16 +169,26 @@ def packet_schema(packet):
169
169
  resource(packet.get("workerPrompt"))
170
170
 
171
171
 
172
- def verify_packet(packet, path, skills):
172
+ def verify_packet(packet, path, skills, *, accepted_receipt=None):
173
173
  packet_schema(packet)
174
174
  if packet.get("schema_version", 1) == 1:
175
175
  return # Preserve v1 execution without upgrading its resources.
176
176
  receipt = packet.get("receipt")
177
177
  require(isinstance(receipt, str) and "\0" not in receipt and Path(receipt).is_absolute()
178
178
  and Path(receipt).name == "receipt.json" and Path(receipt).parent.parent.name == "devlyn-completion"
179
- and Path(receipt) == Path(path).with_name("receipt.json"),
179
+ and (accepted_receipt is not None or Path(receipt) == Path(path).with_name("receipt.json")),
180
180
  "malformed packet receipt path")
181
- binding = read_binding(Path(receipt).parent.parent.parent)
181
+ common = Path(receipt).parent.parent.parent
182
+ if accepted_receipt is not None:
183
+ saved = accepted_receipt
184
+ require(saved.get('acceptance') and saved.get('id') == Path(receipt).parent.name
185
+ and saved.get('common_gitdir') == str(common) and saved.get('worktree') == packet['worktree'],
186
+ 'accepted packet receipt binding changed')
187
+ original = Path(receipt)
188
+ archived = common / 'devlyn-completion/retired' / (
189
+ saved['id'] + '.' + saved.get('storage', {}).get('request', {}).get('operation_id', '')) / 'receipt.json'
190
+ require(Path(path).with_name('receipt.json') in (original, archived), 'accepted packet custody path changed')
191
+ binding = read_binding(common)
182
192
  if packet["harness"] is None:
183
193
  require(binding is None, "bound task packet has no harness/defaults source")
184
194
  # An unbound installation's own defaults change with its normal updates: never gate acceptance or delivery on
@@ -193,15 +203,17 @@ def verify_packet(packet, path, skills):
193
203
  defaults = {"source": "release", **binding["defaults"]}
194
204
  require(packet["methodology"]["defaults"] == defaults, "packet defaults differ from queue binding")
195
205
  expected_runner = [str(Path(ref["root"]) / "config/skills/devlyn-ideate/scripts/acceptance.py"),
196
- "run", "--packet", str(path)]
206
+ "run", "--packet", str(Path(receipt).with_name('packet.json'))]
197
207
  require(isinstance(packet.get("runner"), list) and packet["runner"][1:] == expected_runner, "packet runner differs from bound release")
198
208
  prompt = packet["workerPrompt"]
199
- require(prompt["path"] == str(Path(path).parent / "worker-prompt.md") and file_identity(prompt["path"]) == prompt,
209
+ actual_prompt = file_identity(Path(path).parent / 'worker-prompt.md')
210
+ require(prompt["path"] == str(Path(receipt).parent / "worker-prompt.md") and actual_prompt['sha256'] == prompt['sha256'],
200
211
  "worker prompt changed or missing; restore the bound resource")
201
212
  for entry in packet["methodology"]["projectInstructions"]:
202
213
  name = Path(entry["path"]).name
203
214
  require(name in ("CLAUDE.md", "AGENTS.md") and entry["path"] == str(Path(packet["worktree"]) / name),
204
215
  "project instruction path escapes packet worktree")
205
- committed = subprocess.run(["git", "-C", packet["worktree"], "cat-file", "blob", f"{packet['inputs_sha']}:{name}"], capture_output=True)
216
+ repository = ['--git-dir', str(common)] if accepted_receipt is not None else ['-C', packet['worktree']]
217
+ committed = subprocess.run(["git", *repository, "cat-file", "blob", f"{packet['inputs_sha']}:{name}"], capture_output=True)
206
218
  require(committed.returncode == 0 and hashlib.sha256(committed.stdout).hexdigest() == entry["sha256"],
207
219
  f"project instruction differs from inputs commit: {name}")
@@ -1,4 +1,4 @@
1
- The following Devlyn instructions are defaults for this session. Applicable explicit user instructions and native owner/project instructions take precedence over these defaults. This delegation applies only to the Devlyn defaults below; it does not change other engine instructions or permissions.
1
+ The following Devlyn instructions are defaults for this session. Explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which these defaults replace. They change no other engine instructions or permissions.
2
2
 
3
3
  # Project Instructions
4
4
 
@@ -15,7 +15,7 @@ Seven rules govern every change. Cite them by name when a decision touches one.
15
15
  1. **No workaround** — fix the root cause, never the symptom. No `any`, no `@ts-ignore`, no silent `catch`, no hardcoded fallback that hides a broken contract. No config bypasses.
16
16
  2. **No overengineering** — smallest sufficient change, one responsibility per module. New abstractions need an observed failure. Subtractive-first: ask "what can I delete instead?" before writing anything new. Remove code your change makes unused; only report unrelated pre-existing dead code.
17
17
  3. **No guesswork** — verify with the actual files, logs, diffs, and run output before forming conclusions. State the falsifiable prediction BEFORE the experiment; record raw results AFTER. Retroactive prediction edits are dishonest.
18
- 4. **Worldclass** — code that survives review at a non-trivial codebase. Zero CRITICAL, zero HIGH security/design findings on the shippable path.
18
+ 4. **Worldclass** — zero CRITICAL, zero HIGH security/design findings on the shippable path. Any review is one pass in which every reviewer, including each one the user requires, reads the source the checks ran on; fix each finding or reject it with a reason, re-check the fixes and the invariants they touch, and repeat the full review only while a CRITICAL or HIGH finding remains.
19
19
  5. **Best practice** — idiomatic for the language and framework. Use standard primitives; do not hand-roll what the library already provides.
20
20
  6. **Optimized** — use efficient code and avoid unnecessary work without sacrificing correctness or the user's requirements.
21
21
  7. **Production ready** — error states are explicit and visible; behavior under failure is what the user expects, not silent corruption.
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env python3
2
+ """Protocol-1 finite checkout adapter. Rust owns the allocation subreceipt."""
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from pathlib import Path, PurePosixPath, PureWindowsPath
8
+ import runpy
9
+ import subprocess
10
+
11
+ storage = runpy.run_path(str(Path(__file__).with_name('storage-client.py')))
12
+ require = storage['require']
13
+ validate = storage['validate']
14
+ StorageRefusal = storage['StorageRefusal']
15
+
16
+
17
+ def validate_request(op, request, *, windows=None):
18
+ destination = {'state': ('absent', 'precreated_empty'), 'path': 'str'}
19
+ if type(request.get('destination')) is dict and request['destination'].get('state') == 'precreated_empty':
20
+ destination['identity'] = 'identity'
21
+ shapes = {
22
+ 'capabilities': {},
23
+ 'allocate': {'adapter_protocol': 'u32', 'operation_id': 'str', 'control_directory': 'str',
24
+ 'repository': 'str', 'baseline': 'str', 'branch': 'str', 'owner': 'owner',
25
+ 'external_receipt': 'str', 'destination': destination, 'cache_policy': ('cargo_worktree_v1',)},
26
+ 'reconcile-for-removal': {'adapter_protocol': 'u32', 'allocation_operation_id': 'str',
27
+ 'external_receipt': 'str', 'removal_operation_id': 'str', 'worktree_identity?': 'identity?'},
28
+ 'unenroll': {'adapter_protocol': 'u32', 'operation_id': 'str'},
29
+ }
30
+ require(op in shapes, 'unknown checkout adapter operation', 'storage_invalid')
31
+ validate(request, shapes[op], exact=True, windows=windows)
32
+ if op == 'capabilities':
33
+ return request
34
+ require(request['adapter_protocol'] == 1, 'unsupported checkout adapter protocol', 'storage_invalid')
35
+ for field in ('operation_id', 'allocation_operation_id', 'removal_operation_id'):
36
+ if field in request:
37
+ storage['canonical_operation'](request[field])
38
+ path_type = PureWindowsPath if (os.name == 'nt' if windows is None else windows) else PurePosixPath
39
+ for name in ('repository', 'control_directory', 'external_receipt'):
40
+ if name in request:
41
+ path = path_type(request[name])
42
+ require(path.is_absolute() and '..' not in path.parts and '.' not in request[name].replace('\\', '/').split('/'),
43
+ 'adapter paths must be absolute without traversal', 'storage_invalid')
44
+ if op == 'allocate':
45
+ import re
46
+ require(re.fullmatch(r'(?:[0-9a-f]{40}|[0-9a-f]{64})', request['baseline']),
47
+ 'baseline must be an exact commit', 'storage_invalid')
48
+ return request
49
+
50
+
51
+ def validate_closure(value):
52
+ require(type(value) is dict, 'positive reservation closure missing')
53
+ tag = value.get('established_by')
54
+ shapes = {'released': {'established_by': ('released',), 'reservation': 'reservation'},
55
+ 'authority_expired': {'established_by': ('authority_expired',), 'operation_id': 'str'},
56
+ 'never_admitted': {'established_by': ('never_admitted',), 'operation_id': 'str'},
57
+ 'legacy_unreserved': {'established_by': ('legacy_unreserved',), 'receipt_version': 'u32'}}
58
+ require(tag in shapes, 'positive reservation closure missing')
59
+ validate(value, shapes[tag])
60
+ return value
61
+
62
+
63
+ def decode_reply(raw, op, code):
64
+ require(raw.endswith(b'\n') and b'\n' not in raw[:-1] and len(raw) <= storage['MAX_REPLY'],
65
+ 'uncertain adapter output; replay the saved request')
66
+ value = storage['loads'](raw)
67
+ validate(value, {'adapter_protocol': 'u32', 'op': (op,)})
68
+ require(value['adapter_protocol'] == 1 and ('result' in value) != ('error' in value), 'invalid adapter envelope')
69
+ if 'error' in value:
70
+ error = value['error']
71
+ validate(error, {'code': 'str', 'phase': 'str', 'retryable': 'bool', 'receipt': 'str?', 'diagnostic': 'str'})
72
+ require(code == 2, 'adapter refusal exit mismatch')
73
+ refusal = StorageRefusal('storage_invalid' if error['code'] == 'invalid_request' else error['code'],
74
+ error['diagnostic'], operation=op, phase=error['phase'])
75
+ refusal.retryable = error['retryable']
76
+ raise refusal
77
+ require(code == 0, 'uncertain adapter exit; replay the saved request')
78
+ result = value['result']
79
+ if op == 'capabilities':
80
+ validate(result, {'cli_checkout_v1': 'bool', 'adapter_protocol': 'u32', 'receipt_version': 'u32',
81
+ 'destination_modes': ['str'], 'helper_release': {'version': 'str', 'os': 'str', 'arch': 'str', 'sha256': 'str'}})
82
+ require(result['cli_checkout_v1'] and result['adapter_protocol'] == 1 and result['receipt_version'] == 3,
83
+ 'incompatible checkout helper', 'storage_unsupported')
84
+ elif op == 'allocate':
85
+ validate(result, {'allocation_binding': {'baseline': 'str', 'branch': 'str', 'destination': 'str',
86
+ 'destination_mode': {'state': ('absent', 'precreated_empty'), 'identity?': 'identity'},
87
+ 'owner': 'owner', 'publication_owner': 'owner', 'receipt': 'str',
88
+ 'adapter': {'operation_id': 'str', 'repository': 'str', 'control_directory': 'str', 'external_receipt': 'str'}},
89
+ 'receipt': 'str', 'publications': ['published'], 'warnings': ['warning']})
90
+ validate_closure(result.get('closure_result'))
91
+ elif op == 'reconcile-for-removal':
92
+ validate(result, {'operation_id': 'str', 'cache_ids': ['str']})
93
+ require(len(result['cache_ids']) == len(set(result['cache_ids'])), 'duplicate removal cache ID')
94
+ validate_closure(result.get('closure_result'))
95
+ return result
96
+
97
+
98
+ def call(registration, op, request):
99
+ """One attempt; uncertain stdout leaves the caller's durable request unchanged."""
100
+ validate_request(op, request)
101
+ raw = json.dumps(request, ensure_ascii=False, separators=(',', ':'), allow_nan=False).encode()+b'\n'
102
+ require(len(raw)-1 <= storage['MAX_REQUEST'], 'adapter request exceeds limit', 'storage_invalid')
103
+ try:
104
+ require(storage['regular_digest'](registration['helper_path']) == registration['helper_release']['sha256'],
105
+ 'enrolled helper changed')
106
+ # No timeout: killing an allocating holder can turn replay into crash recovery.
107
+ # Spool output to disk so an invalid helper cannot exhaust Python memory.
108
+ import tempfile
109
+ with tempfile.TemporaryFile() as output, tempfile.TemporaryFile() as diagnostic:
110
+ process = subprocess.run([registration['helper_path'], 'storage-checkout', op, '--json'],
111
+ input=raw, stdout=output, stderr=diagnostic)
112
+ output.seek(0)
113
+ reply = output.read(storage['MAX_REPLY']+1)
114
+ return decode_reply(reply, op, process.returncode)
115
+ except OSError as exc:
116
+ raise StorageRefusal('storage_unavailable', f'helper outcome uncertain: {exc}; replay saved request',
117
+ operation=op, phase='helper') from exc