@mjasnikovs/pi-task 0.40.12 → 0.40.14

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.
@@ -44,9 +44,18 @@ export function isExcerptInContent(excerpt, content) {
44
44
  return false;
45
45
  return normaliseWhitespace(content).includes(ne);
46
46
  }
47
- /** The child writes these to mark its own join, so they are not source text and
48
- * not evidence of invention either. */
47
+ /**
48
+ * Tokens that are not source text and not evidence of invention either: the
49
+ * child's own elision marks, and the provenance tag the PROMPT gave it.
50
+ *
51
+ * `buildExtractionPrompt` wraps the identity as `<package>aeson@2.2.5.1</package>`,
52
+ * and re-run 5 caught a child quoting that back inside an otherwise verbatim
53
+ * excerpt — the only absent word in 24 unverified excerpts across three runs. A
54
+ * complete lowercase element is the whole rule; `Vec<String>` does not start with
55
+ * `<` and `<T>` has no closing tag, so neither is excused.
56
+ */
49
57
  const ELISION = /^(?:\.{3}|\u2026|\/\/\s*\.{3})$/;
58
+ const PROMPT_TAG = /^<([a-z_]+)>[^<]*<\/\1>$/;
50
59
  /**
51
60
  * Cover the excerpt with the longest runs the source actually contains, greedily.
52
61
  *
@@ -67,7 +76,7 @@ function coverBySource(normalisedExcerpt, normalisedContent) {
67
76
  run++;
68
77
  }
69
78
  if (run === 0) {
70
- if (!ELISION.test(words[i]))
79
+ if (!ELISION.test(words[i]) && !PROMPT_TAG.test(words[i]))
71
80
  absent.push(words[i]);
72
81
  i++;
73
82
  }
@@ -22,14 +22,24 @@ export interface RetrieveOptions {
22
22
  contentBudget?: number;
23
23
  }
24
24
  /**
25
- * How many chunks a retrieval returns, per corpus. An npm package gets 8 and
26
- * project source gets 50.
25
+ * How many chunks a retrieval returns. Both corpora get 50, and that is measured.
27
26
  *
28
- * Nothing in this repo records WHY they differ. They are stated here, together,
29
- * so the divergence is at least visible and changing either is a
30
- * retrieval-policy change, not a tidy-up.
27
+ * The package limit was 8 and nothing recorded why it differed from project
28
+ * source's 50. Measured both ways, over every recorded query, on the two things
29
+ * that can be measured:
30
+ *
31
+ * retrieval — does a chunk DEFINE the queried symbol? 91/101 -> 97/101,
32
+ * monotone to 50 and flat at 100, p = 0.0703
33
+ * answers — does the extraction child answer at all? 67/94 -> 79/94,
34
+ * only-8 3, only-50 15, McNemar exact p = 0.0075
35
+ *
36
+ * Abstention on the replay corpus falls 29% to 16%. The cost is +70% of retrieved
37
+ * text, and `RETRIEVE_CONTENT_BUDGET` still bounds it — at 8 only 6 of 74 calls
38
+ * reached that budget, so two thirds of it was never spent.
39
+ *
40
+ * Changing either is still a retrieval-policy change, not a tidy-up.
31
41
  */
32
- export declare const PACKAGE_RETRIEVE_LIMIT = 8;
42
+ export declare const PACKAGE_RETRIEVE_LIMIT = 50;
33
43
  export declare const PROJECT_RETRIEVE_LIMIT = 50;
34
44
  /** Character budget for the assembled chunk text. The same for both corpora. */
35
45
  export declare const RETRIEVE_CONTENT_BUDGET = 24000;
@@ -1,12 +1,22 @@
1
1
  /**
2
- * How many chunks a retrieval returns, per corpus. An npm package gets 8 and
3
- * project source gets 50.
2
+ * How many chunks a retrieval returns. Both corpora get 50, and that is measured.
4
3
  *
5
- * Nothing in this repo records WHY they differ. They are stated here, together,
6
- * so the divergence is at least visible and changing either is a
7
- * retrieval-policy change, not a tidy-up.
4
+ * The package limit was 8 and nothing recorded why it differed from project
5
+ * source's 50. Measured both ways, over every recorded query, on the two things
6
+ * that can be measured:
7
+ *
8
+ * retrieval — does a chunk DEFINE the queried symbol? 91/101 -> 97/101,
9
+ * monotone to 50 and flat at 100, p = 0.0703
10
+ * answers — does the extraction child answer at all? 67/94 -> 79/94,
11
+ * only-8 3, only-50 15, McNemar exact p = 0.0075
12
+ *
13
+ * Abstention on the replay corpus falls 29% to 16%. The cost is +70% of retrieved
14
+ * text, and `RETRIEVE_CONTENT_BUDGET` still bounds it — at 8 only 6 of 74 calls
15
+ * reached that budget, so two thirds of it was never spent.
16
+ *
17
+ * Changing either is still a retrieval-policy change, not a tidy-up.
8
18
  */
9
- export const PACKAGE_RETRIEVE_LIMIT = 8;
19
+ export const PACKAGE_RETRIEVE_LIMIT = 50;
10
20
  export const PROJECT_RETRIEVE_LIMIT = 50;
11
21
  /** Character budget for the assembled chunk text. The same for both corpora. */
12
22
  export const RETRIEVE_CONTENT_BUDGET = 24_000;
@@ -210,6 +220,16 @@ function definitionChunk(cache, opts, name) {
210
220
  * declaration per chunk, so the short chunk carrying the name IS its declaration and
211
221
  * the long ones are the prose that merely mentions it.
212
222
  */
223
+ /** The chunk without the `// path` / `-- path` line the indexer prepends. */
224
+ function chunkBody(content) {
225
+ return content.replace(/^(?:\/\/|--)[^\n]*\n/, '');
226
+ }
227
+ /**
228
+ * A chunk that only declares a MODULE. `pub mod serve;` is shorter than the
229
+ * function it contains and wins smallest-first, while being neither the value nor
230
+ * the type this hop exists to find.
231
+ */
232
+ const MODULE_DECL_RE = /^(?:#\[[^\n]*\]\s*)*(?:pub(?:\s*\([^)]*\))?\s+|export\s+|declare\s+)*(?:mod|module|namespace)\s+[A-Za-z_][\w']*\s*;?\s*$/;
213
233
  function valueChunk(cache, opts, name) {
214
234
  const rows = cache.db
215
235
  .prepare(`SELECT file_path, kind, content, 0 AS rank FROM chunks
@@ -219,7 +239,14 @@ function valueChunk(cache, opts, name) {
219
239
  // LIKE has no word boundary, so `decodeFile` matches `decodeFileStrict` — the
220
240
  // wrong declaration, and the exact confusion these runs keep producing.
221
241
  const whole = new RegExp(`(?<![A-Za-z0-9_])${escapeRe(name)}(?![A-Za-z0-9_])`);
222
- const row = rows.find(r => whole.test(r.content));
242
+ // Over the BODY, never the `// path` line the indexer prepends. Every chunk
243
+ // from `src/serve/mod.rs` carries "serve" in that line, so smallest-first
244
+ // handed axum's `serve` a 50-byte `pub struct TapIo` from a neighbouring file
245
+ // — the name was in the header and nowhere else.
246
+ const row = rows.find(r => {
247
+ const text = chunkBody(r.content);
248
+ return whole.test(text) && !MODULE_DECL_RE.test(text);
249
+ });
223
250
  if (!row)
224
251
  return null;
225
252
  return { filePath: row.file_path, kind: row.kind, content: row.content, rank: row.rank };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasnikovs/pi-task",
3
- "version": "0.40.12",
3
+ "version": "0.40.14",
4
4
  "description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",