@arbiterforge/ca-pi 0.21.4 → 0.21.6
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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/plugins/ca-pi/CHANGELOG.md +12 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-darwin-amd64 +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-darwin-arm64 +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-linux-amd64 +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-linux-arm64 +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-windows-amd64.exe +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-windows-arm64.exe +0 -0
- package/plugins/ca-pi/helpers/artifacts/release.json +36 -36
- package/plugins/ca-pi/hooks/_bashguardlib.py +151 -5
- package/plugins/ca-pi/hooks/_host.py +1 -1
- package/plugins/ca-pi/hooks/hostapi.py +1 -1
- package/plugins/ca-pi/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ and durable project context. You decide. codeArbiter enforces.
|
|
|
10
10
|
<img alt="Claude Code plugin" src="https://img.shields.io/badge/Claude_Code-plugin-d97757">
|
|
11
11
|
<img alt="Codex plugin" src="https://img.shields.io/badge/OpenAI_Codex-plugin-10a37f">
|
|
12
12
|
<img alt="Pi Feature Forge preview" src="https://img.shields.io/badge/ca--pi-Feature_Forge_preview-d97757">
|
|
13
|
-
<img alt="version 3.2.
|
|
13
|
+
<img alt="version 3.2.6" src="https://img.shields.io/badge/version-3.2.6-2b7489">
|
|
14
14
|
<img alt="core lanes" src="https://img.shields.io/badge/core_lanes-18-555">
|
|
15
15
|
<img alt="skills" src="https://img.shields.io/badge/skills-22-555">
|
|
16
16
|
<img alt="agents" src="https://img.shields.io/badge/agents-20-555">
|
package/package.json
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `ca-pi` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.21.6] - 2026-10-19
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Engine model-validation errors now name what the schema accepts. A closed-enum refusal lists the allowed values (for example, applicability `mode` accepts `"conditional"`, `"unconditional"`, `"invariant"` and `"not_applicable"`), and an unknown key lists the record's allowed fields. Before, both said only that the value was refused, so the accepted spelling had to be found in the schema files. The list stays within the 512-byte public diagnostic limit.
|
|
12
|
+
|
|
13
|
+
## [0.21.5] - 2026-10-19
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- The H-05 (audit logs), H-11 (ADRs) and H-18 (`CONTEXT.md`) shell guards blocked any interpreter command that merely named a protected path, so a read-only `python -c` read, an issue body citing the path, or an `ArtifactClient` request whose record text named an audit log was refused as a write. Their interpreter legs now block only when the command shows write intent after the interpreter (a write, delete, copy, move or in-place edit call, a non-read `open` mode, PowerShell content cmdlets, or dynamic execution such as `exec`, `eval` or `subprocess`), or when the interpreter is handed a script or module argument. H-18 also lets a `sed` without `-i` or a `w` command read `CONTEXT.md` (#997, #911).
|
|
18
|
+
|
|
7
19
|
## [0.21.4] - 2026-10-19
|
|
8
20
|
|
|
9
21
|
### Fixed
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,43 +6,43 @@
|
|
|
6
6
|
"binaries": {
|
|
7
7
|
"darwin/amd64": {
|
|
8
8
|
"file": "ca-artifact-darwin-amd64",
|
|
9
|
-
"sha256": "
|
|
9
|
+
"sha256": "9b380f89ce9140ac36959dfd605da990868b2f5cacee9eb28354b27ea64f8b00",
|
|
10
10
|
"native_tested": true
|
|
11
11
|
},
|
|
12
12
|
"darwin/arm64": {
|
|
13
13
|
"file": "ca-artifact-darwin-arm64",
|
|
14
|
-
"sha256": "
|
|
14
|
+
"sha256": "d03006ff6f415d20851241602de366222ee9ff55642829f5db45a7a46fb5ecd6",
|
|
15
15
|
"native_tested": true
|
|
16
16
|
},
|
|
17
17
|
"linux/amd64": {
|
|
18
18
|
"file": "ca-artifact-linux-amd64",
|
|
19
|
-
"sha256": "
|
|
19
|
+
"sha256": "884d70bfa568a5661bacbeabc871af0c3507349efd0014df11fbd6da89a51c39",
|
|
20
20
|
"native_tested": true
|
|
21
21
|
},
|
|
22
22
|
"linux/arm64": {
|
|
23
23
|
"file": "ca-artifact-linux-arm64",
|
|
24
|
-
"sha256": "
|
|
24
|
+
"sha256": "148e62c1d70b94768c0e77acc4c5f96e7992aaa9aa1ff3dbe4d9eca82ee0d8a4",
|
|
25
25
|
"native_tested": true
|
|
26
26
|
},
|
|
27
27
|
"windows/amd64": {
|
|
28
28
|
"file": "ca-artifact-windows-amd64.exe",
|
|
29
|
-
"sha256": "
|
|
29
|
+
"sha256": "c9246accfa753c0aad42df2d22b1652c5bf63bcf2325d8c59e3c08b571ad5aab",
|
|
30
30
|
"native_tested": true
|
|
31
31
|
},
|
|
32
32
|
"windows/arm64": {
|
|
33
33
|
"file": "ca-artifact-windows-arm64.exe",
|
|
34
|
-
"sha256": "
|
|
34
|
+
"sha256": "1b8940f08206330146941bf295c21a1706d90b76e1e8a2abe2129792256eb64b",
|
|
35
35
|
"native_tested": true
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"context_qualification": {
|
|
39
39
|
"darwin/amd64": {
|
|
40
|
-
"source_commit": "
|
|
40
|
+
"source_commit": "849a3cf6fa6f4fdf3256cd3095c92499e9c4ec77",
|
|
41
41
|
"workflow": ".github/workflows/ci.yml",
|
|
42
|
-
"run_id": "
|
|
43
|
-
"binary_sha256": "
|
|
44
|
-
"observation_sha256": "
|
|
45
|
-
"module_sha256": "
|
|
42
|
+
"run_id": "39345028289",
|
|
43
|
+
"binary_sha256": "9b380f89ce9140ac36959dfd605da990868b2f5cacee9eb28354b27ea64f8b00",
|
|
44
|
+
"observation_sha256": "4d3a285100e275215e1495d3fd0b42dbffac2f9c37047bb6bf7925dd5bec063e",
|
|
45
|
+
"module_sha256": "3d78fb86150fbd4180be0dbf9b2b5fc6dc9ae19a6f371ef3e4da1fa1c68dd8fa",
|
|
46
46
|
"native_cases": [
|
|
47
47
|
"TestContextBoundedRender",
|
|
48
48
|
"TestContextDocumentContract",
|
|
@@ -53,12 +53,12 @@
|
|
|
53
53
|
]
|
|
54
54
|
},
|
|
55
55
|
"darwin/arm64": {
|
|
56
|
-
"source_commit": "
|
|
56
|
+
"source_commit": "849a3cf6fa6f4fdf3256cd3095c92499e9c4ec77",
|
|
57
57
|
"workflow": ".github/workflows/ci.yml",
|
|
58
|
-
"run_id": "
|
|
59
|
-
"binary_sha256": "
|
|
60
|
-
"observation_sha256": "
|
|
61
|
-
"module_sha256": "
|
|
58
|
+
"run_id": "39345028289",
|
|
59
|
+
"binary_sha256": "d03006ff6f415d20851241602de366222ee9ff55642829f5db45a7a46fb5ecd6",
|
|
60
|
+
"observation_sha256": "08e16937dabe723df59f48799d8e56dfb4afb104d4a2c536448308b85b71355a",
|
|
61
|
+
"module_sha256": "3d78fb86150fbd4180be0dbf9b2b5fc6dc9ae19a6f371ef3e4da1fa1c68dd8fa",
|
|
62
62
|
"native_cases": [
|
|
63
63
|
"TestContextBoundedRender",
|
|
64
64
|
"TestContextDocumentContract",
|
|
@@ -69,12 +69,12 @@
|
|
|
69
69
|
]
|
|
70
70
|
},
|
|
71
71
|
"linux/amd64": {
|
|
72
|
-
"source_commit": "
|
|
72
|
+
"source_commit": "849a3cf6fa6f4fdf3256cd3095c92499e9c4ec77",
|
|
73
73
|
"workflow": ".github/workflows/ci.yml",
|
|
74
|
-
"run_id": "
|
|
75
|
-
"binary_sha256": "
|
|
76
|
-
"observation_sha256": "
|
|
77
|
-
"module_sha256": "
|
|
74
|
+
"run_id": "39345028289",
|
|
75
|
+
"binary_sha256": "884d70bfa568a5661bacbeabc871af0c3507349efd0014df11fbd6da89a51c39",
|
|
76
|
+
"observation_sha256": "001fd9420f806f17155df87f84e1e60f7342e37936001a4badea473f25a89c64",
|
|
77
|
+
"module_sha256": "3d78fb86150fbd4180be0dbf9b2b5fc6dc9ae19a6f371ef3e4da1fa1c68dd8fa",
|
|
78
78
|
"native_cases": [
|
|
79
79
|
"TestContextBoundedRender",
|
|
80
80
|
"TestContextDocumentContract",
|
|
@@ -85,12 +85,12 @@
|
|
|
85
85
|
]
|
|
86
86
|
},
|
|
87
87
|
"linux/arm64": {
|
|
88
|
-
"source_commit": "
|
|
88
|
+
"source_commit": "849a3cf6fa6f4fdf3256cd3095c92499e9c4ec77",
|
|
89
89
|
"workflow": ".github/workflows/ci.yml",
|
|
90
|
-
"run_id": "
|
|
91
|
-
"binary_sha256": "
|
|
92
|
-
"observation_sha256": "
|
|
93
|
-
"module_sha256": "
|
|
90
|
+
"run_id": "39345028289",
|
|
91
|
+
"binary_sha256": "148e62c1d70b94768c0e77acc4c5f96e7992aaa9aa1ff3dbe4d9eca82ee0d8a4",
|
|
92
|
+
"observation_sha256": "891a448936d6bf5132665a7fe3aa15a2613af0e0f68c47549e60d2cd25ed4308",
|
|
93
|
+
"module_sha256": "3d78fb86150fbd4180be0dbf9b2b5fc6dc9ae19a6f371ef3e4da1fa1c68dd8fa",
|
|
94
94
|
"native_cases": [
|
|
95
95
|
"TestContextBoundedRender",
|
|
96
96
|
"TestContextDocumentContract",
|
|
@@ -101,12 +101,12 @@
|
|
|
101
101
|
]
|
|
102
102
|
},
|
|
103
103
|
"windows/amd64": {
|
|
104
|
-
"source_commit": "
|
|
104
|
+
"source_commit": "849a3cf6fa6f4fdf3256cd3095c92499e9c4ec77",
|
|
105
105
|
"workflow": ".github/workflows/ci.yml",
|
|
106
|
-
"run_id": "
|
|
107
|
-
"binary_sha256": "
|
|
108
|
-
"observation_sha256": "
|
|
109
|
-
"module_sha256": "
|
|
106
|
+
"run_id": "39345028289",
|
|
107
|
+
"binary_sha256": "c9246accfa753c0aad42df2d22b1652c5bf63bcf2325d8c59e3c08b571ad5aab",
|
|
108
|
+
"observation_sha256": "56933f17c134e122f78e350e7edc87460fd4a44c283a0b2724c3954588da0331",
|
|
109
|
+
"module_sha256": "3d78fb86150fbd4180be0dbf9b2b5fc6dc9ae19a6f371ef3e4da1fa1c68dd8fa",
|
|
110
110
|
"native_cases": [
|
|
111
111
|
"TestContextBoundedRender",
|
|
112
112
|
"TestContextDocumentContract",
|
|
@@ -117,12 +117,12 @@
|
|
|
117
117
|
]
|
|
118
118
|
},
|
|
119
119
|
"windows/arm64": {
|
|
120
|
-
"source_commit": "
|
|
120
|
+
"source_commit": "849a3cf6fa6f4fdf3256cd3095c92499e9c4ec77",
|
|
121
121
|
"workflow": ".github/workflows/ci.yml",
|
|
122
|
-
"run_id": "
|
|
123
|
-
"binary_sha256": "
|
|
124
|
-
"observation_sha256": "
|
|
125
|
-
"module_sha256": "
|
|
122
|
+
"run_id": "39345028289",
|
|
123
|
+
"binary_sha256": "1b8940f08206330146941bf295c21a1706d90b76e1e8a2abe2129792256eb64b",
|
|
124
|
+
"observation_sha256": "95dcea9f3b5807f164de47447a43b003a614264da7ef40d0ec76c21a57dfb8b1",
|
|
125
|
+
"module_sha256": "3d78fb86150fbd4180be0dbf9b2b5fc6dc9ae19a6f371ef3e4da1fa1c68dd8fa",
|
|
126
126
|
"native_cases": [
|
|
127
127
|
"TestContextBoundedRender",
|
|
128
128
|
"TestContextDocumentContract",
|
|
@@ -516,6 +516,147 @@ CONTEXT_INTERP_RE = re.compile(
|
|
|
516
516
|
_INTERP_EXECUTABLE + r"[\s\S]*" + CONTEXT_MD, re.I,
|
|
517
517
|
)
|
|
518
518
|
|
|
519
|
+
# #997/#911: the three legs above match an interpreter token followed ANYWHERE
|
|
520
|
+
# by a protected name, so a read-only `python -c "print(open(...).read())"` or
|
|
521
|
+
# an issue body that merely cites the path blocked like a write. They are now a
|
|
522
|
+
# pre-filter: `_interp_protected_write` blocks only when the command shows
|
|
523
|
+
# write intent after the interpreter, or hands the interpreter a script or
|
|
524
|
+
# module argument (script argv stays conservative: its writes are invisible
|
|
525
|
+
# here). Write intent is lexical and deliberately broad; dynamic execution
|
|
526
|
+
# (`exec`, `eval`, `system`, `subprocess`) counts, because it hides the write.
|
|
527
|
+
# The guard stays defense in depth behind the Write/Edit tool guards; a path
|
|
528
|
+
# assembled at runtime (`'.codearbiter/' + 'CONTEXT.md'`) was never seen.
|
|
529
|
+
_PS_WRITE_ALIASES = (r"sc|ac|ni|ri|mi|cpi|rni|ren|clc|del|erase|rd|rmdir|rm|mv|cp"
|
|
530
|
+
r"|move|copy|tee")
|
|
531
|
+
INTERP_WRITE_INTENT_RE = re.compile(
|
|
532
|
+
# Calls whose name carries a write/destroy verb: write_text, writeFileSync,
|
|
533
|
+
# WriteAllText, fwrite, appendFile, os.remove, unlink, shutil.move/copy,
|
|
534
|
+
# rmtree, Path.touch/chmod, os.truncate, rename/replace, File.Delete.
|
|
535
|
+
r"[\w$]*(?:write|append|unlink|remove|rename|replace|truncate|rmtree|rmdir"
|
|
536
|
+
r"|delete|move|copy|symlink|touch|chmod|chown|mkfifo|put_contents|fputs)"
|
|
537
|
+
r"[\w$]*\s*\("
|
|
538
|
+
r"|(?:\b|\.)(?:rm|cp|link)(?:Sync)?\s*\("
|
|
539
|
+
r"|\w*Writer\b|\w*WriteStream\b|\bFileStream\b|\bFileUtils\b"
|
|
540
|
+
r"|::\s*(?:Create\w*|Open(?:Write)?)\s*\("
|
|
541
|
+
# open() with a second positional argument that is not a literal read
|
|
542
|
+
# mode (a write mode, a flag, or a variable), or a non-read mode= keyword.
|
|
543
|
+
r"|\b\w*open(?:Sync)?\s*\(\s*[^,()]+(?:\([^()]*\))?[^,()]*,"
|
|
544
|
+
r"(?!\s*['\"](?:[rbtU]*|<)['\"]\s*[,)]|\s*[A-Za-z_]+\s*=)"
|
|
545
|
+
r"|\bmode\s*=\s*(?!['\"][rbtU]*['\"])"
|
|
546
|
+
r"|\b(?:Set-Content|Add-Content|Out-File|Clear-Content|Remove-Item|Move-Item"
|
|
547
|
+
r"|Copy-Item|Rename-Item|New-Item|Tee-Object|Invoke-Expression|Start-Process"
|
|
548
|
+
r"|Invoke-Command)\b"
|
|
549
|
+
r"|(?:^|[;&|({\n\"'`])\s*(?:" + _PS_WRITE_ALIASES + r")\s"
|
|
550
|
+
r"|\b(?:exec\w*|eval|system|popen|spawn\w*|check_call|check_output|getattr"
|
|
551
|
+
r"|setattr|__import__|compile)\s*\("
|
|
552
|
+
r"|\bsubprocess\b|\bmethodcaller\b|\bDeno\.(?:run|Command)\b|\.Invoke\w*\s*\("
|
|
553
|
+
# Reflection hides the called name: dunders (`os.__dict__['remove']`),
|
|
554
|
+
# vars()/globals(), and a computed member call (`fs['write' + x](...)`).
|
|
555
|
+
r"|__\w+__|\b(?:vars|globals|locals)\s*\(|\]\s*\("
|
|
556
|
+
# A write-mode literal passed or assigned anywhere: `o = open; o(p, 'w')`.
|
|
557
|
+
r"|(?<=[,(=\s])['\"][rbtU]*[wax+][rbtUwax+]*['\"](?=\s*[,);\]])",
|
|
558
|
+
re.I,
|
|
559
|
+
)
|
|
560
|
+
_INTERP_ARG_RE = re.compile(r"\s+([^\s|;&]+)")
|
|
561
|
+
# Words that precede a command without ending a shell word list.
|
|
562
|
+
_COMMAND_PREFIXES = frozenset({"env", "sudo", "call", "exec", "nohup", "time",
|
|
563
|
+
"command", "xargs", "then", "do", "else", "&"})
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def _interp_operand(cmd, end):
|
|
567
|
+
"""Classify what an interpreter token ending at `end` is handed:
|
|
568
|
+
'inline' (an inline-code switch or a heredoc), 'stdin' (`-`, code that
|
|
569
|
+
may arrive from a pipe), 'script' (a positional argument or `<` input
|
|
570
|
+
shaped like a path), 'word' (any other positional argument, such as a
|
|
571
|
+
`-m` module or a dotless script name) or 'bare' (nothing before the
|
|
572
|
+
command ends)."""
|
|
573
|
+
rest = cmd[end:]
|
|
574
|
+
suffix = re.match(r"[\w.-]*[\"'`]?", rest)
|
|
575
|
+
pos = suffix.end()
|
|
576
|
+
while True:
|
|
577
|
+
m = _INTERP_ARG_RE.match(rest, pos)
|
|
578
|
+
if not m:
|
|
579
|
+
return "bare"
|
|
580
|
+
token = m.group(1).rstrip("\"'`")
|
|
581
|
+
inline = re.match(_INTERP_INLINE_CODE, token, re.I)
|
|
582
|
+
if inline and inline.end() == len(token):
|
|
583
|
+
return "inline"
|
|
584
|
+
if token.startswith("<<"):
|
|
585
|
+
return "inline"
|
|
586
|
+
if token == "-":
|
|
587
|
+
return "stdin"
|
|
588
|
+
if not token.startswith("-"):
|
|
589
|
+
return "script" if re.search(r"[./\\]", token) else "word"
|
|
590
|
+
pos = m.end()
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def _at_command_position(cmd, start):
|
|
594
|
+
"""True when the interpreter token at `start` sits where a command starts,
|
|
595
|
+
rather than inside prose such as "each node in the graph"."""
|
|
596
|
+
before = re.sub(r"[^\s;&|()`\"'{}]*$", "", cmd[:start]).rstrip()
|
|
597
|
+
if not before or before[-1] in ";&|({\n`\"'" or before.endswith("$("):
|
|
598
|
+
return True
|
|
599
|
+
word = before.split()[-1]
|
|
600
|
+
return word.lower() in _COMMAND_PREFIXES or "=" in word
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def _interp_protected_write(cmd, interp_re, name_re):
|
|
604
|
+
"""True when an interpreter invocation can write the protected `name_re`
|
|
605
|
+
target: `interp_re` (token then name) matched, and after some interpreter
|
|
606
|
+
token that precedes the name either write intent appears, or the code it
|
|
607
|
+
runs is not visible on the command line (a script, module or file, piped
|
|
608
|
+
stdin, or a dotless argument in command position)."""
|
|
609
|
+
if not interp_re.search(cmd):
|
|
610
|
+
return False
|
|
611
|
+
names = [m.start() for m in name_re.finditer(cmd)]
|
|
612
|
+
for m in re.finditer(_INTERP_EXECUTABLE, cmd, re.I):
|
|
613
|
+
if not any(n >= m.end() for n in names):
|
|
614
|
+
continue
|
|
615
|
+
# Perl and Ruby calls need no parentheses (`open F, ">", p`,
|
|
616
|
+
# `File.write p, ""`), which the lexical intent scan cannot see, so
|
|
617
|
+
# they keep the conservative pre-#997 behavior.
|
|
618
|
+
if m.group(1).lower() in ("perl", "ruby"):
|
|
619
|
+
return True
|
|
620
|
+
if INTERP_WRITE_INTENT_RE.search(cmd, m.start()):
|
|
621
|
+
return True
|
|
622
|
+
operand = _interp_operand(cmd, m.end())
|
|
623
|
+
if operand == "script":
|
|
624
|
+
return True
|
|
625
|
+
if operand == "stdin" and "<<" not in cmd[m.end():]:
|
|
626
|
+
return True
|
|
627
|
+
if operand in ("word", "bare") and _at_command_position(cmd, m.start()):
|
|
628
|
+
return True
|
|
629
|
+
return False
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
LOG_NAME_RE = re.compile(LOG_NAMES, re.I)
|
|
633
|
+
DECISIONS_NAME_RE = re.compile(DECISIONS, re.I)
|
|
634
|
+
CONTEXT_NAME_RE = re.compile(CONTEXT_MD, re.I)
|
|
635
|
+
|
|
636
|
+
# #997: `sed` writes a file only in place (`-i`, `--in-place`) or through a
|
|
637
|
+
# `w`/`W` script command; `sed -n 1,20p .codearbiter/CONTEXT.md` is a read.
|
|
638
|
+
_SED_WRITES_RE = re.compile(
|
|
639
|
+
r"(?<![\w-])-[a-zA-Z]*i|--in-place|(?:^|[\s'\";{}/\d$,])[wW]\s+\S")
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def _context_shell_write(cmd):
|
|
643
|
+
"""CONTEXT_WRITE_RE, except a `sed` invocation that cannot write."""
|
|
644
|
+
text = cmd
|
|
645
|
+
while True:
|
|
646
|
+
m = CONTEXT_WRITE_RE.search(text)
|
|
647
|
+
if not m:
|
|
648
|
+
return False
|
|
649
|
+
if m.group(1).lower() != "sed":
|
|
650
|
+
return True
|
|
651
|
+
# GNU sed accepts options after operands (`sed s/a/b/ FILE -i`), so
|
|
652
|
+
# scan the whole command segment, not only the text before the path.
|
|
653
|
+
segment = re.match(r"[^|;&]*", text[m.end(1):]).group(0)
|
|
654
|
+
if _SED_WRITES_RE.search(segment):
|
|
655
|
+
return True
|
|
656
|
+
# Blank this read-only `sed` and rescan, so another verb later in the
|
|
657
|
+
# same segment is still seen.
|
|
658
|
+
text = text[:m.start(1)] + " " + text[m.end(1):]
|
|
659
|
+
|
|
519
660
|
# H-22's shell flank: the protected-state registry (B1/#564) — Write/Edit are
|
|
520
661
|
# guarded via classify_protected's "state" class (pre-write.py/pre-edit.py,
|
|
521
662
|
# T-05a/T-06/T-07); this closes the shell flank the SAME way the four
|
|
@@ -1430,7 +1571,8 @@ def _check_h05_audit_log(cmd):
|
|
|
1430
1571
|
if enrolled_name and (
|
|
1431
1572
|
LOG_TRUNC_RE.search(cmd) or LOG_DESTROY_RE.search(cmd)
|
|
1432
1573
|
or LOG_GIT_RESTORE_RE.search(cmd)
|
|
1433
|
-
or
|
|
1574
|
+
or _interp_protected_write(cmd, LOG_INTERP_RE, LOG_NAME_RE)
|
|
1575
|
+
and not _is_direct_taskwrite(cmd)):
|
|
1434
1576
|
block("H-05", "The .codearbiter audit logs (overrides.log, triage.log, sprint-log.md, "
|
|
1435
1577
|
"gate-events.log, decisions/decision-log.md) are append-only "
|
|
1436
1578
|
"(ORCHESTRATOR §7). Truncating, overwriting, or deleting the audit "
|
|
@@ -1457,7 +1599,7 @@ def _check_h11_decisions(cmd):
|
|
|
1457
1599
|
cmd = DECISION_LOG_SHELL_RE.sub(" ", cmd)
|
|
1458
1600
|
cmd = ADR_LIFECYCLE_LOG_SHELL_RE.sub(" ", cmd)
|
|
1459
1601
|
if (DECISIONS_REDIRECT_RE.search(cmd) or DECISIONS_WRITE_RE.search(cmd)
|
|
1460
|
-
or
|
|
1602
|
+
or _interp_protected_write(cmd, DECISIONS_INTERP_RE, DECISIONS_NAME_RE)):
|
|
1461
1603
|
block("H-11", "ADR files under .codearbiter/decisions/ are authored only via "
|
|
1462
1604
|
"/adr and are immutable history (ORCHESTRATOR §6) — shell writes, "
|
|
1463
1605
|
"edits, and deletions there are prohibited.")
|
|
@@ -1469,9 +1611,13 @@ def _check_h18_context_md(cmd):
|
|
|
1469
1611
|
Write tool, so nothing legitimate is blocked. Reads pass.
|
|
1470
1612
|
|
|
1471
1613
|
#574: CONTEXT_INTERP_RE closes the interpreter-one-liner flank the same
|
|
1472
|
-
way LOG_INTERP_RE/DECISIONS_INTERP_RE do for H-05/H-11.
|
|
1473
|
-
|
|
1474
|
-
|
|
1614
|
+
way LOG_INTERP_RE/DECISIONS_INTERP_RE do for H-05/H-11.
|
|
1615
|
+
|
|
1616
|
+
#997: reads pass on both flanks. `sed` without in-place or a `w` command
|
|
1617
|
+
is a read, and an interpreter needs write intent or a script argument
|
|
1618
|
+
(`_interp_protected_write`)."""
|
|
1619
|
+
if (CONTEXT_REDIRECT_RE.search(cmd) or _context_shell_write(cmd)
|
|
1620
|
+
or _interp_protected_write(cmd, CONTEXT_INTERP_RE, CONTEXT_NAME_RE)):
|
|
1475
1621
|
block("H-18", ".codearbiter/CONTEXT.md is the activation switch every enforcement "
|
|
1476
1622
|
"hook reads (#159) — shell rewrites, edits, or deletions that could flip "
|
|
1477
1623
|
"`arbiter: enabled` off or corrupt its frontmatter are prohibited. Edit it "
|
|
@@ -11,7 +11,7 @@ import hostapi # noqa: E402
|
|
|
11
11
|
class PiHost(hostapi.Host):
|
|
12
12
|
name = "pi"
|
|
13
13
|
adapter_name = "@arbiterforge/ca-pi"
|
|
14
|
-
adapter_version = "0.21.
|
|
14
|
+
adapter_version = "0.21.6"
|
|
15
15
|
update_target = "ca-pi"
|
|
16
16
|
update_tag_prefix = "ca-pi-v"
|
|
17
17
|
update_command = "pi update npm:@arbiterforge/ca-pi"
|
|
@@ -313,7 +313,7 @@ class Host:
|
|
|
313
313
|
|
|
314
314
|
name = "claude"
|
|
315
315
|
adapter_name = "ca"
|
|
316
|
-
adapter_version = "3.2.
|
|
316
|
+
adapter_version = "3.2.6"
|
|
317
317
|
|
|
318
318
|
# Update-notifier descriptor. Each independently versioned host overrides
|
|
319
319
|
# these three values in its per-plugin _host.py. Keeping the target,
|