NORTHSET
Proof-of-Pass Receipt
Receipt ID M-167
ISSUE / WORKloader: friendlier error when importing .ts without a TS loader · PR #16
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
- 9 totalcurrent
M-167· #9 - 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-143FAILED AUTHORauthoring - ATTEMPT 2
M-144FAILED AUTHORauthoring - ATTEMPT 3
M-145FAILED INFRA TERMINALinfrastructure - ATTEMPT 4
M-155FAILED INFRA TERMINALinfrastructure - ATTEMPT 5
M-156FAILED ORACLEverification - ATTEMPT 6
M-159FAILED ORACLEverification - ATTEMPT 7
M-165FAILED AUTHORauthoring - ATTEMPT 8
M-166FAILED AUTHORauthoring - ATTEMPT 9
M-167READY
ATTEMPT 9 / 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-143attempt 1 · FAILED AUTHOR · authoringM-144attempt 2 · FAILED AUTHOR · authoringM-145attempt 3 · FAILED INFRA TERMINAL · infrastructureM-155attempt 4 · FAILED INFRA TERMINAL · infrastructureM-156attempt 5 · FAILED ORACLE · verificationM-159attempt 6 · FAILED ORACLE · verificationM-165attempt 7 · FAILED AUTHOR · authoringM-166attempt 8 · FAILED AUTHOR · authoringM-167attempt 9 · 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
- 39.9s
- install-only duration
- not captured
- declared commands
- 3.1s
- unclassified executor time
- 9.2s
- 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
loader: friendlier error when importing .ts without a TS loader · PR #16
Verification execution
runtime: northset-oss executor v1
human operator: aeziz
Code
- base
471a5a12ccc364c68196fc13f2c1fc3c3ce375ea- recorded patch commit
85a8363a75f70d80d837205f89f9716b3caf179d
declared metadata; not execution-bound- patch diff SHA-256
sha256:6f531965f335cd27e987e7bb253da2fee90687665dfcc057ddd8dcbd2361ade8
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
ln -s "$PWD/node_modules" /tmp/node_modules && cp vitest.config.ts /tmp/vitest.config.ts && npm test -- --config /tmp/vitest.config.ts --root "$PWD" --no-cache tests/loader.test.tsexit 0 · 3.1s
run wall (derived from recorded timestamps) 52.3s
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/CHANGELOG.md b/CHANGELOG.md
index fbb05390e4b9a78d52d124ecb579a9c0f6163757..369f294bf70c1fe0052011791524704f23400a28 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
## [Unreleased] - 2026-07-08
+### Fixed
+- Explain how to load TypeScript prompt files when Node reports an unknown file extension.
### Maintenance
- Routine maintenance sync (14:47) — dependencies & docs review.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index f030e25b7bf0df99401d263219993c2cdf149f4a..b9845085f8e34faf8b9e4ee6ab0425448cd9ca78 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -30,6 +30,10 @@ export default definePrompt<{ name: string; language?: string }>({
})
```
+TypeScript prompt files require a runtime that supports them, such as `tsx`,
+`bun`, or `ts-node`. Plain Node cannot import `.ts` files directly, so use one
+of those runtimes or pre-compile the files to `.js`.
+
Render it without a model:
```ts
diff --git a/src/loader.ts b/src/loader.ts
index 1b28d40b66a973d5453efcca87b81124c1118653..88a10b4ffad1b2fbb4717be937c51261ea1c3852 100644
--- a/src/loader.ts
+++ b/src/loader.ts
@@ -13,6 +13,9 @@ import { LoaderError } from './errors.js'
import type { PromptDefinition, PromptInput } from './types.js'
const SUPPORTED_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'])
+const TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.mts', '.cts'])
+const TYPESCRIPT_LOADER_HINT =
+ "Node can't import `.ts` files directly. Run with `tsx`, `bun`, or `ts-node`, or pre-compile to `.js`."
/** Options for {@link loadPromptsFromDir}. */
export interface LoadOptions {
@@ -92,8 +95,16 @@ export async function loadPromptFile(
const url = pathToFileURL(filePath).href
mod = (await import(url)) as Record<string, unknown>
} catch (e) {
+ const errorMessage = e instanceof Error ? e.message : String(e)
+ const needsTypeScriptLoader =
+ TYPESCRIPT_EXTENSIONS.has(extname(filePath)) &&
+ typeof e === 'object' &&
+ e !== null &&
+ 'code' in e &&
+ e.code === 'ERR_UNKNOWN_FILE_EXTENSION'
+
throw new LoaderError(
- `Failed to import "${filePath}": ${e instanceof Error ? e.message : String(e)}`,
+ `Failed to import "${filePath}": ${errorMessage}${needsTypeScriptLoader ? ` ${TYPESCRIPT_LOADER_HINT}` : ''}`,
filePath,
)
}
diff --git a/tests/loader.test.ts b/tests/loader.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ed49eb18bc874673a23550b64000f0780fb7786
--- /dev/null
+++ b/tests/loader.test.ts
@@ -0,0 +1,25 @@
+import { describe, it, expect, vi } from 'vitest'
+import { loadPromptFile } from '../src/loader.js'
+
+vi.mock('node:url', () => ({
+ pathToFileURL(path: string) {
+ if (path === 'unsupported-prompt.ts') {
+ const source = `
+ const error = new Error('Unknown file extension ".ts"')
+ error.code = 'ERR_UNKNOWN_FILE_EXTENSION'
+ throw error
+ `
+ return new URL(`data:text/javascript,${encodeURIComponent(source)}`)
+ }
+
+ throw new Error(`Unexpected test path: ${path}`)
+ },
+}))
+
+describe('loadPromptFile', () => {
+ it('explains how to load TypeScript files without a loader', async () => {
+ await expect(loadPromptFile('unsupported-prompt.ts')).rejects.toThrow(
+ "Node can't import `.ts` files directly. Run with `tsx`, `bun`, or `ts-node`, or pre-compile to `.js`.",
+ )
+ })
+})
Redacted stdout
=== cmd 1: ln -s "$PWD/node_modules" /tmp/node_modules && cp vitest.config.ts /tmp/vitest.config.ts && npm test -- --config /tmp/vitest.config.ts --root "$PWD" --no-cache tests/loader.test.ts ===
> promptkit@0.1.0 test
> vitest run --config /tmp/vitest.config.ts --root /workspace --no-cache tests/loader.test.ts
[7m[1m[36m RUN [39m[22m[27m [36mv1.6.1[39m [90m/workspace[39m
[32m✓[39m tests/loader.test.ts [2m ([22m[2m1 test[22m[2m)[22m[90m 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 22:03:54
[2m Duration [22m 576ms[2m (transform 84ms, setup 0ms, collect 81ms, tests 3ms, environment 0ms, prepare 306ms)[22m
Redacted stderr
=== cmd 1: ln -s "$PWD/node_modules" /tmp/node_modules && cp vitest.config.ts /tmp/vitest.config.ts && npm test -- --config /tmp/vitest.config.ts --root "$PWD" --no-cache tests/loader.test.ts ===
[33mThe CJS build of Vite's Node API is deprecated. See https://vite.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.[39m
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:f230958e2caea8d6759d90a00300964538d314c9c16842b4cd12ee42d5eadcca- Signed asset SHA-256
sha256:e0cdd9f4a40597eb00d1091b6dd908083069116734a49e2de127c0ca73042544- Signed provenance recorded
- verified 2026-07-16T22:49:26.832Z
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-167-f230958e2cae.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