@danypops/papyrus 0.47.0 → 0.47.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.47.0",
3
+ "version": "0.47.1",
4
4
  "description": "Daemon-backed graph artifacts, evidence-bearing tasks, rules, skills, and native TUI workflows for Pi",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package"],
@@ -29,6 +29,8 @@ interface OperationSchemaNode {
29
29
  readonly properties?: Readonly<Record<string, OperationSchemaNode>>;
30
30
  readonly required?: readonly string[];
31
31
  readonly additionalProperties?: boolean | OperationSchemaNode;
32
+ /** A key not in `properties` is validated against the first pattern here whose RegExp matches it, instead of falling through to `additionalProperties` -- e.g. a free-form string-keyed map (tasks.create's checklist) uses `{"^.*$": entrySchema}` so a client-side JSON-Schema validator that reports `additionalProperties`-as-schema violations only as a generic top-level "must not have additional properties" (TypeBox's own real, confirmed behavior -- see vehicle-shell.ts's formatSchemaChildren for the matching tools_man rendering) instead descends into the real nested violation, matching an array's `items` precision. */
33
+ readonly patternProperties?: Readonly<Record<string, OperationSchemaNode>>;
32
34
  readonly items?: OperationSchemaNode;
33
35
  readonly minLength?: number;
34
36
  readonly maxLength?: number;
@@ -80,6 +82,12 @@ function validateSchemaValue(value: unknown, schema: OperationSchemaNode, path:
80
82
  }
81
83
  for (const key of Object.keys(record)) {
82
84
  if (key in (schema.properties ?? {})) continue;
85
+ const patternMatch = Object.entries(schema.patternProperties ?? {}).find(([pattern]) => new RegExp(pattern).test(key));
86
+ if (patternMatch) {
87
+ const issues = validateSchemaValue(record[key], patternMatch[1], [...path, key]);
88
+ if (issues.length > 0) return issues;
89
+ continue;
90
+ }
83
91
  if (schema.additionalProperties === false) return schemaIssue([...path, key], `${key} is not allowed`);
84
92
  if (typeof schema.additionalProperties === "object") {
85
93
  const issues = validateSchemaValue(record[key], schema.additionalProperties, [...path, key]);
@@ -114,31 +114,47 @@ const gateProp = {
114
114
  ],
115
115
  } as const;
116
116
 
117
+ /**
118
+ * `patternProperties: {"^.*$": entrySchema}` rather than `additionalProperties: entrySchema`,
119
+ * despite both meaning "every key maps to entrySchema" for a free-form string-keyed map:
120
+ * confirmed live (2026-08-09) that TypeBox's own Value.Errors -- the schema validator Pi's tool-
121
+ * calling harness runs client-side, before a call ever reaches this daemon -- reports an
122
+ * additionalProperties-as-schema violation only as a generic top-level "must not have additional
123
+ * properties", with zero descent into which nested field actually broke, while the structurally
124
+ * identical items-as-schema case (gates, proof arrays below) descends and reports the exact
125
+ * broken field. patternProperties does not have that limitation and gives the same precision as
126
+ * items. See handlers/shared.ts's OperationSchemaNode.patternProperties for the matching
127
+ * server-side runtime check, and vehicle-shell.ts's formatSchemaChildren for the matching
128
+ * tools_man rendering.
129
+ */
117
130
  const checklistProp = {
118
131
  type: "object",
119
132
  description: "Map from completion criterion text to one or more typed proof references. An empty map clears the checklist.",
120
- additionalProperties: {
121
- type: "object",
122
- properties: {
123
- proof: {
124
- type: "array",
125
- minItems: 1,
126
- items: {
127
- type: "object",
128
- description: "Accepted proof shape: {type, target, expect?}.",
129
- properties: {
130
- type: { type: "string", enum: PROOF_TYPES },
131
- target: { type: "string", minLength: 1 },
132
- expect: { type: "string" },
133
+ patternProperties: {
134
+ "^.*$": {
135
+ type: "object",
136
+ properties: {
137
+ proof: {
138
+ type: "array",
139
+ minItems: 1,
140
+ items: {
141
+ type: "object",
142
+ description: "Accepted proof shape: {type, target, expect?}.",
143
+ properties: {
144
+ type: { type: "string", enum: PROOF_TYPES },
145
+ target: { type: "string", minLength: 1 },
146
+ expect: { type: "string" },
147
+ },
148
+ required: ["type", "target"],
149
+ additionalProperties: false,
133
150
  },
134
- required: ["type", "target"],
135
- additionalProperties: false,
136
151
  },
137
152
  },
153
+ required: ["proof"],
154
+ additionalProperties: false,
138
155
  },
139
- required: ["proof"],
140
- additionalProperties: false,
141
156
  },
157
+ additionalProperties: false,
142
158
  examples: [
143
159
  {
144
160
  "tests pass": { proof: [{ type: "test", target: "bun test", expect: "0 failures" }] },