@gonrocca/nodd 0.2.0 → 0.2.1
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/package.json +1 -1
- package/src/gates/request.ts +4 -1
- package/src/gates/track.test.ts +14 -0
- package/src/state.test.ts +14 -0
- package/src/state.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonrocca/nodd",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Non-negotiable Organic Driven Development — the ODD protocol as runtime mechanism for pi: blocking gates, observed evidence, and promotion to /forge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
package/src/gates/request.ts
CHANGED
|
@@ -11,7 +11,10 @@ export type GateRequest = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export function targetPath(request: GateRequest): string | null {
|
|
14
|
-
|
|
14
|
+
// pi's own write and edit tools accept either name and read `file_path ??
|
|
15
|
+
// path` (`write.js:99`, `edit.js:92`), and the write tool sends `file_path`.
|
|
16
|
+
// Reading only `path` left every path-scoped rule blind on the real tool.
|
|
17
|
+
const path = request.input?.file_path ?? request.input?.path;
|
|
15
18
|
return typeof path === "string" && path !== "" ? path : null;
|
|
16
19
|
}
|
|
17
20
|
|
package/src/gates/track.test.ts
CHANGED
|
@@ -47,6 +47,20 @@ test("mutating bash blocks while a test command does not", () => {
|
|
|
47
47
|
|
|
48
48
|
// A doc the model can rewrite is a doc the model can forge, and then evidence
|
|
49
49
|
// is prose again. This holds even when the doc exists and the route is inline.
|
|
50
|
+
test("file_path is read like path: pi's write tool sends either", () => {
|
|
51
|
+
// `write.js:99` and `edit.js:92` both accept `file_path ?? path`, and pi's
|
|
52
|
+
// own write tool sends `file_path`. Reading only `path` left every
|
|
53
|
+
// path-scoped rule blind on the real tool: the .nodd/ guard below let a
|
|
54
|
+
// direct write to the feature document through.
|
|
55
|
+
const decision = trackGate(
|
|
56
|
+
routed("tracked"),
|
|
57
|
+
{ toolName: "write", input: { file_path: "/repo/.nodd/demo/feature.md" } },
|
|
58
|
+
emptyPolicy(),
|
|
59
|
+
present,
|
|
60
|
+
);
|
|
61
|
+
assert.equal(decision.allow, false, "a write under .nodd/ is refused however pi names the argument");
|
|
62
|
+
});
|
|
63
|
+
|
|
50
64
|
test("a direct write to .nodd/** is always blocked, pointing at nodd_task", () => {
|
|
51
65
|
for (const state of [routed("tracked"), routed("inline"), emptyCommitted()]) {
|
|
52
66
|
for (const path of [".nodd/x/feature.md", ".nodd/demo/state.json", "/repo/.nodd/demo/feature.md"]) {
|
package/src/state.test.ts
CHANGED
|
@@ -53,6 +53,20 @@ test("a fixed sequence folds into an exact snapshot", () => {
|
|
|
53
53
|
});
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
test("a write naming file_path is recorded, like pi's own write tool sends it", () => {
|
|
57
|
+
// pi's write tool sends `file_path` (`write.js:99`); edit accepts both
|
|
58
|
+
// (`edit.js:92`). Reading only `path` left filesWritten empty on every real
|
|
59
|
+
// write — and filesWritten is what evidence times a checkoff against and what
|
|
60
|
+
// delegate counts, so both went blind against the actual tool.
|
|
61
|
+
const committed = foldAll(emptyCommitted(), [
|
|
62
|
+
obs({ toolName: "write", input: { file_path: "c.ts" } }),
|
|
63
|
+
obs({ toolName: "read", input: { file_path: "a.ts" } }),
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
assert.deepEqual([...committed.filesWritten.keys()], ["c.ts"]);
|
|
67
|
+
assert.deepEqual([...committed.filesRead], ["a.ts"]);
|
|
68
|
+
});
|
|
69
|
+
|
|
56
70
|
test("distinct-path counting: one file read six times is one file", () => {
|
|
57
71
|
const events = Array.from({ length: 6 }, () => obs({ toolName: "read", input: { path: "same.ts" } }));
|
|
58
72
|
const committed = foldAll(emptyCommitted(), events);
|
package/src/state.ts
CHANGED
|
@@ -117,7 +117,10 @@ export function fold(committed: Committed, obs: Observation): Committed {
|
|
|
117
117
|
declaration: committed.declaration,
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
// `file_path ?? path`: pi's write tool sends the first, edit accepts both
|
|
121
|
+
// (`write.js:99`, `edit.js:92`). Reading only `path` left filesWritten empty
|
|
122
|
+
// on every real write, which is what evidence and delegate count.
|
|
123
|
+
const path = str(obs.input.file_path) || str(obs.input.path);
|
|
121
124
|
if (READ_TOOLS.has(obs.toolName) && path) next.filesRead.add(path);
|
|
122
125
|
if (WRITE_TOOLS.has(obs.toolName) && path) next.filesWritten.set(path, { at: obs.at, seq: next.toolCalls });
|
|
123
126
|
|