@arbiterforge/ca-pi 0.14.10 → 0.14.16

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 (44) hide show
  1. package/README.md +33 -9
  2. package/package.json +2 -1
  3. package/plugins/ca-pi/CHANGELOG.md +67 -0
  4. package/plugins/ca-pi/LICENSE +661 -0
  5. package/plugins/ca-pi/SKILLS.md +2 -2
  6. package/plugins/ca-pi/SPRINT.md +47 -0
  7. package/plugins/ca-pi/THIRD_PARTY_NOTICES.md +40 -0
  8. package/plugins/ca-pi/generated/command-catalog.json +2 -2
  9. package/plugins/ca-pi/helpers/artifacts/ca-artifact-darwin-amd64 +0 -0
  10. package/plugins/ca-pi/helpers/artifacts/ca-artifact-darwin-arm64 +0 -0
  11. package/plugins/ca-pi/helpers/artifacts/ca-artifact-linux-amd64 +0 -0
  12. package/plugins/ca-pi/helpers/artifacts/ca-artifact-linux-arm64 +0 -0
  13. package/plugins/ca-pi/helpers/artifacts/ca-artifact-windows-amd64.exe +0 -0
  14. package/plugins/ca-pi/helpers/artifacts/ca-artifact-windows-arm64.exe +0 -0
  15. package/plugins/ca-pi/helpers/artifacts/release.json +6 -6
  16. package/plugins/ca-pi/hooks/_approvallib.py +58 -22
  17. package/plugins/ca-pi/hooks/_artifactauthoritylib.py +2135 -0
  18. package/plugins/ca-pi/hooks/_artifactlib.py +164 -7
  19. package/plugins/ca-pi/hooks/_artifactpromptlib.py +177 -0
  20. package/plugins/ca-pi/hooks/_host.py +1 -1
  21. package/plugins/ca-pi/hooks/_overrideappendlib.py +5 -1
  22. package/plugins/ca-pi/hooks/_prerequisitelib.py +44 -22
  23. package/plugins/ca-pi/hooks/_reconciliationlib.py +472 -0
  24. package/plugins/ca-pi/hooks/_releaselib.py +345 -111
  25. package/plugins/ca-pi/hooks/_sprintapprovallib.py +302 -0
  26. package/plugins/ca-pi/hooks/artifact-authority-hook.py +127 -0
  27. package/plugins/ca-pi/hooks/artifact-authority.py +102 -0
  28. package/plugins/ca-pi/hooks/artifact-reconcile.py +57 -0
  29. package/plugins/ca-pi/hooks/hostapi.py +1 -1
  30. package/plugins/ca-pi/hooks/prompt-submit.py +20 -3
  31. package/plugins/ca-pi/hooks/releasehash.py +36 -23
  32. package/plugins/ca-pi/includes/artifacts.md +120 -13
  33. package/plugins/ca-pi/includes/sprint-authority.md +69 -0
  34. package/plugins/ca-pi/package.json +1 -1
  35. package/plugins/ca-pi/routines/INDEX.md +7 -4
  36. package/plugins/ca-pi/routines/brainstorming/SKILL.md +97 -29
  37. package/plugins/ca-pi/routines/commit-gate/SKILL.md +7 -1
  38. package/plugins/ca-pi/routines/executing-plans/SKILL.md +32 -15
  39. package/plugins/ca-pi/routines/release/SKILL.md +220 -70
  40. package/plugins/ca-pi/routines/skill-author/SKILL.md +26 -5
  41. package/plugins/ca-pi/routines/writing-plans/SKILL.md +22 -10
  42. package/plugins/ca-pi/skills/ca-commit/SKILL.md +170 -16
  43. package/plugins/ca-pi/skills/ca-new-skill/SKILL.md +116 -21
  44. package/plugins/ca-pi/skills/ca-release/SKILL.md +6 -4
@@ -73,6 +73,33 @@ explicit "decide it this way" steers here. STOP for explicit user approval of th
73
73
  plan before autonomy begins. A blocking `[CONFIRM-NN]` is resolved here, with the user — never carried
74
74
  into autonomous execution unresolved.
75
75
 
76
+ ### Initial HTML approval sequence
77
+
78
+ Prepare one review packet containing the ready spec, ready draft plan, adversarial findings and
79
+ explicit delegation terms. The installed private pair adapter in
80
+ `<plugin-root>/includes/artifacts.md` owns the combined one-reply transaction:
81
+
82
+ 1. Finish the spec as a ready draft. Let `writing-plans` use `_preflight_plan_authoring` with
83
+ `draft_for_pair: true` only for this full-lane initial sprint. Populate the same-slug plan as
84
+ `draft_preview`, validate both, and review their complete criteria, paths, tests and checkpoints.
85
+ Neither draft has execution authority. Do not separately approve or `plan-bind` this pair.
86
+ 2. Arm both exact identities with `arm-sprint`. Include `--delegate-methods` only when the presented
87
+ terms explicitly ask the user to delegate existing task-method revisions. Present the exact returned reply;
88
+ it names both artifacts and `delegate-methods` or `approve-only`, not an ambiguous generic yes.
89
+ 3. The existing qualified host observes that single reply. `sprint-approve` validates both frozen
90
+ definitions and commits approval plus plan binding in one recoverable native transaction.
91
+ Verify both current approved identities before execution. No execution before both approvals.
92
+
93
+ One reply approves the reviewed pair, not future unbounded work. `delegate-methods` does not grant
94
+ initial self-approval, product decisions, prerequisite satisfaction, new files, verification changes,
95
+ commit/push, provider/spending/disclosure/publication authority, or any security bypass. Keep
96
+ no per-feature interviews and no attended execution pauses. A declined, changed or unsupported pair
97
+ retains drafts; capability absence is not permission to invent a legacy shadow or receipt.
98
+ If an observed reply was interrupted, `resume-sprint` replays its stored transaction without asking
99
+ for the same reply again. An armed request with no observed reply cannot be resumed as approval.
100
+ The existing sequential single-artifact path remains available for `/feature` and older workflows;
101
+ Markdown retains its original approval contract.
102
+
76
103
  **Recorded-intent read — BEFORE spec approval, fail-soft (ADR-0025):** consult
77
104
  `decisions/decision-log.md`, the accepted-ADR index, the `plans/` artifacts' section headings, and
78
105
  `open-questions.md`'s deferred sections — index-first, bodies only when the index names them
@@ -128,6 +155,26 @@ ambiguity, a trade-off, a non-obvious option — DECIDE rather than stop:
128
155
  before `· confidence:`. The `low`-confidence
129
156
  entries are exactly what the user reviews in the morning; nothing is hidden behind autonomy.
130
157
 
158
+ ### Typed SMARTS method authority
159
+
160
+ After the pair's explicit `delegate-methods` approval, use the private `smarts-apply` producer for a
161
+ material change to an existing task's method steps. First perform SMARTS Step 0 and the existing
162
+ project/ADR/scope checks. Submit the actual options, six-lens comparison, selected option, strength
163
+ and rationale with the exact plan revision/model hash and the retained initial `grant_receipt`.
164
+ Use the private request shape and adapter in `<plugin-root>/includes/artifacts.md`; a pasted matrix
165
+ or an append-only log is not itself authority. A single evidenced option is allowed; an in-scope tie
166
+ uses the existing priorities rather than another user interview. Do not manufacture alternatives.
167
+
168
+ The producer retains all protected plan fields and requires the original user-approved scope. It can
169
+ change only existing task `steps`; paths, tests, criteria, dependencies, prerequisites, rollback and
170
+ checkpoint membership cannot expand. It quiesces affected writers by refusing `IN_PROGRESS` and
171
+ invalidates affected checkpoint/dependency proof before another attempt, without clearing `BLOCKED`
172
+ members or their recorded reasons. A blocked method revision
173
+ therefore resumes through fresh `eligible`, context tickets, verification and review, never auto-pass.
174
+ The original log still records material reasoning and source citations. No result implies accepted
175
+ work or independently proves the truth of model judgment. Changes outside the granted boundary use
176
+ their actual authority owner; this producer does not satisfy prerequisites or waive security stops.
177
+
131
178
  ### Hard gates — true stops, even mid-sprint
132
179
 
133
180
  NEVER auto-decided. Halt and surface to the user:
@@ -0,0 +1,40 @@
1
+ # Third-party notices
2
+
3
+ ## Go runtime and standard library
4
+
5
+ The packaged `ca-artifact` executable is built with Go and includes Go runtime
6
+ and standard-library code. The following notice is from the Go 1.27.1 toolchain
7
+ used by the native build. Upstream: https://go.dev/LICENSE.
8
+
9
+ This runtime notice does not determine the provenance of the separately authored
10
+ workflow skills. Their historical provenance review remains a separate task.
11
+
12
+ ```text
13
+ Copyright 2009 The Go Authors.
14
+
15
+ Redistribution and use in source and binary forms, with or without
16
+ modification, are permitted provided that the following conditions are
17
+ met:
18
+
19
+ * Redistributions of source code must retain the above copyright
20
+ notice, this list of conditions and the following disclaimer.
21
+ * Redistributions in binary form must reproduce the above
22
+ copyright notice, this list of conditions and the following disclaimer
23
+ in the documentation and/or other materials provided with the
24
+ distribution.
25
+ * Neither the name of Google LLC nor the names of its
26
+ contributors may be used to endorse or promote products derived from
27
+ this software without specific prior written permission.
28
+
29
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
+ ```
@@ -125,7 +125,7 @@
125
125
  },
126
126
  "commit": {
127
127
  "name": "commit",
128
- "description": "Run the full commit gate — the only sanctioned path to a git commit.",
128
+ "description": "Create a verified local Git commit when committing changes is requested. Not for explaining commit history, drafting a message only, or postponing a commit. Applies every commit gate and never implies a push or PR.",
129
129
  "skillPath": "skills/ca-commit/SKILL.md",
130
130
  "visibility": "core",
131
131
  "workflow": "ship",
@@ -227,7 +227,7 @@
227
227
  },
228
228
  "new-skill": {
229
229
  "name": "new-skill",
230
- "description": "Author a new codeArbiter skill: prove the gap is real, get the spec approved, then write it.",
230
+ "description": "Assess a request for a new reusable codeArbiter skill, prove the uncovered gap, agree scope, author and integrate it. Questions and one-off tasks do not authorize adding a skill.",
231
231
  "skillPath": "skills/ca-new-skill/SKILL.md",
232
232
  "visibility": "advanced",
233
233
  "workflow": "extend",
@@ -6,32 +6,32 @@
6
6
  "binaries": {
7
7
  "darwin/amd64": {
8
8
  "file": "ca-artifact-darwin-amd64",
9
- "sha256": "fab98b0fd68bafe5f2f52941f704dc32bf623eaa68be18e9e68cc9bc07f05cac",
9
+ "sha256": "8e2a1f3026d5d9a8de36f3f748a7140ecf1ef8e768022aaa8745884e09551f9a",
10
10
  "native_tested": true
11
11
  },
12
12
  "darwin/arm64": {
13
13
  "file": "ca-artifact-darwin-arm64",
14
- "sha256": "44211f23050e42ac155432e6d3e3edc58fd7180171a982c6c4a39378ec15218d",
14
+ "sha256": "cce4b1cd751ada483bffd2de53bd14cdfda50f5d7392b49ba3f020b58fc59da4",
15
15
  "native_tested": true
16
16
  },
17
17
  "linux/amd64": {
18
18
  "file": "ca-artifact-linux-amd64",
19
- "sha256": "100e36cf452a79fb678ee2f99956953ae97da5b49bc673e77cef094a87bd1cab",
19
+ "sha256": "7637fe233e8731f4f15ff36b2e80714edd7e6fd96117b41f911d0c8f46639615",
20
20
  "native_tested": true
21
21
  },
22
22
  "linux/arm64": {
23
23
  "file": "ca-artifact-linux-arm64",
24
- "sha256": "6c1aa70e582e5054c8125cb89585bfd0f0eda6a2b32f3c67c9cdbff76ca78e0f",
24
+ "sha256": "daa4ed9f1f577c59afd0250701959718f501799c204df01a92f00876c088f619",
25
25
  "native_tested": true
26
26
  },
27
27
  "windows/amd64": {
28
28
  "file": "ca-artifact-windows-amd64.exe",
29
- "sha256": "6a17e6188fede9f8b8271d0f9eeab41512b58118e5b12781e62b4ce8dc0c5519",
29
+ "sha256": "16a3a26c5c606fe77703cbc9ae69414cbbe93f2c50dda9301eb9784e2e842206",
30
30
  "native_tested": true
31
31
  },
32
32
  "windows/arm64": {
33
33
  "file": "ca-artifact-windows-arm64.exe",
34
- "sha256": "746bfd19ed124b14376326b05a435aad381d7a72093f0e8002fa66e186e64194",
34
+ "sha256": "f64155cd951be94039ea85d4420b2d3bad75ebcb14e68aeb15be8ccf9ec1f690",
35
35
  "native_tested": true
36
36
  }
37
37
  }
@@ -15,6 +15,8 @@ import sys
15
15
  from typing import Any
16
16
 
17
17
  import _artifactlib
18
+ import _artifactauthoritylib
19
+ import _artifactpromptlib
18
20
 
19
21
 
20
22
  PENDING = Path(".codearbiter/.markers/pending-user-approval.json")
@@ -150,6 +152,13 @@ def arm_user_approval(
150
152
  raise ApprovalError(
151
153
  "PENDING_APPROVAL", "resolve or clear the existing approval request first"
152
154
  ) from exc
155
+ try:
156
+ _artifactpromptlib.register(
157
+ root, "approval", artifact_id, f"approve {artifact_id} {token}"
158
+ )
159
+ except _artifactpromptlib.PromptRouteError:
160
+ (root / PENDING).unlink()
161
+ raise
153
162
  return {
154
163
  "artifact_id": artifact_id,
155
164
  "reply": f"approve {artifact_id} {token}",
@@ -182,6 +191,9 @@ def _load_pending(root: Path) -> dict[str, Any] | None:
182
191
  return None
183
192
  except (OSError, UnicodeError, ValueError) as exc:
184
193
  raise ApprovalError("INVALID_PENDING_APPROVAL", "pending approval is unreadable") from exc
194
+ if isinstance(value, dict) and value.get("format") == "codearbiter.pending-user-approval/0.2.0":
195
+ import _sprintapprovallib
196
+ return _sprintapprovallib.validate_pending(value)
185
197
  expected = {
186
198
  "format", "artifact_id", "kind", "revision", "model_sha256",
187
199
  "normative_sha256", "token_sha256",
@@ -216,7 +228,11 @@ def cancel_user_approval(root: str | Path, artifact_id: str) -> dict[str, Any]:
216
228
  "PENDING_APPROVAL_MISMATCH",
217
229
  "the pending approval request belongs to another artifact",
218
230
  )
231
+ if pending["format"] == "codearbiter.pending-user-approval/0.2.0":
232
+ import _sprintapprovallib
233
+ return _sprintapprovallib.cancel(root, pending)
219
234
  (root / PENDING).unlink()
235
+ _artifactpromptlib.unregister(root, "approval", artifact_id)
220
236
  return {"artifact_id": artifact_id, "cancelled": True}
221
237
 
222
238
 
@@ -232,6 +248,9 @@ def consume_user_approval(
232
248
  pending = _load_pending(root)
233
249
  if pending is None or not isinstance(prompt, str):
234
250
  return {"matched": False, "approved": False}
251
+ if pending["format"] == "codearbiter.pending-user-approval/0.2.0":
252
+ import _sprintapprovallib
253
+ return _sprintapprovallib.consume(root, client, pending, prompt, host=host, session_id=session_id)
235
254
  parts = prompt.split(" ")
236
255
  if len(parts) != 3 or parts[0] != "approve" or parts[1] != pending["artifact_id"]:
237
256
  return {"matched": False, "approved": False}
@@ -250,7 +269,6 @@ def consume_user_approval(
250
269
  _ready(client, current)
251
270
 
252
271
  event = {
253
- "format": "codearbiter.workflow-event/0.1.0",
254
272
  "kind": "approval",
255
273
  "authority_kind": "user_workflow",
256
274
  "subject": {
@@ -264,20 +282,12 @@ def consume_user_approval(
264
282
  "payload": {},
265
283
  "source_text": prompt,
266
284
  }
267
- event_bytes = _canonical(event)
268
- digest = hashlib.sha256(event_bytes).hexdigest()
269
- source_rel = SOURCE_DIR / f"{digest}.json"
270
- source = root / source_rel
271
285
  try:
272
- _write_new(root, source_rel, event_bytes)
273
- except FileExistsError:
274
- if source.read_bytes() != event_bytes:
275
- raise ApprovalError("INVALID_AUTHORITY_SOURCE", "content-addressed source differs")
276
-
277
- captured = client.call(
278
- "capture",
279
- {"source_ref": source_rel.as_posix(), "source_sha256": digest},
280
- )
286
+ captured = _artifactauthoritylib.capture_user_prompt(
287
+ root, client, event, host=host, session_id=session_id,
288
+ )
289
+ except _artifactauthoritylib.AuthorityError as exc:
290
+ raise ApprovalError("INVALID_AUTHORITY_SOURCE", str(exc)) from exc
281
291
  receipt = captured.get("receipt")
282
292
  if not isinstance(receipt, str) or not receipt:
283
293
  raise ApprovalError("INVALID_RECEIPT", "capture returned no receipt")
@@ -285,7 +295,7 @@ def consume_user_approval(
285
295
  "approve",
286
296
  {
287
297
  "artifact_id": current["artifact_id"],
288
- "operation_id": f"host-user-approval-{digest[:24]}",
298
+ "operation_id": f"host-user-approval-{hashlib.sha256(_canonical(event)).hexdigest()[:24]}",
289
299
  "expected": {
290
300
  "revision": current["revision"],
291
301
  "model_sha256": current["model_sha256"],
@@ -294,11 +304,12 @@ def consume_user_approval(
294
304
  },
295
305
  )
296
306
  (root / PENDING).unlink()
307
+ _artifactpromptlib.unregister(root, "approval", current["artifact_id"])
297
308
  return {
298
309
  "matched": True,
299
310
  "approved": True,
300
311
  "artifact_id": current["artifact_id"],
301
- "authority_source": source_rel.as_posix(),
312
+ "authority_source": captured["authority_source"],
302
313
  "receipt": receipt,
303
314
  "revision": approved.get("revision"),
304
315
  }
@@ -306,17 +317,20 @@ def consume_user_approval(
306
317
 
307
318
  def consume_from_hook(*, root: str | Path, plugin_root: str | Path, prompt: str,
308
319
  host: str, session_id: str) -> str:
309
- if not (Path(root) / PENDING).exists():
310
- return ""
311
320
  try:
321
+ routed = _artifactpromptlib.resolve("approval", prompt)
322
+ root = routed or Path(root)
323
+ if not (Path(root) / PENDING).exists():
324
+ return ""
312
325
  client = _artifactlib.ArtifactClient(
313
326
  root, Path(plugin_root) / "helpers" / "artifacts"
314
327
  )
315
328
  result = consume_user_approval(
316
329
  root, client, prompt, host=host, session_id=session_id
317
330
  )
318
- except (ApprovalError, _artifactlib.ArtifactError, OSError) as exc:
319
- if isinstance(prompt, str) and prompt.startswith("approve "):
331
+ except (ApprovalError, _artifactlib.ArtifactError,
332
+ _artifactpromptlib.PromptRouteError, OSError) as exc:
333
+ if isinstance(prompt, str) and prompt.startswith(("approve ", "approve-sprint ")):
320
334
  return f"codeArbiter: approval capture failed: {exc}"
321
335
  return ""
322
336
  if not result.get("approved"):
@@ -336,6 +350,17 @@ def main(argv: list[str] | None = None) -> int:
336
350
  cancel = sub.add_parser("cancel")
337
351
  cancel.add_argument("--root", required=True)
338
352
  cancel.add_argument("--artifact-id", required=True)
353
+ pair = sub.add_parser("arm-sprint")
354
+ pair.add_argument("--root", required=True)
355
+ pair.add_argument("--spec-id", required=True)
356
+ pair.add_argument("--plan-id", required=True)
357
+ pair.add_argument("--delegate-methods", action="store_true")
358
+ resume = sub.add_parser("resume-sprint")
359
+ resume.add_argument("--root", required=True)
360
+ resume.add_argument("--plan-id", required=True)
361
+ smarts = sub.add_parser("smarts")
362
+ smarts.add_argument("--root", required=True)
363
+ smarts.add_argument("--request-file", required=True)
339
364
  args = parser.parse_args(argv)
340
365
  if args.command == "cancel":
341
366
  print(json.dumps(cancel_user_approval(args.root, args.artifact_id)))
@@ -343,13 +368,24 @@ def main(argv: list[str] | None = None) -> int:
343
368
  client = _artifactlib.ArtifactClient(
344
369
  args.root, _artifactlib.helper_installation(__file__)
345
370
  )
346
- print(json.dumps(arm_user_approval(args.root, client, args.artifact_id)))
371
+ if args.command == "smarts":
372
+ import _sprintapprovallib
373
+ print(json.dumps(_sprintapprovallib.apply_decision(client, args.request_file)))
374
+ return 0
375
+ if args.command in {"arm-sprint", "resume-sprint"}:
376
+ import _sprintapprovallib
377
+ result = (_sprintapprovallib.arm(args.root, client, args.spec_id, args.plan_id, delegate_methods=args.delegate_methods)
378
+ if args.command == "arm-sprint" else _sprintapprovallib.resume(args.root, client, args.plan_id))
379
+ else:
380
+ result = arm_user_approval(args.root, client, args.artifact_id)
381
+ print(json.dumps(result))
347
382
  return 0
348
383
 
349
384
 
350
385
  if __name__ == "__main__":
351
386
  try:
352
387
  raise SystemExit(main())
353
- except (ApprovalError, _artifactlib.ArtifactError) as exc:
388
+ except (ApprovalError, _artifactlib.ArtifactError,
389
+ _artifactpromptlib.PromptRouteError) as exc:
354
390
  sys.stderr.write(str(exc) + "\n")
355
391
  raise SystemExit(1)