@mjasnikovs/pi-task 0.40.29 → 0.40.30

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.
@@ -140,7 +140,7 @@ export interface EcosystemProfile {
140
140
  * retrieval budget for text the reader already has, and neither is
141
141
  * separable downstream — same identifiers, same package, same version.
142
142
  */
143
- selectFiles?: (files: readonly string[]) => string[];
143
+ selectFiles?: (files: readonly string[], pkg: ResolvedPackage) => string[];
144
144
  /** What this ecosystem's packages ship, for a "there is nothing to read" answer. */
145
145
  surfaceLabel: string;
146
146
  /**
@@ -24,7 +24,7 @@ import { dropParallelDeclarations } from './docs-index.js';
24
24
  import { npmVersionLookup } from './npm-version.js';
25
25
  import { resolveCrate, cratesLatest, crateTarballUrl, crateOf, isValidCrateName, isRustFile, lockedVersion, rustSurface, cargoProjectName, childDirs, lockedDeps, manifestCrates, cargoExportGap, cargoContentFingerprint, cargoSupplementCandidates, CARGO_DECL_SPLIT_RE, CARGO_MEMBER_SPLIT_RE } from './eco-cargo.js';
26
26
  import { resolveHackage, hackageLatest, hackageVersion, hackageTarballUrl, hackageExtractDir, hackageProjectName, supplementCandidates, hackageExportGap, hackageContentFingerprint, findCabalTarball, cachedVersions, resolvedVersions, manifestPackages, isValidHackageName, isHaskellFile, haskellSurface, HACKAGE_DECL_SPLIT_RE, HACKAGE_SKIP_DIRS, HACKAGE_MEMBER_SPLIT_RE } from './eco-hackage.js';
27
- import { detectGo, isValidImportPath, resolveGoPackage, acquireGoModule, goDeclaredVersion, goLatest, goProjectName, goDeclaredDeps, goManifestDeps, isGoFile, selectBuildVariants, defaultGoModCache, goContentFingerprintParts } from './eco-go.js';
27
+ import { detectGo, isValidImportPath, resolveGoPackage, acquireGoModule, goDeclaredVersion, goLatest, goProjectName, goDeclaredDeps, goManifestDeps, isGoFile, selectBuildVariants, selectOwnPackage, defaultGoModCache, goContentFingerprintParts } from './eco-go.js';
28
28
  import { goSurface, goContentFingerprint, GO_DECL_SPLIT_RE, GO_MEMBER_SPLIT_RE } from './go-surface.js';
29
29
  import { runChild } from '../shared/child-process.js';
30
30
  /**
@@ -503,7 +503,7 @@ const goProfile = {
503
503
  typeKeywords: ['type', 'struct', 'interface', 'func'],
504
504
  commentPrefix: '//',
505
505
  skipDirs: ['testdata', 'examples', 'vendor', 'internal', '.git'],
506
- selectFiles: selectBuildVariants,
506
+ selectFiles: (files, pkg) => selectBuildVariants(selectOwnPackage(files, pkg.root, pkg.name, pkg.version)),
507
507
  surfaceLabel: '.go source or README',
508
508
  packageSubject: 'a Go package',
509
509
  projectGlobs: ['*.go'],
@@ -181,7 +181,7 @@ function collectFiles(pkg, profile) {
181
181
  const walked = walkSurface(pkg.root, profile);
182
182
  const surface = dropDeadMajors(walked, pkg.root, pkg.version);
183
183
  return {
184
- surface: profile.selectFiles ? profile.selectFiles(surface) : surface,
184
+ surface: profile.selectFiles ? profile.selectFiles(surface, pkg) : surface,
185
185
  readme: pkg.readme
186
186
  };
187
187
  }
@@ -174,6 +174,38 @@ export declare function acquireGoModule(importPath: string, pinned: string | nul
174
174
  * with no tags: a bare negation holds, a bare tag does not.
175
175
  */
176
176
  export declare function selectBuildVariants(files: readonly string[]): string[];
177
+ /**
178
+ * Keep the package the caller asked for, and only the subpackages it can reach.
179
+ *
180
+ * A Go subdirectory is a DIFFERENT importable package — `zapcore` is not reachable
181
+ * as `zap.X` and `ginS` is not `gin` — so walking a module's whole tree files every
182
+ * one of them under the parent's name and its version banner. Measured on re-run 9's
183
+ * cache: 71% of `encoding/json`'s chunks, 54% of zap's and 34% of gin's belonged to
184
+ * a package nobody asked about, and it reached retrieval — a question about
185
+ * registering a gin route came back with 10 `ginS` chunks of 51, in a corpus already
186
+ * spending 19,941 of its 24,000 bytes.
187
+ *
188
+ * The gate is the ROOT'S OWN IMPORTS, closed over: zap's `Field` is
189
+ * `= zapcore.Field`, so dropping `zapcore` would break the alias hop that defect 3
190
+ * exists to serve, and zapcore in turn reaches `buffer`. What that keeps is what a
191
+ * caller of this package can actually be handed; `zapgrpc`, `zaptest`, `ginS`,
192
+ * `httputil` and `net/http/pprof` are reachable only by importing them directly,
193
+ * which the tool already resolves on its own — `github.com/gin-gonic/gin/binding`
194
+ * asked for by path reads 0% foreign.
195
+ *
196
+ * A mismatching top-level `vN/` goes whatever the imports say. `encoding/json` is
197
+ * built ON `encoding/json/v2` and imports it, but v2 is a different major of the
198
+ * same API with the same identifiers — `Marshal`, `Unmarshal` — and half the
199
+ * retrieval for the commonest decode question came back from it under a
200
+ * `Per encoding/json@go1.25.14` header. That is `dropDeadMajors`' case, which
201
+ * cannot fire here because it reads the major with `/^(\d+)\./` and Go spells its
202
+ * versions `v1.12.0` and `go1.25.14`.
203
+ *
204
+ * A module whose root holds no Go files is not a package at all — the aws-sdk shape
205
+ * — and keeps everything, for the same reason `dropDeadMajors` keeps a package whose
206
+ * whole surface lives under one `vN/`.
207
+ */
208
+ export declare function selectOwnPackage(files: readonly string[], root: string, name: string, version: string): string[];
177
209
  /**
178
210
  * Everything below `goSurface` and the file-selection rule that feeds it, by
179
211
  * source, so a fix to either re-indexes rather than being masked by a cache hit.
@@ -650,10 +650,105 @@ function holdsByDefault(src) {
650
650
  return true;
651
651
  return expr.split(/\s*&&\s*/).every(term => term.startsWith('!'));
652
652
  }
653
+ /** The import paths a Go file's import block names. */
654
+ function importsOf(src) {
655
+ const out = [];
656
+ for (const m of src.matchAll(/"([^"\n]+)"/g)) {
657
+ if (/^[a-zA-Z0-9_.-]+(?:\/[a-zA-Z0-9_.~-]+)*$/.test(m[1]))
658
+ out.push(m[1]);
659
+ }
660
+ return out;
661
+ }
662
+ /**
663
+ * The package's own major, as Go spells it.
664
+ *
665
+ * v2 and up put the major in the module PATH, and the version agrees with it, so
666
+ * either reads the same answer. The standard library has neither and is 1: it is
667
+ * `go1.25.14`, and `encoding/json/v2` is a separate experimental package rather
668
+ * than a newer major of this one.
669
+ */
670
+ function goMajor(name, version) {
671
+ const inPath = /\/v(\d+)$/.exec(name);
672
+ if (inPath)
673
+ return Number(inPath[1]);
674
+ const inVersion = /^(?:v|go)?(\d+)\./.exec(version);
675
+ return inVersion ? Number(inVersion[1]) : 1;
676
+ }
677
+ /**
678
+ * Keep the package the caller asked for, and only the subpackages it can reach.
679
+ *
680
+ * A Go subdirectory is a DIFFERENT importable package — `zapcore` is not reachable
681
+ * as `zap.X` and `ginS` is not `gin` — so walking a module's whole tree files every
682
+ * one of them under the parent's name and its version banner. Measured on re-run 9's
683
+ * cache: 71% of `encoding/json`'s chunks, 54% of zap's and 34% of gin's belonged to
684
+ * a package nobody asked about, and it reached retrieval — a question about
685
+ * registering a gin route came back with 10 `ginS` chunks of 51, in a corpus already
686
+ * spending 19,941 of its 24,000 bytes.
687
+ *
688
+ * The gate is the ROOT'S OWN IMPORTS, closed over: zap's `Field` is
689
+ * `= zapcore.Field`, so dropping `zapcore` would break the alias hop that defect 3
690
+ * exists to serve, and zapcore in turn reaches `buffer`. What that keeps is what a
691
+ * caller of this package can actually be handed; `zapgrpc`, `zaptest`, `ginS`,
692
+ * `httputil` and `net/http/pprof` are reachable only by importing them directly,
693
+ * which the tool already resolves on its own — `github.com/gin-gonic/gin/binding`
694
+ * asked for by path reads 0% foreign.
695
+ *
696
+ * A mismatching top-level `vN/` goes whatever the imports say. `encoding/json` is
697
+ * built ON `encoding/json/v2` and imports it, but v2 is a different major of the
698
+ * same API with the same identifiers — `Marshal`, `Unmarshal` — and half the
699
+ * retrieval for the commonest decode question came back from it under a
700
+ * `Per encoding/json@go1.25.14` header. That is `dropDeadMajors`' case, which
701
+ * cannot fire here because it reads the major with `/^(\d+)\./` and Go spells its
702
+ * versions `v1.12.0` and `go1.25.14`.
703
+ *
704
+ * A module whose root holds no Go files is not a package at all — the aws-sdk shape
705
+ * — and keeps everything, for the same reason `dropDeadMajors` keeps a package whose
706
+ * whole surface lives under one `vN/`.
707
+ */
708
+ export function selectOwnPackage(files, root, name, version) {
709
+ const rel = (f) => path.relative(root, f).replace(/\\/g, '/');
710
+ const dirOf = (f) => {
711
+ const parts = rel(f).split('/');
712
+ return parts.slice(0, -1).join('/');
713
+ };
714
+ const byDir = new Map();
715
+ for (const f of files)
716
+ byDir.set(dirOf(f), [...(byDir.get(dirOf(f)) ?? []), f]);
717
+ if ((byDir.get('') ?? []).length === 0)
718
+ return [...files];
719
+ const major = goMajor(name, version);
720
+ const reachable = new Set(['']);
721
+ const queue = [''];
722
+ while (queue.length > 0) {
723
+ const dir = queue.shift();
724
+ for (const f of byDir.get(dir) ?? []) {
725
+ for (const imp of importsOf(safeRead(f) ?? '')) {
726
+ if (!imp.startsWith(`${name}/`))
727
+ continue;
728
+ const sub = imp.slice(name.length + 1);
729
+ const top = /^v(\d+)$/.exec(sub.split('/')[0]);
730
+ if (top && Number(top[1]) !== major)
731
+ continue;
732
+ if (reachable.has(sub) || !byDir.has(sub))
733
+ continue;
734
+ reachable.add(sub);
735
+ queue.push(sub);
736
+ }
737
+ }
738
+ }
739
+ return files.filter(f => reachable.has(dirOf(f)));
740
+ }
653
741
  /**
654
742
  * Everything below `goSurface` and the file-selection rule that feeds it, by
655
743
  * source, so a fix to either re-indexes rather than being masked by a cache hit.
656
744
  */
657
745
  export function goContentFingerprintParts() {
658
- return [String(selectBuildVariants), String(holdsByDefault), String(isWantedEntry)];
746
+ return [
747
+ String(selectBuildVariants),
748
+ String(holdsByDefault),
749
+ String(isWantedEntry),
750
+ String(selectOwnPackage),
751
+ String(goMajor),
752
+ String(importsOf)
753
+ ];
659
754
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasnikovs/pi-task",
3
- "version": "0.40.29",
3
+ "version": "0.40.30",
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",