NORTHSET
Proof-of-Pass Receipt
Receipt ID M-147
ISSUE / WORKtree.delete() skipped on error path — wrap parse+extract in try/finally · PR #156
CONTRIBUTOR SELF-RUN — NOT MAINTAINER VERIFICATION
RUN→
01 / TECHNICAL RESULT
Technical result
declared commands passed
Recorded on the named code in the named execution environment. The evidence annex carries the exact scope.
- Upstream
- OPENmutable external state
- Attempts
- 3 totalcurrent
M-147· #3 - Cost record
- PARTIALunpriced
02 / RECORDED WORK SEQUENCE
Northset Proofline
Every task-bound attempt is shown in recorded order. Anatomy geometry follows each non-null elapsed duration’s share; labels retain exact recorded time.
- ATTEMPT 1
M-125FAILED ORACLEverification - ATTEMPT 2
M-135FAILED INFRA TERMINALinfrastructure - ATTEMPT 3
M-147READY
ATTEMPT 3 / EXECUTION ANATOMY
03 / ECONOMIC NARRATIVE
Economic identity
Observed sponsorship, authorization, scope, transfer, and outcome. No estimates or inferred value.
01 / MANDATE
Sponsored and authorized
The recorded mandate names both its sponsor and initiative, with authorization tied to a named approver and approval time.
02 / WORK
Demand became scoped work
An external issue invitation became a stable task and a recorded code-and-test scope, without treating scope as a measure of value.
03 / EXTERNAL OUTCOME
Transfer and upstream state
The recorded maintainer transfer and its contingency are separate from upstream state, which remains a mutable external observation.
04 / COST COMPLETENESS
Known, unknown, and unpriced
Known: maintainer payment was none. A known zero external transfer is not a zero total cost.
Missing or unpriced components
- model inference
- actual host compute
- human review
- shared tooling
No complete total is recorded; see the missing or unpriced component record above.
Evidence annexEconomic · technical · provenance · limitations
01Economic evidence
Lineage, usage, caps, outcome, and cost provenance
Economic evidence
Lineage, usage, caps, outcome, and cost provenanceAttempt lineage
M-125attempt 1 · FAILED ORACLE · verificationM-135attempt 2 · FAILED INFRA TERMINAL · infrastructureM-147attempt 3 · READY
Recorded usage
- requested reviewer model
- gpt-5.6-sol
- actual reviewer model
- not captured
- requested author model
- gpt-5.6-sol
- actual author model
- not captured
- networked setup phase
- 1m44s
- install-only duration
- not captured
- declared commands
- 6.1s
- unclassified executor time
- 1m03s
- CPU time
- not captured
- peak RSS
- not captured
Configured resource envelope
- CPU cap
- 2
- memory cap
- 4096 MB
- PID cap
- 512
- command wall cap
- 1800s
- output cap / stream
- 2000000 bytes
Configured limits are an envelope, not observed consumption.
Cost lines
- maintainer paymentobserved quantity unpriced · 0 external transfer · amount unavailable
Cost status: partial. Values without source-backed quantities and rates remain null.
Outcome facts
- technical checks passed
- true
- PR opened
- true
- CI
- pending
- merged
- false
- accepted as submitted
- not established
- external cycle time
- not captured
- released
- not observed
- deployed
- not observed
- business result
- not observed
02Technical evidence
Code, environment, exact commands, patch, and outputs
Technical evidence
Code, environment, exact commands, patch, and outputsProject
Work
tree.delete() skipped on error path — wrap parse+extract in try/finally · PR #156
Verification execution
runtime: northset-oss executor v1
human operator: aeziz
Code
- base
0a1d18ebab5f4b19fc8ea05c532fd3f051ab1e5e- recorded patch commit
61c9e8de3d36ec9bcca22fb6185668aa68666c66
declared metadata; not execution-bound- patch diff SHA-256
sha256:7f75f1ffa3ee9b87d11e49aa569eaa4dde913a376e816d0e22317ec56084c86f
bound to executed patch bytes
Environment
- image reference
- node@sha256:40ad9f3064e67d6860b4bc3fe1880b2953934fd6320ada990e45fe0efa6badd7
- repository digest
node@sha256:40ad9f3064e67d6860b4bc3fe1880b2953934fd6320ada990e45fe0efa6badd7- immutable image ID
sha256:6ed6ff875e9714bb845b850b08f3ad231ccbb5848812647bcf11acdd4bd1d2f8- platform
- linux/arm64
- network
- phaseA:bridge,phaseB:none
Declared checks
Execution summary
1/1 declared command returned exit 0 in the recorded environment
npm test -- src/lib/analysis/analyzers/shared.test.tsexit 0 · 6.1s
run wall (derived from recorded timestamps) 2m53s
PASS — 1/1 declared command
Every command listed returned exit 0 in the declared environment. Only the listed commands are in scope. Unlisted test, lint, typecheck, build, coverage, compiler, full-suite, and CI gates are not implied or recorded.
Public scope interpretation
The receipt proves the declared automated checks only; maintainer review and merge remain independent human decisions.
Committed patch.diff
diff --git a/src/lib/analysis/analyzers/shared.test.ts b/src/lib/analysis/analyzers/shared.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..88dfc1282844cff4fc0461145c75661b8a5e6ad8
--- /dev/null
+++ b/src/lib/analysis/analyzers/shared.test.ts
@@ -0,0 +1,61 @@
+import type Parser from "web-tree-sitter";
+import { expect, test, vi } from "vitest";
+import { getParser } from "../treesitter";
+import { analyzeProjectWith, type LangSpec } from "./shared";
+
+vi.mock("../treesitter", () => ({
+ getParser: vi.fn(),
+}));
+
+test("deletes a parsed tree when extraction throws", async () => {
+ const extractionError = new Error("capture failed");
+ const deleteTree = vi.fn();
+ const tree = {
+ rootNode: {},
+ delete: deleteTree,
+ } as unknown as Parser.Tree;
+
+ const defQuery = {
+ captures: vi.fn(() => {
+ throw extractionError;
+ }),
+ delete: vi.fn(),
+ } as unknown as Parser.Query;
+ const callQuery = {
+ captures: vi.fn(() => []),
+ delete: vi.fn(),
+ } as unknown as Parser.Query;
+ const importQuery = {
+ captures: vi.fn(() => []),
+ delete: vi.fn(),
+ } as unknown as Parser.Query;
+
+ const parser = {
+ setTimeoutMicros: vi.fn(),
+ parse: vi.fn(() => tree),
+ delete: vi.fn(),
+ } as unknown as Parser;
+ const language = {
+ query: vi
+ .fn()
+ .mockReturnValueOnce(defQuery)
+ .mockReturnValueOnce(callQuery)
+ .mockReturnValueOnce(importQuery),
+ } as unknown as Parser.Language;
+ vi.mocked(getParser).mockResolvedValue({ parser, language });
+
+ const spec: LangSpec = {
+ language: "test",
+ wasm: "test.wasm",
+ funcDefQuery: "(function) @def",
+ callQuery: "(call) @callee",
+ importQuery: "(import) @mod",
+ funcDefTypes: new Set(["function"]),
+ resolveModule: () => null,
+ };
+
+ await expect(
+ analyzeProjectWith(spec, [{ path: "input.test", content: "source" }]),
+ ).rejects.toBe(extractionError);
+ expect(deleteTree).toHaveBeenCalledOnce();
+});
diff --git a/src/lib/analysis/analyzers/shared.ts b/src/lib/analysis/analyzers/shared.ts
index ae7437761ed8b2f0bace05b23219ad7c3720c22f..a0ab230a1f2a65e9223425892c47d49aa9826b7f 100644
--- a/src/lib/analysis/analyzers/shared.ts
+++ b/src/lib/analysis/analyzers/shared.ts
@@ -122,65 +122,68 @@ function parseFile(
imports: new Set<string>(),
};
}
- const root = tree.rootNode;
-
- const classNodeTypes = spec.classNodeTypes ?? new Set<string>();
- const defs = new Set<string>();
- const methodClass = new Map<string, string | null>();
- for (const { node } of queries.defQuery.captures(root)) {
- const name = resolveName(node);
- if (!name) continue;
- defs.add(name);
- const cls = enclosingClassName(node, classNodeTypes);
- // Same name in different contexts (two classes, or module-level vs class)
- // -> ambiguous, mark null so we don't attribute it to the wrong class.
- if (methodClass.has(name)) {
- if (methodClass.get(name) !== cls) methodClass.set(name, null);
- } else {
- methodClass.set(name, cls);
- }
- }
+ try {
+ const root = tree.rootNode;
- const classes = new Set<string>();
- const extendsRel: { cls: string; base: string }[] = [];
- if (queries.classQuery) {
- for (const { node } of queries.classQuery.captures(root)) {
+ const classNodeTypes = spec.classNodeTypes ?? new Set<string>();
+ const defs = new Set<string>();
+ const methodClass = new Map<string, string | null>();
+ for (const { node } of queries.defQuery.captures(root)) {
const name = resolveName(node);
if (!name) continue;
- classes.add(name);
- const bases = spec.classBases?.(node) ?? [];
- for (const base of bases) extendsRel.push({ cls: name, base });
+ defs.add(name);
+ const cls = enclosingClassName(node, classNodeTypes);
+ // Same name in different contexts (two classes, or module-level vs class)
+ // -> ambiguous, mark null so we don't attribute it to the wrong class.
+ if (methodClass.has(name)) {
+ if (methodClass.get(name) !== cls) methodClass.set(name, null);
+ } else {
+ methodClass.set(name, cls);
+ }
}
- }
- const calls: { caller: string | null; callee: string }[] = [];
- for (const { node } of queries.callQuery.captures(root)) {
- calls.push({
- caller: enclosingFunctionName(node, spec.funcDefTypes),
- callee: node.text,
- });
- }
+ const classes = new Set<string>();
+ const extendsRel: { cls: string; base: string }[] = [];
+ if (queries.classQuery) {
+ for (const { node } of queries.classQuery.captures(root)) {
+ const name = resolveName(node);
+ if (!name) continue;
+ classes.add(name);
+ const bases = spec.classBases?.(node) ?? [];
+ for (const base of bases) extendsRel.push({ cls: name, base });
+ }
+ }
- const imports = new Set<string>();
- for (const { node } of queries.importQuery.captures(root)) {
- const target = spec.resolveModule(
- source.path,
- stripQuotes(node.text),
- paths,
- );
- if (target && target !== source.path) imports.add(target);
- }
+ const calls: { caller: string | null; callee: string }[] = [];
+ for (const { node } of queries.callQuery.captures(root)) {
+ calls.push({
+ caller: enclosingFunctionName(node, spec.funcDefTypes),
+ callee: node.text,
+ });
+ }
- tree.delete();
- return {
- file: source.path,
- defs,
- methodClass,
- classes,
- extendsRel,
- calls,
- imports,
- };
+ const imports = new Set<string>();
+ for (const { node } of queries.importQuery.captures(root)) {
+ const target = spec.resolveModule(
+ source.path,
+ stripQuotes(node.text),
+ paths,
+ );
+ if (target && target !== source.path) imports.add(target);
+ }
+
+ return {
+ file: source.path,
+ defs,
+ methodClass,
+ classes,
+ extendsRel,
+ calls,
+ imports,
+ };
+ } finally {
+ tree.delete();
+ }
}
/** Pick the defining file for a name, preferring local, then imported, then unique. */
Redacted stdout
=== cmd 1: npm test -- src/lib/analysis/analyzers/shared.test.ts ===
> weftmap@0.1.0 test
> vitest run src/lib/analysis/analyzers/shared.test.ts
[1m[30m[46m RUN [49m[39m[22m [36mv4.1.9 [39m[90m/workspace[39m
[32m✓[39m src/lib/analysis/analyzers/shared.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 3[2mms[22m[39m
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
[2m Tests [22m [1m[32m1 passed[39m[22m[90m (1)[39m
[2m Start at [22m 21:38:33
[2m Duration [22m 409ms[2m (transform 55ms, setup 0ms, import 86ms, tests 3ms, environment 0ms)[22m
Redacted stderr
=== cmd 1: npm test -- src/lib/analysis/analyzers/shared.test.ts ===
03Provenance & limitations
Bundle identity, attestation, verification, and claims boundary
Provenance & limitations
Bundle identity, attestation, verification, and claims boundaryRecord details
- payment
- none · not merge-contingent
- redactions
- none recorded
- Bundle contents digest
sha256:378a173ad385ca3dae0c34cbb753b089f380d2d99c0e7b48219fa3bc7ecb235b- Signed asset SHA-256
sha256:307efa5bf1005173de6760660d9b6f9882b90d920706bf548c4a271e204485b4- Signed provenance recorded
- verified 2026-07-16T22:49:26.958Z
NOT INCLUDED
- Does not prove code quality
- Does not prove security
- Contributor self-run record of Northset's own contribution; not the maintainer's verification.
- The receipt proves the declared automated checks only; maintainer review and merge remain independent human decisions.
Signed bundle
Verify this receipt
gh attestation verify run-record-M-147-378a173ad385.tar.gz --repo northset-oss/verification-pilot --signer-workflow northset-oss/verification-pilot/.github/workflows/attest-bundle.ymlAttestation confirms that Northset's signing workflow produced this exact bundle. The signer does not witness the recorded run, and verification does not turn it into maintainer verification.
QR → receipt page